[
  {
    "path": ".gitignore",
    "content": "build\njar\nrst-doc\n*~\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "Makefile",
    "content": "# Global Makefile for RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=.\ninclude defs.mak\n\nall:\n\tcd $(JAVA_DIR) ; make -f Makefile.Server all\n\tcd $(JAVA_DIR) ; make -f Makefile.Monitor all\n\tcd $(JAVA_DIR) ; make -f Makefile.Observer all\n\tcd $(JAVA_DIR) ; make -f Makefile.Diagram all\n\tcd $(JAVA_DIR) ; make -f Makefile.GPIOObserver all\n\tcd $(JAVA_DIR) ; make -f Makefile.CodeObserver all\n\tcd $(JAVA_DIR) ; make -f Makefile.FifoObserver all\n\tcd $(JAVA_DIR) ; make -f Makefile.DocTool all\n\nrun: all\n\tcd $(JAVA_DIR) ; make -f Makefile.Server run\n\ntags:\n\t- find $(JAVA_DIR) -name \\*.java -exec etags {} \\; -print\n\nobjclean:\n\trm -rf $(ROOT_BUILD_DIR)\n\trm -rf $(JAR_DIR)\n\trm -rf $(RST_DOC_DIR)\n\nbkpclean:\n\t- find $(JAVA_DIR) -name \\*~ -exec /bin/rm -f {} \\; -print\n\trm -f *~\n\ncoreclean:\n\trm -f core core.* vgcore.*\n\trm -f $(JAVA_DIR)/core $(JAVA_DIR)/core.* $(JAVA_DIR)/vgcore.*\n\nclean: objclean\n\ndistclean: objclean bkpclean coreclean\n\ntarball: distclean\n\t@TGZ_DATE=`date +%Y-%m-%d_%H-%M-%S` ; \\\n\tPROJECT_NAME=rpi2040pio ; \\\n\tPROJECT_PATH=`basename \\`pwd\\`` ; \\\n\tTGZ_PREFIX=$$PROJECT_NAME\\_$$TGZ_DATE ; cd .. ; \\\n\ttar cvf ./$$TGZ_PREFIX.tar.bz2 \\\n\t\t--exclude=untracked \\\n\t\t--exclude=.git \\\n\t\t--transform=s/$$PROJECT_PATH/$$TGZ_PREFIX/ \\\n\t\t--bzip2 $$PROJECT_PATH\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "README.md",
    "content": "# RP2040 PIO Emulator\n\nAn emulator of the RP2040 state machines.\nThis emulator is _not_ intended as a real-time emulation of the actual\nhardware, but as a handy tool for understanding how the PIO works and\nfor testing and debugging when developing code for the PIO.\n\nFor detailed information, see [the full docs on readthedocs.io][1]\n\n## Motivation\n\nWhat is the purpose of an RP2040 emulator if you can easily use the\noriginal?  There are a number of good reasons for doing so:\n\n* To be able to trace / single step a PIO program as an invaluable aid\n  for developing and debugging, which is not easily possible directly\n  on the RP2040 hardware.\n\n* To inspect all of the PIO's internal state while developing and\n  debugging a PIO program.  This feature applies even to those parts\n  of the PIO that are not accessible when running on real RP2040\n  hardware, such as:\n\n  * the contents of PIO registers X, Y, ISR and OSR,\n\n  * the current value of the ISR / OSR shift count or\n\n  * the number of an instruction's pending delays.\n\n  In contrast, the emulator has access to the PIO's complete internal\n  logical state (otherwise, the emulation could not correctly work).\n\n* To debug your PIO program in the context of your IDE: The emulator\n  will typically run on your development host machine.  That is, there\n  is no need to upload the PIO program to a real RPI2040 for each and\n  every tiny change, thus saving time and stress in the course of\n  small and frequent development cycles.\n\n* To be able to automatically generate detailed timing diagrams\n  straight from an emulated run of a PIO program.  Timing diagrams are\n  highly useful for debugging as well as for documentation, as proof\n  of concept or fact sheet.  The selection of signals shown on the\n  diagram is freely configurable.\n\n* To be able to debug a PIO program even if there is no RP2040\n  hardware at hand.  (Fun fact: The RP2040 is still sold out at all of\n  the distributors easily accessible to me.  That is, I still do not\n  own an RP2040 of my own, but solely have to rely on the specs and\n  other sources of documentation.)\n\n* A motivation that applies to me personally: If I succeed in\n  implementing a (more or less) correctly working emulation of the\n  PIO, than I am somewhat confident that I have basically understood\n  how the PIO works and what capabilities it has when it comes to\n  writing PIO programs for it.\n\n## Current Features\n\n* The core PIO emulator is already running, though it has not yet been\nextensively tested.  That is, there are surely still many bugs in the\nemulation, such that emulation may behave wrong, especially for\nprograms that use more exotic and therefore not well tested features\nof the PIO.\n\n* The emulator can be used to automatically generate timing diagrams.\nHowever, configuration is still hard-wired in the source code (in the\n```run()``` method of class ```org.soundpaint.rp2040pio.Main```).\nWith one of the next commits, flexible configuration and program\nloading will be made possible through a configuration file and/or via\ncommand line arguments, rather than having to recompile the source\ncode of the emulator.  Since there are surely still lots of bugs in\nthe emulation, note that, as of now, the generated timing diagrams may\nbe faulty as well.\n\n<p>\n  <figure>\n    <a href=\"doc/screenshots/squarewave_1.png\">\n      <img src=\"doc/screenshots/squarewave_1.png\" />\n    </a>\n    <br />\n    <figcaption>\n      Fig. 1: Timing Diagram Generated from Emulation of\n      <code>squarewave</code> Program\n    </figcaption>\n  </figure>\n</p>\n\n## Future Plans\n\nA command line based monitor program will be provided to\ninteractively:\n\n* load programs,\n* list (unassemble) program code,\n* modify programs,\n* start / stop / synchronize state machines,\n* single step / trace into programs,\n* define break points,\n* inspect current contents of state machine registers, GPIO pins, FIFO\n  registers, shift registers, etc.\n* manipulate the current contents of registers and pins,\n* generate timing diagrams,\n\nand more.\n\nMaybe, I will also provide a graphical user interface that depicts the\ninternal state of the PIO in a graphical and more vivid manner.\n\n* Support for interaction with the PIO's outer world.  In particular:\n\n  * Support specifying when and which logical values will be put onto\n    the GPIO pins from an external source.\n\n  * Support specifying when and which values will be shifted into the\n    TX FIFO or shifted out from the RX FIFO via DMA access onto the\n    PIO.\n\n  * Support specifying when and which operations are triggered by\n    DMA access onto the PIO memory-mapped registers.\n\n  * Support specifying when and which instructions are inserted by\n    DMA access onto the PIO memory-mapped SMx_INSTR registers.\n\n  * Support specifying when an interrupt is requested by DMA access\n    onto the PIO memory-mapped interrupt registers.\n\n* Generate a warning whenever a race condition is detected.  In\n  particular, generate a warning when multiple state machines\n  concurrently access the same resource, like writing simultaneously\n  to the same GPIO pin.  Warnings can be reported in the monitor\n  program while debugging a program, as well as marked on the timing\n  diagram.\n\n* Generate a warning upon FIFO overflow / underflow.\n\n* Generate a warning when reading from or writing to a GPIO pin that\n  has pin direction that conflicts with the type of access.\n\n* When generating a timing diagram, support alternative backends such\n  as PDF or SVG.\n\n* If an alternative backend for timing diagrams provides tooltips, add\n  tooltips with additional info such as showing the complete\n  instruction (with all of its parameters) when hovering over the\n  instruction mnemonic in the timing diagram.\n\n* Also add tooltips with descriptive / explanatory text for all\n  warnings, where appropriate.\n\n<!-- References -->\n\n[1]: https://rp2040pio-docs.readthedocs.io/\n"
  },
  {
    "path": "defs.mak",
    "content": "# Global Makefile definitions for RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nJAVA_DIR=$(ROOT_DIR)/java\nROOT_BUILD_DIR=$(ROOT_DIR)/build\nJAR_DIR=$(ROOT_DIR)/jar\nRST_DOC_DIR=$(ROOT_DIR)/rst-doc\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.CodeObserver",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator Code Observer\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.observer.code.CodeObserver\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.Diagram",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator Diagram\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.observer.diagram.Diagram\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.DocTool",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator Documentation Tool\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.doctool.DocsBuilder\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.FifoObserver",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator FIFO Observer\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.observer.fifo.FifoObserver\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.GPIOObserver",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator GPIO Observer\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.observer.gpio.GPIOObserver\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.Monitor",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator Monitor\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.monitor.Monitor\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.Observer",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulator Command-Line Observer\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.observer.Observer\n"
  },
  {
    "path": "java/META-INF/MANIFEST.MF.Server",
    "content": "Manifest-Version: 1.0\nSpecification-Title: N.A.\nCreated-By: Jürgen Reuter\nImplementation-Title: RP2040 PIO Emulation Server\nSpecification-Vendor: Juergen Reuter\nImplementation-Vendor: Juergen Reuter\nMain-Class: org.soundpaint.rp2040pio.EmulationServer\n"
  },
  {
    "path": "java/Makefile.CodeObserver",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/CodeObserver\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/observer/code/CodeObserver.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_codeobserver.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.CodeObserver .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.Diagram",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/Diagram\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/observer/diagram/Diagram.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_diagram.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Diagram .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.observer.diagram.Diagram\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.DocTool",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/DocTool\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/doctool/RegistersDocsBuilder.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_doctool.jar\nRST_DOC_OBJ = \\\n\t$(RST_DOC_DIR)/example-scripts.rst \\\n\t$(RST_DOC_DIR)/monitor-commands.rst \\\n\t$(RST_DOC_DIR)/pico-emu-registers.rst \\\n\t$(RST_DOC_DIR)/pio-emu-registers.rst\n\nall: obj jar doc\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\n$(RST_DOC_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.DocTool .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\ndoc: $(RST_DOC_DIR) $(JAR_OBJ) $(RST_DOC_OBJ)\n\n$(RST_DOC_OBJ): $(JAR_OBJ)\n\tcd $(RST_DOC_DIR) ; java -jar $(JAR_OBJ)\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.FifoObserver",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/FifoObserver\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/observer/fifo/FifoObserver.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_fifoobserver.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.FifoObserver .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.GPIOObserver",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/GPIOObserver\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/observer/gpio/GPIOObserver.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_gpioobserver.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.GPIOObserver .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.Monitor",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/Monitor\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/monitor/Monitor.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_monitor.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Monitor .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.Observer",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/Observer\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/observer/Observer.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_observer.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Observer .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/Makefile.Server",
    "content": "# Makefile for Java tree of RP2040 PIO emulator\n#\n# Copyright (C) 2021 Jürgen Reuter\n#\n# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program; if not, write to the Free Software\n# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n#\n# For updates and more info or contacting the author, visit:\n# <https://github.com/soundpaint/rp2040pio>\n#\n# Author's web site: www.juergen-reuter.de\n\nROOT_DIR=..\ninclude ../defs.mak\n\nBUILD_DIR=$(ROOT_BUILD_DIR)/EmulationServer\nCOMPILE_CLASSPATH=$(JAVA_DIR):$(BUILD_DIR)\nRUN_CLASSPATH=.\nPIO_DIR=$(JAVA_DIR)/org/soundpaint/rp2040pio\nJAVA_SRC=$(wildcard $(PIO_DIR)/EmulationServer.java)\nJAVA_OBJ=$(patsubst $(JAVA_DIR)/%.java,$(BUILD_DIR)/%.class,$(JAVA_SRC))\n\nMEDIA_SRC_DIR=$(JAVA_DIR)/media\nMEDIA_OBJ_DIR=$(BUILD_DIR)/media\nMEDIA_SRC=$(wildcard $(MEDIA_SRC_DIR)/*.png $(MEDIA_SRC_DIR)/*.html)\nMEDIA_OBJ=$(patsubst $(MEDIA_SRC_DIR)/%,$(MEDIA_OBJ_DIR)/%,$(MEDIA_SRC))\n\nEXAMPLES_SRC_DIR=$(JAVA_DIR)/examples\nEXAMPLES_OBJ_DIR=$(BUILD_DIR)/examples\nEXAMPLES_SRC=$(wildcard $(EXAMPLES_SRC_DIR)/*.hex) $(wildcard $(EXAMPLES_SRC_DIR)/*.mon)\nEXAMPLES_OBJ=$(patsubst $(EXAMPLES_SRC_DIR)/%,$(EXAMPLES_OBJ_DIR)/%,$(EXAMPLES_SRC))\n\nJAR_OBJ=$(JAR_DIR)/rp2040pio_server.jar\n\nall: obj jar\n\nobj: $(BUILD_DIR) $(JAVA_OBJ) \\\n\t$(MEDIA_OBJ_DIR) $(MEDIA_OBJ) \\\n\t$(EXAMPLES_OBJ_DIR) $(EXAMPLES_OBJ)\n\n$(BUILD_DIR):\n\tmkdir -p $@\n\n$(MEDIA_OBJ_DIR):\n\techo BUILD_MEDIA_DIR=$(MEDIA_OBJ_DIR)\n\tmkdir -p $@\n\n$(EXAMPLES_OBJ_DIR):\n\techo BUILD_EXAMPLES_DIR=$(EXAMPLES_OBJ_DIR)\n\tmkdir -p $@\n\n$(JAR_DIR):\n\tmkdir -p $@\n\njar: $(JAR_DIR) $(JAR_OBJ)\n\n$(JAR_OBJ): $(JAVA_OBJ)\n\tcd $(BUILD_DIR) ; \\\n\tjar -0cvfm ../$(JAR_OBJ) ../$(JAVA_DIR)/META-INF/MANIFEST.MF.Server .\n\n$(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.hex: $(EXAMPLES_SRC_DIR)/%.hex\n\tcp -pf $< $@\n\n$(EXAMPLES_OBJ_DIR)/%.mon: $(EXAMPLES_SRC_DIR)/%.mon\n\tcp -pf $< $@\n\ndepend:\n\nrun: all\n\tcd $(JAR_DIR) ; java -jar $(JAR_OBJ)\n\t#cd $(BUILD_DIR) ; java -ea -cp $(RUN_CLASSPATH) org.soundpaint.rp2040pio.Main\n\nobjclean:\n\t- rm -rf $(BUILD_DIR)\n\njarclean:\n\t- rm -rf $(JAR_DIR)\n\nclean: objclean jarclean\n\n.SUFFIXES: $(SUFFIXES) .java .class\n\n$(BUILD_DIR)%class: $(JAVA_DIR)%java\n\tjavac -Xlint:all -Xdiags:verbose -d $(BUILD_DIR) -cp $(COMPILE_CLASSPATH) $<\n\n#  Local Variables:\n#    coding:utf-8\n#    mode:Makefile\n#  End:\n"
  },
  {
    "path": "java/examples/addition.mon",
    "content": "# Script: Addition\n#\n# Monitor script for addition example (see RP2040\n# datasheet, Sect. 3.6.9. \"Addition\").\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=8 --target=0\n\n# The code.\n# ; This program uses the two's complement identity x + y == ~(~x - y)\nenter -a 0 -v 0x80a0 # pull\nenter -a 1 -v 0xa02f # mov x, ~osr\nenter -a 2 -v 0x80a0 # pull\nenter -a 3 -v 0xa047 # mov y, osr\nenter -a 4 -v 0x0006 # jmp test\n#                         ; this loop is equivalent to the following C code:\n#  incr:                            ; while (y--)\nenter -a 5 -v 0x0046 # jmp x-- test ; x--;\n#  test:                  ; This has the effect of subtracting y from x, eventually.\nenter -a 6 -v 0x0085 # jmp y-- incr\nenter -a 7 -v 0xa0c9 # mov isr, ~x\nenter -a 8 -v 0x8020 # push\n\n# Set up the program for execution on PIO 0, SM 0 with side-set 0.\nside-set --pio=0 --sm=0 --count=0\nsm --pio=0 --sm=0 --enable=true\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=9\n\n# Set up FIFO with 2 example operands\nfifo --tx --enqueue --value 0x0003\nfifo --tx --enqueue --value 0x0005\n\n# Result 0x3 + 0x5 = 0x8 should eventually appear in RX FIFO.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/apa102-mini.mon",
    "content": "# Script: APA102 Mini\n# Group: APA102\n#\n# Monitor script for APA102 Mini example.\n# To be executed on PIO 0, SM 0.\n# pin_din is mapped to GPIO0 in this example.\n# pin_clk is mapped to GPIO1 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/apa102/apa102.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# loop:\n# .wrap_target\nenter -a 00 -v 0x6001 # out pins, 1   side 0\nenter -a 01 -v 0xb042 # nop           side 1\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to apa102_mini_program_init() in apa102.pio).\n########\n\n# Set pin_din and pin_clk to 0, direction out.\ngpio --pio=0 --gpio=0 --clear\ngpio --pio=0 --gpio=0 --enable\ngpio --pio=0 --gpio=1 --clear\ngpio --pio=0 --gpio=1 --enable\n\n# GPIO init pin_din and pin_clk.\ngpio --pio=0 --gpio=0 --init\ngpio --pio=0 --gpio=1 --init\n\n# SM Config Set Out Pins(base=pin_din, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set Side-Set Pins(pin_clk).\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Shift(left, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# We only need TX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0xa55a0ff0\nfifo --enqueue --tx --value 0x1e3c78f0\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/apa102-rgb555.mon",
    "content": "# Script: APA102 RGB555\n# Group: APA102\n#\n# Monitor script for AP102 RGB555 example.\n# To be executed on PIO 0, SM 0.\n# output pin is mapped to GPIO0.\n\n# Make a full reset of the emulator.\nreset\n\n# Since there is no initialization in\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/apa102/apa102.pio, we just do it\n# on our own, extracting all relevant info from\n# the PIO program itself.\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=14 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n# .wrap_target\n# public pixel_out:\nenter -a 00 -v 0x80e0 # pull ifempty\nenter -a 01 -v 0xe022 # set x, 2\n# colour_loop:\nenter -a 02 -v 0x40e5 # in osr, 5\nenter -a 03 -v 0x6065 # out null, 5\nenter -a 04 -v 0x4063 # in null, 3\nenter -a 05 -v 0x0042 # jmp x-- colour_loop\nenter -a 06 -v 0x4048 # in y, 8\nenter -a 07 -v 0xa0d6 # mov isr, ::isr\nenter -a 08 -v 0x6061 # out null, 1\n# public bit_run:\nenter -a 09 -v 0xe03f # set x, 31\n# bit_out:\nenter -a 10 -v 0xe000 # set pins, 0\nenter -a 11 -v 0xa606 # mov pins, isr [6]\nenter -a 12 -v 0xe001 # set pins, 1\nenter -a 13 -v 0x46c1 # in isr, 1 [6]\nenter -a 14 -v 0x004a # jmp x-- bit_out\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=15\n\n########\n# Init program (based on comments and PIO code).\n########\n\n# SM Config Set Out Pins(base=pin, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set Out Shift(right, no autopull)\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=false\n\n# SM Config Set In Shift(right, no autopush)\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=false\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Set consecutive pins, here just a single one.\ngpio --pio=0 --gpio=0 --clear # initialize out with 0\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# GPIO Init\ngpio --pio=0 --gpio=0 --init\n\n# We only need TX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=0 --join --tx\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to program entry point.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Set brightness value.  Only lowest most 5 bits can be freely chosen,\n# i.e.  valid brightness values are in the range 0…31.  The upper 3 bits must be\n# 1.  That is, valid values for setting Y are values in the range 224…255.\n# In this example program, we choose 245 (=0xf5).\nregisters --y=0xf5 # brightness bit pattern 10101, with three leading \"1\"s\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# See https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/apa102/apa102.pio (\"APA102 command structure\") for\n# details on the encoding of the resulting 32 bit frames that are\n# output to the GPIO pin.\n\n# FIFO entries: Each entry contains 2 pixels, each consisting of 5+5+5\n# bits for colors red, green, and blue, respectively.  The first pixel\n# is coded in the lower 16 bits, the next one in the upper 16 bits.\n# Hence, the overall binary bit representation of a FIFO entry is:\n# 0b0bbbbbgggggrrrrr0bbbbbgggggrrrrr.\nfifo --enqueue --tx --value 0x03e0001f # red, then green\nfifo --enqueue --tx --value 0x7fff7c00 # blue, then white\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/auto-push-pull.mon",
    "content": "# Script: Auto-Push-Pull\n# Group: Autopush and Autopull\n#\n# Monitor script for auto-push-pull example (see RP2040\n# datasheet, Sect. 3.5.4. \"Autopush and Autopull\").\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=0 --opt=false   # no side-set\n\n# The code.\n#                  .wrap_target\nenter -a 0 -v 0x6020 # out x, 32\nenter -a 1 -v 0x4020 # in x,32\n#                  .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Configure IN / OUT shift.\nfifo --pio=0 --sm=0 --rx --shift-left    # ISR shift left\nfifo --pio=0 --sm=0 --rx --auto=true     # auto push on\nfifo --pio=0 --sm=0 --rx --threshold=32  # auto push threshold\nfifo --pio=0 --sm=0 --tx --shift-left    # OSR shift left\nfifo --pio=0 --sm=0 --tx --auto=true     # auto pull on\nfifo --pio=0 --sm=0 --tx --threshold=32  # auto pull threshold\n\n# Configure FIFO join: No join.\nfifo --pio=0 --sm=0 --unjoin --tx\nfifo --pio=0 --sm=0 --unjoin --rx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0x0\nfifo --enqueue --tx --value 0x1\nfifo --enqueue --tx --value 0x2\nfifo --enqueue --tx --value 0x3\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/autopull.mon",
    "content": "# Script: Autopull\n# Group: Autopush and Autopull\n#\n# Monitor script for auto pull example (see RP2040\n# datasheet, Sect. 3.5.4. \"Autopush and Autopull\").\n# To be executed on PIO 0, SM 0.\n# Clock (side-set) output is mapped to GPIO 0.\n# Data (OUT) ouput is mapped to GPIO 1.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=false   # .side_set 1\n\n# The code.\n#                  .wrap_target\nenter -a 0 -v 0x6101 # out pins, 1     side 0 [1] ; Shift out data bit and toggle clock low\nenter -a 1 -v 0xb142 # nop             side 1 [1] ; toggle clock high\n#                  .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect GPIO pins 0 and 1 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-left    # OSR shift left\nfifo --pio=0 --sm=0 --tx --auto=true     # auto pull on\nfifo --pio=0 --sm=0 --tx --threshold=4  # auto pull threshold\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Configure clock (side-set) for GPIO 0.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure data (OUT) to output a single bit to GPIO 1.\npinctrl --pio=0 --sm=0 --out-count=1 --out-base=1\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0xffff0000 # bit pattern 11111111...00000000\nfifo --enqueue --tx --value 0x0000ffff # bit pattern 00000000...11111111\nfifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010...10101010\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/blink.mon",
    "content": "# Script: PIO Blink\n#\n# Monitor script for PIO Blink example.\n# To be executed on PIO 0, SM 0.\n# For LED on OUT pin, mapped to GPIO0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/pio_blink/blink.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=7 --target=2\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\nenter -a 00 -v 0x80a0 # pull block\nenter -a 01 -v 0x6040 # out y, 32\n# .wrap_target\nenter -a 02 -v 0xa022 # mov x, y\nenter -a 03 -v 0xe001 # set pins, 1  ; turn LED on\n# lp1:\nenter -a 04 -v 0x0044 # jmp x-- lp1  ; (x + 1) cycles delay, x is 32 bit number\nenter -a 05 -v 0xa022 # mov x, y\nenter -a 06 -v 0xe000 # set pins, 0  ; turn LED off\n# lp2:\nenter -a 07 -v 0x0047 # jmp x-- lp2  ; delay for same number of cycles again\n# .wrap                              ; blink forever\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=8\n\n########\n# Init program (analoguous to st7789_lcd_program_init() in st7789_lcd.pio).\n########\n\n# GPIO init OUT pin\ngpio --pio=0 --gpio=0 --init    # OUT pin on GPIO0\n\n# Set consecutive pindirs, here just a single one for OUT pin.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# SM Config Set Set Pins(base=0, count=1).\npinctrl --pio=0 --sm=0 --set-base=0 --set-count=1\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n########\n\n# Put LED blink period length (number of clock cycles per phase minus\n# 3) into FIFO.  For example, a value of 4 will result in overall\n# blink cycle length of 2 * (4 + 3) = 14 clock cycles.\nfifo --enqueue --tx --value 0x00000004\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/bmc-rx.mon",
    "content": "# Script: Differential Manchester RX\n# Group: Differential Manchester (BMC) TX and RX\n#\n# Monitor script for BMC RX example (see RP2040 datasheet,\n# Sect. 3.6.6. \"Differential Manchester (BMC) TX and RX\").  To be\n# executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=9 --target=5\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n# public start:\n# initial_high:\nenter -a 0 -v 0x2ba0 # wait 1 pin, 0  [11]\nenter -a 1 -v 0x00c4 # jmp pin high_0\n# high_1:\nenter -a 2 -v 0x4021 # in x, 1\nenter -a 3 -v 0x0000 # jmp initial_high\n# high_0:\nenter -a 4 -v 0x4141 # in y, 1 [1]\n# .wrap_target\n# initial_low:\nenter -a 5 -v 0x2b20 # wait 0 pin, 0 [11]\nenter -a 6 -v 0x00c9 # jmp pin low_1\n# low_0:\nenter -a 7 -v 0x4041 # in y, 1\nenter -a 8 -v 0x0000 # jmp initial_high\n# low_1:\nenter -a 9 -v 0x4121 # in x, 1 [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=10\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --disable # set direction in\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Configure side set base (for WAIT).\nside-set --pio=0 --sm=0 --base=0\n\n# SM Config Set JMP PIN (for JMP).\npinctrl --pio=0 --sm=0 --jmp-pin=0\n\n# Configure in shift.\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=true    # auto push\nfifo --pio=0 --sm=0 --rx --threshold=32\n\n# Join FIFO RX\nfifo --join --rx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Set X and Y to 1 and 0, to conveniently emit these to ISR/FIFO\nregisters --x=1\nregisters --y=0\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# While running the PIO program by triggering cycles, insert now and\n# then either:\n\ngpio --gpio=0 --set\n\n# or:\n\ngpio --gpio=0 --clear\n\n# after an appropriate amount of cycles to change GPIO 0 input, as\n# appropriate for testing / emulating external data input to the BMC\n# RX PIO program.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/bmc-tx.mon",
    "content": "# Script: Differential Manchester TX\n# Group: Differential Manchester (BMC) TX and RX\n#\n# Monitor script for BMC TX example (see RP2040 datasheet,\n# Sect. 3.6.6. \"Differential Manchester (BMC) TX and RX\").  To be\n# executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=7 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=true\n\n# The code.\n# public start:\n# initial_high:\nenter -a 0 -v 0x7821 # out x, 1          side 1\nenter -a 1 -v 0x0623 # jmp !x high_0            [6]\n# high_1:\nenter -a 2 -v 0x1700 # jmp initial_high  side 0 [7]\n# high_0:\nenter -a 3 -v 0x0704 # jmp initial_low          [7]\n\n# initial_low:\nenter -a 4 -v 0x7021 # out x, 1          side 0\nenter -a 5 -v 0x0627 # jmp !x low_0             [6]\n# low_1:\nenter -a 6 -v 0x1f04 # jmp initial_low   side 1 [7]\n# low_0:\nenter -a 7 -v 0x0700 # jmp initial_high         [7]\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=8\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set GPIO output value to 0.\ngpio --pio=0 --gpio=0 --clear\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Configure side set base.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=true    # auto pull\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# Join FIFO TX\nfifo --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Unlike the initialization in differential_manchester.pio, we do\n# *not* execute a blocking pull here since we feed in data for\n# immediate consumption a few lines down from here.\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0x115a88a5\nfifo --enqueue --tx --value 0xf0f0f0f0\n\n# Done.\nquit\n\n# Hint: In the Diagram Creator, you can select 16 as \"Cycles\" value,\n# and start the \"emulate\" button with one of the two \"OUT x, 01\"\n# instructions being the next instruction to be executed.  Then, you\n# can watch bit by bit producing the corresponding output level at\n# GPIO0, with the bits shifted one by one out of OSR.\n"
  },
  {
    "path": "java/examples/clocked-input.mon",
    "content": "# Script: Clocked Input\n#\n# Monitor script for Clocked Input example.\n# To be executed on PIO 0, SM 0.\n# Data input pin is mapped to GPIO0.\n# Clock input pin is mapped to GPIO1.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/clocked_input/clocked_input.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=2 --target=0\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x2021 # wait 0 pin 1\nenter -a 01 -v 0x20a1 # wait 1 pin 1\nenter -a 02 -v 0x4001 # in pins, 1\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n########\n# Init program (analoguous to clocked_input_program_init() in\n# clocked_input.pio).\n########\n\n# SM Config Set In Pins(base=pin).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# Set consecutive pindirs, here two pins 0 and 1 for data and clock.\ngpio --pio=0 --gpio=0 --disable # set direction in\ngpio --pio=0 --gpio=1 --disable # set direction in\n\n# GPIO init pins.\ngpio --pio=0 --gpio=0 --init\ngpio --pio=0 --gpio=1 --init\n\n# SM Config Set In Shift(left, autopush, threshold 8)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Join FIFO RX\nfifo --join --rx\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n########\n\n# To see the clocked input in action, you currently have to manually\n# provide some input at GPIO0 (data input) and GPIO1 (clock input).\n# For that purpose, in the Monitor client application, use commands\n# like \"gpio --gpio=0 --clear\" or \"gpio --gpio=0 --set\" to select\n# value 0 or 1 as data value for GPIO0, and \"gpio --gpio=1 --clear\"\n# or \"gpio --gpio=1 --set\" to control the clock signal on GPIO1.\n#\n# Whenever on GPIO1 (the clock input) a change from input value 0 to\n# input value 1 is detected, on the next PIO clock cycle, the value\n# that is then found as input on GPIO0 will be recorded as a bit.  Bits\n# are collected and packaged as bytes (=8 bits), and each byte, as soon\n# as complete, will be pushed onto the RX FIFO.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/exec-example.mon",
    "content": "# Script: Exec Example\n#\n# Monitor script for EXEC'd instructions\n# example.\n#\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# The code.\n# hang:\nenter -a 0 -v 0x0000 # jmp hang\n# execute:\nenter -a 1 -v 0x60e0 # out exec, 32\nenter -a 2 -v 0x0001 # jmp execute\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# SM Config Set Out Shift(left, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Force jump to program location 1.\nexecute --pio=0 --sm=0 --force=0x0001 # jmp 01\n\n# Put PIO instruction opcodes into FIFO.\n\nfifo --enqueue --tx --value 0x00006020 # out x, 32\nfifo --enqueue --tx --value 0x12345678 # data to be OUTed\nfifo --enqueue --tx --value 0x00004020 # in x, 32\nfifo --enqueue --tx --value 0x00008020 # push\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/ext-wave.mon",
    "content": "# Script: External Wave\n# Group: Squarewave\n#\n# Monitor script for providing an external square wave signal put\n# onto GPIO pad 23, that changes the pin's status each 3rd clock\n# cycle.\n# This script serves as an example how to automatically set GPIO\n# pads in sync with another script that is executed in parallel.\n# See chapter \"Interfacing With External Data\" in the RP2040 PIO\n# Emulator documentation for details.\n\n# We do *not* reset the emulator, but assume that this script is run\n# with the emulator already properly set up.  Furthermore, we assume\n# that the emulator starts with clock cycle 0.  Monitor scripts do not\n# (yet) provide loops; hence we have to unroll any periodic activity\n# for now.\n\n# Set GPIO pin 23 to 1\n\ngpio --gpio=23 --set\n\n# Show effect\n\ngpio\n\n# Await *absolute* clock cycle 0x00000002.\n#\n# But (just for case that we are already beyond that cycle), as\n# fallback, finally give up after 5 cycles, or, alternatively, after\n# 300 seconds.\n\nwait --address=0x58000014 --value=0x2 --cycles=5 --time=300000\n\n# The PIO emulator performs instruction fetch & decode during clock\n# cycle phase 0, and instruction execution during cycle phase 1.\n# Therefore, the trace command of the monitor application displays the\n# GPIO state immediately after clock phase 1 has settled,\n# i.e. immediately after instruction execution has been completed.  To\n# avoid a race between the monitor showing the GPIO state after\n# completion of cycle phase 1 and this external signal supplying\n# script updating the GPIO state also immediately after completion of\n# cycle phase 1, scripts like this one are advised to update signal\n# state instead immediately after clock phase 0 has settled\n# (i.e. after PIO instruction fetch & decode, but before instruction\n# execution).  For that purpose, we do another wait for the next cycle\n# phase 0 to become settled.\n\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\n\n# Now, clear GPIO pin 23 to 1\n\ngpio --gpio=23 --clear\n\n# Show effect\n\ngpio\n\n# Now wait again, but this time nor for an absolute amount of clock\n# cycles, but for 3 clock cycles to pass.  This can be done by\n# specifying a value match condition that surely will never be occur,\n# and additionally specify a timeout of 3 cycles, such that the wait\n# command will be for sure terminated after the timeout (rather than\n# by the value to be matched).  Specifically, option \"--mask=0\" will\n# mask out all bits, such that always a value of 0 will be received,\n# such that waiting for \"--value=1\" will never succeed, such that\n# finally the timeout will take effect after 3 cycles.  Also, turn off\n# the default milliseconds timeout of 100 seconds by setting it to 0.\n\nwait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\n\n# GPIO pin 23 := 1, and show effect.\n\ngpio --gpio=23 --set\ngpio\n\n# Wait again 3 cycles\n\nwait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\n\n# GPIO pin 23 := 0, and show effect.\n\ngpio --gpio=23 --clear\ngpio\n\n# Wait.\n\nwait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\n\n# GPIO pin 23 := 1, and show effect.\n\ngpio --gpio=23 --set\ngpio\n\n# And so on, for some more cycles...\n\nwait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\ngpio --gpio=23 --clear\ngpio\nwait --address=0x58000014 --mask=0 --value=1 --cycles=3 --time=0\nwait --address=0x5800000c --value=1 --cycles=0 --time=0\ngpio --gpio=23 --set\ngpio\n\n# And the same with abbreviated syntax.  Note that options \"--cycles\"\n# and \"--time\" can be completely dropped if we choose to use the\n# default values, which is ok, since we expect the wait to finish\n# within a very short period of time.\n\nwa -a 0x58000014 -m 0 -v 1 -c 3\nwa -a 0x5800000c -v 1\ng -g 23 -c\ng\nwa -a 0x58000014 -m 0 -v 1 -c 3\nwa -a 0x5800000c -v 1\ng -g 23 -s\ng\n\n# And some more cycles, this time without showing each change.\n\nwa -a 0x58000014 -m 0 -v 1 -c 3\nwa -a 0x5800000c -v 1\ng -g 23 -c\nwa -a 0x58000014 -m 0 -v 1 -c 3\nwa -a 0x5800000c -v 1\ng -g 23 -s\n\n# And finally quit, when we are done.\n\nq\n\n# Done.\n"
  },
  {
    "path": "java/examples/hello.mon",
    "content": "# Script: Hello PIO\n#\n# Monitor script for Hello PIO example.\n# To be executed on PIO 0, SM 0.\n# pin is mapped to GPIO0 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/hello_pio/hello.pio\n\n# The code.\n# loop:\nenter -a 00 -v 0x80a0 # pull\nenter -a 01 -v 0x6001 # out pins, 1\nenter -a 02 -v 0x0000 # jmp loop\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n########\n# Init program (analoguous to hello_program_init() in hello.pio).\n########\n\n# SM Config Set Out Pins(base=pin, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# GPIO init pin.\ngpio --pio=0 --gpio=0 --init\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n########\n\n# Put example values into FIFO.  Only the most significant bit of\n# each FIFO word is output to the GPIO output pin.  The value of\n# all other bits is irrelevant.\nfifo --enqueue --tx --value 0x80000000\nfifo --enqueue --tx --value 0x00000000\nfifo --enqueue --tx --value 0x80000000\nfifo --enqueue --tx --value 0x80000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/hub75.mon",
    "content": "# Script: HUB75\n#\n# Monitor script for HUB75 example.\n# To be executed on PIO0, SM0 and SM1.\n#\n# This example consists of *two* PIO programs\n# that are executed in parallel on two\n# different state machines.  The first PIO program\n# is to be executed on SM0 and cares for output pins\n# GPIO6…GPIO10, GPIO12 and GPIO13.  The second PIO\n# program is to be executed on SM1 and cares for\n# GPIO0…GPIO5 and GPIO11.  In particular:\n#\n# * SM1 RGB (rrggbb) out data pins, mapped to GPIO0…GPIO5.\n# * SM0 Row Select out pins are A-E, mapped to GPIO6…GPIO10.\n# * SM1 side-set pin 0 is clock_pin, mapped to GPIO11.\n# * SM0 side-set pin 0 is Latch (aka Strobe), mapped to GPIO12.\n# * SM0 side-set pin 1 is OEn, mapped to GPIO13.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/hub75/hub75.pio\n\n#################################################\n####\n#### First of the two PIO programs (\"hub75_row\"):\n####\n#################################################\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=2 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=2 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x7705 # out pins, 5 [7]    side 0x2\nenter -a 01 -v 0x7f3b # out x, 27   [7]    side 0x3\n# pulse_loop:\nenter -a 02 -v 0x0042 # jmp x-- pulse_loop side 0x0\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to hub75_row_program_init() in hub75.pio).\n########\n\n# Set consecutive pindirs (row_base_pin=6, count=5).\ngpio --pio=0 --gpio=6  --enable # row \"A\": set direction out\ngpio --pio=0 --gpio=7  --enable # row \"B\": set direction out\ngpio --pio=0 --gpio=8  --enable # row \"C\": set direction out\ngpio --pio=0 --gpio=9  --enable # row \"D\": set direction out\ngpio --pio=0 --gpio=10 --enable # row \"E\": set direction out\n\n# Set consecutive pindirs (base=latch_base_pin=12, count=2).\ngpio --pio=0 --gpio=12 --enable # \"STB\": set direction out\ngpio --pio=0 --gpio=13 --enable # \"OEn\": set direction out\n\n# GPIO init row \"A\" pin.\ngpio --pio=0 --gpio=6 --init   # row \"A\" pin on GPIO6\n\n# GPIO init row \"B\" pin.\ngpio --pio=0 --gpio=7 --init   # row \"B\" pin on GPIO7\n\n# GPIO init row \"C\" pin.\ngpio --pio=0 --gpio=8 --init   # row \"C\" pin on GPIO8\n\n# GPIO init row \"D\" pin.\ngpio --pio=0 --gpio=9 --init   # row \"D\" pin on GPIO9\n\n# GPIO init row \"E\" pin.\ngpio --pio=0 --gpio=10 --init  # row \"E\" pin on GPIO10\n\n# GPIO init Latch pin.\ngpio --pio=0 --gpio=12 --init  # Latch pin on GPIO12\n\n# GPIO init Oen pin.\ngpio --pio=0 --gpio=13 --init  # Oen pin on GPIO13\n\n# SM Config Set Out Pins(base=row_base_pin=6, count=5).\npinctrl --pio=0 --sm=0 --out-base=6 --out-count=5\n\n# SM Config Set Side-Set Pins(base=latch_base_pin=12).\nside-set --pio=0 --sm=0 --base=12\n\n# SM Config Set Out Shift(right, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --pio=0 --sm=0 --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# DO NOT YET ENABLE STATE MACHINE 0 OF PIO 0.  Because, while\n# initializing state machine 1, we execute a forced instruction,\n# resulting in also executing the first cycle on SM0, if it were\n# enabled already now.  Instead, we enable SM0 together with SM1 after\n# initialization of SM1.\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Lowest 5 bits define ouputs A, B, C,\n# D, E.  Upper 27 bits define number of cycles to set Latch and Oen\n# both to 0, before continuing with next row.  In this example, we\n# choose 7 cycles to keep synchronized with the other PIO program that\n# is executed on SM1.\nfifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f0 # row 16, wait 7 cycles\nfifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f1 # row 17, wait 7 cycles\nfifo --pio=0 --sm=0 --enqueue --tx --value 0x000000f2 # row 18, wait 7 cycles\n\n##########################################################\n####\n#### Second of the two PIO programs (\"hub75_data_rgb888\"):\n####\n##########################################################\n\n# Configure Wrap.\nwrap --pio=0 --sm=1 --wrap=18 --target=3\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=1 --count=1 --opt=false\n\n# The code.\n# public entry_point:\n# .wrap_target\n# public shift0:\nenter -a 03 -v 0x80a0 # pull             side 0\nenter -a 04 -v 0x40e1 # in osr, 1        side 0\nenter -a 05 -v 0x6068 # out null, 8      side 0\nenter -a 06 -v 0x40e1 # in osr, 1        side 0\nenter -a 07 -v 0x6068 # out null, 8      side 0\nenter -a 08 -v 0x40e1 # in osr, 1        side 0\nenter -a 09 -v 0x6060 # out null, 32     side 0\n# public shift1:\nenter -a 10 -v 0x80a0 # pull             side 0\nenter -a 11 -v 0x50e1 # in osr, 1        side 1\nenter -a 12 -v 0x7068 # out null, 8      side 1\nenter -a 13 -v 0x50e1 # in osr, 1        side 1\nenter -a 14 -v 0x7068 # out null, 8      side 1\nenter -a 15 -v 0x50e1 # in osr, 1        side 1\nenter -a 16 -v 0x7060 # out null, 32     side 1\nenter -a 17 -v 0x507a # in null, 26      side 1\nenter -a 18 -v 0xb016 # mov pins, ::isr  side 1\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=1 --address=3 --count=16\n\n########\n# Init program (analoguous to hub75_data_rgb888_program_init()\n# in hub75.pio).\n########\n\n# Set consecutive pindirs (rgb_base_pin=7, count=6).\ngpio --pio=0 --gpio=0 --enable # \"R0\": set direction out\ngpio --pio=0 --gpio=1 --enable # \"G0\": set direction out\ngpio --pio=0 --gpio=2 --enable # \"B0\": set direction out\ngpio --pio=0 --gpio=3 --enable # \"R1\": set direction out\ngpio --pio=0 --gpio=4 --enable # \"G1\": set direction out\ngpio --pio=0 --gpio=5 --enable # \"B1\": set direction out\n\n# Set consecutive pindirs (clock_pin=11, count=1).\ngpio --pio=0 --gpio=11 --enable # set direction out\n\n# GPIO init RGB pins.\ngpio --pio=0 --gpio=0 --init # R0\ngpio --pio=0 --gpio=1 --init # G0\ngpio --pio=0 --gpio=2 --init # B0\ngpio --pio=0 --gpio=3 --init # R1\ngpio --pio=0 --gpio=4 --init # G1\ngpio --pio=0 --gpio=5 --init # B1\n\n# GPIO init clock pins.\ngpio --pio=0 --gpio=11 --init\n\n# SM Config Set Out Pins(base=rgb_base_pin=0, count=6).\npinctrl --pio=0 --sm=1 --out-base=0 --out-count=6\n\n# SM Config Set Side-Set Pins(base=clock_pin).\nside-set --pio=0 --sm=1 --base=11\n\n# SM Config Set Out Shift(right, autopull, threshold 24)\nfifo --pio=0 --sm=1 --tx --shift-right\nfifo --pio=0 --sm=1 --tx --auto=true\nfifo --pio=0 --sm=1 --tx --threshold=24\n\n# SM Config Set In Shift(left, no autopush, threshold 32)\nfifo --pio=0 --sm=1 --rx --shift-left\nfifo --pio=0 --sm=1 --rx --auto=false\nfifo --pio=0 --sm=1 --rx --threshold=32\n\n# We only need TX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=1 --join --tx\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=1 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=1 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=1 --clear-tx-stall\nfifo --pio=0 --sm=1 --clear-tx-over\nfifo --pio=0 --sm=1 --clear-rx-under\nfifo --pio=0 --sm=1 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=1 --restart\n\n# Restart clock.\nclock --pio=0 --sm=1 --restart\n\n# Set instruction pointer (PC) to address 3.\nregisters --pio=0 --sm=1 --address=0x03\n\n########\n# End of SM initialization.\n########\n\nexecute --pio=0 --sm=1 --force=0x80a0\ntrace --cycles=1\n\n# In file https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/hub75/hub75.c, the PULL instructions\n# of the second PIO programs are periodically overwritten\n# by different op-codes, while the PIO program is running.\n# For demonstration purposes, do this here only once and\n# *before* enabling the state machines, such that they do\n# not start running before the program is patched, since\n# we currently do all control from this script.  We use n=3\n# in this example.\n\nenter -a 03 -v 0x6063 # out null, 3      side 0\nenter -a 10 -v 0x6063 # out null, 3      side 0\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=1 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Upper byte is unused.  Lower 3 bytes\n# contain RGB pixels.\nfifo --pio=0 --sm=1 --enqueue --tx --value 0x005a0ff0\nfifo --pio=0 --sm=1 --enqueue --tx --value 0x00f05aa5\nfifo --pio=0 --sm=1 --enqueue --tx --value 0x00a0ff05\nfifo --pio=0 --sm=1 --enqueue --tx --value 0x0005aa5f\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/i2c.mon",
    "content": "# Script: I²C\n#\n# Monitor script for I²C example.\n# To be executed on PIO 0, SM 0.\n# pin_sda is fed to GPIO 0.\n# pin_scl is fed to GPIO 1.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/i2c/i2c.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=17 --target=12\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=true\n\n# The code.\n#                do_nack:\nenter -a 00 -v 0x008c #jmp y-- entry_point        # Continue if NAK was expected\nenter -a 01 -v 0xc030 #irq wait 0 rel             # Otherwise stop, ask for help\n#                do_byte:\nenter -a 02 -v 0xe027 #set x, 7                   # Loop 8 times\n#                bitloop:\nenter -a 03 -v 0x6781 #out pindirs, 1         [7] # Serialise write data\nenter -a 04 -v 0xba42 #nop             side 1 [2] # SCL rising edge\nenter -a 05 -v 0x24a1 #wait 1 pin, 1          [4] # Allow clock to be stretched\nenter -a 06 -v 0x4701 #in pins, 1             [7] # Sample read data in middle\nenter -a 07 -v 0x1743 #jmp x-- bitloop side 0 [7] ; SCL falling edge\n#                     ; Handle ACK pulse\nenter -a 08 -v 0x6781 #out pindirs, 1         [7] # On reads, we provide the ACK\nenter -a 09 -v 0xbf42 #nop             side 1 [7] # SCL rising edge\nenter -a 10 -v 0x27a1 #wait 1 pin, 1          [7] # Allow clock to be stretched\nenter -a 11 -v 0x12c0 #jmp pin do_nack side 0 [2] # Test SDA for ACK/NAK\n#                public entry_point:\n#                .wrap_target\nenter -a 12 -v 0x6026 #out x, 6                   # Unpack Instr count\nenter -a 13 -v 0x6041 #out y, 1                   # Unpack the NAK ignore bit\nenter -a 14 -v 0x0022 #jmp !x do_byte             # Instr == 0, data record.\nenter -a 15 -v 0x6060 #out null, 32               # Instr > 0, OSR rem. invalid\n#                do_exec:\nenter -a 16 -v 0x60f0 #out exec, 16               # Exec one instr / FIFO word\nenter -a 17 -v 0x0050 #jmp x-- do_exec            # Repeat n + 1 times\n#                .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=18\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# I/O Mapping.  Assume pin_sda=0 and pin_scl=1.\n\n# SM Config Set Out Pins(base=pin_sda, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set Set Pins(base=pin_sda, count=1).\npinctrl --pio=0 --sm=0 --set-base=0 --set-count=1\n\n# SM Config Set In Pins(base=pin_sda).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# SM Config Set Side-Set Pins(pin_scl).\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set JMP PIN (jmp_pin=pin_sda).\npinctrl --pio=0 --sm=0 --jmp-pin=0\n\n# SM Config Set Out Shift(left, autopull, threshold 16)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=16\n\n# SM Config Set In Shift(left, autopush, threshold 8)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# TODO: Try to avoid glitch:\n#   gpio_pull_up(pin_scl)\n#   gpio_pull_up(pin_sda)\n\n# Set pins for pin_sda, pin_scl\ngpio --pio=0 --gpio=0 --set    # pin_sda: high\ngpio --pio=0 --gpio=1 --set    # pin_scl: high\n\n# Set pindirs for pin_sda, pin_scl\ngpio --pio=0 --gpio=0 --enable # pin_sda: output\ngpio --pio=0 --gpio=1 --enable # pin_scl: output\n\n# GPIO init pin_sda.\ngpio --pio=0 --gpio=0 --init\n\n# Set GPIO Override Invert for pin_sda.\ngpio --gpio=0 --override-oe --invert\n\n# GPIO init pin_scl.\ngpio --pio=0 --gpio=1 --init\n\n# Set GPIO Override Invert for pin_scl.\ngpio --gpio=1 --override-oe --invert\n\n# Set pins for pin_sda, pin_scl\ngpio --pio=0 --gpio=0 --clear    # pin_sda: low\ngpio --pio=0 --gpio=1 --clear    # pin_scl: low\n\ninterrupt --pio=0 --sm=0 --disable --irq0\ninterrupt --pio=0 --sm=0 --disable --irq1\ninterrupt --pio=0 --sm=0 --value=false\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0c.\nregisters --address=0x0c\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Only the lowest 8 bits are used (8\n# data bits, 1 stop bit).\n\n# Instructions to put into FIFO for executing START/STOP/REPSTART:\n#\n# f780 set pindirs, 0 side 0 [7] ; SCL = 0, SDA = 0\n# f781 set pindirs, 1 side 0 [7] ; SCL = 0, SDA = 1\n# ff80 set pindirs, 0 side 1 [7] ; SCL = 1, SDA = 0\n# ff81 set pindirs, 1 side 1 [7] ; SCL = 1, SDA = 1\n\n# Instruction format to put into FIFO:\n#\n# | 15:10 | 9     | 8:1  | 0   |\n# | Instr | Final | Data | NAK |\n#\n# Instr = 0 for data payload.  For more details on this format, see:\n# https://github.com/raspberrypi/pico-examples/tree/master/pio/i2c/i2c.pio.\n\n# 0x0400: Instr = 1 => announce n + 1 = 2 instructions\n# 0x0000: Instructions always align with FIFO word => add padding\nfifo --enqueue --tx --value 0x04000000\n\n# Now, the n + 1 = 2 instructions, one instruction per FIFO word\n# 0xf780: Instruction \"SCL = 0, SDA = 0\"\n# 0xa042: Instruction \"nop\"\nfifo --enqueue --tx --value 0xf7800000\nfifo --enqueue --tx --value 0xa0420000\n\n# 0x014a: data byte 0xa5\n# 0x0201: data byte 0x00, marked as final byte, but NAK => stop\nfifo --enqueue --tx --value 0x014a0201\n\n# Do *not* pull SCL input to 0 (no clock stretching).\n# Otherwise, the program would get stuck at the\n# \"wait 1 pin, 1\" instruction.\ngpio --gpio=1 --set\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/logic-analyser.mon",
    "content": "# Script: Logic Analyser\n#\n# Monitor script for Logic Analyser example.\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/logic_analyser/logic_analyser.c\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x4007 # in pins, pin_count ; in this example, pin_count=7\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=1\n\n########\n# Init program (analoguous to logic_analyser_init() in logic_analyser.c).\n########\n\n# SM Config Set In Pins(base=pin_base).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=0 --target=0\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# SM Config Set In Shift(right, autopush, threshold)\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=28 # 4 samples á 7 bits\n\n# We only need RX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=0 --join --rx\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n########\n\n# Put some values onto GPIO pins to be sampled.\n# Bit pattern: 1010101 for GPIOs 0…6.\ngpio --gpio=0 --set\ngpio --gpio=1 --clear\ngpio --gpio=2 --set\ngpio --gpio=3 --clear\ngpio --gpio=4 --set\ngpio --gpio=5 --clear\ngpio --gpio=6 --set\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/manchester-rx.mon",
    "content": "# Script: Manchester Serial RX\n# Group: Manchester Serial TX and RX\n#\n# Monitor script for Manchester Serial RX example (see RP2040\n# datasheet, Sect. 3.6.5. \"Manchester Serial TX and RX\").  To be\n# executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=5 --target=3\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n\n# start_of_0:\nenter -a 0 -v 0x2020 # wait 0 pin 0\nenter -a 1 -v 0x4841 # in y, 1 [8]\nenter -a 2 -v 0x00c0 # jmp pin start_of_0\n\n# .wrap_target\n# start_of_1:\nenter -a 3 -v 0x20a0 # wait 1 pin 0\nenter -a 4 -v 0x4821 # in x, 1 [8]\nenter -a 5 -v 0x00c0 # jmp pin start_of_0\n# .wrap\n\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=6\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --disable # set direction in\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Configure side set base (for WAIT).\nside-set --pio=0 --sm=0 --base=0\n\n# SM Config Set JMP PIN (for JMP).\npinctrl --pio=0 --sm=0 --jmp-pin=0\n\n# Configure in shift.\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=true    # auto push\nfifo --pio=0 --sm=0 --rx --threshold=32\n\n# Join FIFO RX\nfifo --join --rx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Set X and Y to 1 and 0, to conveniently emit these to ISR/FIFO\nregisters --x=1\nregisters --y=0\n\n# Assume idle low, first bit is 0.  Wait until 0 detected.\nexecute --pio=0 --sm=0 --force=0x22a0\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# While running the PIO program by triggering cycles, insert now and\n# then either:\n\ngpio --gpio=0 --set\n\n# or:\n\ngpio --gpio=0 --clear\n\n# after an appropriate amount of cycles to change GPIO 0 input, as\n# appropriate for testing / emulating external data input to the\n# Manchester Serial RX PIO program.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/manchester-tx.mon",
    "content": "# Script: Manchester Serial TX\n# Group: Manchester Serial TX and RX\n#\n# Monitor script for Manchester Serial TX example (see RP2040\n# datasheet, Sect. 3.6.5. \"Manchester Serial TX and RX\").  To be\n# executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=5 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=true\n\n# The code.\n# .wrap_target\n# do_1:\nenter -a 0 -v 0xb542 # nop         side 0 [5]\nenter -a 1 -v 0x1b04 # jmp get_bit side 1 [3]\n# do_0:\nenter -a 2 -v 0xbd42 # nop         side 1 [5]\nenter -a 3 -v 0xb342 # nop         side 0 [3]\n# public start:\n# get_bit:\nenter -a 4 -v 0x6021 # out x, 1\nenter -a 5 -v 0x0022 # jmp !x do_0\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=6\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set GPIO output value to 0.\ngpio --pio=0 --gpio=0 --clear\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Configure side set base.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=true    # auto pull\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# Join FIFO TX\nfifo --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0x00000000\nfifo --enqueue --tx --value 0x0ff0a55a\nfifo --enqueue --tx --value 0x12345678\n\n# Done.\nquit\n\n# Hint: In the Diagram Creator, you can select 12 as \"Cycles\" value,\n# and start the \"emulate\" button with the \"OUT x, 1\" instruction\n# being the next instruction to be executed.  Then, you can watch bit\n# by bit producing the corresponding output level at GPIO0, with the\n# bits shifted one by one out of OSR.\n"
  },
  {
    "path": "java/examples/manual-pull.mon",
    "content": "# Script: Manual Pull\n# Group: Autopush and Autopull\n#\n# Monitor script for manual pull example (see RP2040\n# datasheet, Sect. 3.5.4. \"Autopush and Autopull\").\n# To be executed on PIO 0, SM 0.\n# Clock (side-set) output is mapped to GPIO 0.\n# Data (OUT) ouput is mapped to GPIO 1.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=4 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=true   # .side_set 1 opt\n\n# The code.\n#                  .wrap_target\nenter -a 0 -v 0xe022 # set x, 2                   ; X = bit count - 2\nenter -a 1 -v 0x99a0 # pull            side 1 [1] ; Stall here if no TX data\n#                  bitloop:\nenter -a 2 -v 0x7101 # out pins, 1     side 0 [1] ; Shift out data bit and toggle clock low\nenter -a 3 -v 0x1942 # jmp x-- bitloop side 1 [1] ; Loop runs 3 times\nenter -a 4 -v 0x7001 # out pins, 1     side 0     ; Shift out last bit before reloading X\n#                  .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=5\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect GPIO pins 0 and 1 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-left    # OSR shift left\nfifo --pio=0 --sm=0 --tx --auto=false    # no auto pull\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Configure clock (side-set) for GPIO 0.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure data (OUT) to output a single bit to GPIO 1.\npinctrl --pio=0 --sm=0 --out-count=1 --out-base=1\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010...\nfifo --enqueue --tx --value 0xffffffff # bit pattern 11111111...\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/pull-example1.mon",
    "content": "# Script: Pull Example 1\n# Group: Autopush and Autopull\n#\n# Monitor script for pull_example1 (see RP2040\n# datasheet, Sect. 3.2.3.1. \"Output Shift\n# Register (OSR)\").\n# To be executed on PIO 0, SM 0.\n#\n# Each FIFO entries holds 4 bytes of output data.\n# Each 2nd cycle, the next byte is output on pins\n# GPIO0…GPIO7.\n\n# Make a full reset of the emulator.\nreset\n\n# The code.\n# loop:\nenter -a 0 -v 0x6008 # out pins, 8\n# public entry_point:\nenter -a 1 -v 0x80a0 # pull\nenter -a 2 -v 0x6108 # out pins, 8 [1]\nenter -a 3 -v 0x6108 # out pins, 8 [1]\nenter -a 4 -v 0x6008 # out pins, 8\nenter -a 5 -v 0x0000 # jmp loop\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=6\n\n################\n# Init program.\n################\n\n# Connect GPIO pins 0…7 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=2 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=3 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=4 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=5 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=6 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=7 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\ngpio --pio=0 --gpio=2 --enable # set direction out\ngpio --pio=0 --gpio=3 --enable # set direction out\ngpio --pio=0 --gpio=4 --enable # set direction out\ngpio --pio=0 --gpio=5 --enable # set direction out\ngpio --pio=0 --gpio=6 --enable # set direction out\ngpio --pio=0 --gpio=7 --enable # set direction out\n\n# SM Config Set Out Pins(base=0, count=8).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=8\n\n# SM Config Set Out Shift(left, no autopull)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=false\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 1.\nregisters --address=1\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\n\n# bit patterns 10101010, 11111111, 01010101, 00000000\nfifo --enqueue --tx --value 0xaaff5500\n\n# bit patterns 00010001, 11101110, 00110011, 11001100\nfifo --enqueue --tx --value 0x11ee33cc\n\n# bit patterns 01110111, 10001000, 11111111, 00000000\nfifo --enqueue --tx --value 0x7788ff00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/pull-example2.mon",
    "content": "# Script: Pull Example 2\n# Group: Autopush and Autopull\n#\n# Monitor script for pull_example2 (see RP2040\n# datasheet, Sect. 3.2.3.1. \"Output Shift\n# Register (OSR)\").\n# To be executed on PIO 0, SM 0.\n#\n# Each FIFO entries holds 4 bytes of output data.\n# Each 2nd cycle, the next byte is output on pins\n# GPIO0…GPIO7.\n\n# Make a full reset of the emulator.\nreset\n\n# The code.\n# loop:\nenter -a 0 -v 0x6008 # out pins, 8\n# public entry_point:\nenter -a 1 -v 0x0000 # jmp loop\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n################\n# Init program.\n################\n\n# Connect GPIO pins 0…7 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=2 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=3 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=4 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=5 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=6 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=7 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\ngpio --pio=0 --gpio=2 --enable # set direction out\ngpio --pio=0 --gpio=3 --enable # set direction out\ngpio --pio=0 --gpio=4 --enable # set direction out\ngpio --pio=0 --gpio=5 --enable # set direction out\ngpio --pio=0 --gpio=6 --enable # set direction out\ngpio --pio=0 --gpio=7 --enable # set direction out\n\n# SM Config Set Out Pins(base=0, count=8).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=8\n\n# SM Config Set Out Shift(left, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 1.\nregisters --address=1\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\n\n# bit patterns 10101010, 11111111, 01010101, 00000000\nfifo --enqueue --tx --value 0xaaff5500\n\n# bit patterns 00010001, 11101110, 00110011, 11001100\nfifo --enqueue --tx --value 0x11ee33cc\n\n# bit patterns 01110111, 10001000, 11111111, 00000000\nfifo --enqueue --tx --value 0x7788ff00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/pull-example3.mon",
    "content": "# Script: Pull Example 3\n# Group: Autopush and Autopull\n#\n# Monitor script for pull_example3 (see RP2040\n# datasheet, Sect. 3.2.3.1. \"Output Shift\n# Register (OSR)\").\n# To be executed on PIO 0, SM 0.\n#\n# Each FIFO entries holds 4 bytes of output data.\n# Each 2nd cycle, the next byte is output on pins\n# GPIO0…GPIO7.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=0 --target=0\n\n# The code.\n# public entry_point:\n# .wrap_target\n# loop:\nenter -a 0 -v 0x6108 # out pins, 8 [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=1\n\n################\n# Init program.\n################\n\n# Connect GPIO pins 0…7 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=2 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=3 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=4 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=5 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=6 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=7 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\ngpio --pio=0 --gpio=2 --enable # set direction out\ngpio --pio=0 --gpio=3 --enable # set direction out\ngpio --pio=0 --gpio=4 --enable # set direction out\ngpio --pio=0 --gpio=5 --enable # set direction out\ngpio --pio=0 --gpio=6 --enable # set direction out\ngpio --pio=0 --gpio=7 --enable # set direction out\n\n# SM Config Set Out Pins(base=0, count=8).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=8\n\n# SM Config Set Out Shift(left, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\n\n# bit patterns 10101010, 11111111, 01010101, 00000000\nfifo --enqueue --tx --value 0xaaff5500\n\n# bit patterns 00010001, 11101110, 00110011, 11001100\nfifo --enqueue --tx --value 0x11ee33cc\n\n# bit patterns 01110111, 10001000, 11111111, 00000000\nfifo --enqueue --tx --value 0x7788ff00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/pull-example4.mon",
    "content": "# Script: Pull Example 4\n# Group: Autopush and Autopull\n#\n# Monitor script like pull_example3 (see RP2040\n# datasheet, Sect. 3.2.3.1. \"Output Shift\n# Register (OSR)\"), but with an output of 1 byte\n# every system cycle.\n# To be executed on PIO 0, SM 0.\n#\n# Each FIFO entries holds 4 bytes of output data.\n# Each 2nd cycle, the next byte is output on pins\n# GPIO0…GPIO7.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=0 --target=0\n\n# The code.\n# public entry_point:\n# .wrap_target\n# loop:\nenter -a 0 -v 0x6008 # out pins, 8\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=1\n\n################\n# Init program.\n################\n\n# Connect GPIO pins 0…7 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=2 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=3 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=4 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=5 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=6 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=7 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\ngpio --pio=0 --gpio=2 --enable # set direction out\ngpio --pio=0 --gpio=3 --enable # set direction out\ngpio --pio=0 --gpio=4 --enable # set direction out\ngpio --pio=0 --gpio=5 --enable # set direction out\ngpio --pio=0 --gpio=6 --enable # set direction out\ngpio --pio=0 --gpio=7 --enable # set direction out\n\n# SM Config Set Out Pins(base=0, count=8).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=8\n\n# SM Config Set Out Shift(left, autopull, threshold 32)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\n\n# bit patterns 10101010, 11111111, 01010101, 00000000\nfifo --enqueue --tx --value 0xaaff5500\n\n# bit patterns 00010001, 11101110, 00110011, 11001100\nfifo --enqueue --tx --value 0x11ee33cc\n\n# bit patterns 01110111, 10001000, 11111111, 00000000\nfifo --enqueue --tx --value 0x7788ff00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/pwm.mon",
    "content": "# Script: PWM\n#\n# Monitor script for PWM example (see RP2040 datasheet,\n# Sect. 3.6.8. \"PWM\").  To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=6 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=true\n\n# The code.\nenter -a 0 -v 0x9080 # pull noblock side 0\nenter -a 1 -v 0xa027 # mov x, osr\nenter -a 2 -v 0xa046 # mov y, isr\n#         countloop:\nenter -a 3 -v 0x00a5 # jmp x!=y noset\nenter -a 4 -v 0x1806 # jmp skip side 1\n#         noset:\nenter -a 5 -v 0xa042 # nop\n#         skip:\nenter -a 6 -v 0x0083 # jmp y-- countloop\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=7\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Configure side set base.\nside-set --pio=0 --sm=0 --base=0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Set PWM period.  The RP foundation's pwm.c sample program puts the\n# value into TX FIFO (\"pio_sm_put_blocking\"), then moves it to OSR by\n# executing a \"PULL\" command, and finally moves it from OSR to ISR by\n# executing an \"OUT\" command with target ISR.  Thanks to our monitor\n# application, we can achieve the same effect with just a single\n# command, by directly assigning the value to the ISR register.\nregisters --isr=5\n\n# Put example values into FIFO.  Maximum valid value is the PWM period.\nfifo --enqueue --tx --value 0x3\nfifo --enqueue --tx --value 0x5\nfifo --enqueue --tx --value 0x2\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/somewhat-manual-pull.mon",
    "content": "# Script: Somewhat Manual Pull\n# Group: Autopush and Autopull\n#\n# Monitor script for partial auto pull example (see RP2040\n# datasheet, Sect. 3.5.4 \"Autopush and Autopull\").\n# To be executed on PIO 0, SM 0.\n# Clock (side-set) output is mapped to GPIO 0.\n# Data (OUT) ouput is mapped to GPIO 1.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=1 --opt=false   # .side_set 1\n\n# The code.\n#                  .wrap_target\nenter -a 0 -v 0x6101 # out pins, 1     side 0 [1] ; Shift out data bit and toggle clock low\nenter -a 1 -v 0x91e0 # pull ifempty    side 1 [1] ; Stall here if no TX data\n#                  .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect GPIO pins 0 and 1 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO to connect to PIO0\n\n# Set direction out for these two pins.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-left    # OSR shift left\nfifo --pio=0 --sm=0 --tx --auto=false    # auto pull off\nfifo --pio=0 --sm=0 --tx --threshold=4  # auto pull threshold\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Configure clock (side-set) for GPIO 0.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure data (OUT) to output a single bit to GPIO 1.\npinctrl --pio=0 --sm=0 --out-count=1 --out-base=1\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0xffff0000 # bit pattern 11111111...00000000\nfifo --enqueue --tx --value 0x0000ffff # bit pattern 00000000...11111111\nfifo --enqueue --tx --value 0xaaaaaaaa # bit pattern 10101010...10101010\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/spi-cpha0-cs.mon",
    "content": "# Script: SPI CPHA0 with Chip Select\n# Group: SPI\n#\n# Monitor script for SPI CPHA0 with Chip Select example.\n# To be executed on PIO 0, SM 0.\n# SCK is side-set pin 0.\n# CSn is side-set pin 1 (n=1).\n# MOSI is OUT pin (host-to-device).\n# MISO is IN pin (device-to-host).\n# MOSI and MISO mapped to same pin, so we get loopback.\n# n_bits = 8 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/spi/spi.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=8 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=2 --opt=false\n\n# The code.\n# .wrap_target\n# bitloop:\nenter -a 00 -v 0x6101 # out pins, 1        side 0x0 [1]\nenter -a 01 -v 0x4801 # in pins, 1         side 0x1\nenter -a 02 -v 0x0840 # jmp x-- bitloop    side 0x1\n\nenter -a 03 -v 0x6001 # out pins, 1        side 0x0\nenter -a 04 -v 0xa022 # mov x, y           side 0x0\nenter -a 05 -v 0x4801 # in pins, 1         side 0x1\nenter -a 06 -v 0x08e0 # jmp !osre bitloop  side 0x1\n\nenter -a 07 -v 0xa142 # nop                side 0x0 [1]\n# public entry_point:\nenter -a 08 -v 0x91e0 # pull ifempty       side 0x2 [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=9\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# SM Config Set Out Pins(base=pin_mosi, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set In Pins(base=pin_miso).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# SM Config Set Side-Set Pins(pin_sck): pin_sck=GPIO1, pin_csn=GPIO2.\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Shift(left, autopull, threshold n_bits)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=8\n\n# SM Config Set In Shift(left, autopush, threshold n_bits)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Set Pins: pin_csn=1, pin_sck=0, pin_mosi=0.\ngpio --pio=0 --gpio=0 --clear # pin_mosi=0\ngpio --pio=0 --gpio=1 --clear # pin_sck=0\ngpio --pio=0 --gpio=2 --set   # pin_csn=1\n\n# Set PinDirs: pin_csn=out, pin_sck=out, pin_mosi=out, pin_miso=in\ngpio --pio=0 --gpio=0 --disable # pin_miso=in\ngpio --pio=0 --gpio=0 --enable  # pin_mosi=out\ngpio --pio=0 --gpio=1 --enable  # pin_sck=out\ngpio --pio=0 --gpio=2 --enable  # pin_csn=out\n\n# GPIO Init\ngpio --pio=0 --gpio=0 --init    # pin_miso\ngpio --pio=0 --gpio=0 --init    # pin_mosi\ngpio --pio=0 --gpio=1 --init    # pin_sck\ngpio --pio=0 --gpio=2 --init    # pin_csn\n\n# Optionally (for CPOL=1), set pin_sck Override Invert.\ngpio --gpio=1 --override-out --invert\n\n# TODO / NOT IMPLEMENTED:\n# SPI is synchronous, so bypass input synchroniser to reduce input delay.\n# hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to program entry point.\nregisters --address=0x08\n\n########\n# End of SM initialization.\n########\n\nregisters --x=6\nregisters --y=6\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we chose n_bits = 8 in this\n# example, only the most upper bits per FIFO word are significant.\n\nfifo --enqueue --tx --value 0xff000000\nfifo --enqueue --tx --value 0x55000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/spi-cpha0.mon",
    "content": "# Script: SPI CPHA0\n# Group: SPI\n#\n# Monitor script for SPI CPHA0 example.\n# To be executed on PIO 0, SM 0.\n# SCK is side-set pin.\n# MOSI is OUT pin.\n# MISO is IN pin.\n# MOSI and MISO mapped to same pin, so we get loopback.\n# n_bits = 8 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/spi/spi.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x6101 # out pins, 1 side 0 [1]\nenter -a 01 -v 0x5101 # in pins, 1  side 1 [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# SM Config Set Out Pins(base=pin_mosi, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set In Pins(base=pin_miso).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# SM Config Set Side-Set Pins(pin_sck).\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Shift(left, autopull, threshold n_bits)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=8\n\n# SM Config Set In Shift(left, autopush, threshold n_bits)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# MOSI, SCK output are low, MISO is input\ngpio --pio=0 --gpio=0 --disable # MISO input\ngpio --pio=0 --gpio=0 --init    # MISO init\ngpio --pio=0 --gpio=0 --enable  # MOSI output\ngpio --pio=0 --gpio=0 --clear   # MOSI low\ngpio --pio=0 --gpio=0 --init    # MISO / MOSI init\ngpio --pio=0 --gpio=1 --enable  # SCK output\ngpio --pio=0 --gpio=1 --clear   # SCK low\ngpio --pio=0 --gpio=1 --init    # SCK init\n\n# Optionally (for CPOL=1), set pin_sck Override Invert.\ngpio --gpio=1 --override-out --invert\n\n# TODO / NOT IMPLEMENTED:\n# SPI is synchronous, so bypass input synchroniser to reduce input delay.\n# hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we chose n_bits = 8 in this\n# example, only the most upper bits per FIFO word are significant.\n\nfifo --enqueue --tx --value 0xff000000\nfifo --enqueue --tx --value 0x55000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/spi-cpha1-cs.mon",
    "content": "# Script: SPI CPHA1 with Chip Select\n# Group: SPI\n#\n# Monitor script for SPI CPHA1 with Chip Select example.\n# To be executed on PIO 0, SM 0.\n# SCK is side-set pin 0.\n# CSn is side-set pin 1 (n=1).\n# MOSI is OUT pin (host-to-device).\n# MISO is IN pin (device-to-host).\n# MOSI and MISO mapped to same pin, so we get loopback.\n# n_bits = 8 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/spi/spi.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=8 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=2 --opt=false\n\n# The code.\n# .wrap_target\n# bitloop:\nenter -a 00 -v 0x6901 # out pins, 1        side 0x1 [1]\nenter -a 01 -v 0x4001 # in pins, 1         side 0x0\nenter -a 02 -v 0x0040 # jmp x-- bitloop    side 0x0\n\nenter -a 03 -v 0x6801 # out pins, 1        side 0x1\nenter -a 04 -v 0xa822 # mov x, y           side 0x1\nenter -a 05 -v 0x4001 # in pins, 1         side 0x0\nenter -a 06 -v 0x00e0 # jmp !osre bitloop  side 0x0\n\n# public entry_point:\nenter -a 07 -v 0x91e0 # pull ifempty       side 0x2 [1]\nenter -a 08 -v 0xa142 # nop                side 0x0 [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=9\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# SM Config Set Out Pins(base=pin_mosi, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set In Pins(base=pin_miso).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# SM Config Set Side-Set Pins(pin_sck): pin_sck=GPIO1, pin_csn=GPIO2.\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Shift(left, autopull, threshold n_bits)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=8\n\n# SM Config Set In Shift(left, autopush, threshold n_bits)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# Set Pins: pin_csn=1, pin_sck=0, pin_mosi=0.\ngpio --pio=0 --gpio=0 --clear # pin_mosi=0\ngpio --pio=0 --gpio=1 --clear # pin_sck=0\ngpio --pio=0 --gpio=2 --set   # pin_csn=1\n\n# Set PinDirs: pin_csn=out, pin_sck=out, pin_mosi=out, pin_miso=in\ngpio --pio=0 --gpio=0 --disable # pin_miso=in\ngpio --pio=0 --gpio=0 --enable  # pin_mosi=out\ngpio --pio=0 --gpio=1 --enable  # pin_sck=out\ngpio --pio=0 --gpio=2 --enable  # pin_csn=out\n\n# GPIO Init\ngpio --pio=0 --gpio=0 --init    # pin_miso\ngpio --pio=0 --gpio=0 --init    # pin_mosi\ngpio --pio=0 --gpio=1 --init    # pin_sck\ngpio --pio=0 --gpio=2 --init    # pin_csn\n\n# Optionally (for CPOL=1), set pin_sck Override Invert.\ngpio --gpio=1 --override-out --invert\n\n# TODO / NOT IMPLEMENTED:\n# SPI is synchronous, so bypass input synchroniser to reduce input delay.\n# hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to program entry point.\nregisters --address=0x07\n\n########\n# End of SM initialization.\n########\n\nregisters --x=6\nregisters --y=6\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we chose n_bits = 8 in this\n# example, only the most upper bits per FIFO word are significant.\n\nfifo --enqueue --tx --value 0xff000000\nfifo --enqueue --tx --value 0x55000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/spi-cpha1.mon",
    "content": "# Script: SPI CPHA1\n# Group: SPI\n#\n# Monitor script for SPI CPHA1 example.\n# To be executed on PIO 0, SM 0.\n# SCK is side-set pin.\n# MOSI is OUT pin.\n# MISO is IN pin.\n# MOSI and MISO mapped to same pin, so we get loopback.\n# n_bits = 8 in this example.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/spi/spi.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=2 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x6021 # out x, 1    side 0\nenter -a 01 -v 0xb101 # mov pins, x side 1 [1]\nenter -a 02 -v 0x4001 # in pins, 1  side 0\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# SM Config Set Out Pins(base=pin_mosi, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# SM Config Set In Pins(base=pin_miso).\npinctrl --pio=0 --sm=0 --in-base=0\n\n# SM Config Set Side-Set Pins(pin_sck).\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Shift(left, autopull, threshold n_bits)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=8\n\n# SM Config Set In Shift(left, autopush, threshold n_bits)\nfifo --pio=0 --sm=0 --rx --shift-left\nfifo --pio=0 --sm=0 --rx --auto=true\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# MOSI, SCK output are low, MISO is input\ngpio --pio=0 --gpio=0 --disable # MISO input\ngpio --pio=0 --gpio=0 --init    # MISO init\ngpio --pio=0 --gpio=0 --enable  # MOSI output\ngpio --pio=0 --gpio=0 --clear   # MOSI low\ngpio --pio=0 --gpio=0 --init    # MISO / MOSI init\ngpio --pio=0 --gpio=1 --enable  # SCK output\ngpio --pio=0 --gpio=1 --clear   # SCK low\ngpio --pio=0 --gpio=1 --init    # SCK init\n\n# Optionally (for CPOL=1), set pin_sck Override Invert.\ngpio --gpio=1 --override-out --invert\n\n# TODO / NOT IMPLEMENTED:\n# SPI is synchronous, so bypass input synchroniser to reduce input delay.\n# hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso);\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we chose n_bits = 8 in this\n# example, only the most upper bits per FIFO word are significant.\n\nfifo --enqueue --tx --value 0xff000000\nfifo --enqueue --tx --value 0x55000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/spi-tx-fast.mon",
    "content": "# Script: SPI TX Fast\n# Group: SPI\n#\n# Monitor script for SPI TX Fast example,\n# as described in RP2040 datasheet,\n# Sect. 3.5.1. \"Side-set\".\n#\n# To be executed on PIO 0, SM 0.\n# Data output is fed to GPIO 0.\n# Clock output is fed to GPIO 1.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# loop:\nenter -a 0 -v 0x6001 # out pins, 1  side 0\nenter -a 1 -v 0x1000 # jmp loop     side 1\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n################\n# Init program.\n################\n\n# Outputs GPIO0=data, GPIO1=clock.\n# Initially drive output-high on clock output.\ngpio --pio=0 --gpio=0 --enable # output\ngpio --pio=0 --gpio=0 --init\ngpio --pio=0 --gpio=1 --enable # output\ngpio --pio=0 --gpio=1 --set    # high\ngpio --pio=0 --gpio=1 --init\n\n# OUT shifts to right, autopull, threshold 32 (use all bits).\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# data on GPIO0\npinctrl --pio=0 --sm=0 --out-count=1 --out-base=0\n\n# clock on GPIO1\nside-set --pio=0 --sm=0 --base=1\n\n# We only need TX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Bits are serialized in LSB to MSB order.\nfifo --enqueue --tx --value 0x00ff55aa\nfifo --enqueue --tx --value 0xcc33ee11\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/squarewave-fast.mon",
    "content": "# Script: Squarewave Fast\n# Group: Squarewave\n#\n# Monitor script for Squarewave example (\"fast\" version).\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/squarewave/squarewave.c\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=2 --target=1\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\nenter -a 0 -v 0xe081 # set pindirs, 1   ; Set pin to output\n# .wrap_target\nenter -a 1 -v 0xe001 # set pins, 1      ; Drive pin high\nenter -a 2 -v 0xe000 # set pins, 0      ; Drive pin low\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n# Configure Clock Divider, here, for demonstration purposes,\n# with average divisor 2.5.  See RP2040 datasheet, Sect. 3.5.5.,\n# Fig. 47, for the effect of a clock divider of value 2.5 onto\n# the CLK_ENABLE signal.\n#\n# Note that the fast version in combination with divisor 2.5\n# will result in an assymmetrical output signal (2+3 cycles), since\n# there are no fractional clock cycles, but the divisor specifies\n# the _average_ time division.  To get a symmetrical signal, use\n# instead a non-fractional divisor such as 1, 2 or 3.\nclock --pio=0 --sm=0 --divider=2.5\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/squarewave-wrap.mon",
    "content": "# Script: Squarewave Wrap\n# Group: Squarewave\n#\n# Monitor script for Squarewave example (\"wrap\" version).\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/squarewave/squarewave.c\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=2 --target=1\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\nenter -a 0 -v 0xe081 # set pindirs, 1   ; Set pin to output\n# .wrap_target\nenter -a 1 -v 0xe101 # set pins, 1 [1]  ; Drive pin high & delay for one cycle\nenter -a 2 -v 0xe100 # set pins, 0 [1]  ; Drive pin low & delay for one cycle\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=3\n\n# Configure Clock Divider, here, for demonstration purposes,\n# with average divisor 2.5.  See RP2040 datasheet, Sect. 3.5.5.,\n# Fig. 47, for the effect of a clock divider of value 2.5 onto\n# the CLK_ENABLE signal.\nclock --pio=0 --sm=0 --divider=2.5\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/squarewave.hex",
    "content": "# .program squarewave\n# ; periodically turns off / on GPIO0\ne081\ne101\ne000\n0001"
  },
  {
    "path": "java/examples/squarewave.mon",
    "content": "# Script: Squarewave\n# Group: Squarewave\n#\n# Monitor script for Squarewave example (base version).\n# To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/squarewave/squarewave.c\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\nenter -a 0 -v 0xe081 # set pindirs, 1   ; Set pin to output\n# again:\nenter -a 1 -v 0xe101 # set pins, 1 [1]  ; Drive pin high & delay for one cycle\nenter -a 2 -v 0xe000 # set pins, 0      ; Drive pin low\nenter -a 3 -v 0x0001 # jmp again        ; Set PC to label `again`\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=4\n\n# Configure Clock Divider, here, for demonstration purposes,\n# with average divisor 2.5.  See RP2040 datasheet, Sect. 3.5.5.,\n# Fig. 47, for the effect of a clock divider of value 2.5 onto\n# the CLK_ENABLE signal.\nclock --pio=0 --sm=0 --divider=2.5\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/st7789-lcd.mon",
    "content": "# Script: ST7789 LCD\n#\n# Monitor script for ST7789 LCD example.\n# To be executed on PIO 0, SM 0.\n# Data on OUT pin, mapped to GPIO0.\n# Clock on side-set pin, mapped to GPIO1.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/st7789_lcd/st7789_lcd.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=1 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 00 -v 0x6001 # out pins, 1  side 0 ; stall here if no data (clock low)\nenter -a 01 -v 0xb042 # nop          side 1\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=2\n\n########\n# Init program (analoguous to st7789_lcd_program_init() in st7789_lcd.pio).\n########\n\n# GPIO init data_pin\ngpio --pio=0 --gpio=0 --init    # data_pin on GPIO0\n\n# GPIO init clk_pin\ngpio --pio=0 --gpio=1 --init    # clk_pin on GPIO1\n\n# Set consecutive pindirs, here just a single one for data_pin.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Set consecutive pindirs, here just a single one for clk_pin.\ngpio --pio=0 --gpio=1 --enable # set direction out\n\n# SM Config Set Side-Set Pins(base=clk_pin).\nside-set --pio=0 --sm=0 --base=1\n\n# SM Config Set Out Pins(base=data_pin, count=1).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=1\n\n# Set FIFO Join TX\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n# SM Config Set Out Shift(left, autopull, threshold 8)\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=8\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0x00\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we chose n_bits = 8 in this\n# example, only the most upper bits per FIFO word are significant.\n\nfifo --enqueue --tx --value 0xa5000000\nfifo --enqueue --tx --value 0x0f000000\nfifo --enqueue --tx --value 0x01000000\nfifo --enqueue --tx --value 0x0e000000\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/uart-rx-mini.mon",
    "content": "# Script: UART RX Mini\n# Group: UART\n#\n# Monitor script for UART RX Mini example (see RP2040 datasheet,\n# Sect. 3.6.4. \"UART RX\").  To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=3 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n\n# .wrap_target\nenter -a 0 -v 0x2020 # wait 0 pin 0\nenter -a 1 -v 0xea27 # set x, 7 [10]\n# bitloop:\nenter -a 2 -v 0x4001 # in pins, 1\nenter -a 3 -v 0x0642 # jmp x-- bitloop [6]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=4\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --disable # set direction in\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# Configure side set base (for WAIT and IN).\nside-set --pio=0 --sm=0 --base=0\n\n# Configure in shift.\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=true    # auto push\nfifo --pio=0 --sm=0 --rx --threshold=8\n\n# Join FIFO RX\nfifo --join --rx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# While running the PIO program by triggering cycles, insert now and\n# then either:\n\ngpio --gpio=0 --set\n\n# or:\n\ngpio --gpio=0 --clear\n\n# after an appropriate amount of cycles to change GPIO 0 input, as\n# appropriate for testing / emulating external data input to the\n# UART RX Mini PIO program.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/uart-rx.mon",
    "content": "# Script: UART RX\n# Group: UART\n#\n# Monitor script for UART RX example (see RP2040 datasheet,\n# Sect. 3.6.4. \"UART RX\").  To be executed on PIO 0, SM 0.\n\n# Make a full reset of the emulator.\nreset\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=8 --target=0\n\n# Configure Side Set\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n\n\n# .wrap_target\n# start:\nenter -a 0 -v 0x2020 # wait 0 pin 0\nenter -a 1 -v 0xea27 # set x, 7    [10]\n# bitloop:\nenter -a 2 -v 0x4001 # in pins, 1\nenter -a 3 -v 0x0642 # jmp x-- bitloop [6]\nenter -a 4 -v 0x00c8 # jmp pin good_stop\n\nenter -a 5 -v 0xc014 # irq 4 rel\nenter -a 6 -v 0x20a0 # wait 1 pin 0\nenter -a 7 -v 0x0000 # jmp start\n\n# good_stop:\nenter -a 8 -v 0x8020 # push\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=8\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --disable # set direction in\n\n# Connect GPIO 0 with PIO 0\ngpio --pio=0 --gpio=0 --init\n\n# TODO: Try to avoid glitch:\n#   gpio_pull_up(0)\n\n# Configure side set base (for WAIT and IN).\nside-set --pio=0 --sm=0 --base=0\n\n# SM Config Set JMP PIN.\npinctrl --pio=0 --sm=0 --jmp-pin=0\n\n# Configure in shift.\nfifo --pio=0 --sm=0 --rx --shift-right\nfifo --pio=0 --sm=0 --rx --auto=false    # no auto push\nfifo --pio=0 --sm=0 --rx --threshold=32\n\n# Join FIFO RX\nfifo --join --rx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# While running the PIO program by triggering cycles, insert now and\n# then either:\n\ngpio --gpio=0 --set\n\n# or:\n\ngpio --gpio=0 --clear\n\n# after an appropriate amount of cycles to change GPIO 0 input, as\n# appropriate for testing / emulating external data input to the\n# UART RX Mini PIO program.\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/uart-tx.mon",
    "content": "# Script: UART TX\n# Group: UART\n#\n# Monitor script for UART TX example.\n# To be executed on PIO 0, SM 0.\n# Output is fed to GPIO 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/uart_tx/uart_tx.pio\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=3 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=true\n\n# The code.\nenter -a 0 -v 0x9fa0 # pull            side 1 [7]\nenter -a 1 -v 0xf727 # set x, 7        side 0 [7]\nenter -a 2 -v 0x6001 # out pins, 1\n#                bitloop:\nenter -a 3 -v 0x0642 # jmp x-- bitloop        [6]\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=4\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Tell PIO to initially drive output-high on GPIO 0.\ngpio --pio=0 --gpio=0 --enable # output\ngpio --pio=0 --gpio=0 --set    # high\n\n# Then map PIO onto that pin with the IO muxes.\ngpio --pio=0 --gpio=0 --init\n\n# OUT shifts to right, no autopull.\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=false\n\n# We are mapping both OUT and side-set to the same pin, because\n# sometimes we need to assert user data onto the pin (with OUT) and\n# sometimes assert constant values (start/stop bit).\npinctrl --pio=0 --sm=0 --out-count=1 --out-base=0\nside-set --pio=0 --sm=0 --base=0\n\n# We only need TX, so get an 8-deep FIFO!\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Only the lowest 8 bits are used (8\n# data bits, 1 stop bit).\nfifo --enqueue --tx --value 0x000000a5\nfifo --enqueue --tx --value 0x000000f0\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/ws2812-led.mon",
    "content": "# Script: WS2812 LED\n# Group: WS2812\n#\n# Monitor script for ws2812_led example,\n# as described in RP2040 datasheet,\n# Sect. 3.2.3.4. \"Scratch Registers\".\n#\n# To be executed on PIO 0, SM 0.\n# Output is fed to GPIO 0.\n\n# Make a full reset of the emulator.\nreset\n\n# This example uses timing T1=7, T2=8, T3=6.\n\n# The code.\n# public entry_point:\nenter -a 0 -v 0x80a0 # pull\nenter -a 1 -v 0xe037 # set x, 23     ; Loop over 24 bits\n# bitloop:\nenter -a 2 -v 0xe001 # set pins, 1   ; Drive pin high\nenter -a 3 -v 0x6541 # out y, 1 [5]  ; Shift 1 bit out, and write it to y\nenter -a 4 -v 0x0066 # jmp !y skip   ; Skip the extra delay if the bit was 0\nenter -a 5 -v 0xa542 # nop [5]\n# skip:\nenter -a 6 -v 0xe500 # set pins, 0 [5]\nenter -a 7 -v 0x0042 # jmp x-- bitloop ; Jump if x nonzero, and decrement x\nenter -a 8 -v 0x0000 # jmp entry_point\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=9\n\n################\n# Init program.\n################\n\n# Connect GPIO 0 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# SM Config Set Set Pins(base=GPIO0, count=1).\npinctrl --pio=0 --sm=0 --set-base=0 --set-count=1\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=false    # no auto pull\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\n# Format of FIFO entry is: RRRRRRRRGGGGGGGGBBBBBBBB00000000.\nfifo --enqueue --tx --value 0xaa118800 # RGB mode: end with byte 0x00\nfifo --enqueue --tx --value 0x55881100 # RGB mode: end with byte 0x00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/ws2812-parallel.mon",
    "content": "# Script: WS2812 Parallel\n# Group: WS2812\n#\n# Monitor script for ws2812_parallel example.\n# To be executed on PIO 0, SM 0.\n# Output is fed to GPIO 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/ws2812/generated/ws2812.pio.h\n#\n# This example uses timing T1=2, T2=5, T3=3.\n# Note that, according to the discussion in\n# https://github.com/raspberrypi/pico-feedback/issues/121,\n# T1=T2=T3=2 may be a better choice.\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=3 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=0 --opt=false\n\n# The code.\n# .wrap_target\nenter -a 0 -v 0x6020 # out x, 32\nenter -a 1 -v 0xa10b # mov pins, !null [1]\nenter -a 2 -v 0xa401 # mov pins, x     [4]\nenter -a 3 -v 0xa103 # mov pins, null  [1]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=4\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect any of the GPIOs with PIO 0.  In this example,\n# we choose GPIO 0-3.\ngpio --pio=0 --gpio=0 --init   # tell GPIO0 to connect to PIO0\ngpio --pio=0 --gpio=1 --init   # tell GPIO1 to connect to PIO0\ngpio --pio=0 --gpio=2 --init   # tell GPIO2 to connect to PIO0\ngpio --pio=0 --gpio=3 --init   # tell GPIO3 to connect to PIO0\n\n# Set consecutive pindirs, again 4 GPIOs, base=0.\ngpio --pio=0 --gpio=0 --enable # set direction out\ngpio --pio=0 --gpio=1 --enable # set direction out\ngpio --pio=0 --gpio=2 --enable # set direction out\ngpio --pio=0 --gpio=3 --enable # set direction out\n\n# SM Config Set Out Shift(right, autopull, threshold=32)\nfifo --pio=0 --sm=0 --tx --shift-right\nfifo --pio=0 --sm=0 --tx --auto=true\nfifo --pio=0 --sm=0 --tx --threshold=32\n\n# SM Config Set Out Pins(base=0, count=4).\npinctrl --pio=0 --sm=0 --out-base=0 --out-count=4\n\n# SM Config Set Set Pins(base=0, count=4).\npinctrl --pio=0 --sm=0 --set-base=0 --set-count=4\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.  Since we have configured only\n# GPIOs 0…3, only the lowest 4 bits per FIFO word (=last hex digit)\n# are relevant.\nfifo --enqueue --tx --value 0x0000000a\nfifo --enqueue --tx --value 0x00000005\nfifo --enqueue --tx --value 0x00000001\nfifo --enqueue --tx --value 0x0000000f\nfifo --enqueue --tx --value 0x00000002\nfifo --enqueue --tx --value 0x0000000e\n\n# Done.\nquit\n"
  },
  {
    "path": "java/examples/ws2812.hex",
    "content": "# .program ws2812\n# ; .side_set 1\n# ; .wrap_target\n6221\n1123\n1400\na442\n# ; .wrap\n"
  },
  {
    "path": "java/examples/ws2812.mon",
    "content": "# Script: WS2812\n# Group: WS2812\n#\n# Monitor script for ws2812 example.\n# To be executed on PIO 0, SM 0.\n# Output is fed to GPIO 0.\n\n# Make a full reset of the emulator.\nreset\n\n# We loosely follow the initialization sequence as shown in:\n# https://github.com/raspberrypi/pico-examples\n# /blob/master/pio/ws2812/generated/ws2812.pio.h\n#\n# This example uses timing T1=2, T2=5, T3=3.\n# Note that, according to the discussion in\n# https://github.com/raspberrypi/pico-feedback/issues/121,\n# T1=T2=T3=2 may be a better choice.\n\n# Configure Wrap.\nwrap --pio=0 --sm=0 --wrap=3 --target=0\n\n# Configure Side Set Count.\nside-set --pio=0 --sm=0 --count=1 --opt=false\n\n# The code.\n# .wrap_target\n# bitloop:\nenter -a 0 -v 0x6221 # out x, 01       side 0 [2]\nenter -a 1 -v 0x1123 # jmp !x, 03      side 1 [1]\n# do_one:\nenter -a 2 -v 0x1400 # jmp 00          side 1 [4]\n# do_zero:\nenter -a 3 -v 0xa442 # nop             side 0 [4]\n# .wrap\n\n# Just for convience and verification, list the program that\n# we just entered, as viewed by PIO 0, SM 0.\nunassemble --pio=0 --sm=0 --address=0 --count=4\n\n########\n# Init program (analoguous to ws2812_program_init() in ws2812.pio.h as\n# created by pioasm).\n########\n\n# Connect GPIO 0 with PIO 0.\ngpio --pio=0 --gpio=0 --init   # tell GPIO to connect to PIO0\n\n# Set consecutive pindirs, here just a single one.\ngpio --pio=0 --gpio=0 --enable # set direction out\n\n# Configure side set base.\nside-set --pio=0 --sm=0 --base=0\n\n# Configure out shift.\nfifo --pio=0 --sm=0 --tx --shift-left\nfifo --pio=0 --sm=0 --tx --auto=true    # auto pull\nfifo --pio=0 --sm=0 --tx --threshold=24 # or 32 for rgbw\n\n# Configure FIFO join: Join TX.\nfifo --pio=0 --sm=0 --join --tx\n\n# Configure Clock Divider, here as 1.0 (maximum speed).\n# We choose maximum speed since we do not want to see gaps when\n# tracing the code.\nclock --pio=0 --sm=0 --divider=1.0\n\n########\n# Initialize SM (analoguous to function pio_sm_init() in Pico C SDK).\n########\n\n# Disable state machine 0 of PIO 0 while executing the following\n# commands.\nsm --pio=0 --sm=0 --enable=false\n\n# Clear FIFOs.\nfifo --pio=0 --sm=0 --clear\n\n# Clear FIFO debug flags.\nfifo --pio=0 --sm=0 --clear-tx-stall\nfifo --pio=0 --sm=0 --clear-tx-over\nfifo --pio=0 --sm=0 --clear-rx-under\nfifo --pio=0 --sm=0 --clear-rx-stall\n\n# Restart SM.\nsm --pio=0 --sm=0 --restart\n\n# Restart clock.\nclock --pio=0 --sm=0 --restart\n\n# Set instruction pointer (PC) to address 0.\nregisters --address=0\n\n########\n# End of SM initialization.\n########\n\n# Enable state machine 0 of PIO 0 to execute the program.\nsm --pio=0 --sm=0 --enable=true\n\n########\n# End of program initialization.\n# Next, we feed in example data.\n########\n\n# Put example values into FIFO.\nfifo --enqueue --tx --value 0xaa118800 # RGB mode: end with byte 0x00\nfifo --enqueue --tx --value 0x55881100 # RGB mode: end with byte 0x00\n\n# Done.\nquit\n"
  },
  {
    "path": "java/media/gpl-2.0-standalone.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n <title>GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF)</title>\n <link rel=\"alternate\" type=\"application/rdf+xml\"\n       href=\"http://www.gnu.org/licenses/old-licenses/gpl-2.0.rdf\" />\n</head>\n<body>\n<h3 id=\"SEC1\">GNU GENERAL PUBLIC LICENSE</h3>\n<p>\nVersion 2, June 1991\n</p>\n\n<pre>\nCopyright (C) 1989, 1991 Free Software Foundation, Inc.  \n51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n\nEveryone is permitted to copy and distribute verbatim copies\nof this license document, but changing it is not allowed.\n</pre>\n\n<span id=\"SEC2\"></span>\n<h4 id=\"preamble\">Preamble</h4>\n\n<p>\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n</p>\n\n<p>\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n</p>\n\n<p>\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n</p>\n\n<p>\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n</p>\n\n<p>\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n</p>\n\n<p>\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n</p>\n\n<p>\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n</p>\n\n<p>\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n</p>\n\n<span id=\"SEC3\"></span>\n<h4 id=\"terms\">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</h4>\n\n<p id=\"section0\">\n<strong>0.</strong>\n This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n</p>\n\n<p>\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n</p>\n\n<p id=\"section1\">\n<strong>1.</strong>\n You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n</p>\n\n<p>\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n</p>\n\n<p id=\"section2\">\n<strong>2.</strong>\n You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n</p>\n\n<dl>\n  <dt></dt>\n    <dd>\n      <strong>a)</strong>\n      You must cause the modified files to carry prominent notices\n      stating that you changed the files and the date of any change.\n    </dd>\n  <dt></dt>\n    <dd>\n      <strong>b)</strong>\n      You must cause any work that you distribute or publish, that in\n      whole or in part contains or is derived from the Program or any\n      part thereof, to be licensed as a whole at no charge to all third\n      parties under the terms of this License.\n    </dd>\n  <dt></dt>\n    <dd>\n      <strong>c)</strong>\n      If the modified program normally reads commands interactively\n      when run, you must cause it, when started running for such\n      interactive use in the most ordinary way, to print or display an\n      announcement including an appropriate copyright notice and a\n      notice that there is no warranty (or else, saying that you provide\n      a warranty) and that users may redistribute the program under\n      these conditions, and telling the user how to view a copy of this\n      License.  (Exception: if the Program itself is interactive but\n      does not normally print such an announcement, your work based on\n      the Program is not required to print an announcement.)\n    </dd>\n</dl>\n\n<p>\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n</p>\n\n<p>\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n</p>\n\n<p>\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n</p>\n\n<p id=\"section3\">\n<strong>3.</strong>\n You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n</p>\n\n<!-- we use this doubled UL to get the sub-sections indented, -->\n<!-- while making the bullets as unobvious as possible. -->\n\n<dl>\n  <dt></dt>\n    <dd>\n      <strong>a)</strong>\n      Accompany it with the complete corresponding machine-readable\n      source code, which must be distributed under the terms of Sections\n      1 and 2 above on a medium customarily used for software interchange; or,\n    </dd>\n  <dt></dt>\n    <dd>\n      <strong>b)</strong>\n      Accompany it with a written offer, valid for at least three\n      years, to give any third party, for a charge no more than your\n      cost of physically performing source distribution, a complete\n      machine-readable copy of the corresponding source code, to be\n      distributed under the terms of Sections 1 and 2 above on a medium\n      customarily used for software interchange; or,\n    </dd>\n  <dt></dt>\n    <dd>\n      <strong>c)</strong>\n      Accompany it with the information you received as to the offer\n      to distribute corresponding source code.  (This alternative is\n      allowed only for noncommercial distribution and only if you\n      received the program in object code or executable form with such\n      an offer, in accord with Subsection b above.)\n    </dd>\n</dl>\n\n<p>\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n</p>\n\n<p>\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n</p>\n\n<p id=\"section4\">\n<strong>4.</strong>\n You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n</p>\n\n<p id=\"section5\">\n<strong>5.</strong>\n You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n</p>\n\n<p id=\"section6\">\n<strong>6.</strong>\n Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n</p>\n\n<p id=\"section7\">\n<strong>7.</strong>\n If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n</p>\n\n<p>\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n</p>\n\n<p>\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n</p>\n\n<p>\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n</p>\n\n<p id=\"section8\">\n<strong>8.</strong>\n If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n</p>\n\n<p id=\"section9\">\n<strong>9.</strong>\n The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n</p>\n\n<p>\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n</p>\n\n<p id=\"section10\">\n<strong>10.</strong>\n If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n</p>\n\n<p id=\"section11\"><strong>NO WARRANTY</strong></p>\n\n<p>\n<strong>11.</strong>\n BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n</p>\n\n<p id=\"section12\">\n<strong>12.</strong>\n IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n</p>\n\n<h4>END OF TERMS AND CONDITIONS</h4>\n\n<span id=\"SEC4\"></span>\n<h4 id=\"howto\">How to Apply These Terms to Your New Programs</h4>\n\n<p>\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n</p>\n\n<p>\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n</p>\n\n<pre>\n<var>one line to give the program's name and an idea of what it does.</var>\nCopyright (C) <var>yyyy</var>  <var>name of author</var>\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n</pre>\n\n<p>\nAlso add information on how to contact you by electronic and paper mail.\n</p>\n\n<p>\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n</p>\n\n<pre>\nGnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var>\nGnomovision comes with ABSOLUTELY NO WARRANTY; for details\ntype `show w'.  This is free software, and you are welcome\nto redistribute it under certain conditions; type `show c' \nfor details.\n</pre>\n\n<p>\nThe hypothetical commands <samp>`show w'</samp> and <samp>`show c'</samp> should show\nthe appropriate parts of the General Public License.  Of course, the\ncommands you use may be called something other than <samp>`show w'</samp> and\n<samp>`show c'</samp>; they could even be mouse-clicks or menu items--whatever\nsuits your program.\n</p>\n\n<p>\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n</p>\n\n\n<pre>\nYoyodyne, Inc., hereby disclaims all copyright\ninterest in the program `Gnomovision'\n(which makes passes at compilers) written \nby James Hacker.\n\n<var>signature of Ty Coon</var>, 1 April 1989\nTy Coon, President of Vice\n</pre>\n\n<p>\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the \n<a href=\"https://www.gnu.org/licenses/lgpl.html\">GNU Lesser General Public License</a>\ninstead of this License.\n</p>\n\n</body></html>\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/AddressSpace.java",
    "content": "/*\n * @(#)AddressSpace.java 1.00 21/03/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.IOException;\n\npublic abstract class AddressSpace\n{\n  static final int REG_ALIAS_RW_BITS = 0x0000;\n  static final int REG_ALIAS_XOR_BITS = 0x1000;\n  static final int REG_ALIAS_SET_BITS = 0x2000;\n  static final int REG_ALIAS_CLR_BITS = 0x3000;\n\n  private enum AccessMethod {\n    NORMAL_RW, ATOMIC_XOR, ATOMIC_SET, ATOMIC_CLEAR;\n  };\n\n  private static AccessMethod[] ACCESS_METHODS = AccessMethod.values();\n\n  protected static void checkAddressAligned(final int address)\n  {\n    if ((address & 0x3) != 0x0) {\n      throw new IllegalArgumentException(\"address not word-aligned: \" +\n                                         String.format(\"0x%08x\", address));\n    }\n  }\n\n  private static void checkAddressNormalRWSpace(final int address)\n  {\n    if ((address & 0x3000) != 0x0) {\n      throw new IllegalArgumentException(\"address is not in the space of \" +\n                                         \"normal read / write access: \" +\n                                         String.format(\"0x%08x\", address));\n    }\n  }\n\n  public abstract String getEmulatorInfo() throws IOException;\n\n  public abstract boolean providesAddress(final int address)\n    throws IOException;\n\n  public abstract String getRegisterSetId(final int address) throws IOException;\n\n  public abstract String getAddressLabel(final int address) throws IOException;\n\n  public abstract int readAddress(final int address) throws IOException;\n\n  public abstract void writeAddressMasked(final int address, final int bits,\n                                          final int mask, final boolean xor)\n    throws IOException;\n\n  public abstract int waitAddress(final int address, final int expectedValue,\n                                  final int mask,\n                                  final long cyclesTimeout,\n                                  final long millisTimeout)\n    throws IOException;\n\n  public void writeAddress(final int address, final int value)\n    throws IOException\n  {\n    checkAddressAligned(address);\n    final AccessMethod accessMethod = ACCESS_METHODS[((address >> 12) & 0x3)];\n    final int mask;\n    final int bits;\n    switch (accessMethod) {\n    case NORMAL_RW:\n      mask = ~0x0;\n      bits = value;\n      break;\n    case ATOMIC_XOR:\n      mask = value;\n      bits = value;\n      break;\n    case ATOMIC_SET:\n      mask = value;\n      bits = value;\n      break;\n    case ATOMIC_CLEAR:\n      mask = value;\n      bits = 0x0;\n      break;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n    writeAddressMasked(address & ~0x3000, bits, mask,\n                       accessMethod == AccessMethod.ATOMIC_XOR);\n  }\n\n  public void hwSetBits(final int address, final int mask) throws IOException\n  {\n    checkAddressNormalRWSpace(address);\n    writeAddress(address | REG_ALIAS_SET_BITS, mask);\n  }\n\n  public void hwClearBits(final int address, final int mask) throws IOException\n  {\n    checkAddressNormalRWSpace(address);\n    writeAddress(address | REG_ALIAS_CLR_BITS, mask);\n  }\n\n  public void hwXorBits(final int address, final int mask) throws IOException\n  {\n    checkAddressNormalRWSpace(address);\n    writeAddress(address | REG_ALIAS_XOR_BITS, mask);\n  }\n\n  public void hwWriteMasked(final int address, final int values,\n                            final int writeMask)\n    throws IOException\n  {\n    checkAddressNormalRWSpace(address);\n    writeAddressMasked(address, values, writeMask, false);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Bit.java",
    "content": "/*\n * @(#)Bit.java 1.00 21/02/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Representation of a single bit value.\n */\npublic enum Bit\n{\n  LOW(0, '0', \"low\", \"\\u001b[30;42m0\\u001b[0m\", \"\\u001b[37;41m0\\u001b[0m\"),\n  HIGH(1, '1', \"high\", \"\\u001b[30;42m1\\u001b[0m\", \"\\u001b[37;41m1\\u001b[0m\");\n\n  private final int value;\n  private final char charLabel;\n  private final String level;\n  private final String superScriptLabel;\n  private final String subScriptLabel;\n  private final String stringLabel;\n\n  private Bit(final int value, final char charLabel, final String level,\n              final String superScriptLabel, final String subScriptLabel)\n  {\n    this.value = value;\n    this.charLabel = charLabel;\n    this.level = level;\n    this.superScriptLabel = superScriptLabel;\n    this.subScriptLabel = subScriptLabel;\n    this.stringLabel = String.valueOf(charLabel);\n  }\n\n  public int getValue() { return value; }\n\n  public String getLevel() { return level; }\n\n  public static Bit fromValue(final boolean value)\n  {\n    return value ? HIGH : LOW;\n  }\n\n  public static Bit fromValue(final int value)\n  {\n    if (value == LOW.value) return LOW;\n    if (value == HIGH.value) return HIGH;\n    throw new IllegalArgumentException(\"value not a bit: \" + value);\n  }\n\n  public static Bit fromValue(final int value, final Bit defaultValue)\n  {\n    if (value == LOW.value) return LOW;\n    if (value == HIGH.value) return HIGH;\n    return defaultValue;\n  }\n\n  public String toChar(final Direction direction)\n  {\n    if (direction == null) return String.valueOf(charLabel);\n    return direction == Direction.IN ? superScriptLabel : subScriptLabel;\n  }\n\n  public Bit inverse()\n  {\n    return this == LOW ? HIGH : LOW;\n  }\n\n  @Override\n  public String toString()\n  {\n    return stringLabel;\n  }\n};\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Clock.java",
    "content": "/*\n * @(#)Clock.java 1.00 21/02/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Clock Signal Provider\n */\npublic interface Clock\n{\n  public enum Phase\n  {\n    PHASE_0_IN_PROGRESS,\n    PHASE_0_STABLE,\n    PHASE_1_IN_PROGRESS,\n    PHASE_1_STABLE\n  };\n\n  public static interface TransitionListener\n  {\n    void risingEdge(final long wallClock);\n    void fallingEdge(final long wallClock);\n  }\n\n  void addTransitionListener(final TransitionListener listener);\n  boolean removeTransitionListener(final TransitionListener listener);\n  long getWallClock();\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/CmdOptions.java",
    "content": "/*\n * @(#)CommadLineOptions.java 1.00 17/01/21\n *\n * Copyright (C) 2017, 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Parsing and managing command line options.\n */\npublic class CmdOptions\n{\n  public abstract static class OptionDeclaration<T>\n  {\n    private final String typeName;\n    private final boolean mandatory;\n    private final Character shortName;\n    private final String longName;\n    private final String defaultValueAsString;\n    private final String description;\n\n    private OptionDeclaration()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private OptionDeclaration(final String typeName,\n                              final boolean mandatory,\n                              final Character shortName,\n                              final String longName,\n                              final String defaultValueAsString,\n                              final String description)\n    {\n      if ((shortName == null) && (longName == null)) {\n        throw new NullPointerException(\"either shortName or longName \" +\n                                       \"must be non-null\");\n      }\n      this.typeName = typeName;\n      this.mandatory = mandatory;\n      this.shortName = shortName;\n      this.longName = longName;\n      this.description = description;\n      this.defaultValueAsString = defaultValueAsString;\n    }\n\n    private String getTypeName()\n    {\n      return typeName;\n    }\n\n    private boolean isMandatory()\n    {\n      return mandatory;\n    }\n\n    private Character getShortName()\n    {\n      return shortName;\n    }\n\n    private String getShortNameAsString()\n    {\n      return\n        shortName != null ? String.valueOf(shortName) : null;\n    }\n\n    private String getLongName()\n    {\n      return longName;\n    }\n\n    private String getDefaultValueAsString()\n    {\n      return defaultValueAsString;\n    }\n\n    private String getDescription()\n    {\n      return description;\n    }\n\n    abstract OptionDefinition<T> define() throws ParseException;\n\n    private String getOptionHelp()\n    {\n      final StringBuffer sb = new StringBuffer();\n      sb.append(String.format(\"  %s\", this));\n      if (description != null) {\n        sb.append(String.format(\"%n            %s\", description));\n      }\n      return sb.toString();\n    }\n\n    abstract String getDefaultTypeName();\n\n    @Override\n    public String toString()\n    {\n      final StringBuffer sb = new StringBuffer();\n      if (shortName != null) {\n        if (this instanceof BooleanOptionDeclaration) {\n          sb.append(\"+\");\n          sb.append(shortName);\n          sb.append(\" / -\");\n          sb.append(shortName);\n        } else {\n          sb.append(\"-\");\n          sb.append(shortName);\n        }\n        if (longName != null) {\n          sb.append(\", \");\n        }\n      }\n      if (longName != null) {\n        sb.append(\"--\");\n        sb.append(longName);\n      }\n      if (!(this instanceof FlagOptionDeclaration) &&\n          !(this instanceof BooleanOptionDeclaration)) {\n        sb.append(\"=\");\n        if (typeName != null) {\n          sb.append(typeName);\n        } else {\n          sb.append(getDefaultTypeName());\n        }\n      }\n      if (defaultValueAsString != null) {\n        sb.append(\" (default: \");\n        sb.append(defaultValueAsString != null ?\n                  defaultValueAsString : \"<empty>\");\n        sb.append(\")\");\n      } else {\n        sb.append(\" (mandatory: \");\n        sb.append(mandatory ? \"yes\" : \"no\");\n        sb.append(\")\");\n      }\n      return sb.toString();\n    }\n  }\n\n  public static class FlagOptionDeclaration extends OptionDeclaration<Flag>\n  {\n    public FlagOptionDeclaration(final boolean mandatory,\n                                 final Character shortName,\n                                 final String longName,\n                                 final Flag defaultValue,\n                                 final String description)\n    {\n      super(null, mandatory, shortName, longName,\n            String.valueOf(defaultValue), description);\n    }\n\n    String getDefaultTypeName() { return \"FLAG\"; }\n\n    OptionDefinition<Flag> define() throws ParseException\n    {\n      return new FlagOptionDefinition(this);\n    }\n  }\n\n  public static FlagOptionDeclaration\n    createFlagOption(final boolean mandatory,\n                     final Character shortName,\n                     final String longName,\n                     final Flag defaultValue,\n                     final String description)\n  {\n    return new FlagOptionDeclaration(mandatory, shortName, longName,\n                                     defaultValue, description);\n  }\n\n  public static class BooleanOptionDeclaration\n    extends OptionDeclaration<Boolean>\n  {\n    public BooleanOptionDeclaration(final boolean mandatory,\n                                    final Character shortName,\n                                    final String longName,\n                                    final Boolean defaultValue,\n                                    final String description)\n    {\n      super(null, mandatory, shortName, longName,\n            defaultValue != null ? String.valueOf(defaultValue) : null,\n            description);\n    }\n\n    String getDefaultTypeName() { return \"BOOLEAN\"; }\n\n    OptionDefinition<Boolean> define() throws ParseException\n    {\n      return new BooleanOptionDefinition(this);\n    }\n  }\n\n  public static BooleanOptionDeclaration\n    createBooleanOption(final boolean mandatory,\n                        final Character shortName,\n                        final String longName,\n                        final Boolean defaultValue,\n                        final String description)\n  {\n    return new BooleanOptionDeclaration(mandatory, shortName, longName,\n                                        defaultValue, description);\n  }\n\n  public static class IntegerOptionDeclaration\n    extends OptionDeclaration<Integer>\n  {\n    public IntegerOptionDeclaration(final String typeName,\n                                    final boolean mandatory,\n                                    final Character shortName,\n                                    final String longName,\n                                    final Integer defaultValue,\n                                    final String description)\n    {\n      super(typeName, mandatory, shortName, longName,\n            defaultValue != null ? String.valueOf(defaultValue) : null,\n            description);\n    }\n\n    String getDefaultTypeName() { return \"INTEGER\"; }\n\n    OptionDefinition<Integer> define() throws ParseException\n    {\n      return new IntegerOptionDefinition(this);\n    }\n  }\n\n  public static IntegerOptionDeclaration\n    createIntegerOption(final String typeName,\n                        final boolean mandatory,\n                        final Character shortName,\n                        final String longName,\n                        final Integer defaultValue,\n                        final String description)\n  {\n    return new IntegerOptionDeclaration(typeName, mandatory, shortName,\n                                        longName, defaultValue, description);\n  }\n\n  public static class FloatOptionDeclaration\n    extends OptionDeclaration<Float>\n  {\n    public FloatOptionDeclaration(final String typeName,\n                                  final boolean mandatory,\n                                  final Character shortName,\n                                  final String longName,\n                                  final Float defaultValue,\n                                  final String description)\n    {\n      super(typeName, mandatory, shortName, longName,\n            defaultValue != null ? String.valueOf(defaultValue) : null,\n            description);\n    }\n\n    String getDefaultTypeName() { return \"FLOAT\"; }\n\n    OptionDefinition<Float> define() throws ParseException\n    {\n      return new FloatOptionDefinition(this);\n    }\n  }\n\n  public static FloatOptionDeclaration\n    createFloatOption(final String typeName,\n                      final boolean mandatory,\n                      final Character shortName,\n                      final String longName,\n                      final Float defaultValue,\n                      final String description)\n  {\n    return new FloatOptionDeclaration(typeName, mandatory, shortName,\n                                      longName, defaultValue, description);\n  }\n\n  public static class StringOptionDeclaration\n    extends OptionDeclaration<String>\n  {\n    public StringOptionDeclaration(final String typeName,\n                                   final boolean mandatory,\n                                   final Character shortName,\n                                   final String longName,\n                                   final String defaultValue,\n                                   final String description)\n    {\n      super(typeName, mandatory, shortName, longName,\n            defaultValue, description);\n    }\n\n    String getDefaultTypeName() { return \"STRING\"; }\n\n    OptionDefinition<String> define() throws ParseException\n    {\n      return new StringOptionDefinition(this);\n    }\n  }\n\n  public static StringOptionDeclaration\n    createStringOption(final String typeName,\n                       final boolean mandatory,\n                       final Character shortName,\n                       final String longName,\n                       final String defaultValue,\n                       final String description)\n  {\n    return new StringOptionDeclaration(typeName, mandatory, shortName,\n                                       longName, defaultValue, description);\n  }\n\n  public static class ParseException extends Exception\n  {\n    private static final long serialVersionUID = 7201021940370903355L;\n\n    private final OptionDeclaration<?> optionDeclaration;\n\n    public ParseException(final String message)\n    {\n      this(message, (OptionDeclaration<?>)null);\n    }\n\n    public ParseException(final String message,\n                          final OptionDeclaration<?> optionDeclaration)\n    {\n      super(message);\n      this.optionDeclaration = optionDeclaration;\n    }\n\n    public ParseException(final String message, final Throwable cause)\n    {\n      this(message, cause, null);\n    }\n\n    public ParseException(final String message, final Throwable cause,\n                          final OptionDeclaration<?> optionDeclaration)\n    {\n      super(message, cause);\n      this.optionDeclaration = optionDeclaration;\n    }\n\n    public ParseException(final Throwable cause)\n    {\n      this(cause, null);\n    }\n\n    public ParseException(final Throwable cause,\n                          final OptionDeclaration<?> optionDeclaration)\n    {\n      super(cause);\n      this.optionDeclaration = optionDeclaration;\n    }\n\n    @Override\n    public String getMessage()\n    {\n      return\n        (optionDeclaration != null ?\n         \"option \" + optionDeclaration + \": \" : \"\") +\n        super.getMessage();\n    }\n  }\n\n  private abstract static class OptionDefinition<T>\n  {\n    private final OptionDeclaration<T> declaration;\n    private T defaultValue;\n    private T parsedValue;\n\n    private OptionDefinition(final OptionDeclaration<T> declaration)\n      throws ParseException\n    {\n      this.declaration = declaration;\n      final String defaultValueAsString = declaration.getDefaultValueAsString();\n      defaultValue =\n        defaultValueAsString != null ? parse(defaultValueAsString) : null;\n      parsedValue = null;\n    }\n\n    abstract T parse(final String strValue) throws ParseException;\n\n    private T parseAndSet(final String strValue) throws ParseException\n    {\n      parsedValue = parse(strValue);\n      return parsedValue;\n    }\n\n    protected OptionDeclaration<T> getDeclaration()\n    {\n      return declaration;\n    }\n\n    protected void setParsedValue(final T value)\n    {\n      if (parsedValue == null) {\n        throw new NullPointerException(\"parsedValue\");\n      }\n      this.parsedValue = parsedValue;\n    }\n\n    protected void clear()\n    {\n      parsedValue = null;\n    }\n\n    protected boolean isParsed()\n    {\n      return parsedValue != null;\n    }\n\n    protected boolean hasDefaultValue()\n    {\n      return defaultValue != null;\n    }\n\n    private boolean isDefined()\n    {\n      return isParsed() || hasDefaultValue();\n    }\n\n    private boolean isDefinedIfMandatory()\n    {\n      return isDefined() || !declaration.isMandatory();\n    }\n\n    protected T getValue()\n    {\n      return isParsed() ? parsedValue : defaultValue;\n    }\n\n    @Override\n    public String toString()\n    {\n      return declaration.toString();\n    }\n  }\n\n  public static enum Flag\n  {\n    OFF(\"off\"), ON(\"on\");\n\n    private final String displayValue;\n\n    private Flag(final String displayValue)\n    {\n      this.displayValue = displayValue;\n    }\n\n    public boolean isOff() { return this == OFF; }\n\n    public boolean isOn() { return this == ON; }\n\n    @Override\n    public String toString() { return displayValue; }\n  }\n\n  public static class FlagOptionDefinition extends OptionDefinition<Flag>\n  {\n    public FlagOptionDefinition(final FlagOptionDeclaration declaration)\n      throws ParseException\n    {\n      super(declaration);\n    }\n\n    @Override\n    Flag parse(final String strValue) throws ParseException\n    {\n      final String normalizedStrValue = strValue.trim();\n      if (Flag.OFF.toString().equals(normalizedStrValue)) {\n        return Flag.OFF;\n      } else if (Flag.ON.toString().equals(normalizedStrValue)) {\n        return Flag.ON;\n      } else {\n        throw new ParseException(\"'\" + Flag.OFF + \"' or \" +\n                                 \"'\" + Flag.ON + \"' expected, \" +\n                                 \"but found: \" + strValue,\n                                 getDeclaration());\n      }\n    }\n  }\n\n  public static class BooleanOptionDefinition extends OptionDefinition<Boolean>\n  {\n    public BooleanOptionDefinition(final BooleanOptionDeclaration declaration)\n      throws ParseException\n    {\n      super(declaration);\n    }\n\n    @Override\n    Boolean parse(final String strValue) throws ParseException\n    {\n      final String normalizedStrValue = strValue.trim();\n      if (\"false\".equals(normalizedStrValue)) {\n        return false;\n      } else if (\"true\".equals(normalizedStrValue)) {\n        return true;\n      } else {\n        throw new ParseException(\"'false' or 'true' expected, \" +\n                                 \"but found: \" + strValue,\n                                 getDeclaration());\n      }\n    }\n  }\n\n  public static class IntegerOptionDefinition extends OptionDefinition<Integer>\n  {\n    public IntegerOptionDefinition(final IntegerOptionDeclaration declaration)\n      throws ParseException\n    {\n      super(declaration);\n    }\n\n    @Override\n    Integer parse(final String strValue) throws ParseException\n    {\n      final String normalizedStrValue = strValue.toLowerCase().trim();\n      try {\n        if (normalizedStrValue.startsWith(\"0x\")) {\n          return Integer.parseUnsignedInt(normalizedStrValue.substring(2), 16);\n        } else {\n          return Integer.parseInt(normalizedStrValue);\n        }\n      } catch (final NumberFormatException e) {\n        throw new ParseException(\"integer value expected: \" + e.getMessage(),\n                                 getDeclaration());\n      }\n    }\n  }\n\n  public static class FloatOptionDefinition extends OptionDefinition<Float>\n  {\n    public FloatOptionDefinition(final FloatOptionDeclaration declaration)\n      throws ParseException\n    {\n      super(declaration);\n    }\n\n    @Override\n    Float parse(final String strValue) throws ParseException\n    {\n      final String normalizedStrValue = strValue.toLowerCase().trim();\n      try {\n        return Float.parseFloat(normalizedStrValue);\n      } catch (final NumberFormatException e) {\n        throw new ParseException(\"float value expected: \" + e.getMessage(),\n                                 getDeclaration());\n      }\n    }\n  }\n\n  public static class StringOptionDefinition extends OptionDefinition<String>\n  {\n    public StringOptionDefinition(final StringOptionDeclaration declaration)\n      throws ParseException\n    {\n      super(declaration);\n    }\n\n    @Override\n    String parse(final String strValue) throws ParseException\n    {\n      return strValue;\n    }\n  }\n\n  private final String prgName;\n  private final String prgSingleLineDescription;\n  private final String prgNotes;\n  private final List<OptionDeclaration<?>> declarations;\n  private final OptionDefinition<?>[] definitions;\n  private String parsedCommand;\n\n  public CmdOptions(final String prgName,\n                    final String prgSingleLineDescription,\n                    final String prgNotes,\n                    final OptionDeclaration<?> ... declarations)\n    throws ParseException\n  {\n    this(prgName, prgSingleLineDescription, prgNotes,\n         Arrays.asList(declarations));\n  }\n\n  public CmdOptions(final String prgName,\n                    final String prgSingleLineDescription,\n                    final String prgNotes,\n                    final List<OptionDeclaration<?>> declarations)\n    throws ParseException\n  {\n    if (prgName == null) {\n      throw new NullPointerException(\"prgName\");\n    }\n    if (declarations == null) {\n      throw new NullPointerException(\"declarations\");\n    }\n    this.prgName = prgName;\n    this.prgSingleLineDescription = prgSingleLineDescription;\n    this.prgNotes = prgNotes;\n    this.declarations = declarations;\n    checkShortNamesAreUnique();\n    checkLongNamesAreUnique();\n    definitions = createDefinitions();\n  }\n\n  private String getPrgName()\n  {\n    return prgName;\n  }\n\n  private String getPrgSingleLineDescription()\n  {\n    return prgSingleLineDescription;\n  }\n\n  private String getPrgNotes()\n  {\n    return prgNotes;\n  }\n\n  public String getUsage()\n  {\n    return prgName + \" [OPTION]…\";\n  }\n\n  public String getOptionsHelp()\n  {\n    final StringBuffer sb = new StringBuffer();\n    for (final OptionDeclaration<?> declaration : declarations) {\n      sb.append(String.format(\"%s%n\", declaration.getOptionHelp()));\n    }\n    return sb.toString();\n  }\n\n  public String getFullInfo()\n  {\n    final StringBuffer sb = new StringBuffer();\n    sb.append(String.format(\"Usage: %s%n%n\", getUsage()));\n    if (prgSingleLineDescription != null) {\n      sb.append(String.format(\"%s%n%n\", prgSingleLineDescription));\n    }\n    final String optionsHelp = getOptionsHelp();\n    if (!optionsHelp.isEmpty()) {\n      sb.append(String.format(\"Options:%n%n\"));\n      sb.append(optionsHelp);\n    }\n    if (prgNotes != null) {\n      sb.append(String.format(\"%nNotes:%n\" + prgNotes + \"%n\"));\n    }\n    return sb.toString();\n  }\n\n  private void checkShortNamesAreUnique()\n  {\n    final Map<Character, OptionDeclaration<?>> shortName2Declaration =\n      new HashMap<Character, OptionDeclaration<?>>();\n    for (final OptionDeclaration<?> declaration : declarations) {\n      final Character shortName = declaration.getShortName();\n      if (shortName != null) {\n        if (shortName2Declaration.containsKey(shortName)) {\n          final OptionDeclaration<?> otherDeclaration =\n            shortName2Declaration.get(shortName);\n          final String message =\n            String.format(\"duplicate short name '%s' for options:%n%s%n%s\",\n                          shortName, declaration, otherDeclaration);\n          throw new IllegalArgumentException(message);\n        }\n        shortName2Declaration.put(shortName, declaration);\n      }\n    }\n  }\n\n  private void checkLongNamesAreUnique()\n  {\n    final Map<String, OptionDeclaration<?>> longName2Declaration =\n      new HashMap<String, OptionDeclaration<?>>();\n    for (final OptionDeclaration<?> declaration : declarations) {\n      final String longName = declaration.getLongName();\n      if (longName2Declaration.containsKey(longName)) {\n        final OptionDeclaration<?> otherDeclaration =\n          longName2Declaration.get(longName);\n        final String message =\n          String.format(\"duplicate long name \\\"%s\\\" for options:%n%s%n%s\",\n                        longName, declaration, otherDeclaration);\n        throw new IllegalArgumentException(message);\n      }\n      longName2Declaration.put(longName, declaration);\n    }\n  }\n\n  private static final OptionDefinition<?>[] EMPTY_DEFINITIONS =\n    new OptionDefinition<?>[0];\n\n  private OptionDefinition<?>[] createDefinitions() throws ParseException\n  {\n    final ArrayList<OptionDefinition<?>> definitionList =\n      new ArrayList<OptionDefinition<?>>();\n    for (final OptionDeclaration<?> declaration : declarations) {\n      final OptionDefinition<?> definition = declaration.define();\n      definitionList.add(definition);\n    }\n    return definitionList.toArray(EMPTY_DEFINITIONS);\n  }\n\n  public void clear()\n  {\n    parsedCommand = null;\n    for (final OptionDefinition<?> definition : definitions) {\n      definition.clear();\n    }\n  }\n\n  private boolean\n    updateDefinition(final OptionDefinition<?> definition, String strValue)\n    throws ParseException\n  {\n    if (definition.isParsed()) {\n      throw new ParseException(\"option redefined\",\n                               definition.getDeclaration());\n    }\n    if (definition instanceof FlagOptionDefinition) {\n      if (strValue != null) {\n        throw new ParseException(\"unexpected surplus argument: \" + strValue,\n                                 definition.getDeclaration());\n      }\n      definition.parseAndSet(Flag.ON.toString());\n      return false;\n    } else if (strValue != null) {\n      definition.parseAndSet(strValue);\n      return false;\n    } else {\n      return true;\n    }\n  }\n\n  private OptionDefinition<?> parseLongOptionIdentifier(final String option)\n    throws ParseException\n  {\n    if (option.isEmpty()) {\n      throw new ParseException(\"long option must not be empty: --\");\n    }\n    final String name;\n    final String value;\n    final int pos = option.indexOf('=');\n    if (pos >= 0) {\n      name = option.substring(0, pos);\n      value = option.substring(pos + 1);\n    } else {\n      name = option;\n      value = null;\n    }\n    if (name.isEmpty()) {\n      throw new ParseException(\"long option name must not be empty: --\" +\n                               option);\n    }\n    OptionDefinition<?> definition = null;\n    for (final OptionDeclaration<?> declaration : declarations) {\n      final String longName = declaration.getLongName();\n      if (name.equals(longName)) {\n        definition = findDefinitionForDeclaration(declaration);\n        break;\n      }\n    }\n    if (definition != null) {\n      return updateDefinition(definition, value) ? definition : null;\n    } else {\n      throw new ParseException(\"unknown long option name: \" + name);\n    }\n  }\n\n  private OptionDefinition<?> parseShortOptionIdentifier(final boolean plus,\n                                                         final String name)\n    throws ParseException\n  {\n    if (name.length() != 1) {\n      throw new ParseException(\"short option name: \" +\n                               \"expected single character, but found: \" +\n                               name);\n    }\n    OptionDefinition<?> definition = null;\n    for (final OptionDeclaration<?> declaration : declarations) {\n      final String shortName = declaration.getShortNameAsString();\n      if (name.equals(shortName)) {\n        definition = findDefinitionForDeclaration(declaration);\n        break;\n      }\n    }\n    if (definition != null) {\n      if (definition.getDeclaration() instanceof BooleanOptionDeclaration) {\n        final String value = plus ? \"true\" : \"false\";\n        return updateDefinition(definition, value) ? definition : null;\n      }\n      if (plus) {\n        throw new ParseException(\"'+' valid only on Boolean options: \" +\n                                 name);\n      }\n      return updateDefinition(definition, null) ? definition : null;\n    } else {\n      throw new ParseException(\"unknown short option name: \" + name);\n    }\n  }\n\n  private OptionDefinition<?> parseOptionIdentifier(final String arg)\n    throws ParseException\n  {\n    if (arg.startsWith(\"--\")) {\n      return parseLongOptionIdentifier(arg.substring(2));\n    } else if (arg.startsWith(\"-\")) {\n      return parseShortOptionIdentifier(false, arg.substring(1));\n    } else if (arg.startsWith(\"+\")) {\n      return parseShortOptionIdentifier(true, arg.substring(1));\n    } else {\n      throw new ParseException(\"option identifier expected, but found: \" + arg);\n    }\n  }\n\n  private static boolean isWhiteSpace(final char ch)\n  {\n    return\n      (ch <= ' ') ||\n      (ch == '\\t') ||\n      (ch == '\\n') ||\n      (ch == '\\r');\n  }\n\n  private static final String[] EMPTY_STRING_ARRAY = new String[0];\n\n  public static String[] splitArgs(final String args) throws ParseException\n  {\n    if ((args == null) || args.isEmpty()) {\n      return EMPTY_STRING_ARRAY;\n    }\n    final List<String> argv = new ArrayList<String>();\n    final StringBuffer token = new StringBuffer();\n    boolean inToken = false;\n    boolean quoted = false;\n    boolean escaped = false;\n    for (int pos = 0; pos < args.length(); pos++) {\n      final char ch = args.charAt(pos);\n      if (!inToken) {\n        if (isWhiteSpace(ch)) continue;\n        if (ch == '#') break;\n        token.setLength(0);\n        inToken = true;\n      }\n      if (escaped) {\n        if ((ch != ' ') && (ch != '\"') && (ch != '#') && (ch != '\\\\')) {\n          throw new ParseException(\"unsupported escaped character: \" + ch);\n        }\n        token.append(ch);\n        escaped = false;\n        continue;\n      }\n      if (ch == '\\\\') {\n        escaped = true;\n        continue;\n      }\n      if (ch == '\"') {\n        quoted = !quoted;\n        continue;\n      }\n      if ((ch == ' ') && !quoted) {\n        inToken = false;\n        argv.add(token.toString());\n        continue;\n      }\n      if ((ch == '#') && !quoted) {\n        break;\n      }\n      token.append(ch);\n    }\n    if (escaped) {\n      throw new ParseException(\"premature end of line after escape symbol\");\n    }\n    if (quoted) {\n      throw new ParseException(\"missing closing quotation marks\");\n    }\n    if (inToken) {\n      argv.add(token.toString());\n    }\n    return argv.toArray(EMPTY_STRING_ARRAY);\n  }\n\n  public void parse(final String argv[]) throws ParseException\n  {\n    parse(argv, false);\n  }\n\n  public void parse(final String argv[], final boolean includesCommand)\n    throws ParseException\n  {\n    clear();\n    OptionDefinition<?> currentOption = null;\n    for (final String arg : argv) {\n      if (arg == null) {\n        throw new NullPointerException(\"arg\");\n      }\n      if (includesCommand && (parsedCommand == null)) {\n        parsedCommand = arg;\n        continue;\n      }\n      if (currentOption != null) {\n        if (currentOption.isParsed()) {\n          throw new ParseException(\"option redefined\",\n                                   currentOption.getDeclaration());\n        }\n        currentOption.parseAndSet(arg);\n        currentOption = null;\n      } else {\n        currentOption = parseOptionIdentifier(arg);\n      }\n    }\n    if (currentOption != null) {\n      throw new ParseException(\"missing argument\",\n                               currentOption.getDeclaration());\n    }\n    for (final OptionDefinition<?> definition : definitions) {\n      if (!definition.isDefinedIfMandatory()) {\n        throw new ParseException(\"mandatory option not specified\",\n                                 definition.getDeclaration());\n      }\n    }\n  }\n\n  public String getCommand()\n  {\n    return parsedCommand;\n  }\n\n  private OptionDefinition<?>\n    findDefinitionForDeclaration(final OptionDeclaration<?> declaration)\n  {\n    // TODO: Performance: Pre-build a hash map, rather than\n    // iterating each time thorugh all definitions.\n    for (final OptionDefinition<?> definition : definitions) {\n      if (definition.getDeclaration() == declaration) {\n        return definition;\n      }\n    }\n    return null;\n  }\n\n  private void checkForDeclaration(final OptionDeclaration<?> declaration)\n  {\n    if (!declarations.contains(declaration)) {\n      final String message =\n        String.format(\"unregistered declaration: %s\", declaration);\n      throw new IllegalArgumentException(message);\n    }\n  }\n\n  public Flag getValue(final FlagOptionDeclaration declaration)\n  {\n    checkForDeclaration(declaration);\n    final FlagOptionDefinition definition =\n      (FlagOptionDefinition)findDefinitionForDeclaration(declaration);\n    return definition != null ? definition.getValue() : null;\n  }\n\n  public Boolean getValue(final BooleanOptionDeclaration declaration)\n  {\n    checkForDeclaration(declaration);\n    final BooleanOptionDefinition definition =\n      (BooleanOptionDefinition)findDefinitionForDeclaration(declaration);\n    return definition != null ? definition.getValue() : null;\n  }\n\n  public Integer getValue(final IntegerOptionDeclaration declaration)\n  {\n    checkForDeclaration(declaration);\n    final IntegerOptionDefinition definition =\n      (IntegerOptionDefinition)findDefinitionForDeclaration(declaration);\n    return definition != null ? definition.getValue() : null;\n  }\n\n  public Float getValue(final FloatOptionDeclaration declaration)\n  {\n    checkForDeclaration(declaration);\n    final FloatOptionDefinition definition =\n      (FloatOptionDefinition)findDefinitionForDeclaration(declaration);\n    return definition != null ? definition.getValue() : null;\n  }\n\n  public String getValue(final StringOptionDeclaration declaration)\n  {\n    checkForDeclaration(declaration);\n    final StringOptionDefinition definition =\n      (StringOptionDefinition)findDefinitionForDeclaration(declaration);\n    return definition != null ? definition.getValue() : null;\n  }\n\n  public boolean isDefined(final OptionDeclaration<?> declaration)\n  {\n    final OptionDefinition<?> definition =\n      findDefinitionForDeclaration(declaration);\n    if (definition == null) {\n      throw new InternalError(\"no such declaration in this set of options: \" +\n                              declaration);\n    }\n    return definition.isDefined();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/CollapsiblePanel.java",
    "content": "/*\n * @(#)CollapsiblePanel.java 1.00 21/06/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.awt.Component;\nimport java.awt.event.ActionListener;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JToggleButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\n\npublic class CollapsiblePanel extends JPanel\n{\n  private static final long serialVersionUID = -1313958807949980972L;\n\n  private final Component component;\n  private final JToggleButton btToggle;\n\n  public CollapsiblePanel(final Component component,\n                          final String label,\n                          final boolean initiallyOpen)\n  {\n    Objects.requireNonNull(component);\n    Objects.requireNonNull(label);\n    this.component = component;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    btToggle = new JToggleButton();\n    btToggle.setBorderPainted(false);\n    btToggle.setContentAreaFilled(false);\n    btToggle.setFocusPainted(false);\n    btToggle.setOpaque(false);\n    btToggle.setBorder(BorderFactory.createEmptyBorder());\n    final ActionListener toggleAction = (action) -> {\n      final boolean isSelected = btToggle.isSelected();\n      component.setVisible(isSelected);\n      btToggle.setText(isSelected ? \"⊟\" : \"⊞\");\n    };\n    btToggle.addActionListener(toggleAction);\n    add(createToggleButtonLine(label));\n    add(component);\n    btToggle.setSelected(initiallyOpen);\n    toggleAction.actionPerformed(null);\n  }\n\n  private Box createToggleButtonLine(final String label)\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(btToggle);\n\n    // Workaround: hBox.add(Box.createHorizontalStrut(5)) modifies Y\n    // layout behavior. => Add JLabel instead.\n    hBox.add(new JLabel(\" \"));\n\n    hBox.add(new JLabel(label));\n    hBox.add(Box.createHorizontalGlue());\n    return hBox;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Constants.java",
    "content": "/*\n * @(#)Constants.java 1.00 21/02/27\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\npublic interface Constants\n{\n  public static final String PROGRAM_ID = \"RP2040 PIO Emulator\";\n  public static final String VERSION_ID = \"0.1\";\n  public static final String COPYRIGHT_TAG_LINE =\n    String.format(\"Copyright © 2021 by Jürgen Reuter, Karlsruhe, Germany.%n\");\n  public static final String CMD_LINE_COPYRIGHT_NOTICE =\n    String.format(COPYRIGHT_TAG_LINE +\n                  \"%n\" +\n                  \"This software comes with ABSOLUTELY NO WARRANTY;%n\" +\n                  \"for details please look at the license file that you%n\" +\n                  \"should have received together with this software.%n\" +\n                  \"This is free software, and you are welcome to%n\" +\n                  \"redistribute it under certain conditions;%n\" +\n                  \"for details please look at the license file that you%n\" +\n                  \"should have received together with this software.%n\");\n  public static final String MONITOR_COPYRIGHT_NOTICE =\n    String.format(COPYRIGHT_TAG_LINE +\n                  \"%n\" +\n                  \"This software comes with ABSOLUTELY NO WARRANTY;%n\" +\n                  \"for details please look at the license file that you%n\" +\n                  \"should have received together with this software.%n\" +\n                  \"This is free software, and you are welcome to%n\" +\n                  \"redistribute it under certain conditions;%n\" +\n                  \"for details please look at the license file that you%n\" +\n                  \"should have received together with this software.%n\");\n  public static final String GUI_COPYRIGHT_NOTICE =\n    String.format(COPYRIGHT_TAG_LINE +\n                  \"%n\" +\n                  \"This software comes with ABSOLUTELY NO WARRANTY;%n\" +\n                  \"for details type `Alt-L' in the main window.%n\" +\n                  \"This is free software, and you are welcome to%n\" +\n                  \"redistribute it under certain conditions;%n\" +\n                  \"type `Alt-L' in the main window for details.%n\");\n\n  public static String getEmulatorId()\n  {\n    return PROGRAM_ID;\n  }\n\n  public static String getEmulatorVersion()\n  {\n    return VERSION_ID;\n  }\n\n  public static String getCmdLineCopyrightNotice()\n  {\n    return CMD_LINE_COPYRIGHT_NOTICE;\n  }\n\n  public static String getMonitorCopyrightNotice()\n  {\n    return MONITOR_COPYRIGHT_NOTICE;\n  }\n\n  public static String getGuiCopyrightNotice()\n  {\n    return GUI_COPYRIGHT_NOTICE;\n  }\n\n  public static String getEmulatorIdAndVersionWithOs()\n  {\n    return\n      getEmulatorId() +\n      \" Version \" + getEmulatorVersion() +\n      \" / jvm \" + System.getProperty(\"java.version\") +\n      \" / \" + System.getProperty(\"java.class.version\");\n  }\n\n  public static final int GPIO_NUM = 32;\n  public static final int MEMORY_SIZE = 32;\n  public static final int FIFO_DEPTH = 4;\n  public static final int PIO_NUM = 2;\n  public static final int SM_COUNT = 4;\n  public static final int INTR_NUM = 4;\n  public static final int DEFAULT_FREQUENCY = 1000000000;\n\n  // address map\n  public static final int IO_BANK0_BASE = 0x40014000;\n  public static final int PADS_BANK0_BASE = 0x4001c000;\n  public static final int PIO0_BASE = 0x50200000;\n  public static final int PIO0_EMU_BASE = PIO0_BASE + 0x08000000;\n  public static final int PIO1_BASE = 0x50300000;\n  public static final int PIO1_EMU_BASE = PIO1_BASE + 0x08000000;\n  public static final int EMULATOR_BASE = 0x58000000;\n\n  public static int getPIOBaseAddress(final int pioNum)\n  {\n    checkPioNum(pioNum, \"PIO index number\");\n    return pioNum == 0 ? PIO0_BASE : PIO1_BASE;\n  }\n\n  public static int getPIOEmuBaseAddress(final int pioNum)\n  {\n    checkPioNum(pioNum, \"PIO index number\");\n    return pioNum == 0 ? PIO0_EMU_BASE : PIO1_EMU_BASE;\n  }\n\n  // Emulator registers addressing\n  public static final int PICO_PWR_UP_VALUE = 0xa55a5aa5;\n\n  // GPIO registers addressing\n  public static final int IO_BANK0_GPIO0_CTRL_IRQOVER_LSB = 28;\n  public static final int IO_BANK0_GPIO0_CTRL_IRQOVER_BITS = 0x30000000;\n  public static final int IO_BANK0_GPIO0_CTRL_INOVER_LSB = 16;\n  public static final int IO_BANK0_GPIO0_CTRL_INOVER_BITS = 0x00030000;\n  public static final int IO_BANK0_GPIO0_CTRL_OEOVER_LSB = 12;\n  public static final int IO_BANK0_GPIO0_CTRL_OEOVER_BITS = 0x00003000;\n  public static final int IO_BANK0_GPIO0_CTRL_OUTOVER_LSB = 8;\n  public static final int IO_BANK0_GPIO0_CTRL_OUTOVER_BITS = 0x00000300;\n  public static final int IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB = 0;\n  public static final int IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS = 0x0000001f;\n  public static final int IO_BANK0_GPIO0_STATUS_IRQTOPROC_LSB = 26;\n  public static final int IO_BANK0_GPIO0_STATUS_IRQTOPROC_BITS = 0x04000000;\n  public static final int IO_BANK0_GPIO0_STATUS_IRQFROMPAD_LSB = 24;\n  public static final int IO_BANK0_GPIO0_STATUS_IRQFROMPAD_BITS = 0x01000000;\n  public static final int IO_BANK0_GPIO0_STATUS_INTOPERI_LSB = 19;\n  public static final int IO_BANK0_GPIO0_STATUS_INTOPERI_BITS = 0x00080000;\n  public static final int IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB = 17;\n  public static final int IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS = 0x00020000;\n  public static final int IO_BANK0_GPIO0_STATUS_OETOPAD_LSB = 13;\n  public static final int IO_BANK0_GPIO0_STATUS_OETOPAD_BITS = 0x00002000;\n  public static final int IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB = 12;\n  public static final int IO_BANK0_GPIO0_STATUS_OEFROMPERI_BITS = 0x00001000;\n  public static final int IO_BANK0_GPIO0_STATUS_OUTTOPAD_LSB = 9;\n  public static final int IO_BANK0_GPIO0_STATUS_OUTTOPAD_BITS = 0x00000200;\n  public static final int IO_BANK0_GPIO0_STATUS_OUTFROMPERI_LSB = 8;\n  public static final int IO_BANK0_GPIO0_STATUS_OUTFROMPERI_BITS = 0x00000100;\n  public static final int PADS_BANK0_GPIO0_IE_LSB = 6;\n  public static final int PADS_BANK0_GPIO0_IE_BITS = 0x00000040;\n\n  // PIO registers addressing\n  public static final int CTRL_CLKDIV_RESTART_LSB = 8;\n  public static final int CTRL_CLKDIV_RESTART_BITS = 0x00000f00;\n  public static final int CTRL_SM_RESTART_LSB = 4;\n  public static final int CTRL_SM_RESTART_BITS = 0x000000f0;\n  public static final int CTRL_SM_ENABLE_LSB = 0;\n  public static final int CTRL_SM_ENABLE_BITS = 0x0000000f;\n  public static final int FSTAT_TXEMPTY_LSB = 24;\n  public static final int FSTAT_TXEMPTY_BITS = 0x0f000000;\n  public static final int FSTAT_TXFULL_LSB = 16;\n  public static final int FSTAT_TXFULL_BITS = 0x000f0000;\n  public static final int FSTAT_RXEMPTY_LSB = 8;\n  public static final int FSTAT_RXEMPTY_BITS = 0x00000f00;\n  public static final int FSTAT_RXFULL_LSB = 0;\n  public static final int FSTAT_RXFULL_BITS = 0x0000000f;\n  public static final int FDEBUG_TXSTALL_LSB = 24;\n  public static final int FDEBUG_TXSTALL_BITS = 0x0f000000;\n  public static final int FDEBUG_TXOVER_LSB = 16;\n  public static final int FDEBUG_TXOVER_BITS = 0x000f0000;\n  public static final int FDEBUG_RXUNDER_LSB = 8;\n  public static final int FDEBUG_RXUNDER_BITS = 0x00000f00;\n  public static final int FDEBUG_RXSTALL_LSB = 0;\n  public static final int FDEBUG_RXSTALL_BITS = 0x0000000f;\n  public static final int FLEVEL_RX1_LSB = 12;\n  public static final int FLEVEL_RX1_BITS = 0x0000f000;\n  public static final int FLEVEL_TX1_LSB = 8;\n  public static final int FLEVEL_TX1_BITS = 0x00000f00;\n  public static final int FLEVEL_RX0_LSB = 4;\n  public static final int FLEVEL_RX0_BITS = 0x000000f0;\n  public static final int FLEVEL_TX0_LSB = 0;\n  public static final int FLEVEL_TX0_BITS = 0x0000000f;\n  public static final int SM0_CLKDIV_INT_LSB = 16;\n  public static final int SM0_CLKDIV_INT_BITS = 0xffff0000;\n  public static final int SM0_CLKDIV_FRAC_LSB = 8;\n  public static final int SM0_CLKDIV_FRAC_BITS = 0x0000ff00;\n  public static final int SM0_EXECCTRL_EXEC_STALLED_LSB = 31;\n  public static final int SM0_EXECCTRL_EXEC_STALLED_BITS = 0x80000000;\n  public static final int SM0_EXECCTRL_SIDE_EN_LSB = 30;\n  public static final int SM0_EXECCTRL_SIDE_EN_BITS = 0x40000000;\n  public static final int SM0_EXECCTRL_SIDE_PINDIR_LSB = 29;\n  public static final int SM0_EXECCTRL_SIDE_PINDIR_BITS = 0x20000000;\n  public static final int SM0_EXECCTRL_JMP_PIN_LSB = 24;\n  public static final int SM0_EXECCTRL_JMP_PIN_BITS = 0x1f000000;\n  public static final int SM0_EXECCTRL_OUT_EN_SEL_LSB = 19;\n  public static final int SM0_EXECCTRL_OUT_EN_SEL_BITS = 0x00f80000;\n  public static final int SM0_EXECCTRL_INLINE_OUT_EN_LSB = 18;\n  public static final int SM0_EXECCTRL_INLINE_OUT_EN_BITS = 0x00040000;\n  public static final int SM0_EXECCTRL_OUT_STICKY_LSB = 17;\n  public static final int SM0_EXECCTRL_OUT_STICKY_BITS = 0x00020000;\n  public static final int SM0_EXECCTRL_WRAP_TOP_LSB = 12;\n  public static final int SM0_EXECCTRL_WRAP_TOP_BITS = 0x0001f000;\n  public static final int SM0_EXECCTRL_WRAP_BOTTOM_LSB = 7;\n  public static final int SM0_EXECCTRL_WRAP_BOTTOM_BITS = 0x00000f80;\n  public static final int SM0_EXECCTRL_STATUS_SEL_LSB = 4;\n  public static final int SM0_EXECCTRL_STATUS_SEL_BITS = 0x00000010;\n  public static final int SM0_EXECCTRL_STATUS_N_LSB = 0;\n  public static final int SM0_EXECCTRL_STATUS_N_BITS = 0x0000000f;\n  public static final int SM0_SHIFTCTRL_FJOIN_RX_LSB = 31;\n  public static final int SM0_SHIFTCTRL_FJOIN_RX_BITS = 0x80000000;\n  public static final int SM0_SHIFTCTRL_FJOIN_TX_LSB = 30;\n  public static final int SM0_SHIFTCTRL_FJOIN_TX_BITS = 0x40000000;\n  public static final int SM0_SHIFTCTRL_PULL_THRESH_LSB = 25;\n  public static final int SM0_SHIFTCTRL_PULL_THRESH_BITS = 0x3e000000;\n  public static final int SM0_SHIFTCTRL_PUSH_THRESH_LSB = 20;\n  public static final int SM0_SHIFTCTRL_PUSH_THRESH_BITS = 0x01f00000;\n  public static final int SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB = 19;\n  public static final int SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS = 0x00080000;\n  public static final int SM0_SHIFTCTRL_IN_SHIFTDIR_LSB = 18;\n  public static final int SM0_SHIFTCTRL_IN_SHIFTDIR_BITS = 0x00040000;\n  public static final int SM0_SHIFTCTRL_AUTOPULL_LSB = 17;\n  public static final int SM0_SHIFTCTRL_AUTOPULL_BITS = 0x00020000;\n  public static final int SM0_SHIFTCTRL_AUTOPUSH_LSB = 16;\n  public static final int SM0_SHIFTCTRL_AUTOPUSH_BITS = 0x00010000;\n  public static final int SM0_PINCTRL_SIDESET_COUNT_LSB = 29;\n  public static final int SM0_PINCTRL_SIDESET_COUNT_BITS = 0xe0000000;\n  public static final int SM0_PINCTRL_SET_COUNT_LSB = 26;\n  public static final int SM0_PINCTRL_SET_COUNT_BITS = 0x1c000000;\n  public static final int SM0_PINCTRL_OUT_COUNT_LSB = 20;\n  public static final int SM0_PINCTRL_OUT_COUNT_BITS = 0x03f00000;\n  public static final int SM0_PINCTRL_IN_BASE_LSB = 15;\n  public static final int SM0_PINCTRL_IN_BASE_BITS = 0x000f8000;\n  public static final int SM0_PINCTRL_SIDESET_BASE_LSB = 10;\n  public static final int SM0_PINCTRL_SIDESET_BASE_BITS = 0x00007c00;\n  public static final int SM0_PINCTRL_SET_BASE_LSB = 5;\n  public static final int SM0_PINCTRL_SET_BASE_BITS = 0x000003e0;\n  public static final int SM0_PINCTRL_OUT_BASE_LSB = 0;\n  public static final int SM0_PINCTRL_OUT_BASE_BITS = 0x0000001f;\n\n  public static final int REGISTER_SERVER_DEFAULT_PORT_NUMBER = 2040;\n\n  // Instruction Origin\n  public static final int INSTR_ORIGIN_UNKNOWN = -3;\n  public static final int INSTR_ORIGIN_EXECD = -2;\n  public static final int INSTR_ORIGIN_FORCED = -1;\n  public static final int INSTR_ORIGIN_MEMORY = 0;\n\n  public enum GPIO_Function {\n    XIP(0, \"xip\"),\n    SPI(1, \"spi\"),\n    UART(2, \"uart\"),\n    I2C(3, \"i2c\"),\n    PWM(4, \"pwm\"),\n    SIO(5, \"sio\"),\n    PIO0(6, \"pio0\"),\n    PIO1(7, \"pio1\"),\n    GPCK(8, \"gpck\"),\n    USB(9, \"usb\"),\n    NULL(15, \"null\");\n\n    private static final GPIO_Function[] values = GPIO_Function.values();\n\n    private final int value;\n    private final String label;\n\n    private GPIO_Function(final int value, final String label)\n    {\n      this.value = value;\n      this.label = label;\n    }\n\n    public int getValue() { return value; }\n\n    public static GPIO_Function fromValue(final int value)\n    {\n      if ((value >= 0) && (value <= 9)) return values[value];\n      if (value == 15) return NULL;\n      throw new IllegalArgumentException(\"value: \" + value);\n    }\n\n    public static GPIO_Function fromValue(final int value,\n                                          final GPIO_Function defaultValue)\n    {\n      try {\n        return fromValue(value);\n      } catch (final IllegalArgumentException e) {\n        return defaultValue;\n      }\n    }\n\n    @Override\n    public String toString()\n    {\n      return label;\n    }\n  };\n\n  public static void checkBit(final int bit)\n  {\n    if (bit < 0) {\n      throw new IllegalArgumentException(\"bit < 0: \" + bit);\n    }\n    if (bit > 31) {\n      throw new IllegalArgumentException(\"bit > 31: \" + bit);\n    }\n  }\n\n  public static void checkMSBLSB(final int msb, final int lsb)\n  {\n    if (lsb < 0) {\n      throw new IllegalArgumentException(\"lsb < 0: \" + lsb);\n    }\n    if (msb > 31) {\n      throw new IllegalArgumentException(\"msb > 31: \" + msb);\n    }\n    if (lsb > msb) {\n      throw new IllegalArgumentException(\"lsb > msb: \" + lsb + \" > \" + msb);\n    }\n  }\n\n  public static void checkFIFOAddr(final int address, final String label)\n  {\n    if (address < 0) {\n      throw new IllegalArgumentException(label + \" < 0\" + address);\n    }\n    if (address > (2 * FIFO_DEPTH) - 1) {\n      throw new IllegalArgumentException(label + \" > \" +\n                                         ((2 * FIFO_DEPTH) - 1) + \":\" +\n                                         address);\n    }\n  }\n\n  public static void checkGpioPin(final int pin, final String label)\n  {\n    if (pin < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + pin);\n    }\n    if (pin > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(label + \" > \" + (GPIO_NUM - 1) + \": \" +\n                                         pin);\n    }\n  }\n\n  public static void checkGpioPinsCount(final int count, final String label)\n  {\n    if (count < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + count);\n    }\n    if (count > GPIO_NUM) {\n      throw new IllegalArgumentException(label + \" > \" + GPIO_NUM + \": \" +\n                                         count);\n    }\n  }\n\n  public static void checkPioNum(final int pioNum, final String label)\n  {\n    if (pioNum < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + pioNum);\n    }\n    if (pioNum > PIO_NUM - 1) {\n      throw new IllegalArgumentException(label + \" > \" + (PIO_NUM - 1) + \": \" +\n                                         pioNum);\n    }\n  }\n\n  public static void checkSmMemAddr(final int address, final String label)\n  {\n    if (address < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + address);\n    }\n    if (address > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(label + \" > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         address);\n    }\n  }\n\n  public static void checkSmNum(final int smNum)\n  {\n    if (smNum < 0) {\n      throw new IllegalArgumentException(\"smNum < 0: \" + smNum);\n    }\n    if (smNum > SM_COUNT - 1) {\n      throw new IllegalArgumentException(\"smNum > \" + (SM_COUNT - 1) + \": \" +\n                                         smNum);\n    }\n  }\n\n  public static void checkIntrNum(final int intrNum, final String label)\n  {\n    if (intrNum < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + intrNum);\n    }\n    if (intrNum > INTR_NUM - 1) {\n      throw new IllegalArgumentException(label + \" > \" + (INTR_NUM - 1) + \": \" +\n                                         intrNum);\n    }\n  }\n\n  public static int checkBitCount(final int bitCount, final String label)\n  {\n    if (bitCount < 0) {\n      throw new IllegalArgumentException(label + \" < 0: \" + bitCount);\n    }\n    if (bitCount > 31) {\n      throw new IllegalArgumentException(label + \" > 31: \" + bitCount);\n    }\n    return bitCount > 0 ? bitCount : 32;\n  }\n\n  /**\n   * Functionally equivalent replacement for GNU GCC's built-in\n   * function __builtin_ctz().\n   *\n   * @return The number of trailing 0-bits in x, starting at\n   * the least significant bit position. If x is 0, the result is\n   * undefined.\n   */\n  public static int ctz(final int x)\n  {\n    int count = 0;\n    int shifted = x;\n    while (((shifted & 0x1) == 0x0) && (count < 32)) {\n      shifted >>>= 1;\n      count++;\n    }\n    return count;\n  }\n\n  public static int hwSetBits(final int oldBits, final int newBits,\n                              final int mask, final boolean xor)\n  {\n    return (mask & (xor ? oldBits ^ newBits : newBits)) | (~mask & oldBits);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Decoder.java",
    "content": "/*\n * @(#)Decoder.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.List;\n\n/**\n * Instruction Decoder\n */\npublic class Decoder\n{\n  public static class DecodeException extends Exception\n  {\n    private static final long serialVersionUID = -3754988538292081517L;\n\n    private final Instruction instruction;\n    private final int opCode;\n\n    private DecodeException()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    public DecodeException(final Instruction instruction, final int opCode)\n    {\n      super(\"decode failed: unsupported op-code: \" +\n            String.format(\"%04x\", opCode));\n      this.instruction = instruction;\n      this.opCode = opCode;\n    }\n\n    public Instruction getInstruction() { return instruction; }\n\n    public int getOpCode() { return opCode; }\n  }\n\n  private final Instructions instructions;\n\n  private class Instructions\n  {\n    private final Instruction.Jmp jmp;\n    private final Instruction.Wait wait;\n    private final Instruction.In in;\n    private final Instruction.Out out;\n    private final Instruction.Push push;\n    private final Instruction.Pull pull;\n    private final Instruction.Mov mov;\n    private final Instruction.Irq irq;\n    private final Instruction.Set set;\n    private final Instruction[] instructionSet;\n\n    public Instructions()\n    {\n      jmp = new Instruction.Jmp();\n      wait = new Instruction.Wait();\n      in = new Instruction.In();\n      out = new Instruction.Out();\n      push = new Instruction.Push();\n      pull = new Instruction.Pull();\n      mov = new Instruction.Mov();\n      irq = new Instruction.Irq();\n      set = new Instruction.Set();\n      instructionSet =\n        new Instruction[] { jmp, wait, in, out, push, pull, mov, irq, set };\n    }\n  }\n\n  public Decoder()\n  {\n    instructions = new Instructions();\n  }\n\n  public void reset()\n  {\n    for (final Instruction instruction : instructions.instructionSet) {\n      instruction.reset();\n    }\n  }\n\n  public Instruction decode(final short word,\n                            final int pinCtrlSidesetCount,\n                            final boolean execCtrlSideEn)\n    throws DecodeException\n  {\n    final Instruction instruction;\n    switch ((word >>> 13) & 0x7) {\n    case 0b000:\n      instruction = instructions.jmp;\n      break;\n    case 0b001:\n      instruction = instructions.wait;\n      break;\n    case 0b010:\n      instruction = instructions.in;\n      break;\n    case 0b011:\n      instruction = instructions.out;\n      break;\n    case 0b100:\n      if ((word & 0x80) == 0)\n        instruction = instructions.push;\n      else\n        instruction = instructions.pull;\n      break;\n    case 0b101:\n      instruction = instructions.mov;\n      break;\n    case 0b110:\n      instruction = instructions.irq;\n      break;\n    case 0b111:\n      instruction = instructions.set;\n      break;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n    return instruction.decode(word, pinCtrlSidesetCount, execCtrlSideEn);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Direction.java",
    "content": "/*\n * @(#)Direction.java 1.00 21/03/19\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Representation of a single GPIO pin's direction.\n */\npublic enum Direction\n{\n  IN(0, \"in\"),\n  OUT(1, \"out\");\n\n  private final int value;\n  private final String label;\n\n  private Direction(final int value, final String label)\n  {\n    this.value = value;\n    this.label = label;\n  }\n\n  public int getValue() { return value; }\n\n  public static Direction fromValue(final int value)\n  {\n    if (value == IN.value) return IN;\n    if (value == OUT.value) return OUT;\n    throw new IllegalArgumentException(\"value not a direction: \" + value);\n  }\n\n  public Direction inverse()\n  {\n    return this == IN ? OUT : IN;\n  }\n\n  @Override\n  public String toString()\n  {\n    return label;\n  }\n};\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/EmulationServer.java",
    "content": "/*\n * @(#)EmulationServer.java 1.00 21/03/27\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Arrays;\nimport java.util.List;\n\npublic class EmulationServer\n{\n  private static final String PRG_TITLE = \"EmulationServer\";\n  private static final String PRG_FULL_NAME = \"Emulation Server Version 0.1\";\n\n  private static final CmdOptions.FlagOptionDeclaration optVersion =\n    CmdOptions.createFlagOption(false, 'V', \"version\", CmdOptions.Flag.OFF,\n                                \"display version information and exit\");\n  private static final CmdOptions.FlagOptionDeclaration optHelp =\n    CmdOptions.createFlagOption(false, 'h', \"help\", CmdOptions.Flag.OFF,\n                                \"display this help text and exit\");\n  private static final CmdOptions.FlagOptionDeclaration optSilent =\n    CmdOptions.createFlagOption(false, 's', \"silent\", CmdOptions.Flag.OFF,\n                                \"print no info at all except fatal errors\");\n  private static final CmdOptions.FlagOptionDeclaration optVerbose =\n    CmdOptions.createFlagOption(false, 'v', \"verbose\", CmdOptions.Flag.OFF,\n                                \"print verbose information\");\n  private static final CmdOptions.IntegerOptionDeclaration optPort =\n    CmdOptions.createIntegerOption(\"PORT\", false, 'p', \"port\",\n                                   Constants.\n                                   REGISTER_SERVER_DEFAULT_PORT_NUMBER,\n                                   \"use PORT as server port number\");\n  private static final List<CmdOptions.OptionDeclaration<?>>\n    optionDeclarations =\n    Arrays.asList(new CmdOptions.OptionDeclaration<?>[]\n                  { optVersion, optHelp, optSilent, optVerbose, optPort });\n\n  private final PrintStream console;\n  private final CmdOptions options;\n\n  private EmulationServer(final PrintStream console, final String[] argv)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    options = parseArgs(argv);\n    if (options.getValue(optSilent) != CmdOptions.Flag.ON) {\n      printAbout();\n    }\n  }\n\n  private CmdOptions parseArgs(final String argv[])\n  {\n    final CmdOptions options;\n    try {\n      options = new CmdOptions(PRG_TITLE, PRG_FULL_NAME, null,\n                               optionDeclarations);\n      options.parse(argv);\n      checkValidity(options);\n    } catch (final CmdOptions.ParseException e) {\n      console.println(e.getMessage());\n      System.exit(-1);\n      throw new InternalError();\n    }\n    if (options.getValue(optVersion) == CmdOptions.Flag.ON) {\n      console.println(PRG_FULL_NAME);\n      console.println(Constants.getEmulatorIdAndVersionWithOs());\n      System.exit(0);\n      throw new InternalError();\n    }\n    if (options.getValue(optHelp) == CmdOptions.Flag.ON) {\n      console.println(options.getFullInfo());\n      System.exit(0);\n      throw new InternalError();\n    }\n    return options;\n  }\n\n  private void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int port = options.getValue(optPort);\n    if ((port < 0) || (port > 65535)) {\n      throw new CmdOptions.\n        ParseException(\"PORT must be in the range 0…65535\");\n    }\n    if ((options.getValue(optSilent) == CmdOptions.Flag.ON) &&\n        (options.getValue(optVerbose) == CmdOptions.Flag.ON)) {\n      throw new CmdOptions.\n        ParseException(\"either 'silent' or 'verbose' can be activated\");\n    }\n  }\n\n  private void printAbout()\n  {\n    console.printf(\"%s%n%s%n%s%n\",\n                   \"Emulation Server Daemon\",\n                   Constants.getEmulatorIdAndVersionWithOs(),\n                   Constants.getCmdLineCopyrightNotice());\n  }\n\n  private void run()\n  {\n    try {\n      final Emulator emulator = new Emulator(console);\n      final LocalAddressSpace memory = new LocalAddressSpace(emulator);\n      final int port = options.getValue(optPort);\n      final RemoteAddressSpaceServer server =\n        new RemoteAddressSpaceServer(console, memory, port);\n      if (options.getValue(optSilent) != CmdOptions.Flag.ON) {\n        console.println(\"started emulation server at port \" + port);\n      }\n    } catch (final IOException e) {\n      console.println(\"failed starting emulation server: \" +\n                      e.getMessage());\n      System.exit(-1);\n    }\n  }\n\n  public static void main(final String argv[])\n  {\n    new EmulationServer(System.out, argv).run();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Emulator.java",
    "content": "/*\n * @(#)Emulator.java 1.00 21/03/19\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\n\n/**\n * Holds internal subsystems of core emulator.\n */\npublic class Emulator\n{\n  private final PrintStream console;\n  private final MasterClock masterClock;\n  private final GPIO gpio;\n  private final PIO pio0;\n  private final PIO pio1;\n\n  private Emulator()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public Emulator(final PrintStream console)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    masterClock = new MasterClock(console);\n    gpio = new GPIO(console, masterClock);\n    pio0 = gpio.getPIO0();\n    pio1 = gpio.getPIO1();\n  }\n\n  public PrintStream getConsole()\n  {\n    return console;\n  }\n\n  public MasterClock getMasterClock()\n  {\n    return masterClock;\n  }\n\n  public GPIO getGPIO()\n  {\n    return gpio;\n  }\n\n  public PIO getPIO0()\n  {\n    return pio0;\n  }\n\n  public PIO getPIO1()\n  {\n    return pio1;\n  }\n\n  public void reset()\n  {\n    masterClock.reset();\n    gpio.reset();\n    pio0.reset();\n    pio1.reset();\n  }\n\n  public void terminate()\n  {\n    masterClock.terminate();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/FIFO.java",
    "content": "/*\n * @(#)FIFO.java 1.00 21/02/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * A pair of an RX FIFO and a TX FIFO, each having a capacity of DEPTH\n * words of 32 bits.  One of the FIFOs' capacity can be reconfigured\n * to be joined with the capacity of the other FIFO, thus resulting in\n * 8 words of capacity for that FIFO and leaving no capacity left for\n * the other FIFO.\n */\npublic class FIFO implements Constants\n{\n  private static int JOINED_FIFO_DEPTH = FIFO_DEPTH + FIFO_DEPTH;\n\n  private static enum Mode\n  {\n    JoinNone(false, false, FIFO_DEPTH, FIFO_DEPTH),\n    JoinTX(true, false, JOINED_FIFO_DEPTH, 0),\n    JoinRX(false, true, 0, JOINED_FIFO_DEPTH),\n    JoinBoth(true, true, 0, 0);\n\n    private final boolean joinTX;\n    private final boolean joinRX;\n    private final int txSize;\n    private final int rxSize;\n\n    private Mode(final boolean joinTX, final boolean joinRX,\n                 final int txSize, final int rxSize)\n    {\n      this.joinTX = joinTX;\n      this.joinRX = joinRX;\n      this.txSize = txSize;\n      this.rxSize = rxSize;\n    }\n\n    private boolean isJoinTX() { return joinTX; }\n    private boolean isJoinRX() { return joinRX; }\n    private int getTXSize() { return txSize; }\n    private int getRXSize() { return rxSize; }\n\n    private int incPtrTX(final int ptr)\n    {\n      return\n        txSize == 0 ? 0 :\n        (ptr + 1) & (txSize - 1);\n    }\n\n    private int incPtrRX(final int ptr)\n    {\n      final int rxOffset = JOINED_FIFO_DEPTH - rxSize;\n      return\n        rxSize == 0 ? 0 :\n        ((ptr + 1) & (rxSize - 1)) + rxOffset;\n    }\n\n    private static Mode fromJoins(final boolean joinTX, final boolean joinRX)\n    {\n      return\n        joinTX ? (joinRX ? JoinBoth : JoinTX) : (joinRX ? JoinRX : JoinNone);\n    }\n  }\n\n  private final int smNum;\n  private final IRQ irq;\n  private int[] memory;\n  private Mode mode;\n  private int txReadPtr;\n  private int txWritePtr;\n  private boolean txFull;\n  private int rxReadPtr;\n  private int rxWritePtr;\n  private boolean rxFull;\n  private boolean regFDEBUG_TXSTALL; // one of bits 27:24 of FDEBUG\n  private boolean regFDEBUG_TXOVER; // one of bits 19:16 of FDEBUG\n  private boolean regFDEBUG_RXUNDER; // one of bits 11:8 of FDEBUG\n  private boolean regFDEBUG_RXSTALL; // one of bits 3:0 of FDEBUG\n\n  public FIFO(final int smNum, final IRQ irq)\n  {\n    Constants.checkSmNum(smNum);\n    if (irq == null) {\n      throw new NullPointerException(\"irq\");\n    }\n    this.smNum = smNum;\n    this.irq = irq;\n    memory = new int[JOINED_FIFO_DEPTH];\n    reset();\n  }\n\n  public synchronized void reset()\n  {\n    reset(false, false);\n  }\n\n  private void reset(final boolean joinTX, final boolean joinRX)\n  {\n    for (int index = 0; index < memory.length; index++) {\n      memory[index] = 0;\n    }\n    mode = Mode.fromJoins(joinTX, joinRX);\n    regFDEBUG_TXSTALL = false;\n    regFDEBUG_TXOVER = false;\n    regFDEBUG_RXUNDER = false;\n    regFDEBUG_RXSTALL = false;\n    txReadPtr = joinTX && joinRX ? -1 : 0;\n    txWritePtr = txReadPtr;\n    txFull = joinRX;\n    rxReadPtr = joinTX && joinRX ? -1 : (joinRX ? 0 : FIFO_DEPTH);\n    rxWritePtr = rxReadPtr;\n    rxFull = joinTX;\n    irq.setRxNEmpty(smNum, !fstatRxEmpty());\n    irq.setTxNFull(smNum, !fstatTxFull());\n    notifyAll();\n  }\n\n  public synchronized void setJoinRX(final boolean join)\n  {\n    if (mode.isJoinRX() == join) return;\n    reset(mode.isJoinTX(), join);\n  }\n\n  public boolean getJoinRX()\n  {\n    return mode.isJoinRX();\n  }\n\n  private int getRXSize()\n  {\n    return mode.getRXSize();\n  }\n\n  public synchronized int getRXReadPointer()\n  {\n    return rxReadPtr;\n  }\n\n  public synchronized boolean fstatRxFull()\n  {\n    // bit 0, 1, 2 or 3 (for SM_0…SM_3) of FSTAT\n    return rxFull;\n  }\n\n  public synchronized boolean fstatRxEmpty()\n  {\n    // bit 8, 9, 10 or 11 (for SM_0…SM_3) of FSTAT\n    return (rxReadPtr == rxWritePtr) && !rxFull;\n  }\n\n  public synchronized int getRXLevel()\n  {\n    final int rxSize = mode.getRXSize();\n    return\n      rxFull ? rxSize : (rxSize + rxWritePtr - rxReadPtr) & (rxSize - 1);\n  }\n\n  /**\n   * @return &lt;code&gt;true&lt;/code&gt; if the operation succeeded.\n   */\n  public synchronized boolean rxPush(final int value, final boolean stallIfFull)\n  {\n    final boolean modified;\n    if (!fstatRxFull()) {\n      memory[rxWritePtr] = value;\n      rxWritePtr = mode.incPtrRX(rxWritePtr);\n      rxFull = rxWritePtr == rxReadPtr;\n      modified = true;\n    } else {\n      if (stallIfFull) {\n        regFDEBUG_RXSTALL = true;\n      }\n      modified = false;\n    }\n    irq.setRxNEmpty(smNum, !fstatRxEmpty());\n    notifyAll();\n    return modified;\n  }\n\n  public synchronized int rxDMARead()\n  {\n    final int value;\n    if (!fstatRxEmpty()) {\n      value = memory[rxReadPtr];\n      rxReadPtr = mode.incPtrRX(rxReadPtr);\n      rxFull = false;\n    } else {\n      regFDEBUG_RXUNDER = true;\n      value = 0;\n    }\n    irq.setRxNEmpty(smNum, !fstatRxEmpty());\n    notifyAll();\n    return value;\n  }\n\n  public boolean isRXUnder()\n  {\n    return regFDEBUG_RXUNDER;\n  }\n\n  public void clearRXUnder()\n  {\n    regFDEBUG_RXUNDER = false;\n  }\n\n  public boolean isRXStall()\n  {\n    return regFDEBUG_RXSTALL;\n  }\n\n  public void clearRXStall()\n  {\n    regFDEBUG_RXSTALL = false;\n  }\n\n  public synchronized void setJoinTX(final boolean join)\n  {\n    if (mode.isJoinTX() == join) return;\n    reset(join, mode.isJoinRX());\n  }\n\n  public boolean getJoinTX()\n  {\n    return mode.isJoinTX();\n  }\n\n  public synchronized int getTXReadPointer()\n  {\n    return txReadPtr;\n  }\n\n  public synchronized boolean fstatTxFull()\n  {\n    // bit 16, 17, 18 or 19 (for SM_0…SM_3) of FSTAT\n    return txFull;\n  }\n\n  public synchronized boolean fstatTxEmpty()\n  {\n    // bit 24, 25, 26 or 27 (for SM_0…SM_3) of FSTAT\n    return (txReadPtr == txWritePtr) && !txFull;\n  }\n\n  public synchronized int getTXLevel()\n  {\n    final int txSize = mode.getTXSize();\n    return\n      txFull ? txSize : (txSize + txWritePtr - txReadPtr) & (txSize - 1);\n  }\n\n  public synchronized int txPull(final boolean stallIfEmpty)\n  {\n    final int value;\n    if (!fstatTxEmpty()) {\n      value = memory[txReadPtr];\n      txReadPtr = mode.incPtrTX(txReadPtr);\n      txFull = false;\n    } else {\n      value = 0;\n      if (stallIfEmpty) {\n        regFDEBUG_TXSTALL = true;\n      }\n    }\n    irq.setTxNFull(smNum, !fstatTxFull());\n    notifyAll();\n    return value;\n  }\n\n  public synchronized void txDMAWrite(final int value)\n  {\n    if (!fstatTxFull()) {\n      memory[txWritePtr] = value;\n      txWritePtr = mode.incPtrTX(txWritePtr);\n      txFull = txWritePtr == txReadPtr;\n    } else {\n      // overwrite most recent value\n      if (txWritePtr >= 0) {\n        memory[txWritePtr] = value;\n      }\n      regFDEBUG_TXOVER = true;\n    }\n    irq.setTxNFull(smNum, !fstatTxFull());\n    notifyAll();\n  }\n\n  public boolean isTXOver()\n  {\n    return regFDEBUG_TXOVER;\n  }\n\n  public void clearTXOver()\n  {\n    regFDEBUG_TXOVER = false;\n  }\n\n  public boolean isTXStall()\n  {\n    return regFDEBUG_TXSTALL;\n  }\n\n  public void clearTXStall()\n  {\n    regFDEBUG_TXSTALL = false;\n  }\n\n  public int getMemValue(final int address)\n  {\n    Constants.checkFIFOAddr(address, \"address\");\n    return memory[address];\n  }\n\n  public void setMemValue(final int address, final int value)\n  {\n    Constants.checkFIFOAddr(address, \"address\");\n    memory[address] = value;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/GPIO.java",
    "content": "/*\n * @(#)GPIO.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\nimport java.util.function.Function;\n\n/**\n * General-Purpose Set of 32 Peripheral I/O Terminals\n */\npublic class GPIO implements Constants\n{\n  private enum Override\n  {\n    BYPASS((value) -> value, (value) -> value),\n    INVERT((value) -> value.inverse(), (value) -> value.inverse()),\n    ALWAYS_LOW((value) -> Bit.LOW, (value) -> Direction.IN),\n    ALWAYS_HIGH((value) -> Bit.HIGH, (value) -> Direction.OUT);\n\n    private static final Override[] values = Override.values();\n\n    private final Function<Bit, Bit> fnBit;\n    private final Function<Direction, Direction> fnDirection;\n\n    private Override(final Function<Bit, Bit> fnBit,\n                     final Function<Direction, Direction> fnDirection)\n    {\n      if (fnBit == null) throw new NullPointerException(\"fnBit\");\n      this.fnBit = fnBit;\n      if (fnDirection == null) throw new NullPointerException(\"fnDirection\");\n      this.fnDirection = fnDirection;\n    }\n\n    public Bit apply(final Bit value)\n    {\n      return fnBit.apply(value);\n    }\n\n    public Direction apply(final Direction value)\n    {\n      return fnDirection.apply(value);\n    }\n\n    public static Override fromValue(final int value)\n    {\n      if (value < 0) {\n        final String message = String.format(\"value < 0: %d\", value);\n        throw new IllegalArgumentException(message);\n      }\n      if (value > values.length) {\n        final String message =\n          String.format(\"value >= %d: %d\", values.length, value);\n        throw new IllegalArgumentException(message);\n      }\n      return values[value];\n    }\n\n    public int getValue() { return ordinal(); }\n  }\n\n  private static class Terminal\n  {\n    private int num;\n    private GPIO_Function function;\n    private Override irqOverride;\n    private Override inputOverride;\n    private Override oeOverride;\n    private Override outputOverride;\n    private Bit externalInput;\n\n    private Terminal()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Terminal(final int num)\n    {\n      Constants.checkGpioPin(num, \"GPIO port\");\n      this.num = num;\n      reset();\n    }\n\n    public void reset()\n    {\n      function = GPIO_Function.NULL;\n      irqOverride = Override.BYPASS;\n      inputOverride = Override.BYPASS;\n      oeOverride = Override.BYPASS;\n      outputOverride = Override.BYPASS;\n      externalInput = Bit.LOW;\n    }\n\n    private Bit getPadIn(final Bit outBeforeOverride,\n                         final Direction oeBeforeOverride)\n    {\n      /*\n       * Loopback GPIO output as pad input, if a PIO drives this GPIO\n       * pin as output, while listening to this GPIO pin as input pin.\n       *\n       * See comment in file pico-examples/pio/spi/spi_loopback.c:\n       *\n       *   #define PIN_MISO 16 // same as MOSI, so we get loopback\n       *\n       * Note that, as a result from loopback, a PIO may even observe\n       * the other PIO's GPIO pad output.\n       */\n      final Direction oeAfterOverride = getOeAfterOverride(oeBeforeOverride);\n      return\n        oeAfterOverride == Direction.OUT ?\n        getOutAfterOverride(outBeforeOverride) :\n        externalInput;\n    }\n\n    private Bit getInputAfterOverride(final Bit outBeforeOverride,\n                                      final Direction oeBeforeOverride)\n    {\n      final Bit padIn = getPadIn(outBeforeOverride, oeBeforeOverride);\n      return inputOverride.apply(padIn);\n    }\n\n    private Bit getIrqAfterOverride(final Bit outBeforeOverride,\n                                    final Direction oeBeforeOverride)\n    {\n      final Bit padIn = getPadIn(outBeforeOverride, oeBeforeOverride);\n      return irqOverride.apply(padIn);\n    }\n\n    private Direction getOeAfterOverride(final Direction oeBeforeOverride)\n    {\n      return oeOverride.apply(oeBeforeOverride);\n    }\n\n    private Bit getOutAfterOverride(final Bit outBeforeOverride)\n    {\n      return outputOverride.apply(outBeforeOverride);\n    }\n  }\n\n  private final PrintStream console;\n  private final PIO pio0;\n  private final PIO pio1;\n  private final Terminal[] terminals;\n  private int regINPUT_SYNC_BYPASS; // bits 0…31 of INPUT_SYNC_BYPASS\n                                    // (contents currently ignored)\n\n  private GPIO()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public GPIO(final PrintStream console, final MasterClock masterClock)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    if (masterClock == null) {\n      throw new NullPointerException(\"masterClock\");\n    }\n    this.console = console;\n    pio0 = new PIO(0, console, masterClock, this);\n    pio1 = new PIO(1, console, masterClock, this);\n    terminals = new Terminal[GPIO_NUM];\n    for (int port = 0; port < terminals.length; port++) {\n      terminals[port] = new Terminal(port);\n    }\n    reset();\n  }\n\n  public void reset()\n  {\n    for (int port = 0; port < terminals.length; port++) {\n      terminals[port].reset();\n    }\n  }\n\n  public PIO getPIO0() { return pio0; }\n\n  public PIO getPIO1() { return pio1; }\n\n  public synchronized int getGPIO_PADIN()\n  {\n    int status = 0x0;\n    for (int port = 0; port < terminals.length; port++) {\n      status <<= 0x1;\n      status |= terminals[terminals.length - 1 - port].externalInput.getValue();\n    }\n    return status;\n  }\n\n  public synchronized void setGPIO_PADIN(final int bits, final int mask,\n                                         final boolean xor)\n  {\n    final int status = Constants.hwSetBits(getGPIO_PADIN(), bits, mask, xor);\n    for (int port = 0; port < terminals.length; port++) {\n      terminals[port].externalInput =\n        Bit.fromValue((status >>> port) & 0x1);\n    }\n  }\n\n  /**\n   * Set GPIOx_CTRL_FUNCSEL to 6 (for PIO0) or 7 (for PIO1), see\n   * Sect. 2.19.2. \"Function Select\" of RP2040 datasheet for details.\n   */\n  public void setFunction(final int gpio, final GPIO_Function fn)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    if (fn == null) {\n      throw new NullPointerException(\"fn\");\n    }\n    terminals[gpio].function = fn;\n  }\n\n  private GPIO_Function getFunction(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    return terminals[gpio].function;\n  }\n\n  public void setCTRL(final int gpio, final int value,\n                      final int mask, final boolean xor)\n  {\n    final int ctrl = Constants.hwSetBits(getCTRL(gpio), value, mask, xor);\n    final Terminal terminal = terminals[gpio];\n\n    final Override irqOverride =\n      Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_IRQOVER_BITS) >>\n                         IO_BANK0_GPIO0_CTRL_IRQOVER_LSB);\n    terminal.irqOverride = irqOverride;\n\n    final Override inputOverride =\n      Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_INOVER_BITS) >>\n                         IO_BANK0_GPIO0_CTRL_INOVER_LSB);\n    terminal.inputOverride = inputOverride;\n\n    final Override oeOverride =\n      Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_OEOVER_BITS) >>\n                         IO_BANK0_GPIO0_CTRL_OEOVER_LSB);\n    terminal.oeOverride = oeOverride;\n\n    final Override outputOverride =\n      Override.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_OUTOVER_BITS) >>\n                         IO_BANK0_GPIO0_CTRL_OUTOVER_LSB);\n    terminal.outputOverride = outputOverride;\n\n    final GPIO_Function fn =\n      GPIO_Function.fromValue((ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >>\n                              IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB,\n                              GPIO_Function.NULL);\n    terminal.function = fn;\n  }\n\n  public int getCTRL(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Terminal terminal = terminals[gpio];\n    return\n      (terminal.irqOverride.getValue() << IO_BANK0_GPIO0_CTRL_IRQOVER_LSB) |\n      (terminal.inputOverride.getValue() << IO_BANK0_GPIO0_CTRL_INOVER_LSB) |\n      (terminal.oeOverride.getValue() << IO_BANK0_GPIO0_CTRL_OEOVER_LSB) |\n      (terminal.outputOverride.getValue() << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB) |\n      (terminal.function.getValue() << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB);\n  }\n\n  public int getSTATUS(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    return\n      (getIrqToProc(gpio).getValue() << IO_BANK0_GPIO0_STATUS_IRQTOPROC_LSB) |\n      (getIrqFromPad(gpio).getValue() <<\n       IO_BANK0_GPIO0_STATUS_IRQFROMPAD_LSB) |\n      (getInToPeri(gpio).getValue() << IO_BANK0_GPIO0_STATUS_INTOPERI_LSB) |\n      (getInFromPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB) |\n      (getOeToPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OETOPAD_LSB) |\n      (getOeFromPeripheral(gpio).getValue() <<\n       IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB) |\n      (getOutToPad(gpio).getValue() << IO_BANK0_GPIO0_STATUS_OUTTOPAD_LSB) |\n      (getOutFromPeripheral(gpio).getValue() <<\n       IO_BANK0_GPIO0_STATUS_OUTFROMPERI_LSB);\n  }\n\n  private Bit getIrqToProc(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Bit outBeforeOverride = getOutFromPeripheral(gpio);\n    final Direction oeBeforeOverride = getOeFromPeripheral(gpio);\n    return terminals[gpio].getIrqAfterOverride(outBeforeOverride,\n                                               oeBeforeOverride);\n  }\n\n  private Bit getIrqFromPad(final int gpio)\n  {\n    /*\n     * TODO: Clarify: How does / should this method differ from method\n     * getInFromPad()?  It seems the RP2040 datasheet does not explain\n     * the difference between interrupt from pad\n     * (IO_BANK0_GPIOx_STATUS_IRQFROMPAD) and input signal from pad\n     * (IO_BANK0_GPIOx_STATUS_INFROMPAD).  Maybe, interrupt from pad\n     * is the value of an edge-triggered flip-flop (but how is the\n     * flip-flop reset again?), while input signal from pad is the\n     * pad's current logical value in terms of voltage level?\n     */\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Bit outBeforeOverride = getOutFromPeripheral(gpio);\n    final Direction oeBeforeOverride = getOeFromPeripheral(gpio);\n    return terminals[gpio].getPadIn(outBeforeOverride, oeBeforeOverride);\n   }\n\n  public int getPinsToPeri(final int base, final int count)\n  {\n    Constants.checkGpioPin(base, \"GPIO pin base\");\n    Constants.checkGpioPinsCount(count, \"GPIO pin count\");\n    int pins = 0;\n    for (int pin = 0; pin < count; pin++) {\n      pins = (pins << 0x1) | getInToPeri((base - pin - 1) & 0x1f).getValue();\n    }\n    return pins;\n  }\n\n  public Bit getInToPeri(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Bit outBeforeOverride = getOutFromPeripheral(gpio);\n    final Direction oeBeforeOverride = getOeFromPeripheral(gpio);\n    return terminals[gpio].getInputAfterOverride(outBeforeOverride,\n                                                 oeBeforeOverride);\n  }\n\n  private Bit getInFromPad(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Bit outBeforeOverride = getOutFromPeripheral(gpio);\n    final Direction oeBeforeOverride = getOeFromPeripheral(gpio);\n    return terminals[gpio].getPadIn(outBeforeOverride, oeBeforeOverride);\n  }\n\n  private Direction getOeToPad(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Direction oeBeforeOverride = getOeFromPeripheral(gpio);\n    return terminals[gpio].getOeAfterOverride(oeBeforeOverride);\n  }\n\n  private Direction getOeFromPeripheral(final int gpio)\n  {\n    switch (getFunction(gpio)) {\n    case XIP:\n    case SPI:\n    case UART:\n    case I2C:\n    case PWM:\n    case SIO:\n      // not implemented by this emulator\n      return Direction.IN;\n    case PIO0:\n      return pio0.getDirection(gpio);\n    case PIO1:\n      return pio1.getDirection(gpio);\n    case GPCK:\n    case USB:\n      // not implemented by this emulator\n      return Direction.IN;\n    case NULL:\n      return Direction.IN;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  private Bit getOutToPad(final int gpio)\n  {\n    Constants.checkGpioPin(gpio, \"GPIO port\");\n    final Bit outBeforeOverride = getOutFromPeripheral(gpio);\n    return terminals[gpio].getOutAfterOverride(outBeforeOverride);\n  }\n\n  private Bit getOutFromPeripheral(final int gpio)\n  {\n    switch (getFunction(gpio)) {\n    case XIP:\n    case SPI:\n    case UART:\n    case I2C:\n    case PWM:\n    case SIO:\n      // not implemented by this emulator\n      return Bit.LOW;\n    case PIO0:\n      return pio0.getLevel(gpio);\n    case PIO1:\n      return pio1.getLevel(gpio);\n    case GPCK:\n    case USB:\n      // not implemented by this emulator\n      return Bit.LOW;\n    case NULL:\n      return Bit.LOW;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  public void setInputSyncByPass(final int bits, final int mask,\n                                 final boolean xor)\n  {\n    regINPUT_SYNC_BYPASS =\n      Constants.hwSetBits(regINPUT_SYNC_BYPASS, bits, mask, xor);\n  }\n\n  public int getInputSyncByPass()\n  {\n    return regINPUT_SYNC_BYPASS;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/GPIOIOBank0Registers.java",
    "content": "/*\n * @(#)GPIOIOBank0Registers.java 1.00 21/03/20\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\n\n/**\n * Facade to the internal GPIO IO Bank 0 subsystem.  The layout of\n * registers follows the list of registers in Sect. 2.19.6 of the\n * RP2040 datasheet.  The facade is in particular intended for use by\n * the SDK.\n *\n * Note: This class implements only a subset of the RP2040 GPIO set of\n * registers, focussing on those registers that are relevant for PIO\n * simulation.  All GPIO registers specified by the RP2040 datasheet\n * are addressable, but writing to non-relevant registers or register\n * bits will have no effect, and reading from non-relevant registers\n * or register bits will return a constant value of 0.\n */\npublic abstract class GPIOIOBank0Registers extends RegisterSet\n{\n  public enum Regs implements RegistersDocs<Regs>\n  {\n    GPIO0_STATUS(\"GPIO status.\",\n                 new BitsInfo[] {\n                   new BitsInfo(null, 31, 27, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"IRQTOPROC\", 26, 26, null, BitsType.RO, 0),\n                   new BitsInfo(null, 25, 25, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"IRQFROMPAD\", 24, 24, null, BitsType.RO, 0),\n                   new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"INTOPERI\", 19, 19, null, BitsType.RO, 0),\n                   new BitsInfo(null, 18, 18, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"INFROMPAD\", 17, 17, null, BitsType.RO, 0),\n                   new BitsInfo(null, 16, 14, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"OETOPAD\", 13, 13, null, BitsType.RO, 0),\n                   new BitsInfo(\"OEFROMPERI\", 12, 12, null, BitsType.RO, 0),\n                   new BitsInfo(null, 11, 10, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"OUTTOPAD\", 9, 9, null, BitsType.RO, 0),\n                   new BitsInfo(\"OUTFROMPERI\", 8, 8, null, BitsType.RO, 0),\n                   new BitsInfo(null, 7, 0, null, BitsType.RESERVED, null)\n                 }),\n    GPIO0_CTRL(\"GPIO control.\",\n               new BitsInfo[] {\n                 new BitsInfo(null, 31, 30, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"IRQOVER\", 29, 28, null, BitsType.RW, 0),\n                 new BitsInfo(null, 27, 18, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"INOVER\", 17, 16, null, BitsType.RW, 0),\n                 new BitsInfo(null, 15, 14, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"OEOVER\", 13, 12, null, BitsType.RW, 0),\n                 new BitsInfo(null, 11, 10, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"OUTOVER\", 9, 8, null, BitsType.RW, 0),\n                 new BitsInfo(null, 7, 5, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"FUNCSEL\", 4, 0, null, BitsType.RW, 0x1f)\n               }),\n    GPIO1_STATUS(Regs.GPIO0_STATUS),\n    GPIO1_CTRL(Regs.GPIO0_CTRL),\n    GPIO2_STATUS(Regs.GPIO0_STATUS),\n    GPIO2_CTRL(Regs.GPIO0_CTRL),\n    GPIO3_STATUS(Regs.GPIO0_STATUS),\n    GPIO3_CTRL(Regs.GPIO0_CTRL),\n    GPIO4_STATUS(Regs.GPIO0_STATUS),\n    GPIO4_CTRL(Regs.GPIO0_CTRL),\n    GPIO5_STATUS(Regs.GPIO0_STATUS),\n    GPIO5_CTRL(Regs.GPIO0_CTRL),\n    GPIO6_STATUS(Regs.GPIO0_STATUS),\n    GPIO6_CTRL(Regs.GPIO0_CTRL),\n    GPIO7_STATUS(Regs.GPIO0_STATUS),\n    GPIO7_CTRL(Regs.GPIO0_CTRL),\n    GPIO8_STATUS(Regs.GPIO0_STATUS),\n    GPIO8_CTRL(Regs.GPIO0_CTRL),\n    GPIO9_STATUS(Regs.GPIO0_STATUS),\n    GPIO9_CTRL(Regs.GPIO0_CTRL),\n    GPIO10_STATUS(Regs.GPIO0_STATUS),\n    GPIO10_CTRL(Regs.GPIO0_CTRL),\n    GPIO11_STATUS(Regs.GPIO0_STATUS),\n    GPIO11_CTRL(Regs.GPIO0_CTRL),\n    GPIO12_STATUS(Regs.GPIO0_STATUS),\n    GPIO12_CTRL(Regs.GPIO0_CTRL),\n    GPIO13_STATUS(Regs.GPIO0_STATUS),\n    GPIO13_CTRL(Regs.GPIO0_CTRL),\n    GPIO14_STATUS(Regs.GPIO0_STATUS),\n    GPIO14_CTRL(Regs.GPIO0_CTRL),\n    GPIO15_STATUS(Regs.GPIO0_STATUS),\n    GPIO15_CTRL(Regs.GPIO0_CTRL),\n    GPIO16_STATUS(Regs.GPIO0_STATUS),\n    GPIO16_CTRL(Regs.GPIO0_CTRL),\n    GPIO17_STATUS(Regs.GPIO0_STATUS),\n    GPIO17_CTRL(Regs.GPIO0_CTRL),\n    GPIO18_STATUS(Regs.GPIO0_STATUS),\n    GPIO18_CTRL(Regs.GPIO0_CTRL),\n    GPIO19_STATUS(Regs.GPIO0_STATUS),\n    GPIO19_CTRL(Regs.GPIO0_CTRL),\n    GPIO20_STATUS(Regs.GPIO0_STATUS),\n    GPIO20_CTRL(Regs.GPIO0_CTRL),\n    GPIO21_STATUS(Regs.GPIO0_STATUS),\n    GPIO21_CTRL(Regs.GPIO0_CTRL),\n    GPIO22_STATUS(Regs.GPIO0_STATUS),\n    GPIO22_CTRL(Regs.GPIO0_CTRL),\n    GPIO23_STATUS(Regs.GPIO0_STATUS),\n    GPIO23_CTRL(Regs.GPIO0_CTRL),\n    GPIO24_STATUS(Regs.GPIO0_STATUS),\n    GPIO24_CTRL(Regs.GPIO0_CTRL),\n    GPIO25_STATUS(Regs.GPIO0_STATUS),\n    GPIO25_CTRL(Regs.GPIO0_CTRL),\n    GPIO26_STATUS(Regs.GPIO0_STATUS),\n    GPIO26_CTRL(Regs.GPIO0_CTRL),\n    GPIO27_STATUS(Regs.GPIO0_STATUS),\n    GPIO27_CTRL(Regs.GPIO0_CTRL),\n    GPIO28_STATUS(Regs.GPIO0_STATUS),\n    GPIO28_CTRL(Regs.GPIO0_CTRL),\n    GPIO29_STATUS(Regs.GPIO0_STATUS),\n    GPIO29_CTRL(Regs.GPIO0_CTRL),\n    INTR0(\"Raw interrupts.\", createBitsInfo(0, null)),\n    INTR1(\"Raw interrupts.\", createBitsInfo(1, null)),\n    INTR2(\"Raw interrupts.\", createBitsInfo(2, null)),\n    INTR3(\"Raw interrupts.\", createBitsInfo(3, null)),\n    PROC0_INTE0(\"Interrupt enable for proc0.\", createBitsInfo(0, BitsType.RW)),\n    PROC0_INTE1(\"Interrupt enable for proc0.\", createBitsInfo(1, BitsType.RW)),\n    PROC0_INTE2(\"Interrupt enable for proc0.\", createBitsInfo(2, BitsType.RW)),\n    PROC0_INTE3(\"Interrupt enable for proc0.\", createBitsInfo(3, BitsType.RW)),\n    PROC0_INTF0(\"Interrupt force for proc0.\", createBitsInfo(0, BitsType.RW)),\n    PROC0_INTF1(\"Interrupt force for proc0.\", createBitsInfo(1, BitsType.RW)),\n    PROC0_INTF2(\"Interrupt force for proc0.\", createBitsInfo(2, BitsType.RW)),\n    PROC0_INTF3(\"Interrupt force for proc0.\", createBitsInfo(3, BitsType.RW)),\n    PROC0_INTS0(\"Interrupt status for proc0.\", createBitsInfo(0, BitsType.RO)),\n    PROC0_INTS1(\"Interrupt status for proc0.\", createBitsInfo(1, BitsType.RO)),\n    PROC0_INTS2(\"Interrupt status for proc0.\", createBitsInfo(2, BitsType.RO)),\n    PROC0_INTS3(\"Interrupt status for proc0.\", createBitsInfo(3, BitsType.RO)),\n    PROC1_INTE0(\"Interrupt enable for proc1.\", createBitsInfo(0, BitsType.RW)),\n    PROC1_INTE1(\"Interrupt enable for proc1.\", createBitsInfo(1, BitsType.RW)),\n    PROC1_INTE2(\"Interrupt enable for proc1.\", createBitsInfo(2, BitsType.RW)),\n    PROC1_INTE3(\"Interrupt enable for proc1.\", createBitsInfo(3, BitsType.RW)),\n    PROC1_INTF0(\"Interrupt force for proc1.\", createBitsInfo(0, BitsType.RW)),\n    PROC1_INTF1(\"Interrupt force for proc1.\", createBitsInfo(1, BitsType.RW)),\n    PROC1_INTF2(\"Interrupt force for proc1.\", createBitsInfo(2, BitsType.RW)),\n    PROC1_INTF3(\"Interrupt force for proc1.\", createBitsInfo(3, BitsType.RW)),\n    PROC1_INTS0(\"Interrupt status for proc1.\", createBitsInfo(0, BitsType.RO)),\n    PROC1_INTS1(\"Interrupt status for proc1.\", createBitsInfo(1, BitsType.RO)),\n    PROC1_INTS2(\"Interrupt status for proc1.\", createBitsInfo(2, BitsType.RO)),\n    PROC1_INTS3(\"Interrupt status for proc1.\", createBitsInfo(3, BitsType.RO)),\n    DORMANT_WAKE_INTE0(\"Interrupt enable for dormant wake.\",\n                       createBitsInfo(0, BitsType.RW)),\n    DORMANT_WAKE_INTE1(\"Interrupt enable for dormant wake.\",\n                       createBitsInfo(1, BitsType.RW)),\n    DORMANT_WAKE_INTE2(\"Interrupt enable for dormant wake.\",\n                       createBitsInfo(2, BitsType.RW)),\n    DORMANT_WAKE_INTE3(\"Interrupt enable for dormant wake.\",\n                       createBitsInfo(3, BitsType.RW)),\n    DORMANT_WAKE_INTF0(\"Interrupt force for dormant wake.\",\n                       createBitsInfo(0, BitsType.RW)),\n    DORMANT_WAKE_INTF1(\"Interrupt force for dormant wake.\",\n                       createBitsInfo(1, BitsType.RW)),\n    DORMANT_WAKE_INTF2(\"Interrupt force for dormant wake.\",\n                       createBitsInfo(2, BitsType.RW)),\n    DORMANT_WAKE_INTF3(\"Interrupt force for dormant wake.\",\n                       createBitsInfo(3, BitsType.RW)),\n    DORMANT_WAKE_INTS0(\"Interrupt status for dormant wake.\",\n                       createBitsInfo(0, BitsType.RO)),\n    DORMANT_WAKE_INTS1(\"Interrupt status for dormant wake.\",\n                       createBitsInfo(1, BitsType.RO)),\n    DORMANT_WAKE_INTS2(\"Interrupt status for dormant wake.\",\n                       createBitsInfo(2, BitsType.RO)),\n    DORMANT_WAKE_INTS3(\"Interrupt status for dormant wake.\",\n                       createBitsInfo(3, BitsType.RO));\n\n    private static List<BitsInfo> createBitsInfo(final int octett,\n                                                 final BitsType forcedBitsType)\n    {\n      if (octett < 0) {\n        throw new IllegalArgumentException(\"octett < 0: \" + octett);\n      }\n      if (octett > 3) {\n        throw new IllegalArgumentException(\"octett > 3: \" + octett);\n      }\n      final int gpioMin = octett << 3;\n      final int gpioCount = octett < 3 ? 8 : 6;\n      final int gpioMax = gpioCount << 2;\n      final List<BitsInfo> bitsInfo =\n        IntStream.rangeClosed(0, gpioMax - 1).boxed()\n        .map(n -> new BitsInfo(\"GPIO\" + (((gpioMax - 1 - n) >> 2) + gpioMin) +\n                               \"_\" + ((n & 2) == 0 ? \"EDGE\" : \"LEVEL\") +\n                               \"_\" + ((n & 1) == 0 ? \"HIGH\" : \"LOW\"),\n                               gpioMax - 1 - n, gpioMax - 1 - n, null,\n                               forcedBitsType != null ? forcedBitsType :\n                               ((n & 2) == 0 ? BitsType.WC : BitsType.RO), 0))\n        .collect(Collectors.toList());\n      if (octett < 3) {\n        return bitsInfo;\n      } else {\n        final List<BitsInfo> paddedBitsInfo = new ArrayList<BitsInfo>();\n        paddedBitsInfo.add(new BitsInfo(null, 31, 24, null,\n                                        BitsType.RESERVED, null));\n        paddedBitsInfo.addAll(bitsInfo);\n        return paddedBitsInfo;\n      }\n    }\n\n    public static String getRegisterSetLabel()\n    {\n      return \"GPIO User Bank Pad Control Registers\";\n    }\n\n    public static String getRegisterSetDescription()\n    {\n      return\n        \"The GPIO user bank pad control registers as described in%n\" +\n        \"Sect. 2.19.6.3 of the RP2040 datasheet.%n\" +\n        \"Base address for this register set is%n\" +\n        String.format(\"0x%08x.%n\", PADS_BANK0_BASE);\n    }\n\n    private final RegisterDetails registerDetails;\n\n    private Regs()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Regs(final Regs ref)\n    {\n      this(ref.registerDetails);\n    }\n\n    private Regs(final Regs ref, final int smNum)\n    {\n      this(ref.registerDetails.createCopyForDifferentSm(smNum));\n    }\n\n    private Regs(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final RegisterDetails registerDetails)\n    {\n      this.registerDetails = registerDetails;\n    }\n\n    @Override\n    public String getInfo()\n    {\n      return registerDetails.getInfo();\n    }\n\n    @Override\n    public RegisterDetails getRegisterDetails()\n    {\n      return registerDetails;\n    }\n  }\n\n  protected static final Regs[] REGS = Regs.values();\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  protected <T extends Enum<T>> T[] getRegs() { return (T[])REGS; }\n\n  protected static final int GPIO_DATA_SIZE =\n    Regs.GPIO1_STATUS.ordinal() - Regs.GPIO0_STATUS.ordinal();\n\n  protected static final int PROC_INT_DATA_SIZE =\n    Regs.PROC1_INTE0.ordinal() - Regs.PROC0_INTE0.ordinal();\n\n  public static int getAddress(final GPIOIOBank0Registers.Regs register)\n  {\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    return IO_BANK0_BASE + 0x4 * register.ordinal();\n  }\n\n  public static int getGPIOAddress(final int gpioNum,\n                                   final GPIOIOBank0Registers.Regs register)\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    switch (register) {\n    case GPIO0_STATUS:\n    case GPIO0_CTRL:\n      break; // ok\n    default:\n      throw new IllegalArgumentException(\"register not one of GPIO0_*: \" +\n                                         register);\n    }\n    return\n      IO_BANK0_BASE + 0x4 * (register.ordinal() + gpioNum * GPIO_DATA_SIZE);\n  }\n\n  public static int getIntr(final int intrNum)\n  {\n    Constants.checkIntrNum(intrNum, \"INTR number\");\n    return IO_BANK0_BASE + 0x4 * (Regs.INTR0.ordinal() + intrNum);\n  }\n\n  public static int getProcIntE(final int pioNum, final int inteNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO number\");\n    Constants.checkIntrNum(inteNum, \"INTE number\");\n    final int regsOffs = pioNum * PROC_INT_DATA_SIZE + inteNum;\n    return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTE0.ordinal() + regsOffs);\n  }\n\n  public static int getProcIntF(final int pioNum, final int intfNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO number\");\n    Constants.checkIntrNum(intfNum, \"INTF number\");\n    final int regsOffs = pioNum * PROC_INT_DATA_SIZE + intfNum;\n    return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTF0.ordinal() + regsOffs);\n  }\n\n  public static int getProcIntS(final int pioNum, final int intsNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO number\");\n    Constants.checkIntrNum(intsNum, \"INTS number\");\n    final int regsOffs = pioNum * PROC_INT_DATA_SIZE + intsNum;\n    return IO_BANK0_BASE + 0x4 * (Regs.PROC0_INTS0.ordinal() + regsOffs);\n  }\n\n  public static int getDormantWakeIntE(final int inteNum)\n  {\n    Constants.checkIntrNum(inteNum, \"Dormant Wake INTE number\");\n    return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTE0.ordinal() + inteNum);\n  }\n\n  public static int getDormantWakeIntF(final int intfNum)\n  {\n    Constants.checkIntrNum(intfNum, \"Dormant Wake INTF number\");\n    return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTF0.ordinal() + intfNum);\n  }\n\n  public static int getDormantWakeIntS(final int intsNum)\n  {\n    Constants.checkIntrNum(intsNum, \"Dormant Wake INTS number\");\n    return IO_BANK0_BASE + 0x4 * (Regs.DORMANT_WAKE_INTS0.ordinal() + intsNum);\n  }\n\n  public GPIOIOBank0Registers()\n  {\n    super(\"GPIOIOBank0\", IO_BANK0_BASE);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/GPIOIOBank0RegistersImpl.java",
    "content": "/*\n * @(#)GPIOIOBank0RegistersImpl.java 1.00 21/03/20\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Facade to the internal GPIO IO Bank 0 subsystem.  The layout of\n * registers follows the list of registers in Sect. 2.19.6 of the\n * RP2040 datasheet.  The facade is in particular intended for use by\n * the SDK.\n *\n * Note: This class implements only a subset of the RP2040 GPIO set of\n * registers, focussing on those registers that are relevant for PIO\n * simulation.  All GPIO registers specified by the RP2040 datasheet\n * are addressable, but writing to non-relevant registers or register\n * bits will have no effect, and reading from non-relevant registers\n * or register bits will return a constant value of 0.\n */\npublic class GPIOIOBank0RegistersImpl extends GPIOIOBank0Registers\n{\n  public static final int GPIO_DATA_SIZE =\n    Regs.GPIO1_STATUS.ordinal() - Regs.GPIO0_STATUS.ordinal();\n\n  public static final int PROC_INT_DATA_SIZE =\n    Regs.PROC1_INTE0.ordinal() - Regs.PROC0_INTE0.ordinal();\n\n  private final GPIO gpio;\n\n  public GPIOIOBank0RegistersImpl(final GPIO gpio)\n  {\n    if (gpio == null) {\n      throw new NullPointerException(\"gpio\");\n    }\n    this.gpio = gpio;\n  }\n\n  public GPIO getGPIO() { return gpio; }\n\n  @Override\n  public void writeRegister(final int regNum, final int value,\n                            final int mask, final boolean xor)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case GPIO0_STATUS:\n    case GPIO1_STATUS:\n    case GPIO2_STATUS:\n    case GPIO3_STATUS:\n    case GPIO4_STATUS:\n    case GPIO5_STATUS:\n    case GPIO6_STATUS:\n    case GPIO7_STATUS:\n    case GPIO8_STATUS:\n    case GPIO9_STATUS:\n    case GPIO10_STATUS:\n    case GPIO11_STATUS:\n    case GPIO12_STATUS:\n    case GPIO13_STATUS:\n    case GPIO14_STATUS:\n    case GPIO15_STATUS:\n    case GPIO16_STATUS:\n    case GPIO17_STATUS:\n    case GPIO18_STATUS:\n    case GPIO19_STATUS:\n    case GPIO20_STATUS:\n    case GPIO21_STATUS:\n    case GPIO22_STATUS:\n    case GPIO23_STATUS:\n    case GPIO24_STATUS:\n    case GPIO25_STATUS:\n    case GPIO26_STATUS:\n    case GPIO27_STATUS:\n    case GPIO28_STATUS:\n    case GPIO29_STATUS:\n      break; // read-only address\n    case GPIO0_CTRL:\n    case GPIO1_CTRL:\n    case GPIO2_CTRL:\n    case GPIO3_CTRL:\n    case GPIO4_CTRL:\n    case GPIO5_CTRL:\n    case GPIO6_CTRL:\n    case GPIO7_CTRL:\n    case GPIO8_CTRL:\n    case GPIO9_CTRL:\n    case GPIO10_CTRL:\n    case GPIO11_CTRL:\n    case GPIO12_CTRL:\n    case GPIO13_CTRL:\n    case GPIO14_CTRL:\n    case GPIO15_CTRL:\n    case GPIO16_CTRL:\n    case GPIO17_CTRL:\n    case GPIO18_CTRL:\n    case GPIO19_CTRL:\n    case GPIO20_CTRL:\n    case GPIO21_CTRL:\n    case GPIO22_CTRL:\n    case GPIO23_CTRL:\n    case GPIO24_CTRL:\n    case GPIO25_CTRL:\n    case GPIO26_CTRL:\n    case GPIO27_CTRL:\n    case GPIO28_CTRL:\n    case GPIO29_CTRL:\n      gpio.setCTRL((regNum - Regs.GPIO0_CTRL.ordinal()) / GPIO_DATA_SIZE,\n                   value, mask, xor);\n      break;\n    case INTR0:\n    case INTR1:\n    case INTR2:\n    case INTR3:\n      // TODO\n      break;\n    case PROC0_INTE0:\n    case PROC0_INTE1:\n    case PROC0_INTE2:\n    case PROC0_INTE3:\n    case PROC1_INTE0:\n    case PROC1_INTE1:\n    case PROC1_INTE2:\n    case PROC1_INTE3:\n      // TODO\n      break;\n    case PROC0_INTF0:\n    case PROC0_INTF1:\n    case PROC0_INTF2:\n    case PROC0_INTF3:\n    case PROC1_INTF0:\n    case PROC1_INTF1:\n    case PROC1_INTF2:\n    case PROC1_INTF3:\n      // TODO\n      break;\n    case PROC0_INTS0:\n    case PROC0_INTS1:\n    case PROC0_INTS2:\n    case PROC0_INTS3:\n    case PROC1_INTS0:\n    case PROC1_INTS1:\n    case PROC1_INTS2:\n    case PROC1_INTS3:\n      // TODO\n      break;\n    case DORMANT_WAKE_INTE0:\n    case DORMANT_WAKE_INTE1:\n    case DORMANT_WAKE_INTE2:\n    case DORMANT_WAKE_INTE3:\n      // TODO\n      break;\n    case DORMANT_WAKE_INTF0:\n    case DORMANT_WAKE_INTF1:\n    case DORMANT_WAKE_INTF2:\n    case DORMANT_WAKE_INTF3:\n      // TODO\n      break;\n    case DORMANT_WAKE_INTS0:\n    case DORMANT_WAKE_INTS1:\n    case DORMANT_WAKE_INTS2:\n    case DORMANT_WAKE_INTS3:\n      // TODO\n      break;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  @Override\n  public synchronized int readRegister(final int regNum)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case GPIO0_STATUS:\n    case GPIO1_STATUS:\n    case GPIO2_STATUS:\n    case GPIO3_STATUS:\n    case GPIO4_STATUS:\n    case GPIO5_STATUS:\n    case GPIO6_STATUS:\n    case GPIO7_STATUS:\n    case GPIO8_STATUS:\n    case GPIO9_STATUS:\n    case GPIO10_STATUS:\n    case GPIO11_STATUS:\n    case GPIO12_STATUS:\n    case GPIO13_STATUS:\n    case GPIO14_STATUS:\n    case GPIO15_STATUS:\n    case GPIO16_STATUS:\n    case GPIO17_STATUS:\n    case GPIO18_STATUS:\n    case GPIO19_STATUS:\n    case GPIO20_STATUS:\n    case GPIO21_STATUS:\n    case GPIO22_STATUS:\n    case GPIO23_STATUS:\n    case GPIO24_STATUS:\n    case GPIO25_STATUS:\n    case GPIO26_STATUS:\n    case GPIO27_STATUS:\n    case GPIO28_STATUS:\n    case GPIO29_STATUS:\n      return\n        gpio.getSTATUS((regNum - Regs.GPIO0_STATUS.ordinal()) / GPIO_DATA_SIZE);\n    case GPIO0_CTRL:\n    case GPIO1_CTRL:\n    case GPIO2_CTRL:\n    case GPIO3_CTRL:\n    case GPIO4_CTRL:\n    case GPIO5_CTRL:\n    case GPIO6_CTRL:\n    case GPIO7_CTRL:\n    case GPIO8_CTRL:\n    case GPIO9_CTRL:\n    case GPIO10_CTRL:\n    case GPIO11_CTRL:\n    case GPIO12_CTRL:\n    case GPIO13_CTRL:\n    case GPIO14_CTRL:\n    case GPIO15_CTRL:\n    case GPIO16_CTRL:\n    case GPIO17_CTRL:\n    case GPIO18_CTRL:\n    case GPIO19_CTRL:\n    case GPIO20_CTRL:\n    case GPIO21_CTRL:\n    case GPIO22_CTRL:\n    case GPIO23_CTRL:\n    case GPIO24_CTRL:\n    case GPIO25_CTRL:\n    case GPIO26_CTRL:\n    case GPIO27_CTRL:\n    case GPIO28_CTRL:\n    case GPIO29_CTRL:\n      return\n        gpio.getCTRL((regNum - Regs.GPIO0_CTRL.ordinal()) / GPIO_DATA_SIZE);\n    case INTR0:\n    case INTR1:\n    case INTR2:\n    case INTR3:\n      return 0; // TODO\n    case PROC0_INTE0:\n    case PROC0_INTE1:\n    case PROC0_INTE2:\n    case PROC0_INTE3:\n    case PROC1_INTE0:\n    case PROC1_INTE1:\n    case PROC1_INTE2:\n    case PROC1_INTE3:\n      return 0; // TODO\n    case PROC0_INTF0:\n    case PROC0_INTF1:\n    case PROC0_INTF2:\n    case PROC0_INTF3:\n    case PROC1_INTF0:\n    case PROC1_INTF1:\n    case PROC1_INTF2:\n    case PROC1_INTF3:\n      return 0; // TODO\n    case PROC0_INTS0:\n    case PROC0_INTS1:\n    case PROC0_INTS2:\n    case PROC0_INTS3:\n    case PROC1_INTS0:\n    case PROC1_INTS1:\n    case PROC1_INTS2:\n    case PROC1_INTS3:\n      return 0; // TODO\n    case DORMANT_WAKE_INTE0:\n    case DORMANT_WAKE_INTE1:\n    case DORMANT_WAKE_INTE2:\n    case DORMANT_WAKE_INTE3:\n      return 0; // TODO\n    case DORMANT_WAKE_INTF0:\n    case DORMANT_WAKE_INTF1:\n    case DORMANT_WAKE_INTF2:\n    case DORMANT_WAKE_INTF3:\n      return 0; // TODO\n    case DORMANT_WAKE_INTS0:\n    case DORMANT_WAKE_INTS1:\n    case DORMANT_WAKE_INTS2:\n    case DORMANT_WAKE_INTS3:\n      return 0; // TODO\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/GPIOPadsBank0Registers.java",
    "content": "/*\n * @(#)GPIOPadsBank0Registers.java 1.00 21/03/20\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.List;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\n\n/**\n * Facade to the internal GPIO Pads Bank 0 subsystem.  The layout of\n * registers follows the list of registers in Sect. 2.19.6 of the\n * RP2040 datasheet.  The facade is in particular intended for use by\n * the SDK.\n *\n * Note: This class implements only a subset of the RP2040 GPIO set of\n * registers, focussing on those registers that are relevant for PIO\n * simulation.  All GPIO registers specified by the RP2040 datasheet\n * are addressable, but writing to non-relevant registers or register\n * bits will have no effect, and reading from non-relevant registers\n * or register bits will return a constant value of 0.\n */\npublic abstract class GPIOPadsBank0Registers extends RegisterSet\n{\n  public enum Regs implements RegistersDocs<Regs>\n  {\n    VOLTAGE_SELECT(\"Voltage select.\",\n                   new BitsInfo[] {\n                     new BitsInfo(null, 31, 1, null, BitsType.RESERVED, null),\n                     new BitsInfo(null, 0, 0,  \"3.3V / 1.8V\", BitsType.RW, null)\n                   }),\n    GPIO0(\"Pad control register.\",\n          new BitsInfo[] {\n            new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null),\n            new BitsInfo(\"OD\", 7, 7, \"Output disable.\", BitsType.RW, 0),\n            new BitsInfo(\"IE\", 6, 6, \"Input enable.\", BitsType.RW, 1),\n            new BitsInfo(\"DRIVE\", 5, 4, \"Drive strength.\", BitsType.RW, 1),\n            new BitsInfo(\"PUE\", 3, 3, \"Pull up enable.\", BitsType.RW, 0),\n            new BitsInfo(\"PDE\", 2, 2, \"Pull down enable.\", BitsType.RW, 1),\n            new BitsInfo(\"SCHMITT\", 1, 1, \"Enable schmitt trigger\",\n                         BitsType.RW, 1),\n            new BitsInfo(\"SLEWFAST\", 0, 0, \"Slew rate control.\",\n                         BitsType.RW, 0)\n          }),\n    GPIO1(Regs.GPIO0),\n    GPIO2(Regs.GPIO0),\n    GPIO3(Regs.GPIO0),\n    GPIO4(Regs.GPIO0),\n    GPIO5(Regs.GPIO0),\n    GPIO6(Regs.GPIO0),\n    GPIO7(Regs.GPIO0),\n    GPIO8(Regs.GPIO0),\n    GPIO9(Regs.GPIO0),\n    GPIO10(Regs.GPIO0),\n    GPIO11(Regs.GPIO0),\n    GPIO12(Regs.GPIO0),\n    GPIO13(Regs.GPIO0),\n    GPIO14(Regs.GPIO0),\n    GPIO15(Regs.GPIO0),\n    GPIO16(Regs.GPIO0),\n    GPIO17(Regs.GPIO0),\n    GPIO18(Regs.GPIO0),\n    GPIO19(Regs.GPIO0),\n    GPIO20(Regs.GPIO0),\n    GPIO21(Regs.GPIO0),\n    GPIO22(Regs.GPIO0),\n    GPIO23(Regs.GPIO0),\n    GPIO24(Regs.GPIO0),\n    GPIO25(Regs.GPIO0),\n    GPIO26(Regs.GPIO0),\n    GPIO27(Regs.GPIO0),\n    GPIO28(Regs.GPIO0),\n    GPIO29(Regs.GPIO0),\n    SWCLK(\"Pad control register.\",\n          new BitsInfo[] {\n            new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null),\n            new BitsInfo(\"OD\", 7, 7, \"Output disable.\", BitsType.RW, 1),\n            new BitsInfo(\"IE\", 6, 6, \"Input enable.\", BitsType.RW, 1),\n            new BitsInfo(\"DRIVE\", 5, 4, \"Drive strength.\", BitsType.RW, 1),\n            new BitsInfo(\"PUE\", 3, 3, \"Pull up enable.\", BitsType.RW, 1),\n            new BitsInfo(\"PDE\", 2, 2, \"Pull down enable.\", BitsType.RW, 0),\n            new BitsInfo(\"SCHMITT\", 1, 1, \"Enable schmitt trigger\",\n                         BitsType.RW, 1),\n            new BitsInfo(\"SLEWFAST\", 0, 0, \"Slew rate control.\",\n                         BitsType.RW, 0)\n          }),\n    SWD(\"Pad control register.\",\n          new BitsInfo[] {\n            new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null),\n            new BitsInfo(\"OD\", 7, 7, \"Output disable.\", BitsType.RW, 0),\n            new BitsInfo(\"IE\", 6, 6, \"Input enable.\", BitsType.RW, 1),\n            new BitsInfo(\"DRIVE\", 5, 4, \"Drive strength.\", BitsType.RW, 1),\n            new BitsInfo(\"PUE\", 3, 3, \"Pull up enable.\", BitsType.RW, 1),\n            new BitsInfo(\"PDE\", 2, 2, \"Pull down enable.\", BitsType.RW, 0),\n            new BitsInfo(\"SCHMITT\", 1, 1, \"Enable schmitt trigger\",\n                         BitsType.RW, 1),\n            new BitsInfo(\"SLEWFAST\", 0, 0, \"Slew rate control.\",\n                         BitsType.RW, 0)\n          });\n\n    public static String getRegisterSetLabel()\n    {\n      return \"GPIO User Bank Pad Control Registers\";\n    }\n\n    public static String getRegisterSetDescription()\n    {\n      return\n        \"The GPIO user bank pad control registers as described in%n\" +\n        \"Sect. 2.19.6.3 of the RP2040 datasheet.%n\" +\n        \"Base address for this register set is%n\" +\n        String.format(\"0x%08x.%n\", PADS_BANK0_BASE);\n    }\n\n    private final RegisterDetails registerDetails;\n\n    private Regs()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Regs(final Regs ref)\n    {\n      this(ref.registerDetails);\n    }\n\n    private Regs(final Regs ref, final int smNum)\n    {\n      this(ref.registerDetails.createCopyForDifferentSm(smNum));\n    }\n\n    private Regs(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final RegisterDetails registerDetails)\n    {\n      this.registerDetails = registerDetails;\n    }\n\n    @Override\n    public String getInfo()\n    {\n      return registerDetails.getInfo();\n    }\n\n    @Override\n    public RegisterDetails getRegisterDetails()\n    {\n      return registerDetails;\n    }\n  }\n\n  protected static final Regs[] REGS = Regs.values();\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  protected <T extends Enum<T>> T[] getRegs() { return (T[])REGS; }\n\n  public static int getAddress(final GPIOPadsBank0Registers.Regs register)\n  {\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    return PADS_BANK0_BASE + 0x4 * register.ordinal();\n  }\n\n  public static int getGPIOAddress(final int gpioNum)\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    return PADS_BANK0_BASE + 0x4 * (Regs.GPIO0.ordinal() + gpioNum);\n  }\n\n  public GPIOPadsBank0Registers()\n  {\n    super(\"GPIOPadsBank0\", PADS_BANK0_BASE);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/GPIOPadsBank0RegistersImpl.java",
    "content": "/*\n * @(#)GPIOPadsBank0RegistersImpl.java 1.00 21/03/20\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Facade to the internal GPIO Pads Bank 0 subsystem.  The layout of\n * registers follows the list of registers in Sect. 2.19.6 of the\n * RP2040 datasheet.  The facade is in particular intended for use by\n * the SDK.\n *\n * Note: This class implements only a subset of the RP2040 GPIO set of\n * registers, focussing on those registers that are relevant for PIO\n * simulation.  All GPIO registers specified by the RP2040 datasheet\n * are addressable, but writing to non-relevant registers or register\n * bits will have no effect, and reading from non-relevant registers\n * or register bits will return a constant value of 0.\n */\npublic class GPIOPadsBank0RegistersImpl extends GPIOPadsBank0Registers\n{\n  private final GPIO gpio;\n\n  public GPIOPadsBank0RegistersImpl(final GPIO gpio)\n  {\n    if (gpio == null) {\n      throw new NullPointerException(\"gpio\");\n    }\n    this.gpio = gpio;\n  }\n\n  public GPIO getGPIO() { return gpio; }\n\n  @Override\n  public void writeRegister(final int regNum, final int value,\n                            final int mask, final boolean xor)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case VOLTAGE_SELECT:\n      // TODO\n      break;\n    case GPIO0:\n    case GPIO1:\n    case GPIO2:\n    case GPIO3:\n    case GPIO4:\n    case GPIO5:\n    case GPIO6:\n    case GPIO7:\n    case GPIO8:\n    case GPIO9:\n    case GPIO10:\n    case GPIO11:\n    case GPIO12:\n    case GPIO13:\n    case GPIO14:\n    case GPIO15:\n    case GPIO16:\n    case GPIO17:\n    case GPIO18:\n    case GPIO19:\n    case GPIO20:\n    case GPIO21:\n    case GPIO22:\n    case GPIO23:\n    case GPIO24:\n    case GPIO25:\n    case GPIO26:\n    case GPIO27:\n    case GPIO28:\n    case GPIO29:\n      // TODO\n      break;\n    case SWCLK:\n      // TODO\n      break;\n    case SWD:\n      // TODO\n      break;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  @Override\n  public synchronized int readRegister(final int regNum)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case VOLTAGE_SELECT:\n      return 0; // TODO\n    case GPIO0:\n    case GPIO1:\n    case GPIO2:\n    case GPIO3:\n    case GPIO4:\n    case GPIO5:\n    case GPIO6:\n    case GPIO7:\n    case GPIO8:\n    case GPIO9:\n    case GPIO10:\n    case GPIO11:\n    case GPIO12:\n    case GPIO13:\n    case GPIO14:\n    case GPIO15:\n    case GPIO16:\n    case GPIO17:\n    case GPIO18:\n    case GPIO19:\n    case GPIO20:\n    case GPIO21:\n    case GPIO22:\n    case GPIO23:\n    case GPIO24:\n    case GPIO25:\n    case GPIO26:\n    case GPIO27:\n    case GPIO28:\n    case GPIO29:\n      return 0; // TODO\n    case SWCLK:\n      return 0; // TODO\n    case SWD:\n      return 0; // TODO\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/IOUtils.java",
    "content": "/*\n * @(#)IOUtils.java 1.00 21/04/08\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.jar.JarEntry;\nimport java.util.jar.JarFile;\n\npublic class IOUtils\n{\n  /**\n   * @param resourcePath absolute Path within root package, i.e. with\n   * leading \"/\".\n   */\n  public static InputStream getStreamForResourcePath(final String resourcePath)\n    throws IOException\n  {\n    final InputStream fromFile;\n    try {\n      fromFile = new FileInputStream(resourcePath);\n    } catch (final FileNotFoundException e) {\n      final InputStream fromResource =\n        Constants.class.getResourceAsStream(resourcePath);\n      if (fromResource == null) {\n        throw new IOException(\"resource not found: \" + resourcePath);\n      }\n      return fromResource;\n    }\n    return fromFile;\n  }\n\n  public static LineNumberReader\n    getReaderForResourcePath(final String resourcePath)\n    throws IOException\n  {\n    final InputStream in = getStreamForResourcePath(resourcePath);\n    return new LineNumberReader(new InputStreamReader(in));\n  }\n\n  /**\n   * @param resourcePath Path relative to root package, i.e. without\n   * leading \"/\".\n   */\n  public static List<String> list(final String resourcePath) throws IOException\n  {\n    final List<String> paths = new ArrayList<String>();\n    final File resourceFile =\n      new File(IOUtils.class.getProtectionDomain().\n               getCodeSource().getLocation().getPath());\n    if (resourceFile.isFile()) {\n      final JarFile jarFile = new JarFile(resourceFile);\n      final Enumeration<JarEntry> entries = jarFile.entries();\n      final String prefix = resourcePath + \"/\";\n      while (entries.hasMoreElements()) {\n        final String path = entries.nextElement().getName();\n        if (path.startsWith(prefix) && (path.length() > prefix.length())) {\n          paths.add(path.substring(prefix.length()));\n        }\n      }\n      jarFile.close();\n    } else {\n      final URL url = IOUtils.class.getResource(\"/\" + resourcePath);\n      if (url != null) {\n        final File directoryPath;\n        try {\n          directoryPath = new File(url.toURI());\n        } catch (final URISyntaxException e) {\n          throw new InternalError(\"unexpected exception\", e);\n        }\n        final String prefix = directoryPath + \"/\";\n        for (final File file : directoryPath.listFiles()) {\n          final String path = file.getPath();\n          if (path.startsWith(prefix) && (path.length() > prefix.length())) {\n            paths.add(path.substring(prefix.length()));\n          }\n        }\n      }\n    }\n    return paths;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/IRQ.java",
    "content": "/*\n * @(#)IRQ.java 1.00 21/02/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * IRQ Register Set\n */\npublic class IRQ implements Constants\n{\n  private int regIRQ; // bits 0…7 of IRQ\n  private int regIRQ0_INTE; // bits 0…11 of IRQ0_INTE\n  private int regIRQ0_INTF; // bits 0…11 of IRQ0_INTF\n  private int regIRQ1_INTE; // bits 0…11 of IRQ1_INTE\n  private int regIRQ1_INTF; // bits 0…11 of IRQ1_INTF\n  private int fifoStatus;\n\n  public IRQ()\n  {\n    reset();\n  }\n\n  public void reset()\n  {\n    regIRQ = 0;\n    regIRQ0_INTE = 0;\n    regIRQ0_INTF = 0;\n    regIRQ1_INTE = 0;\n    regIRQ1_INTF = 0;\n    fifoStatus = 0;\n  }\n\n  public void setTxNFull(final int smNum, final boolean nFull)\n  {\n    Constants.checkSmNum(smNum);\n    if (nFull) {\n      fifoStatus |= 0x10 << smNum;\n    } else {\n      fifoStatus &= ~(0x10 << smNum);\n    }\n  }\n\n  public void setRxNEmpty(final int smNum, final boolean nEmpty)\n  {\n    Constants.checkSmNum(smNum);\n    if (nEmpty) {\n      fifoStatus |= 0x1 << smNum;\n    } else {\n      fifoStatus &= ~(0x1 << smNum);\n    }\n  }\n\n  public void writeRegIRQ(final int value)\n  {\n    regIRQ &= (~value) & 0xff; // ignore reserved bits 31:8\n  }\n\n  public void writeRegIRQ_FORCE(final int value)\n  {\n    regIRQ |= value & 0xff; // ignore reserved bits 31:8\n  }\n\n  public Bit get(final int index)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"IRQ index < 0: \" + index);\n    }\n    if (index > 7) {\n      throw new IllegalArgumentException(\"IRQ index > 7: \" + index);\n    }\n    return ((regIRQ >> index) & 0x1) == 0x0 ? Bit.LOW : Bit.HIGH;\n  }\n\n  public void clear(final int index)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"IRQ index < 0: \" + index);\n    }\n    if (index > 7) {\n      throw new IllegalArgumentException(\"IRQ index > 7: \" + index);\n    }\n    regIRQ &= ~(0x1 << index);\n  }\n\n  public void set(final int index)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"IRQ index < 0: \" + index);\n    }\n    if (index > 7) {\n      throw new IllegalArgumentException(\"IRQ index > 7: \" + index);\n    }\n    regIRQ |= 0x1 << index;\n  }\n\n  public int getIRQ()\n  {\n    return regIRQ;\n  }\n\n  public int getIRQ0_INTE()\n  {\n    return regIRQ0_INTE;\n  }\n\n  public void setIRQ0_INTE(final int value, final int mask, final boolean xor)\n  {\n    setIRQ0_INTE(Constants.hwSetBits(regIRQ0_INTE, value, mask, xor));\n  }\n\n  private void setIRQ0_INTE(final int value)\n  {\n    regIRQ0_INTE = value & 0xfff; // ignore reserved bits 31:12\n  }\n\n  public int getIRQ1_INTE()\n  {\n    return regIRQ1_INTE;\n  }\n\n  public void setIRQ1_INTE(final int value, final int mask, final boolean xor)\n  {\n    setIRQ1_INTE(Constants.hwSetBits(regIRQ1_INTE, value, mask, xor));\n  }\n\n  private void setIRQ1_INTE(final int value)\n  {\n    regIRQ1_INTE = value & 0xfff; // ignore reserved bits 31:12\n  }\n\n  public int getIRQ0_INTF()\n  {\n    return regIRQ0_INTF;\n  }\n\n  public void setIRQ0_INTF(final int value, final int mask, final boolean xor)\n  {\n    setIRQ0_INTF(Constants.hwSetBits(regIRQ0_INTF, value, mask, xor));\n  }\n\n  private void setIRQ0_INTF(final int value)\n  {\n    regIRQ0_INTF = value & 0xfff; // ignore reserved bits 31:12\n  }\n\n  public int getIRQ1_INTF()\n  {\n    return regIRQ1_INTF;\n  }\n\n  public void setIRQ1_INTF(final int value, final int mask, final boolean xor)\n  {\n    setIRQ1_INTF(Constants.hwSetBits(regIRQ1_INTF, value, mask, xor));\n  }\n\n  private void setIRQ1_INTF(final int value)\n  {\n    regIRQ1_INTF = value & 0xfff; // ignore reserved bits 31:12\n  }\n\n  public int readINTR()\n  {\n    return ((regIRQ & 0x7) << 8) | fifoStatus;\n  }\n\n  public int readIRQ0_INTS()\n  {\n    return (readINTR() & regIRQ0_INTE) | regIRQ0_INTF;\n  }\n\n  public int readIRQ1_INTS()\n  {\n    return (readINTR() & regIRQ1_INTE) | regIRQ1_INTF;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Instruction.java",
    "content": "/*\n * @(#)Instruction.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.function.IntConsumer;\n\n/**\n * Instruction\n */\npublic abstract class Instruction\n{\n  private int delay;\n  private int sideSet;\n  private int sideSetCount;\n  private boolean sideSetEnabled;\n  private int opCode;\n\n  public enum ResultState\n  {\n    /**\n     * The instruction leaves it to the program control to ordinarily\n     * update the program counter (PC) by increasing it by one and\n     * perform any delay.\n     */\n    COMPLETE,\n    /**\n     * The PC must be kept unmodified (e.g. as the result of a wait\n     * instruction keeping unfulfilled or an additional synthetic\n     * instruction being inserted), and any delay must not yet be\n     * performed.\n     */\n    STALL,\n    /**\n     * The instruction has modified the PC by itself.  Delay must be\n     * ordinarily performed.\n     */\n    JUMP\n  };\n\n  public Instruction()\n  {\n    reset();\n  }\n\n  public int getDelay()\n  {\n    return delay;\n  }\n\n  private String getDelayDisplayValue()\n  {\n    return delay > 0 ? \"[\" + delay + \"]\" : \"\";\n  }\n\n  public int getOpCode()\n  {\n    return opCode;\n  }\n\n  private String getSideSetDisplayValue()\n  {\n    final boolean printSideSet = sideSetEnabled && sideSetCount > 0;\n    return printSideSet ? \"side \" + Integer.toString(sideSet) : \"\";\n  }\n\n  public void reset()\n  {\n    delay = 0;\n    sideSet = 0;\n    sideSetCount = 0;\n    sideSetEnabled = false;\n    resetParams();\n  }\n\n  abstract protected void resetParams();\n\n  public Instruction decode(final short opCode,\n                            final int pinCtrlSidesetCount,\n                            final boolean execCtrlSideEn)\n    throws Decoder.DecodeException\n  {\n    final int delayAndSideSet = (opCode >>> 0x8) & 0x1f;\n    final int delayMask = (0x1 << (5 - pinCtrlSidesetCount)) - 1;\n    this.opCode = opCode;\n    delay = delayAndSideSet & delayMask;\n    final int delayBitCount = 5 - pinCtrlSidesetCount;\n    final boolean haveSideSetEnableBit =\n      execCtrlSideEn && (pinCtrlSidesetCount > 0);\n    sideSetEnabled = !haveSideSetEnableBit || (delayAndSideSet & 0x10) != 0x0;\n    sideSetCount = pinCtrlSidesetCount - (haveSideSetEnableBit ? 1 : 0);\n    final int delayAndSideSetWithoutSideEn =\n      execCtrlSideEn ? delayAndSideSet & 0xf : delayAndSideSet;\n    sideSet = delayAndSideSetWithoutSideEn >>> delayBitCount;\n    decodeLSB(opCode & 0xff);\n    return this;\n  }\n\n  protected int getDelayAndSideSetBits(final int pinCtrlSidesetCount,\n                                       final boolean execCtrlSideEn)\n  {\n    final int delayBitCount = 5 - pinCtrlSidesetCount;\n    final boolean haveSideSetEnableBit =\n      execCtrlSideEn && (pinCtrlSidesetCount > 0);\n    final int delayAndSideSet =\n      (haveSideSetEnableBit && (sideSet > 0) ? 0x10 : 0x00) |\n      (sideSet << delayBitCount) |\n      delay;\n    return (delayAndSideSet & 0x1f) << 8;\n  }\n\n  /**\n   * Updates all internal data of this instruction according to the\n   * argument bits of the instruction word.\n   */\n  abstract void decodeLSB(final int lsb) throws Decoder.DecodeException;\n\n  private void executeSideSet(final SM.Status smStatus)\n  {\n    final int pinCtrlSidesetBase = smStatus.regPINCTRL_SIDESET_BASE;\n    final PIO.PinDir execCtrlSidePinDir = smStatus.regEXECCTRL_SIDE_PINDIR;\n    if (sideSetCount > 0) {\n      if (execCtrlSidePinDir == PIO.PinDir.GPIO_LEVELS) {\n        smStatus.collatePins(sideSet, pinCtrlSidesetBase, sideSetCount, true);\n      } else {\n        smStatus.collatePinDirs(sideSet, pinCtrlSidesetBase, sideSetCount);\n      }\n    }\n  }\n\n  abstract ResultState executeOperation(final SM sm);\n\n  public ResultState execute(final SM sm)\n  {\n    final ResultState resultState = executeOperation(sm);\n    if (sideSetEnabled) executeSideSet(sm.getStatus());\n    return resultState;\n  }\n\n  public abstract String getMnemonic();\n\n  abstract String getParamsDisplay();\n\n  protected void checkIRQIndex(final int irqIndex)\n    throws Decoder.DecodeException\n  {\n    if ((irqIndex & 0x08) != 0) {\n      throw new Decoder.DecodeException(this, getOpCode());\n    }\n  }\n\n  protected static int getIRQNum(final int smNum, final int index)\n  {\n    final boolean isRel = (index & 0x10) != 0;\n    return\n      isRel ?\n      (index & 0x4) | ((smNum + index) & 0x3) :\n      index & 0x7;\n  }\n\n  protected static String getIRQNumDisplay(final int index)\n  {\n    return\n      String.format(\"%1x%s\", index & 0x7, (index & 0x10) != 0 ? \"_rel\" : \"\");\n  }\n\n  @Override\n  public String toString()\n  {\n    final String mnemonic = getMnemonic();\n    final String paramsDisplay = getParamsDisplay();\n    final String sideSetDisplayValue = getSideSetDisplayValue();\n    final String delayDisplayValue = getDelayDisplayValue();\n    return\n      String.format(\"%-16s%s\",\n                    mnemonic +\n                    (!paramsDisplay.isEmpty() ? \" \" + paramsDisplay : \"\"),\n                    sideSetDisplayValue +\n                    (!sideSetDisplayValue.isEmpty() &&\n                     !delayDisplayValue.isEmpty() ? \" \" : \"\") +\n                    delayDisplayValue);\n  }\n\n  public static class Jmp extends Instruction\n  {\n    private static final Map<Integer, Condition> code2cond =\n      new HashMap<Integer, Condition>();\n\n    public enum Condition\n    {\n      ALWAYS(0b000, \"\", (smStatus) -> true),\n      NOT_X(0b001, \"!x\", (smStatus) -> smStatus.regX == 0),\n      DEC_X(0b010, \"x--\", (smStatus) -> smStatus.regX-- != 0),\n      NOT_Y(0b011, \"!y\", (smStatus) -> smStatus.regY == 0),\n      DEC_Y(0b100, \"y--\", (smStatus) -> smStatus.regY-- != 0),\n      X_NEQ_Y(0b101, \"x!=y\", (smStatus) -> smStatus.regX != smStatus.regY),\n      PIN(0b110, \"pin\", (smStatus) -> smStatus.jmpPin() == Bit.HIGH),\n      NOT_OSRE(0b111, \"!osre\",\n               (smStatus) -> !smStatus.isOsrCountBeyondThreshold());\n\n      private final int code;\n      private final String mnemonic;\n      private final Function<SM.Status, Boolean> eval;\n\n      private Condition(final int code, final String mnemonic,\n                        final Function<SM.Status, Boolean> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2cond.put(code, this);\n      }\n\n      public boolean fulfilled(final SM.Status smStatus)\n      {\n        return eval.apply(smStatus);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private int address;\n    private Condition condition;\n\n    @Override\n    protected void resetParams()\n    {\n      address = 0;\n      condition = Condition.ALWAYS;\n    }\n\n    public void setCondition(final Condition condition)\n    {\n      if (condition == null) {\n        throw new NullPointerException(\"condition\");\n      }\n      this.condition = condition;\n    }\n\n    public void setAddress(final int address)\n    {\n      if (address < 0) {\n        throw new IllegalArgumentException(\"address < 0: \" + address);\n      }\n      if (address > 31) {\n        throw new IllegalArgumentException(\"address > 31: \" + address);\n      }\n      this.address = address;\n    }\n\n    public int encode(final int pinCtrlSidesetCount,\n                      final boolean execCtrlSideEn)\n    {\n      return\n        0x0000 |\n        getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) |\n        (condition.ordinal() << 5) |\n        (address & 0x1f);\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n    {\n      address = lsb & 0x1f;\n      condition = code2cond.get((lsb >>> 5) & 0x7);\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      final SM.Status smStatus = sm.getStatus();\n      final boolean doJump = condition.fulfilled(smStatus);\n      if (doJump) smStatus.regADDR = address;\n      return doJump ? ResultState.JUMP : ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"jmp\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      final String conditionDisplay = condition.toString();\n      return\n        (!conditionDisplay.isEmpty() ? conditionDisplay + \", \" : \"\") +\n        String.format(\"%02x\", address);\n    }\n  }\n\n  public static class Wait extends Instruction\n  {\n    private static final Map<Integer, Source> code2src =\n      new HashMap<Integer, Source>();\n\n    private enum Source\n    {\n      GPIO_(0b00, \"gpio\", (wait, sm) ->\n            sm.getPIOGPIO().getGPIO().getInToPeri(wait.index)),\n      PIN(0b01, \"pin\", (wait, sm) -> {\n          final int gpioNum =\n            (wait.index + sm.getStatus().regPINCTRL_IN_BASE) &\n            (Constants.GPIO_NUM - 1);\n          return sm.getPIOGPIO().getGPIO().getInToPeri(gpioNum);\n        }),\n      IRQ(0b10, \"irq\", (wait, sm) -> {\n          final int irqNum = getIRQNum(sm.getNum(), wait.index);\n          final Bit bit = sm.getIRQ(irqNum);\n          if ((wait.polarity == Bit.HIGH) && (bit == wait.polarity))\n            sm.clearIRQ(irqNum);\n          return bit;\n        }),\n      RESERVED_3(0b11, \"???\", null);\n\n      private final int code;\n      private final String mnemonic;\n      private final BiFunction<Wait, SM, Bit> eval;\n\n      private Source(final int code, final String mnemonic,\n                     final BiFunction<Wait, SM, Bit> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2src.put(code, this);\n      }\n\n      public Bit getBit(final Wait wait, SM sm)\n      {\n        return eval.apply(wait, sm);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private Bit polarity;\n    private Source src;\n    private int index;\n\n    @Override\n    protected void resetParams()\n    {\n      polarity = Bit.LOW;\n      src = Source.GPIO_;\n      index = 0;\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      polarity = (lsb & 0x80) != 0 ? Bit.HIGH : Bit.LOW;\n      src = code2src.get((lsb & 0x60) >>> 5);\n      if (src == Source.RESERVED_3) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      index = lsb & 0x1f;\n      checkIRQIndex(index);\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      final boolean doStall = src.getBit(this, sm) != polarity;\n      return doStall ? ResultState.STALL : ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"wait\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      final int maskedIndex;\n      final String num =\n        src == Source.IRQ ?\n        getIRQNumDisplay(index) :\n        String.format(\"%02x\", index);\n      return polarity + \" \" + src + \" \" + num;\n    }\n  }\n\n  public static class In extends Instruction\n  {\n    private static final Map<Integer, Source> code2src =\n      new HashMap<Integer, Source>();\n\n    private enum Source\n    {\n      PINS(0b000, \"pins\", (sm) -> {\n          final int base = sm.getStatus().regPINCTRL_IN_BASE;\n          return\n            sm.getPIOGPIO().getGPIO().getPinsToPeri(base, Constants.GPIO_NUM);\n        }),\n      X(0b001, \"x\", (sm) -> sm.getX()),\n      Y(0b010, \"y\", (sm) -> sm.getY()),\n      NULL(0b011, \"null\", (sm) -> 0),\n      RESERVED_4(0b100, \"???\", null),\n      RESERVED_5(0b101, \"???\", null),\n      ISR(0b110, \"ISR\", (sm) -> sm.getISRValue()),\n      OSR(0b111, \"OSR\", (sm) -> sm.getOSRValue());\n\n      private final int code;\n      private final String mnemonic;\n      private final Function<SM, Integer> eval;\n\n      private Source(final int code, final String mnemonic,\n                     final Function<SM, Integer> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2src.put(code, this);\n      }\n\n      public Integer getData(final SM sm)\n      {\n        return eval.apply(sm);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private Source src;\n    private int bitCount;\n\n    @Override\n    protected void resetParams()\n    {\n      src = Source.PINS;\n      bitCount = 0;\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      src = code2src.get((lsb & 0xe0) >>> 5);\n      if ((src == Source.RESERVED_4) ||\n          (src == Source.RESERVED_5)) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      bitCount = lsb & 0x1f;\n    }\n\n    private void shiftIn(final SM sm, final SM.Status smStatus, final int data,\n                         final int bitsToShift)\n    {\n      if (bitsToShift < 32) {\n        if (sm.getInShiftDir() == PIO.ShiftDir.SHIFT_LEFT) {\n          smStatus.isrValue <<= bitsToShift;\n          smStatus.isrValue |= data & ((0x1 << bitsToShift) - 1);\n        } else /* SHIFT RIGHT */ {\n          smStatus.isrValue >>>= bitsToShift;\n          smStatus.isrValue |=\n            (data & ((0x1 << bitsToShift) - 1)) << (32 - bitsToShift);\n        }\n      } else {\n        smStatus.isrValue = data;\n      }\n    }\n\n    private void saturate(final SM sm, final SM.Status smStatus,\n                          final int bitsToShift)\n    {\n      smStatus.isrShiftCount =\n        SM.saturate(smStatus.isrShiftCount, bitsToShift, 32);\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      final int bitsToShift =\n        Constants.checkBitCount(bitCount, \"shift ISR bitCount\");\n      final SM.Status smStatus = sm.getStatus();\n      /*\n       * TODO: Clarify: Do we need to stall if ISR and RX FIFO are\n       * both full, prior to call shiftIn() (see built-in example\n       * \"logic-analyser\" as test case)?  The spec does not say so\n       * (see Sect. 3.5.4.1.), but I would expect it.  In the latter\n       * case, we need the following additional code te be executed\n       * first, prior to the call to shiftIn():\n       */\n      /*\n      if (smStatus.isIsrCountBeyondThreshold()) {\n        if (sm.isRXFIFOFull()) {\n          return ResultState.STALL;\n        }\n      }\n      */\n      shiftIn(sm, smStatus, src.getData(sm), bitsToShift);\n      saturate(sm, smStatus, bitsToShift);\n      final boolean stall;\n      if (smStatus.regSHIFTCTRL_AUTOPUSH) {\n        // Cp. pseudocode sequence for \"IN\" cycle in RP2040 datasheet,\n        // Sect. 3.5.4.1. \"Autopush Details\".\n        if (smStatus.isIsrCountBeyondThreshold()) {\n          if (sm.isRXFIFOFull()) {\n            stall = true;\n          } else {\n            stall = sm.rxPush(false, true);\n          }\n        } else {\n          stall = false;\n        }\n      } else {\n        stall = false;\n      }\n      return stall ? ResultState.STALL : ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"in\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      return src + \", \" + String.format(\"%02x\", bitCount != 0 ? bitCount : 32);\n    }\n  }\n\n  public static class Out extends Instruction\n  {\n    private static final Map<Integer, Destination> code2dst =\n      new HashMap<Integer, Destination>();\n\n    public enum Destination\n    {\n      PINS(0b000, \"pins\", (sm, data) -> {\n          SM.IOMapping.OUT.collatePins(sm, data);\n          return null;\n        }),\n      X(0b001, \"x\", (sm, data) -> {\n          sm.setX(data);\n          return null;\n        }),\n      Y(0b010, \"y\", (sm, data) -> {\n          sm.setY(data);\n          return null;\n        }),\n      NULL(0b011, \"null\", (sm, data) -> {\n          return null;\n        }),\n      PINDIRS(0b100, \"pindirs\", (sm, data) -> {\n          SM.IOMapping.OUT.collatePinDirs(sm, data);\n          return null;\n        }),\n      PC(0b101, \"pc\", (sm, data) -> {\n          sm.setPC(data & 0x1f);\n          return null;\n        }),\n      ISR(0b110, \"isr\", (sm, data) -> {\n          sm.setISRValue(data);\n          return null;\n        }),\n      EXEC(0b111, \"exec\", (sm, data) -> {\n          sm.execInstruction(data);\n          return null;\n        });\n\n      private final int code;\n      private final String mnemonic;\n      private final BiFunction<SM, Integer, Void> eval;\n\n      private Destination(final int code, final String mnemonic,\n                          final BiFunction<SM, Integer, Void> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2dst.put(code, this);\n      }\n\n      public IntConsumer getConsumer(final SM sm)\n      {\n        return (data) -> {\n          eval.apply(sm, data);\n        };\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private Destination dst;\n    private int bitCount;\n\n    @Override\n    protected void resetParams()\n    {\n      dst = Destination.PINS;\n      bitCount = 0;\n    }\n\n    public void setDestination(final Destination dst)\n    {\n      if (dst == null) {\n        throw new NullPointerException(\"dst\");\n      }\n      this.dst = dst;\n    }\n\n    public void setBitCount(final int bitCount)\n    {\n      if (bitCount < 0) {\n        throw new IllegalArgumentException(\"bit count < 0: \" + bitCount);\n      }\n      if (bitCount > 31) {\n        throw new IllegalArgumentException(\"bit count > 31: \" + bitCount);\n      }\n      this.bitCount = bitCount;\n    }\n\n    public int encode(final int pinCtrlSidesetCount,\n                      final boolean execCtrlSideEn)\n    {\n      return\n        0x6000 |\n        getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) |\n        (dst.ordinal() << 5) |\n        bitCount;\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n    {\n      dst = code2dst.get((lsb & 0xe0) >>> 5);\n      bitCount = lsb & 0x1f;\n    }\n\n    private void outputOsr(final SM sm, final SM.Status smStatus,\n                           final int bitsToShift)\n    {\n      final int shiftOutBits;\n      if (bitsToShift < 32) {\n        if (sm.getOutShiftDir() == PIO.ShiftDir.SHIFT_LEFT) {\n          shiftOutBits =\n            (smStatus.osrValue >>> (32 - bitsToShift)) &\n            ((0x1 << bitsToShift) - 1);\n        } else /* SHIFT_RIGHT */ {\n          shiftOutBits = smStatus.osrValue & ((0x1 << bitsToShift) - 1);\n        }\n      } else {\n        shiftOutBits = smStatus.osrValue;\n      }\n      dst.getConsumer(sm).accept(shiftOutBits);\n    }\n\n    private void shiftOsr(final SM sm, final SM.Status smStatus,\n                          final int bitsToShift)\n    {\n      if (bitsToShift < 32) {\n        if (sm.getOutShiftDir() == PIO.ShiftDir.SHIFT_LEFT) {\n          smStatus.osrValue <<= bitsToShift;\n        } else /* SHIFT_RIGHT */ {\n          smStatus.osrValue >>>= bitsToShift;\n        }\n      } else {\n        smStatus.osrValue = 0;\n      }\n    }\n\n    private void saturate(final SM sm, final SM.Status smStatus,\n                          final int bitsToShift)\n    {\n      smStatus.osrShiftCount =\n        SM.saturate(smStatus.osrShiftCount, bitsToShift, 32);\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      // Cp. pseudocode sequence for \"OUT\" cycles in RP2040 datasheet,\n      // Sect. 3.5.4.2. \"Autopull Details\".\n      final SM.Status smStatus = sm.getStatus();\n      final boolean stall;\n      if (smStatus.regSHIFTCTRL_AUTOPULL &&\n          smStatus.isOsrCountBeyondThreshold()) {\n        // block=true to avoid loading regX\n        sm.txPull(false, true); // also sets osr count = 0\n\n        // RP2040 cannot fill empty OSR and \"OUT\" in same cycle =>\n        // always stall regardless of TX state\n        stall = true;\n      } else {\n        final int bitsToShift =\n          Constants.checkBitCount(bitCount, \"shift OSR bitCount\");\n        outputOsr(sm, smStatus, bitsToShift);\n        shiftOsr(sm, smStatus, bitsToShift);\n        saturate(sm, smStatus, bitsToShift);\n        if (smStatus.regSHIFTCTRL_AUTOPULL) {\n          if (smStatus.isOsrCountBeyondThreshold()) {\n            // block=true to avoid loading regX\n            sm.txPull(false, true); // also sets osr count = 0\n          }\n        }\n        // stall always false, since we *did* output from OSR\n        stall = false;\n      }\n      return (stall || dst == Destination.EXEC) ?\n        ResultState.STALL :\n        (dst == Destination.PC ?\n         ResultState.JUMP :\n         ResultState.COMPLETE);\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"out\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      return dst + \", \" + String.format(\"%02x\", bitCount != 0 ? bitCount : 32);\n    }\n  }\n\n  public static class Push extends Instruction\n  {\n    private boolean ifFull;\n    private boolean block;\n\n    @Override\n    protected void resetParams()\n    {\n      ifFull = false;\n      block = false;\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      ifFull = (lsb & 0x40) != 0;\n      block = (lsb & 0x20) != 0;\n      if ((lsb & 0x1f) != 0) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      return sm.rxPush(ifFull, block) ?\n        ResultState.STALL :\n        ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"push\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      return\n        (ifFull ? \"iffull\" : \"\") +\n        (ifFull && !block ? \" \" : \"\") +\n        (block ? \"\" : \"noblock\");\n    }\n  }\n\n  public static class Pull extends Instruction\n  {\n    private boolean ifEmpty;\n    private boolean block;\n\n    @Override\n    protected void resetParams()\n    {\n      ifEmpty = false;\n      block = false;\n    }\n\n    public void setIfEmpty(final boolean ifEmpty)\n    {\n      this.ifEmpty = ifEmpty;\n    }\n\n    public void setBlock(final boolean block)\n    {\n      this.block = block;\n    }\n\n    public int encode(final int pinCtrlSidesetCount,\n                      final boolean execCtrlSideEn)\n    {\n      return\n        0x8080 |\n        getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) |\n        (ifEmpty ? 0x1 << 6 : 0) |\n        (block ? 0x1 << 5 : 0);\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      ifEmpty = (lsb & 0x40) != 0;\n      block = (lsb & 0x20) != 0;\n      if ((lsb & 0x1f) != 0) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      return sm.txPull(ifEmpty, block) ?\n        ResultState.STALL :\n        ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"pull\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      return\n        (ifEmpty ? \"ifempty\" : \"\") +\n        (ifEmpty && !block ? \" \" : \"\") +\n        (block ? \"\" : \"noblock\");\n    }\n  }\n\n  public static class Mov extends Instruction\n  {\n    private static final Map<Integer, Source> code2src =\n      new HashMap<Integer, Source>();\n    private static final Map<Integer, Destination> code2dst =\n      new HashMap<Integer, Destination>();\n    private static final Map<Integer, Operation> code2op =\n      new HashMap<Integer, Operation>();\n\n    private enum Source\n    {\n      PINS(0b000, \"pins\", (sm) -> {\n          final int base = sm.getStatus().regPINCTRL_IN_BASE;\n          return\n            sm.getPIOGPIO().getGPIO().getPinsToPeri(base, Constants.GPIO_NUM);\n        }),\n      X(0b001, \"x\", (sm) -> sm.getX()),\n      Y(0b010, \"y\", (sm) -> sm.getY()),\n      NULL(0b011, \"null\", (sm) -> 0),\n      RESERVED_4(0b100, \"???\", null),\n      STATUS(0b101, \"status\",\n             (sm) -> (sm.getStatus().getFIFOStatus())),\n      ISR(0b110, \"isr\", (sm) -> sm.getISRValue()),\n      OSR(0b111, \"osr\", (sm) -> sm.getOSRValue());\n\n      private final int code;\n      private final String mnemonic;\n      private final Function<SM, Integer> eval;\n\n      private Source(final int code, final String mnemonic,\n                     final Function<SM, Integer> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2src.put(code, this);\n      }\n\n      public Integer read(final SM sm)\n      {\n        return eval.apply(sm);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private enum Destination\n    {\n      PINS(0b000, \"pins\", (sm, data) -> {\n          SM.IOMapping.OUT.collatePins(sm, data);\n          return null;\n        }),\n      X(0b001, \"x\", (sm, data) -> {\n          sm.setX(data);\n          return null;\n        }),\n      Y(0b010, \"y\", (sm, data) -> {\n          sm.setY(data);\n          return null;\n        }),\n      RESERVED_3(0b011, \"???\", null),\n      EXEC(0b100, \"exec\", (sm, data) -> {\n          sm.execInstruction(data);\n          return null;\n        }),\n      PC(0b101, \"pc\", (sm, data) -> {\n          sm.setPC(data & 0x1f);\n          return null;\n        }),\n      ISR(0b110, \"isr\", (sm, data) -> {\n          sm.setISRValue(data);\n          return null;\n        }),\n      OSR(0b111, \"osr\", (sm, data) -> {\n          sm.setOSRValue(data);\n          return null;\n        });\n\n      private final int code;\n      private final String mnemonic;\n      private final BiFunction<SM, Integer, Void> eval;\n\n      private Destination(final int code, final String mnemonic,\n                          final BiFunction<SM, Integer, Void> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2dst.put(code, this);\n      }\n\n      public void write(final SM sm, final int data)\n      {\n        eval.apply(sm, data);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private enum Operation\n    {\n      NONE(0b00, \"\", (data) -> data),\n      INVERT(0b01, \"~\", (data) -> ~data),\n      BIT_REVERSE(0b10, \"::\", (data) -> Integer.reverse(data)),\n      RESERVED_3(0b11, \"???\", null);\n\n      private final int code;\n      private final String mnemonic;\n      private final Function<Integer, Integer> eval;\n\n      private Operation(final int code, final String mnemonic,\n                     final Function<Integer, Integer> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2op.put(code, this);\n      }\n\n      private int apply(final int data)\n      {\n        return eval.apply(data);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private Source src;\n    private Destination dst;\n    private Operation op;\n\n    @Override\n    protected void resetParams()\n    {\n      src = Source.PINS;\n      dst = Destination.PINS;\n      op = Operation.NONE;\n    }\n\n    private boolean isNop()\n    {\n      return\n        (src == Source.Y) &&\n        (dst == Destination.Y) &&\n        (op == Operation.NONE);\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      src = code2src.get(lsb & 0x7);\n      if (src == Source.RESERVED_4) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      dst = code2dst.get((lsb & 0xe0) >>> 5);\n      if (dst == Destination.RESERVED_3) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      op = code2op.get((lsb & 0x18) >>> 3);\n      if (op == Operation.RESERVED_3) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      dst.write(sm, op.apply(src.read(sm)));\n      return\n        dst == Destination.EXEC ?\n        ResultState.STALL :\n        (dst == Destination.PC ?\n         ResultState.JUMP :\n         ResultState.COMPLETE);\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return isNop() ? \"nop\" : \"mov\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      if (isNop()) return \"\";\n      final String strOp = op.toString();\n      return dst + \", \" + (!strOp.isEmpty() ? strOp : \"\") + src;\n    }\n  }\n\n  public static class Irq extends Instruction\n  {\n    private boolean clr;\n    private boolean wait;\n    private int index;\n\n    @Override\n    protected void resetParams()\n    {\n      clr = false;\n      wait = false;\n      index = 0;\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      if ((lsb & 0x80) != 0) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      clr = (lsb & 0x40) != 0;\n      wait = (lsb & 0x20) != 0;\n      index = lsb & 0x1f;\n      checkIRQIndex(index);\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      final boolean stall;\n      final int irqNum = getIRQNum(sm.getNum(), index);\n      if (clr) {\n        sm.clearIRQ(irqNum);\n        stall = false;\n      } else {\n        sm.setIRQ(irqNum);\n        stall = wait;\n      }\n      return stall ? ResultState.STALL : ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"irq\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      /*\n       * Note: Modes \"\", \"set\" and \"nowait\" are all synonyms for the\n       * same thing, namely that both flags (clr, wait) are not set.\n       * For display, we deliberately choose \"\".\n       */\n      final String mode = clr ? \"clear \" : (wait ? \"wait \" : \"\");\n      return mode + getIRQNumDisplay(index);\n    }\n  }\n\n  public static class Set extends Instruction\n  {\n    private static final Map<Integer, Destination> code2dst =\n      new HashMap<Integer, Destination>();\n\n    public enum Destination\n    {\n      PINS(0b000, \"pins\", (sm, data) -> {\n          SM.IOMapping.SET.collatePins(sm, data);\n          return null;\n        }),\n      X(0b001, \"x\", (sm, data) -> {\n          sm.setX(data);\n          return null;\n        }),\n      Y(0b010, \"y\", (sm, data) -> {\n          sm.setY(data);\n          return null;\n        }),\n      RESERVED_3(0b011, \"???\", null),\n      PINDIRS(0b100, \"pindirs\", (sm, data) -> {\n          SM.IOMapping.SET.collatePinDirs(sm, data);\n          return null;\n        }),\n      RESERVED_5(0b101, \"???\", null),\n      RESERVED_6(0b110, \"???\", null),\n      RESERVED_7(0b111, \"???\", null);\n\n      private final int code;\n      private final String mnemonic;\n      private final BiFunction<SM, Integer, Void> eval;\n\n      private Destination(final int code, final String mnemonic,\n                          final BiFunction<SM, Integer, Void> eval)\n      {\n        this.code = code;\n        this.mnemonic = mnemonic;\n        this.eval = eval;\n        code2dst.put(code, this);\n      }\n\n      public void write(final SM sm, final int data)\n      {\n        eval.apply(sm, data);\n      }\n\n      @Override\n      public String toString()\n      {\n        return mnemonic;\n      }\n    }\n\n    private Destination dst;\n    private int data;\n\n    @Override\n    protected void resetParams()\n    {\n      dst = Destination.PINS;\n      data = 0;\n    }\n\n    public void setDestination(final Destination dst)\n    {\n      if (dst == null) {\n        throw new NullPointerException(\"dst\");\n      }\n      this.dst = dst;\n    }\n\n    public void setData(final int data)\n    {\n      if (data < 0) {\n        throw new IllegalArgumentException(\"data < 0: \" + data);\n      }\n      if (data > 31) {\n        throw new IllegalArgumentException(\"data > 31: \" + data);\n      }\n      this.data = data;\n    }\n\n    public int encode(final int pinCtrlSidesetCount,\n                      final boolean execCtrlSideEn)\n    {\n      return\n        0xe000 |\n        getDelayAndSideSetBits(pinCtrlSidesetCount, execCtrlSideEn) |\n        (dst.ordinal() << 5) |\n        (data & 0x1f);\n    }\n\n    @Override\n    public void decodeLSB(final int lsb)\n      throws Decoder.DecodeException\n    {\n      dst = code2dst.get((lsb & 0xe0) >>> 5);\n      if ((dst == Destination.RESERVED_3) ||\n          (dst == Destination.RESERVED_5) ||\n          (dst == Destination.RESERVED_6) ||\n          (dst == Destination.RESERVED_7)) {\n        throw new Decoder.DecodeException(this, getOpCode());\n      }\n      data = lsb & 0x1f;\n    }\n\n    @Override\n    public ResultState executeOperation(final SM sm)\n    {\n      dst.write(sm, data);\n      return ResultState.COMPLETE;\n    }\n\n    @Override\n    public String getMnemonic()\n    {\n      return \"set\";\n    }\n\n    @Override\n    public String getParamsDisplay()\n    {\n      return dst + \", \" + String.format(\"%02x\", data);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/LocalAddressSpace.java",
    "content": "/*\n * @(#)LocalAddressSpace.java 1.00 21/03/25\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class LocalAddressSpace extends AddressSpace\n{\n  private final Emulator emulator;\n  private final PicoEmuRegistersImpl picoEmuRegisters;\n  private final GPIOIOBank0RegistersImpl gpioIOBank0Registers;\n  private final GPIOPadsBank0RegistersImpl gpioPadsBank0Registers;\n  private final PIORegistersImpl pio0Registers;\n  private final PIOEmuRegistersImpl pio0EmuRegisters;\n  private final PIORegistersImpl pio1Registers;\n  private final PIOEmuRegistersImpl pio1EmuRegisters;\n\n  /*\n   * TODO: Really should replace this simple-minded list approach with\n   * either a responsibility chain or a composite design pattern, as\n   * soon as the number of registers interfaces grows.\n   */\n  private final List<RegisterSet> registerSetList;\n\n  public LocalAddressSpace(final Emulator emulator)\n  {\n    this.emulator = emulator;\n\n    registerSetList = new ArrayList<RegisterSet>();\n    picoEmuRegisters = new PicoEmuRegistersImpl(emulator);\n    registerSetList.add(picoEmuRegisters);\n\n    final GPIO gpio = emulator.getGPIO();\n    gpioIOBank0Registers = new GPIOIOBank0RegistersImpl(gpio);\n    registerSetList.add(gpioIOBank0Registers);\n    gpioPadsBank0Registers = new GPIOPadsBank0RegistersImpl(gpio);\n    registerSetList.add(gpioPadsBank0Registers);\n\n    final PIO pio0 = emulator.getPIO0();\n    pio0Registers = new PIORegistersImpl(pio0);\n    registerSetList.add(pio0Registers);\n    pio0EmuRegisters = new PIOEmuRegistersImpl(pio0);\n    registerSetList.add(pio0EmuRegisters);\n\n    final PIO pio1 = emulator.getPIO1();\n    pio1Registers = new PIORegistersImpl(pio1);\n    registerSetList.add(pio1Registers);\n    pio1EmuRegisters = new PIOEmuRegistersImpl(pio1);\n    registerSetList.add(pio1EmuRegisters);\n  }\n\n  @Override\n  public String getEmulatorInfo() throws IOException\n  {\n    return Constants.getEmulatorIdAndVersionWithOs();\n  }\n\n  public int getGPIOAddress(final GPIOIOBank0RegistersImpl.Regs register)\n  {\n    return GPIOIOBank0RegistersImpl.getAddress(register);\n  }\n\n  public int getGPIOAddress(final GPIOPadsBank0RegistersImpl.Regs register)\n  {\n    return GPIOPadsBank0RegistersImpl.getAddress(register);\n  }\n\n  public int getPIO0Address(final PIORegistersImpl.Regs register)\n  {\n    return pio0Registers.getAddress(register);\n  }\n\n  public int getPIO1Address(final PIORegistersImpl.Regs register)\n  {\n    return pio1Registers.getAddress(register);\n  }\n\n  public int getPIO0Address(final PIOEmuRegistersImpl.Regs register)\n  {\n    return pio0EmuRegisters.getAddress(register);\n  }\n\n  public int getPIO1Address(final PIOEmuRegistersImpl.Regs register)\n  {\n    return pio1EmuRegisters.getAddress(register);\n  }\n\n  private static int address2register(final RegisterSet registers,\n                                      final int address)\n  {\n    checkAddressAligned(address);\n    return ((address - registers.getBaseAddress()) & ~0x3000) >>> 2;\n  }\n\n  private RegisterSet getProvidingRegisters(final int address)\n    throws IOException\n  {\n    for (final RegisterSet registers : registerSetList) {\n      final int regNum = address2register(registers, address);\n      if (regNum < registers.getSize()) {\n        return registers;\n      }\n    }\n    return null;\n  }\n\n  @Override\n  public boolean providesAddress(final int address) throws IOException\n  {\n    return getProvidingRegisters(address) != null;\n  }\n\n  @Override\n  public String getRegisterSetId(final int address) throws IOException\n  {\n    final RegisterSet registers = getProvidingRegisters(address);\n    if (registers != null) {\n      return registers.getId();\n    }\n    final String message =\n      String.format(\"requesting register set ID for unsupported address: %08x\",\n                    address);\n    throw new IOException(message);\n  }\n\n  @Override\n  public String getAddressLabel(final int address) throws IOException\n  {\n    final RegisterSet registers = getProvidingRegisters(address);\n    if (registers != null) {\n      final int regNum = address2register(registers, address);\n      return registers.getRegisterLabel(regNum);\n    }\n    final String message =\n      String.format(\"requesting label for unsupported address: %08x\",\n                    address);\n    throw new IOException(message);\n  }\n\n  @Override\n  public synchronized void writeAddressMasked(final int address, final int bits,\n                                              final int mask, final boolean xor)\n    throws IOException\n  {\n    if ((address & 0x3000) != 0x0000) {\n      final String message =\n        String.format(\"writeAddressMasked(): \" +\n                      \"address not in base address range: 0x%8x\", address);\n      throw new IOException(message);\n    }\n    final RegisterSet registers = getProvidingRegisters(address);\n    if (registers != null) {\n      final int regNum = address2register(registers, address);\n      try {\n        registers.writeRegister(regNum, bits, mask, xor);\n      } catch (final Throwable t) {\n        final String message = t.getMessage();\n        emulator.getConsole().\n          printf(\"warning: internal error occurred: %s%n\", message);\n        t.printStackTrace(emulator.getConsole());\n        throw new IOException(message);\n      }\n      return;\n    }\n    final String message =\n      String.format(\"write on unsupported address: %08x\", address);\n    throw new IOException(message);\n  }\n\n  @Override\n  public synchronized int readAddress(final int address) throws IOException\n  {\n    final RegisterSet registers = getProvidingRegisters(address);\n    if (registers != null) {\n      final int regNum = address2register(registers, address);\n      try {\n        return registers.readRegister(regNum);\n      } catch (final Throwable t) {\n        final String message = t.getMessage();\n        emulator.getConsole().\n          printf(\"warning: internal error occurred: %s%n\", message);\n        t.printStackTrace(emulator.getConsole());\n        throw new IOException(message);\n      }\n    }\n    final String message =\n      String.format(\"read from unsupported address: %08x\", address);\n    throw new IOException(message);\n  }\n\n  private static boolean timedOut(final long startWallClock,\n                                  final long stopWallClock,\n                                  final long wallClock)\n  {\n    return\n      (startWallClock < stopWallClock) ?\n      (wallClock < startWallClock) || (wallClock >= stopWallClock) :\n      (wallClock < startWallClock) && (wallClock >= stopWallClock);\n  }\n\n  @Override\n  public int waitAddress(final int address, final int expectedValue,\n                         final int mask,\n                         final long cyclesTimeout, final long millisTimeout)\n    throws IOException\n  {\n    if (cyclesTimeout < 0) {\n      throw new IllegalArgumentException(\"cyclesTimeout < 0: \" + cyclesTimeout);\n    }\n    if (millisTimeout < 0) {\n      throw new IllegalArgumentException(\"millisTimeout < 0: \" + millisTimeout);\n    }\n    final MasterClock masterClock = emulator.getMasterClock();\n    final long startWallClock = masterClock.getWallClock();\n    final long stopWallClock = startWallClock + cyclesTimeout;\n    final long startTime = System.currentTimeMillis();\n    final long stopTime = startTime + millisTimeout;\n    int receivedValue;\n    while (((receivedValue = readAddress(address) & mask) != expectedValue)) {\n      final long wallClock = masterClock.getWallClock();\n      if (timedOut(startWallClock, stopWallClock, wallClock)) break;\n      try {\n        if (millisTimeout != 0) {\n          final long time = System.currentTimeMillis();\n          if (timedOut(startTime, stopTime, time)) break;\n          masterClock.awaitPhaseChange(stopTime - time);\n        } else {\n          masterClock.awaitPhaseChange();\n        }\n      } catch (final InterruptedException e) {\n        // ignore here, since check in while condition\n      }\n    }\n    return receivedValue;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/MasterClock.java",
    "content": "/*\n * @(#)MasterClock.java 1.00 21/02/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * System Master Clock\n */\npublic class MasterClock implements Clock, Constants\n{\n  public enum Mode {\n    TARGET_FREQUENCY,\n    SINGLE_STEP;\n\n    public static Mode fromValue(final int value) {\n      if ((value < 0) || (value >= MODES.length)) {\n        throw new IllegalArgumentException(\"value: \" + value);\n      }\n      return MODES[value];\n    }\n  };\n\n  private static final Mode[] MODES = Mode.values();\n\n  private class DrivingGear extends Thread\n  {\n    public DrivingGear()\n    {\n      super(\"Emulation Thread\");\n    }\n\n    private void runSingleStep()\n    {\n      synchronized(this) {\n        while ((mode == Mode.SINGLE_STEP) &&\n               (phase == Phase.PHASE_1_STABLE)) {\n          try {\n            wait();\n          } catch (final InterruptedException e) {\n            // ignore here, since check in while condition\n          }\n          if (terminate) return;\n        }\n        if (phase == Phase.PHASE_0_IN_PROGRESS) {\n          syncWithRealTime();\n          cyclePhase0();\n        }\n        while ((mode == Mode.SINGLE_STEP) &&\n               (phase == Phase.PHASE_0_STABLE)) {\n          try {\n            wait();\n          } catch (final InterruptedException e) {\n            // ignore here, since check in while condition\n          }\n          if (terminate) return;\n        }\n        if (phase == Phase.PHASE_1_IN_PROGRESS) {\n          cyclePhase1();\n        }\n      }\n    }\n\n    private void runTargetFrequency()\n    {\n      syncWithRealTime();\n      phase = Phase.PHASE_0_IN_PROGRESS;\n      cyclePhase0();\n      phase = Phase.PHASE_1_IN_PROGRESS;\n      cyclePhase1();\n    }\n\n    @Override\n    public void run()\n    {\n      while (true) {\n        while (mode == Mode.SINGLE_STEP) {\n          runSingleStep();\n          if (terminate) return;\n        }\n        while (mode == Mode.TARGET_FREQUENCY) {\n          runTargetFrequency();\n          if (terminate) return;\n        }\n      }\n    }\n  }\n\n  private final PrintStream console;\n\n  /**\n   * Insure access to the following set of variables is atomic:\n   * (refWallClock, refRealTime, frequency, milliSecondsPerCycle).\n   */\n  private final Object accountingLock;\n\n  /**\n   * Emulator-wide lock for synchronizing register reads waiting for a\n   * specific masked value to match.\n   */\n  private final Object registerWaitLock;\n\n  private final DrivingGear drivingGear;\n  private final List<TransitionListener> listeners;\n  private long frequency;\n  private double milliSecondsPerCycle;\n  private Mode mode;\n  private Phase phase;\n  private long wallClock;\n  private long refWallClock;\n  private long refRealTime;\n  private boolean terminate;\n\n  private MasterClock()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public MasterClock(final PrintStream console)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    accountingLock = new Object();\n    registerWaitLock = new Object();\n    drivingGear = new DrivingGear();\n    listeners = new ArrayList<TransitionListener>();\n    reset();\n    start();\n  }\n\n  public void reset()\n  {\n    setFrequency(DEFAULT_FREQUENCY);\n    setMode(Mode.SINGLE_STEP);\n    phase = Phase.PHASE_1_STABLE;\n    wallClock = 0;\n  }\n\n  private void start()\n  {\n    terminate = false;\n    drivingGear.start();\n  }\n\n  public void terminate()\n  {\n    synchronized(drivingGear) {\n      terminate = true;\n      drivingGear.notify();\n    }\n  }\n\n  private void resetRef()\n  {\n    synchronized(accountingLock) {\n      refWallClock = wallClock;\n      refRealTime = System.currentTimeMillis();\n    }\n  }\n\n  private long getMilliSecondsAhead()\n  {\n    synchronized(accountingLock) {\n      if (frequency == 0) {\n        return 0;\n      }\n      final long wallTimeMillisSinceRef =\n        Math.round(milliSecondsPerCycle * (wallClock - refWallClock));\n      final long realTimeMillisSinceRef =\n        System.currentTimeMillis() - refRealTime;\n      final long milliSecondsAhead =\n        wallTimeMillisSinceRef - realTimeMillisSinceRef;\n      return milliSecondsAhead >= 0 ? milliSecondsAhead : 0;\n    }\n  }\n\n  private void syncWithRealTime()\n  {\n    if (mode != Mode.TARGET_FREQUENCY) return;\n    final long milliSecondsAhead = getMilliSecondsAhead();\n    if (milliSecondsAhead > 0) {\n      try {\n        Thread.sleep(milliSecondsAhead);\n      } catch (final InterruptedException e) {\n        // ignore\n      }\n    }\n  }\n\n  private void setFrequency(final int frequency)\n  {\n    synchronized(accountingLock) {\n      this.frequency = frequency & 0xffffffff;\n      milliSecondsPerCycle =\n        frequency != 0 ? 8000.0 / this.frequency : Double.POSITIVE_INFINITY;\n      resetRef();\n    }\n  }\n\n  public Object getRegisterWaitLock()\n  {\n    return registerWaitLock;\n  }\n\n  public void setMASTERCLK_FREQ(final int frequency)\n  {\n    synchronized(drivingGear) {\n      setFrequency(frequency);\n      drivingGear.notify();\n    }\n  }\n\n  public int getMASTERCLK_FREQ()\n  {\n    return (int)frequency;\n  }\n\n  public void setMode(final Mode mode)\n  {\n    synchronized(drivingGear) {\n      this.mode = mode;\n      drivingGear.notify();\n      resetRef();\n    }\n  }\n\n  public Mode getMode() { return mode; }\n\n  public void setMASTERCLK_MODE(final int value)\n  {\n    setMode(Mode.fromValue(value & 0x1));\n  }\n\n  public int getMASTERCLK_MODE()\n  {\n    return mode.ordinal();\n  }\n\n  @Override\n  public void addTransitionListener(final TransitionListener listener)\n  {\n    listeners.add(listener);\n  }\n\n  @Override\n  public boolean removeTransitionListener(final TransitionListener listener)\n  {\n    return listeners.remove(listener);\n  }\n\n  @Override\n  public long getWallClock()\n  {\n    return wallClock;\n  }\n\n  private void announceRisingEdge()\n  {\n    for (final TransitionListener listener : listeners) {\n      listener.risingEdge(wallClock);\n    }\n  }\n\n  private void announceFallingEdge()\n  {\n    for (final TransitionListener listener : listeners) {\n      listener.fallingEdge(wallClock);\n    }\n  }\n\n  public Phase getPhase() { return phase; }\n\n  public void triggerPhase0()\n  {\n    synchronized(accountingLock) {\n      if (mode != Mode.SINGLE_STEP) return;\n      synchronized(drivingGear) {\n        if (phase == Phase.PHASE_1_STABLE) {\n          phase = Phase.PHASE_0_IN_PROGRESS;\n          drivingGear.notify();\n        }\n      }\n    }\n  }\n\n  private void cyclePhase0()\n  {\n    if (phase != Phase.PHASE_0_IN_PROGRESS) {\n      console.println(\"warning: cyclePhase0: unexpected phase: \" + phase);\n      return;\n    }\n    announceRisingEdge();\n    phase = Phase.PHASE_0_STABLE;\n    synchronized(registerWaitLock) {\n      registerWaitLock.notifyAll();\n    }\n  }\n\n  public void triggerPhase1()\n  {\n    synchronized(accountingLock) {\n      if (mode != Mode.SINGLE_STEP) return;\n      synchronized(drivingGear) {\n        if (phase == Phase.PHASE_0_STABLE) {\n          phase = Phase.PHASE_1_IN_PROGRESS;\n          drivingGear.notify();\n        }\n      }\n    }\n  }\n\n  private void cyclePhase1()\n  {\n    if (phase != Phase.PHASE_1_IN_PROGRESS) {\n      console.println(\"warning: cyclePhase1: unexpected phase: \" + phase);\n      return;\n    }\n    announceFallingEdge();\n    wallClock++;\n    phase = Phase.PHASE_1_STABLE;\n    synchronized(registerWaitLock) {\n      registerWaitLock.notifyAll();\n    }\n  }\n\n  public void awaitPhaseChange() throws InterruptedException\n  {\n    synchronized(registerWaitLock) {\n      registerWaitLock.wait();\n    }\n  }\n\n  public void awaitPhaseChange(final long millisTimeout)\n    throws InterruptedException\n  {\n    synchronized(registerWaitLock) {\n      registerWaitLock.wait(millisTimeout);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/Memory.java",
    "content": "/*\n * @(#)Memory.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * 32 32-Bit Words of Shared Instruction Memory\n */\npublic class Memory implements Constants\n{\n  public final Object FETCH_LOCK;\n  private final short[] code;\n\n  public Memory()\n  {\n    FETCH_LOCK = new Object();\n    code = new short[MEMORY_SIZE];\n  }\n\n  public void reset()\n  {\n    synchronized(FETCH_LOCK) {\n      for (int address = 0; address < MEMORY_SIZE; address++) {\n        set(address, (short)0);\n      }\n    }\n  }\n\n  public void set(final int address, final int value,\n                  final int mask, final boolean xor)\n  {\n    set(address, (short)Constants.hwSetBits(get(address), value, mask, xor));\n  }\n\n  private void set(final int address, final short value)\n  {\n    Constants.checkSmMemAddr(address, \"write address\");\n    code[address] = value;\n  }\n\n  public short get(final int address)\n  {\n    Constants.checkSmMemAddr(address, \"read address\");\n    return code[address];\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIO.java",
    "content": "/*\n * @(#)PIO.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\n/**\n * Peripheral I/O Unit\n */\npublic class PIO implements Constants, Clock.TransitionListener\n{\n  private final int index;\n  private final PrintStream console;\n  private final MasterClock masterClock;\n  private final GPIO gpio;\n  private final PIOGPIO pioGpio;\n  private final Memory memory;\n  private final IRQ irq;\n  private final SM[] sms;\n  private int smEnabled; // bits 0…3 of CTRL_SM_ENABLE\n\n  public enum PinDir {\n    GPIO_LEVELS(0, \"levels\"),\n    GPIO_DIRECTIONS(1, \"directions\");\n\n    private final int value;\n    private final String label;\n\n    private PinDir(final int value, final String label)\n    {\n      this.value = value;\n      this.label = label;\n    }\n\n    public int getValue() { return value; }\n\n    public static PinDir fromValue(final int value)\n    {\n      if (value == 0)\n        return GPIO_LEVELS;\n      if (value == 1)\n        return GPIO_DIRECTIONS;\n      throw new IllegalArgumentException(\"value neither 0 nor 1\");\n    }\n\n    public static PinDir fromValue(final int value, final PinDir defaultValue)\n    {\n      if (value == 0)\n        return GPIO_LEVELS;\n      if (value == 1)\n        return GPIO_DIRECTIONS;\n      return defaultValue;\n    }\n  };\n\n  public enum ShiftDir {\n    SHIFT_LEFT(0, \"shift left\"),\n    SHIFT_RIGHT(1, \"shift right\");\n\n    private final int value;\n    private final String label;\n\n    private ShiftDir(final int value, final String label)\n    {\n      this.value = value;\n      this.label = label;\n    }\n\n    public int getValue() { return value; }\n\n    public static ShiftDir fromValue(final int value)\n    {\n      if (value == 0)\n        return SHIFT_LEFT;\n      if (value == 1)\n        return SHIFT_RIGHT;\n      throw new IllegalArgumentException(\"value neither 0 nor 1\");\n    }\n  };\n\n  private PIO()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PIO(final int index, final PrintStream console,\n             final MasterClock masterClock, final GPIO gpio)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"PIO index < 0: \" + index);\n    }\n    if (index > 1) {\n      throw new IllegalArgumentException(\"PIO index > 1: \" + index);\n    }\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(masterClock);\n    Objects.requireNonNull(gpio);\n    this.index = index;\n    this.console = console;\n    this.masterClock = masterClock;\n    this.gpio = gpio;\n    masterClock.addTransitionListener(this);\n    pioGpio = new PIOGPIO(gpio);\n    memory = new Memory();\n    irq = new IRQ();\n    sms = new SM[SM_COUNT];\n    for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n      sms[smNum] = new SM(smNum, console, masterClock, pioGpio, memory, irq);\n    }\n    smEnabled = 0x0;\n  }\n\n  public void reset()\n  {\n    pioGpio.reset();\n    memory.reset();\n    irq.reset();\n    for (final SM sm : sms) sm.reset();\n    smEnabled = 0x0;\n  }\n\n  public int getIndex()\n  {\n    return index;\n  }\n\n  public int getDBG_CFGINFO_IMEM_SIZE()\n  {\n    return MEMORY_SIZE;\n  }\n\n  public int getDBG_CFGINFO_SM_COUNT()\n  {\n    return SM_COUNT;\n  }\n\n  public int getDBG_CFGINFO_FIFO_DEPTH()\n  {\n    return FIFO_DEPTH;\n  }\n\n  public PrintStream getConsole()\n  {\n    return console;\n  }\n\n  public MasterClock getMasterClock()\n  {\n    return masterClock;\n  }\n\n  public GPIO getGPIO()\n  {\n    return gpio;\n  }\n\n  public PIOGPIO getPIOGPIO()\n  {\n    return pioGpio;\n  }\n\n  public Memory getMemory()\n  {\n    return memory;\n  }\n\n  public SM getSM(final int index)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"state machine index < 0\");\n    }\n    if (index > SM_COUNT) {\n      throw new IllegalArgumentException(\"state machine index > \" + SM_COUNT);\n    }\n    return sms[index];\n  }\n\n  public IRQ getIRQ()\n  {\n    return irq;\n  }\n\n  public int getSM_ENABLED()\n  {\n    return smEnabled;\n  }\n\n  public void setSM_ENABLED(final int smEnabled)\n  {\n    if (smEnabled < 0) {\n      throw new IllegalArgumentException(\"SM_ENABLED < 0: \" + smEnabled);\n    }\n    if (smEnabled > 15) {\n      throw new IllegalArgumentException(\"SM_ENABLED > 15:\" + smEnabled);\n    }\n    this.smEnabled = smEnabled;\n  }\n\n  public int getCtrl()\n  {\n    return getSM_ENABLED();\n  }\n\n  public void setCtrl(final int ctrl, final int mask)\n  {\n    synchronized(sms) {\n      smEnabled = Constants.hwSetBits(smEnabled, ctrl, mask, false) & 0xf;\n      for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n        final boolean clkDivRestart =\n          ((ctrl >> (8 + smNum)) & 0x1) != 0x0 &&\n          ((mask >> (8 + smNum)) & 0x1) != 0x0;\n        final boolean smRestart =\n          ((ctrl >> (4 + smNum)) & 0x1) != 0x0 &&\n          ((mask >> (4 + smNum)) & 0x1) != 0x0;\n        final SM sm = getSM(smNum);\n        if (clkDivRestart) {\n          sm.resetCLKDIV();\n        }\n        if (smRestart) {\n          sm.restart();\n        }\n      }\n    }\n  }\n\n  public void setSideSetCount(final int count)\n  {\n    if (count < 0) {\n      throw new IllegalArgumentException(\"side set count < 0\");\n    }\n    if (count > 5) {\n      throw new IllegalArgumentException(\"side set count > 5\");\n    }\n    for (final SM sm : sms) {\n      sm.setSideSetCount(count);\n    }\n  }\n\n  private boolean smIsEnabled(final int smNum)\n  {\n    if (smNum < 0) {\n      throw new IllegalArgumentException(\"smNum < 0: \" + smNum);\n    }\n    if (smNum > SM_COUNT - 1) {\n      throw new IllegalArgumentException(\"smNum > \" +\n                                         (SM_COUNT - 1) + \": \" +\n                                         smNum);\n    }\n    return (smEnabled & (0x1 << smNum)) != 0x0;\n  }\n\n  public Direction getDirection(final int gpio)\n  {\n    return pioGpio.getDirection(gpio);\n  }\n\n  public Bit getLevel(final int gpio)\n  {\n    return pioGpio.getLevel(gpio);\n  }\n\n  @Override\n  public void risingEdge(final long wallClock)\n  {\n    synchronized(sms) {\n      for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n        final SM sm = getSM(smNum);\n        sm.clockRisingEdge(smIsEnabled(smNum), wallClock);\n      }\n    }\n  }\n\n  @Override\n  public void fallingEdge(final long wallClock) {\n    synchronized(sms) {\n      for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n        final SM sm = getSM(smNum);\n        sm.clockFallingEdge(wallClock);\n      }\n      pioGpio.applyCollatedWrites();\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIOEmuRegisters.java",
    "content": "/*\n * @(#)PIOEmuRegisters.java 1.00 21/03/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\n\n/**\n * Facade to additonal emulator properties of the internal subsystems\n * of a PIO that are not available via the PIORegisters facade.  This\n * facade is in particular intended for use by software that wants to\n * exploit the emulator's debug facilities.\n */\npublic abstract class PIOEmuRegisters extends RegisterSet\n{\n  public enum Regs implements RegistersDocs<Regs>\n  {\n    SM0_REGX(\"Direct read / write access to the SM's%n\" +\n             \"scratch register X.\", 0,\n             new BitsInfo[] {\n               new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n             }),\n    SM0_REGY(\"Direct read / write access to the SM's%n\" +\n             \"scratch register Y.\", 0,\n             new BitsInfo[] {\n               new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n             }),\n    SM0_PC(\"Direct read / write access to the SM's%n\" +\n           \"instruction pointer / program counter.\", 0,\n           new BitsInfo[] {\n             new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n           }),\n    SM0_ISR(\"Direct read / write access to the SM's%n\" +\n            \"input shift register.\", 0,\n            new BitsInfo[] {\n              new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n             }),\n    SM0_ISR_SHIFT_COUNT(\"Direct read / write access to the SM's%n\" +\n                        \"input shift count register.\", 0,\n                        new BitsInfo[] {\n                          new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n                        }),\n    SM0_OSR(\"Direct read / write access to all of the SM's%n\" +\n            \"output shift register.\", 0,\n            new BitsInfo[] {\n              new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n             }),\n    SM0_OSR_SHIFT_COUNT(\"Direct read / write access to the SM's%n\" +\n                        \"output shift count register.\", 0,\n                        new BitsInfo[] {\n                          new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n                        }),\n    SM0_FIFO_MEM0(\"Read / write access to FIFO memory word.\", 0,\n                  new BitsInfo[] {\n                    new BitsInfo(null, 31, 0, null, BitsType.RW, 0)\n                  }),\n    SM0_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 0),\n    SM0_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 0),\n    SM0_CLEAR_FORCED(\"When writing to this address, any pending%n\" +\n                     \"forced instruction is cancelled, provided that%n\" +\n                     \"instruction fetch & decode has not yet been started.\", 0,\n                     new BitsInfo[] {\n                       new BitsInfo(null, 31, 0, null, BitsType.WF, 0)\n                     }),\n    SM0_CLEAR_EXECD(\"When writing to this address, any pending%n\" +\n                    \"EXEC'd instruction is cancelled, provided that%n\" +\n                    \"instruction fetch & decode has not yet been started.\", 0,\n                    new BitsInfo[] {\n                      new BitsInfo(null, 31, 0, null, BitsType.WF, 0)\n                    }),\n    SM0_INSTR_ORIGIN(\"Direct read-only access to the origin of the SM's%n\" +\n                     \"currently executed instruction.  The mode bits%n\" +\n                     \"determine the origin category.  If the origin%n\" +\n                     \"category is memory address, the memory address bits%n\" +\n                     \"will contain the memory instruction's address.%n\" +\n                     \"Otherwise, the bits of the memory address are%n\" +\n                     \"undefined.%n\" +\n                     \"Note that for memory instructions, the address may%n\" +\n                     \"differ from the value of the instruction pointer PC,%n\" +\n                     \"if the PC has already been updated while the%n\" +\n                     \"instruction is still in progress.\", 0,\n                     new BitsInfo[] {\n                       new BitsInfo(null, 31, 7, null, BitsType.RESERVED, null),\n                       new BitsInfo(\"CATEGORY\", 6, 5,\n                                    \"For forced instructions,%n\" +\n                                    \"this is the value \" +\n                                    (INSTR_ORIGIN_FORCED & 0x3) +\n                                    \".%n\" +\n                                    \"For EXEC'd instructions,%n\" +\n                                    \"this is the value \" +\n                                    (INSTR_ORIGIN_EXECD & 0x3) +\n                                    \".%n\" +\n                                    \"Otherwise (e.g. after reset),%n\" +\n                                    \"this is the value \" +\n                                    (INSTR_ORIGIN_UNKNOWN & 0x3) +\n                                    \".%n\",\n                                    BitsType.RO, INSTR_ORIGIN_UNKNOWN & 0x3),\n                       new BitsInfo(\"MEMORY_ADDRESS\", 4, 0,\n                                    \"memory address value (0x00…0x1f)\",\n                                    BitsType.RO, 0)\n                     }),\n    SM0_DELAY(\"Direct read-only access to the SM's%n\" +\n              \"currently executed instruction's number of delay cycles.\", 0,\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null),\n                new BitsInfo(null, 4, 0, null, BitsType.RO, 0)\n              }),\n    SM0_DELAY_CYCLE(\"Read-only access to the SM's delay status.\", 0,\n                    new BitsInfo[] {\n                      new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null),\n                      new BitsInfo(\"DELAY_CYCLE\", 0, 0,\n                                   \"0x1, if the currently executed cycles%n\" +\n                                   \"is a delay cycle.\", BitsType.RO, 0)\n                    }),\n    SM0_PENDING_DELAY(\"Direct read-only access to the SM's%n\" +\n                      \"number of pending delay cycles.\", 0,\n                      new BitsInfo[] {\n                        new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null),\n                        new BitsInfo(\"PENDING_DELAY\", 4, 0,\n                                     \"Number (0x00…0x1f) of pending delays%n\" +\n                                     \"of the currently executed instruction.\",\n                                     BitsType.RO, 0)\n                      }),\n    SM0_FORCED_INSTR(\"Direct read-only access to the op-code of a forced%n\" +\n                     \"instruction that is awaiting execution during the%n\" +\n                     \"next clock cycle.  For writing a forced instruction,%n\" +\n                     \"use SMx_INSTR of PIORegisters instead.\", 0,\n                      new BitsInfo[] {\n                        new BitsInfo(null, 31, 17, null,\n                                     BitsType.RESERVED, null),\n                        new BitsInfo(\"PENDING\", 16, 16,\n                                     \"0x1, if a forced instruction is%n\" +\n                                     \"awaiting execution, otherwise 0x0.\",\n                                     BitsType.RO, 0),\n                        new BitsInfo(\"INSTR\", 15, 0,\n                                     \"Instruction op-code, if any;%n\" +\n                                     \"otherwise, 0x0000.\",\n                                     BitsType.RO, 0)\n                      }),\n    SM0_EXECD_INSTR(\"Direct read/write access to the op-code of an EXEC'd%n\" +\n                    \"instruction that is awaiting execution during the%n\" +\n                    \"next clock cycle, unless the state machine's clock%n\" +\n                    \"signal does not evaluate to true, or there is a%n\" +\n                    \"pending forced instruction, in which case the forced%n\" +\n                    \"instruction will be executed first.\", 0,\n                    new BitsInfo[] {\n                      new BitsInfo(null, 31, 17, null,\n                                   BitsType.RESERVED, null),\n                      new BitsInfo(\"PENDING\", 16, 16,\n                                   \"0x1, if an EXEC'd instruction is%n\" +\n                                   \"awaiting execution, otherwise 0x0.\",\n                                   BitsType.RO, 0),\n                      new BitsInfo(\"INSTR\", 15, 0,\n                                   \"Instruction op-code, if any;%n\" +\n                                   \"otherwise, 0x0000.\",\n                                   BitsType.RW, 0)\n                    }),\n    SM0_CLK_ENABLE(\"Read-only access to the SM's current clock enable status.\",\n                   0,\n                   new BitsInfo[] {\n                     new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null),\n                     new BitsInfo(\"CLK_ENABLE\", 0, 0,\n                                  \"0x1, if in the current cycle the clock%n\" +\n                                  \"enable signal evaluates to 0x1.\",\n                                  BitsType.RO, 0)\n                   }),\n    SM0_NEXT_CLK_ENABLE(\"Read-only access to the SM's next clock enable%n\" +\n                        \"status.  May differ from current clock enable%n\" +\n                        \"when master clock phase 1 has been completed,%n\" +\n                        \"otherwise identical with current clock enable.\", 0,\n                   new BitsInfo[] {\n                     new BitsInfo(null, 31, 1, null, BitsType.UNUSED, null),\n                     new BitsInfo(\"CLK_ENABLE\", 0, 0,\n                                  \"0x1, if in the pre-computed clock%n\" +\n                                  \"enable signal for the upcoming master%n\" +\n                                  \"clock cycle evaluates to 0x1.\",\n                                  BitsType.RO, 0)\n                   }),\n    SM0_BREAKPOINTS(\"Each bit of this value corresponds to each of the%n\" +\n                    \"32 memory locations of the PIO instruction memory%n\" +\n                    \"(with the LSB of the word corresponding to the lowest%n\" +\n                    \"memory address).  Setting a bit to 1 marks the%n\" +\n                    \"corresponding memory address as location of a%n\" +\n                    \"breakpoint.  Setting a bit to 0 removes the%n\" +\n                    \"breakpoint.%n\" +\n                    \"%n\" +\n                    \"As soon as the program counter of the state machine%n\" +\n                    \"reaches an address that is marked as a breakpoint,%n\" +\n                    \"master clock MASTERCLK_MODE will be automatically set%n\" +\n                    \"to single step mode.\", 0,\n                    IntStream.rangeClosed(0, 31).boxed()\n                    .map(n -> new BitsInfo(\"BP_MEM\" + (31 - n), 31 - n, 31 - n,\n                                           \"0x1, if the memory address is \" +\n                                           \"marked as breakpoint.\",\n                                           BitsType.RW, 0))\n                    .collect(Collectors.toList())),\n    SM0_TRACEPOINTS(\"Tracepoints work like breakpoints with the difference%n\" +\n                    \"that master clock MASTERCLK_MODE it not automatically%n\" +\n                    \"set to single step mode, but instead a message is%n\" +\n                    \"typically printed to console output (depending on%n\" +\n                    \"the specific client application).  The message may,%n\" +\n                    \"for example, caontain the state machine's number and%n\" +\n                    \"disassembled instruction with prefixed instruction%n\" +\n                    \"memory address.  Tracepoints work in all master clock%n\" +\n                    \"MASTERCLK_MODE modes.\", 0,\n                    IntStream.rangeClosed(0, 31).boxed()\n                    .map(n -> new BitsInfo(\"TP_MEM\" + (31 - n), 31 - n, 31 - n,\n                                           \"0x1, if the memory address is \" +\n                                           \"marked as tracepoint.\",\n                                           BitsType.RW, 0))\n                    .collect(Collectors.toList())),\n    SM1_REGX(Regs.SM0_REGX, 1),\n    SM1_REGY(Regs.SM0_REGY, 1),\n    SM1_PC(Regs.SM0_PC, 1),\n    SM1_ISR(Regs.SM0_ISR, 1),\n    SM1_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 1),\n    SM1_OSR(Regs.SM0_OSR, 1),\n    SM1_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 1),\n    SM1_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 1),\n    SM1_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 1),\n    SM1_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 1),\n    SM1_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 1),\n    SM1_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 1),\n    SM1_DELAY(Regs.SM0_DELAY, 1),\n    SM1_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 1),\n    SM1_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 1),\n    SM1_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 1),\n    SM1_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 1),\n    SM1_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 1),\n    SM1_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 1),\n    SM1_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 1),\n    SM1_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 1),\n    SM2_REGX(Regs.SM0_REGX, 2),\n    SM2_REGY(Regs.SM0_REGY, 2),\n    SM2_PC(Regs.SM0_PC, 2),\n    SM2_ISR(Regs.SM0_ISR, 2),\n    SM2_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 2),\n    SM2_OSR(Regs.SM0_OSR, 2),\n    SM2_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 2),\n    SM2_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 2),\n    SM2_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 2),\n    SM2_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 2),\n    SM2_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 2),\n    SM2_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 2),\n    SM2_DELAY(Regs.SM0_DELAY, 2),\n    SM2_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 2),\n    SM2_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 2),\n    SM2_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 2),\n    SM2_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 2),\n    SM2_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 2),\n    SM2_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 2),\n    SM2_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 2),\n    SM2_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 2),\n    SM3_REGX(Regs.SM0_REGX, 3),\n    SM3_REGY(Regs.SM0_REGY, 3),\n    SM3_PC(Regs.SM0_PC, 3),\n    SM3_ISR(Regs.SM0_ISR, 3),\n    SM3_ISR_SHIFT_COUNT(Regs.SM0_ISR_SHIFT_COUNT, 3),\n    SM3_OSR(Regs.SM0_OSR, 3),\n    SM3_OSR_SHIFT_COUNT(Regs.SM0_OSR_SHIFT_COUNT, 3),\n    SM3_FIFO_MEM0(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM1(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM2(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM3(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM4(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM5(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM6(Regs.SM0_FIFO_MEM0, 3),\n    SM3_FIFO_MEM7(Regs.SM0_FIFO_MEM0, 3),\n    SM3_CLEAR_FORCED(Regs.SM0_CLEAR_FORCED, 3),\n    SM3_CLEAR_EXECD(Regs.SM0_CLEAR_EXECD, 3),\n    SM3_INSTR_ORIGIN(Regs.SM0_INSTR_ORIGIN, 3),\n    SM3_DELAY(Regs.SM0_DELAY, 3),\n    SM3_DELAY_CYCLE(Regs.SM0_DELAY_CYCLE, 3),\n    SM3_PENDING_DELAY(Regs.SM0_PENDING_DELAY, 3),\n    SM3_FORCED_INSTR(Regs.SM0_FORCED_INSTR, 3),\n    SM3_EXECD_INSTR(Regs.SM0_EXECD_INSTR, 3),\n    SM3_CLK_ENABLE(Regs.SM0_CLK_ENABLE, 3),\n    SM3_NEXT_CLK_ENABLE(Regs.SM0_NEXT_CLK_ENABLE, 3),\n    SM3_BREAKPOINTS(Regs.SM0_BREAKPOINTS, 3),\n    SM3_TRACEPOINTS(Regs.SM0_TRACEPOINTS, 3),\n    INSTR_MEM0(\"Read / write access to instruction memory word.\",\n               new BitsInfo[] {\n                 new BitsInfo(null, 31, 16, null, BitsType.UNUSED, null),\n                 new BitsInfo(null, 15, 0, null, BitsType.RW, 0)\n               }),\n    INSTR_MEM1(Regs.INSTR_MEM0),\n    INSTR_MEM2(Regs.INSTR_MEM0),\n    INSTR_MEM3(Regs.INSTR_MEM0),\n    INSTR_MEM4(Regs.INSTR_MEM0),\n    INSTR_MEM5(Regs.INSTR_MEM0),\n    INSTR_MEM6(Regs.INSTR_MEM0),\n    INSTR_MEM7(Regs.INSTR_MEM0),\n    INSTR_MEM8(Regs.INSTR_MEM0),\n    INSTR_MEM9(Regs.INSTR_MEM0),\n    INSTR_MEM10(Regs.INSTR_MEM0),\n    INSTR_MEM11(Regs.INSTR_MEM0),\n    INSTR_MEM12(Regs.INSTR_MEM0),\n    INSTR_MEM13(Regs.INSTR_MEM0),\n    INSTR_MEM14(Regs.INSTR_MEM0),\n    INSTR_MEM15(Regs.INSTR_MEM0),\n    INSTR_MEM16(Regs.INSTR_MEM0),\n    INSTR_MEM17(Regs.INSTR_MEM0),\n    INSTR_MEM18(Regs.INSTR_MEM0),\n    INSTR_MEM19(Regs.INSTR_MEM0),\n    INSTR_MEM20(Regs.INSTR_MEM0),\n    INSTR_MEM21(Regs.INSTR_MEM0),\n    INSTR_MEM22(Regs.INSTR_MEM0),\n    INSTR_MEM23(Regs.INSTR_MEM0),\n    INSTR_MEM24(Regs.INSTR_MEM0),\n    INSTR_MEM25(Regs.INSTR_MEM0),\n    INSTR_MEM26(Regs.INSTR_MEM0),\n    INSTR_MEM27(Regs.INSTR_MEM0),\n    INSTR_MEM28(Regs.INSTR_MEM0),\n    INSTR_MEM29(Regs.INSTR_MEM0),\n    INSTR_MEM30(Regs.INSTR_MEM0),\n    INSTR_MEM31(Regs.INSTR_MEM0),\n    TXF0(\"Direct read access to the TX FIFO for the corresponding state%n\" +\n         \"machine.  Each read pops one word from the FIFO. Attempting to%n\" +\n         \"read from an empty FIFO has no effect on the FIFO state,%n\" +\n         \"and sets the sticky FDEBUG_TXUNDER error flag for this FIFO.%n\" +\n         \"The data returned to the system on a read from an empty FIFO%n\" +\n         \"is undefined.\", 0,\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 0, null, BitsType.RF, null)\n         }),\n    TXF1(Regs.TXF0, 1),\n    TXF2(Regs.TXF0, 2),\n    TXF3(Regs.TXF0, 3),\n    RXF0(\"Direct write access to the RX FIFO for the corresponding state%n\" +\n         \"machine.  Each write pushes one word to the FIFO.  Attempting to%n\" +\n         \"write to a full FIFO has no effect on the FIFO state or contents,%n\" +\n         \"and sets the sticky FDEBUG_RXOVER error flag for this FIFO.\", 0,\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 0, null, BitsType.WF, 0)\n         }),\n    RXF1(Regs.RXF0, 1),\n    RXF2(Regs.RXF0, 2),\n    RXF3(Regs.RXF0, 3),\n    FREAD_PTR(\"Read pointers of all of the SM's TX and RX FIFOs.\",\n              IntStream.rangeClosed(0, 7).boxed()\n              .map(n -> new BitsInfo(((n & 0x1) == 0 ? \"TX\" : \"RX\") + \"F\" +\n                                     (n >> 1) + \"_READ_PTR\",\n                                     31 - (n << 2),\n                                     28 - (n << 2),\n                                     \"Offset (0…7) within FIFO memory for%n\" +\n                                     \"the next FIFO read operation\",\n                                     BitsType.RO, 0))\n              .collect(Collectors.toList())),\n    GPIO_PINS(\"Direct read / write access to all of the 32 GPIO pins%n\" +\n              \"that PIO is currently driving to the GPIOs.\",\n              IntStream.rangeClosed(0, 31).boxed()\n              .map(n -> new BitsInfo(\"GPIO_PIN\" + (31 - n), 31 - n, 31 - n,\n                                     \"0x1 for HIGH or 0x0 for LOW\",\n                                     BitsType.RW, 0))\n              .collect(Collectors.toList())),\n    GPIO_PINDIRS(\"Direct read / write access to all of the 32 GPIO pin%n\" +\n                 \"directions that PIO is currently driving to the GPIOs.\",\n                 IntStream.rangeClosed(0, 31).boxed()\n                 .map(n -> new BitsInfo(\"GPIO_PINDIR\" + (31 - n),\n                                        31 - n, 31 - n,\n                                        \"0x1 for pin direction out or%n\" +\n                                        \"0x0 for pin direction in\",\n                                        BitsType.RW, 0))\n                 .collect(Collectors.toList())),\n    IRQ(\"Direct read access of all PIO IRQ.\",\n        IntStream.rangeClosed(0, 7).boxed()\n        .map(n -> new BitsInfo(\"IRQ\" + (7 - n), 7 - n, 7 - n,\n                               \"0x1 for HIGH or 0x0 for LOW\",\n                               BitsType.RO, 0))\n        .collect(Collectors.toList()));\n\n    public static String getRegisterSetLabel()\n    {\n      return \"Emulator PIO Registers\";\n    }\n\n    public static String getRegisterSetDescription()\n    {\n      return\n        \"The PIO emulator provides registers in addition to those%n\" +\n        \"of the PIO as specified in the RP2040 datasheet to allow%n\" +\n        \"for inspection of more details of the PIO's internal state%n\" +\n        \"such as its scratch registers X and Y, its shift registers%n\" +\n        \"ISR, OSR, FIFO memory, and read access to PIO instruction%n\" +\n        \"memory for enhanced debugging of programs.%n\" +\n        \"Base address for the two emulator PIO register sets (one %n\" +\n        \"register set for each of the two PIOs) is%n\" +\n        String.format(\"0x%08x and 0x%08x for PIO0 and PIO1, respectively.%n\",\n                      PIO0_EMU_BASE, PIO1_EMU_BASE);\n    }\n\n    private final RegisterDetails registerDetails;\n\n    private Regs()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Regs(final Regs ref)\n    {\n      this(ref.registerDetails);\n    }\n\n    private Regs(final Regs ref, final int smNum)\n    {\n      this(ref.registerDetails.createCopyForDifferentSm(smNum));\n    }\n\n    private Regs(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final RegisterDetails registerDetails)\n    {\n      this.registerDetails = registerDetails;\n    }\n\n    @Override\n    public String getInfo()\n    {\n      return registerDetails.getInfo();\n    }\n\n    @Override\n    public RegisterDetails getRegisterDetails()\n    {\n      return registerDetails;\n    }\n  }\n\n  protected static final Regs[] REGS = Regs.values();\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  protected <T extends Enum<T>> T[] getRegs() { return (T[])REGS; }\n\n  protected static final int SM_SIZE =\n    Regs.SM1_REGX.ordinal() - Regs.SM0_REGX.ordinal();\n\n  public static int getAddress(final int pioNum,\n                               final PIOEmuRegisters.Regs register)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    return Constants.getPIOEmuBaseAddress(pioNum) + 0x4 * register.ordinal();\n  }\n\n  public static int getSMAddress(final int pioNum,\n                                 final int smNum,\n                                 final PIOEmuRegisters.Regs register)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    switch (register) {\n    case SM0_REGX:\n    case SM0_REGY:\n    case SM0_PC:\n    case SM0_ISR:\n    case SM0_ISR_SHIFT_COUNT:\n    case SM0_OSR:\n    case SM0_OSR_SHIFT_COUNT:\n    case SM0_FIFO_MEM0:\n    case SM0_FIFO_MEM1:\n    case SM0_FIFO_MEM2:\n    case SM0_FIFO_MEM3:\n    case SM0_FIFO_MEM4:\n    case SM0_FIFO_MEM5:\n    case SM0_FIFO_MEM6:\n    case SM0_FIFO_MEM7:\n    case SM0_CLEAR_FORCED:\n    case SM0_CLEAR_EXECD:\n    case SM0_INSTR_ORIGIN:\n    case SM0_DELAY:\n    case SM0_DELAY_CYCLE:\n    case SM0_PENDING_DELAY:\n    case SM0_FORCED_INSTR:\n    case SM0_EXECD_INSTR:\n    case SM0_CLK_ENABLE:\n    case SM0_NEXT_CLK_ENABLE:\n    case SM0_BREAKPOINTS:\n    case SM0_TRACEPOINTS:\n      break; // ok\n    default:\n      throw new IllegalArgumentException(\"register not one of SM0_*: \" +\n                                         register);\n    }\n    return\n      Constants.getPIOEmuBaseAddress(pioNum) +\n      0x4 * (register.ordinal() + smNum * SM_SIZE);\n  }\n\n  public static int getFIFOMemAddress(final int pioNum, final int smNum,\n                                      final int address)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    Constants.checkFIFOAddr(address, \"FIFO address\");\n    return\n      Constants.getPIOEmuBaseAddress(pioNum) +\n      0x4 * (Regs.SM0_FIFO_MEM0.ordinal() + smNum * SM_SIZE + address);\n  }\n\n  public static int getMemoryAddress(final int pioNum,\n                                     final int memoryAddress)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmMemAddr(memoryAddress, \"memory address\");\n    return\n      Constants.getPIOEmuBaseAddress(pioNum) +\n      0x4 * (Regs.INSTR_MEM0.ordinal() + memoryAddress);\n  }\n\n  public static int getTXFAddress(final int pioNum, final int smNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    return\n      Constants.getPIOEmuBaseAddress(pioNum) +\n      0x4 * (Regs.TXF0.ordinal() + smNum);\n  }\n\n  public static int getRXFAddress(final int pioNum, final int smNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    return\n      Constants.getPIOEmuBaseAddress(pioNum) +\n      0x4 * (Regs.RXF0.ordinal() + smNum);\n  }\n\n  public PIOEmuRegisters(final String id, final int baseAddress)\n  {\n    super(id, baseAddress);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIOEmuRegistersImpl.java",
    "content": "/*\n * @(#)PIOEmuRegistersImpl.java 1.00 21/03/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Facade to additonal emulator properties of the internal subsystems\n * of a PIO that are not available via the PIORegisters facade.  This\n * facade is in particular intended for use by software that wants to\n * exploit the emulator's debug facilities.\n */\npublic class PIOEmuRegistersImpl extends PIOEmuRegisters\n{\n  private final PIO pio;\n\n  public PIOEmuRegistersImpl(final PIO pio)\n  {\n    super(\"PIOEmu\" + pio.getIndex(),\n          Constants.getPIOEmuBaseAddress(pio.getIndex()));\n    this.pio = pio;\n  }\n\n  public PIO getPIO() { return pio; }\n\n  public int getPIOIndex()\n  {\n    return pio.getIndex();\n  }\n\n  public int getAddress(final PIOEmuRegisters.Regs register)\n  {\n    return getAddress(getPIOIndex(), register);\n  }\n\n  public int getSMAddress(final PIOEmuRegisters.Regs register, final int smNum)\n  {\n    return getSMAddress(getPIOIndex(), smNum, register);\n  }\n\n  public int getFIFOMemAddress(final int smNum, final int address)\n  {\n    return getFIFOMemAddress(getPIOIndex(), smNum, address);\n  }\n\n  public int getMemoryAddress(final int memoryAddress)\n  {\n    return getMemoryAddress(getPIOIndex(), memoryAddress);\n  }\n\n  private void setFIFOMemValue(final int regsOffset, final int value,\n                               final int mask, final boolean xor)\n  {\n    final int smNum = regsOffset / SM_SIZE;\n    Constants.checkSmNum(smNum);\n    final int address = regsOffset - SM_SIZE * smNum;\n    final FIFO fifo = pio.getSM(smNum).getFIFO();\n    fifo.setMemValue(address,\n                     Constants.hwSetBits(fifo.getMemValue(address),\n                                         value, mask, xor));\n  }\n\n  @Override\n  public void writeRegister(final int regNum, final int value,\n                            final int mask, final boolean xor)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case SM0_REGX:\n    case SM1_REGX:\n    case SM2_REGX:\n    case SM3_REGX:\n      pio.getSM((regNum - Regs.SM0_REGX.ordinal()) / SM_SIZE).\n        setX(value, mask, xor);\n      break;\n    case SM0_REGY:\n    case SM1_REGY:\n    case SM2_REGY:\n    case SM3_REGY:\n      pio.getSM((regNum - Regs.SM0_REGY.ordinal()) / SM_SIZE).\n        setY(value, mask, xor);\n      break;\n    case SM0_PC:\n    case SM1_PC:\n    case SM2_PC:\n    case SM3_PC:\n      pio.getSM((regNum - Regs.SM0_PC.ordinal()) / SM_SIZE).\n        setPC(value, mask, xor);\n      break;\n    case SM0_ISR:\n    case SM1_ISR:\n    case SM2_ISR:\n    case SM3_ISR:\n      pio.getSM((regNum - Regs.SM0_ISR.ordinal()) / SM_SIZE).\n        setISRValue(value, mask, xor);\n      break;\n    case SM0_ISR_SHIFT_COUNT:\n    case SM1_ISR_SHIFT_COUNT:\n    case SM2_ISR_SHIFT_COUNT:\n    case SM3_ISR_SHIFT_COUNT:\n      pio.getSM((regNum - Regs.SM0_ISR_SHIFT_COUNT.ordinal()) / SM_SIZE).\n        setISRShiftCount(value, mask, xor);\n      break;\n    case SM0_OSR:\n    case SM1_OSR:\n    case SM2_OSR:\n    case SM3_OSR:\n      pio.getSM((regNum - Regs.SM0_OSR.ordinal()) / SM_SIZE).\n        setOSRValue(value, mask, xor);\n      break;\n    case SM0_OSR_SHIFT_COUNT:\n    case SM1_OSR_SHIFT_COUNT:\n    case SM2_OSR_SHIFT_COUNT:\n    case SM3_OSR_SHIFT_COUNT:\n      pio.getSM((regNum - Regs.SM0_OSR_SHIFT_COUNT.ordinal()) / SM_SIZE).\n        setOSRShiftCount(value, mask, xor);\n      break;\n    case SM0_FIFO_MEM0:\n    case SM0_FIFO_MEM1:\n    case SM0_FIFO_MEM2:\n    case SM0_FIFO_MEM3:\n    case SM0_FIFO_MEM4:\n    case SM0_FIFO_MEM5:\n    case SM0_FIFO_MEM6:\n    case SM0_FIFO_MEM7:\n    case SM1_FIFO_MEM0:\n    case SM1_FIFO_MEM1:\n    case SM1_FIFO_MEM2:\n    case SM1_FIFO_MEM3:\n    case SM1_FIFO_MEM4:\n    case SM1_FIFO_MEM5:\n    case SM1_FIFO_MEM6:\n    case SM1_FIFO_MEM7:\n    case SM2_FIFO_MEM0:\n    case SM2_FIFO_MEM1:\n    case SM2_FIFO_MEM2:\n    case SM2_FIFO_MEM3:\n    case SM2_FIFO_MEM4:\n    case SM2_FIFO_MEM5:\n    case SM2_FIFO_MEM6:\n    case SM2_FIFO_MEM7:\n    case SM3_FIFO_MEM0:\n    case SM3_FIFO_MEM1:\n    case SM3_FIFO_MEM2:\n    case SM3_FIFO_MEM3:\n    case SM3_FIFO_MEM4:\n    case SM3_FIFO_MEM5:\n    case SM3_FIFO_MEM6:\n    case SM3_FIFO_MEM7:\n      setFIFOMemValue(regNum - Regs.SM0_FIFO_MEM0.ordinal(), value, mask, xor);\n      break;\n    case SM0_CLEAR_FORCED:\n    case SM1_CLEAR_FORCED:\n    case SM2_CLEAR_FORCED:\n    case SM3_CLEAR_FORCED:\n      pio.getSM((regNum - Regs.SM0_CLEAR_FORCED.ordinal()) / SM_SIZE).\n        clearPendingForcedInstruction();\n      break;\n    case SM0_CLEAR_EXECD:\n    case SM1_CLEAR_EXECD:\n    case SM2_CLEAR_EXECD:\n    case SM3_CLEAR_EXECD:\n      pio.getSM((regNum - Regs.SM0_CLEAR_EXECD.ordinal()) / SM_SIZE).\n        clearPendingExecdInstruction();\n      break;\n    case SM0_INSTR_ORIGIN:\n    case SM1_INSTR_ORIGIN:\n    case SM2_INSTR_ORIGIN:\n    case SM3_INSTR_ORIGIN:\n      break; // (for now) read-only address\n    case SM0_DELAY:\n    case SM1_DELAY:\n    case SM2_DELAY:\n    case SM3_DELAY:\n      break; // (for now) read-only address\n    case SM0_DELAY_CYCLE:\n    case SM1_DELAY_CYCLE:\n    case SM2_DELAY_CYCLE:\n    case SM3_DELAY_CYCLE:\n      break; // (for now) read-only address\n    case SM0_PENDING_DELAY:\n    case SM1_PENDING_DELAY:\n    case SM2_PENDING_DELAY:\n    case SM3_PENDING_DELAY:\n      break; // (for now) read-only address\n    case SM0_FORCED_INSTR:\n    case SM1_FORCED_INSTR:\n    case SM2_FORCED_INSTR:\n    case SM3_FORCED_INSTR:\n      break; // (for now) read-only address\n    case SM0_EXECD_INSTR:\n    case SM1_EXECD_INSTR:\n    case SM2_EXECD_INSTR:\n    case SM3_EXECD_INSTR:\n      pio.getSM((regNum - Regs.SM0_EXECD_INSTR.ordinal()) / SM_SIZE).\n        execInstruction(value & mask);\n      break;\n    case SM0_CLK_ENABLE:\n    case SM1_CLK_ENABLE:\n    case SM2_CLK_ENABLE:\n    case SM3_CLK_ENABLE:\n      break; // (for now) read-only address\n    case SM0_NEXT_CLK_ENABLE:\n    case SM1_NEXT_CLK_ENABLE:\n    case SM2_NEXT_CLK_ENABLE:\n    case SM3_NEXT_CLK_ENABLE:\n      break; // (for now) read-only address\n    case SM0_BREAKPOINTS:\n    case SM1_BREAKPOINTS:\n    case SM2_BREAKPOINTS:\n    case SM3_BREAKPOINTS:\n      pio.getSM((regNum - Regs.SM0_BREAKPOINTS.ordinal()) / SM_SIZE).\n        setBreakPoints(value, mask, xor);\n      break;\n    case SM0_TRACEPOINTS:\n    case SM1_TRACEPOINTS:\n    case SM2_TRACEPOINTS:\n    case SM3_TRACEPOINTS:\n      pio.getSM((regNum - Regs.SM0_TRACEPOINTS.ordinal()) / SM_SIZE).\n        setTracePoints(value, mask, xor);\n      break;\n    case INSTR_MEM0:\n    case INSTR_MEM1:\n    case INSTR_MEM2:\n    case INSTR_MEM3:\n    case INSTR_MEM4:\n    case INSTR_MEM5:\n    case INSTR_MEM6:\n    case INSTR_MEM7:\n    case INSTR_MEM8:\n    case INSTR_MEM9:\n    case INSTR_MEM10:\n    case INSTR_MEM11:\n    case INSTR_MEM12:\n    case INSTR_MEM13:\n    case INSTR_MEM14:\n    case INSTR_MEM15:\n    case INSTR_MEM16:\n    case INSTR_MEM17:\n    case INSTR_MEM18:\n    case INSTR_MEM19:\n    case INSTR_MEM20:\n    case INSTR_MEM21:\n    case INSTR_MEM22:\n    case INSTR_MEM23:\n    case INSTR_MEM24:\n    case INSTR_MEM25:\n    case INSTR_MEM26:\n    case INSTR_MEM27:\n    case INSTR_MEM28:\n    case INSTR_MEM29:\n    case INSTR_MEM30:\n    case INSTR_MEM31:\n      pio.getMemory().set(regNum - Regs.INSTR_MEM0.ordinal(), value, mask, xor);\n      break;\n    case RXF0:\n    case RXF1:\n    case RXF2:\n    case RXF3:\n      pio.getSM(regNum - Regs.RXF0.ordinal()).putRXF(value & mask);\n      break;\n    case TXF0:\n    case TXF1:\n    case TXF2:\n    case TXF3:\n      break; // read-only address\n    case FREAD_PTR:\n      break; // read-only address\n    case GPIO_PINS:\n      pio.getPIOGPIO().setPinsMask(value, mask, xor);\n      break;\n    case GPIO_PINDIRS:\n      pio.getPIOGPIO().setPinDirsMask(value, mask, xor);\n      break;\n    case IRQ:\n      break; // read-only address\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  private int getClockEnable(final int smNum)\n  {\n    Constants.checkSmNum(smNum);\n    final boolean clockEnable = pio.getSM(smNum).getPLL().getClockEnable();\n    return clockEnable ? 0x1 : 0x0;\n  }\n\n  private int getNextClockEnable(final int smNum)\n  {\n    Constants.checkSmNum(smNum);\n    final boolean nextClockEnable =\n      pio.getSM(smNum).getPLL().getNextClockEnable();\n    return nextClockEnable ? 0x1 : 0x0;\n  }\n\n  private int getFIFOMemValue(final int regsOffset)\n  {\n    final int smNum = regsOffset / SM_SIZE;\n    final int address = regsOffset - SM_SIZE * smNum;\n    Constants.checkSmNum(smNum);\n    return pio.getSM(smNum).getFIFO().getMemValue(address);\n  }\n\n  private int getFIFOReadPointers()\n  {\n    int readPointers = 0;\n    for (int smNum = SM_COUNT - 1; smNum >= 0; smNum--) {\n      final FIFO fifo = pio.getSM(smNum).getFIFO();\n      readPointers <<= 8;\n      readPointers |= (fifo.getRXReadPointer() & 0x7) << 4;\n      readPointers |= fifo.getTXReadPointer() & 0x7;\n    }\n    return readPointers;\n  }\n\n  @Override\n  public synchronized int readRegister(final int regNum)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case SM0_REGX:\n    case SM1_REGX:\n    case SM2_REGX:\n    case SM3_REGX:\n      return\n        pio.getSM((regNum - Regs.SM0_REGX.ordinal()) / SM_SIZE).getX();\n    case SM0_REGY:\n    case SM1_REGY:\n    case SM2_REGY:\n    case SM3_REGY:\n      return\n        pio.getSM((regNum - Regs.SM0_REGY.ordinal()) / SM_SIZE).getY();\n    case SM0_PC:\n    case SM1_PC:\n    case SM2_PC:\n    case SM3_PC:\n      return\n        pio.getSM((regNum - Regs.SM0_PC.ordinal()) / SM_SIZE).getPC();\n    case SM0_ISR:\n    case SM1_ISR:\n    case SM2_ISR:\n    case SM3_ISR:\n      return\n        pio.getSM((regNum - Regs.SM0_ISR.ordinal()) / SM_SIZE).getISRValue();\n    case SM0_ISR_SHIFT_COUNT:\n    case SM1_ISR_SHIFT_COUNT:\n    case SM2_ISR_SHIFT_COUNT:\n    case SM3_ISR_SHIFT_COUNT:\n      return\n        pio.getSM((regNum - Regs.SM0_ISR_SHIFT_COUNT.ordinal()) / SM_SIZE).\n        getISRShiftCount();\n    case SM0_OSR:\n    case SM1_OSR:\n    case SM2_OSR:\n    case SM3_OSR:\n      return\n        pio.getSM((regNum - Regs.SM0_OSR.ordinal()) / SM_SIZE).getOSRValue();\n    case SM0_OSR_SHIFT_COUNT:\n    case SM1_OSR_SHIFT_COUNT:\n    case SM2_OSR_SHIFT_COUNT:\n    case SM3_OSR_SHIFT_COUNT:\n      return\n        pio.getSM((regNum - Regs.SM0_OSR_SHIFT_COUNT.ordinal()) / SM_SIZE).\n        getOSRShiftCount();\n    case SM0_FIFO_MEM0:\n    case SM0_FIFO_MEM1:\n    case SM0_FIFO_MEM2:\n    case SM0_FIFO_MEM3:\n    case SM0_FIFO_MEM4:\n    case SM0_FIFO_MEM5:\n    case SM0_FIFO_MEM6:\n    case SM0_FIFO_MEM7:\n    case SM1_FIFO_MEM0:\n    case SM1_FIFO_MEM1:\n    case SM1_FIFO_MEM2:\n    case SM1_FIFO_MEM3:\n    case SM1_FIFO_MEM4:\n    case SM1_FIFO_MEM5:\n    case SM1_FIFO_MEM6:\n    case SM1_FIFO_MEM7:\n    case SM2_FIFO_MEM0:\n    case SM2_FIFO_MEM1:\n    case SM2_FIFO_MEM2:\n    case SM2_FIFO_MEM3:\n    case SM2_FIFO_MEM4:\n    case SM2_FIFO_MEM5:\n    case SM2_FIFO_MEM6:\n    case SM2_FIFO_MEM7:\n    case SM3_FIFO_MEM0:\n    case SM3_FIFO_MEM1:\n    case SM3_FIFO_MEM2:\n    case SM3_FIFO_MEM3:\n    case SM3_FIFO_MEM4:\n    case SM3_FIFO_MEM5:\n    case SM3_FIFO_MEM6:\n    case SM3_FIFO_MEM7:\n      return getFIFOMemValue(regNum - Regs.SM0_FIFO_MEM0.ordinal());\n    case SM0_CLEAR_FORCED:\n    case SM1_CLEAR_FORCED:\n    case SM2_CLEAR_FORCED:\n    case SM3_CLEAR_FORCED:\n      return 0; // write-only address\n    case SM0_CLEAR_EXECD:\n    case SM1_CLEAR_EXECD:\n    case SM2_CLEAR_EXECD:\n    case SM3_CLEAR_EXECD:\n      return 0; // write-only address\n    case SM0_INSTR_ORIGIN:\n    case SM1_INSTR_ORIGIN:\n    case SM2_INSTR_ORIGIN:\n    case SM3_INSTR_ORIGIN:\n      return\n        pio.getSM((regNum - Regs.SM0_INSTR_ORIGIN.ordinal()) / SM_SIZE).\n        getINSTR_ORIGIN();\n    case SM0_DELAY:\n    case SM1_DELAY:\n    case SM2_DELAY:\n    case SM3_DELAY:\n      return\n        pio.getSM((regNum - Regs.SM0_DELAY.ordinal()) / SM_SIZE).\n        getTotalDelay();\n    case SM0_DELAY_CYCLE:\n    case SM1_DELAY_CYCLE:\n    case SM2_DELAY_CYCLE:\n    case SM3_DELAY_CYCLE:\n      return\n        pio.getSM((regNum - Regs.SM0_DELAY_CYCLE.ordinal()) / SM_SIZE).\n        isDelayCycle() ? 0x1 : 0x0;\n    case SM0_PENDING_DELAY:\n    case SM1_PENDING_DELAY:\n    case SM2_PENDING_DELAY:\n    case SM3_PENDING_DELAY:\n      return\n        pio.getSM((regNum - Regs.SM0_PENDING_DELAY.ordinal()) / SM_SIZE).\n        getPendingDelay();\n    case SM0_FORCED_INSTR:\n    case SM1_FORCED_INSTR:\n    case SM2_FORCED_INSTR:\n    case SM3_FORCED_INSTR:\n      return\n        pio.getSM((regNum - Regs.SM0_FORCED_INSTR.ordinal()) / SM_SIZE).\n        getFORCED_INSTR();\n    case SM0_EXECD_INSTR:\n    case SM1_EXECD_INSTR:\n    case SM2_EXECD_INSTR:\n    case SM3_EXECD_INSTR:\n      return\n        pio.getSM((regNum - Regs.SM0_EXECD_INSTR.ordinal()) / SM_SIZE).\n        getEXECD_INSTR();\n    case SM0_CLK_ENABLE:\n    case SM1_CLK_ENABLE:\n    case SM2_CLK_ENABLE:\n    case SM3_CLK_ENABLE:\n      return getClockEnable((regNum - Regs.SM0_CLK_ENABLE.ordinal()) / SM_SIZE);\n    case SM0_NEXT_CLK_ENABLE:\n    case SM1_NEXT_CLK_ENABLE:\n    case SM2_NEXT_CLK_ENABLE:\n    case SM3_NEXT_CLK_ENABLE:\n      return getNextClockEnable((regNum - Regs.SM0_CLK_ENABLE.ordinal()) / SM_SIZE);\n    case SM0_BREAKPOINTS:\n    case SM1_BREAKPOINTS:\n    case SM2_BREAKPOINTS:\n    case SM3_BREAKPOINTS:\n      return\n        pio.getSM((regNum - Regs.SM0_BREAKPOINTS.ordinal()) / SM_SIZE).\n        getBreakPoints();\n    case SM0_TRACEPOINTS:\n    case SM1_TRACEPOINTS:\n    case SM2_TRACEPOINTS:\n    case SM3_TRACEPOINTS:\n      return\n        pio.getSM((regNum - Regs.SM0_TRACEPOINTS.ordinal()) / SM_SIZE).\n        getTracePoints();\n    case INSTR_MEM0:\n    case INSTR_MEM1:\n    case INSTR_MEM2:\n    case INSTR_MEM3:\n    case INSTR_MEM4:\n    case INSTR_MEM5:\n    case INSTR_MEM6:\n    case INSTR_MEM7:\n    case INSTR_MEM8:\n    case INSTR_MEM9:\n    case INSTR_MEM10:\n    case INSTR_MEM11:\n    case INSTR_MEM12:\n    case INSTR_MEM13:\n    case INSTR_MEM14:\n    case INSTR_MEM15:\n    case INSTR_MEM16:\n    case INSTR_MEM17:\n    case INSTR_MEM18:\n    case INSTR_MEM19:\n    case INSTR_MEM20:\n    case INSTR_MEM21:\n    case INSTR_MEM22:\n    case INSTR_MEM23:\n    case INSTR_MEM24:\n    case INSTR_MEM25:\n    case INSTR_MEM26:\n    case INSTR_MEM27:\n    case INSTR_MEM28:\n    case INSTR_MEM29:\n    case INSTR_MEM30:\n    case INSTR_MEM31:\n      return pio.getMemory().get(regNum - Regs.INSTR_MEM0.ordinal()) & 0xffff;\n    case TXF0:\n    case TXF1:\n    case TXF2:\n    case TXF3:\n      return pio.getSM(regNum - Regs.TXF0.ordinal()).getTXF();\n    case RXF0:\n    case RXF1:\n    case RXF2:\n    case RXF3:\n      return 0; // write-only address\n    case FREAD_PTR:\n      return getFIFOReadPointers();\n    case GPIO_PINS:\n      return pio.getPIOGPIO().getPins(0, GPIO_NUM);\n    case GPIO_PINDIRS:\n      return pio.getPIOGPIO().getPinDirs(0, GPIO_NUM);\n    case IRQ:\n      return pio.getIRQ().getIRQ();\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIOGPIO.java",
    "content": "/*\n * @(#)PIOGPIO.java 1.00 21/03/19\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * General-Purpose Set of 32 Peripheral I/O Terminals\n */\npublic class PIOGPIO implements Constants\n{\n  private final GPIO gpio;\n  private final Bit[] collatedLevels;\n  private final Direction[] collatedDirections;\n  private final PinState[] appliedStates;\n\n  private PIOGPIO()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PIOGPIO(final GPIO gpio)\n  {\n    if (gpio == null) {\n      throw new NullPointerException(\"gpio\");\n    }\n    this.gpio = gpio;\n    collatedLevels = new Bit[GPIO_NUM];\n    collatedDirections = new Direction[GPIO_NUM];\n    appliedStates = new PinState[GPIO_NUM];\n    reset();\n  }\n\n  public void reset()\n  {\n    for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) {\n      collatedLevels[gpioNum] = null;\n      collatedDirections[gpioNum] = null;\n      appliedStates[gpioNum] = PinState.IN_LOW;\n    }\n  }\n\n  public GPIO getGPIO() { return gpio; }\n\n  private void setLevel(final int gpioNum, final Bit level)\n  {\n    if (level == null) {\n      throw new NullPointerException(\"level\");\n    }\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    final PinState pinState = appliedStates[gpioNum];\n    appliedStates[gpioNum] =\n      PinState.fromValues(pinState.getDirection(), level);\n  }\n\n  public Bit getLevel(final int gpioNum)\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    return appliedStates[gpioNum].getLevel();\n  }\n\n  private void setDirection(final int gpioNum, final Direction direction)\n  {\n    if (direction == null) {\n      throw new NullPointerException(\"direction\");\n    }\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    final PinState pinState = appliedStates[gpioNum];\n    appliedStates[gpioNum] =\n      PinState.fromValues(direction, pinState.getLevel());\n  }\n\n  public Direction getDirection(final int gpioNum)\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    return appliedStates[gpioNum].getDirection();\n  }\n\n  public int getPins(final int base, final int count)\n  {\n    Constants.checkGpioPin(base, \"GPIO pin base\");\n    Constants.checkGpioPinsCount(count, \"GPIO pin count\");\n    int pins = 0;\n    for (int pin = 0; pin < count; pin++) {\n      pins = (pins << 0x1) | getLevel((base - pin - 1) & 0x1f).getValue();\n    }\n    return pins;\n  }\n\n  private void collateLevel(final int gpioNum, final Bit bit)\n  {\n    // As of now, SMs do not run parallel in separate threads, but one\n    // after the other with ascending SM number.  Therefore, no\n    // further action / writer tracking needs to be taken for assuring\n    // output priority (cp. Sect. 3.5.6.1 of RP2040 datasheet).\n    collatedLevels[gpioNum] = bit;\n  }\n\n  public void collatePins(final int pins, final int base, final int count)\n  {\n    for (int pin = 0; pin < count; pin++) {\n      collateLevel((base + pin) & (GPIO_NUM - 1),\n                   Bit.fromValue((pins >>> pin) & 0x1));\n    }\n  }\n\n  public void setPins(final int pins, final int base, final int count)\n  {\n    Constants.checkGpioPin(base, \"GPIO pin base\");\n    Constants.checkGpioPinsCount(count, \"GPIO pin count\");\n    for (int pin = 0; pin < count; pin++) {\n      setLevel((base + pin) & 0x1f, Bit.fromValue((pins >>> pin) & 0x1));\n    }\n  }\n\n  public void setPinsMask(final int pins, final int mask, final boolean xor)\n  {\n    for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) {\n      final int oldLevel = getLevel(gpioNum).getValue();\n      final int pin = pins >>> gpioNum & 0x1;\n      final int maskBit = mask >>> gpioNum & 0x1;\n      final int newLevel = Constants.hwSetBits(oldLevel, pin, maskBit, xor);\n      setLevel(gpioNum, Bit.fromValue(newLevel));\n    }\n  }\n\n  public int getPinDirs(final int base, final int count)\n  {\n    Constants.checkGpioPin(base, \"GPIO pin base\");\n    Constants.checkGpioPinsCount(count, \"GPIO pin count\");\n    int pinDirs = 0;\n    for (int pin = 0; pin < count; pin++) {\n      pinDirs =\n        (pinDirs << 0x1) | getDirection((base - pin - 1) & 0x1f).getValue();\n    }\n    return pinDirs;\n  }\n\n  private void collatePinDir(final int gpioNum, final Direction direction)\n  {\n    // As of now, SMs do not run parallel in separate threads, but one\n    // after the other with ascending SM number.  Therefore, no\n    // further action / writer tracking needs to be taken for assuring\n    // output priority (cp. Sect. 3.5.6.1 of RP2040 datasheet).\n    collatedDirections[gpioNum] = direction;\n  }\n\n  public void collatePinDirs(final int pinDirs, final int base, final int count)\n  {\n    for (int pin = 0; pin < count; pin++) {\n      collatePinDir((base + pin) & 0x1f,\n                    Direction.fromValue((pinDirs >>> pin) & 0x1));\n    }\n  }\n\n  public void setPinDirs(final int pinDirs, final int base, final int count)\n  {\n    Constants.checkGpioPin(base, \"GPIO pin base\");\n    Constants.checkGpioPinsCount(count, \"GPIO pin count\");\n    for (int pin = 0; pin < count; pin++) {\n      setDirection((base + pin) & 0x1f,\n                   Direction.fromValue((pinDirs >>> pin) & 0x1));\n    }\n  }\n\n  public void setPinDirsMask(final int pinDirs, final int mask,\n                             final boolean xor)\n  {\n    for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) {\n      final int oldDirection = getDirection(gpioNum).getValue();\n      final int pinDir = pinDirs >>> gpioNum & 0x1;\n      final int maskBit = mask >>> gpioNum & 0x1;\n      final int newDirection = Constants.hwSetBits(oldDirection, pinDir,\n                                                   maskBit, xor);\n      setDirection(gpioNum, Direction.fromValue(newDirection));\n    }\n  }\n\n  public void applyCollatedWrites()\n  {\n    for (int gpioNum = 0; gpioNum < GPIO_NUM; gpioNum++) {\n      final PinState state = appliedStates[gpioNum];\n      final Bit collatedLevel = collatedLevels[gpioNum];\n      final Direction collatedDirection = collatedDirections[gpioNum];\n      final Bit level =\n        collatedLevel == null ? state.getLevel() : collatedLevel;\n      final Direction direction =\n        collatedDirection == null ? state.getDirection() : collatedDirection;\n      appliedStates[gpioNum] = PinState.fromValues(direction, level);\n      collatedLevels[gpioNum] = null;\n      collatedDirections[gpioNum] = null;\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIORegisters.java",
    "content": "/*\n * @(#)PIORegisters.java 1.00 21/02/25\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\n\n/**\n * Facade to the internal subsystems of a PIO.  The layout of\n * registers follows the list of registers in Sect. 3.7 of the RP2040\n * datasheet.  The facade is in particular intended for use by the\n * SDK.\n */\npublic abstract class PIORegisters extends RegisterSet\n{\n  public enum Regs implements RegistersDocs<Regs>\n  {\n    CTRL(\"PIO control register.\",\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n           new BitsInfo(\"CLKDIV3_RESTART\", 11, 11, null, BitsType.SC, 0),\n           new BitsInfo(\"CLKDIV2_RESTART\", 10, 10, null, BitsType.SC, 0),\n           new BitsInfo(\"CLKDIV1_RESTART\", 9, 9, null, BitsType.SC, 0),\n           new BitsInfo(\"CLKDIV0_RESTART\", 8, 8, null, BitsType.SC, 0),\n           new BitsInfo(\"SM3_RESTART\", 7, 7, null, BitsType.SC, 0),\n           new BitsInfo(\"SM2_RESTART\", 6, 6, null, BitsType.SC, 0),\n           new BitsInfo(\"SM1_RESTART\", 5, 5, null, BitsType.SC, 0),\n           new BitsInfo(\"SM0_RESTART\", 4, 4, null, BitsType.SC, 0),\n           new BitsInfo(\"SM3_ENABLE\", 3, 3, null, BitsType.RW, 0),\n           new BitsInfo(\"SM2_ENABLE\", 2, 2, null, BitsType.RW, 0),\n           new BitsInfo(\"SM1_ENABLE\", 1, 1, null, BitsType.RW, 0),\n           new BitsInfo(\"SM0_ENABLE\", 0, 0, null, BitsType.RW, 0)\n         }),\n    FSTAT(\"FIFO status register.\",\n          new BitsInfo[] {\n            new BitsInfo(null, 31, 28, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_TXEMPTY\", 27, 27, null, BitsType.RO, 1),\n            new BitsInfo(\"SM2_TXEMPTY\", 26, 26, null, BitsType.RO, 1),\n            new BitsInfo(\"SM1_TXEMPTY\", 25, 25, null, BitsType.RO, 1),\n            new BitsInfo(\"SM0_TXEMPTY\", 24, 24, null, BitsType.RO, 1),\n            new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_TXFULL\", 19, 19, null, BitsType.RO, 0),\n            new BitsInfo(\"SM2_TXFULL\", 18, 18, null, BitsType.RO, 0),\n            new BitsInfo(\"SM1_TXFULL\", 17, 17, null, BitsType.RO, 0),\n            new BitsInfo(\"SM0_TXFULL\", 16, 16, null, BitsType.RO, 0),\n            new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_RXEMPTY\", 11, 11, null, BitsType.RO, 1),\n            new BitsInfo(\"SM2_RXEMPTY\", 10, 10, null, BitsType.RO, 1),\n            new BitsInfo(\"SM1_RXEMPTY\", 9, 9, null, BitsType.RO, 1),\n            new BitsInfo(\"SM0_RXEMPTY\", 8, 8, null, BitsType.RO, 1),\n            new BitsInfo(null, 7, 4, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_RXFULL\", 3, 3, null, BitsType.RO, 0),\n            new BitsInfo(\"SM2_RXFULL\", 2, 2, null, BitsType.RO, 0),\n            new BitsInfo(\"SM1_RXFULL\", 1, 1, null, BitsType.RO, 0),\n            new BitsInfo(\"SM0_RXFULL\", 0, 0, null, BitsType.RO, 0)\n          }),\n    FDEBUG(\"FIFO debug register.\",\n          new BitsInfo[] {\n            new BitsInfo(null, 31, 28, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_TXSTALL\", 27, 27, null, BitsType.WC, 0),\n            new BitsInfo(\"SM2_TXSTALL\", 26, 26, null, BitsType.WC, 0),\n            new BitsInfo(\"SM1_TXSTALL\", 25, 25, null, BitsType.WC, 0),\n            new BitsInfo(\"SM0_TXSTALL\", 24, 24, null, BitsType.WC, 0),\n            new BitsInfo(null, 23, 20, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_TXOVER\", 19, 19, null, BitsType.WC, 0),\n            new BitsInfo(\"SM2_TXOVER\", 18, 18, null, BitsType.WC, 0),\n            new BitsInfo(\"SM1_TXOVER\", 17, 17, null, BitsType.WC, 0),\n            new BitsInfo(\"SM0_TXOVER\", 16, 16, null, BitsType.WC, 0),\n            new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_RXUNDER\", 11, 11, null, BitsType.WC, 0),\n            new BitsInfo(\"SM2_RXUNDER\", 10, 10, null, BitsType.WC, 0),\n            new BitsInfo(\"SM1_RXUNDER\", 9, 9, null, BitsType.WC, 0),\n            new BitsInfo(\"SM0_RXUNDER\", 8, 8, null, BitsType.WC, 0),\n            new BitsInfo(null, 7, 4, null, BitsType.RESERVED, null),\n            new BitsInfo(\"SM3_RXSTALL\", 3, 3, null, BitsType.WC, 0),\n            new BitsInfo(\"SM2_RXSTALL\", 2, 2, null, BitsType.WC, 0),\n            new BitsInfo(\"SM1_RXSTALL\", 1, 1, null, BitsType.WC, 0),\n            new BitsInfo(\"SM0_RXSTALL\", 0, 0, null, BitsType.WC, 0)\n          }),\n    FLEVEL(\"FIFO levels.\",\n           IntStream.rangeClosed(0, 7).boxed()\n           .map(n -> new BitsInfo(((n & 1) == 0 ? \"R\" : \"T\") +\n                                  \"X\" + (3 - (n / 2)), 31 - (n << 2),\n                                  28 - (n << 2), null, BitsType.RO, 0))\n           .collect(Collectors.toList())),\n    TXF0(\"Direct write access to the TX FIFO for state machine *N*.\", 0,\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 0, null, BitsType.WF, 0)\n         }),\n    TXF1(Regs.TXF0, 1),\n    TXF2(Regs.TXF0, 2),\n    TXF3(Regs.TXF0, 3),\n    RXF0(\"Direct read access to the RX FIFO for state machine *N*.\", 0,\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 0, null, BitsType.RF, 0)\n         }),\n    RXF1(Regs.RXF0, 1),\n    RXF2(Regs.RXF0, 2),\n    RXF3(Regs.RXF0, 3),\n    IRQ(\"State machine IRQ flags register.  Write 1 to clear.\",\n        new BitsInfo[] {\n          new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null),\n          new BitsInfo(\"IRQ7\", 7, 7, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ6\", 6, 6, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ5\", 5, 5, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ4\", 4, 4, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ3\", 3, 3, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ2\", 2, 2, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ1\", 1, 1, null, BitsType.WC, 0),\n          new BitsInfo(\"IRQ0\", 0, 0, null, BitsType.WC, 0),\n        }),\n    IRQ_FORCE(\"Writing a 1 to each the bit will forcibly assert \" +\n              \"the corresponding IRQ.\",\n        new BitsInfo[] {\n          new BitsInfo(null, 31, 8, null, BitsType.RESERVED, null),\n          new BitsInfo(\"IRQ7\", 7, 7, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ6\", 6, 6, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ5\", 5, 5, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ4\", 4, 4, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ3\", 3, 3, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ2\", 2, 2, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ1\", 1, 1, null, BitsType.WF, 0),\n          new BitsInfo(\"IRQ0\", 0, 0, null, BitsType.WF, 0),\n        }),\n    INPUT_SYNC_BYPASS(\"GPIO input synchronizer policy.\",\n                      IntStream.rangeClosed(0, 31).boxed()\n                      .map(n -> new BitsInfo(\"GPIO\" + (31 - n),\n                                             31 - n, 31 - n, null,\n                                             BitsType.RW, 0))\n                      .collect(Collectors.toList())),\n    DBG_PADOUT(\"Read to sample pad output value from PIO.\",\n               IntStream.rangeClosed(0, 31).boxed()\n               .map(n -> new BitsInfo(\"GPIO\" + (31 - n),\n                                      31 - n, 31 - n, null,\n                                      BitsType.RO, 0))\n               .collect(Collectors.toList())),\n    DBG_PADOE(\"Read to sample pad output enable from PIO.\",\n              IntStream.rangeClosed(0, 31).boxed()\n              .map(n -> new BitsInfo(\"GPIO\" + (31 - n),\n                                     31 - n, 31 - n, null,\n                                     BitsType.RO, 0))\n              .collect(Collectors.toList())),\n    DBG_CFGINFO(\"PIO hardware free parameters.\",\n               new BitsInfo[] {\n                 new BitsInfo(null, 31, 22, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"IMEM_SIZE\", 21, 16, null, BitsType.RO, null),\n                 new BitsInfo(null, 15, 12, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"SM_COUNT\", 11, 8, null, BitsType.RO, null),\n                 new BitsInfo(null, 7, 6, null, BitsType.RESERVED, null),\n                 new BitsInfo(\"FIFO_DEPTH\", 5, 0, null, BitsType.RO, null)\n               }),\n    INSTR_MEM0(\"Write-only access to instruction memory location *N*.\",\n               new BitsInfo[] {\n                 new BitsInfo(null, 31, 16, null, BitsType.RESERVED, null),\n                 new BitsInfo(null, 15, 0, null, BitsType.RO, 0)\n               }),\n    INSTR_MEM1(Regs.INSTR_MEM0),\n    INSTR_MEM2(Regs.INSTR_MEM0),\n    INSTR_MEM3(Regs.INSTR_MEM0),\n    INSTR_MEM4(Regs.INSTR_MEM0),\n    INSTR_MEM5(Regs.INSTR_MEM0),\n    INSTR_MEM6(Regs.INSTR_MEM0),\n    INSTR_MEM7(Regs.INSTR_MEM0),\n    INSTR_MEM8(Regs.INSTR_MEM0),\n    INSTR_MEM9(Regs.INSTR_MEM0),\n    INSTR_MEM10(Regs.INSTR_MEM0),\n    INSTR_MEM11(Regs.INSTR_MEM0),\n    INSTR_MEM12(Regs.INSTR_MEM0),\n    INSTR_MEM13(Regs.INSTR_MEM0),\n    INSTR_MEM14(Regs.INSTR_MEM0),\n    INSTR_MEM15(Regs.INSTR_MEM0),\n    INSTR_MEM16(Regs.INSTR_MEM0),\n    INSTR_MEM17(Regs.INSTR_MEM0),\n    INSTR_MEM18(Regs.INSTR_MEM0),\n    INSTR_MEM19(Regs.INSTR_MEM0),\n    INSTR_MEM20(Regs.INSTR_MEM0),\n    INSTR_MEM21(Regs.INSTR_MEM0),\n    INSTR_MEM22(Regs.INSTR_MEM0),\n    INSTR_MEM23(Regs.INSTR_MEM0),\n    INSTR_MEM24(Regs.INSTR_MEM0),\n    INSTR_MEM25(Regs.INSTR_MEM0),\n    INSTR_MEM26(Regs.INSTR_MEM0),\n    INSTR_MEM27(Regs.INSTR_MEM0),\n    INSTR_MEM28(Regs.INSTR_MEM0),\n    INSTR_MEM29(Regs.INSTR_MEM0),\n    INSTR_MEM30(Regs.INSTR_MEM0),\n    INSTR_MEM31(Regs.INSTR_MEM0),\n    SM0_CLKDIV(\"Clock divisor register for state machine *N*.\", 0,\n               new BitsInfo[] {\n                 new BitsInfo(\"INT\", 31, 16, null, BitsType.RW, 1),\n                 new BitsInfo(\"FRAC\", 15, 8, null, BitsType.RW, 0),\n                 new BitsInfo(null, 7, 0, null, BitsType.RESERVED, null)\n               }),\n    SM0_EXECCTRL(\"Execution/behavioural settings for state machine *N*.\", 0,\n                 new BitsInfo[] {\n                   new BitsInfo(\"EXEC_STALLED\", 31, 31, null, BitsType.RO, 0),\n                   new BitsInfo(\"SIDE_EN\", 30, 30, null, BitsType.RW, 0),\n                   new BitsInfo(\"SIDE_PINDIR\", 29, 29, null, BitsType.RW, 0),\n                   new BitsInfo(\"JMP_PIN\", 28, 24, null, BitsType.RW, 0),\n                   new BitsInfo(\"OUT_EN_SEL\", 23, 19, null, BitsType.RW, 0),\n                   new BitsInfo(\"INLINE_OUT_EN\", 18, 18, null, BitsType.RW, 0),\n                   new BitsInfo(\"OUT_STICKY\", 17, 17, null, BitsType.RW, 0),\n                   new BitsInfo(\"WRAP_TOP\", 16, 12, null, BitsType.RW, 0x1f),\n                   new BitsInfo(\"WRAP_BOTTOM\", 11, 7, null, BitsType.RW, 0),\n                   new BitsInfo(null, 6, 5, null, BitsType.RESERVED, null),\n                   new BitsInfo(\"STATUS_SEL\", 4, 4, null, BitsType.RW, 0),\n                   new BitsInfo(\"STATUS_N\", 3, 0, null, BitsType.RW, 0)\n                 }),\n    SM0_SHIFTCTRL(\"Control behaviour of the input/output shift registers \" +\n                  \"for state machine *N*.\", 0,\n                  new BitsInfo[] {\n                    new BitsInfo(\"FJOIN_RX\", 31, 31, null, BitsType.RW, 0),\n                    new BitsInfo(\"FJOIN_TX\", 30, 30, null, BitsType.RW, 0),\n                    new BitsInfo(\"PULL_THRESH\", 29, 25, null, BitsType.RW, 0),\n                    new BitsInfo(\"PUSH_THRESH\", 24, 20, null, BitsType.RW, 0),\n                    new BitsInfo(\"OUT_SHIFTDIR\", 19, 19, null, BitsType.RW, 1),\n                    new BitsInfo(\"IN_SHIFTDIR\", 18, 18, null, BitsType.RW, 1),\n                    new BitsInfo(\"AUTOPULL\", 17, 17, null, BitsType.RW, 0),\n                    new BitsInfo(\"AUTOPUSH\", 16, 16, null, BitsType.RW, 0),\n                    new BitsInfo(null, 15, 0, null, BitsType.RESERVED, null)\n                  }),\n    SM0_ADDR(\"Current instruction address of state machine *N*.\", 0,\n             new BitsInfo[] {\n               new BitsInfo(null, 31, 5, null, BitsType.RESERVED, null),\n               new BitsInfo(null, 4, 0, null, BitsType.RO, 0)\n             }),\n    SM0_INSTR(\"Read to see current instruction on state machine *N*.  Write \" +\n              \"to execute instruction immediately on state machine *N*.\", 0,\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 16, null, BitsType.RESERVED, null),\n                new BitsInfo(null, 15, 0, null, BitsType.RW, null)\n              }),\n    SM0_PINCTRL(\"State machine pin control for state machine *N*.\", 0,\n                new BitsInfo[] {\n                  new BitsInfo(\"SIDESET_COUNT\", 31, 29, null, BitsType.RW, 0),\n                  new BitsInfo(\"SET_COUNT\", 28, 26, null, BitsType.RW, 5),\n                  new BitsInfo(\"OUT_COUNT\", 25, 20, null, BitsType.RW, 0),\n                  new BitsInfo(\"IN_BASE\", 19, 15, null, BitsType.RW, 0),\n                  new BitsInfo(\"SIDESET_BASE\", 14, 10, null, BitsType.RW, 0),\n                  new BitsInfo(\"SET_BASE\", 9, 5, null, BitsType.RW, 0),\n                  new BitsInfo(\"OUT_BASE\", 4, 0, null, BitsType.RW, 0)\n                }),\n    SM1_CLKDIV(Regs.SM0_CLKDIV, 1),\n    SM1_EXECCTRL(Regs.SM0_EXECCTRL, 1),\n    SM1_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 1),\n    SM1_ADDR(Regs.SM0_ADDR, 1),\n    SM1_INSTR(Regs.SM0_INSTR, 1),\n    SM1_PINCTRL(Regs.SM0_PINCTRL, 1),\n    SM2_CLKDIV(Regs.SM0_CLKDIV, 2),\n    SM2_EXECCTRL(Regs.SM0_EXECCTRL, 2),\n    SM2_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 2),\n    SM2_ADDR(Regs.SM0_ADDR, 2),\n    SM2_INSTR(Regs.SM0_INSTR, 2),\n    SM2_PINCTRL(Regs.SM0_PINCTRL, 2),\n    SM3_CLKDIV(Regs.SM0_CLKDIV, 3),\n    SM3_EXECCTRL(Regs.SM0_EXECCTRL, 3),\n    SM3_SHIFTCTRL(Regs.SM0_SHIFTCTRL, 3),\n    SM3_ADDR(Regs.SM0_ADDR, 3),\n    SM3_INSTR(Regs.SM0_INSTR, 3),\n    SM3_PINCTRL(Regs.SM0_PINCTRL, 3),\n    INTR(\"Raw Interrupts.\",\n         new BitsInfo[] {\n           new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n           new BitsInfo(\"SM3\", 11, 11, null, BitsType.RO, 0),\n           new BitsInfo(\"SM2\", 10, 10, null, BitsType.RO, 0),\n           new BitsInfo(\"SM1\", 9, 9, null, BitsType.RO, 0),\n           new BitsInfo(\"SM0\", 8, 8, null, BitsType.RO, 0),\n           new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RO, 0),\n           new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RO, 0),\n           new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RO, 0),\n           new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RO, 0),\n           new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RO, 0),\n           new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RO, 0),\n           new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RO, 0),\n           new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RO, 0)\n         }),\n    IRQ0_INTE(\"Interrupt enable for IRQ0.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RW, 0)\n         }),\n    IRQ0_INTF(\"Interrupt force for IRQ0.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RW, 0)\n         }),\n    IRQ0_INTS(\"Interrupt status after masking & forcing for IRQ0.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RO, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RO, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RO, 0)\n         }),\n    IRQ1_INTE(\"Interrupt enable for IRQ1.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RW, 0)\n         }),\n    IRQ1_INTF(\"Interrupt force for IRQ1.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RW, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RW, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RW, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RW, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RW, 0)\n         }),\n    IRQ1_INTS(\"Interrupt status after masking & forcing for IRQ1.\",\n              new BitsInfo[] {\n                new BitsInfo(null, 31, 12, null, BitsType.RESERVED, null),\n                new BitsInfo(\"SM3\", 11, 11, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2\", 10, 10, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1\", 9, 9, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0\", 8, 8, null, BitsType.RO, 0),\n                new BitsInfo(\"SM3_TXNFULL\", 7, 7, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2_TXNFULL\", 6, 6, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1_TXNFULL\", 5, 5, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0_TXNFULL\", 4, 4, null, BitsType.RO, 0),\n                new BitsInfo(\"SM3_RXNEMPTY\", 3, 3, null, BitsType.RO, 0),\n                new BitsInfo(\"SM2_RXNEMPTY\", 2, 2, null, BitsType.RO, 0),\n                new BitsInfo(\"SM1_RXNEMPTY\", 1, 1, null, BitsType.RO, 0),\n                new BitsInfo(\"SM0_RXNEMPTY\", 0, 0, null, BitsType.RO, 0)\n         });\n\n    public static String getRegisterSetLabel()\n    {\n      return \"PIO Registers\";\n    }\n\n    public static String getRegisterSetDescription()\n    {\n      return\n        \"The PIO registers as described in Sect. 3.7 of the RP2040%n\" +\n        \"datasheet.%n\" +\n        \"Base address for the two emulator PIO register sets (one %n\" +\n        \"register set for each of the two PIOs) is%n\" +\n        String.format(\"0x%08x and 0x%08x for PIO0 and PIO1, respectively.%n\",\n                      PIO0_BASE, PIO1_BASE);\n    }\n\n    private final RegisterDetails registerDetails;\n\n    private Regs()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Regs(final Regs ref)\n    {\n      this(ref.registerDetails);\n    }\n\n    private Regs(final Regs ref, final int smNum)\n    {\n      this(ref.registerDetails.createCopyForDifferentSm(smNum));\n    }\n\n    private Regs(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final RegisterDetails registerDetails)\n    {\n      this.registerDetails = registerDetails;\n    }\n\n    @Override\n    public String getInfo()\n    {\n      return registerDetails.getInfo();\n    }\n\n    @Override\n    public RegisterDetails getRegisterDetails()\n    {\n      return registerDetails;\n    }\n  }\n\n  protected static final Regs[] REGS = Regs.values();\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  protected <T extends Enum<T>> T[] getRegs() { return (T[])REGS; }\n\n  protected static final int SM_SIZE =\n    Regs.SM1_CLKDIV.ordinal() - Regs.SM0_CLKDIV.ordinal();\n\n  public static int getAddress(final int pioNum,\n                               final PIORegisters.Regs register)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    return Constants.getPIOBaseAddress(pioNum) + 0x4 * register.ordinal();\n  }\n\n  public static int getSMAddress(final int pioNum,\n                                 final int smNum,\n                                 final PIORegisters.Regs register)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    switch (register) {\n    case SM0_CLKDIV:\n    case SM0_EXECCTRL:\n    case SM0_SHIFTCTRL:\n    case SM0_ADDR:\n    case SM0_INSTR:\n    case SM0_PINCTRL:\n      break; // ok\n    default:\n      throw new IllegalArgumentException(\"register not one of SM0_*: \" +\n                                         register);\n    }\n    return\n      Constants.getPIOBaseAddress(pioNum) +\n      0x4 * (register.ordinal() + smNum * SM_SIZE);\n  }\n\n  public static int getMemoryAddress(final int pioNum,\n                                     final int memoryAddress)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmMemAddr(memoryAddress, \"memory address\");\n    return\n      Constants.getPIOBaseAddress(pioNum) +\n      0x4 * (Regs.INSTR_MEM0.ordinal() + memoryAddress);\n  }\n\n  public static int getTXFAddress(final int pioNum, final int smNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    return\n      Constants.getPIOBaseAddress(pioNum) +\n      0x4 * (Regs.TXF0.ordinal() + smNum);\n  }\n\n  public static int getRXFAddress(final int pioNum, final int smNum)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    Constants.checkSmNum(smNum);\n    return\n      Constants.getPIOBaseAddress(pioNum) +\n      0x4 * (Regs.RXF0.ordinal() + smNum);\n  }\n\n  public PIORegisters(final String id, final int baseAddress)\n  {\n    super(id, baseAddress);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PIORegistersImpl.java",
    "content": "/*\n * @(#)PIORegistersImpl.java 1.00 21/02/25\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Facade to the internal subsystems of a PIO.  The layout of\n * registers follows the list of registers in Sect. 3.7 of the RP2040\n * datasheet.  The facade is in particular intended for use by the\n * SDK.\n */\npublic class PIORegistersImpl extends PIORegisters\n{\n  private final PIO pio;\n\n  public PIORegistersImpl(final PIO pio)\n  {\n    super(\"PIO\" + pio.getIndex(),\n          Constants.getPIOBaseAddress(pio.getIndex()));\n    this.pio = pio;\n  }\n\n  public PIO getPIO() { return pio; }\n\n  public int getPIOIndex()\n  {\n    return pio.getIndex();\n  }\n\n  public int getAddress(final PIORegisters.Regs register)\n  {\n    return getAddress(getPIOIndex(), register);\n  }\n\n  public int getSMAddress(final PIORegisters.Regs register, final int smNum)\n  {\n    return getSMAddress(getPIOIndex(), smNum, register);\n  }\n\n  public int getMemoryAddress(final int memoryAddress)\n  {\n    return getMemoryAddress(getPIOIndex(), memoryAddress);\n  }\n\n  public int getTXFAddress(final int smNum)\n  {\n    return getTXFAddress(getPIOIndex(), smNum);\n  }\n\n  public int getRXFAddress(final int smNum)\n  {\n    return getRXFAddress(getPIOIndex(), smNum);\n  }\n\n  /*\n   * TODO: In all of the following methods, use constants declared in\n   * class Constants for bit shifting & masking.\n   */\n  private void writeFDebug(final int value, final int mask)\n  {\n    for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n      final SM sm = pio.getSM(smNum);\n      final FIFO fifo = sm.getFIFO();\n      if (((value >>> (24 + smNum)) & 0x1) != 0x0 &&\n          ((mask >>> (24 + smNum)) & 0x1) != 0x0) {\n        fifo.clearTXStall();\n      }\n      if (((value >>> (16 + smNum)) & 0x1) != 0x0 &&\n          ((mask >>> (16 + smNum)) & 0x1) != 0x0) {\n        fifo.clearTXOver();\n      }\n      if (((value >>> (8 + smNum)) & 0x1) != 0x0 &&\n          ((mask >>> (8 + smNum)) & 0x1) != 0x0) {\n        fifo.clearRXUnder();\n      }\n      if (((value >>> smNum) & 0x1) != 0x0 &&\n          ((mask >>> smNum) & 0x1) != 0x0) {\n        fifo.clearRXStall();\n      }\n    }\n  }\n\n  @Override\n  public void writeRegister(final int regNum, final int value,\n                            final int mask, final boolean xor)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case CTRL:\n      pio.setCtrl(value, mask);\n      break;\n    case FSTAT:\n      break; // read-only address\n    case FDEBUG:\n      writeFDebug(value, mask);\n      break;\n    case FLEVEL:\n      break; // read-only address\n    case TXF0:\n    case TXF1:\n    case TXF2:\n    case TXF3:\n      pio.getSM(regNum - Regs.TXF0.ordinal()).put(value & mask);\n      break;\n    case RXF0:\n    case RXF1:\n    case RXF2:\n    case RXF3:\n      break; // read-only address\n    case IRQ:\n      pio.getIRQ().writeRegIRQ(value & mask);\n      break;\n    case IRQ_FORCE:\n      pio.getIRQ().writeRegIRQ_FORCE(value & mask);\n      break;\n    case INPUT_SYNC_BYPASS:\n      pio.getPIOGPIO().getGPIO().setInputSyncByPass(value, mask, xor);\n      break;\n    case DBG_PADOUT:\n      break; // read-only address\n    case DBG_PADOE:\n      break; // read-only address\n    case DBG_CFGINFO:\n      break; // read-only address\n    case INSTR_MEM0:\n    case INSTR_MEM1:\n    case INSTR_MEM2:\n    case INSTR_MEM3:\n    case INSTR_MEM4:\n    case INSTR_MEM5:\n    case INSTR_MEM6:\n    case INSTR_MEM7:\n    case INSTR_MEM8:\n    case INSTR_MEM9:\n    case INSTR_MEM10:\n    case INSTR_MEM11:\n    case INSTR_MEM12:\n    case INSTR_MEM13:\n    case INSTR_MEM14:\n    case INSTR_MEM15:\n    case INSTR_MEM16:\n    case INSTR_MEM17:\n    case INSTR_MEM18:\n    case INSTR_MEM19:\n    case INSTR_MEM20:\n    case INSTR_MEM21:\n    case INSTR_MEM22:\n    case INSTR_MEM23:\n    case INSTR_MEM24:\n    case INSTR_MEM25:\n    case INSTR_MEM26:\n    case INSTR_MEM27:\n    case INSTR_MEM28:\n    case INSTR_MEM29:\n    case INSTR_MEM30:\n    case INSTR_MEM31:\n      pio.getMemory().set(regNum - Regs.INSTR_MEM0.ordinal(), value, mask, xor);\n      break;\n    case SM0_CLKDIV:\n    case SM1_CLKDIV:\n    case SM2_CLKDIV:\n    case SM3_CLKDIV:\n      pio.getSM((regNum - Regs.SM0_CLKDIV.ordinal()) / SM_SIZE).\n        setCLKDIV(value, mask, xor);\n      break;\n    case SM0_EXECCTRL:\n    case SM1_EXECCTRL:\n    case SM2_EXECCTRL:\n    case SM3_EXECCTRL:\n      pio.getSM((regNum - Regs.SM0_EXECCTRL.ordinal()) / SM_SIZE).\n        setEXECCTRL(value, mask, xor);\n      break;\n    case SM0_SHIFTCTRL:\n    case SM1_SHIFTCTRL:\n    case SM2_SHIFTCTRL:\n    case SM3_SHIFTCTRL:\n      pio.getSM((regNum - Regs.SM0_SHIFTCTRL.ordinal()) / SM_SIZE).\n        setSHIFTCTRL(value, mask, xor);\n      break;\n    case SM0_ADDR:\n    case SM1_ADDR:\n    case SM2_ADDR:\n    case SM3_ADDR:\n      break; // read-only address\n    case SM0_INSTR:\n    case SM1_INSTR:\n    case SM2_INSTR:\n    case SM3_INSTR:\n      pio.getSM((regNum - Regs.SM0_INSTR.ordinal()) / SM_SIZE).\n        forceInstruction(value & mask);\n      break;\n    case SM0_PINCTRL:\n    case SM1_PINCTRL:\n    case SM2_PINCTRL:\n    case SM3_PINCTRL:\n      pio.getSM((regNum - Regs.SM0_PINCTRL.ordinal()) / SM_SIZE).\n        setPINCTRL(value, mask, xor);\n      break;\n    case INTR:\n      break; // read-only address\n    case IRQ0_INTE:\n      pio.getIRQ().setIRQ0_INTE(value, mask, xor);\n      break;\n    case IRQ1_INTE:\n      pio.getIRQ().setIRQ1_INTE(value, mask, xor);\n      break;\n    case IRQ0_INTF:\n      pio.getIRQ().setIRQ0_INTF(value, mask, xor);\n      break;\n    case IRQ1_INTF:\n      pio.getIRQ().setIRQ1_INTF(value, mask, xor);\n      break;\n    case IRQ0_INTS:\n    case IRQ1_INTS:\n      break; // read-only address\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  private int readFStat()\n  {\n    return\n      ((pio.getSM(3).isTXFIFOEmpty() ? 0x1 : 0x0) << 27) |\n      ((pio.getSM(2).isTXFIFOEmpty() ? 0x1 : 0x0) << 26) |\n      ((pio.getSM(1).isTXFIFOEmpty() ? 0x1 : 0x0) << 25) |\n      ((pio.getSM(0).isTXFIFOEmpty() ? 0x1 : 0x0) << 24) |\n      ((pio.getSM(3).isTXFIFOFull() ? 0x1 : 0x0) << 19) |\n      ((pio.getSM(2).isTXFIFOFull() ? 0x1 : 0x0) << 18) |\n      ((pio.getSM(1).isTXFIFOFull() ? 0x1 : 0x0) << 17) |\n      ((pio.getSM(0).isTXFIFOFull() ? 0x1 : 0x0) << 16) |\n      ((pio.getSM(3).isRXFIFOEmpty() ? 0x1 : 0x0) << 11) |\n      ((pio.getSM(2).isRXFIFOEmpty() ? 0x1 : 0x0) << 10) |\n      ((pio.getSM(1).isRXFIFOEmpty() ? 0x1 : 0x0) << 9) |\n      ((pio.getSM(0).isRXFIFOEmpty() ? 0x1 : 0x0) << 8) |\n      ((pio.getSM(3).isRXFIFOFull() ? 0x1 : 0x0) << 3) |\n      ((pio.getSM(2).isRXFIFOFull() ? 0x1 : 0x0) << 2) |\n      ((pio.getSM(1).isRXFIFOFull() ? 0x1 : 0x0) << 1) |\n      ((pio.getSM(0).isRXFIFOFull() ? 0x1 : 0x0) << 0);\n  }\n\n  private int readFDebug()\n  {\n    int value = 0;\n    for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n      final SM sm = pio.getSM(smNum);\n      final FIFO fifo = sm.getFIFO();\n      if (fifo.isTXStall()) {\n        value |= 0x1 << (24 + smNum);\n      }\n      if (fifo.isTXOver()) {\n        value |= 0x1 << (16 + smNum);\n      }\n      if (fifo.isRXUnder()) {\n        value |= 0x1 << (8 + smNum);\n      }\n      if (fifo.isRXStall()) {\n        value |= 0x1 << smNum;\n      }\n    }\n    return value;\n  }\n\n  private int readFLevel()\n  {\n    return\n      (pio.getSM(3).getRXFIFOLevel() << 28) |\n      (pio.getSM(3).getTXFIFOLevel() << 24) |\n      (pio.getSM(2).getRXFIFOLevel() << 20) |\n      (pio.getSM(2).getTXFIFOLevel() << 16) |\n      (pio.getSM(1).getRXFIFOLevel() << 12) |\n      (pio.getSM(1).getTXFIFOLevel() << 8) |\n      (pio.getSM(0).getRXFIFOLevel() << 4) |\n      pio.getSM(0).getTXFIFOLevel();\n  }\n\n  private int getCfgInfo()\n  {\n    return\n      MEMORY_SIZE << 16 |\n      SM_COUNT << 8 |\n      FIFO_DEPTH;\n  }\n\n  @Override\n  public synchronized int readRegister(final int regNum)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case CTRL:\n      return pio.getCtrl();\n    case FSTAT:\n      return readFStat();\n    case FDEBUG:\n      return readFDebug();\n    case FLEVEL:\n      return readFLevel();\n    case TXF0:\n    case TXF1:\n    case TXF2:\n    case TXF3:\n      return 0; // write-only address\n    case RXF0:\n    case RXF1:\n    case RXF2:\n    case RXF3:\n      return pio.getSM(regNum - Regs.RXF0.ordinal()).get();\n    case IRQ:\n      return 0; // write-only address\n    case IRQ_FORCE:\n      return 0; // write-only address\n    case INPUT_SYNC_BYPASS:\n      return pio.getPIOGPIO().getGPIO().getInputSyncByPass();\n    case DBG_PADOUT:\n      return pio.getPIOGPIO().getPins(0, 32);\n    case DBG_PADOE:\n      return pio.getPIOGPIO().getPinDirs(0, 32);\n    case DBG_CFGINFO:\n      return getCfgInfo();\n    case INSTR_MEM0:\n    case INSTR_MEM1:\n    case INSTR_MEM2:\n    case INSTR_MEM3:\n    case INSTR_MEM4:\n    case INSTR_MEM5:\n    case INSTR_MEM6:\n    case INSTR_MEM7:\n    case INSTR_MEM8:\n    case INSTR_MEM9:\n    case INSTR_MEM10:\n    case INSTR_MEM11:\n    case INSTR_MEM12:\n    case INSTR_MEM13:\n    case INSTR_MEM14:\n    case INSTR_MEM15:\n    case INSTR_MEM16:\n    case INSTR_MEM17:\n    case INSTR_MEM18:\n    case INSTR_MEM19:\n    case INSTR_MEM20:\n    case INSTR_MEM21:\n    case INSTR_MEM22:\n    case INSTR_MEM23:\n    case INSTR_MEM24:\n    case INSTR_MEM25:\n    case INSTR_MEM26:\n    case INSTR_MEM27:\n    case INSTR_MEM28:\n    case INSTR_MEM29:\n    case INSTR_MEM30:\n    case INSTR_MEM31:\n      return 0; // write-only address\n    case SM0_CLKDIV:\n    case SM1_CLKDIV:\n    case SM2_CLKDIV:\n    case SM3_CLKDIV:\n      return\n        pio.getSM((regNum - Regs.SM0_CLKDIV.ordinal()) / SM_SIZE).getCLKDIV();\n    case SM0_EXECCTRL:\n    case SM1_EXECCTRL:\n    case SM2_EXECCTRL:\n    case SM3_EXECCTRL:\n      return\n        pio.getSM((regNum - Regs.SM0_EXECCTRL.ordinal()) / SM_SIZE).\n        getEXECCTRL();\n    case SM0_SHIFTCTRL:\n    case SM1_SHIFTCTRL:\n    case SM2_SHIFTCTRL:\n    case SM3_SHIFTCTRL:\n      return\n        pio.getSM((regNum - Regs.SM0_SHIFTCTRL.ordinal()) / SM_SIZE).\n        getSHIFTCTRL();\n    case SM0_ADDR:\n    case SM1_ADDR:\n    case SM2_ADDR:\n    case SM3_ADDR:\n      return pio.getSM((regNum - Regs.SM0_ADDR.ordinal()) / SM_SIZE).getPC();\n    case SM0_INSTR:\n    case SM1_INSTR:\n    case SM2_INSTR:\n    case SM3_INSTR:\n      return\n        pio.getSM((regNum - Regs.SM0_INSTR.ordinal()) / SM_SIZE).\n        getOpCode();\n    case SM0_PINCTRL:\n    case SM1_PINCTRL:\n    case SM2_PINCTRL:\n    case SM3_PINCTRL:\n      return\n        pio.getSM((regNum - Regs.SM0_PINCTRL.ordinal()) / SM_SIZE).getPINCTRL();\n    case INTR:\n      return pio.getIRQ().readINTR();\n    case IRQ0_INTE:\n      return pio.getIRQ().getIRQ0_INTE();\n    case IRQ1_INTE:\n      return pio.getIRQ().getIRQ1_INTE();\n    case IRQ0_INTF:\n      return pio.getIRQ().getIRQ0_INTF();\n    case IRQ1_INTF:\n      return pio.getIRQ().getIRQ1_INTF();\n    case IRQ0_INTS:\n      return pio.getIRQ().readIRQ0_INTS();\n    case IRQ1_INTS:\n      return pio.getIRQ().readIRQ1_INTS();\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PLL.java",
    "content": "/*\n * @(#)PLL.java 1.00 21/02/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Phase Locked Loop (PLL)\n */\npublic class PLL implements Clock.TransitionListener\n{\n  private final PrintStream console;\n  private int regCLKDIV_INT; // bits 16…31 of SMx_CLKDIV\n  private int regCLKDIV_FRAC; // bits 8…15 of SMx_CLKDIV\n  private int countIntegerBits;\n  private int countFractionalBits;\n  private boolean clockEnable;\n  private boolean nextClockEnable;\n\n  private PLL()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PLL(final PrintStream console)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    reset();\n  }\n\n  public void reset()\n  {\n    regCLKDIV_INT = 0x0001;\n    regCLKDIV_FRAC = 0x00;\n    countIntegerBits = 0x1;\n    countFractionalBits = 0x0;\n    clockEnable = false;\n    nextClockEnable = false;\n  }\n\n  public int getDivIntegerBits()\n  {\n    return regCLKDIV_INT;\n  }\n\n  public void setDivIntegerBits(final int divIntegerBits)\n  {\n    if (divIntegerBits < 0) {\n      throw new IllegalArgumentException(\"div integer bits < 0: \" +\n                                         divIntegerBits);\n    }\n    if (divIntegerBits > 0xffff) {\n      throw new IllegalArgumentException(\"div integer bits > 65535: \" +\n                                         divIntegerBits);\n    }\n    if (divIntegerBits == 0) {\n      if (regCLKDIV_FRAC != 0) {\n        // RP2040 datasheet, Table 391: \"If INT is 0, FRAC must also\n        // be 0.\"\n        final String message =\n          String.format(\"warning: ignoring request for setting CLK int bits \" +\n                        \"to 0, since CLK frac bits are non-zero\");\n        console.printf(\"%s%n\", message);\n        return;\n      }\n      // TODO: Clarify: Should we ignore the change (as implemented),\n      // or should we silently set also FRAC to 0?\n    }\n    this.regCLKDIV_INT = divIntegerBits;\n  }\n\n  public int getDivFractionalBits()\n  {\n    return regCLKDIV_FRAC;\n  }\n\n  public void setDivFractionalBits(final int divFractionalBits)\n  {\n    if (divFractionalBits < 0) {\n      throw new IllegalArgumentException(\"div fractional bits < 0: \" +\n                                         divFractionalBits);\n    }\n    if (divFractionalBits > 0xff) {\n      throw new IllegalArgumentException(\"div fractional bits > 255: \" +\n                                         divFractionalBits);\n    }\n    if (regCLKDIV_INT == 0) {\n      if (divFractionalBits != 0) {\n        // RP2040 datasheet, Table 391: \"If INT is 0, FRAC must also\n        // be 0.\"\n        final String message =\n          String.format(\"warning: ignoring request for setting CLK frac bits \" +\n                        \"to non-zero value, since CLK int bits are zero\");\n        console.printf(\"%s%n\", message);\n        return;\n      }\n    }\n    this.regCLKDIV_FRAC = divFractionalBits;\n  }\n\n  private void setCLKDIV(final int divIntegerBits, final int divFractionalBits)\n  {\n    if ((divIntegerBits == 0) && (divFractionalBits == 0)) {\n      // Special case: RP2040 datasheet, Table 391: \"If INT is 0, FRAC\n      // must also be 0.\"\n      regCLKDIV_INT = 0;\n      regCLKDIV_FRAC = 0;\n    } else {\n      setDivIntegerBits(divIntegerBits);\n      setDivFractionalBits(divFractionalBits);\n    }\n  }\n\n  public void setCLKDIV(final int clkdiv)\n  {\n    setCLKDIV(clkdiv >>> 16, (clkdiv >>> 8) & 0xff);\n  }\n\n  public int getCLKDIV()\n  {\n    return\n      (getDivIntegerBits() << 16) |\n      (getDivFractionalBits() << 8);\n  }\n\n  public boolean getClockEnable()\n  {\n    return clockEnable;\n  }\n\n  public boolean getNextClockEnable()\n  {\n    return nextClockEnable;\n  }\n\n  private void prepareClockEnable()\n  {\n    /*\n     * TODO: Clarify: Sect. 3.5.5. \"Clock Dividers\", Fig. 46: \"clock\n     * divider … emits an enable pulse when it reaches 1\"\n     *\n     * -- Really \"1\", not \"0\"?\n     */\n    if (countIntegerBits <= 1) {\n      countIntegerBits += regCLKDIV_INT;\n      countFractionalBits += regCLKDIV_FRAC;\n      if (countFractionalBits >= 0x100) {\n        countFractionalBits -= 0x100;\n        countIntegerBits++;\n      }\n      nextClockEnable = true;\n    } else {\n      nextClockEnable = false;\n    }\n    countIntegerBits--;\n  }\n\n  @Override\n  public void risingEdge(final long wallClock)\n  {\n    clockEnable = nextClockEnable;\n  }\n\n  @Override\n  public void fallingEdge(final long wallClock) {\n    prepareClockEnable();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/ParseException.java",
    "content": "/*\n * @(#)ParseException.java 1.00 21/02/16\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.IOException;\n\npublic class ParseException extends IOException\n{\n  private static final long serialVersionUID = -3298538004378904681L;\n\n  private ParseException()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public ParseException(final String message)\n  {\n    super(message);\n  }\n\n  public ParseException(final String message, final Throwable cause)\n  {\n    super(message, cause);\n  }\n\n  public static ParseException create(final String message,\n                                      final String resourcePath,\n                                      final int lineIndex,\n                                      final Throwable cause)\n  {\n    final String fullMessage =\n      String.format(\"parse exception in %s, line %d: %s\",\n                    resourcePath, lineIndex, message);\n    return cause != null ?\n      new ParseException(fullMessage, cause) :\n      new ParseException(fullMessage);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PicoEmuRegisters.java",
    "content": "/*\n * @(#)PicoEmuRegisters.java 1.00 21/03/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\n\n/**\n * Facade to additonal emulator properties of the internal subsystems\n * of a PIO that are not available via the PIORegisters facade.  This\n * facade is in particular intended for use by software that wants to\n * exploit the emulator's debug facilities.\n */\npublic abstract class PicoEmuRegisters extends RegisterSet\n{\n  public enum Regs implements RegistersDocs<Regs>\n  {\n    PWR_UP(\"Writing the value 0xa55a5aa5 to this address will fully reset%n\" +\n           \"the emulator.  Writing any other value will have no effect.\",\n             new BitsInfo[] {\n               new BitsInfo(null, 31, 0, null, BitsType.WF, 0)\n             }),\n    MASTERCLK_FREQ(\"Unsigned integer value that represents the%n\" +\n                   \"target frequency of the emulation in 1/8Hz.%n\" +\n                   \"That is, a value of 1 represents a frequency of%n\" +\n                   \"0.125 Hz, and the maximum value of 2^32 - 1 =%n\" +\n                   \"4294967295 represents a frequency of%n\" +\n                   \"536.870911875MHz.%n\" +\n                   \"%n\" +\n                   \"A value of 0 indicates that the emulation should%n\" +\n                   \"execute as fast as possible.%n\" +\n                   \"%n\" +\n                   \"Note that there is no guarantee at all to run at%n\" +\n                   \"the specified frequency.  Instead, the value is%n\" +\n                   \"just the frequency that the emulation tries to%n\" +\n                   \"catch up with as close as possible.  The reset%n\" +\n                   \"value corresponds to a target frequency of 125MHz.\",\n                   new BitsInfo[] {\n                     new BitsInfo(null, 31, 0, null, BitsType.RW,\n                                  DEFAULT_FREQUENCY)\n                   }),\n    MASTERCLK_MODE(\"Selects the clock mode.\",\n                   new BitsInfo[] {\n                     new BitsInfo(null, 31, 1, null, BitsType.RESERVED, null),\n                     new BitsInfo(null, 0, 0,\n                                  \"Bit 0 = 0: Target frequency mode.%n\" +\n                                  \"Bit 0 = 1: Single step mode.\",\n                                  BitsType.RW, 0)\n                   }),\n    MASTERCLK_TRIGGER_PHASE0(\"When master clock is in single step%n\" +\n                             \"mode, writing any value to this address%n\" +\n                             \"will trigger the emulator to execute phase%n\" +\n                             \"0 of the next clock cycle.  In phase%n\" +\n                             \"0, the emulator fetches and decodes the%n\" +\n                             \"next instruction.  When already in phase%n\" +\n                             \"0, writing once more to this address will%n\" +\n                             \"have no effect.  When master clock is in%n\" +\n                             \"target frequency mode, writing to this%n\" +\n                             \"address will have no effect.  Upon reset,%n\" +\n                             \"the system is in phase 1.%n\" +\n                             \"Reading from this register will return value%n\" +\n                             \"0x1 if and only if the emulator is in phase%n\" +\n                             \"0 *and* phase 0 is settled (i.e. the emulator%n\" +\n                             \"has completed all operations to be performed%n\" +\n                             \"during this phase), and 0x0 otherwise.\",\n                             new BitsInfo[] {\n                               new BitsInfo(null, 31, 0, null,\n                                            BitsType.WF, null)\n                             }),\n    MASTERCLK_TRIGGER_PHASE1(\"When master clock is in single step%n\" +\n                             \"mode, writing any value to this address%n\" +\n                             \"will trigger the emulator to execute phase%n\" +\n                             \"1 of the current clock cycle.  In phase%n\" +\n                             \"1, the emulator will execute the%n\" +\n                             \"instruction previously decoded in%n\" +\n                             \"phase 0.  When already in phase%n\" +\n                             \"1, writing once more to this address will%n\" +\n                             \"have no effect. When master clock is in%n\" +\n                             \"target frequency mode, writing to this%n\" +\n                             \"address will have no effect.  Upon reset,%n\" +\n                             \"the system is in phase 1.%n\" +\n                             \"Reading from this register will return value%n\" +\n                             \"0x1 if and only if the emulator is in phase%n\" +\n                             \"1 *and* phase 1 is settled (i.e. the emulator%n\" +\n                             \"has completed all operations to be performed%n\" +\n                             \"during this phase), and 0x0 otherwise.\",\n                             new BitsInfo[] {\n                               new BitsInfo(null, 31, 0, null,\n                                            BitsType.WF, null)\n                             }),\n    WALLCLOCK_LSB(\"LSB value (lower 32 bits) of wall clock.  The%n\" +\n                  \"wall clock is a 64 bit counter that is initialized%n\" +\n                  \"to 0 and incremented whenever the master clock has%n\" +\n                  \"completed a cycle.\",\n                  new BitsInfo[] {\n                    new BitsInfo(null, 31, 0, null, BitsType.RO, null)\n                  }),\n    WALLCLOCK_MSB(\"MSB value (upper 32 bits) of wall clock.  The%n\" +\n                  \"wall clock is a 64 bit counter that is initialized%n\" +\n                  \"to 0 and incremented whenever the master clock has%n\" +\n                  \"completed a cycle.\",\n                  new BitsInfo[] {\n                    new BitsInfo(null, 31, 0, null, BitsType.RO, null)\n                  }),\n    GPIO_PADIN(\"Each bit of this value represents the corresponding%n\" +\n               \"pad input state of the 32 GPIO pins, virtually provided%n\" +\n               \"from some external source.\",\n               IntStream.rangeClosed(0, 31).boxed()\n               .map(n -> new BitsInfo(\"INFROMPAD_GPIO\" + (31 - n),\n                                      31 - n, 31 - n,\n                                      \"signal value 0x0 or 0x1, as%n\" +\n                                      \"provided by some external source.\",\n                                      BitsType.RW, 0))\n               .collect(Collectors.toList()));\n\n    public static String getRegisterSetLabel()\n    {\n      return \"Emulator Global Registers\";\n    }\n\n    public static String getRegisterSetDescription()\n    {\n      return\n        \"The PIO emulator provides global registers, hereafter%n\" +\n        \"called *Emulator Global Registers*, that are used to inspect%n\" +\n        \"and control the emulator as a whole (rather than just%n\" +\n        \"referring to a specifc PIO) and that are accessible through%n\" +\n        \"this registers facade and provided in addition to the%n\" +\n        \"registers of the original RP2040 hardware.%n\" +\n        \"Base address for the emulator global register set is%n\" +\n        String.format(\"0x%08x.%n\", EMULATOR_BASE);\n    }\n\n    private final RegisterDetails registerDetails;\n\n    private Regs()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Regs(final Regs ref)\n    {\n      this(ref.registerDetails);\n    }\n\n    private Regs(final Regs ref, final int smNum)\n    {\n      this(ref.registerDetails.createCopyForDifferentSm(smNum));\n    }\n\n    private Regs(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final BitsInfo[] bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final String info, final int smNum,\n                 final List<BitsInfo> bitsInfos)\n    {\n      this(new RegisterDetails(info, smNum, bitsInfos));\n    }\n\n    private Regs(final RegisterDetails registerDetails)\n    {\n      this.registerDetails = registerDetails;\n    }\n\n    @Override\n    public String getInfo()\n    {\n      return registerDetails.getInfo();\n    }\n\n    @Override\n    public RegisterDetails getRegisterDetails()\n    {\n      return registerDetails;\n    }\n  }\n\n  protected static final Regs[] REGS = Regs.values();\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  protected <T extends Enum<T>> T[] getRegs() { return (T[])REGS; }\n\n  public static int getAddress(final PicoEmuRegisters.Regs register)\n  {\n    if (register == null) {\n      throw new NullPointerException(\"register\");\n    }\n    return EMULATOR_BASE + 0x4 * register.ordinal();\n  }\n\n  public PicoEmuRegisters()\n  {\n    super(\"PicoEmu\", EMULATOR_BASE);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PicoEmuRegistersImpl.java",
    "content": "/*\n * @(#)PicoEmuRegistersImpl.java 1.00 21/03/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport org.soundpaint.rp2040pio.Clock;\n\n/**\n * Facade to additonal emulator properties of the internal subsystems\n * of a PIO that are not available via the PIORegisters facade.  This\n * facade is in particular intended for use by software that wants to\n * exploit the emulator's debug facilities.\n */\npublic class PicoEmuRegistersImpl extends PicoEmuRegisters\n{\n  private final Emulator emulator;\n\n  public PicoEmuRegistersImpl(final Emulator emulator)\n  {\n    this.emulator = emulator;\n  }\n\n  public Emulator getEmulator() { return emulator; }\n\n  @Override\n  public void writeRegister(final int regNum, final int value,\n                            final int mask, final boolean xor)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case PWR_UP:\n      if (value == PICO_PWR_UP_VALUE) emulator.reset();\n      break;\n    case MASTERCLK_FREQ:\n      emulator.getMasterClock().setMASTERCLK_FREQ(value);\n      break;\n    case MASTERCLK_MODE:\n      emulator.getMasterClock().setMASTERCLK_MODE(value);\n      break;\n    case MASTERCLK_TRIGGER_PHASE0:\n      emulator.getMasterClock().triggerPhase0();\n      break;\n    case MASTERCLK_TRIGGER_PHASE1:\n      emulator.getMasterClock().triggerPhase1();\n      break;\n    case WALLCLOCK_LSB:\n    case WALLCLOCK_MSB:\n      break; // read-only address\n    case GPIO_PADIN:\n      emulator.getGPIO().setGPIO_PADIN(value, mask, xor);\n      break;\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  @Override\n  public synchronized int readRegister(final int regNum)\n  {\n    checkRegNum(regNum);\n    final Regs register = REGS[regNum];\n    switch (register) {\n    case PWR_UP:\n      return 0; // write-only address\n    case MASTERCLK_FREQ:\n      return emulator.getMasterClock().getMASTERCLK_FREQ();\n    case MASTERCLK_MODE:\n      return emulator.getMasterClock().getMASTERCLK_MODE();\n    case MASTERCLK_TRIGGER_PHASE0:\n      return\n        emulator.getMasterClock().getPhase() == Clock.Phase.PHASE_0_STABLE ?\n        0x1 : 0x0;\n    case MASTERCLK_TRIGGER_PHASE1:\n      return\n        emulator.getMasterClock().getPhase() == Clock.Phase.PHASE_1_STABLE ?\n        0x1 : 0x0;\n    case WALLCLOCK_LSB:\n      return (int)emulator.getMasterClock().getWallClock();\n    case WALLCLOCK_MSB:\n      return (int)(emulator.getMasterClock().getWallClock() >>> 32);\n    case GPIO_PADIN:\n      return emulator.getGPIO().getGPIO_PADIN();\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/PinState.java",
    "content": "/*\n * @(#)PinState.java 1.00 21/04/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\n/**\n * Representation of a single GPIO pin's state.\n */\npublic enum PinState\n{\n  IN_LOW(Direction.IN, Bit.LOW),\n  IN_HIGH(Direction.IN, Bit.HIGH),\n  OUT_LOW(Direction.OUT, Bit.LOW),\n  OUT_HIGH(Direction.OUT, Bit.HIGH);\n\n  private final Direction direction;\n  private final Bit level;\n\n  private PinState(final Direction direction, final Bit level)\n  {\n    if (direction == null) {\n      throw new NullPointerException(\"direction\");\n    }\n    if (level == null) {\n      throw new NullPointerException(\"level\");\n    }\n    this.direction = direction;\n    this.level = level;\n  }\n\n  public Direction getDirection() { return direction; }\n\n  public Bit getLevel() { return level; }\n\n  public static PinState fromValues(final Direction direction, final Bit level)\n  {\n    if (direction == null) {\n      throw new NullPointerException(\"direction\");\n    }\n    if (level == null) {\n      throw new NullPointerException(\"level\");\n    }\n    switch (direction) {\n    case IN:\n      switch (level) {\n      case LOW: return IN_LOW;\n      case HIGH: return IN_HIGH;\n      default: throw new InternalError(\"unexpected case fall-through\");\n      }\n    case OUT:\n      switch (level) {\n      case LOW: return OUT_LOW;\n      case HIGH: return OUT_HIGH;\n      default: throw new InternalError(\"unexpected case fall-through\");\n      }\n    default: throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  @Override\n  public String toString()\n  {\n    return String.format(\"PinState[level=%s, direction=%s]\", level, direction);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/RegisterSet.java",
    "content": "/*\n * @(#)RegisterSet.java 1.00 21/03/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.IOException;\nimport java.util.Objects;\n\npublic abstract class RegisterSet implements Constants\n{\n  private final String id;\n  private final int baseAddress;\n  private final short size;\n\n  private RegisterSet()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  /**\n   * @param baseAddress The base address of the set of registers.\n   * @param size Number of words provided by this registers interface.\n   * The maximum allowed address computes as &lt;code&gt;baseAddress +\n   * (size - 1) * 0x4&lt;/code&gt;.\n   */\n  protected RegisterSet(final String id, final int baseAddress)\n  {\n    Objects.requireNonNull(id);\n    this.id = id;\n    if ((baseAddress & 0x3fff) != 0x0) {\n      throw new IllegalArgumentException(\"base address not conforming to \" +\n                                         \"model of register access methods: \" +\n                                         String.format(\"0x%08x\", baseAddress));\n    }\n    this.baseAddress = baseAddress;\n    size = (short)getRegs().length;\n    if (size * 0x4 > 0x1000) {\n      throw new IllegalArgumentException(String.format(\"size * 0x4 > 0x1000: \" +\n                                                       \"0x%08x\" + size * 0x4));\n    }\n  }\n\n  public String getId() { return id; }\n\n  public int getBaseAddress() { return baseAddress; }\n\n  public int getSize() { return size; }\n\n  protected void checkRegNum(final int regNum)\n  {\n    if ((regNum < 0) || (regNum >= size)) {\n      final String message =\n        String.format(\"regNum out of bounds: 0x%08x\", regNum);\n      throw new InternalError(message);\n    }\n  }\n\n  /**\n   * Returns all instance values of the subclass's REGS enum.\n   */\n  protected abstract <T extends Enum<T>> T[] getRegs();\n\n  public <T extends Enum<T>> String getRegisterLabel(final int regNum)\n    throws IOException\n  {\n    if (regNum < 0) {\n      throw new IllegalArgumentException(\"regNum < 0: \" + regNum);\n    }\n    if (regNum > 0xfff) {\n      throw new IllegalArgumentException(\"regNum > 0xfff: \" +\n                                         String.format(\"%08x\", regNum));\n    }\n    final T[] regs = getRegs();\n    return regNum < regs.length ? regs[regNum].toString() : null;\n  }\n\n  public abstract void writeRegister(final int regNum,\n                                     final int bits, final int mask,\n                                     final boolean xor)\n    throws IOException;\n\n  public abstract int readRegister(final int regNum) throws IOException;\n\n  @Override\n  public String toString()\n  {\n    return\n      String.format(\"%s@%08x, size=%08x\", super.toString(), baseAddress, size);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/RemoteAddressSpaceClient.java",
    "content": "/*\n * @(#)RemoteAddressSpaceClient.java 1.00 21/03/17\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * TCP/IP Client that connects to a RemoteAddressSpaceServer via\n * socket.\n */\npublic class RemoteAddressSpaceClient extends AddressSpace\n{\n  private static final String MSG_NO_CONNECTION = \"no connection\";\n\n  private static class Response\n  {\n    private final PrintStream console;\n    private final int statusCode;\n    private final String statusId;\n    private final String result;\n\n    private Response()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private Response(final PrintStream console,\n                     final int statusCode, final String statusId,\n                     final String result)\n    {\n      if (console == null) {\n        throw new NullPointerException(\"console\");\n      }\n      this.console = console;\n      this.statusCode = statusCode;\n      this.statusId = statusId;\n      this.result = result;\n    }\n\n    public int getStatusCode()\n    {\n      return statusCode;\n    }\n\n    public String getStatusId()\n    {\n      return statusId;\n    }\n\n    public String getResult()\n    {\n      return result;\n    }\n\n    public boolean isOk()\n    {\n      return statusCode == 101; // TODO: Use global constant.\n    }\n\n    public String getResultOrThrowOnFailure(final String errorMessage)\n      throws IOException\n    {\n      if (!isOk()) {\n        final String responseMessage = errorMessage + \": \" + toString();\n        /*\n         * TODO: To avoid duplicate error message display, the message\n         * should be logged (e.g. using log4j) separately rather than\n         * just being printed to the console, since a typical client\n         * application (such as the Monitor application) usually will\n         * already display the message by itself.\n         */\n        console.printf(\"Remote Address Map Client: %s%n\" ,responseMessage);\n        throw new IOException(responseMessage);\n      }\n      return result;\n    }\n\n    @Override\n    public String toString()\n    {\n      return\n        statusCode + \" \" + statusId + (result != null ? \": \" + result : \"\");\n    }\n  }\n\n  private final PrintStream console;\n  private int port;\n  private String host;\n  private Socket socket;\n\n  /**\n   * Creates a register client, but does not yet connect to any\n   * emulation server.\n   *\n   * @see #connect\n   */\n  public RemoteAddressSpaceClient(final PrintStream console) throws IOException\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n  }\n\n  /**\n   * Creates register client and connects to the default port of the\n   * specified host.  If host is null, connects to localhost.\n   */\n  public RemoteAddressSpaceClient(final PrintStream console, final String host)\n    throws IOException\n  {\n    this(console);\n    connect(host);\n  }\n\n  /**\n   * Creates register client and connects to the specified port of the\n   * specified host.  If host is null, connects to localhost.\n   */\n  public RemoteAddressSpaceClient(final PrintStream console,\n                                  final String host, final int port)\n    throws IOException\n  {\n    this(console);\n    connect(host, port);\n  }\n\n  /**\n   * Return host of most recently successfully established connection.\n   * Return value is undefined if no connection has been successfully\n   * established so far.  To check if this is the case, use method\n   * getPort() and check for return value of -1.\n   */\n  public String getHost() { return host; }\n\n  /**\n   * Return port number of most recently successfully established\n   * connection or -1, if no connection has been successfully\n   * established so far.\n   */\n  public int getPort() { return port; }\n\n  /**\n   * Connects this register client to the default port of the\n   * specified host.  If host is null, connects to localhost.\n   */\n  public void connect(final String host)\n    throws IOException\n  {\n    connect(host, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER);\n  }\n\n  /**\n   * Connects this register client to the specified port of\n   * localhost.\n   */\n  public void connect(final int port)\n    throws IOException\n  {\n    connect(null, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER);\n  }\n\n  /**\n   * Connects this register client to the specified port of the\n   * specified host.  If host is null, connects to localhost.\n   */\n  public void connect(final String host, final int port)\n    throws IOException\n  {\n    if (socket != null) {\n      try {\n        socket.close();\n      } catch (final IOException e) {\n        // ignore, we are throwing this connection away anyway\n      }\n    }\n    socket = new Socket();\n    socket.connect(host != null ?\n                   new InetSocketAddress(host, port) :\n                   new InetSocketAddress(InetAddress.getByName(null), port));\n    this.host = host;\n    this.port = port;\n  }\n\n  private synchronized Response getResponse(final String request)\n    throws IOException\n  {\n    final PrintWriter out = new PrintWriter(socket.getOutputStream(), true);\n    final BufferedReader in =\n      new BufferedReader(new InputStreamReader(socket.getInputStream()));\n    out.println(request);\n    final String response = in.readLine();\n    if (response == null) {\n      return null;\n    }\n    final int colonPos = response.indexOf(':');\n    final String statusDisplay =\n      colonPos >= 0 ? response.substring(0, colonPos) : response;\n    final int spacePos = statusDisplay.indexOf(' ');\n    if (spacePos < 0) {\n      throw new IOException(\"failed parsing server response status: \" +\n                            statusDisplay);\n    }\n    final String statusCodeAsString = statusDisplay.substring(0, spacePos);\n    final int statusCode;\n    try {\n      statusCode = Integer.parseInt(statusCodeAsString);\n    } catch (final NumberFormatException e) {\n      throw new IOException(\"failed parsing server response status code: \" +\n                            statusCodeAsString);\n    }\n    final String statusId = statusDisplay.substring(spacePos + 1).trim();\n    final String result =\n      colonPos >= 0 ? response.substring(colonPos + 1).trim() : null;\n    return new Response(console, statusCode, statusId, result);\n  }\n\n  private void checkResponse(final Response response) throws IOException\n  {\n    if (response == null) {\n      throw new IOException(MSG_NO_CONNECTION);\n    }\n  }\n\n  @Override\n  public String getEmulatorInfo() throws IOException\n  {\n    final Response response = getResponse(\"v\");\n    checkResponse(response);\n    return response.getResultOrThrowOnFailure(\"failed retreiving version\");\n  }\n\n  public String getHelp() throws IOException\n  {\n    final Response response = getResponse(\"h\");\n    checkResponse(response);\n    return response.getResultOrThrowOnFailure(\"failed retreiving help\");\n  }\n\n  public void quit() throws IOException\n  {\n    final Response response = getResponse(\"q\");\n    if (response != null) {\n      throw new IOException(\"unexpected response on quit: \" + response);\n    }\n  }\n\n  @Override\n  public boolean providesAddress(final int address) throws IOException\n  {\n    final String request = String.format(\"p 0x%08x\", address);\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String retrievalMessage =\n      String.format(\"failed retrieving provision info for address 0x%08x\",\n                    address);\n    final String result =\n      response.getResultOrThrowOnFailure(retrievalMessage);\n    if (result == null) {\n      final String message =\n        String.format(\"missing provision info for address 0x%08x\", address);\n      throw new IOException(message);\n    }\n    final boolean provided;\n    try {\n      provided = Boolean.parseBoolean(result);\n    } catch (final NumberFormatException e) {\n      final String message =\n        String.format(\"failed parsing provision info for address 0x%08x: %s\",\n                      address, result);\n      throw new IOException(message);\n    }\n    return provided;\n  }\n\n  @Override\n  public String getRegisterSetId(final int address) throws IOException\n  {\n    final String request = String.format(\"s 0x%08x\", address);\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String retrievalMessage =\n      String.format(\"failed retrieving register set for address 0x%08x\",\n                    address);\n    final String result =\n      response.getResultOrThrowOnFailure(retrievalMessage);\n    if (result == null) {\n      final String message =\n        String.format(\"missing register set for address 0x%08x\", address);\n      throw new IOException(message);\n    }\n    return result;\n  }\n\n  @Override\n  public String getAddressLabel(final int address) throws IOException\n  {\n    final String request = String.format(\"l 0x%08x\", address);\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String retrievalMessage =\n      String.format(\"failed retrieving label for address 0x%08x\", address);\n    final String result =\n      response.getResultOrThrowOnFailure(retrievalMessage);\n    if (result == null) {\n      final String message =\n        String.format(\"missing label for address 0x%08x\", address);\n      throw new IOException(message);\n    }\n    return result;\n  }\n\n  @Override\n  public void writeAddressMasked(final int address, final int bits,\n                                 final int mask, final boolean xor)\n    throws IOException\n  {\n    final String request = String.format(\"w 0x%08x 0x%08x 0x%08x %s\",\n                                         address, bits, mask, xor ? \"t\" : \"f\");\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String message =\n      String.format(\"failed writing value 0x%08x to address 0x%08x with \" +\n                    \"mask 0x%08x and xor=%s\", bits, address, mask, xor);\n    response.getResultOrThrowOnFailure(message);\n  }\n\n  private int parseIntResult(final int address, final String result)\n    throws IOException\n  {\n    if (result == null) {\n      final String message =\n        String.format(\"missing value for address 0x%08x\", address);\n      throw new IOException(message);\n    }\n    final int value;\n    try {\n      value = Integer.parseInt(result);\n    } catch (final NumberFormatException e) {\n      final String message =\n        String.format(\"failed parsing value for address 0x%08x: %s\",\n                      address, result);\n      throw new IOException(message);\n    }\n    return value;\n  }\n\n  @Override\n  public int readAddress(final int address) throws IOException\n  {\n    final String request = String.format(\"r 0x%08x\", address);\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String message =\n      String.format(\"failed retrieving value for address 0x%08x\", address);\n    final String result = response.getResultOrThrowOnFailure(message);\n    return parseIntResult(address, result);\n  }\n\n  @Override\n  public int waitAddress(final int address,\n                         final int expectedValue, final int mask,\n                         final long cyclesTimeout, final long millisTimeout)\n    throws IOException\n  {\n    final StringBuffer query = new StringBuffer();\n    final String request =\n      String.format(\"i 0x%08x 0x%08x 0x%08x %d %d\",\n                    address, expectedValue, mask, cyclesTimeout, millisTimeout);\n    final Response response = getResponse(request);\n    checkResponse(response);\n    final String message =\n      String.format(\"failed waiting for IRQ on address 0x%08x\", address);\n    final String result =\n      response.getResultOrThrowOnFailure(message);\n    return parseIntResult(address, result);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/RemoteAddressSpaceServer.java",
    "content": "/*\n * @(#)RemoteAddressSpaceServer.java 1.00 21/03/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.net.ServerSocket;\nimport java.net.Socket;\n\n/**\n * The idea of the RemoteAddressSpaceServer class is to provide access\n * to the PIO emulator applicable even for processes other than the\n * JVM instance that hosts the PIO emulator, and potential integration\n * with other languages such as C/C++ or Python.  Effectively, this\n * class adds an architectural layer that provides the PIO emulator as\n * software as a service (SaaS).  Access is provided via a standard\n * TC/IP socket with a simple protocol for accessing the PIO\n * emulator's pseudo-memory-mapped registers, including the additional\n * emulator-specific extended set of registers (such as for accessing\n * the internal X and Y register or FIFO values).  For example, even\n * an ordinary C program (like one created with the pioasm tool) may\n * make access the PIO emulator by compiling it against a special\n * extended version of the Pico C SDK, such that e.g. set up and\n * control of the PIO Emulator can be done directly from the C code\n * injected in a .pio file.  Similarly, a Python library may be\n * developed that replaces the standard Pico Python libary with one\n * that accesses the emulator instead of real Pico hardware.\n *\n * The Pico Host SDL shows a specific example that draws the general\n * idea of how to extend the Pico C SDK in such a manner (see:\n * https://github.com/raspberrypi/pico-host-sdl).  For this PIO\n * emulator, the SDK is to be extended in a way similar to the Pico\n * Host SDL, such that access to the PIO's registers is not performed\n * via direct memory access (as the default implementation of the C\n * SDK does), but via the socket interface that this\n * RemoteAddressSpaceServer class provides.\n */\npublic class RemoteAddressSpaceServer\n{\n  private static final String[] NULL_ARGS = new String[0];\n\n  private final PrintStream console;\n  private final AddressSpace memory;\n  private final int portNumber;\n  private final ServerSocket serverSocket;\n  private int connectionCounter;\n\n  private RemoteAddressSpaceServer()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public RemoteAddressSpaceServer(final PrintStream console,\n                                  final AddressSpace memory)\n    throws IOException\n  {\n    this(console, memory, Constants.REGISTER_SERVER_DEFAULT_PORT_NUMBER);\n  }\n\n  public RemoteAddressSpaceServer(final PrintStream console,\n                                  final AddressSpace memory,\n                                  final int portNumber)\n    throws IOException\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    if (memory == null) {\n      throw new NullPointerException(\"memory\");\n    }\n    this.console = console;\n    this.memory = memory;\n    this.portNumber = portNumber;\n    serverSocket = new ServerSocket(portNumber);\n    connectionCounter = 0;\n    // TODO: Maybe introduce pool of reusable client threads to limit\n    // maximum number of simultaneously open connections.\n    new Thread(() -> listen(),\n               \"RemoteAddressSpaceServer Client Thread\").start();\n  }\n\n  private void listen()\n  {\n    while (true) {\n      try {\n        final Socket clientSocket = serverSocket.accept();\n        new Thread(() -> serve(clientSocket),\n                   \"RemoteAddressSpaceServer Server Thread\").start();\n      } catch (final IOException e) {\n        // establishing connection failed => abort connection\n      }\n    }\n  }\n\n  private String getHelp()\n  {\n    final String ls = System.lineSeparator();\n    return \"available commands: \" + ls +\n      \"h                   (help)\" + ls +\n      \"v                   (version)\" + ls +\n      \"q                   (quit)\" + ls +\n      \"r <addr>            (read address)\" + ls +\n      \"w <addr> <value> <mask> <xor>\" + ls +\n      \"                    (write address)\" + ls +\n      \"i <addr> <value> [<mask> [<timeout cycles> [<timeout millis>]]]\" + ls +\n      \"                    (await value)\" + ls +\n      \"s <addr>            (show address register set id)\" + ls +\n      \"l <addr>            (show address label)\" + ls +\n      \"p <addr>            (check address validity)\";\n  }\n\n  private enum ResponseStatus\n  {\n    BYE(\"bye\", 100),\n    OK(\"ok\", 101),\n    ERR_UNKNOWN_COMMAND(\"unknown command\", 400),\n    ERR_MISSING_OPERAND(\"missing operand\", 401),\n    ERR_UNPARSED_INPUT(\"unparsed input\", 402),\n    ERR_INVALID_NUMBER(\"invalid number\", 403),\n    ERR_INVALID_BOOL(\"invalid Boolean value\", 404),\n    ERR_IO(\"input / output error\", 405),\n    ERR_UNEXPECTED(\"unexpected error\", 406);\n\n    private final String id;\n    private final int code;\n\n    private ResponseStatus(final String id, final int code)\n    {\n      if (id == null) {\n        throw new NullPointerException(\"id\");\n      }\n      this.id = id;\n      this.code = code;\n    }\n\n    public String getId() { return id; }\n\n    public int getCode() { return code; }\n\n    public String getDisplayValue()\n    {\n      return code + \" \" + id.toUpperCase();\n    }\n  };\n\n  private String createResponse(final ResponseStatus status)\n  {\n    return createResponse(status, null);\n  }\n\n  private String createResponse(final ResponseStatus status,\n                                final String message)\n  {\n    if (status == null) {\n      throw new NullPointerException(\"status\");\n    }\n    final String statusDisplay = status.getDisplayValue();\n    return statusDisplay + (message != null ? \": \" + message : \"\");\n  }\n\n  private boolean parseBoolean(final String unparsed)\n  {\n    if (unparsed.equals(\"t\") ||\n        unparsed.equals(\"T\")) {\n      return true;\n    } else if (unparsed.equals(\"f\") ||\n        unparsed.equals(\"F\")) {\n      return false;\n    } else {\n      final String message =\n        String.format(\"expected Boolean value 't' or 'f': %s\", unparsed);\n      throw new IllegalArgumentException(message);\n    }\n  }\n\n  private int parseInt(final String unparsed)\n  {\n    if (unparsed.startsWith(\"0x\") ||\n        unparsed.startsWith(\"0X\")) {\n      return Integer.parseUnsignedInt(unparsed.substring(2), 16);\n    } else {\n      return Integer.parseInt(unparsed);\n    }\n  }\n\n  private int parseAddress(final String unparsed)\n  {\n    final int address = parseInt(unparsed);\n    if ((address & 0x3) != 0x0) {\n      final String message =\n        String.format(\"address not word-aligned: 0x%08x\", address);\n      throw new NumberFormatException(message);\n    }\n    return address;\n  }\n\n  private String handleGetVersion(final String[] args) throws IOException\n  {\n    if (args.length > 0) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]);\n    }\n    return createResponse(ResponseStatus.OK, memory.getEmulatorInfo());\n  }\n\n  private String handleGetHelp(final String[] args)\n  {\n    if (args.length > 0) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]);\n    }\n    return createResponse(ResponseStatus.OK, getHelp());\n  }\n\n  private String handleQuit(final String[] args)\n  {\n    if (args.length > 0) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[0]);\n    }\n    return null;\n  }\n\n  private String handleProvidesAddress(final String[] args) throws IOException\n  {\n    if (args.length < 1) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 1) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final boolean providesAddress = memory.providesAddress(address);\n    return createResponse(ResponseStatus.OK, String.valueOf(providesAddress));\n  }\n\n  private String handleGetRegisterSetId(final String[] args) throws IOException\n  {\n    if (args.length < 1) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 1) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final String id = memory.getRegisterSetId(address);\n    return createResponse(ResponseStatus.OK, id);\n  }\n\n  private String handleGetLabel(final String[] args) throws IOException\n  {\n    if (args.length < 1) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 1) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final String label = memory.getAddressLabel(address);\n    return createResponse(ResponseStatus.OK, label);\n  }\n\n  private String handleWriteAddress(final String[] args) throws IOException\n  {\n    if (args.length < 4) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 4) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[4]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final int value;\n    try {\n      value = parseInt(args[1]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final int mask;\n    try {\n      mask = parseInt(args[2]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final boolean xor;\n    try {\n      xor = parseBoolean(args[3]);\n    } catch (final IllegalArgumentException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_BOOL, e.getMessage());\n    }\n    memory.writeAddressMasked(address, value, mask, xor);\n    return createResponse(ResponseStatus.OK);\n  }\n\n  private String handleReadAddress(final String[] args) throws IOException\n  {\n    if (args.length < 1) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 1) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[1]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final int value = memory.readAddress(address);\n    return createResponse(ResponseStatus.OK, String.valueOf(value));\n  }\n\n  private String handleWait(final String[] args) throws IOException\n  {\n    if (args.length < 2) {\n      return createResponse(ResponseStatus.ERR_MISSING_OPERAND, null);\n    }\n    if (args.length > 5) {\n      return createResponse(ResponseStatus.ERR_UNPARSED_INPUT, args[5]);\n    }\n    final int address;\n    try {\n      address = parseAddress(args[0]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final int expectedValue;\n    try {\n      expectedValue = parseInt(args[1]);\n    } catch (final NumberFormatException e) {\n      return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n    }\n    final int mask;\n    if (args.length > 2) {\n      try {\n        mask = parseInt(args[2]);\n      } catch (final NumberFormatException e) {\n        return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n      }\n    } else {\n      mask = 0xffffffff;\n    }\n    final int cyclesTimeout;\n    if (args.length > 3) {\n      try {\n        cyclesTimeout = parseInt(args[3]);\n      } catch (final NumberFormatException e) {\n        return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n      }\n    } else {\n      cyclesTimeout = 0x0;\n    }\n    final int millisTimeout;\n    if (args.length > 4) {\n      try {\n        millisTimeout = parseInt(args[4]);\n      } catch (final NumberFormatException e) {\n        return createResponse(ResponseStatus.ERR_INVALID_NUMBER, e.getMessage());\n      }\n    } else {\n      millisTimeout = 0x0;\n    }\n    final int value =\n      memory.waitAddress(address, expectedValue, mask,\n                         ((long)cyclesTimeout) & 0xffffffffL,\n                         ((long)millisTimeout) & 0xffffffffL);\n    return createResponse(ResponseStatus.OK, String.valueOf(value));\n  }\n\n  private String handleRequest(final String request) throws IOException\n  {\n    if (request.isEmpty()) {\n      return null;\n    }\n    final char command = request.charAt(0);\n    final String unparsedArgs = request.substring(1).trim();\n    final String[] args =\n      unparsedArgs.length() > 0 ? unparsedArgs.split(\" \") : NULL_ARGS;\n    /*\n     * TODO: Idea: Introduce another command 's' for waiting (or\n     * \"sleeping\") until the emulator runs idle (in MasterClock\n     * SINGLE_STEP mode).  This way, an external application like\n     * TimingDiagram does not need to busy wait (via periodic polling\n     * with 'r' read command) until a triggered clock phase has been\n     * completed.\n     */\n    switch (command) {\n    case 'v':\n      return handleGetVersion(args);\n    case 'h':\n    case '?':\n      return handleGetHelp(args);\n    case 'q':\n      return handleQuit(args);\n    case 'p':\n      return handleProvidesAddress(args);\n    case 's':\n      return handleGetRegisterSetId(args);\n    case 'l':\n      return handleGetLabel(args);\n    case 'w':\n      return handleWriteAddress(args);\n    case 'r':\n      return handleReadAddress(args);\n    case 'i':\n      return handleWait(args);\n    default:\n      return createResponse(ResponseStatus.ERR_UNKNOWN_COMMAND,\n                            String.valueOf(command));\n    }\n  }\n\n  private void handleThrowable(final PrintWriter clientOut, final Throwable t,\n                               final ResponseStatus responseStatus,\n                               final int id)\n  {\n    if (clientOut != null) {\n      try {\n        clientOut.println(createResponse(responseStatus, t.getMessage()));\n      } catch (final Throwable s) {\n        // ignore\n      }\n    }\n    t.printStackTrace(console);\n    console.printf(\"connection #%d aborted: %s%n\", id, t);\n  }\n\n  private void serve(final Socket clientSocket)\n  {\n    final int id = connectionCounter++;\n    console.printf(\"connection #%d opened%n\", id);\n    PrintWriter clientOut = null;\n    try {\n      clientOut = new PrintWriter(clientSocket.getOutputStream(), true);\n      final BufferedReader in =\n        new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));\n      String request;\n      while ((request = in.readLine()) != null) {\n        final String response = handleRequest(request.trim());\n        if (response == null) {\n          break;\n        }\n        clientOut.println(response);\n      }\n    } catch (final IOException e) {\n      handleThrowable(clientOut, e, ResponseStatus.ERR_IO, id);\n    } catch (final Throwable t) {\n      handleThrowable(clientOut, t, ResponseStatus.ERR_UNEXPECTED, id);\n    } finally {\n      console.printf(\"connection #%d closed%n\", id);\n      try {\n        clientSocket.close();\n      } catch (final IOException e) {\n        console.println(\"warning: failed closing client socket: \" + e);\n      }\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/SM.java",
    "content": "/*\n * @(#)SM.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.io.PrintStream;\nimport java.util.function.Function;\nimport java.util.function.IntConsumer;\n\n/**\n * State Machine\n */\npublic class SM implements Constants\n{\n  private final int num;\n  private final PrintStream console;\n  private final MasterClock masterClock;\n  private final PIOGPIO pioGpio;\n  private final Memory memory;\n  private final IRQ irq;\n  private final Status status;\n  private final Decoder decoder;\n  private final FIFO fifo;\n  private final PLL pll;\n\n  public enum IOMapping\n  {\n    SET((sm) -> sm.status.regPINCTRL_SET_BASE,\n        (sm) -> sm.status.regPINCTRL_SET_COUNT),\n    OUT((sm) -> sm.status.regPINCTRL_OUT_BASE,\n        (sm) -> sm.status.regPINCTRL_OUT_COUNT);\n\n    private final Function<SM, Integer> baseGetter;\n    private final Function<SM, Integer> countGetter;\n\n    private IOMapping(final Function<SM, Integer> baseGetter,\n                      final Function<SM, Integer> countGetter)\n    {\n      this.baseGetter = baseGetter;\n      this.countGetter = countGetter;\n    }\n\n    public void collatePins(final SM sm, final int data)\n    {\n      sm.status.\n        collatePins(data, baseGetter.apply(sm), countGetter.apply(sm), false);\n    }\n\n    public void collatePinDirs(final SM sm, final int data)\n    {\n      sm.status.\n        collatePinDirs(data, baseGetter.apply(sm), countGetter.apply(sm));\n    }\n  };\n\n  public class Status\n  {\n    public Instruction instruction;\n    public int origin;\n    public Instruction.ResultState resultState;\n    public boolean processing;\n    public boolean smEnabled;\n    public boolean clockEnabled;\n    public boolean isDelayCycle;\n    public int collateSideSetPins;\n    public int collateSideSetBase;\n    public int collateSideSetCount;\n    public int outStickyPins;\n    public int outStickyBase;\n    public int outStickyCount;\n    public boolean havePendingOutOrSetPins;\n    public int regX;\n    public int regY;\n    public int isrValue;\n    public int isrShiftCount;\n    public int osrValue;\n    public int osrShiftCount;\n    public int totalDelay;\n    public int pendingDelay;\n    public int pendingForcedInstruction;\n    public boolean isForcedInstruction;\n    public int pendingExecdInstruction;\n    public int regADDR; // bits 0…4 of SMx_ADDR\n    public boolean regEXECCTRL_SIDE_EN; // bit 30 of SMx_EXECCTRL\n    public PIO.PinDir regEXECCTRL_SIDE_PINDIR; // bit 29 of SMx_EXECCTRL\n    public int regEXECCTRL_JMP_PIN; // bits 24…28 of SMx_EXECCTRL\n    public int regEXECCTRL_OUT_EN_SEL; // bits 19…23 of SMx_EXECCTRL\n    public boolean regEXECCTRL_INLINE_OUT_EN; // bit 18 of SMx_EXECCTRL\n    public boolean regEXECCTRL_OUT_STICKY; // bit 17 of SMx_EXECCTRL\n    public int regEXECCTRL_WRAP_TOP; // bits 12…16 of SMx_EXECCTRL\n    public int regEXECCTRL_WRAP_BOTTOM; // bits 7…11 of SMx_EXECCTRL\n    public boolean regEXECCTRL_STATUS_SEL; // bit 4 of SMx_EXECCTRL\n    public int regEXECCTRL_STATUS_N; // bits 0…3 of SMx_EXECCTRL\n    public int regSHIFTCTRL_PULL_THRESH; // bits 25…29 of SMx_SHIFTCTRL\n    public int regSHIFTCTRL_PUSH_THRESH; // bits 20…24 of SMx_SHIFTCTRL\n    public PIO.ShiftDir regSHIFTCTRL_IN_SHIFTDIR; // bit 18 of SMx_SHIFTCTRL\n    public boolean regSHIFTCTRL_AUTOPULL; // bit 17 of SMx_SHIFTCTRL\n    public PIO.ShiftDir regSHIFTCTRL_OUT_SHIFTDIR; // bit 19 of SMx_SHIFTCTRL\n    public boolean regSHIFTCTRL_AUTOPUSH; // bit 16 of SMx_SHIFTCTRL\n    public int regPINCTRL_SIDESET_COUNT; // bits 29…31 of SMx_PINCTRL\n    public int regPINCTRL_SET_COUNT; // bits 26…28 of SMx_PINCTRL\n    public int regPINCTRL_OUT_COUNT; // bits 20…25 of SMx_PINCTRL\n    public int regPINCTRL_IN_BASE; // bits 15…19 of SMx_PINCTRL\n    public int regPINCTRL_SIDESET_BASE; // bits 10…14 of SMx_PINCTRL\n    public int regPINCTRL_SET_BASE; // bits 5…9 of SMx_PINCTRL\n    public int regPINCTRL_OUT_BASE; // bits 0…4 of SMx_PINCTRL\n\n    // PIOEmuRegisters Status\n    public int regBREAKPOINTS; // bits 0…31 of SMx_BREAKPOINTS\n    public int regTRACEPOINTS; // bits 0…31 of SMx_TRACEPOINTS\n\n    public Status()\n    {\n      reset();\n    }\n\n    private void reset()\n    {\n      instruction = null;\n      origin = INSTR_ORIGIN_UNKNOWN;\n      resultState = null;\n      processing = false;\n      smEnabled = false;\n      clockEnabled = false;\n      isDelayCycle = false;\n      collateSideSetPins = 0;\n      collateSideSetBase = 0;\n      collateSideSetCount = 0;\n      outStickyPins = 0;\n      outStickyBase = 0;\n      outStickyCount = 0;\n      havePendingOutOrSetPins = false;\n      regX = 0;\n      regY = 0;\n      isrValue = 0;\n      isrShiftCount = 0;\n      osrValue = 0;\n      osrShiftCount = 32;\n      totalDelay = 0;\n      pendingDelay = 0;\n      pendingForcedInstruction = -1;\n      isForcedInstruction = false;\n      pendingExecdInstruction = -1;\n      regADDR = 0;\n      regEXECCTRL_STATUS_SEL = false;\n      regEXECCTRL_STATUS_N = 0;\n      regEXECCTRL_SIDE_EN = false;\n      regEXECCTRL_SIDE_PINDIR = PIO.PinDir.GPIO_LEVELS;\n      regEXECCTRL_JMP_PIN = 0;\n      regEXECCTRL_OUT_EN_SEL = 0;\n      regEXECCTRL_INLINE_OUT_EN = false;\n      regEXECCTRL_OUT_STICKY = false;\n      regEXECCTRL_WRAP_TOP = MEMORY_SIZE - 1;\n      regEXECCTRL_WRAP_BOTTOM = 0x00;\n      regSHIFTCTRL_PULL_THRESH = 0;\n      regSHIFTCTRL_PUSH_THRESH = 0;\n      regSHIFTCTRL_IN_SHIFTDIR = PIO.ShiftDir.SHIFT_LEFT;\n      regSHIFTCTRL_AUTOPULL = false;\n      regSHIFTCTRL_OUT_SHIFTDIR = PIO.ShiftDir.SHIFT_LEFT;\n      regSHIFTCTRL_AUTOPUSH = false;\n      regPINCTRL_SIDESET_COUNT = 0;\n      regPINCTRL_SET_COUNT = 0x5;\n      regPINCTRL_OUT_COUNT = 0;\n      regPINCTRL_IN_BASE = 0;\n      regPINCTRL_SIDESET_BASE = 0;\n      regPINCTRL_SET_BASE = 0;\n      regPINCTRL_OUT_BASE = 0;\n\n      // PIOEmuRegisters Status\n      regBREAKPOINTS = 0;\n      regTRACEPOINTS = 0;\n    }\n\n    public void restart()\n    {\n      /*\n       * See RP2040 datasheet, Table 378: CTRL Register, SM_RESTART:\n       *\n       * Specifically, the following are cleared: input and output\n       * shift counters; the contents of the input shift register; the\n       * delay counter; the waiting-on-IRQ state; any stalled\n       * instruction written to SMx_INSTR or run by OUT/MOV EXEC; any\n       * pin write left asserted due to OUT_STICKY.\n       */\n      isrShiftCount = 0;\n      osrShiftCount = 32;\n      isrValue = 0;\n      isDelayCycle = false;\n      outStickyPins = 0;\n      outStickyBase = 0;\n      outStickyCount = 0;\n      havePendingOutOrSetPins = false;\n      totalDelay = 0;\n      pendingDelay = 0;\n      pendingForcedInstruction = -1;\n      isForcedInstruction = false;\n      pendingExecdInstruction = -1;\n      regEXECCTRL_OUT_STICKY = false;\n    }\n\n    public Bit jmpPin()\n    {\n      /*\n       * RP2040 datasheet 3.4.2. \"JMP\": \"JMP PIN branches on the GPIO\n       * … independently of the state machine's other input mapping.\"\n       * => Return global GPIO's input to peripherals rather than the\n       * local GPIO pin state of this state machine's PIO.\n       */\n      return pioGpio.getGPIO().getInToPeri(regEXECCTRL_JMP_PIN);\n    }\n\n    public void collatePins(final int pins, final int base, final int count,\n                            final boolean isSideSetOperation)\n    {\n      if (isSideSetOperation) {\n        collateSideSetPins = pins;\n        collateSideSetBase = base;\n        collateSideSetCount = count;\n      } else {\n        outStickyPins = pins;\n        outStickyBase = base;\n        outStickyCount = count;\n        havePendingOutOrSetPins = true;\n      }\n    }\n\n    private void flushCollatePins()\n    {\n      if (havePendingOutOrSetPins || regEXECCTRL_OUT_STICKY) {\n        final boolean outEn =\n          !regEXECCTRL_INLINE_OUT_EN ||\n          (((outStickyPins >>> regEXECCTRL_OUT_EN_SEL) & 0x1) == 0x1);\n        if (outEn) {\n          pioGpio.collatePins(outStickyPins, outStickyBase, outStickyCount);\n        }\n        havePendingOutOrSetPins = false;\n      }\n      /*\n       * RP2040 datasheet, Sect. 3.5.6. \"GPIO Mapping\": If side-set\n       * overlaps with OUT/SET, side-set takes precedence.  => Perform\n       * collatePins() for side-set as last step.\n       */\n      if (collateSideSetCount > 0) {\n        pioGpio.collatePins(collateSideSetPins, collateSideSetBase,\n                            collateSideSetCount);\n        collateSideSetCount = 0;\n      }\n    }\n\n    public void collatePinDirs(final int pins, final int base, final int count)\n    {\n      pioGpio.collatePinDirs(pins, base, count);\n    }\n\n    public int getFIFOStatus()\n    {\n      final boolean fulfilled;\n      if (regEXECCTRL_STATUS_SEL) {\n        fulfilled = fifo.getRXLevel() < regEXECCTRL_STATUS_N;\n      } else {\n        fulfilled = fifo.getTXLevel() < regEXECCTRL_STATUS_N;\n      }\n      return fulfilled ? ~0 : 0;\n    }\n\n    public boolean isIsrCountBeyondThreshold()\n    {\n      return isrShiftCount >=\n        (regSHIFTCTRL_PUSH_THRESH != 0 ? regSHIFTCTRL_PUSH_THRESH : 32);\n    }\n\n    public boolean isOsrCountBeyondThreshold()\n    {\n      return osrShiftCount >=\n        (regSHIFTCTRL_PULL_THRESH != 0 ? regSHIFTCTRL_PULL_THRESH : 32);\n    }\n\n    private boolean consumePendingDelay()\n    {\n      if (pendingDelay == 0)\n        return false;\n      pendingDelay--;\n      return true;\n    }\n\n    private void setPendingDelay(final int delay)\n    {\n      if (delay < 0) {\n        throw new IllegalArgumentException(\"delay < 0: \" + delay);\n      }\n      if (delay > 31) {\n        throw new IllegalArgumentException(\"delay > 31: \" + delay);\n      }\n      this.pendingDelay = delay;\n      this.totalDelay = delay;\n    }\n\n    @Override\n    public String toString()\n    {\n      return String.format(\"Status(SM%d)\", SM.this.num);\n    }\n  }\n\n  private SM()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public SM(final int num, final PrintStream console,\n            final MasterClock masterClock, final PIOGPIO pioGpio,\n            final Memory memory, final IRQ irq)\n  {\n    if (num < 0) {\n      throw new IllegalArgumentException(\"SM num < 0: \" + num);\n    }\n    if (num > 3) {\n      throw new IllegalArgumentException(\"SM num > 3: \" + num);\n    }\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    if (masterClock == null) {\n      throw new NullPointerException(\"masterClock\");\n    }\n    if (pioGpio == null) {\n      throw new NullPointerException(\"pioGpio\");\n    }\n    if (memory == null) {\n      throw new NullPointerException(\"memory\");\n    }\n    if (irq == null) {\n      throw new NullPointerException(\"irq\");\n    }\n    this.num = num;\n    this.console = console;\n    this.masterClock = masterClock;\n    this.pioGpio = pioGpio;\n    this.memory = memory;\n    this.irq = irq;\n    status = new Status();\n    decoder = new Decoder();\n    fifo = new FIFO(num, irq);\n    pll = new PLL(console);\n  }\n\n  public int getNum() { return num; }\n\n  public PIOGPIO getPIOGPIO() { return pioGpio; }\n\n  public Memory getMemory() { return memory; }\n\n  public Status getStatus() { return status; }\n\n  public FIFO getFIFO() { return fifo; }\n\n  public PLL getPLL() { return pll; }\n\n  public void reset()\n  {\n    status.reset();\n    decoder.reset();\n    fifo.reset();\n    pll.reset();\n  }\n\n  public void setCLKDIV(final int clkdiv, final int mask, final boolean xor)\n  {\n    pll.setCLKDIV(Constants.hwSetBits(pll.getCLKDIV(), clkdiv, mask, xor));\n  }\n\n  public int getCLKDIV()\n  {\n    return pll.getCLKDIV();\n  }\n\n  public void resetCLKDIV()\n  {\n    pll.reset();\n  }\n\n  public int outEnSel()\n  {\n    return status.regEXECCTRL_OUT_EN_SEL;\n  }\n\n  public boolean inlineOutEn()\n  {\n    return status.regEXECCTRL_INLINE_OUT_EN;\n  }\n\n  public boolean outSticky()\n  {\n    return status.regEXECCTRL_OUT_STICKY;\n  }\n\n  public void setEXECCTRL(final int execctrl, final int mask, final boolean xor)\n  {\n    setEXECCTRL(Constants.hwSetBits(getEXECCTRL(), execctrl, mask, xor));\n  }\n\n  private void setEXECCTRL(final int execctrl)\n  {\n    status.regEXECCTRL_SIDE_EN =\n      ((execctrl & SM0_EXECCTRL_SIDE_EN_BITS) >>>\n       SM0_EXECCTRL_SIDE_EN_LSB) != 0x0;\n    status.regEXECCTRL_SIDE_PINDIR =\n      PIO.PinDir.fromValue((execctrl & SM0_EXECCTRL_SIDE_PINDIR_BITS) >>>\n                           SM0_EXECCTRL_SIDE_PINDIR_LSB);\n    status.regEXECCTRL_JMP_PIN =\n      (execctrl & SM0_EXECCTRL_JMP_PIN_BITS) >>> SM0_EXECCTRL_JMP_PIN_LSB;\n    status.regEXECCTRL_OUT_EN_SEL =\n      (execctrl & SM0_EXECCTRL_OUT_EN_SEL_BITS) >>> SM0_EXECCTRL_OUT_EN_SEL_LSB;\n    status.regEXECCTRL_INLINE_OUT_EN =\n      ((execctrl & SM0_EXECCTRL_INLINE_OUT_EN_BITS) >>>\n       SM0_EXECCTRL_INLINE_OUT_EN_LSB) != 0x0;\n    status.regEXECCTRL_OUT_STICKY =\n      ((execctrl & SM0_EXECCTRL_OUT_STICKY_BITS) >>>\n       SM0_EXECCTRL_OUT_STICKY_LSB) != 0x0;\n    status.regEXECCTRL_WRAP_TOP =\n      (execctrl & SM0_EXECCTRL_WRAP_TOP_BITS) >>> SM0_EXECCTRL_WRAP_TOP_LSB;\n    status.regEXECCTRL_WRAP_BOTTOM =\n      (execctrl & SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>>\n      SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n    status.regEXECCTRL_STATUS_SEL =\n      ((execctrl & SM0_EXECCTRL_STATUS_SEL_BITS) >>>\n       SM0_EXECCTRL_STATUS_SEL_LSB) != 0x0;\n    status.regEXECCTRL_STATUS_N =\n      (execctrl & SM0_EXECCTRL_STATUS_N_BITS) >>> SM0_EXECCTRL_STATUS_N_LSB;\n  }\n\n  public int getEXECCTRL()\n  {\n    return\n      (isExecStalled() ? 1 : 0) << SM0_EXECCTRL_EXEC_STALLED_LSB |\n      (status.regEXECCTRL_SIDE_EN ? 1 : 0) << SM0_EXECCTRL_SIDE_EN_LSB |\n      status.regEXECCTRL_SIDE_PINDIR.getValue() <<\n      SM0_EXECCTRL_SIDE_PINDIR_LSB |\n      status.regEXECCTRL_JMP_PIN << SM0_EXECCTRL_JMP_PIN_LSB |\n      status.regEXECCTRL_OUT_EN_SEL << SM0_EXECCTRL_OUT_EN_SEL_LSB |\n      (status.regEXECCTRL_INLINE_OUT_EN ? 1 : 0) <<\n      SM0_EXECCTRL_INLINE_OUT_EN_LSB |\n      (status.regEXECCTRL_OUT_STICKY ? 1 : 0) << SM0_EXECCTRL_OUT_STICKY_LSB |\n      status.regEXECCTRL_WRAP_TOP << SM0_EXECCTRL_WRAP_TOP_LSB |\n      status.regEXECCTRL_WRAP_BOTTOM << SM0_EXECCTRL_WRAP_BOTTOM_LSB |\n      (status.regEXECCTRL_STATUS_SEL ? 1 : 0) << SM0_EXECCTRL_STATUS_SEL_LSB |\n      status.regEXECCTRL_STATUS_N << SM0_EXECCTRL_STATUS_N_LSB;\n  }\n\n  public void setSHIFTCTRL(final int shiftctrl, final int mask,\n                           final boolean xor)\n  {\n    setSHIFTCTRL(Constants.hwSetBits(getSHIFTCTRL(), shiftctrl, mask, xor));\n  }\n\n  private void setSHIFTCTRL(final int shiftctrl)\n  {\n    fifo.setJoinRX(((shiftctrl & SM0_SHIFTCTRL_FJOIN_RX_BITS) >>>\n                    SM0_SHIFTCTRL_FJOIN_RX_LSB) != 0x0);\n    fifo.setJoinTX(((shiftctrl & SM0_SHIFTCTRL_FJOIN_TX_BITS) >>>\n                    SM0_SHIFTCTRL_FJOIN_TX_LSB) != 0x0);\n    status.regSHIFTCTRL_PULL_THRESH =\n      (shiftctrl & SM0_SHIFTCTRL_PULL_THRESH_BITS) >>>\n      SM0_SHIFTCTRL_PULL_THRESH_LSB;\n    status.regSHIFTCTRL_PUSH_THRESH =\n      (shiftctrl & SM0_SHIFTCTRL_PUSH_THRESH_BITS) >>>\n      SM0_SHIFTCTRL_PUSH_THRESH_LSB;\n    status.regSHIFTCTRL_OUT_SHIFTDIR =\n      PIO.ShiftDir.fromValue((shiftctrl & SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS) >>>\n                             SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB);\n    status.regSHIFTCTRL_IN_SHIFTDIR =\n      PIO.ShiftDir.fromValue((shiftctrl & SM0_SHIFTCTRL_IN_SHIFTDIR_BITS) >>>\n                             SM0_SHIFTCTRL_IN_SHIFTDIR_LSB);\n    status.regSHIFTCTRL_AUTOPULL =\n      ((shiftctrl & SM0_SHIFTCTRL_AUTOPULL_BITS) >>>\n       SM0_SHIFTCTRL_AUTOPULL_LSB) != 0x0;\n    status.regSHIFTCTRL_AUTOPUSH =\n      ((shiftctrl & SM0_SHIFTCTRL_AUTOPUSH_BITS) >>>\n       SM0_SHIFTCTRL_AUTOPUSH_LSB) != 0x0;\n  }\n\n  public int getSHIFTCTRL()\n  {\n    return\n      (fifo.getJoinRX() ? 1 : 0) << SM0_SHIFTCTRL_FJOIN_RX_LSB |\n      (fifo.getJoinTX() ? 1 : 0) << SM0_SHIFTCTRL_FJOIN_TX_LSB |\n      status.regSHIFTCTRL_PULL_THRESH << SM0_SHIFTCTRL_PULL_THRESH_LSB |\n      status.regSHIFTCTRL_PUSH_THRESH << SM0_SHIFTCTRL_PUSH_THRESH_LSB |\n      status.regSHIFTCTRL_OUT_SHIFTDIR.getValue() <<\n      SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB |\n      status.regSHIFTCTRL_IN_SHIFTDIR.getValue() <<\n      SM0_SHIFTCTRL_IN_SHIFTDIR_LSB |\n      (status.regSHIFTCTRL_AUTOPULL ? 1 : 0) << SM0_SHIFTCTRL_AUTOPULL_LSB |\n      (status.regSHIFTCTRL_AUTOPUSH ? 1 : 0) << SM0_SHIFTCTRL_AUTOPUSH_LSB;\n  }\n\n  public void setPINCTRL(final int pinctrl, final int mask, final boolean xor)\n  {\n    setPINCTRL(Constants.hwSetBits(getPINCTRL(), pinctrl, mask, xor));\n  }\n\n  private void setPINCTRL(final int pinctrl)\n  {\n    status.regPINCTRL_SIDESET_COUNT =\n      (pinctrl & SM0_PINCTRL_SIDESET_COUNT_BITS) >>>\n      SM0_PINCTRL_SIDESET_COUNT_LSB;\n    status.regPINCTRL_SET_COUNT =\n      (pinctrl & SM0_PINCTRL_SET_COUNT_BITS) >>> SM0_PINCTRL_SET_COUNT_LSB;\n    status.regPINCTRL_OUT_COUNT =\n      (pinctrl & SM0_PINCTRL_OUT_COUNT_BITS) >>> SM0_PINCTRL_OUT_COUNT_LSB;\n    status.regPINCTRL_IN_BASE =\n      (pinctrl & SM0_PINCTRL_IN_BASE_BITS) >>> SM0_PINCTRL_IN_BASE_LSB;\n    status.regPINCTRL_SIDESET_BASE =\n      (pinctrl & SM0_PINCTRL_SIDESET_BASE_BITS) >>>\n      SM0_PINCTRL_SIDESET_BASE_LSB;\n    status.regPINCTRL_SET_BASE =\n      (pinctrl & SM0_PINCTRL_SET_BASE_BITS) >>> SM0_PINCTRL_SET_BASE_LSB;\n    status.regPINCTRL_OUT_BASE =\n      (pinctrl & SM0_PINCTRL_OUT_BASE_BITS) >>> SM0_PINCTRL_OUT_BASE_LSB;\n  }\n\n  public int getPINCTRL()\n  {\n    return\n      status.regPINCTRL_SIDESET_COUNT << SM0_PINCTRL_SIDESET_COUNT_LSB |\n      status.regPINCTRL_SET_COUNT << SM0_PINCTRL_SET_COUNT_LSB |\n      status.regPINCTRL_OUT_COUNT << SM0_PINCTRL_OUT_COUNT_LSB |\n      status.regPINCTRL_IN_BASE << SM0_PINCTRL_IN_BASE_LSB |\n      status.regPINCTRL_SIDESET_BASE << SM0_PINCTRL_SIDESET_BASE_LSB |\n      status.regPINCTRL_SET_BASE << SM0_PINCTRL_SET_BASE_LSB |\n      status.regPINCTRL_OUT_BASE << SM0_PINCTRL_OUT_BASE_LSB;\n  }\n\n  public void clockRisingEdge(final boolean smEnabled, final long wallClock)\n  {\n    status.smEnabled = smEnabled;\n    if (smEnabled) {\n      pll.risingEdge(wallClock);\n      status.clockEnabled = pll.getClockEnable();\n    } else {\n      status.clockEnabled = false;\n    }\n    /*\n     * Sect. 3.5.7.: ... instructions written to the INSTR register\n     * ... execute immediately, ignoring the state machine clock\n     * divider.\n     */\n    status.processing =\n      status.clockEnabled || (status.pendingForcedInstruction >= 0);\n    if (status.processing) {\n      try {\n        if ((status.pendingForcedInstruction >= 0) ||\n            (status.pendingExecdInstruction >= 0) ||\n            !status.consumePendingDelay()) {\n          status.isDelayCycle = false;\n          fetchAndDecode();\n        } else {\n          status.isDelayCycle = true;\n        }\n      } catch (final Decoder.DecodeException e) {\n        console.println(e.getMessage());\n      }\n    } else {\n      status.origin = INSTR_ORIGIN_UNKNOWN;\n    }\n  }\n\n  public void clockFallingEdge(final long wallClock)\n  {\n    if (status.smEnabled) {\n      pll.fallingEdge(wallClock);\n    }\n    if (status.processing) {\n      try {\n        execute();\n      } catch (final RuntimeException e) {\n        e.printStackTrace(console);\n        console.printf(\"internal error: %s%n\", e.getMessage());\n      }\n    }\n  }\n\n  public void restart()\n  {\n    status.restart();\n  }\n\n  public Bit getIRQ(final int index)\n  {\n    return irq.get(index);\n  }\n\n  public void clearIRQ(final int index)\n  {\n    irq.clear(index);\n  }\n\n  public void setIRQ(final int index)\n  {\n    irq.set(index);\n  }\n\n  /**\n   * @return &lt;code&gt;true&lt;/code&gt; if operation stall due to\n   * full FIFO.\n   */\n  public boolean rxPush(final boolean ifFull, final boolean block)\n  {\n    final boolean isrCountBeyondThreshold = status.isIsrCountBeyondThreshold();\n    if (!ifFull || isrCountBeyondThreshold) {\n      final boolean succeeded = fifo.rxPush(status.isrValue, block);\n      if (succeeded) {\n        status.isrValue = 0;\n        status.isrShiftCount = 0;\n        return false;\n      } else {\n        return block;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * @return True if operation stall due to empty FIFO.\n   */\n  public boolean txPull(final boolean ifEmpty, final boolean block)\n  {\n    final boolean osrCountBeyondThreshold = status.isOsrCountBeyondThreshold();\n    if (!ifEmpty || osrCountBeyondThreshold) {\n      synchronized(fifo) {\n        final boolean fifoEmpty = fifo.fstatTxEmpty();\n        if (fifoEmpty) {\n          if (!block) {\n            status.osrValue = status.regX;\n            status.osrShiftCount = 0;\n          }\n          return block; // stall on block\n        } else {\n          status.osrValue = fifo.txPull(block);\n          status.osrShiftCount = 0;\n          return false;\n        }\n      }\n    } else {\n      return false;\n    }\n  }\n\n  public static int saturate(final int base, final int increment,\n                             final int limit)\n  {\n    final int sum = base + increment;\n    return sum < limit ? sum : limit;\n  }\n\n  public int getISRValue() { return status.isrValue; }\n\n  public void setISRValue(final int value)\n  {\n    status.isrValue = value;\n  }\n\n  public void setISRValue(final int value, final int mask, final boolean xor)\n  {\n    status.isrValue = Constants.hwSetBits(status.isrValue, value, mask, xor);\n  }\n\n  public int getISRShiftCount() { return status.isrShiftCount; }\n\n  public void setISRShiftCount(final int value, final int mask,\n                               final boolean xor)\n  {\n    status.isrShiftCount =\n      Constants.hwSetBits(status.isrShiftCount, value, mask, xor);\n  }\n\n  public int getOSRValue() { return status.osrValue; }\n\n  public void setOSRValue(final int value)\n  {\n    status.osrValue = value;\n  }\n\n  public void setOSRValue(final int value, final int mask, final boolean xor)\n  {\n    status.osrValue = Constants.hwSetBits(status.osrValue, value, mask, xor);\n  }\n\n  public int getOSRShiftCount() { return status.osrShiftCount; }\n\n  public void setOSRShiftCount(final int value, final int mask,\n                               final boolean xor)\n  {\n    status.osrShiftCount =\n      Constants.hwSetBits(status.osrShiftCount, value, mask, xor);\n  }\n\n  public void setSideSetCount(final int count)\n  {\n    if (count < 0) {\n      throw new IllegalArgumentException(\"side set count < 0: \" + count);\n    }\n    if (count > 5) {\n      throw new IllegalArgumentException(\"side set count > 5: \" + count);\n    }\n    status.regPINCTRL_SIDESET_COUNT = count;\n  }\n\n  public PIO.ShiftDir getInShiftDir()\n  {\n    return status.regSHIFTCTRL_IN_SHIFTDIR;\n  }\n\n  public PIO.ShiftDir getOutShiftDir()\n  {\n    return status.regSHIFTCTRL_OUT_SHIFTDIR;\n  }\n\n  public int getX() { return status.regX; }\n\n  public void setX(final int value)\n  {\n    status.regX = value;\n  }\n\n  public void setX(final int value, final int mask, final boolean xor)\n  {\n    status.regX = Constants.hwSetBits(status.regX, value, mask, xor);\n  }\n\n  private void decX()\n  {\n    status.regX--;\n  }\n\n  public int getY() { return status.regY; }\n\n  public void setY(final int value)\n  {\n    status.regY = value;\n  }\n\n  public void setY(final int value, final int mask, final boolean xor)\n  {\n    status.regY = Constants.hwSetBits(status.regY, value, mask, xor);\n  }\n\n  private void decY()\n  {\n    status.regY--;\n  }\n\n  public void put(final int data)\n  {\n    synchronized(fifo) {\n      fifo.txDMAWrite(data);\n    }\n  }\n\n  public void putRXF(final int data)\n  {\n    synchronized(fifo) {\n      fifo.rxPush(data, false);\n    }\n  }\n\n  public int get()\n  {\n    synchronized(fifo) {\n      final int value = fifo.rxDMARead();\n      return value;\n    }\n  }\n\n  public int getTXF()\n  {\n    synchronized(fifo) {\n      final int value = fifo.txPull(false);\n      return value;\n    }\n  }\n\n  public boolean isRXFIFOFull()\n  {\n    return fifo.fstatRxFull();\n  }\n\n  public boolean isRXFIFOEmpty()\n  {\n    return fifo.fstatRxEmpty();\n  }\n\n  public int getRXFIFOLevel()\n  {\n    return fifo.getRXLevel();\n  }\n\n  public boolean isTXFIFOFull()\n  {\n    return fifo.fstatTxFull();\n  }\n\n  public boolean isTXFIFOEmpty()\n  {\n    return fifo.fstatTxEmpty();\n  }\n\n  public int getTXFIFOLevel()\n  {\n    return fifo.getTXLevel();\n  }\n\n  public void setBreakPoints(final int breakPoints,\n                             final int mask, final boolean xor)\n  {\n    status.regBREAKPOINTS =\n      Constants.hwSetBits(status.regBREAKPOINTS, breakPoints, mask, xor);\n  }\n\n  public int getBreakPoints()\n  {\n    return status.regBREAKPOINTS;\n  }\n\n  public void setTracePoints(final int tracePoints,\n                             final int mask, final boolean xor)\n  {\n    status.regTRACEPOINTS =\n      Constants.hwSetBits(status.regTRACEPOINTS, tracePoints, mask, xor);\n  }\n\n  public int getTracePoints()\n  {\n    return status.regTRACEPOINTS;\n  }\n\n  private int encodeJmp(final Instruction.Jmp.Condition condition,\n                        final int address)\n  {\n    if (condition == null) {\n      throw new NullPointerException(\"condition\");\n    }\n    if (address < 0) {\n      throw new IllegalArgumentException(\"address < 0: \" + address);\n    }\n    if (address > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"address > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         address);\n    }\n    final Instruction.Jmp instruction = new Instruction.Jmp();\n    instruction.setCondition(condition);\n    instruction.setAddress(address);\n    return instruction.encode(status.regPINCTRL_SIDESET_COUNT,\n                              status.regEXECCTRL_SIDE_EN);\n  }\n\n  public int getPC()\n  {\n    return status.regADDR;\n  }\n\n  public void setPC(final int value)\n  {\n    if (value < 0) {\n      throw new IllegalArgumentException(\"pc value < 0: \" + value);\n    }\n    if (value > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"pc value > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         value);\n    }\n    // sync: don't change regADDR while PC is updated\n    synchronized(memory.FETCH_LOCK) {\n      status.regADDR = value;\n    }\n  }\n\n  public void setPC(final int value, final int mask, final boolean xor)\n  {\n    final int pc = Constants.hwSetBits(status.regADDR, value, mask, xor);\n    setPC(pc & (MEMORY_SIZE - 1));\n  }\n\n  private void updatePC()\n  {\n    synchronized(memory.FETCH_LOCK) {\n      if (status.regADDR == status.regEXECCTRL_WRAP_TOP) {\n        status.regADDR = status.regEXECCTRL_WRAP_BOTTOM;\n      } else {\n        status.regADDR = (status.regADDR + 1) & (MEMORY_SIZE - 1);\n      }\n      if (((status.regBREAKPOINTS >>> status.regADDR) & 0x1) != 0x0) {\n        masterClock.setMode(MasterClock.Mode.SINGLE_STEP);\n      }\n    }\n  }\n\n  private short fetch()\n  {\n    final int pendingForcedInstruction = status.pendingForcedInstruction;\n    if (pendingForcedInstruction >= 0) {\n      status.pendingForcedInstruction = -1;\n      status.isForcedInstruction = true;\n      status.origin = INSTR_ORIGIN_FORCED;\n      return (short)pendingForcedInstruction;\n    }\n    final int pendingExecdInstruction = status.pendingExecdInstruction;\n    if (pendingExecdInstruction >= 0) {\n      status.pendingExecdInstruction = -1;\n      status.origin = INSTR_ORIGIN_EXECD;\n      return (short)pendingExecdInstruction;\n    }\n    // notify blocking methods that condition may have changed\n    memory.FETCH_LOCK.notifyAll();\n    status.origin = status.regADDR & (MEMORY_SIZE - 1);\n    return memory.get(status.regADDR);\n  }\n\n  public int getOpCode()\n  {\n    /*\n     * TODO / FIXME: getOpCode() works only for instructions created\n     * from a call to decode(), but will return 0 for synthesized\n     * ones.\n     */\n    final Instruction instruction = status.instruction;\n    if (instruction == null) {\n      /*\n       * Trying to access instruction before any decode has been\n       * executed.\n       *\n       * TODO: The RP2040 datasheet is unclear for this situation:\n       * Table 378 \"CTRL Register\" states for CTRL_SM_ENABLE:\n       *\n       * \"When disabled, a state machine will cease executing\n       * instructions, except those written directly to SMx_INSTR by\n       * the system\"\n       *\n       * And Table 395 \"SMx_INSTR Registers\" does *not* provide a\n       * reset value for SMx_INSTR.\n       *\n       * This means, at startup, as long as a state machine has not\n       * yet been enabled and therefore no instruction has been\n       * fetched and decoded so far, the value of register SMx_INSTR\n       * upon read access is undefined.\n       *\n       * For this specific case, we assume that SMx_INSTR will return\n       * a value of 0.\n       */\n      return 0;\n    }\n    return instruction.getOpCode();\n  }\n\n  public int getPendingForcedInstruction()\n  {\n    return status.pendingForcedInstruction;\n  }\n\n  public int getFORCED_INSTR()\n  {\n    if (status.pendingForcedInstruction >= 0) {\n      return 0x00010000 | (status.pendingForcedInstruction & 0x0000ffff);\n    }\n    return 0x0;\n  }\n\n  public void clearPendingForcedInstruction()\n  {\n    status.pendingForcedInstruction = -1;\n  }\n\n  public int getPendingExecdInstruction()\n  {\n    return status.pendingExecdInstruction;\n  }\n\n  public int getEXECD_INSTR()\n  {\n    if (status.pendingExecdInstruction >= 0) {\n      return 0x00010000 | (status.pendingExecdInstruction & 0x0000ffff);\n    }\n    return 0x0;\n  }\n\n  public void clearPendingExecdInstruction()\n  {\n    status.pendingExecdInstruction = -1;\n  }\n\n  public void forceInstruction(final int instruction)\n  {\n    if (instruction < 0) {\n      throw new IllegalArgumentException(\"instruction < 0: \" + instruction);\n    }\n    if (instruction > 65535) {\n      throw new IllegalArgumentException(\"instruction > 65535: \" +\n                                         instruction);\n    }\n    final boolean discarded;\n    synchronized(memory.FETCH_LOCK) {\n      discarded = status.pendingForcedInstruction >= 0;\n      status.pendingForcedInstruction = instruction;\n    }\n    if (discarded) {\n      console.println(\"WARNING: \" +\n                      \"discarding already pending forced instruction\");\n    }\n  }\n\n  public void execInstruction(final int instruction)\n  {\n    synchronized(memory.FETCH_LOCK) {\n      if (status.pendingExecdInstruction >= 0) {\n        throw new InternalError(\"already have pending EXEC instruction\");\n      }\n      if (instruction < 0) {\n        throw new IllegalArgumentException(\"instruction < 0: \" + instruction);\n      }\n      if (instruction > 65535) {\n        throw new IllegalArgumentException(\"instruction > 65535: \" +\n                                           instruction);\n      }\n      status.pendingExecdInstruction = instruction;\n    }\n  }\n\n  public boolean isExecStalled()\n  {\n    synchronized(memory.FETCH_LOCK) {\n      return (status.pendingForcedInstruction >= 0) && isStalled();\n    }\n  }\n\n  public int getINSTR_ORIGIN()\n  {\n    final int origin = status.origin;\n    final int mode = origin < 0 ? origin & 0x3 : INSTR_ORIGIN_MEMORY;\n    final int address = origin < 0 ? 0 : origin & (MEMORY_SIZE - 1);\n    return (mode << 5) | address;\n  }\n\n  /**\n   * Tracking the total delay of the latest instruction with delay in\n   * effect is, strictly speaking, not necessary for the emulation,\n   * but highly useful as additional information for client\n   * applications, e.g. when displaying the percentage of already\n   * passed delay.\n   */\n  public int getTotalDelay()\n  {\n    return status.totalDelay;\n  }\n\n  public int getPendingDelay()\n  {\n    return status.pendingDelay;\n  }\n\n  private void fetchAndDecode() throws Decoder.DecodeException\n  {\n    synchronized(memory.FETCH_LOCK) {\n      final short word = fetch();\n      final Instruction instruction =\n        decoder.decode(word,\n                       status.regPINCTRL_SIDESET_COUNT,\n                       status.regEXECCTRL_SIDE_EN);\n      if (((status.regTRACEPOINTS >>> status.regADDR) & 0x1) != 0x0) {\n        console.println(\"SM\" + num + \": \" + instruction);\n      }\n      status.instruction = instruction;\n    }\n  }\n\n  private void executeInstruction()\n  {\n    if (status.isDelayCycle && !status.isForcedInstruction) {\n      return;\n    }\n    final Instruction instruction = status.instruction;\n    if (instruction == null) {\n      throw new InternalError(\"seems emulator started with falling \" +\n                              \"clock edge:  can not execute instruction \" +\n                              \"before decode\");\n    }\n    status.resultState = instruction.execute(this);\n    if (status.resultState == Instruction.ResultState.COMPLETE) {\n      /*\n       * Sect. 3.4.2.2.: \"Delay cycles … take place after … the program\n       * counter is updated\" (though this specifically refers to JMP\n       * instruction). => Update PC immediately, before executing\n       * delay.\n       */\n      updatePC();\n    }\n    /*\n     * Sect. 3.5.7.: \"Delay cycles are ignored on instructions written\n     * to the INSTR register.\"\n     */\n    if (!status.isForcedInstruction) {\n      if (status.resultState != Instruction.ResultState.STALL) {\n        status.setPendingDelay(instruction.getDelay());\n      }\n    } else {\n      status.isForcedInstruction = false;\n    }\n  }\n\n  private void executeAsyncAutoPull()\n  {\n    /*\n     * Cp. pseudocode sequence for non-\"OUT\" cycles in RP2040\n     * datasheet, Sect. 3.5.4.2. \"Autopull Details\".\n     */\n    final boolean osrCountBeyondThreshold = status.isOsrCountBeyondThreshold();\n    if (osrCountBeyondThreshold) {\n      final boolean txFifoEmpty = fifo.fstatTxEmpty();\n      /*\n       * TODO: Check: Possible race condition between above\n       * fifo.fstatTxEmpty() and below fifo.txPull()?\n       */\n      if (!txFifoEmpty) {\n        status.osrValue = fifo.txPull(false);\n        status.osrShiftCount = 0;\n      }\n    }\n  }\n\n  private void execute()\n  {\n    executeInstruction();\n    /*\n     * TODO: Clarify when the asynchronous fill mechanism is enabled.\n     * On each master clock cycle?  Or only when the state machine is\n     * enabled?  Or even only if clock enabled is true\n     * (i.e. considering clock divider)?  Currently, we check for\n     * clock enable.\n     */\n    if (status.clockEnabled && status.regSHIFTCTRL_AUTOPULL) {\n      if (!(status.instruction instanceof Instruction.Out)) {\n        executeAsyncAutoPull();\n      }\n    }\n    status.flushCollatePins();\n  }\n\n  public boolean isStalled()\n  {\n    return status.resultState == Instruction.ResultState.STALL;\n  }\n\n  public boolean isDelayCycle()\n  {\n    return !status.isForcedInstruction && status.isDelayCycle;\n  }\n\n  @Override\n  public String toString()\n  {\n    return \"SM\" + num + \"{PC=\" + getPC() + \"}\";\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/SwingUtils.java",
    "content": "/*\n * @(#)SwingUtils.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.io.IOException;\nimport java.net.URL;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JMenuItem;\n\npublic class SwingUtils\n{\n  public static ImageIcon createImageIcon(final String iconFileName,\n                                          final String label)\n    throws IOException\n  {\n    final String iconPath = \"/media/\" + iconFileName;\n    final URL iconURL = SwingUtils.class.getResource(iconPath);\n    if (iconURL != null) {\n      return new ImageIcon(iconURL, label);\n    }\n    throw new IOException(\"icon resource not found: \" + iconPath);\n  }\n\n  public static JMenuItem createIconMenuItem(final String iconFileName,\n                                             final String label)\n  {\n    final JMenuItem menuItem = new JMenuItem(label);\n    try {\n      final ImageIcon icon = createImageIcon(iconFileName, label);\n      menuItem.setIcon(icon);\n    } catch (final IOException e) {\n      System.err.println(\"failed creating JMenuItem icon: \" + e.getMessage());\n    }\n    return menuItem;\n  }\n\n  public static JButton createIconButton(final String iconFileName,\n                                         final String label)\n  {\n    final JButton button = new JButton(label);\n    try {\n      final ImageIcon icon = createImageIcon(iconFileName, label);\n      return new JButton(icon);\n    } catch (final IOException e) {\n      System.err.println(\"failed creating JButton icon: \" + e.getMessage());\n      return null;\n    }\n  }\n\n  public static void setPreferredWidthAsMaximum(final Component component)\n  {\n    final Dimension preferredSize = component.getPreferredSize();\n    final Dimension maximumSize =\n      new Dimension(preferredSize.width, Integer.MAX_VALUE);\n    component.setMaximumSize(maximumSize);\n  }\n\n  public static void setPreferredHeightAsMaximum(final Component component)\n  {\n    final Dimension preferredSize = component.getPreferredSize();\n    final Dimension maximumSize =\n      new Dimension(Integer.MAX_VALUE, preferredSize.height);\n    component.setMaximumSize(maximumSize);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/doctool/DocsBuilder.java",
    "content": "/*\n * @(#)DocsBuilder.java 1.00 21/04/18\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.doctool;\n\nimport java.io.FileWriter;\nimport java.io.IOException;\n\n/**\n * Automatically create Sphinx documentation for all Monitor commands,\n * using its integrated help functionality.\n */\npublic class DocsBuilder\n{\n  public static String fill(final char ch, final int length)\n  {\n    final StringBuilder s = new StringBuilder();\n    s.setLength(length);\n    for (int pos = 0; pos < length; pos++) {\n      s.setCharAt(pos, ch);\n    }\n    return s.toString();\n  }\n\n  public static String csvEncode(final String raw)\n  {\n    final StringBuilder s = new StringBuilder();\n    s.append(\"\\\"\");\n    boolean escaped = false;\n    for (final char ch : raw.toCharArray()) {\n      if (escaped) {\n        s.append(ch);\n        escaped = false;\n      } else if (ch == '\\\\') {\n        escaped = true;\n      } else if (ch == '\"') {\n        s.append(\"\\\\\\\"\");\n      } else {\n        s.append(ch);\n      }\n    }\n    s.append(\"\\\"\");\n    return s.toString();\n  }\n\n  /**\n   * Replaces all characters that could have special meaning for\n   * Sphinx.\n   */\n  public static String createIdFromLabel(final String registersSetLabel)\n  {\n    return\n      registersSetLabel\n      .trim()\n      .toLowerCase()\n      .replace(\" \", \"_\")\n      .replace(\"\\\"\", \"\")\n      .replace(\"'\", \"\")\n      .replace(\"`\", \"\")\n      .replace(\":\", \"\");\n  }\n\n  public static final String leadinComment =\n    \".. # WARNING: This sphinx documentation file was automatically%n\" +\n    \".. # created directly from documentation info in the source code.%n\" +\n    \".. # DO NOT CHANGE THIS FILE, since changes will be lost upon%n\" +\n    \".. # its next update.  Instead, change the info in the source code.%n\" +\n    \".. # This file was automatically created on:%n\" +\n    \".. # %s%n\" +\n    \"%n\";\n\n  public static void writeToFile(final String rstFilePath,\n                                 final String docs)\n    throws IOException\n  {\n    try {\n      final FileWriter writer = new FileWriter(rstFilePath);\n      writer.write(docs);\n      writer.close();\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed creating documentation file %s: %s%n\",\n                      rstFilePath, e.getMessage());\n      throw new IOException(message, e);\n    }\n  }\n\n  public static void main(final String argv[])\n  {\n    System.out.println(\"building monitor commands documentation...\");\n    MonitorCommandsDocsBuilder.main(argv);\n    System.out.println(\"building registers documentation...\");\n    RegistersDocsBuilder.main(argv);\n    System.out.println(\"building example scripts documentation...\");\n    ExampleScriptsDocsBuilder.main(argv);\n    System.out.println(\"documentation successfully built\");\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/doctool/ExampleScriptsDocsBuilder.java",
    "content": "/*\n * @(#)ExampleScriptsDocsBuilder.java 1.00 21/06/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.doctool;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.Map;\nimport org.soundpaint.rp2040pio.monitor.ScriptInfo;\n\n/**\n * Automatically create Sphinx documentation for all example scripts,\n * using the script file name and extracting information from the\n * comments in the script file header.\n */\npublic class ExampleScriptsDocsBuilder\n{\n  private ExampleScriptsDocsBuilder()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  private void listExampleScript(final StringBuilder s,\n                                 final ScriptInfo scriptInfo)\n  {\n    final String scriptId = scriptInfo.getScriptId();\n    final String scriptName = scriptInfo.getScriptName();\n    final String description = scriptInfo.getDescription();\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: script; %s%n\", scriptId));\n    s.append(String.format(\"   single: %s script%n\", scriptId));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. _%s-example-script:%n\", scriptId));\n    s.append(String.format(\"%n\"));\n    final String scriptTitle =\n      String.format(\"%s (``%s``)\", scriptName, scriptId);\n    s.append(String.format(scriptTitle + \"%n\"));\n    s.append(String.format(\"%s%n\", \"^\".repeat(scriptTitle.length())));\n    s.append(String.format(\"%n\"));\n    s.append(description);\n    s.append(String.format(\"%n\"));\n  }\n\n  private void listExampleScriptGroup(final StringBuilder s,\n                                      final Map<String, ScriptInfo>\n                                      scriptsGroupInfo,\n                                      final String groupName)\n  {\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: script group; %s%n\", groupName));\n    s.append(String.format(\"   single: %s script group%n\", groupName));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. _%s-example-script-group:%n\", groupName));\n    s.append(String.format(\"%n\"));\n    final String groupTitle = String.format(\"%s\", groupName);\n    s.append(String.format(groupTitle + \"%n\"));\n    s.append(String.format(\"%s%n\", \"-\".repeat(groupTitle.length())));\n    s.append(String.format(\"%n\"));\n    for (final String scriptId : scriptsGroupInfo.keySet()) {\n      final ScriptInfo scriptInfo = scriptsGroupInfo.get(scriptId);\n      listExampleScript(s, scriptInfo);\n    }\n  }\n\n  private void listExampleScripts(final Map<String, Map<String, ScriptInfo>>\n                                  scriptsInfo,\n                                  final StringBuilder s)\n    throws IOException\n  {\n    Map<String, ScriptInfo> defaultScriptsGroupInfo = null;\n    for (final String groupName : scriptsInfo.keySet()) {\n      final Map<String, ScriptInfo> scriptsGroupInfo =\n        scriptsInfo.get(groupName);\n      if (groupName != ScriptInfo.DEFAULT_GROUP_NAME) {\n        listExampleScriptGroup(s, scriptsGroupInfo, groupName);\n      } else {\n        // defer default group to end\n        defaultScriptsGroupInfo = scriptsGroupInfo;\n      }\n    }\n    if (defaultScriptsGroupInfo != null) {\n      listExampleScriptGroup(s, defaultScriptsGroupInfo,\n                             ScriptInfo.DEFAULT_GROUP_NAME);\n    } else {\n      throw new IOException(\"default script group not found\");\n    }\n  }\n\n  private String createDocs() throws IOException\n  {\n    final StringBuilder s = new StringBuilder();\n    s.append(String.format(DocsBuilder.leadinComment, Instant.now()));\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: scripts; examples%n\"));\n    s.append(String.format(\"   single: example scripts%n\"));\n    s.append(String.format(\"   single: reference; example scripts%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. _example-scripts-reference:%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"Example Scripts Reference%n\"));\n    s.append(String.format(\"=========================%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"The RP2040 PIO Emulator and client%n\"));\n    s.append(String.format(\"applications are bundled with a set of%n\"));\n    s.append(String.format(\"built-in example scripts.  These scripts%n\"));\n    s.append(String.format(\"loosely follow some of the PIO code examples%n\"));\n    s.append(String.format(\"in the RP2040 datasheet, but are adapted to%n\"));\n    s.append(String.format(\"run as Monitor scripts, using the Monitor%n\"));\n    s.append(String.format(\"specific syntax of commands.%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"In the Monitor application, the set of these\"));\n    s.append(String.format(\"built-in example scripts can be listed with%n\"));\n    s.append(String.format(\"the Monitor command ``script --list``.%n\"));\n    s.append(String.format(\"%n\"));\n    final Map<String, Map<String, ScriptInfo>> scriptsInfo =\n      ScriptInfo.createScriptsInfo();\n    listExampleScripts(scriptsInfo, s);\n    return s.toString();\n  }\n\n  public ExampleScriptsDocsBuilder(final String rstFilePath)\n    throws IOException\n  {\n    final String docs = createDocs();\n    DocsBuilder.writeToFile(rstFilePath, docs);\n  }\n\n  public static void main(final String argv[])\n  {\n    try {\n      new ExampleScriptsDocsBuilder(\"example-scripts.rst\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed creating example scripts documentation: %s%n\",\n                      e.getMessage());\n      System.err.printf(message);\n      System.exit(-1);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/doctool/MonitorCommandsDocsBuilder.java",
    "content": "/*\n * @(#)MonitorCommandsDocsBuilder.java 1.00 21/04/18\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.doctool;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Emulator;\nimport org.soundpaint.rp2040pio.LocalAddressSpace;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.CommandRegistry;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Automatically create Sphinx documentation for all Monitor commands,\n * using its integrated help functionality.\n */\npublic class MonitorCommandsDocsBuilder\n{\n  private MonitorCommandsDocsBuilder()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  private String createCommandDocs(final Command command)\n  {\n    final StringBuilder s = new StringBuilder();\n    final String commandName = command.getFullName();\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: monitor command; %s%n\", commandName));\n    s.append(String.format(\"   single: %s%n\", commandName));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. _%s-command-label:%n\", commandName));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(commandName + \"%n\"));\n    s.append(String.format(DocsBuilder.fill('-', commandName.length()) + \"%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"**Usage**%n\"));\n    s.append(String.format(\"^^^^^^^^^%n\"));\n    s.append(String.format(\"%n%s%n%n\", command.getUsage()));\n    s.append(String.format(\"**Description**%n\"));\n    s.append(String.format(\"^^^^^^^^^^^^^^^%n\"));\n    s.append(String.format(\"%n%s%n%n\", command.getSingleLineDescription()));\n    final String optionsHelp = command.getOptionsHelp();\n    if (!optionsHelp.isEmpty()) {\n      s.append(String.format(\"**Options**%n\"));\n      s.append(String.format(\"^^^^^^^^^^^%n\"));\n      s.append(String.format(\"%n%s%n\", optionsHelp));\n    }\n    final String notes = command.getNotes();\n    if ((notes != null) && !notes.isEmpty()) {\n      s.append(String.format(\"**Notes**%n\"));\n      s.append(String.format(\"^^^^^^^^^%n\"));\n      s.append(String.format(\"%n\" + notes + \"%n%n\"));\n    }\n    s.append(String.format(\":ref:`Back to Overview <commands-overview>`%n\"));\n    s.append(String.format(\"%n\"));\n    return s.toString();\n  }\n\n  private String createCommandsOverview(final CommandRegistry commandRegistry)\n  {\n    final StringBuilder s = new StringBuilder();\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: monitor; commands overview%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. _commands-overview:%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"Overview%n\"));\n    s.append(String.format(\"--------%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"The monitor supports all of the commands%n\"));\n    s.append(String.format(\"listed below.%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. csv-table::%n\"));\n    s.append(String.format(\"   :header: Command, Short Description%n\"));\n    s.append(String.format(\"   :widths: 20, 80%n\"));\n    s.append(String.format(\"%n\"));\n    for (final Command command : commandRegistry) {\n      final String commandName = command.getFullName();\n      final String commandRef =\n        String.format(\":ref:`%s <%s-command-label>`\", commandName, commandName);\n      final String commandDescription = command.getSingleLineDescription();\n      s.append(String.format(\"   %s,%s%n\",\n                             DocsBuilder.csvEncode(commandRef),\n                             DocsBuilder.csvEncode(commandDescription)));\n    }\n    s.append(String.format(\"%n\"));\n    return s.toString();\n  }\n\n  private String createDocs() throws IOException\n  {\n    final StringBuilder s = new StringBuilder();\n    s.append(String.format(DocsBuilder.leadinComment, Instant.now()));\n    s.append(String.format(\"Monitor & Control Program Commands Reference%n\"));\n    s.append(String.format(\"============================================%n\"));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\"The *Monitor & Control Program*, or in short,%n\"));\n    s.append(String.format(\"just *monitor*, features a set of built-in%n\"));\n    s.append(String.format(\"commands with integrated, self-documenting%n\"));\n    s.append(String.format(\"help.  The following reference documentation%n\"));\n    s.append(String.format(\"has been compiled from these sources.%n\"));\n    s.append(String.format(\"%n\"));\n    final PrintStream console = System.out;\n    final Emulator emulator = new Emulator(console);\n    final AddressSpace memory = new LocalAddressSpace(emulator);\n    final BufferedReader in =\n      new BufferedReader(new InputStreamReader(System.in));\n    final SDK sdk = new SDK(console, memory);\n    final CommandRegistry commandRegistry =\n      new CommandRegistry(console, in, sdk, null);\n    s.append(createCommandsOverview(commandRegistry));\n    for (final Command command : commandRegistry) {\n      s.append(createCommandDocs(command));\n    }\n    emulator.terminate();\n    return s.toString();\n  }\n\n  public MonitorCommandsDocsBuilder(final String rstFilePath)\n    throws IOException\n  {\n    final String docs = createDocs();\n    DocsBuilder.writeToFile(rstFilePath, docs);\n  }\n\n  public static void main(final String argv[])\n  {\n    try {\n      new MonitorCommandsDocsBuilder(\"monitor-commands.rst\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed creating monitor commands documentation: %s%n\",\n                      e.getMessage());\n      System.err.printf(message);\n      System.exit(-1);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/doctool/RegistersDocs.java",
    "content": "/*\n * @(#)RegistersDocs.java 1.00 21/04/15\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.doctool;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport org.soundpaint.rp2040pio.Constants;\n\n/**\n * Documentation interface for automatic creation of registers\n * documentation from annotations in the code.\n */\npublic interface RegistersDocs<T>\n{\n  public enum BitsType\n  {\n    RESERVED(\"―\", \"n/a\"),\n    UNUSED(\"―\", \"unused\"),\n    SC(\"SC\", \"???\"),\n    WC(\"WC\", \"write 1 to clear\"),\n    RW(\"RW\", \"read/write\"),\n    RO(\"RO\", \"read-only\"),\n    WO(\"WO\", \"write-only\"),\n    RF(\"RF\", \"read to trigger function\"),\n    WF(\"WF\", \"wrtite to trigger function\");\n\n    private final String id;\n    private final String description;\n\n    private BitsType(final String id, final String description)\n    {\n      this.id = id;\n      this.description = description;\n    }\n\n    public boolean isRelevant()\n    {\n      return (this != RESERVED) && (this != UNUSED);\n    }\n\n    public String getId() { return id; }\n\n    public String getDescription() { return description; }\n  }\n\n  public static class BitsRange\n  {\n    private final int msb;\n    private final int lsb;\n\n    private BitsRange()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    public BitsRange(final int msb, final int lsb)\n    {\n      if (msb < 0) {\n        throw new IllegalArgumentException(\"msb < 0: \" + msb);\n      }\n      if (msb > 31) {\n        throw new IllegalArgumentException(\"msb > 31: \" + msb);\n      }\n      if (lsb < 0) {\n        throw new IllegalArgumentException(\"lsb < 0: \" + lsb);\n      }\n      if (lsb > 31) {\n        throw new IllegalArgumentException(\"lsb > 31: \" + lsb);\n      }\n      if (lsb > msb) {\n        throw new IllegalArgumentException(\"lsb > msb: \" + lsb + \" > \" + msb);\n      }\n      this.msb = msb;\n      this.lsb = lsb;\n    }\n\n    public int getMsb() { return msb; }\n\n    public int getLsb() { return lsb; }\n\n    public String toShortString()\n    {\n      return\n        msb == lsb ?\n        String.format(\"%d\", msb) :\n        String.format(\"%d:%d\", msb, lsb);\n    }\n\n    @Override\n    public String toString()\n    {\n      return\n        msb == lsb ?\n        String.format(\"bit %d\", msb) :\n        String.format(\"bits [%d:%d]\", msb, lsb);\n    }\n  }\n\n  public static class BitsInfo\n  {\n    private final String name;\n    private final BitsRange bitsRange;\n    private final String description;\n    private final BitsType type;\n    private final Integer resetValue;\n\n    private BitsInfo()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    public BitsInfo(final String name,\n                    final int msb,\n                    final int lsb,\n                    final String description,\n                    final BitsType type,\n                    final Integer resetValue)\n    {\n      this(name, new BitsRange(msb, lsb), description, type, resetValue);\n    }\n\n    public BitsInfo(final String name,\n                    final BitsRange bitsRange,\n                    final String description,\n                    final BitsType type,\n                    final Integer resetValue)\n    {\n      if (bitsRange == null) {\n        throw new NullPointerException(\"bitsRange\");\n      }\n      if (type == null) {\n        throw new NullPointerException(\"type\");\n      }\n      if (resetValue != null) {\n        final int msb = bitsRange.msb;\n        final int lsb = bitsRange.lsb;\n        final long maxResetValue = ((long)0x1 << (msb - lsb + 1)) - 1;\n        final long resetValueAsLong = 0x00000000FFFFFFFFL & (long)resetValue;\n        if (resetValueAsLong > maxResetValue) {\n          final String message =\n            String.format(\"%s [%d:%d]: \" +\n                          \"resetValueAsLong > maxResetValue: %d > %d\",\n                          name, msb, lsb, resetValueAsLong, maxResetValue);\n          throw new IllegalArgumentException(message);\n        }\n      }\n      this.name = name;\n      this.bitsRange = bitsRange;\n      this.description = description;\n      this.type = type;\n      this.resetValue = resetValue;\n    }\n\n    public int getMsb() { return bitsRange.msb; }\n    public int getLsb() { return bitsRange.lsb; }\n    public String getName() { return name; }\n    public BitsRange getBitsRange() { return bitsRange; }\n    public String getDescription() { return description; }\n    public BitsType getType() { return type; }\n    public Integer getResetValue() { return resetValue; }\n\n    private static String renderName(final String name)\n    {\n      return name != null ? name + \": \" : \"\";\n    }\n\n    private static String renderName(final String name,\n                                     final String defaultName)\n    {\n      return renderName(name != null ? name : defaultName);\n    }\n\n    private String renderBitsRange()\n    {\n      return bitsRange.toString();\n    }\n\n    private String renderDescription()\n    {\n      return description != null ? \" \" + description : \"\";\n    }\n\n    private String renderType()\n    {\n      return String.format(\"Type: %s\", type);\n    }\n\n    private String renderResetValue()\n    {\n      if ((type == BitsType.UNUSED) || (type == BitsType.RESERVED))\n        return \"\";\n      return String.format(\", Reset Value: %s\",\n                           resetValue != null ? resetValue : \"―\");\n    }\n\n    public String toString(final String defaultName)\n    {\n      return String.format(\"%s %s%s, %s %s\",\n                           renderName(name, defaultName),\n                           renderBitsRange(),\n                           renderDescription(),\n                           renderType(),\n                           renderResetValue());\n    }\n\n    @Override\n    public String toString()\n    {\n      return toString(null);\n    }\n  }\n\n  public static class RegisterDetails\n  {\n    public static final int SM_UNDEFINED = -1;\n\n    private String info;\n    private int smNum;\n    private List<BitsInfo> bitsInfos;\n\n    private static void checkSmNum(final int smNum)\n    {\n      if (smNum != SM_UNDEFINED) {\n        Constants.checkSmNum(smNum);\n      }\n    }\n\n    private RegisterDetails()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    public RegisterDetails(final String info, final BitsInfo[] bitsInfos)\n    {\n      this(info, Arrays.asList(bitsInfos));\n    }\n\n    public RegisterDetails(final String info, final int smNum,\n                           final BitsInfo[] bitsInfos)\n    {\n      this(info, smNum, Arrays.asList(bitsInfos));\n    }\n\n    public RegisterDetails(final String info, final List<BitsInfo> bitsInfos)\n    {\n      this(info, SM_UNDEFINED, bitsInfos);\n    }\n\n    public RegisterDetails(final String info, final int smNum,\n                           final List<BitsInfo> bitsInfos)\n    {\n      Objects.requireNonNull(info);\n      Objects.requireNonNull(bitsInfos);\n      checkSmNum(smNum);\n      this.info = info;\n      this.smNum = smNum;\n      this.bitsInfos = new ArrayList<BitsInfo>();\n      this.bitsInfos.addAll(bitsInfos);\n    }\n\n    public String getInfo() { return info; }\n\n    public int getSmNum() { return smNum; }\n\n    public Iterable<BitsInfo> getBitsInfos()\n    {\n      final List<BitsInfo> bitsInfosCopy = new ArrayList<BitsInfo>();\n      bitsInfosCopy.addAll(bitsInfos);\n      return bitsInfosCopy;\n    }\n\n    public RegisterDetails createCopyForDifferentSm(final int smNum)\n    {\n      return new RegisterDetails(info, smNum, bitsInfos);\n    }\n  }\n\n  String getInfo();\n  RegisterDetails getRegisterDetails();\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/doctool/RegistersDocsBuilder.java",
    "content": "/*\n * @(#)RegistersDocsBuilder.java 1.00 21/04/15\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.doctool;\n\nimport java.io.IOException;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.EnumSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\n\n/**\n * Automatically create registers documentation from annotations in\n * the code.\n */\npublic class RegistersDocsBuilder<T extends Enum<T> & RegistersDocs<T>>\n{\n  private static String formatBitsRange(final RegistersDocs.BitsInfo bitsInfo)\n  {\n    final int msb = bitsInfo.getMsb();\n    final int lsb = bitsInfo.getLsb();\n    if (msb == lsb)\n      return String.format(\"%d\", msb);\n    return String.format(\"%d:%d\", msb, lsb);\n  }\n\n  private static String formatName(final RegistersDocs.BitsInfo bitsInfo)\n  {\n    final String name = bitsInfo.getName();\n    if (bitsInfo.getType() == RegistersDocs.BitsType.RESERVED)\n      return \"Reserved.\";\n    if (bitsInfo.getType() == RegistersDocs.BitsType.UNUSED)\n      return \"Unused.\";\n    if (name == null)\n      return \"―\";\n    return String.format(name);\n  }\n\n  private static String\n    formatTableDescription(final String defaultDescription,\n                           final Iterable<RegistersDocs.BitsInfo> bitsInfos)\n  {\n    boolean hasBitsDescription = false;\n    for (final RegistersDocs.BitsInfo bitsInfo : bitsInfos) {\n      if (bitsInfo.getDescription() != null) {\n        hasBitsDescription = true;\n        break;\n      }\n    }\n    if (hasBitsDescription) {\n      if (defaultDescription != null) {\n        return String.format(defaultDescription);\n      } else {\n        return null;\n      }\n    } else {\n      return null;\n    }\n  }\n\n  private static String formatDescription(final RegistersDocs.BitsInfo bitsInfo,\n                                          final String defaultDescription)\n  {\n    final RegistersDocs.BitsType type = bitsInfo.getType();\n    final String bitsInfoDescription = bitsInfo.getDescription();\n    final String description;\n    if (bitsInfoDescription != null) {\n      description = bitsInfoDescription;\n    } else if (type == RegistersDocs.BitsType.RESERVED) {\n      description = \"―\";\n    } else if (type == RegistersDocs.BitsType.UNUSED) {\n      description = \"―\";\n    } else if (defaultDescription != null) {\n      description = defaultDescription;\n    } else {\n      description = \"―\";\n    }\n    return DocsBuilder.csvEncode(description.replace(\"%n\", \" \"));\n  }\n\n  private static String formatType(final RegistersDocs.BitsInfo bitsInfo)\n  {\n    final RegistersDocs.BitsType type = bitsInfo.getType();\n    return String.format(\"%s\",\n                         (type == RegistersDocs.BitsType.RESERVED) ||\n                         (type == RegistersDocs.BitsType.UNUSED) ?\n                         \"―\" : type);\n  }\n\n  private static String formatResetValue(final RegistersDocs.BitsInfo bitsInfo)\n  {\n    final Integer resetValue = bitsInfo.getResetValue();\n    if (resetValue == null)\n      return \"―\";\n    return String.format(\"%d\", resetValue);\n  }\n\n  private String createDetailTableLabels(final List<T> regsList)\n  {\n    final StringBuilder labels = new StringBuilder();\n    for (final T reg : regsList) {\n      labels.append(String.format(\".. _%s-details-label:%n\", reg.toString()));\n    }\n    labels.append(String.format(\"%n\"));\n    return labels.toString();\n  }\n\n  private String createDetailTableIndices(final List<T> regsList)\n  {\n    final StringBuilder labels = new StringBuilder();\n    for (final T reg : regsList) {\n      labels.append(String.format(\".. index::%n\"));\n      labels.append(String.format(\"   single: register details; %s%n\",\n                                  reg.toString()));\n      labels.append(String.format(\"   single: %s%n%n\", reg.toString()));\n    }\n    return labels.toString();\n  }\n\n  private String formatRegNames(final List<T> regsList)\n  {\n    final StringBuilder regNames = new StringBuilder();\n    boolean multiple = false;\n    for (final T reg : regsList) {\n      if (regNames.length() > 0) {\n        regNames.append(\", \");\n        multiple = true;\n      }\n      regNames.append(reg.toString());\n    }\n    regNames.append(multiple ? \" Registers\" : \" Register\");\n    return String.format(\"%s\", regNames);\n  }\n\n  private String formatOffsets(final List<T> regsList)\n  {\n    final StringBuilder offsets = new StringBuilder();\n    boolean haveMultipleRegs = false;\n    for (final T reg : regsList) {\n      if (offsets.length() > 0) {\n        offsets.append(\", \");\n        haveMultipleRegs = true;\n      }\n      offsets.append(String.format(\"0x%03x\", reg.ordinal() << 2));\n    }\n    return String.format(\"**%s:** %s\",\n                         haveMultipleRegs ? \"Offsets\" : \"Offset\", offsets);\n  }\n\n  private String createDetailTable(final String registersSetLabel,\n                                   final RegistersDocs.RegisterDetails\n                                   registerDetails,\n                                   final List<T> regsList)\n  {\n    final StringBuilder s = new StringBuilder();\n    s.append(createDetailTableLabels(regsList));\n    s.append(createDetailTableIndices(regsList));\n    final String regNames = formatRegNames(regsList);\n    final String registersSetId =\n      DocsBuilder.createIdFromLabel(registersSetLabel);\n    final String headLine =\n      String.format(\":ref:`%s <section-top_%s>`: %s\",\n                    registersSetLabel, registersSetId, regNames);\n    s.append(String.format(\"%s%n\", headLine));\n    s.append(String.format(\"%s%n\", DocsBuilder.fill('-', headLine.length())));\n    s.append(String.format(\"%n\"));\n    final String offsets = formatOffsets(regsList);\n    s.append(String.format(\"%s%n\", offsets));\n    s.append(String.format(\"%n\"));\n    final String defaultDescription = registerDetails.getInfo();\n    final String tableDescription =\n      formatTableDescription(defaultDescription,\n                             registerDetails.getBitsInfos());\n    if (tableDescription != null) {\n      s.append(String.format(\"**Description**%n%n%s%n\", tableDescription));\n      s.append(String.format(\"%n\"));\n    }\n    s.append(String.format(\".. csv-table::%n\"));\n    s.append(String.format(\"   :header: Bits, Name, Description, Type, Reset%n\"));\n    s.append(String.format(\"   :widths: 8, 20, 40, 8, 20%n\"));\n    s.append(String.format(\"%n\"));\n    for (final T.BitsInfo bitsInfo : registerDetails.getBitsInfos()) {\n      final String bitsRange = formatBitsRange(bitsInfo);\n      final String name = formatName(bitsInfo);\n      final String description =\n        formatDescription(bitsInfo, defaultDescription);\n      final String type = formatType(bitsInfo);\n      final String reset = formatResetValue(bitsInfo);\n      s.append(String.format(\"   %s, %s, %s, %s, %s%n\",\n                             bitsRange, name, description, type, reset));\n    }\n    s.append(String.format(\"%n\"));\n    return s.toString();\n  }\n\n  private String createDetailTableRef(final T reg)\n  {\n    final String regName = reg.toString();\n    return String.format(\":ref:`%s <%s-details-label>`\", regName, regName);\n  }\n\n  private String createOverviewTable(final String registersSetLabel,\n                                     final String registersSetDescription,\n                                     final EnumSet<T> regs,\n                                     final Map<T.RegisterDetails, List<T>>\n                                     registerDetails2regs)\n  {\n    final StringBuilder s = new StringBuilder();\n    final String registersSetId =\n      DocsBuilder.createIdFromLabel(registersSetLabel);\n    s.append(String.format(\".. _section-top_%s:%n\", registersSetId));\n    s.append(String.format(\"%n\"));\n    s.append(String.format(\".. index::%n\"));\n    s.append(String.format(\"   single: %s%n\", registersSetLabel));\n    s.append(String.format(\"   single: registers set; %s%n\", registersSetLabel));\n    s.append(String.format(\"%n\"));\n    final String sectionHeader = String.format(\"%s\", registersSetLabel);\n    s.append(String.format(\"%s%n\", sectionHeader));\n    s.append(String.format(\"%s%n\",\n                           DocsBuilder.fill('=', sectionHeader.length())));\n    s.append(String.format(\"%n\"));\n    final String overviewTableHeader = \"List of Registers\";\n    s.append(String.format(\"%s%n\", overviewTableHeader));\n    s.append(String.format(\"%s%n\",\n                           DocsBuilder.fill('-', overviewTableHeader.length())));\n    s.append(String.format(\"%n\"));\n    if (registersSetDescription != null) {\n      s.append(String.format(registersSetDescription));\n      s.append(String.format(\"%n\"));\n      s.append(String.format(\"%n\"));\n    }\n    s.append(String.format(\".. csv-table::%n\"));\n    s.append(String.format(\"   :header: Offset, Name, Info%n\"));\n    s.append(String.format(\"   :widths: 8, 20, 40%n\"));\n    s.append(String.format(\"%n\"));\n    int address = 0x000;\n    for (final T reg : regs) {\n      final T.RegisterDetails registerDetails = reg.getRegisterDetails();\n      final List<T> regsList;\n      if (registerDetails2regs.containsKey(registerDetails)) {\n        regsList = registerDetails2regs.get(registerDetails);\n      } else {\n        regsList = new ArrayList<T>();\n        registerDetails2regs.put(registerDetails, regsList);\n      }\n      regsList.add(reg);\n      s.append(String.format(\"   0x%03x, %s, %s%n\",\n                             address, createDetailTableRef(reg),\n                             DocsBuilder.csvEncode(reg.getInfo().\n                                                   replace(\"%n\", \" \"))));\n      address += 0x004;\n    }\n    s.append(String.format(\"%n\"));\n    return s.toString();\n  }\n\n  private String createDocs(final String registersSetLabel,\n                            final String registersSetDescription,\n                            final EnumSet<T> regs)\n  {\n    final Map<T.RegisterDetails, List<T>> registerDetails2regs\n      = new LinkedHashMap<T.RegisterDetails, List<T>>();\n    final StringBuilder s = new StringBuilder();\n    s.append(String.format(DocsBuilder.leadinComment, Instant.now()));\n    s.append(createOverviewTable(registersSetLabel, registersSetDescription,\n                                 regs, registerDetails2regs));\n    for (final T.RegisterDetails registerDetails :\n           registerDetails2regs.keySet()) {\n      final List<T> regsList = registerDetails2regs.get(registerDetails);\n      s.append(createDetailTable(registersSetLabel, registerDetails, regsList));\n    }\n    return s.toString();\n  }\n\n  private RegistersDocsBuilder()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public RegistersDocsBuilder(final Class<T> regsClass,\n                              final String registerSetLabel,\n                              final String registerSetDescription,\n                              final String rstFilePath)\n    throws IOException\n  {\n    final String docs =\n      createDocs(registerSetLabel, registerSetDescription,\n                 EnumSet.allOf(regsClass));\n    DocsBuilder.writeToFile(rstFilePath, docs);\n  }\n\n  public static void main(final String argv[])\n  {\n    try {\n      new RegistersDocsBuilder<PicoEmuRegisters.Regs>\n        (PicoEmuRegisters.Regs.class,\n         PicoEmuRegisters.Regs.getRegisterSetLabel(),\n         PicoEmuRegisters.Regs.getRegisterSetDescription(),\n         \"pico-emu-registers.rst\");\n      new RegistersDocsBuilder<PIOEmuRegisters.Regs>\n        (PIOEmuRegisters.Regs.class,\n         PIOEmuRegisters.Regs.getRegisterSetLabel(),\n         PIOEmuRegisters.Regs.getRegisterSetDescription(),\n         \"pio-emu-registers.rst\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed creating registers documentation: %s%n\",\n                      e.getMessage());\n      System.err.printf(message);\n      System.exit(-1);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/Command.java",
    "content": "/*\n * @(#)Command.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport org.soundpaint.rp2040pio.CmdOptions;\n\n/**\n * Common abstract super class for all monitor commands.\n */\npublic abstract class Command\n{\n  private static final List<CmdOptions.OptionDeclaration<?>>\n    EMPTY_OPTION_DECLARATIONS =\n    new ArrayList<CmdOptions.OptionDeclaration<?>>();\n\n  protected static final CmdOptions.FlagOptionDeclaration optHelp =\n    CmdOptions.createFlagOption(false, 'h', \"help\", CmdOptions.Flag.OFF,\n                                \"display this help text and exit\");\n  protected static final String commandHint =\n    \"For a list of available commands, enter 'help'.%n\" +\n    \"When invoked with the \\\"-s\\\" script execution option, the specified%n\" +\n    \"script is executed, and the monitor exits immediately after execution.\";\n  protected static final String helpNotes =\n    \"For detail help of a command, enter: <command> -h.%n\" +\n    \"Commands may be abbreviated as long as unambiguity is preserved.\";\n  protected static final String panicNotes =\n    \"The system may be now in a corrupted state.%n\" +\n    \"You may consider to fully reset the emulator with%n\" +\n    \"the \\\"reset\\\" commmand, if unexpected behavior shows up.\";\n\n  protected final PrintStream console;\n  private final String fullName;\n  private final String singleLineDescription;\n  private final String notes;\n  private final List<CmdOptions.OptionDeclaration<?>> optionDeclarations;\n  private final CmdOptions options;\n\n  private Command()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription)\n  {\n    this(console, fullName, singleLineDescription, null,\n         EMPTY_OPTION_DECLARATIONS);\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription, final String notes)\n  {\n    this(console, fullName, singleLineDescription, notes,\n         EMPTY_OPTION_DECLARATIONS);\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription,\n                 final CmdOptions.OptionDeclaration<?>[] optionDeclarations)\n  {\n    this(console, fullName, singleLineDescription,\n         Arrays.asList(optionDeclarations));\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription, final String notes,\n                 final CmdOptions.OptionDeclaration<?>[] optionDeclarations)\n  {\n    this(console, fullName, singleLineDescription, notes,\n         Arrays.asList(optionDeclarations));\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription,\n                 final List<CmdOptions.OptionDeclaration<?>> optionDeclarations)\n  {\n    this(console, fullName, singleLineDescription, null, optionDeclarations);\n  }\n\n  public Command(final PrintStream console, final String fullName,\n                 final String singleLineDescription, final String notes,\n                 final List<CmdOptions.OptionDeclaration<?>> optionDeclarations)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    if (fullName == null) {\n      throw new NullPointerException(\"fullName\");\n    }\n    if (fullName.length() == 0) {\n      throw new IllegalArgumentException(\"empty fullName\");\n    }\n    if (singleLineDescription == null) {\n      throw new NullPointerException(\"singleLineDescription\");\n    }\n    if (optionDeclarations == null) {\n      throw new NullPointerException(\"optionDeclarations\");\n    }\n    for (final CmdOptions.OptionDeclaration<?> decl : optionDeclarations) {\n      if (decl  == null) {\n        throw new NullPointerException(\"null value in optionDeclarations\");\n      }\n    }\n    this.console = console;\n    this.fullName = fullName;\n    this.singleLineDescription = singleLineDescription;\n    this.notes = notes;\n    this.optionDeclarations = new ArrayList<CmdOptions.OptionDeclaration<?>>();\n    this.optionDeclarations.addAll(optionDeclarations);\n    this.optionDeclarations.add(optHelp);\n    try {\n      options =\n        new CmdOptions(fullName, singleLineDescription, notes,\n                       this.optionDeclarations);\n    } catch (final CmdOptions.ParseException e) {\n      throw new InternalError(\"unexpected command configuration error\");\n    }\n  }\n\n  public String getFullName()\n  {\n    return fullName;\n  }\n\n  public String getSingleLineDescription()\n  {\n    return singleLineDescription;\n  }\n\n  public String getNotes()\n  {\n    return notes;\n  }\n\n  public String getUsage()\n  {\n    return options.getUsage();\n  }\n\n  public String getOptionsHelp()\n  {\n    return options.getOptionsHelp();\n  }\n\n  public Iterator<CmdOptions.OptionDeclaration<?>>\n    getOptionDeclarationsIterator()\n  {\n    return optionDeclarations.iterator();\n  }\n\n  /**\n   * Returns true if no error occurred.\n   */\n  public void parse(final String[] argv) throws CmdOptions.ParseException\n  {\n    options.parse(argv, true);\n    checkValidity(options);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  public boolean execute() throws IOException\n  {\n    if (options.getValue(optHelp) == CmdOptions.Flag.ON) {\n      console.println(options.getFullInfo());\n      return false;\n    }\n    return execute(options);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  protected abstract boolean execute(final CmdOptions options)\n    throws IOException;\n\n  /**\n   * Subclasses that implement this abstract class should override\n   * this method and throw a CmdOptions.ParseException, if they\n   * require additional constraints that the supplied options do not\n   * fulfill.  An example for a possible constraint is a combination\n   * of two options that both are valid if specified alone, but that\n   * can not be specified together.  Another example is a value\n   * constraint, e.g. if a string parameter must be from a fixed set\n   * of strings or may not contain specific special characters.\n   */\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    /**\n     * Example code for illegal combination of silent flag and verbose\n     * flag:\n     *\n     *    if ((options.getValue(optSilent) == CmdOptions.Flag.ON) &amp;&amp;\n     *    (options.getValue(optVerbose) == CmdOptions.Flag.ON)) {\n     *      throw new CmdOptions.\n     *        ParseException(\"at most one of 'silent', 'verbose' allowed\");\n     *    }\n     */\n  }\n\n  public String getHelp()\n  {\n    return options.getFullInfo();\n  }\n\n  @Override\n  public String toString()\n  {\n    return \"command \\\"\" + fullName + \"\\\"\";\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/CommandRegistry.java",
    "content": "/*\n * @(#)CommandRegistry.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.TreeSet;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.ParseException;\nimport org.soundpaint.rp2040pio.monitor.commands.BreakPoints;\nimport org.soundpaint.rp2040pio.monitor.commands.Clear;\nimport org.soundpaint.rp2040pio.monitor.commands.Clock;\nimport org.soundpaint.rp2040pio.monitor.commands.Enter;\nimport org.soundpaint.rp2040pio.monitor.commands.Execute;\nimport org.soundpaint.rp2040pio.monitor.commands.Fifo;\nimport org.soundpaint.rp2040pio.monitor.commands.Gpio;\nimport org.soundpaint.rp2040pio.monitor.commands.Help;\nimport org.soundpaint.rp2040pio.monitor.commands.Interrupt;\nimport org.soundpaint.rp2040pio.monitor.commands.Label;\nimport org.soundpaint.rp2040pio.monitor.commands.Load;\nimport org.soundpaint.rp2040pio.monitor.commands.Quit;\nimport org.soundpaint.rp2040pio.monitor.commands.PinCtrl;\nimport org.soundpaint.rp2040pio.monitor.commands.Read;\nimport org.soundpaint.rp2040pio.monitor.commands.Registers;\nimport org.soundpaint.rp2040pio.monitor.commands.Reset;\nimport org.soundpaint.rp2040pio.monitor.commands.Save;\nimport org.soundpaint.rp2040pio.monitor.commands.Script;\nimport org.soundpaint.rp2040pio.monitor.commands.SideSet;\nimport org.soundpaint.rp2040pio.monitor.commands.Sm;\nimport org.soundpaint.rp2040pio.monitor.commands.Trace;\nimport org.soundpaint.rp2040pio.monitor.commands.Unassemble;\nimport org.soundpaint.rp2040pio.monitor.commands.Unload;\nimport org.soundpaint.rp2040pio.monitor.commands.Version;\nimport org.soundpaint.rp2040pio.monitor.commands.Wait;\nimport org.soundpaint.rp2040pio.monitor.commands.Wrap;\nimport org.soundpaint.rp2040pio.monitor.commands.Write;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Used for command dispatching.\n */\npublic class CommandRegistry implements Iterable<Command>\n{\n  private final PrintStream console;\n  private final Set<Command> commands;\n  private final HashMap<String, List<Command>> token2commands;\n  private final Command quit;\n\n  private CommandRegistry()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public CommandRegistry(final PrintStream console,\n                         final BufferedReader in,\n                         final SDK sdk,\n                         final String appFullName)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    commands =\n      new TreeSet<Command>((cmd1, cmd2) ->\n                           cmd1.getFullName().compareTo(cmd2.getFullName()));\n    token2commands = new HashMap<String, List<Command>>();\n    quit = installCommands(in, sdk, appFullName);\n  }\n\n  private Quit installCommands(final BufferedReader in, final SDK sdk,\n                               final String appFullName)\n  {\n    final Quit quit;\n    add(new BreakPoints(console, sdk));\n    add(new Clear(console));\n    add(new Clock(console, sdk));\n    add(new Enter(console, sdk, in));\n    add(new Execute(console, sdk));\n    add(new Fifo(console, sdk));\n    add(new Gpio(console, sdk));\n    add(new Help(console, this));\n    add(new Interrupt(console, sdk));\n    add(new Label(console, sdk));\n    add(new Load(console, sdk));\n    add(quit = new Quit(console));\n    add(new PinCtrl(console, sdk));\n    add(new Read(console, sdk));\n    add(new Registers(console, sdk));\n    add(new Reset(console, sdk));\n    add(new Save(console, sdk));\n    add(new Script(console, this));\n    add(new SideSet(console, sdk));\n    add(new Sm(console, sdk));\n    add(new Trace(console, sdk));\n    add(new Unassemble(console, sdk));\n    add(new Unload(console, sdk));\n    add(new Version(console, sdk, appFullName));\n    add(new Wait(console, sdk));\n    add(new Wrap(console, sdk));\n    add(new Write(console, sdk));\n    return quit;\n  }\n\n  private void add(final Command command)\n  {\n    if (commands.contains(command)) {\n      throw new IllegalArgumentException(\"command already registered\");\n    }\n    final String fullName = command.getFullName();\n    for (final Command otherCommand : commands) {\n      final String otherFullName = otherCommand.getFullName();\n      if (fullName.startsWith(otherFullName)) {\n        throw new IllegalArgumentException(\"name clash with other command: \" +\n                                           otherCommand);\n      }\n      if (otherFullName.startsWith(fullName)) {\n        throw new IllegalArgumentException(\"name clash with other command: \" +\n                                           otherCommand);\n      }\n    }\n    commands.add(command);\n    updateTokenHashes(command);\n  }\n\n  private void updateTokenHashes(final Command command)\n  {\n    final String fullName = command.getFullName();\n    for (int i = 1; i <= fullName.length(); i++) {\n      final String partialName = fullName.substring(0, i);\n      final List<Command> commands;\n      if (token2commands.containsKey(partialName)) {\n        commands = token2commands.get(partialName);\n      } else {\n        commands = new ArrayList<Command>();\n        token2commands.put(partialName, commands);\n      }\n      commands.add(command);\n    }\n  }\n\n  public void remove(final Command command)\n  {\n    if (!commands.contains(command)) {\n      throw new IllegalArgumentException(\"no such command registered\");\n    }\n    commands.remove(command);\n    final String fullName = command.getFullName();\n    for (int i = 1; i < fullName.length(); i++) {\n      final String partialName = fullName.substring(0, i);\n      final List<Command> commands = token2commands.get(partialName);\n      commands.remove(command);\n    }\n  }\n\n  public List<Command> lookup(final String partialName)\n  {\n    if (token2commands.containsKey(partialName)) {\n      return token2commands.get(partialName);\n    }\n    return null;\n  }\n\n  @Override\n  public Iterator<Command> iterator()\n  {\n    return commands.iterator();\n  }\n\n  private ParseException createCommandParseException(final Command command,\n                                                     final Exception e)\n  {\n    final String helpNotes = String.format(Command.helpNotes);\n    final String message =\n      String.format(\"%s:%n%s%n%s\", command, e.getMessage(), helpNotes);\n    return new ParseException(message);\n  }\n\n  /**\n   * @return &lt;code&gt;true&lt;/code&gt;, if and only if command\n   * \"quit\" is to be successfully be executed (the command itself, not\n   * showing help with \"-h\" option).\n   * @throws &lt;code&gt;ParseException&lt;/code&gt;, if the command\n   * can not be executed due to a parse exception.  In dry-run mode,\n   * checking for this exception can be used to check the syntax of a\n   * command without actually executing it.\n   */\n  public boolean parseAndExecute(final String commandLine, boolean dryRun)\n    throws ParseException\n  {\n    final String[] argv;\n    try {\n      argv = CmdOptions.splitArgs(commandLine);\n    } catch (final CmdOptions.ParseException e) {\n      final String message =\n        String.format(\"failed tokenizing command line: %s%n\", e.getMessage());\n      throw new ParseException(message, e);\n    }\n    if (argv.length == 0) {\n      return false;\n    }\n    final String commandToken = argv[0];\n    final List<Command> matchingCommands = lookup(commandToken);\n    if ((matchingCommands == null) || (matchingCommands.size() == 0)) {\n      final String message =\n        String.format(\"unknown command: %s%n%s%n\",\n                      commandToken, String.format(Command.commandHint));\n      throw new ParseException(message);\n    }\n    if (matchingCommands.size() > 1) {\n      final String message =\n        String.format(\"ambiguous command: %s%npossible resolutions: %s%n\",\n                      commandToken, matchingCommands);\n      throw new ParseException(message);\n    }\n    final Command command = matchingCommands.get(0);\n    try {\n      command.parse(argv);\n    } catch (final CmdOptions.ParseException e) {\n      throw createCommandParseException(command, e);\n    }\n    if (dryRun) {\n      return false;\n    }\n    final boolean executed;\n    try {\n      executed = command.execute();\n    } catch (final IOException e) {\n      throw createCommandParseException(command, e);\n    }\n    return (command == quit) && executed;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/Monitor.java",
    "content": "/*\n * @(#)Monitor.java 1.00 21/02/02\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.RemoteAddressSpaceClient;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.Panic;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\nimport org.soundpaint.rp2040pio.sdk.Program;\nimport org.soundpaint.rp2040pio.sdk.ProgramParser;\n\n/**\n * Program Execution Monitor And Control\n */\npublic class Monitor\n{\n  private static final String APP_TITLE = \"Monitor\";\n  private static final String APP_FULL_NAME =\n    \"Emulation Monitor Control Program Version 0.1\";\n  private static final CmdOptions.FlagOptionDeclaration optVersion =\n    CmdOptions.createFlagOption(false, 'V', \"version\", CmdOptions.Flag.OFF,\n                                \"display version information and exit\");\n  private static final CmdOptions.FlagOptionDeclaration optHelp =\n    CmdOptions.createFlagOption(false, 'h', \"help\", CmdOptions.Flag.OFF,\n                                \"display this help text and exit\");\n  private static final CmdOptions.IntegerOptionDeclaration optPort =\n    CmdOptions.createIntegerOption(\"PORT\", false, 'p', \"port\",\n                                   Constants.\n                                   REGISTER_SERVER_DEFAULT_PORT_NUMBER,\n                                   \"use PORT as server port number\");\n  private static final CmdOptions.StringOptionDeclaration optExample =\n    CmdOptions.createStringOption(\"NAME\", false, 'e', \"example\", null,\n                                  \"name of built-in example script to execute\");\n  private static final CmdOptions.StringOptionDeclaration optFile =\n    CmdOptions.createStringOption(\"PATH\", false, 'f', \"file\", null,\n                                  \"path of monitor script file to execute\");\n  private static final List<CmdOptions.OptionDeclaration<?>>\n    optionDeclarations =\n    Arrays.asList(new CmdOptions.OptionDeclaration<?>[]\n                  { optVersion, optHelp, optPort, optExample, optFile });\n\n  private final BufferedReader in;\n  private final PrintStream console;\n  private final SDK sdk;\n  private final PIOSDK pioSdk;\n  private final GPIOSDK gpioSdk;\n  private final CmdOptions options;\n  private final CommandRegistry commands;\n\n  private Monitor()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public Monitor(final BufferedReader in, final PrintStream console,\n                 final String[] argv)\n    throws IOException\n  {\n    if (in == null) {\n      throw new NullPointerException(\"in\");\n    }\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.in = in;\n    this.console = console;\n    if ((options = parseArgs(argv)) != null) {\n      printAbout();\n      sdk = new SDK(console, connect());\n      pioSdk = sdk.getPIO0SDK();\n      gpioSdk = sdk.getGPIOSDK();\n      commands = new CommandRegistry(console, in, sdk, APP_FULL_NAME);\n    } else {\n      sdk = null;\n      pioSdk = null;\n      gpioSdk = null;\n      commands = null;\n    }\n  }\n\n  private CmdOptions parseArgs(final String argv[]) throws IOException\n  {\n    final CmdOptions options;\n    try {\n      options = new CmdOptions(APP_TITLE, APP_FULL_NAME, null,\n                               optionDeclarations);\n      options.parse(argv);\n      checkValidity(options);\n    } catch (final CmdOptions.ParseException e) {\n      final String message =\n        String.format(\"parsing command line failed: %s\", e.getMessage());\n      throw new IOException(message);\n    }\n    if (options.getValue(optVersion) == CmdOptions.Flag.ON) {\n      printAbout();\n      return null;\n    }\n    if (options.getValue(optHelp) == CmdOptions.Flag.ON) {\n      console.println(options.getFullInfo());\n      return null;\n    }\n    return options;\n  }\n\n  private void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int port = options.getValue(optPort);\n    if ((port < 0) || (port > 65535)) {\n      throw new CmdOptions.\n        ParseException(\"PORT must be in the range 0…65535\");\n    }\n    if (options.isDefined(optExample) && options.isDefined(optFile)) {\n      throw new CmdOptions.\n        ParseException(\"at most one of options \\\"-e\\\" and \\\"-f\\\" may be \" +\n                       \"specified at the same time\");\n    }\n  }\n\n  private void printAbout()\n  {\n    console.printf(\"%s for%n%s%n%s%n%s%n\",\n                   APP_FULL_NAME,\n                   Constants.getEmulatorIdAndVersionWithOs(),\n                   Constants.getMonitorCopyrightNotice(),\n                   String.format(Command.commandHint));\n  }\n\n  private AddressSpace connect() throws IOException\n  {\n    final int port = options.getValue(optPort);\n    try {\n      console.printf(\"connecting to emulation server at port %d…%n\", port);\n      return new RemoteAddressSpaceClient(console, null, port);\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed to connect to emulation server: %s%n\" +\n                      \"check that emulation server runs at port address %d%n\",\n                      e.getMessage(), port);\n      throw new IOException(message);\n    }\n  }\n\n  private int run(final boolean localEcho)\n  {\n    if (sdk == null) return 0;\n    final BufferedReader scriptIn;\n    try {\n      if (options.isDefined(optExample)) {\n        final String optExampleValue = options.getValue(optExample);\n        final String resourcePath =\n          String.format(\"/examples/%s.mon\", optExampleValue);\n        scriptIn = IOUtils.getReaderForResourcePath(resourcePath);\n      } else if (options.isDefined(optFile)) {\n        final String optFileValue = options.getValue(optFile);\n        scriptIn = IOUtils.getReaderForResourcePath(optFileValue);\n      } else {\n        scriptIn = null;\n      }\n    } catch (final IOException e) {\n      console.println(e.getMessage());\n      return -1;\n    }\n    return\n      (scriptIn != null) ?\n      session(scriptIn, false, true, \"script> \") :\n      session(in, false, localEcho, \"> \");\n  }\n\n  private int session(final BufferedReader in,\n                      final boolean dryRun,\n                      final boolean localEcho,\n                      final String prompt) {\n    try {\n      while (true) {\n        console.print(prompt);\n        try {\n          final String line = in.readLine();\n          if (line == null) break;\n          if (localEcho) console.println(line);\n          if (commands.parseAndExecute(line, dryRun)) break;\n        } catch (final Panic | IOException e) {\n          console.println(e.getMessage());\n          if (e instanceof Panic) {\n            console.printf(Command.panicNotes);\n            console.println();\n          }\n        }\n      }\n      console.println(\"bye\");\n      return 0;\n    } catch (final RuntimeException e) {\n      console.printf(\"fatal error: %s%n\", e.getMessage());\n      console.println();\n      console.println(\"detailed debug information:\");\n      e.printStackTrace(console);\n      return -1;\n    }\n  }\n\n  public static int main(final String argv[],\n                         final InputStream in,\n                         final PrintStream out,\n                         final boolean localEcho)\n  {\n    final BufferedReader reader =\n      new BufferedReader(new InputStreamReader(in));\n    try {\n      final int exitCode = new Monitor(reader, out, argv).run(localEcho);\n      return exitCode;\n    } catch (final IOException e) {\n      out.println(e.getMessage());\n      return -1;\n    }\n  }\n\n  public static void main(final String argv[])\n  {\n    final int exitCode = main(argv, System.in, System.out, false);\n    System.exit(exitCode);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/MonitorUtils.java",
    "content": "/*\n * @(#)MonitorUtils.java 1.00 21/04/14\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor;\n\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.io.PrintStream;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.PinState;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Utility methods that are used by multiple monitor commands.\n */\npublic class MonitorUtils\n{\n  public static boolean listExampleHexDumps(final PrintStream console)\n    throws IOException\n  {\n    final String suffix = \".hex\";\n    final List<String> examples =\n      IOUtils.list(\"examples\").stream().\n      filter(s -> s.endsWith(suffix)).\n      map(s -> { return s.substring(0, s.length() - suffix.length()); }).\n      collect(Collectors.toList());\n    for (final String example : examples) {\n      console.printf(\"(pio*:sm*) %s%n\", example);\n    }\n    return true;\n  }\n\n  public static boolean showExampleHexDump(final PrintStream console,\n                                           final String hexDumpId)\n    throws IOException\n  {\n    final String resourcePath = String.format(\"/examples/%s.hex\", hexDumpId);\n    final LineNumberReader reader =\n      IOUtils.getReaderForResourcePath(resourcePath);\n    console.printf(\"(pio*:sm*) [hex dump %s]%n\", hexDumpId);\n    while (true) {\n      final String line = reader.readLine();\n      if (line == null) break;\n      console.printf(\"(pio*:sm*) %3d: %s%n\", reader.getLineNumber(), line);\n    }\n    console.printf(\"(pio*:sm*) [end of hex dump %s]%n\", hexDumpId);\n    return true;\n  }\n\n  private static String asBitArrayDisplay(final PinState[] pinStates)\n  {\n    final StringBuffer display = new StringBuffer();\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      if (((gpioNum & 0x7) == 0) && (gpioNum > 0)) {\n        display.append(' ');\n      }\n      final PinState pinState = pinStates[gpioNum];\n      final String bitDisplay =\n        pinState.getLevel().toChar(pinState.getDirection());\n      display.append(bitDisplay);\n    }\n    return display.toString();\n  }\n\n  /**\n   * Global GPIO view of pins.\n   */\n  public static String gpioDisplay(final SDK sdk,\n                                   final GPIOSDK.Override override)\n    throws IOException\n  {\n    final PinState[] pinStates = sdk.getGPIOSDK().getPinStates(override);\n    final String gpioPinBits = asBitArrayDisplay(pinStates);\n    return String.format(\"(pio%s:sm*) %s%n\", \"*\", gpioPinBits);\n  }\n\n  /**\n   * @param pioNum Either 0 or 1 for GPIO pins of PIO0 or PIO1.\n   */\n  public static String gpioDisplay(final SDK sdk, final int pioNum)\n    throws IOException\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final PinState[] pinStates = pioSdk.getPinStates();\n    final String pioNumId = String.format(\"%d\", pioNum);\n    final String gpioPinBits = asBitArrayDisplay(pinStates);\n    return String.format(\"(pio%s:sm*) %s%n\", pioNumId, gpioPinBits);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/ScriptInfo.java",
    "content": "/*\n * @(#)ScriptInfo.java 1.00 21/06/20\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor;\n\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.TreeMap;\nimport java.util.stream.Collectors;\nimport org.soundpaint.rp2040pio.IOUtils;\n\npublic class ScriptInfo\n{\n  public static class ParseException extends IOException\n  {\n    private static final long serialVersionUID = 8116265717249238138L;\n\n    private static String createMessage(final String innerMessage,\n                                        final String scriptId)\n    {\n      return String.format(\"script info parse exception: script %s: %s\",\n                           scriptId, innerMessage);\n    }\n\n    private ParseException()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    public ParseException(final String innerMessage, final String scriptId)\n    {\n      super(createMessage(innerMessage, scriptId));\n    }\n  }\n\n  public static final String DEFAULT_GROUP_NAME = \"Other\";\n\n  private final String scriptId;\n  private final String scriptName;\n  private final String groupName;\n  private final String description;\n\n  public ScriptInfo(final String scriptId,\n                    final String scriptName,\n                    final String groupName,\n                    final String description)\n  {\n    if (scriptId == null) {\n      throw new NullPointerException(\"scriptId\");\n    }\n    if (scriptName == null) {\n      throw new NullPointerException(\"scriptName\");\n    }\n    this.scriptId = scriptId;\n    this.scriptName = scriptName;\n    this.groupName = groupName;\n    this.description = description;\n  }\n\n  public String getScriptId() { return scriptId; }\n\n  public String getScriptName() { return scriptName; }\n\n  public String getGroupName() { return groupName; }\n\n  public String getDescription() { return description; }\n\n  private static void checkHeaderAlreadyDefined(final String header,\n                                                final String headerId,\n                                                final String scriptId)\n    throws IOException\n  {\n    if (header != null) {\n      final String message = String.format(\"duplicate '%s' header\", headerId);\n      throw new ScriptInfo.ParseException(message, scriptId);\n    }\n  }\n\n  private static void checkHeaderDefined(final String header,\n                                         final String headerId,\n                                         final String scriptId)\n    throws IOException\n  {\n    if (header == null) {\n      final String message = String.format(\"missing '%s' header\", headerId);\n      throw new ScriptInfo.ParseException(message, scriptId);\n    }\n  }\n\n  private static final String SCRIPT_HEADER_ID = \"Script:\";\n  private static final String GROUP_HEADER_ID = \"Group:\";\n\n  private static ScriptInfo createScriptInfo(final String scriptId)\n    throws IOException\n  {\n    final String resourcePath = String.format(\"/examples/%s.mon\", scriptId);\n    final LineNumberReader reader =\n      IOUtils.getReaderForResourcePath(resourcePath);\n\n    // parse headers\n    String scriptName = null;\n    String groupName = null;\n    while (true) {\n      final String line = reader.readLine();\n      if (line == null) break;\n      if (!(line.startsWith(\"#\"))) break;\n      final String comment = line.substring(1).trim();\n      if (comment.isEmpty()) break;\n      if (comment.startsWith(SCRIPT_HEADER_ID)) {\n        checkHeaderAlreadyDefined(scriptName, SCRIPT_HEADER_ID, scriptId);\n        scriptName = comment.substring(SCRIPT_HEADER_ID.length()).trim();\n      } else if (comment.startsWith(GROUP_HEADER_ID)) {\n        checkHeaderAlreadyDefined(groupName, GROUP_HEADER_ID, scriptId);\n        groupName = comment.substring(GROUP_HEADER_ID.length()).trim();\n      } else {\n        final String message =\n          String.format(\"invalid start of header line: %s\", comment);\n        throw new ScriptInfo.ParseException(message, scriptId);\n      }\n    }\n    checkHeaderDefined(scriptName, SCRIPT_HEADER_ID, scriptId);\n    if (groupName == null) groupName = ScriptInfo.DEFAULT_GROUP_NAME;\n\n    // parse description\n    final StringBuffer s = new StringBuffer();\n    while (true) {\n      final String line = reader.readLine();\n      if (line == null) break;\n      if (!(line.startsWith(\"#\"))) break;\n      s.append(String.format(\"%s%n\", line.substring(1).trim()));\n    }\n    final String description = s.toString();\n\n    return new ScriptInfo(scriptId, scriptName, groupName, description);\n  }\n\n  public static Map<String, Map<String, ScriptInfo>> createScriptsInfo()\n    throws IOException\n  {\n    final String suffix = \".mon\";\n    final List<String> scriptIds =\n      IOUtils.list(\"examples\").stream().\n      filter(t -> t.endsWith(suffix)).\n      map(t -> { return t.substring(0, t.length() - suffix.length()); }).\n      collect(Collectors.toList());\n    final Map<String, Map<String, ScriptInfo>> scriptsInfo =\n      new TreeMap<String, Map<String, ScriptInfo>>();\n    for (final String scriptId : scriptIds) {\n      final ScriptInfo scriptInfo = createScriptInfo(scriptId);\n      final String groupName = scriptInfo.getGroupName();\n      final Map<String, ScriptInfo> scriptsGroupInfo;\n      if (scriptsInfo.containsKey(groupName)) {\n        scriptsGroupInfo = scriptsInfo.get(groupName);\n      } else {\n        scriptsGroupInfo = new TreeMap<String, ScriptInfo>();\n        scriptsInfo.put(groupName, scriptsGroupInfo);\n      }\n      scriptsGroupInfo.put(scriptId, scriptInfo);\n    }\n    return scriptsInfo;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/BreakPoints.java",
    "content": "/*\n * @(#)BreakPoints.java 1.00 21/04/04\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"breakpoints\" manages breakpoints.  Breakpoints are\n * not a feature of the RP2040 itself, but have been added to the PIO\n * emulator for advanced debugging.\n */\npublic class BreakPoints extends Command\n{\n  private static final String fullName = \"breakpoints\";\n  private static final String singleLineDescription =\n    \"change breakpoints\";\n  private static final String notes =\n    \"For displaying breakpoints, use the \\\"unassemble\\\" command.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optAdd =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"add\", null,\n                                   \"add breakpoint at specified address \" +\n                                   \"(0x00…0x1f)\");\n  private static final CmdOptions.IntegerOptionDeclaration optDelete =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'd', \"delete\", null,\n                                   \"remove breakpoint from specified \" +\n                                   \"address (0x00…0x1f)\");\n\n  private final SDK sdk;\n\n  public BreakPoints(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optAdd, optDelete });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      if (!options.isDefined(optAdd) && !options.isDefined(optDelete)) {\n        throw new CmdOptions.\n          ParseException(\"at least one of options -a and -d must be specified\");\n      }\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optAddValue = options.getValue(optAdd);\n    final Integer optDeleteValue = options.getValue(optDelete);\n    final int address =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_BREAKPOINTS);\n    if (optAddValue != null) {\n      sdk.hwSetBits(address, 0x1 << optAddValue);\n    }\n    if (optDeleteValue != null) {\n      sdk.hwClearBits(address, 0x1 << optDeleteValue);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Clear.java",
    "content": "/*\n * @(#)Clear.java 1.00 21/04/22\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"clear\" clears the screen and, optionally,\n * scrollback buffer.\n */\npublic class Clear extends Command\n{\n  private static final String fullName = \"clear\";\n  private static final String singleLineDescription =\n    \"clear screen and optionally scrollback buffer\";\n\n  private static final CmdOptions.FlagOptionDeclaration optBuffer =\n    CmdOptions.createFlagOption(false, 'b', \"buffer\", CmdOptions.Flag.OFF,\n                                \"also clear scrollback buffer\");\n\n  public Clear(final PrintStream console)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[] { optBuffer });\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    if (options.getValue(optBuffer).isOn()) {\n      console.printf(\"\\u001b[3J\");\n    }\n    console.printf(\"\\u001b[2J\");\n    console.printf(\"\\u001b[H\");\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Clock.java",
    "content": "/*\n * @(#)Clock.java 1.00 21/05/29\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"clock\" displays or modifies a state machine's\n * clock configuration.\n */\npublic class Clock extends Command\n{\n  private static final float FRAC_MUL = 1.0f / 256;\n  private static final float MIN_DIVIDER = 1.0f;\n  private static final float MAX_DIVIDER = 65536.0f;\n  private static final String fullName = \"clock\";\n  private static final String singleLineDescription =\n    \"display or change internal state machine's clock configuration\";\n  private static final String notes =\n    \"If none of the modification options is specified, the status%n\"+\n    \"of the clock of the selected is displayed.%n\" +\n    \"Otherwise, for all specified options \\\"-i\\\", \\\"-f\\\" and%n\" +\n    \"\\\"-r\\\", the corresponding modification will be performed for%n\" +\n    \"the selected state machine.  Option \\\"-d\\\" can be used%n\" +\n    \"alternatively to options \\\"-i\\\" and \\\"-f\\\".\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optIntDivider =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'i', \"int-divider\", null,\n                                   \"set clock divider integer part for \" +\n                                   \"selected PIO and SM\");\n  private static final CmdOptions.IntegerOptionDeclaration optFracDivider =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'f', \"frac-divider\", null,\n                                   \"set clock divider fractional part for \" +\n                                   \"selected PIO and SM\");\n  private static final CmdOptions.FloatOptionDeclaration optDivider =\n    CmdOptions.createFloatOption(\"NUMBER\", false, 'd', \"divider\", null,\n                                 \"set nearby clock divider from float \" +\n                                 \"value for selected PIO and SM\");\n  private static final CmdOptions.FlagOptionDeclaration optRestart =\n    CmdOptions.createFlagOption(false, 'r', \"restart\", CmdOptions.Flag.OFF,\n                                \"restart clock for selected PIO and SM\");\n\n  private final SDK sdk;\n\n  public Clock(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm,\n              optIntDivider, optFracDivider, optDivider, optRestart });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      final Integer optIntDividerValue = options.getValue(optIntDivider);\n      final Integer optFracDividerValue = options.getValue(optFracDivider);\n      final Float optDividerValue = options.getValue(optDivider);\n      if (optDividerValue != null) {\n        if ((optIntDividerValue != null) || (optFracDividerValue != null)) {\n          final String message =\n            \"when option \\\"-d\\\" is defined, none of competing options \" +\n            \"\\\"-i\\\" or \\\"-f\\\" may be defined\";\n          throw new CmdOptions.ParseException(message);\n        }\n        final float divider = optDividerValue;\n        if (divider < MIN_DIVIDER) {\n          final String message =\n            String.format(\"divider < %f: %f\", MIN_DIVIDER, divider);\n          throw new CmdOptions.ParseException(message);\n        }\n        if (divider > MAX_DIVIDER) {\n          final String message =\n            String.format(\"divider > %f: %f\", MAX_DIVIDER, divider);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      if (optIntDividerValue != null) {\n        final int intDivider = optIntDividerValue;\n        if ((intDivider < 0) || (intDivider > 0x10000)) {\n          final String message =\n            String.format(\"expected integer divider value in the range \" +\n                          \"0…0x%5x, but got: 0x%x\", 0x10000, intDivider);\n          throw new CmdOptions.ParseException(message);\n        }\n        if (optFracDividerValue != null) {\n          if ((intDivider & 0xffff) == 0) {\n            if (optFracDividerValue != 0) {\n              final String message =\n                String.format(\"if int-divider is 0, frac-divider must \" +\n                              \"also be 0, but got: %x\", optFracDividerValue);\n              throw new CmdOptions.ParseException(message);\n            }\n          }\n        }\n      }\n      if (optFracDividerValue != null) {\n        final int fracDivider = optFracDividerValue;\n        if ((fracDivider < 0) || (fracDivider > 0xff)) {\n          final String message =\n            String.format(\"expected fractional divider value in the range \" +\n                          \"0…0x%02x, but got: 0x%x\", 0xff, fracDivider);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n    }\n  }\n\n  private int getClkDivValue(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressClkDiv =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV);\n    return sdk.readAddress(addressClkDiv);\n  }\n\n  private int getIntDivider(final int clkDivValue)\n  {\n    return\n      (clkDivValue & Constants.SM0_CLKDIV_INT_BITS) >>>\n      Constants.SM0_CLKDIV_INT_LSB;\n  }\n\n  private int getFracDivider(final int clkDivValue)\n  {\n    return\n      (clkDivValue & Constants.SM0_CLKDIV_FRAC_BITS) >>>\n      Constants.SM0_CLKDIV_FRAC_LSB;\n  }\n\n  private boolean getEnabled(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressClkEnable =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_CLK_ENABLE);\n    return sdk.readAddress(addressClkEnable) != 0x0;\n  }\n\n  private void displayStatus(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int clkDivValue = getClkDivValue(pioNum, smNum);\n    final int intDivider = getIntDivider(clkDivValue);\n    final int displayIntDivider = intDivider == 0 ? 0x10000 : intDivider;\n    final int fracDivider = getFracDivider(clkDivValue);\n    final float divider = displayIntDivider + FRAC_MUL * fracDivider;\n    final boolean enabled = getEnabled(pioNum, smNum);\n    console.printf(\"(pio%d:sm%d) int-divider=0x%05x, frac-divider=0x%02x \" +\n                   \"(divider=%f)%n\", pioNum, smNum,\n                   displayIntDivider, fracDivider, divider);\n    console.printf(\"           enabled=%s%n\", enabled);\n  }\n\n  private void setIntDivider(final int pioNum, final int smNum,\n                             final int intDivider)\n    throws IOException\n  {\n    final int addressClkDiv =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV);\n    final int clkDiv = intDivider << Constants.SM0_CLKDIV_INT_LSB;\n    final int mask = Constants.SM0_CLKDIV_INT_BITS;\n    sdk.hwWriteMasked(addressClkDiv, clkDiv, mask);\n    final int displayIntDivider = intDivider == 0 ? 0x10000 : intDivider;\n    console.printf(\"(pio%d:sm%d) set int-divider=0x%05x%n\",\n                   pioNum, smNum, displayIntDivider);\n  }\n\n  private void setFracDivider(final int pioNum, final int smNum,\n                              final int fracDivider)\n    throws IOException\n  {\n    final int addressClkDiv =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV);\n    final int clkDiv = fracDivider << Constants.SM0_CLKDIV_FRAC_LSB;\n    final int mask = Constants.SM0_CLKDIV_FRAC_BITS;\n    sdk.hwWriteMasked(addressClkDiv, clkDiv, mask);\n    console.printf(\"(pio%d:sm%d) set frac-divider=0x%02x%n\",\n                   pioNum, smNum, fracDivider);\n  }\n\n  private void setDivider(final int pioNum, final int smNum,\n                          final float divider)\n    throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.smSetClkDiv(smNum, divider);\n    final int clkDivValue = getClkDivValue(pioNum, smNum);\n    final int intDivider = getIntDivider(clkDivValue);\n    final int fracDivider = getFracDivider(clkDivValue);\n    final float setDivider =\n      (intDivider == 0 ? 0x10000 : intDivider) + FRAC_MUL * fracDivider;\n    console.printf(\"(pio%d:sm%d) set divider=%f%n\", pioNum, smNum, setDivider);\n  }\n\n  private void restart(final int pioNum, final int smNum) throws IOException\n  {\n    final int addressCtrl =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    final int mask =\n      (0x1 << (Constants.CTRL_SM_RESTART_LSB + smNum)) &\n      Constants.CTRL_SM_RESTART_BITS;\n    sdk.hwSetBits(addressCtrl, mask);\n    console.printf(\"(pio%d:sm%d) restarted clock%n\", pioNum, smNum);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optIntDividerValue = options.getValue(optIntDivider);\n    final Integer optFracDividerValue = options.getValue(optFracDivider);\n    final Float optDividerValue = options.getValue(optDivider);\n    final boolean optRestartValue = options.getValue(optRestart).isOn();\n    final boolean haveModOp =\n      (optIntDividerValue != null) || (optFracDividerValue != null) ||\n      (optDividerValue != null) || optRestartValue;\n    if (!haveModOp) {\n      displayStatus(pioNum, smNum);\n    }\n    if (optIntDividerValue != null) {\n      setIntDivider(pioNum, smNum, optIntDividerValue);\n    }\n    if (optFracDividerValue != null) {\n      setFracDivider(pioNum, smNum, optFracDividerValue);\n    }\n    if (optDividerValue != null) {\n      setDivider(pioNum, smNum, optDividerValue);\n    }\n    if (optRestartValue) {\n      restart(pioNum, smNum);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Enter.java",
    "content": "/*\n * @(#)Enter.java 1.00 21/03/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"enter\" lets the user enter instruction opcodes to\n * be stored in a PIO's program memory.\n */\npublic class Enter extends Command\n{\n  private static final String fullName = \"enter\";\n  private static final String singleLineDescription =\n    \"enter instruction opcodes; exit by entering an empty line\";\n  private static final String enterInstructions =\n    \"Per input line, enter 16 bit hex intruction word without '0x' prefix.%n\" +\n    \"Enter \\\".\\\" to keep current instruction word.%n\" +\n    \"Enter empty line to quit enter mode.%n\";\n\n  private final SDK sdk;\n  private final BufferedReader in;\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"start address (0x00…0x1f)\");\n  private static final CmdOptions.IntegerOptionDeclaration optValue =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'v', \"value\", null,\n                                   \"instruction op-code\");\n\n  public Enter(final PrintStream console, final SDK sdk,\n               final BufferedReader in)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optAddress, optValue });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    if (in == null) {\n      throw new NullPointerException(\"in\");\n    }\n    this.sdk = sdk;\n    this.in = in;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n    }\n  }\n\n  private void unassemble(final int pioNum, final PIOSDK pioSdk,\n                          final int address)\n    throws IOException\n  {\n    final PIOSDK.InstructionInfo instructionInfo =\n      pioSdk.getMemoryInstruction(pioNum, address, false, true);\n    console.printf(\"(pio%d:sm*) %02x:        %s%n\", pioNum, address,\n                   instructionInfo.getToolTipText());\n  }\n\n  private boolean enterWord(final int pioNum, final PIOSDK pioSdk,\n                            final int address, final String line)\n    throws IOException\n  {\n    try {\n      final int value = Integer.parseInt(line, 16);\n      sdk.writeAddress(PIORegisters.getMemoryAddress(pioNum, address), value);\n      unassemble(pioNum, pioSdk, address);\n      return true;\n    } catch (final NumberFormatException e) {\n      console.printf(\"error: expected 16 bit hex value or '.' or empty line, \" +\n                     \"but got: %s%n\", line);\n      return false;\n    }\n  }\n\n  private static String stripOffComment(final String line)\n  {\n    if ((line == null) || line.isEmpty()) return line;\n    final int hashPos = line.indexOf('#');\n    if (hashPos < 0) return line;\n    return line.substring(0, hashPos);\n  }\n\n  private void enterWords(final int pioNum, final PIOSDK pioSdk,\n                          final int startAddress)\n    throws IOException\n  {\n    console.printf(enterInstructions);\n    int address = startAddress;\n    int count = 0;\n    while (true) {\n      unassemble(pioNum, pioSdk, address);\n      final int currentValue =\n        sdk.readAddress(PIOEmuRegisters.getMemoryAddress(pioNum, address));\n      console.printf(\"(pio%d:sm*) %02x: (%04x) \",\n                     pioNum, address, currentValue);\n      final String line = stripOffComment(in.readLine()).trim();\n      if ((line == null) || line.isEmpty()) break;\n      if (!line.equals(\".\")) {\n        if (!enterWord(pioNum, pioSdk, address, line)) continue;\n      }\n      address = (address + 1) & Constants.MEMORY_SIZE - 1;\n      count++;\n    }\n    console.printf(\"entered %d words%n\", count);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final Integer optAddressValue = options.getValue(optAddress);\n    final int address =\n      optAddressValue != null ?\n      optAddressValue & Constants.MEMORY_SIZE - 1 :\n      0;\n    final Integer optValueValue = options.getValue(optValue);\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    if (optValueValue != null) {\n      sdk.writeAddress(PIORegisters.getMemoryAddress(pioNum, address),\n                       optValueValue);\n      unassemble(pioNum, pioSdk, address);\n    } else {\n      enterWords(pioNum, pioSdk, address);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Execute.java",
    "content": "/*\n * @(#)Execute.java 1.00 21/04/04\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"execute\" writes an instruction for immediate\n * execution (including jumps) and then resuming execution.\n *\n * Note: The idea of this command is to provide explicit access to the\n * PIO SMx_INSTR registers.  Downside of this approach is, that\n * writing to the registers schedules an instruction for execution\n * during the _next_ clock cycle, while reading from the register will\n * return the instruction that is executed during the _current_ clock\n * cycle, that is, in general, a different instruction than the\n * inserted one.  Also, once inserted, there is no way to revert\n * insertion.  An alternative approach is to separate both functions:\n * Provide a command \"insert\" for inserting an instruction to be\n * executed in the upcoming clock cycle, for showing the inserted\n * instruction, if any, and for deleting the inserted instruction, if\n * any; and also, have a separate command for showing the currently\n * executed instruction (maybe show it together with the registers via\n * the \"registers\" command).\n */\npublic class Execute extends Command\n{\n  private static final String fullName = \"execute\";\n  private static final String singleLineDescription =\n    \"set instruction for immediate execution or display \" +\n    \"instructions currently executed or pending for execution\";\n  private static final String notes =\n    \"Writes an instruction for immediate execution (including jumps)%n\" +\n    \"and then resuming execution or displays the currently excuted%n\" +\n    \"instruction.  Immediate execution means execution during the next%n\" +\n    \"clock cycle.%n\" +\n    \"%n\" +\n    \"Options -p and -s select the state machine that this command%n\" +\n    \"applies to.  Default is PIO0 and SM0.%n\" +\n    \"%n\" +\n    \"If neither of options -c, -d, -e, -f is specified, the instruction%n\" +\n    \"currently being executed by the selected state machine and any%n\" +\n    \"pending forced or EXEC'd instruction will be displayed.%n\" +\n    \"%n\" +\n    \"If option -f is specified, the specified instruction is written%n\" +\n    \"for immediate execution (forced instruction).%n\" +\n    \"If option -c is specified, any pending forced instruction%n\" +\n    \"will be cancelled.%n\" +\n    \"If option -e is specified, the specified instruction is written%n\" +\n    \"for execution on the next enabled clock cycle (EXEC'd instruction),%n\" +\n    \"provided that there is no pending forced instruction that would have%n\" +\n    \"higher priority of execution.%n\" +\n    \"If option -d is specified, any pending EXEC'd instruction%n\" +\n    \"will be deleted.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optForce =\n    CmdOptions.createIntegerOption(\"CODE\", false, 'f', \"force\", null,\n                                   \"set or overwrite opcode of forced \" +\n                                   \"instruction to be executed\");\n  private static final CmdOptions.IntegerOptionDeclaration optExec =\n    CmdOptions.createIntegerOption(\"CODE\", false, 'e', \"exec\", null,\n                                   \"set or overwrite opcode of EXEC'd \" +\n                                   \"instruction to be executed\");\n  private static final CmdOptions.FlagOptionDeclaration optCancel =\n    CmdOptions.createFlagOption(false, 'c', \"cancel\", CmdOptions.Flag.OFF,\n                                \"cancel pending forced instruction, if any\");\n  private static final CmdOptions.FlagOptionDeclaration optDelete =\n    CmdOptions.createFlagOption(false, 'd', \"delete\", CmdOptions.Flag.OFF,\n                                \"delete pending EXEC'd instruction, if any\");\n\n  private final SDK sdk;\n\n  public Execute(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optForce, optExec, optCancel, optDelete });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      if (options.isDefined(optForce) && options.getValue(optCancel).isOn()) {\n        throw new CmdOptions.\n          ParseException(\"at most one of options \\\"-c\\\" and \\\"-f\\\" may be \" +\n                         \"specified at the same time\");\n      }\n      if (options.isDefined(optExec) && options.getValue(optDelete).isOn()) {\n        throw new CmdOptions.\n          ParseException(\"at most one of options \\\"-e\\\" and \\\"-d\\\" may be \" +\n                         \"specified at the same time\");\n      }\n    }\n  }\n\n  private int getPendingDelay(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressPendingDelay =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_PENDING_DELAY);\n    final int pendingDelay = sdk.readAddress(addressPendingDelay);\n    return pendingDelay & 0x1f;\n  }\n\n  private void displayInstruction(final int pioNum, final int smNum,\n                                  final PIOSDK pioSdk,\n                                  final int origin, final int opCode)\n    throws IOException\n  {\n    final PIOSDK.InstructionInfo instructionInfo =\n      pioSdk.getInstructionFromOpCode(smNum, origin, \"\", opCode,\n                                      false, false, 0);\n    console.printf(\"(pio%d:sm%d) last executed: %s%n\",\n                   pioNum, smNum, instructionInfo.getToolTipText());\n  }\n\n  private void displayForcedInstruction(final int pioNum, final int smNum,\n                                        final SDK sdk, final PIOSDK pioSdk)\n    throws IOException\n  {\n    final int forcedInstrAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_FORCED_INSTR);\n    final int forcedInstr = sdk.readAddress(forcedInstrAddress);\n    if ((forcedInstr & 0x00010000) != 0x0) {\n      final PIOSDK.InstructionInfo forcedInstrInfo =\n        pioSdk.getInstructionFromOpCode(smNum,\n                                        Constants.INSTR_ORIGIN_FORCED,\n                                        \"\", forcedInstr & 0xffff,\n                                        true, false, 0);\n      console.printf(\"(pio%d:sm%d) forced instr : %s%n\", pioNum, smNum,\n                     forcedInstrInfo.getFullStatement());\n    }\n  }\n\n  private void displayExecdInstruction(final int pioNum, final int smNum,\n                                       final SDK sdk, final PIOSDK pioSdk)\n    throws IOException\n  {\n    final int execdInstrAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_EXECD_INSTR);\n    final int execdInstr = sdk.readAddress(execdInstrAddress);\n    if ((execdInstr & 0x00010000) != 0x0) {\n      final PIOSDK.InstructionInfo execdInstrInfo =\n        pioSdk.getInstructionFromOpCode(smNum,\n                                        Constants.INSTR_ORIGIN_EXECD,\n                                        \"\", execdInstr & 0xffff,\n                                        true, false, 0);\n      console.printf(\"(pio%d:sm%d) execd instr  : %s%n\", pioNum, smNum,\n                     execdInstrInfo.getFullStatement());\n    }\n  }\n\n  private void displayPendingDelay(final int pioNum, final int smNum,\n                                   final PIOSDK.InstructionInfo currentInstrInfo)\n    throws IOException\n  {\n    final int pendingDelay = getPendingDelay(pioNum, smNum);\n    final int totalDelay = currentInstrInfo.getDelay();\n    if (totalDelay > 0) {\n      console.printf(\"(pio%d:sm%d) pending delay: %d of %d cycles done%n\",\n                     pioNum, smNum, totalDelay - pendingDelay, totalDelay);\n    }\n  }\n\n  private void displayInstructions(final int pioNum, final int smNum,\n                                   final SDK sdk, final PIOSDK pioSdk)\n    throws IOException\n  {\n    final PIOSDK.InstructionInfo currentInstrInfo =\n      pioSdk.getCurrentInstruction(smNum, true, true);\n    console.printf(\"(pio%d:sm%d) last executed: %s%n\", pioNum, smNum,\n                   currentInstrInfo.getFullStatement());\n    displayForcedInstruction(pioNum, smNum, sdk, pioSdk);\n    displayExecdInstruction(pioNum, smNum, sdk, pioSdk);\n    displayPendingDelay(pioNum, smNum, currentInstrInfo);\n  }\n\n  private void deleteExecdInstruction(final int pioNum, final int smNum,\n                                      final SDK sdk, final PIOSDK pioSdk)\n    throws IOException\n  {\n    final int clearExecdAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_CLEAR_EXECD);\n    sdk.writeAddress(clearExecdAddress, 0);\n    console.printf(\"(pio%d:sm%d) deleted any pending EXEC'd instruction%n\",\n                   pioNum, smNum);\n  }\n\n  private void setExecdInstruction(final int pioNum, final int smNum,\n                                   final SDK sdk, final PIOSDK pioSdk,\n                                   final int instr)\n    throws IOException\n  {\n    final int execdInstrAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_EXECD_INSTR);\n    sdk.writeAddress(execdInstrAddress, instr);\n    console.println(\"EXEC'd instruction written for pending execution:\");\n    displayInstruction(pioNum, smNum, pioSdk,\n                       Constants.INSTR_ORIGIN_EXECD, instr);\n  }\n\n  private void cancelForcedInstruction(final int pioNum, final int smNum,\n                                       final SDK sdk, final PIOSDK pioSdk)\n    throws IOException\n  {\n    final int clearForcedAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_CLEAR_FORCED);\n    sdk.writeAddress(clearForcedAddress, 0);\n    console.printf(\"(pio%d:sm%d) cancelled any pending forced instruction%n\",\n                   pioNum, smNum);\n  }\n\n  private void setForcedInstruction(final int pioNum, final int smNum,\n                                    final SDK sdk, final PIOSDK pioSdk,\n                                    final int instr)\n    throws IOException\n  {\n    final int instrAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_INSTR);\n    sdk.writeAddress(instrAddress, instr);\n    console.println(\"forced instruction written for pending execution:\");\n    displayInstruction(pioNum, smNum, pioSdk,\n                       Constants.INSTR_ORIGIN_FORCED, instr);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final Integer optForceValue = options.getValue(optForce);\n    final Integer optExecValue = options.getValue(optExec);\n    if (optForceValue != null) {\n      setForcedInstruction(pioNum, smNum, sdk, pioSdk, optForceValue);\n    }\n    if (options.getValue(optCancel).isOn()) {\n      cancelForcedInstruction(pioNum, smNum, sdk, pioSdk);\n    }\n    if (optExecValue != null) {\n      setExecdInstruction(pioNum, smNum, sdk, pioSdk, optExecValue);\n    }\n    if (options.getValue(optDelete).isOn()) {\n      deleteExecdInstruction(pioNum, smNum, sdk, pioSdk);\n    }\n    if ((optForceValue == null) &&\n        (optExecValue == null) &&\n        !options.getValue(optCancel).isOn() &&\n        !options.getValue(optDelete).isOn()) {\n      displayInstructions(pioNum, smNum, sdk, pioSdk);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Fifo.java",
    "content": "/*\n * @(#)Fifo.java 1.00 21/04/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"fifo\" displays or modifies a state machine's\n * FIFO status.\n *\n * TODO: It is unclear whether the FIFO is internally organized as a\n * cyclic buffer, or a shift register.  Therefore, the numbering of\n * the FIFO memory registers may appear wrong, if the emulator's\n * behavior is compared directly with a real RP2040's behavior.\n */\npublic class Fifo extends Command\n{\n  private static final String fullName = \"fifo\";\n  private static final String singleLineDescription =\n    \"display or change internal state machine's FIFO status\";\n  private static final String notes =\n    \"Use options \\\"-p\\\" and \\\"-s\\\" to select a state machine.%n\" +\n    \"If none of the FIFO modification options is specified, the status%n\"+\n    \"of the FIFO of the selected state machine is displayed.%n\" +\n    \"Option '-a' together with option '-v' can be used for directly%n\" +\n    \"low-level write a value into one of the 8 FIFO's data registers.%n\" +\n    \"Otherwise, for all specified modification options \\\"-d\\\", \\\"-e\\\",%n\" +\n    \"\\\"-j\\\", \\\"-u\\\", \\\"--threshold\\\", \\\"--shift-left\\\", \\\"--shift-right\\\"%n\" +\n    \"and \\\"--auto\\\", the corresponding modification will be performed for%n\" +\n    \"the selected state machine and the selected FIFO (either RX or TX).%n\" +\n    \"Modification option \\\"-c\\\" will clear both FIFOs and, if specified%n\" +\n    \"together with one of the other modification options, will be%n\" +\n    \"executed first.  Similarly, options \\\"--clear-tx-stall\\\",%n\" +\n    \"\\\"--clear-tx-over\\\", \\\"clear-rx-under\\\" and \\\"clear-rx-stall\\\"%n\" +\n    \"will clear the corresponding FDEBUG flag of the specified%n\" +\n    \"state machine.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"FIFO memory address (0x0…0x7) to write \" +\n                                   \"value into\");\n  private static final CmdOptions.IntegerOptionDeclaration optValue =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'v', \"value\", null,\n                                   \"value to enqueue or directly write \" +\n                                   \"into FIFO memory\");\n  private static final CmdOptions.FlagOptionDeclaration optClear =\n    CmdOptions.createFlagOption(false, 'c', \"clear\", CmdOptions.Flag.OFF,\n                                \"clear both FIFOs, RX and TX\");\n  private static final CmdOptions.FlagOptionDeclaration optClearTxStall =\n    CmdOptions.createFlagOption(false, null, \"clear-tx-stall\",\n                                CmdOptions.Flag.OFF,\n                                \"clear FDEBUG flag 'TX Stall'\");\n  private static final CmdOptions.FlagOptionDeclaration optClearTxOver =\n    CmdOptions.createFlagOption(false, null, \"clear-tx-over\",\n                                CmdOptions.Flag.OFF,\n                                \"clear FDEBUG flag 'TX Over'\");\n  private static final CmdOptions.FlagOptionDeclaration optClearRxUnder =\n    CmdOptions.createFlagOption(false, null, \"clear-rx-under\",\n                                CmdOptions.Flag.OFF,\n                                \"clear FDEBUG flag 'RX Under'\");\n  private static final CmdOptions.FlagOptionDeclaration optClearRxStall =\n    CmdOptions.createFlagOption(false, null, \"clear-rx-stall\",\n                                CmdOptions.Flag.OFF,\n                                \"clear FDEBUG flag 'RX Stall'\");\n  private static final CmdOptions.FlagOptionDeclaration optDequeue =\n    CmdOptions.createFlagOption(false, 'd', \"dequeue\", CmdOptions.Flag.OFF,\n                                \"dequeue value from either RX or TX FIFO\");\n  private static final CmdOptions.FlagOptionDeclaration optEnqueue =\n    CmdOptions.createFlagOption(false, 'e', \"enqueue\", CmdOptions.Flag.OFF,\n                                \"enqueue value provided with option -v \" +\n                                \"into either RX or TX FIFO\");\n  private static final CmdOptions.FlagOptionDeclaration optJoin =\n    CmdOptions.createFlagOption(false, 'j', \"join\", CmdOptions.Flag.OFF,\n                                \"let either RX or TX FIFO steal the other \" +\n                                \"FIFO's storage\");\n  private static final CmdOptions.FlagOptionDeclaration optUnjoin =\n    CmdOptions.createFlagOption(false, 'u', \"unjoin\", CmdOptions.Flag.OFF,\n                                \"revoke join operation of either RX or TX FIFO\");\n  private static final CmdOptions.IntegerOptionDeclaration optThreshold =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, null, \"threshold\", null,\n                                   \"set pull threshold (when TX selected) \" +\n                                   \"or push threshold (when RX selected)\");\n  private static final CmdOptions.FlagOptionDeclaration optShiftLeft =\n    CmdOptions.createFlagOption(false, null, \"shift-left\", CmdOptions.Flag.OFF,\n                                \"set shift direction left for OSR (when TX \" +\n                                \"selected or for ISR (when RX selected)\");\n  private static final CmdOptions.FlagOptionDeclaration optShiftRight =\n    CmdOptions.createFlagOption(false, null, \"shift-right\", CmdOptions.Flag.OFF,\n                                \"set shift direction left for OSR (when TX \" +\n                                \"selected or for ISR (when RX selected)\");\n  private static final CmdOptions.BooleanOptionDeclaration optAuto =\n    CmdOptions.createBooleanOption(false, null, \"auto\", null,\n                                   \"turn on or off auto-pull (when TX \" +\n                                   \"selected) or auto-push (when RX selected)\");\n  private static final CmdOptions.FlagOptionDeclaration optTX =\n    CmdOptions.createFlagOption(false, 't', \"tx\", CmdOptions.Flag.OFF,\n                                \"apply modification on TX FIFO\");\n  private static final CmdOptions.FlagOptionDeclaration optRX =\n    CmdOptions.createFlagOption(false, 'r', \"rx\", CmdOptions.Flag.OFF,\n                                \"apply modification on RX FIFO\");\n\n  private enum Type\n  {\n    RX, TX;\n  };\n\n  private final SDK sdk;\n\n  public Fifo(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optAddress, optValue, optClear,\n              optClearTxStall, optClearTxOver, optClearRxUnder, optClearRxStall,\n              optDequeue, optEnqueue, optJoin, optUnjoin,\n              optThreshold, optShiftLeft, optShiftRight, optAuto,\n              optTX, optRX });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      final Integer optAddressValue = options.getValue(optAddress);\n      final Integer optValueValue = options.getValue(optValue);\n      if (optAddressValue != null) {\n        final int address = optAddressValue;\n        if ((address < 0) || (address > (Constants.FIFO_DEPTH << 1) - 1)) {\n          final String message =\n            String.format(\"expected address value in the range 0…%d, \" +\n                          \"but got: %d\",\n                          (Constants.FIFO_DEPTH << 1) - 1, address);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      if (options.isDefined(optAddress) || options.getValue(optEnqueue).isOn()) {\n        if (optValueValue == null) {\n          throw new CmdOptions.ParseException(\"missing option: -v\");\n        }\n      }\n      if (optValueValue != null) {\n        if (!options.isDefined(optAddress) &&\n            !options.getValue(optEnqueue).isOn()) {\n          throw new CmdOptions.ParseException(\"missing option: -a or -e\");\n        }\n      }\n      final Integer optThresholdValue = options.getValue(optThreshold);\n      if (optThresholdValue != null) {\n        final int threshold = optThresholdValue;\n        if ((threshold < 0) || (threshold > 32 /* assume (32 == 0) */)) {\n          final String message =\n            String.format(\"expected threshold value in the range 0…%d, \" +\n                          \"but got: %d\", 32, threshold);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      final Boolean optAutoValue = options.getValue(optAuto);\n      if (options.getValue(optRX).isOn() && options.getValue(optTX).isOn()) {\n        final String message =\n          \"either option -r and -t can be specified, but not both\";\n        throw new CmdOptions.ParseException(message);\n      }\n      int opCount = 0;\n      if (optAddressValue != null) opCount++;\n      if (options.getValue(optDequeue).isOn()) opCount++;\n      if (options.getValue(optEnqueue).isOn()) opCount++;\n      if (options.getValue(optJoin).isOn()) opCount++;\n      if (options.getValue(optUnjoin).isOn()) opCount++;\n      if (optThresholdValue != null) opCount++;\n      if (options.getValue(optShiftLeft).isOn()) opCount++;\n      if (options.getValue(optShiftRight).isOn()) opCount++;\n      if (optAutoValue != null) opCount++;\n      if (opCount > 1) {\n        final String message =\n          \"only one of options -a, -d, -q, -j, -u, --threshold, \" +\n          \"--shift-left, --shift-right and --auto may be specified\";\n        throw new CmdOptions.ParseException(message);\n      }\n      if (opCount > 0) {\n        if (!options.getValue(optRX).isOn() && !options.getValue(optTX).isOn()) {\n          final String message =\n            \"if one of options -d, -q, -j, -u, --threshold, \" +\n            \"--shift-left, --shift-right or --auto is specified, either \" +\n            \"option -r or -t must be specified to select a FIFO\";\n          throw new CmdOptions.ParseException(message);\n        }\n      } else {\n        if (options.getValue(optRX).isOn() && options.getValue(optTX).isOn()) {\n          final String message =\n            \"options -r or -t may be specified only if one of \" +\n            \"options -d, -q, -j, -u, --threshold, \" +\n            \"--shift-left, --shift-right and --auto is specified\";\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n    }\n  }\n\n  private int getSMFReadPtr(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressFReadPtr =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.FREAD_PTR);\n    final int fReadPtr = sdk.readAddress(addressFReadPtr);\n    return (fReadPtr >>> (smNum << 3)) & 0xff;\n  }\n\n  private int getSMFLevel(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressFLevel =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FLEVEL);\n    final int fLevel = sdk.readAddress(addressFLevel);\n    return (fLevel >>> (smNum << 3)) & 0xff;\n  }\n\n  private static String shiftDirectionAsString(final boolean isRight)\n  {\n    return isRight ? \"right\" : \"left\";\n  }\n\n  private boolean getFDebug(final int smNum, final int fDebugValue,\n                            final int lsb, final int bits)\n  {\n    return (((fDebugValue & bits) >>> (lsb + smNum)) & 0x01) != 0x0;\n  }\n\n  private boolean getFTxStall(final int smNum, final int fDebugValue)\n  {\n    return\n      getFDebug(smNum, fDebugValue,\n                Constants.FDEBUG_TXSTALL_LSB, Constants.FDEBUG_TXSTALL_BITS);\n  }\n\n  private boolean getFTxOver(final int smNum, final int fDebugValue)\n  {\n    return\n      getFDebug(smNum, fDebugValue,\n                Constants.FDEBUG_TXOVER_LSB, Constants.FDEBUG_TXOVER_BITS);\n  }\n\n  private boolean getFRxUnder(final int smNum, final int fDebugValue)\n  {\n    return\n      getFDebug(smNum, fDebugValue,\n                Constants.FDEBUG_RXUNDER_LSB, Constants.FDEBUG_RXUNDER_BITS);\n  }\n\n  private boolean getFRxStall(final int smNum, final int fDebugValue)\n  {\n    return\n      getFDebug(smNum, fDebugValue,\n                Constants.FDEBUG_RXSTALL_LSB, Constants.FDEBUG_RXSTALL_BITS);\n  }\n\n  private void displayFifo(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int smfReadPtr = getSMFReadPtr(pioNum, smNum);\n    final int txReadPtr = smfReadPtr & 0xf;\n    final int rxReadPtr = (smfReadPtr & 0xf0) >> 4;\n    final int addressFLevel =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FLEVEL);\n    final int smfLevel = getSMFLevel(pioNum, smNum);\n    final int txLevel = smfLevel & 0xf;\n    final int rxLevel = (smfLevel >>> 4) & 0xf;\n    final int addressFDebug =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FDEBUG);\n    final int fDebugValue = sdk.readAddress(addressFDebug);\n    final boolean fTxStall = getFTxStall(smNum, fDebugValue);\n    final boolean fTxOver = getFTxOver(smNum, fDebugValue);\n    final boolean fRxUnder = getFRxUnder(smNum, fDebugValue);\n    final boolean fRxStall = getFRxStall(smNum, fDebugValue);\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final int shiftCtrlValue = sdk.readAddress(addressShiftCtrl);\n    final boolean fJoinRxValue =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_FJOIN_RX_BITS) != 0x0;\n    final boolean fJoinTxValue =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_FJOIN_TX_BITS) != 0x0;\n    final boolean autoPullValue =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_AUTOPULL_BITS) != 0x0;\n    final boolean autoPushValue =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_AUTOPUSH_BITS) != 0x0;\n    final boolean outShiftRight =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS) != 0x0;\n    final boolean inShiftRight =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_BITS) != 0x0;\n    final int pullThresholdBits =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_PULL_THRESH_BITS) >>>\n      Constants.SM0_SHIFTCTRL_PULL_THRESH_LSB;\n    final int pushThresholdBits =\n      (shiftCtrlValue & Constants.SM0_SHIFTCTRL_PUSH_THRESH_BITS) >>>\n      Constants.SM0_SHIFTCTRL_PUSH_THRESH_LSB;\n    final StringBuffer fifoHeader = new StringBuffer();\n    Type type = fJoinRxValue ? (fJoinTxValue ? null : Type.RX) : Type.TX;\n    int regCount = 0;\n    for (int index = 0; index < (Constants.FIFO_DEPTH << 1); index++) {\n      if (!fJoinRxValue && !fJoinTxValue && (index == Constants.FIFO_DEPTH)) {\n        type = Type.RX;\n        regCount = 0;\n      }\n      fifoHeader.append(type != null ? type : \"__\");\n      fifoHeader.append(String.format(\"%01x       \", regCount++));\n    }\n    final StringBuffer fifoContents = new StringBuffer();\n    for (int fifoMemAddress = 0; fifoMemAddress < (Constants.FIFO_DEPTH << 1);\n         fifoMemAddress++) {\n      final int addressFifoMem =\n        PIOEmuRegisters.getFIFOMemAddress(pioNum, smNum, fifoMemAddress);\n      final int fifoMemValue = sdk.readAddress(addressFifoMem);\n      final String readPtr =\n        (!fJoinRxValue && (fifoMemAddress == txReadPtr)) ||\n        (!fJoinTxValue && (fifoMemAddress == rxReadPtr)) ?\n        \"→\" : \" \";\n      fifoContents.append(String.format(\"%08x%s \", fifoMemValue, readPtr));\n    }\n    final StringBuffer fifoLevels = new StringBuffer();\n    if (!fJoinRxValue) {\n      fifoLevels.append(String.format(\"TX_LEVEL=%01x\", txLevel));\n    }\n    if (!fJoinTxValue) {\n      if (fifoLevels.length() > 0) fifoLevels.append(\", \");\n      fifoLevels.append(String.format(\"RX_LEVEL=%01x\", rxLevel));\n    }\n    final int pullThreshold =\n      Constants.checkBitCount(pullThresholdBits, \"TX: OSR threshold\");\n    final int pushThreshold =\n      Constants.checkBitCount(pushThresholdBits, \"RX: ISR threshold\");\n    console.printf(\"(pio%d:sm%d) %s%n\", pioNum, smNum, fifoHeader);\n    console.printf(\"           %s%n\", fifoContents);\n    if (fifoLevels.length() > 0) {\n      console.printf(\"           (%s)%n\", fifoLevels);\n    }\n    console.printf(\"(pio%d:sm%d) TX: threshold=%d, shift direction=%s, \" +\n                   \"auto-pull=%s%n\", pioNum, smNum, pullThreshold,\n                   shiftDirectionAsString(outShiftRight), autoPullValue);\n    console.printf(\"(pio%d:sm%d) RX: threshold=%d, shift direction=%s, \" +\n                   \"auto-push=%s%n\", pioNum, smNum, pushThreshold,\n                   shiftDirectionAsString(inShiftRight), autoPushValue);\n    console.printf(\"(pio%d:sm%d) FDEBUG: TX Stall: %s, TX Over: %s, \" +\n                   \"RX Under: %s, RX Stall: %s%n\",\n                   pioNum, smNum, fTxStall, fTxOver, fRxUnder, fRxStall);\n  }\n\n  private void setThreshold(final int pioNum, final int smNum, final Type type,\n                            final int threshold)\n    throws IOException\n  {\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final int mask;\n    final int lsb;\n    if (type == Type.TX) {\n      mask = Constants.SM0_SHIFTCTRL_PULL_THRESH_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_PULL_THRESH_LSB;\n    } else {\n      mask = Constants.SM0_SHIFTCTRL_PUSH_THRESH_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_PUSH_THRESH_LSB;\n    }\n    sdk.hwWriteMasked(addressShiftCtrl, threshold << lsb, mask);\n    console.printf(\"(pio%d:sm%d) set %s threshold to %d%n\",\n                   pioNum, smNum, type == Type.TX ? \"pull\" : \"push\", threshold);\n  }\n\n  private void setShiftDir(final int pioNum, final int smNum, final Type type,\n                           final boolean right)\n    throws IOException\n  {\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final int mask;\n    final int lsb;\n    if (type == Type.TX) {\n      mask = Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB;\n    } else {\n      mask = Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_LSB;\n    }\n    sdk.hwWriteMasked(addressShiftCtrl, (right ? 0x1 : 0x0) << lsb, mask);\n    console.printf(\"(pio%d:sm%d) set shift direction for %s to %s%n\",\n                   pioNum, smNum, type == Type.TX ? \"OSR\" : \"ISR\",\n                   shiftDirectionAsString(right));\n  }\n\n  private void setAuto(final int pioNum, final int smNum, final Type type,\n                       final boolean auto)\n    throws IOException\n  {\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final int mask;\n    final int lsb;\n    if (type == Type.TX) {\n      mask = Constants.SM0_SHIFTCTRL_AUTOPULL_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_AUTOPULL_LSB;\n    } else {\n      mask = Constants.SM0_SHIFTCTRL_AUTOPUSH_BITS;\n      lsb = Constants.SM0_SHIFTCTRL_AUTOPUSH_LSB;\n    }\n    sdk.hwWriteMasked(addressShiftCtrl, (auto ? 0x1 : 0x0) << lsb, mask);\n    console.printf(\"(pio%d:sm%d) set auto-%s=%s%n\",\n                   pioNum, smNum, type == Type.TX ? \"pull\" : \"push\", auto);\n  }\n\n  private void writeFifoAddress(final int pioNum, final int smNum,\n                                final int fifoMemAddress, final int value)\n    throws IOException\n  {\n    final int address =\n      PIOEmuRegisters.getFIFOMemAddress(pioNum, smNum, fifoMemAddress);\n    sdk.writeAddress(address, value);\n    console.printf(\"(pio%d:sm%d) wrote value %08x into FIFO register %1x%n\",\n                   pioNum, smNum, value, fifoMemAddress);\n  }\n\n  private void clear(final int pioNum, final int smNum) throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.smClearFIFOs(smNum);\n    console.printf(\"(pio%d:sm%d) cleared FIFOs%n\", pioNum, smNum);\n  }\n\n  private void clearFDebug(final int pioNum, final int smNum,\n                           final int lsb, final String flagName)\n    throws IOException\n  {\n    final int addressFDebug =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FDEBUG);\n    sdk.writeAddress(addressFDebug, 0x1 << (lsb + smNum));\n    console.printf(\"(pio%d:sm%d) cleared FDEBUG flag %s%n\",\n                   pioNum, smNum, flagName);\n  }\n\n  private void dequeue(final int pioNum, final int smNum, final Type type)\n    throws IOException\n  {\n    final int address = type == Type.TX ?\n      PIOEmuRegisters.getTXFAddress(pioNum, smNum) :\n      PIORegisters.getRXFAddress(pioNum, smNum);\n    final int value = sdk.readAddress(address);\n    console.printf(\"(pio%d:sm%d) dequeued 0x%08x from %s%n\",\n                   pioNum, smNum, value, type);\n  }\n\n  private void enqueue(final int pioNum, final int smNum, final Type type,\n                       final int value)\n    throws IOException\n  {\n    final int address = type == Type.RX ?\n      PIOEmuRegisters.getRXFAddress(pioNum, smNum) :\n      PIORegisters.getTXFAddress(pioNum, smNum);\n    sdk.writeAddress(address, value);\n    console.printf(\"(pio%d:sm%d) enqueued 0x%08x to %s%n\",\n                   pioNum, smNum, value, type);\n  }\n\n  private void setFJoin(final int pioNum, final int smNum,\n                        final Type type, final boolean join)\n    throws IOException\n  {\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final int mask =\n      type == Type.RX ?\n      Constants.SM0_SHIFTCTRL_FJOIN_RX_BITS :\n      Constants.SM0_SHIFTCTRL_FJOIN_TX_BITS;\n    if (join) {\n      sdk.hwSetBits(addressShiftCtrl, mask);\n      console.printf(\"(pio%d:sm%d) set join %s%n\", pioNum, smNum, type);\n    } else {\n      sdk.hwClearBits(addressShiftCtrl, mask);\n      console.printf(\"(pio%d:sm%d) unset join %s%n\", pioNum, smNum, type);\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optAddressValue = options.getValue(optAddress);\n    final boolean optClearValue = options.getValue(optClear).isOn();\n    final boolean optClearTxStallValue =\n      options.getValue(optClearTxStall).isOn();\n    final boolean optClearTxOverValue =\n      options.getValue(optClearTxOver).isOn();\n    final boolean optClearRxUnderValue =\n      options.getValue(optClearRxUnder).isOn();\n    final boolean optClearRxStallValue =\n      options.getValue(optClearRxStall).isOn();\n    final Integer optValueValue = options.getValue(optValue);\n    final boolean optDequeueValue = options.getValue(optDequeue).isOn();\n    final boolean optEnqueueValue = options.getValue(optEnqueue).isOn();\n    final boolean optJoinValue = options.getValue(optJoin).isOn();\n    final boolean optUnjoinValue = options.getValue(optUnjoin).isOn();\n    final Integer optThresholdValue = options.getValue(optThreshold);\n    final boolean optShiftLeftValue = options.getValue(optShiftLeft).isOn();\n    final boolean optShiftRightValue = options.getValue(optShiftRight).isOn();\n    final Boolean optAutoValue = options.getValue(optAuto);\n    final boolean haveModOp =\n      optClearValue || optClearTxStallValue || optClearTxOverValue ||\n      optClearRxUnderValue || optClearRxStallValue ||\n      optDequeueValue || optEnqueueValue ||\n      optJoinValue || optUnjoinValue || (optThresholdValue != null) ||\n      optShiftLeftValue || optShiftRightValue || (optAutoValue != null) ||\n      (optAddressValue != null) || (optValueValue != null);\n    if (!haveModOp) {\n      displayFifo(pioNum, smNum);\n    }\n    if (optClearValue) {\n      clear(pioNum, smNum);\n    }\n    if (optClearTxStallValue) {\n      clearFDebug(pioNum, smNum, Constants.FDEBUG_TXSTALL_LSB, \"TX Stall\");\n    }\n    if (optClearTxOverValue) {\n      clearFDebug(pioNum, smNum, Constants.FDEBUG_TXOVER_LSB, \"TX Over\");\n    }\n    if (optClearRxUnderValue) {\n      clearFDebug(pioNum, smNum, Constants.FDEBUG_RXUNDER_LSB, \"RX Under\");\n    }\n    if (optClearRxStallValue) {\n      clearFDebug(pioNum, smNum, Constants.FDEBUG_RXSTALL_LSB, \"RX Stall\");\n    }\n    final Type type = options.getValue(optRX).isOn() ? Type.RX : Type.TX;\n    if (optDequeueValue) {\n      dequeue(pioNum, smNum, type);\n    }\n    if (optEnqueueValue) {\n      enqueue(pioNum, smNum, type, optValueValue);\n    }\n    if (optJoinValue) {\n      setFJoin(pioNum, smNum, type, true);\n    }\n    if (optUnjoinValue) {\n      setFJoin(pioNum, smNum, type, false);\n    }\n    if (optThresholdValue != null) {\n      setThreshold(pioNum, smNum, type, optThresholdValue);\n    }\n    if (optShiftLeftValue) {\n      setShiftDir(pioNum, smNum, type, false);\n    }\n    if (optShiftRightValue) {\n      setShiftDir(pioNum, smNum, type, true);\n    }\n    if (optAutoValue != null) {\n      setAuto(pioNum, smNum, type, optAutoValue);\n    }\n    if ((optAddressValue != null) && (optValueValue != null)) {\n      writeFifoAddress(pioNum, smNum, optAddressValue, optValueValue);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Gpio.java",
    "content": "/*\n * @(#)Gpio.java 1.00 21/04/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.MonitorUtils;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"gpio\" the status of the RP2040's GPIO pins.\n * Support for modifying GPIO status is not yet implemented.\n *\n * TODO: Also show GPIO status individual for each PIO / SM, as well\n * as if PIO GPIO pins are routed to RP2040's GPIO pins or not.  Also\n * consider adding support for changing status of GPIO pins.\n */\npublic class Gpio extends Command\n{\n  private enum Policy\n  {\n    PASS(\"pass\"), INVERT(\"invert\"), LOW(\"low\"), HIGH(\"high\");\n\n    private final String displayValue;\n\n    private Policy(final String displayValue)\n    {\n      this.displayValue = displayValue;\n    }\n\n    public String getDisplayValue()\n    {\n      return displayValue;\n    }\n\n    public static Policy fromValue(final int value)\n    {\n      if ((value < 0) || (value >= POLICIES.length)) {\n        throw new IllegalArgumentException(\"value: \" + value);\n      }\n      return POLICIES[value];\n    }\n  }\n\n  private static final Policy[] POLICIES = Policy.values();\n\n  private static final String fullName = \"gpio\";\n  private static final String singleLineDescription =\n    \"display or change status of GPIO pins\";\n  private static final String notes =\n    \"Each PIO has a set of local GPIO pins that, depending on the GPIO's%n\" +\n    \"function selection settings, are propagated to the RP2040's GPIO%n\" +\n    \"pins or not.  Use this command for displaying the RP2040's GPIO pins%n\" +\n    \"after function selection, or as directly output by a specific PIO's%n\" +\n    \"local GPIO pins.%n\" +\n    \"%n\" +\n    \"Use one of options \\\"-i\\\", \\\"-s\\\", \\\"-c\\\", \\\"-e\\\", \\\"-d\\\", together%n\" +\n    \"with option \\\"-g\\\", for either initializing a GPIO pin for a PIO, or%n\" +\n    \"for clearing or setting its status or for specifying its pin%n\" +\n    \"direction by enabling or disabling its output, respectively.%n\" +\n    \"Use options \\\"-p\\\" and \\\"-g\\\" option to specify which PIO and GPIO%n\" +\n    \"pin to apply the operation.  Option \\\"-p\\\" can be ommitted when%n\" +\n    \"clearing or setting GPIO pin status; in that case, the operation%n\" +\n    \"will apply the new pin status as external input for the specified%n\" +\n    \"pin.%n\" +\n    \"%n\" +\n    \"If none of options \\\"-i\\\", \\\"-s\\\", \\\"-c\\\", \\\"-e\\\", \\\"-d\\\", nor any of%n\" +\n    \"the override options is specified, the current status of all GPIO pins%n\" +\n    \"will be displayed, depending on option \\\"-p\\\" for either of the PIOs or,%n\" +\n    \"if \\\"-p\\\" is not specified, for the GPIO after function selection.\" +\n    \"%n\" +\n    \"One of options \\\"--override-irq\\\", \\\"--override-in\\\", \\\"--override-oe\\\", and%n\" +\n    \"\\\"--override-out\\\" may be specified together with one of policy options%n\" +\n    \"\\\"--pass\\\", \\\"--invert\\\", \\\"--low\\\", \\\"--high\\\" to change override policy%n\" +\n    \"of the specified GPIO pin.  If no policy option is specified, the current%n\" +\n    \"policy is displayed for the specified override target.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", null,\n                                   \"PIO number, either 0 or 1 or undefined\");\n  private static final CmdOptions.IntegerOptionDeclaration optGpio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'g', \"gpio\", null,\n                                   \"number of GPIO pin (0…31)\");\n  private static final CmdOptions.FlagOptionDeclaration optInit =\n    CmdOptions.createFlagOption(false, 'i', \"init\", CmdOptions.Flag.OFF,\n                                \"initialize GPIO pin for use with the \" +\n                                \"specified PIO\");\n  private static final CmdOptions.FlagOptionDeclaration optSet =\n    CmdOptions.createFlagOption(false, 's', \"set\", CmdOptions.Flag.OFF,\n                                \"set GPIO pin of the specified PIO or input\");\n  private static final CmdOptions.FlagOptionDeclaration optClear =\n    CmdOptions.createFlagOption(false, 'c', \"clear\", CmdOptions.Flag.OFF,\n                                \"clear GPIO pin of the specified PIO or input\");\n  private static final CmdOptions.FlagOptionDeclaration optEnable =\n    CmdOptions.createFlagOption(false, 'e', \"enable\", CmdOptions.Flag.OFF,\n                                \"enable GPIO output of the specified PIO, \" +\n                                \"setting direction to \\\"out\\\"\");\n  private static final CmdOptions.FlagOptionDeclaration optDisable =\n    CmdOptions.createFlagOption(false, 'd', \"disable\", CmdOptions.Flag.OFF,\n                                \"disable GPIO output of the specified PIO, \" +\n                                \"setting direction to \\\"in\\\"\");\n  private static final CmdOptions.FlagOptionDeclaration optBefore =\n    CmdOptions.createFlagOption(false, null, \"before\", CmdOptions.Flag.OFF,\n                                \"when displaying global GPIO status, show \" +\n                                \"status before rather than after override\");\n  private static final CmdOptions.FlagOptionDeclaration optOverrideIrq =\n    CmdOptions.createFlagOption(false, null, \"override-irq\", CmdOptions.Flag.OFF,\n                                \"specify override policy for a GPIO pin \" +\n                                \"interrupt input\");\n  private static final CmdOptions.FlagOptionDeclaration optOverrideIn =\n    CmdOptions.createFlagOption(false, null, \"override-in\", CmdOptions.Flag.OFF,\n                                \"specify override policy for a GPIO pin \" +\n                                \"peripheral input\");\n  private static final CmdOptions.FlagOptionDeclaration optOverrideOe =\n    CmdOptions.createFlagOption(false, null, \"override-oe\", CmdOptions.Flag.OFF,\n                                \"specify override policy for a GPIO pin \" +\n                                \"output enable\");\n  private static final CmdOptions.FlagOptionDeclaration optOverrideOut =\n    CmdOptions.createFlagOption(false, null, \"override-out\", CmdOptions.Flag.OFF,\n                                \"specify override policy for a GPIO pin \" +\n                                \"output level\");\n  private static final CmdOptions.FlagOptionDeclaration optPass =\n    CmdOptions.createFlagOption(false, null, \"pass\", CmdOptions.Flag.OFF,\n                                \"select 'pass' override policy\");\n  private static final CmdOptions.FlagOptionDeclaration optInvert =\n    CmdOptions.createFlagOption(false, null, \"invert\", CmdOptions.Flag.OFF,\n                                \"select 'invert' override policy\");\n  private static final CmdOptions.FlagOptionDeclaration optLow =\n    CmdOptions.createFlagOption(false, null, \"low\", CmdOptions.Flag.OFF,\n                                \"select 'low' override policy\");\n  private static final CmdOptions.FlagOptionDeclaration optHigh =\n    CmdOptions.createFlagOption(false, null, \"high\", CmdOptions.Flag.OFF,\n                                \"select 'high' override policy\");\n\n  private final SDK sdk;\n\n  public Gpio(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optGpio,\n              optInit, optSet, optClear, optEnable, optDisable, optBefore,\n              optOverrideIrq, optOverrideIn, optOverrideOe, optOverrideOut,\n              optPass, optInvert, optLow, optHigh });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (options.isDefined(optPio)) {\n        final int pioNum = options.getValue(optPio);\n        if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n          throw new CmdOptions.\n            ParseException(\"PIO number must be either 0 or 1\");\n        }\n      }\n      if (options.isDefined(optGpio)) {\n        final int gpioNum = options.getValue(optGpio);\n        if ((gpioNum < 0) || (gpioNum > Constants.GPIO_NUM - 1)) {\n          final String message =\n            String.format(\"GPIO number must be in the range 0x00…0x%02x\",\n                          Constants.GPIO_NUM - 1);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      int editOpCount = 0;\n      if (options.getValue(optSet) == CmdOptions.Flag.ON) editOpCount++;\n      if (options.getValue(optClear) == CmdOptions.Flag.ON) editOpCount++;\n      int manageOpCount = 0;\n      if (options.getValue(optInit) == CmdOptions.Flag.ON) manageOpCount++;\n      if (options.getValue(optEnable) == CmdOptions.Flag.ON) manageOpCount++;\n      if (options.getValue(optDisable) == CmdOptions.Flag.ON) manageOpCount++;\n      int overrideOpCount = 0;\n      if (options.getValue(optOverrideIrq) == CmdOptions.Flag.ON) overrideOpCount++;\n      if (options.getValue(optOverrideIn) == CmdOptions.Flag.ON) overrideOpCount++;\n      if (options.getValue(optOverrideOe) == CmdOptions.Flag.ON) overrideOpCount++;\n      if (options.getValue(optOverrideOut) == CmdOptions.Flag.ON) overrideOpCount++;\n      int overridePolicyCount = 0;\n      if (options.getValue(optPass) == CmdOptions.Flag.ON) overridePolicyCount++;\n      if (options.getValue(optInvert) == CmdOptions.Flag.ON) overridePolicyCount++;\n      if (options.getValue(optLow) == CmdOptions.Flag.ON) overridePolicyCount++;\n      if (options.getValue(optHigh) == CmdOptions.Flag.ON) overridePolicyCount++;\n\n      final int count = editOpCount + manageOpCount + overrideOpCount;\n      if (count > 1) {\n        throw new CmdOptions.\n          ParseException(\"at most one of options \\\"-i\\\", \\\"-s\\\", \\\"-c\\\", \" +\n                         \"\\\"-e\\\", \\\"-d\\\" and the override options may be specified \" +\n                         \"at the same time\");\n      }\n      if (count > 0) {\n        if (!options.isDefined(optGpio)) {\n          throw new CmdOptions.\n            ParseException(\"option not specified: \" + optGpio);\n        }\n        if (options.getValue(optBefore) == CmdOptions.Flag.ON) {\n          throw new CmdOptions.\n            ParseException(\"option 'before' only valid when no operation is specified\");\n        }\n      }\n      if (count == 0) {\n        if (options.isDefined(optGpio)) {\n          throw new CmdOptions.\n            ParseException(\"option may be specified only together with an operation: \" +\n                           optGpio);\n        }\n      }\n      if (overridePolicyCount > 1) {\n        throw new CmdOptions.\n          ParseException(\"at most one of options \\\"--pass\\\", \\\"--invert\\\", \" +\n                         \"\\\"--low\\\", and \\\"--high\\\" may be specified \" +\n                         \"at the same time\");\n      }\n      if (overrideOpCount < overridePolicyCount) {\n        throw new CmdOptions.\n          ParseException(\"override policy may not be specified without override option\");\n      }\n      if (manageOpCount > 0) {\n        if (!options.isDefined(optPio)) {\n          throw new CmdOptions.\n            ParseException(\"option not specified: \" + optPio);\n        }\n      }\n      if (overrideOpCount > 0) {\n        if (options.isDefined(optPio)) {\n          throw new CmdOptions.\n            ParseException(\"option must not be specified for overrides: \" + optPio);\n        }\n      }\n    }\n  }\n\n  private void displayGpio(final Integer optPioValue, final boolean before)\n    throws IOException\n  {\n    final GPIOSDK.Override override =\n      before ? GPIOSDK.Override.BEFORE : GPIOSDK.Override.AFTER;\n    final String gpioDisplay =\n      optPioValue != null ?\n      MonitorUtils.gpioDisplay(sdk, optPioValue) :\n      MonitorUtils.gpioDisplay(sdk, override);\n    console.printf(gpioDisplay);\n  }\n\n  private void initGpio(final int pioNum, final int gpioNum) throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.gpioInit(gpioNum);\n    console.printf(\"(pio%d:sm*) initialized GPIO pin %02x for use with PIO%d%n\",\n                   pioNum, gpioNum, pioNum);\n  }\n\n  private void setGpio(final Integer pioNum, final int gpioNum)\n    throws IOException\n  {\n    if (pioNum != null) {\n      final int address =\n        PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINS);\n      final int mask = 0x1 << gpioNum;\n      sdk.hwSetBits(address, mask);\n      console.printf(\"(pio%d:sm*) set GPIO output pin %02x of PIO%d to 1%n\",\n                     pioNum, gpioNum, pioNum);\n    } else {\n      final int address =\n        PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.GPIO_PADIN);\n      final int mask = 0x1 << gpioNum;\n      sdk.hwSetBits(address, mask);\n      console.printf(\"(pio*:sm*) set GPIO external input %02x to 1%n\",\n                     gpioNum, pioNum);\n    }\n  }\n\n  private void clearGpio(final Integer pioNum, final int gpioNum)\n    throws IOException\n  {\n    if (pioNum != null) {\n      final int address =\n        PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINS);\n      final int mask = 0x1 << gpioNum;\n      sdk.hwClearBits(address, mask);\n      console.printf(\"(pio%d:sm*) set GPIO output pin %02x of PIO%d to 0%n\",\n                     pioNum, gpioNum, pioNum);\n    } else {\n      final int address =\n        PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.GPIO_PADIN);\n      final int mask = 0x1 << gpioNum;\n      sdk.hwClearBits(address, mask);\n      console.printf(\"(pio*:sm*) set GPIO external input %02x to 0%n\",\n                     gpioNum, pioNum);\n    }\n  }\n\n  private void enableGpio(final int pioNum, final int gpioNum)\n    throws IOException\n  {\n    final int address =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINDIRS);\n    final int mask = 0x1 << gpioNum;\n    sdk.hwSetBits(address, mask);\n    console.printf(\"(pio%d:sm*) set direction of GPIO pin %02x of PIO%d to \" +\n                   \"\\\"out\\\"%n\",\n                   pioNum, gpioNum, pioNum);\n  }\n\n  private void disableGpio(final int pioNum, final int gpioNum)\n    throws IOException\n  {\n    final int address =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINDIRS);\n    final int mask = 0x1 << gpioNum;\n    sdk.hwClearBits(address, mask);\n    console.printf(\"(pio%d:sm*) set direction of GPIO pin %02x of PIO%d to \" +\n                   \"\\\"in\\\"%n\",\n                   pioNum, gpioNum, pioNum);\n  }\n\n  private void setOverride(final String target, final String policy, final int gpioNum,\n                           final int overridePolicy, final int lsb, final int policyBits)\n    throws IOException\n  {\n    final int address =\n      GPIOIOBank0Registers.getGPIOAddress(gpioNum,\n                                          GPIOIOBank0Registers.Regs.GPIO0_CTRL);\n    sdk.hwWriteMasked(address, overridePolicy << lsb, policyBits);\n    console.printf(\"(pio*:sm*) set %s override of GPIO pin %02x to policy '%s'%n\",\n                   target, gpioNum, policy);\n  }\n\n  private void displayOverride(final String target, final int gpioNum,\n                               final int lsb, final int policyBits)\n    throws IOException\n  {\n    final int address =\n      GPIOIOBank0Registers.getGPIOAddress(gpioNum,\n                                          GPIOIOBank0Registers.Regs.GPIO0_CTRL);\n    final int ctrl = sdk.readAddress(address);\n    final Policy policy = Policy.fromValue((ctrl & policyBits) >>> lsb);\n    console.printf(\"(pio*:sm*) %s override of GPIO pin %02x policy is '%s'%n\",\n                   target, gpioNum, policy.getDisplayValue());\n  }\n\n  private void displayOrSetOverride(final String target, final String policy,\n                                    final int gpioNum, final Integer overridePolicy,\n                                    final int lsb, final int policyBits)\n    throws IOException\n  {\n    if (policy != null) {\n      setOverride(target, policy, gpioNum, overridePolicy, lsb, policyBits);\n    } else {\n      displayOverride(target, gpioNum, lsb, policyBits);\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final boolean init = options.getValue(optInit) == CmdOptions.Flag.ON;\n    final boolean clear = options.getValue(optClear) == CmdOptions.Flag.ON;\n    final boolean set = options.getValue(optSet) == CmdOptions.Flag.ON;\n    final boolean enable = options.getValue(optEnable) == CmdOptions.Flag.ON;\n    final boolean disable = options.getValue(optDisable) == CmdOptions.Flag.ON;\n    final boolean overrideIrq = options.getValue(optOverrideIrq) == CmdOptions.Flag.ON;\n    final boolean overrideIn = options.getValue(optOverrideIn) == CmdOptions.Flag.ON;\n    final boolean overrideOe = options.getValue(optOverrideOe) == CmdOptions.Flag.ON;\n    final boolean overrideOut = options.getValue(optOverrideOut) == CmdOptions.Flag.ON;\n    final String policy;\n    final Integer policyBits;\n    if (options.getValue(optHigh) == CmdOptions.Flag.ON) {\n      policy = \"high\";\n      policyBits = 0x3;\n    } else if (options.getValue(optLow) == CmdOptions.Flag.ON) {\n      policy = \"low\";\n      policyBits = 0x2;\n    } else if (options.getValue(optInvert) == CmdOptions.Flag.ON) {\n      policy = \"invert\";\n      policyBits = 0x1;\n    } else if (options.getValue(optPass) == CmdOptions.Flag.ON) {\n      policy = \"pass\";\n      policyBits = 0x0;\n    } else {\n      policy = null;\n      policyBits = null;\n    }\n    if (init || clear || set || enable || disable ||\n        overrideIrq || overrideIn || overrideOe || overrideOut) {\n      final Integer pioNum = options.getValue(optPio);\n      final Integer gpioNum = options.getValue(optGpio);\n      if (init) {\n        initGpio(pioNum, gpioNum);\n      } else if (set) {\n        setGpio(pioNum, gpioNum);\n      } else if (clear) {\n        clearGpio(pioNum, gpioNum);\n      } else if (enable) {\n        enableGpio(pioNum, gpioNum);\n      } else if (disable) {\n        disableGpio(pioNum, gpioNum);\n      } else if (overrideIrq) {\n        displayOrSetOverride(\"IRQ \", policy, gpioNum, policyBits,\n                             Constants.IO_BANK0_GPIO0_CTRL_IRQOVER_LSB,\n                             Constants.IO_BANK0_GPIO0_CTRL_IRQOVER_BITS);\n      } else if (overrideIn) {\n        displayOrSetOverride(\"input\", policy, gpioNum, policyBits,\n                             Constants.IO_BANK0_GPIO0_CTRL_INOVER_LSB,\n                             Constants.IO_BANK0_GPIO0_CTRL_INOVER_BITS);\n      } else if (overrideOe) {\n        displayOrSetOverride(\"output enable\", policy, gpioNum, policyBits,\n                             Constants.IO_BANK0_GPIO0_CTRL_OEOVER_LSB,\n                             Constants.IO_BANK0_GPIO0_CTRL_OEOVER_BITS);\n      } else if (overrideOut) {\n        displayOrSetOverride(\"output\", policy, gpioNum, policyBits,\n                             Constants.IO_BANK0_GPIO0_CTRL_OUTOVER_LSB,\n                             Constants.IO_BANK0_GPIO0_CTRL_OUTOVER_BITS);\n      } else {\n        throw new InternalError(\"unexpected case fall-through\");\n      }\n    } else {\n      final boolean before = options.getValue(optBefore) == CmdOptions.Flag.ON;\n      displayGpio(options.getValue(optPio), before);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Help.java",
    "content": "/*\n * @(#)Help.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.CommandRegistry;\n\n/**\n * Monitor command \"help\" prints a list of all available monitor\n * commands.\n */\npublic class Help extends Command\n{\n  private static final String fullName = \"help\";\n  private static final String singleLineDescription =\n    \"list all available monitor commands\";\n\n  private final CommandRegistry commands;\n\n  public Help(final PrintStream console, final CommandRegistry commands)\n  {\n    super(console, fullName, singleLineDescription);\n    if (commands == null) {\n      throw new NullPointerException(\"commands\");\n    }\n    this.commands = commands;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options)\n  {\n    console.println(\"Available commands:\");\n    for (final Command command : commands) {\n      console.printf(\"  %-12s %s%n\", command.getFullName(),\n                     command.getSingleLineDescription());\n    }\n    console.println();\n    console.printf(helpNotes);\n    console.println();\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Interrupt.java",
    "content": "/*\n * @(#)Interrupt.java 1.00 21/06/08\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"interrupt\" displays or modifies a PIO's interrupt\n * flags configuration and status.\n */\npublic class Interrupt extends Command\n{\n  private static final String fullName = \"interrupt\";\n  private static final String singleLineDescription =\n    \"display or change a PIO's IRQs configuration and status\";\n  private static final String notes =\n    \"Use option \\\"-p\\\"to select a PIO.%n\" +\n    \"If none of the IRQ modification options is specified, the status%n\"+\n    \"of the IRQ bits of the selected state machine is displayed.%n\" +\n    \"For modification operation, additionally specify either option%n\" +\n    \"\\\"-s\\\" for selecting a state machine within the selected PIO when%n\" +\n    \"going to modify an SM specific IRQ flag.  Or specify option \\\"-i\\\"%n\" +\n    \"for selecting one of those IRQ flags that are visible to all SMs.%n\" +\n    \"Use option \\\"-t\\\" to apply modification on the TXNFULL group of%n\" +\n    \"IRQ flags, or option \\\"-r\\\" to apply on the RXNEMPTY group of%n\" +\n    \"IRQ flags; if none of those two options is specified, modification%n\" +\n    \"will affect the SM group of IRQ flags.%n\" +\n    \"For disabling the selected IRQ flag, use option \\\"-d\\\".%n\" +\n    \"For enabling the selected IRQ flag, use option \\\"-e\\\".%n\" +\n    \"For enforcing the selected IRQ flag to be set, use option \\\"-f\\\".%n\" +\n    \"For undoing enforcement, use option \\\"-u\\\".%n\" +\n    \"For selecting to which IRQ (IRQ0, IRQ1) option \\\"-e\\\", \\\"-d\\\",%n\" +\n    \"\\\"-f\\\" or \\\"-u\\\" will apply, use option \\\"-0\\\" or \\\"-1\\\".%n\" +\n    \"For setting or clearing one of the IRQs visible to all SMs, use%n\" +\n    \"option \\\"-v\\\".\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", null,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optIrq =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'i', \"irq\", null,\n                                   \"PIO IRQ number (0…7)\");\n  private static final CmdOptions.FlagOptionDeclaration optTxNFull =\n    CmdOptions.createFlagOption(false, 't', \"txnfull\", CmdOptions.Flag.OFF,\n                                \"select TXNFULL interrupt flag of the \" +\n                                \"selected state machine for modification\");\n  private static final CmdOptions.FlagOptionDeclaration optRxNEmpty =\n    CmdOptions.createFlagOption(false, 'r', \"rxnempty\", CmdOptions.Flag.OFF,\n                                \"select RXNEMPTY interrupt flag of the \" +\n                                \"selected state machine for modification\");\n  private static final CmdOptions.FlagOptionDeclaration optDisable =\n    CmdOptions.createFlagOption(false, 'd', \"disable\", CmdOptions.Flag.OFF,\n                                \"override selected interrupt flag to \" +\n                                \"be always cleared\");\n  private static final CmdOptions.FlagOptionDeclaration optEnable =\n    CmdOptions.createFlagOption(false, 'e', \"enable\", CmdOptions.Flag.OFF,\n                                \"revert \\\"-d\\\" option for the specified\" +\n                                \"interrupt flag\");\n  private static final CmdOptions.FlagOptionDeclaration optForce =\n    CmdOptions.createFlagOption(false, 'f', \"force\", CmdOptions.Flag.OFF,\n                                \"override selected interrupt flag to \" +\n                                \"be always set\");\n  private static final CmdOptions.FlagOptionDeclaration optUnforce =\n    CmdOptions.createFlagOption(false, 'u', \"unforce\", CmdOptions.Flag.OFF,\n                                \"revert \\\"-f\\\" option for the specified\" +\n                                \"interrupt flag\");\n  private static final CmdOptions.FlagOptionDeclaration optZero =\n    CmdOptions.createFlagOption(false, '0', \"irq0\", CmdOptions.Flag.OFF,\n                                \"select PIO IRQ0 as override target\");\n  private static final CmdOptions.FlagOptionDeclaration optOne =\n    CmdOptions.createFlagOption(false, '1', \"irq1\", CmdOptions.Flag.OFF,\n                                \"select PIO IRQ1 as override target\");\n  private static final CmdOptions.BooleanOptionDeclaration optValue =\n    CmdOptions.createBooleanOption(false, 'v', \"value\", null,\n                                   \"set value for the selected IRQ flag \" +\n                                   \"of those visible to all SMs\");\n\n  private enum FlagsGroup\n  {\n    IRQ_SM(8, \"IRQ / SM\"), TXNFULL(4, \"TxNFull\"), RXNEMPTY(0, \"RxNEmpty\");\n\n    private final int lsb;\n    private final String label;\n\n    private FlagsGroup(final int lsb, final String label)\n    {\n      this.lsb = lsb;\n      this.label = label;\n    }\n\n    public int getLSB() { return lsb; }\n\n    public String getLabel() { return label; }\n\n    public static FlagsGroup fromOptions(final boolean txNFull,\n                                         final boolean rxNEmpty)\n    {\n      if (txNFull && rxNEmpty) {\n        throw new IllegalArgumentException(\"txNFull and rxNEmpty both true\");\n      }\n      return txNFull ? TXNFULL : (rxNEmpty ? RXNEMPTY : IRQ_SM);\n    }\n  }\n\n  private final SDK sdk;\n\n  public Interrupt(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optIrq, optTxNFull, optRxNEmpty,\n              optDisable, optEnable, optForce, optUnforce,\n              optZero, optOne, optValue });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final boolean txNFull = options.getValue(optTxNFull).isOn();\n      final boolean rxNEmpty = options.getValue(optRxNEmpty).isOn();\n      final boolean enable = options.getValue(optEnable).isOn();\n      final boolean disable = options.getValue(optDisable).isOn();\n      final boolean force = options.getValue(optForce).isOn();\n      final boolean unforce = options.getValue(optUnforce).isOn();\n      final boolean zero = options.getValue(optZero).isOn();\n      final boolean one = options.getValue(optOne).isOn();\n      final Boolean optValueValue = options.getValue(optValue);\n      final boolean haveMaskingOrForcingOp =\n        enable || disable || force || unforce;\n      final boolean haveModOp =\n        haveMaskingOrForcingOp || (optValueValue != null);\n      final Integer optSmValue = options.getValue(optSm);\n      if (optSmValue != null) {\n        final int smNum = optSmValue;\n        if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n          throw new CmdOptions.\n            ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n        }\n        if (!haveModOp) {\n          final String message = \"missing option: -e, -d, -f, -u, -l or -h\";\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      final Integer optIrqValue = options.getValue(optIrq);\n      if (optIrqValue != null) {\n        final int irqNum = optIrqValue;\n        if ((irqNum < 0) || (irqNum > 7)) {\n          throw new CmdOptions.\n            ParseException(\"IRQ number must be in the range 0…7\");\n        }\n        if (optValueValue == null) {\n          throw new CmdOptions.ParseException(\"missing option: -v\");\n        }\n        if (optSmValue != null) {\n          throw new CmdOptions.ParseException(\"conflicting options: -s and -i\");\n        }\n        if (txNFull) {\n          throw new CmdOptions.ParseException(\"conflicting options: -i and -t\");\n        }\n        if (rxNEmpty) {\n          throw new CmdOptions.ParseException(\"conflicting options: -i and -t\");\n        }\n      }\n      if (enable && disable) {\n        throw new CmdOptions.ParseException(\"conflicting options: -e and -d\");\n      }\n      if (force && unforce) {\n        throw new CmdOptions.ParseException(\"conflicting options: -f and -u\");\n      }\n      if (txNFull | rxNEmpty) {\n        if ((optSmValue == null) && (optIrqValue == null)) {\n          throw new CmdOptions.ParseException(\"missing option: -i or -s\");\n        }\n      }\n      final boolean haveIrqSelection = zero || one;\n      if (haveIrqSelection) {\n        if (!haveMaskingOrForcingOp) {\n          final String message = \"missing option: -e, -d, -f or -u\";\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      if (haveMaskingOrForcingOp) {\n        if (!haveIrqSelection) {\n          throw new CmdOptions.ParseException(\"missing option: -0 or -1\");\n        }\n      }\n      if (haveModOp) {\n        if ((optSmValue == null) && (optIrqValue == null)) {\n          throw new CmdOptions.ParseException(\"missing option: -s or -i\");\n        }\n      }\n    }\n  }\n\n  private static String int2bin(final int d)\n  {\n    return String.format(\"%4s\", Integer.toBinaryString(d)).replace(' ', '0');\n  }\n\n  private void displayInterrupts(final int pioNum) throws IOException\n  {\n    console.printf(\"(pio%d:sm*) IRQ / SM  TxNFull  RxNEmpty%n\", pioNum);\n    console.printf(\"           76543210     3210      3210%n\");\n    final int addressIrq =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.IRQ);\n    final int irq = sdk.readAddress(addressIrq);\n    final int addressIntR =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.INTR);\n    final int intR = sdk.readAddress(addressIntR);\n    console.printf(\"           %s%s     %s      %s (INTR)%n\",\n                   int2bin(irq >> 4),\n                   int2bin(irq & 0xf),\n                   int2bin((intR >> 4) & 0xf),\n                   int2bin(intR & 0xf));\n    final int addressIRQ0_IntE =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTE);\n    final int i0IntE = sdk.readAddress(addressIRQ0_IntE);\n    console.printf(\"               %s     %s      %s (IRQ0_INTE)%n\",\n                   int2bin((i0IntE >> 8) & 0xf),\n                   int2bin((i0IntE >> 4) & 0xf),\n                   int2bin(i0IntE & 0xf));\n    final int addressIRQ0_IntF =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTF);\n    final int i0IntF = sdk.readAddress(addressIRQ0_IntF);\n    console.printf(\"               %s     %s      %s (IRQ0_INTF)%n\",\n                   int2bin((i0IntF >> 8) & 0xf),\n                   int2bin((i0IntF >> 4) & 0xf),\n                   int2bin(i0IntF & 0xf));\n    final int addressIRQ0_IntS =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ0_INTS);\n    final int i0IntS = sdk.readAddress(addressIRQ0_IntS);\n    console.printf(\"               %s     %s      %s (IRQ0_INTS)%n\",\n                   int2bin((i0IntS >> 8) & 0xf),\n                   int2bin((i0IntS >> 4) & 0xf),\n                   int2bin(i0IntS & 0xf));\n    final int addressIRQ1_IntE =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTE);\n    final int i1IntE = sdk.readAddress(addressIRQ1_IntE);\n    console.printf(\"               %s     %s      %s (IRQ1_INTE)%n\",\n                   int2bin((i1IntE >> 8) & 0xf),\n                   int2bin((i1IntE >> 4) & 0xf),\n                   int2bin(i1IntE & 0xf));\n    final int addressIRQ1_IntF =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTF);\n    final int i1IntF = sdk.readAddress(addressIRQ1_IntF);\n    console.printf(\"               %s     %s      %s (IRQ1_INTF)%n\",\n                   int2bin((i1IntF >> 8) & 0xf),\n                   int2bin((i1IntF >> 4) & 0xf),\n                   int2bin(i1IntF & 0xf));\n    final int addressIRQ1_IntS =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.IRQ1_INTS);\n    final int i1IntS = sdk.readAddress(addressIRQ1_IntS);\n    console.printf(\"               %s     %s      %s (IRQ1_INTS)%n\",\n                   int2bin((i1IntS >> 8) & 0xf),\n                   int2bin((i1IntS >> 4) & 0xf),\n                   int2bin(i1IntS & 0xf));\n  }\n\n  private void disable(final int pioNum, final int smNum,\n                       final FlagsGroup flagsGroup, final int irqNum)\n    throws IOException\n  {\n    final int addressINTE =\n      PIORegisters.getAddress(pioNum, irqNum == 0 ?\n                              PIORegisters.Regs.IRQ0_INTE :\n                              PIORegisters.Regs.IRQ1_INTE);\n    final int lsb = flagsGroup.getLSB();\n    sdk.hwClearBits(addressINTE, 1 << (lsb + smNum));\n    console.printf(\"(pio%d:sm%d) disabled %s for IRQ%d (IRQ%d_INTE)%n\",\n                   pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum);\n  }\n\n  private void enable(final int pioNum, final int smNum,\n                      final FlagsGroup flagsGroup, final int irqNum)\n    throws IOException\n  {\n    final int addressINTE =\n      PIORegisters.getAddress(pioNum, irqNum == 0 ?\n                              PIORegisters.Regs.IRQ0_INTE :\n                              PIORegisters.Regs.IRQ1_INTE);\n    final int lsb = flagsGroup.getLSB();\n    sdk.hwSetBits(addressINTE, 1 << (lsb + smNum));\n    console.printf(\"(pio%d:sm%d) enabled %s for IRQ%d (IRQ%d_INTE)%n\",\n                   pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum);\n  }\n\n  private void force(final int pioNum, final int smNum,\n                     final FlagsGroup flagsGroup, final int irqNum)\n    throws IOException\n  {\n    final int addressINTF =\n      PIORegisters.getAddress(pioNum, irqNum == 0 ?\n                              PIORegisters.Regs.IRQ0_INTF :\n                              PIORegisters.Regs.IRQ1_INTF);\n    final int lsb = flagsGroup.getLSB();\n    sdk.hwSetBits(addressINTF, 1 << (lsb + smNum));\n    console.printf(\"(pio%d:sm%d) set force %s for IRQ%d (IRQ%d_INTF)%n\",\n                   pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum);\n  }\n\n  private void unforce(final int pioNum, final int smNum,\n                       final FlagsGroup flagsGroup, final int irqNum)\n    throws IOException\n  {\n    final int addressINTF =\n      PIORegisters.getAddress(pioNum, irqNum == 0 ?\n                              PIORegisters.Regs.IRQ0_INTF :\n                              PIORegisters.Regs.IRQ1_INTF);\n    final int lsb = flagsGroup.getLSB();\n    sdk.hwClearBits(addressINTF, 1 << (lsb + smNum));\n    console.printf(\"(pio%d:sm%d) unset force %s for IRQ%d (IRQ%d_INTF)%n\",\n                   pioNum, smNum, flagsGroup.getLabel(), irqNum, irqNum);\n  }\n\n  private void setValue(final int pioNum, final int irqNum, final Boolean value)\n    throws IOException\n  {\n    final int bitValue = value ? 1 : 0;\n    final int addressIrq =\n      PIORegisters.getAddress(pioNum, value ?\n                              PIORegisters.Regs.IRQ_FORCE :\n                              PIORegisters.Regs.IRQ);\n    sdk.hwWriteMasked(addressIrq, 1 << irqNum, 1 << irqNum);\n    console.printf(\"(pio%d:sm*) set IRQ bit %d to %d%n\",\n                   pioNum, irqNum, bitValue);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final Integer optSmValue = options.getValue(optSm);\n    final Integer optIrqValue = options.getValue(optIrq);\n    final boolean disable = options.getValue(optDisable).isOn();\n    final boolean enable = options.getValue(optEnable).isOn();\n    final boolean force = options.getValue(optForce).isOn();\n    final boolean unforce = options.getValue(optUnforce).isOn();\n    final boolean zero = options.getValue(optZero).isOn();\n    final boolean one = options.getValue(optOne).isOn();\n    final Boolean optValueValue = options.getValue(optValue);\n    final boolean txNFull = options.getValue(optTxNFull).isOn();\n    final boolean rxNEmpty = options.getValue(optRxNEmpty).isOn();\n    final boolean haveModOp =\n      enable || disable || force || unforce || zero || one ||\n      (optValueValue != null);\n    final FlagsGroup flagsGroup = FlagsGroup.fromOptions(txNFull, rxNEmpty);\n    final int irqNum = one ? 1 : (zero ? 0 : -1);\n\n    if (!haveModOp) {\n      displayInterrupts(pioNum);\n    }\n    if (disable) {\n      disable(pioNum, optSmValue, flagsGroup, irqNum);\n    }\n    if (enable) {\n      enable(pioNum, optSmValue, flagsGroup, irqNum);\n    }\n    if (force) {\n      force(pioNum, optSmValue, flagsGroup, irqNum);\n    }\n    if (unforce) {\n      unforce(pioNum, optSmValue, flagsGroup, irqNum);\n    }\n    if (optValueValue != null) {\n      setValue(pioNum,\n               optSmValue != null ? optSmValue : optIrqValue,\n               optValueValue);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Label.java",
    "content": "/*\n * @(#)Label.java 1.00 21/03/29\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"label\" displays a register's name, if available.\n */\npublic class Label extends Command\n{\n  private static final String fullName = \"label\";\n  private static final String singleLineDescription =\n    \"display a register's label\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"address (0x00000000…0xffffffff) of the \" +\n                                   \"register to display\");\n\n  private final SDK sdk;\n\n  public Label(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[] { optAddress });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if ((options.getValue(optHelp) != CmdOptions.Flag.ON) &&\n        (!options.isDefined(optAddress))) {\n      throw new CmdOptions.\n        ParseException(\"option not specified: \" + optAddress);\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int address = options.getValue(optAddress);\n    final boolean validAddress = sdk.providesAddress(address);\n    if (!validAddress) {\n      final String message =\n        String.format(\"unsupported address: 0x%08x\", address);\n      throw new IOException(message);\n    }\n    final String label = sdk.getLabelForAddress(address);\n    console.printf(\"%s (0x%08x)%n\" , label, address);\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Load.java",
    "content": "/*\n * @(#)Load.java 1.00 21/03/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.MonitorUtils;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"load\" loads a program from a file and stores it in\n * a PIO's memory.\n */\npublic class Load extends Command\n{\n  private static final String fullName = \"load\";\n  private static final String singleLineDescription =\n    \"load program from file and mark affected PIO memory area as allocated\";\n  private static final String notes =\n    \"The \\\"load\\\" command reads in the specified hex dump and stores it%n\" +\n    \"as a PIO program in one of the two PIOs' instruction memory.%n\" +\n    \"By convention, hex dump files have \\\".hex\\\" file name suffix.%n\" +\n    \"%n\" +\n    \"Built-in example hex dumps are available that can be listed with%n\" +\n    \"the \\\"-l\\\" option.  To select any of the example hex dumps, use the%n\" +\n    \"\\\"-e\\\" option and pass to this option the hex dump's name as shown%n\" +\n    \"in the list of available built-in hex dumps.  To view a built-in hex%n\" +\n    \"dump prior to loading it, use the \\\"-s\\\" option.%n\" +\n    \"For user-provided hex dumps, use the \\\"-f\\\" option to specify the%n\" +\n    \"file path of the hex dump, including the \\\".hex\\\" file name suffix.\" +\n    \"%n\" +\n    \"Note that tracking memory allocation is not a feature of the%n\" +\n    \"RP2040, but local to this monitor instance, just to avoid%n\" +\n    \"accidentally overwriting your own PIO programs.  Other applications%n\" +\n    \"that concurrently access the RP2040 will therefore ignore%n\" +\n    \"this instance's allocation tracking and may arbitrarily%n\" +\n    \"overwrite allocated PIO memory, using their own allocation scheme.%n\" +\n    \"%n\" +\n    \"Expected file format:%n\" +\n    \"The program file to be loaded must be a regular text file with%n\" +\n    \"either \\\"\\\\n\\\" or \\\"\\\\r\\\\n\\\" line endings and UTF-8 encoding.%n\" +\n    \"Other encodings may also work for the core instruction opcodes, but%n\" +\n    \"may give unexpected results if meta information is relevant.%n\" +\n    \"Empty lines are ignored.  Each non-empty line of the text file%n\" +\n    \"must contain either meta information or an opcode.  A meta%n\" +\n    \"information line starts with a leading '#' character and may%n\" +\n    \"contain either a special directive, or,  if the '#' is followed by%n\" +\n    \"a ';' character, an arbitrary user comment.  Each non-empty line that%n\" +\n    \"is not a meta information line must contain a single opcode.  Each%n\" +\n    \"opcode is a 32 bits integer and represented as a plain four-digit%n\" +\n    \"hexadecimal value without leading \\\"0x\\\".  The maximum allowed number%n\" +\n    \"of opcodes is 32.\";\n\n  private final SDK sdk;\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.FlagOptionDeclaration optList =\n    CmdOptions.createFlagOption(false, 'l', \"list\", CmdOptions.Flag.OFF,\n                                \"list names of available example hex dumps\");\n  private static final CmdOptions.StringOptionDeclaration optShow =\n    CmdOptions.createStringOption(\"NAME\", false, 's', \"show\", null,\n                                  \"name of built-in example hex dump to show\");\n  private static final CmdOptions.StringOptionDeclaration optExample =\n    CmdOptions.createStringOption(\"NAME\", false, 'e', \"example\", null,\n                                  \"name of built-in example hex dump to load\");\n  private static final CmdOptions.StringOptionDeclaration optFile =\n    CmdOptions.createStringOption(\"PATH\", false, 'f', \"file\", null,\n                                  \"path of hex dump file to load\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"preferred program start address \" +\n                                   \"(0x00…0x1f)\");\n\n  public Load(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optList, optShow, optExample, optFile, optAddress });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int pioNum = options.getValue(optPio);\n    if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n      throw new CmdOptions.\n        ParseException(\"PIO number must be either 0 or 1\");\n    }\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    int count = 0;\n    if (optListValue) count++;\n    if (optShowValue != null) count++;\n    if (optExampleValue != null) count++;\n    if (optFileValue != null) count++;\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (count == 0) {\n        throw new CmdOptions.\n          ParseException(\"at least one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                         \"and \\\"-f\\\" must be specified\");\n      }\n    }\n    if (count > 1) {\n      throw new CmdOptions.\n        ParseException(\"at most one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                       \"and \\\"-f\\\" may be specified at the same time\");\n    }\n  }\n\n  private boolean loadHexDump(final int pioNum,\n                              final BufferedReader reader,\n                              final String hexDumpId,\n                              final Integer address)\n    throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final int assignedAddress =\n      address != null ?\n      pioSdk.addProgramAtOffset(hexDumpId, reader, address) :\n      pioSdk.addProgram(hexDumpId, reader);\n    console.printf(\"(pio%d:sm*) loaded program %s at address 0x%02x%n\",\n                   pioNum, hexDumpId, assignedAddress);\n    return true;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    final Integer optAddressValue = options.getValue(optAddress);\n    if (optListValue) {\n      return MonitorUtils.listExampleHexDumps(console);\n    } else if (optShowValue != null) {\n      return MonitorUtils.showExampleHexDump(console, optShowValue);\n    } else if (optExampleValue != null) {\n      final String resourcePath =\n        String.format(\"/examples/%s.hex\", optExampleValue);\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(resourcePath);\n      return loadHexDump(pioNum, reader, optExampleValue, optAddressValue);\n    } else if (optFileValue != null) {\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(optFileValue);\n      return loadHexDump(pioNum, reader, optFileValue, optAddressValue);\n    }\n    return false;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/PinCtrl.java",
    "content": "/*\n * @(#)PinCtrl.java 1.00 21/06/01\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"pinctrl\" displays or modifies a state machine's\n * pin control.\n */\npublic class PinCtrl extends Command\n{\n  private static final String fullName = \"pinctrl\";\n  private static final String singleLineDescription =\n    \"display or change state machine's pin control\";\n  private static final String notes =\n    \"Use options \\\"-p\\\" and \\\"-s\\\" to select a state machine.%n\" +\n    \"If none of the pin control modification options is specified, the%n\" +\n    \"status of the pin control of the selected state machine is displayed.%n\" +\n    \"For setting pin count or pin base for SET / OUT / IN instructions or%n\" +\n    \"the GPIO pin number to check when executing the JMP PIN instruction,%n\" +\n    \"use the corresponding \\\"--set-count\\\", \\\"--set-base\\\",%n\" +\n    \"\\\"--out-count\\\", \\\"--out-base\\\", \\\"--in-base\\\" or \\\"--jmp-pin\\\"%n\" +\n    \"option.%n\" +\n    \"This command does not support setting the side-set count or%n\" +\n    \"side-set base.  For modifying side-set configuration, use the%n\" +\n    \"monitor command \\\"side-set\\\" instead.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optSetCount =\n    CmdOptions.createIntegerOption(\"COUNT\", false, null, \"set-count\", null,\n                                   \"number of GPIO pins asserted by SET \"+\n                                   \"instruction (0…5)\");\n  private static final CmdOptions.IntegerOptionDeclaration optSetBase =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, null, \"set-base\", null,\n                                   \"lowest-numbered GPIO pin affected by \"+\n                                   \"SET PINS / PINDIRS instruction (0…31)\");\n  private static final CmdOptions.IntegerOptionDeclaration optOutCount =\n    CmdOptions.createIntegerOption(\"COUNT\", false, null, \"out-count\", null,\n                                   \"number of GPIO pins asserted by OUT PINS \"+\n                                   \"/ PINDIRS or MOV PINS instruction (0…32)\");\n  private static final CmdOptions.IntegerOptionDeclaration optOutBase =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, null, \"out-base\", null,\n                                   \"lowest-numbered GPIO pin affected by OUT \"+\n                                   \"/ MOV PINS / PINDIRS instruction (0…31)\");\n  private static final CmdOptions.IntegerOptionDeclaration optInBase =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, null, \"in-base\", null,\n                                   \"GPIO pin mapped to LSB for IN \" +\n                                   \"instruction (0…31)\");\n  private static final CmdOptions.IntegerOptionDeclaration optJmpPin =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, null, \"jmp-pin\", null,\n                                   \"GPIO pin to check when executing \" +\n                                   \"JMP PIN instruction (0…31)\");\n\n  private final SDK sdk;\n\n  public PinCtrl(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm,\n              optSetCount, optSetBase, optOutCount, optOutBase,\n              optInBase, optJmpPin });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      final Integer optSetCountValue = options.getValue(optSetCount);\n      if (optSetCountValue != null) {\n        final int setCount = optSetCountValue;\n        if ((setCount < 0) || (setCount > 5)) {\n          throw new CmdOptions.\n            ParseException(\"set-count must be in the range 0…5\");\n        }\n      }\n      final Integer optSetBaseValue = options.getValue(optSetBase);\n      if (optSetBaseValue != null) {\n        final int setBase = optSetBaseValue;\n        if ((setBase < 0) || (setBase > 31)) {\n          throw new CmdOptions.\n            ParseException(\"set-base must be in the range 0…31\");\n        }\n      }\n      final Integer optOutCountValue = options.getValue(optOutCount);\n      if (optOutCountValue != null) {\n        final int outCount = optOutCountValue;\n        if ((outCount < 0) || (outCount > 32)) {\n          throw new CmdOptions.\n            ParseException(\"out-count must be in the range 0…32\");\n        }\n      }\n      final Integer optOutBaseValue = options.getValue(optOutBase);\n      if (optOutBaseValue != null) {\n        final int outBase = optOutBaseValue;\n        if ((outBase < 0) || (outBase > 31)) {\n          throw new CmdOptions.\n            ParseException(\"out-base must be in the range 0…31\");\n        }\n      }\n      final Integer optInBaseValue = options.getValue(optInBase);\n      if (optInBaseValue != null) {\n        final int inBase = optInBaseValue;\n        if ((inBase < 0) || (inBase > 31)) {\n          throw new CmdOptions.\n            ParseException(\"in-base must be in the range 0…31\");\n        }\n      }\n      final Integer optJmpPinValue = options.getValue(optJmpPin);\n      if (optJmpPinValue != null) {\n        final int jmpPin = optJmpPinValue;\n        if ((jmpPin < 0) || (jmpPin > 31)) {\n          throw new CmdOptions.\n            ParseException(\"jmp-pin must be in the range 0…31\");\n        }\n      }\n    }\n  }\n\n  private void displayPinCtrl(final int pioNum, final int smNum,\n                              final SDK sdk)\n    throws IOException\n  {\n    final int pinCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    final int pinCtrl = sdk.readAddress(pinCtrlAddress);\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(execCtrlAddress);\n    final int setCount =\n      (pinCtrl & Constants.SM0_PINCTRL_SET_COUNT_BITS) >>>\n      Constants.SM0_PINCTRL_SET_COUNT_LSB;\n    final int setBase =\n      (pinCtrl & Constants.SM0_PINCTRL_SET_BASE_BITS) >>>\n      Constants.SM0_PINCTRL_SET_BASE_LSB;\n    final int outCount =\n      (pinCtrl & Constants.SM0_PINCTRL_OUT_COUNT_BITS) >>>\n      Constants.SM0_PINCTRL_OUT_COUNT_LSB;\n    final int outBase =\n      (pinCtrl & Constants.SM0_PINCTRL_OUT_BASE_BITS) >>>\n      Constants.SM0_PINCTRL_OUT_BASE_LSB;\n    final int inBase =\n      (pinCtrl & Constants.SM0_PINCTRL_IN_BASE_BITS) >>>\n      Constants.SM0_PINCTRL_IN_BASE_LSB;\n    final int jmpPin =\n      (execCtrl & Constants.SM0_EXECCTRL_JMP_PIN_BITS) >>>\n      Constants.SM0_EXECCTRL_JMP_PIN_LSB;\n    console.printf(\"(pio%d:sm%d) set-count=%d, set-base=%d%n\",\n                   pioNum, smNum, setCount, setBase);\n    console.printf(\"(pio%d:sm%d) out-count=%d, out-base=%d%n\",\n                   pioNum, smNum, outCount, outBase);\n    console.printf(\"(pio%d:sm%d) jmp-pin=%d, in-base=%d%n\",\n                   pioNum, smNum, jmpPin, inBase);\n  }\n\n  private void setAny(final int pioNum, final int smNum,\n                      final SDK sdk, final String name,\n                      final int value, final int mask, final int lsb,\n                      final boolean execCtrl)\n    throws IOException\n  {\n    final int address =\n      execCtrl ?\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL) :\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    final int bits = value << lsb;\n    sdk.hwWriteMasked(address, bits, mask);\n    console.printf(\"(pio%d:sm%d) set %s to %d%n\", pioNum, smNum, name, value);\n  }\n\n  private void setSetCount(final int pioNum, final int smNum,\n                           final SDK sdk, final int count)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"SET count\", count,\n           Constants.SM0_PINCTRL_SET_COUNT_BITS,\n           Constants.SM0_PINCTRL_SET_COUNT_LSB, false);\n  }\n\n  private void setSetBase(final int pioNum, final int smNum,\n                          final SDK sdk, final int base)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"SET base\", base,\n           Constants.SM0_PINCTRL_SET_BASE_BITS,\n           Constants.SM0_PINCTRL_SET_BASE_LSB, false);\n  }\n\n  private void setOutCount(final int pioNum, final int smNum,\n                           final SDK sdk, final int count)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"OUT count\", count,\n           Constants.SM0_PINCTRL_OUT_COUNT_BITS,\n           Constants.SM0_PINCTRL_OUT_COUNT_LSB, false);\n  }\n\n  private void setOutBase(final int pioNum, final int smNum,\n                          final SDK sdk, final int base)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"OUT base\", base,\n           Constants.SM0_PINCTRL_OUT_BASE_BITS,\n           Constants.SM0_PINCTRL_OUT_BASE_LSB, false);\n  }\n\n  private void setInBase(final int pioNum, final int smNum,\n                         final SDK sdk, final int base)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"IN base\", base,\n           Constants.SM0_PINCTRL_IN_BASE_BITS,\n           Constants.SM0_PINCTRL_IN_BASE_LSB, false);\n  }\n\n  private void setJmpPin(final int pioNum, final int smNum,\n                         final SDK sdk, final int gpioNum)\n    throws IOException\n  {\n    setAny(pioNum, smNum, sdk, \"JMP PIN\", gpioNum,\n           Constants.SM0_EXECCTRL_JMP_PIN_BITS,\n           Constants.SM0_EXECCTRL_JMP_PIN_LSB, true);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optSetCountValue = options.getValue(optSetCount);\n    final Integer optSetBaseValue = options.getValue(optSetBase);\n    final Integer optOutCountValue = options.getValue(optOutCount);\n    final Integer optOutBaseValue = options.getValue(optOutBase);\n    final Integer optInBaseValue = options.getValue(optInBase);\n    final Integer optJmpPinValue = options.getValue(optJmpPin);\n    if ((optSetCountValue == null) && (optSetBaseValue == null) &&\n        (optOutCountValue == null) && (optOutBaseValue == null) &&\n        (optInBaseValue == null) && (optJmpPinValue == null)) {\n      displayPinCtrl(pioNum, smNum, sdk);\n    } else {\n      if (optSetCountValue != null) {\n        setSetCount(pioNum, smNum, sdk, optSetCountValue);\n      }\n      if (optSetBaseValue != null) {\n        setSetBase(pioNum, smNum, sdk, optSetBaseValue);\n      }\n      if (optOutCountValue != null) {\n        setOutCount(pioNum, smNum, sdk, optOutCountValue);\n      }\n      if (optOutBaseValue != null) {\n        setOutBase(pioNum, smNum, sdk, optOutBaseValue);\n      }\n      if (optInBaseValue != null) {\n        setInBase(pioNum, smNum, sdk, optInBaseValue);\n      }\n      if (optJmpPinValue != null) {\n        setJmpPin(pioNum, smNum, sdk, optJmpPinValue);\n      }\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Quit.java",
    "content": "/*\n * @(#)Quit.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\n\n/**\n * Monitor command \"quit\" quits the monitor application.\n */\npublic class Quit extends Command\n{\n  private static final String fullName = \"quit\";\n  private static final String singleLineDescription = \"quit monitor\";\n\n  public Quit(final PrintStream console)\n  {\n    super(console, fullName, singleLineDescription);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options)\n  {\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Read.java",
    "content": "/*\n * @(#)Read.java 1.00 21/03/29\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"read\" provides low-level read access to a\n * register.\n */\npublic class Read extends Command\n{\n  private static final String fullName = \"read\";\n  private static final String singleLineDescription =\n    \"low-level read access to a register\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"address (0x00000000…0xffffffff) of the \" +\n                                   \"register to access\");\n\n  private final SDK sdk;\n\n  public Read(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[] { optAddress });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (!options.isDefined(optAddress)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optAddress);\n      }\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int address = options.getValue(optAddress);\n    final boolean validAddress = sdk.providesAddress(address);\n    if (!validAddress) {\n      final String message =\n        String.format(\"read from unsupported address: 0x%08x\", address);\n      throw new IOException(message);\n    }\n    final int value = sdk.readAddress(address);\n    final String label = sdk.getLabelForAddress(address);\n    console.printf(\"read %s (0x%08x): 0x%04x%n\", label, address, value);\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Registers.java",
    "content": "/*\n * @(#)Registers.java 1.00 21/04/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"registers\" displays or modifies a state machine's\n * internal registers.\n */\npublic class Registers extends Command\n{\n  private static final String fullName = \"registers\";\n  private static final String singleLineDescription =\n    \"display or change internal registers of a state machine\";\n  private static final String notes =\n    \"If none of the register options is specified, the status of%n\"+\n    \"all those registers is displayed.%n\" +\n    \"Otherwise, for all specified register options, the corresponding%n\" +\n    \"register is set to the specified value.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optRegX =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'x', \"x\", null,\n                                   \"set value of register X\");\n  private static final CmdOptions.IntegerOptionDeclaration optRegY =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'y', \"y\", null,\n                                   \"set value of register Y\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'a', \"address\", null,\n                                   \"set value of PC to specified address\");\n  private static final CmdOptions.IntegerOptionDeclaration optIsr =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'i', \"isr\", null,\n                                   \"set value of ISR register\");\n  private static final CmdOptions.IntegerOptionDeclaration optIsrShiftCount =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'k', \"isrshiftcount\", null,\n                                   \"set value of ISR shift count register\");\n  private static final CmdOptions.IntegerOptionDeclaration optOsr =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'o', \"osr\", null,\n                                   \"set value of OSR register\");\n  private static final CmdOptions.IntegerOptionDeclaration optOsrShiftCount =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'q', \"osrshiftcount\", null,\n                                   \"set value of OSR shift count register\");\n\n  private final SDK sdk;\n\n  public Registers(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optRegX, optRegY, optAddress,\n              optIsr, optIsrShiftCount, optOsr, optOsrShiftCount });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n    }\n  }\n\n  private void displayRegisters(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final int addressRegX =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_REGX);\n    final int regXValue = sdk.readAddress(addressRegX);\n    final int addressRegY =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_REGY);\n    final int regYValue = sdk.readAddress(addressRegY);\n    final int addressPC =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_PC);\n    final int pcValue = sdk.readAddress(addressPC);\n    final int addressISR =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_ISR);\n    final int isrValue = sdk.readAddress(addressISR);\n    final int addressISRShiftCount =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_ISR_SHIFT_COUNT);\n    final int isrShiftCountValue = sdk.readAddress(addressISRShiftCount);\n    final int addressOSR =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_OSR);\n    final int osrValue = sdk.readAddress(addressOSR);\n    final int addressOSRShiftCount =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_OSR_SHIFT_COUNT);\n    final int osrShiftCountValue = sdk.readAddress(addressOSRShiftCount);\n    console.printf(\"(pio%d:sm%d) X=%08x, Y=%08x, PC=%02x%n\",\n                   pioNum, smNum, regXValue, regYValue, pcValue);\n    console.printf(\"           ISR=%08x, ISR_SHIFT_COUNT=%08x%n\",\n                   isrValue, isrShiftCountValue);\n    console.printf(\"           OSR=%08x, OSR_SHIFT_COUNT=%08x%n\",\n                   osrValue, osrShiftCountValue);\n  }\n\n  private void setEmuRegister(final int pioNum, final int smNum,\n                              final int value, final int digits,\n                              final PIOEmuRegisters.Regs register)\n    throws IOException\n  {\n    if (digits <= 0) {\n      throw new IllegalArgumentException(\"digits <= 0\");\n    }\n    final int address = PIOEmuRegisters.getSMAddress(pioNum, smNum, register);\n    sdk.writeAddress(address, value);\n    console.printf(\"(pio%d:sm%d) set %s to value 0x%0\" + digits + \"x%n\",\n                   pioNum, smNum, register, value);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optRegXValue = options.getValue(optRegX);\n    final Integer optRegYValue = options.getValue(optRegY);\n    final Integer optAddressValue = options.getValue(optAddress);\n    final Integer optIsrValue = options.getValue(optIsr);\n    final Integer optIsrShiftCountValue = options.getValue(optIsrShiftCount);\n    final Integer optOsrValue = options.getValue(optOsr);\n    final Integer optOsrShiftCountValue = options.getValue(optOsrShiftCount);\n    if ((optRegXValue == null) && (optRegYValue == null) &&\n        (optAddressValue == null) &&\n        (optIsrValue == null) && (optIsrShiftCountValue == null) &&\n        (optOsrValue == null) && (optOsrShiftCountValue == null)) {\n      displayRegisters(pioNum, smNum);\n    }\n    if (optRegXValue != null) {\n      setEmuRegister(pioNum, smNum, optRegXValue, 8,\n                     PIOEmuRegisters.Regs.SM0_REGX);\n    }\n    if (optRegYValue != null) {\n      setEmuRegister(pioNum, smNum, optRegYValue, 8,\n                     PIOEmuRegisters.Regs.SM0_REGY);\n    }\n    if (optAddressValue != null) {\n      setEmuRegister(pioNum, smNum,\n                     optAddressValue & (Constants.MEMORY_SIZE - 1), 2,\n                     PIOEmuRegisters.Regs.SM0_PC);\n    }\n    if (optIsrValue != null) {\n      setEmuRegister(pioNum, smNum, optIsrValue, 8,\n                     PIOEmuRegisters.Regs.SM0_ISR);\n    }\n    if (optIsrShiftCountValue != null) {\n      setEmuRegister(pioNum, smNum, optIsrShiftCountValue, 8,\n                     PIOEmuRegisters.Regs.SM0_ISR_SHIFT_COUNT);\n    }\n    if (optOsrValue != null) {\n      setEmuRegister(pioNum, smNum, optOsrValue, 8,\n                     PIOEmuRegisters.Regs.SM0_OSR);\n    }\n    if (optOsrShiftCountValue != null) {\n      setEmuRegister(pioNum, smNum, optOsrShiftCountValue, 8,\n                     PIOEmuRegisters.Regs.SM0_OSR_SHIFT_COUNT);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Reset.java",
    "content": "/*\n * @(#)Reset.java 1.00 21/03/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"reset\" initiates a full reset of the emulator's\n * complete internal state.\n */\npublic class Reset extends Command\n{\n  private static final String fullName = \"reset\";\n  private static final String singleLineDescription = \"emulator full reset\";\n\n  private final SDK sdk;\n\n  public Reset(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription);\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    sdk.reset();\n    console.println(\"(pio*:sm*) emulator successfully reset\");\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Save.java",
    "content": "/*\n * @(#)Save.java 1.00 21/04/05\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.time.Instant;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"save\" saves a selected range of a PIO's\n * instruction memory to a file.\n */\npublic class Save extends Command\n{\n  private static final String fullName = \"save\";\n  private static final String singleLineDescription =\n    \"save a selected range of a PIO's instruction memory to a file\";\n  private static final String notes =\n    \"The file is written as a text file, with each instruction%n\" +\n    \"added as a line consisting of its operation code represented%n\" +\n    \"as hexadecimal 32 bit integer value (without \\\"0x\\\" prefix).%n\" +\n    \"%n\" +\n    \"If the specified stop address is lower than start address, then%n\" +\n    \"the program is assumed to wrap from the highest memory address to%n\" +\n    \"the first memory address.  Any configuration of a SM specific wrap%n\" +\n    \"or wrap target is ignored.%n\" +\n    \"%n\" +\n    \"If the file is specified to be not relocatable, a proper%n\" +\n    \"\\\".origin\\\" directive will be added as a comment line.%n\" +\n    \"%n\" +\n    \"If a program name is provided, it will be added as a%n\" +\n    \"\\\".program\\\" directive in a separate comment line.%n\" +\n    \"%n\" +\n    \"Comment lines start with the hash symbol \\\"#\\\".\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optStart =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"start\", null,\n                                   \"first address (0x00…0x1f) of the program\");\n  private static final CmdOptions.IntegerOptionDeclaration optStop =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 's', \"stop\", null,\n                                   \"last address (0x00…0x1f, inclusive) of \"+\n                                   \"the program\");\n  private static final CmdOptions.StringOptionDeclaration optFile =\n    CmdOptions.createStringOption(\"PATH\", false, 'f', \"file\", null,\n                                  \"path of file to write\");\n  private static final CmdOptions.StringOptionDeclaration optName =\n    CmdOptions.createStringOption(\"NAME\", false, 'n', \"name\", null,\n                                  \"program name to be added as \\\".program\\\"\" +\n                                  \"directive\");\n  private static final CmdOptions.BooleanOptionDeclaration optOverWrite =\n    CmdOptions.createBooleanOption(false, 'o', \"overwrite\", false,\n                                   \"overwrite if file already exists\");\n  private static final CmdOptions.BooleanOptionDeclaration optRelocatable =\n    CmdOptions.createBooleanOption(false, 'r', \"relocatable\", true,\n                                   \"true, if the PIO program may be loaded \" +\n                                   \"anywhere into instruction memory\");\n\n  private final SDK sdk;\n\n  public Save(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optStart, optStop, optFile, optName,\n              optOverWrite, optRelocatable });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final Integer optPioValue = options.getValue(optPio);\n      if (optPioValue != null) {\n        final int pioNum = optPioValue;\n        if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n          throw new CmdOptions.\n            ParseException(\"PIO number must be either 0 or 1, if defined\");\n        }\n      }\n      if (!options.isDefined(optStart)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optStart);\n      }\n      if (!options.isDefined(optStop)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optStop);\n      }\n      if (!options.isDefined(optFile)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optFile);\n      }\n    }\n  }\n\n  private void writeProgram(final int pioNum,\n                            final int startAddress, final int stopAddress,\n                            final PrintWriter out, final String name,\n                            final boolean relocatable)\n    throws IOException\n  {\n    out.printf(\"# ; PIO Program Hexdump%n\");\n    out.printf(\"# ; automatically created on %s%n\", Instant.now());\n    out.printf(\"# ; by Monitor Control Program%n\");\n    out.printf(\"# ; %s%n\", Constants.getEmulatorIdAndVersionWithOs());\n    if (name != null) {\n      // TODO: Escape program name (\"\\r\", \"\\n\", \"\\\"\", …)?\n      out.printf(\"# .program %s%n\", name);\n    }\n    if (!relocatable) {\n      out.printf(\"# .origin %d%n\", startAddress);\n    }\n    int address = startAddress;\n    do {\n      final int instrAddress =\n        PIOEmuRegisters.getMemoryAddress(pioNum, address);\n      final int opCode = sdk.readAddress(instrAddress) & 0xffff;\n      out.printf(\"%04x%n\", opCode);\n      address = (address + 1) & (Constants.MEMORY_SIZE - 1);\n    } while (address != stopAddress);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int startAddress =\n      options.getValue(optStart) & (Constants.MEMORY_SIZE - 1);\n    final int stopAddress =\n      (options.getValue(optStop) + 1) & (Constants.MEMORY_SIZE - 1);\n    final String filePath = options.getValue(optFile);\n    final String name = options.getValue(optName);\n    final boolean overWrite = options.getValue(optOverWrite);\n    final boolean relocatable = options.getValue(optRelocatable);\n    final File file = new File(filePath);\n    if (file.exists() & !overWrite) {\n      console.println(\"file already exists: \" + filePath);\n      return false;\n    }\n    try {\n      final PrintWriter out = new PrintWriter(filePath);\n      writeProgram(pioNum, startAddress, stopAddress, out, name, relocatable);\n      out.close();\n      console.printf(\"(pio%d:sm*) saved 0x%02x instruction words to file %s%n\",\n                     pioNum, (stopAddress - startAddress) & 0x1f, filePath);\n      return true;\n    } catch (final IOException e) {\n      console.println(\"failed saving to file: \" + e.getMessage());\n      return false;\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Script.java",
    "content": "/*\n * @(#)Script.java 1.00 21/03/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.io.PrintStream;\nimport java.util.Map;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.CommandRegistry;\nimport org.soundpaint.rp2040pio.monitor.ScriptInfo;\nimport org.soundpaint.rp2040pio.sdk.Panic;\n\n/**\n * Monitor command \"script\" loads a monitor script from a file and\n * executes it.\n */\npublic class Script extends Command\n{\n  private static final String fullName = \"script\";\n  private static final String singleLineDescription =\n    \"load monitor script from file and execute it\";\n  private static final String notes =\n    \"By convention, monitor scripts files have \\\".mon\\\" file name suffix.%n\" +\n    \"They contain commands to be executed verbatim as if they were%n\" +\n    \"manually entered in exactly the same way.\" +\n    \"%n\" +\n    \"For safety reasons as well as for providing for future extensions,%n\" +\n    \"an additional flag \\\"+d\\\" is by default set to dry-run the script.%n\" +\n    \"To actually run the script, you need to explicitly spcify \\\"-d\\\" to%n\" +\n    \"override dry-run mode.%n\" +\n    \"%n\" +\n    \"Some built-in example scripts are available that can be listed with%n\" +\n    \"the \\\"-l\\\" option.  To execute a built-in script, use the \\\"-e\\\"%n\" +\n    \"option and pass to this option the script's name as shown in the%n\" +\n    \"list of available built-in scripts.%n\" +\n    \"For user-provided script files, use the \\\"-f\\\" option to specify the%n\" +\n    \"file path of the script, including the \\\".mon\\\" file name suffix.\";\n\n  private final CommandRegistry commands;\n\n  private static final CmdOptions.FlagOptionDeclaration optList =\n    CmdOptions.createFlagOption(false, 'l', \"list\", CmdOptions.Flag.OFF,\n                                \"list names of available example scripts\");\n  private static final CmdOptions.StringOptionDeclaration optShow =\n    CmdOptions.createStringOption(\"NAME\", false, 's', \"show\", null,\n                                  \"name of built-in example script to show\");\n  private static final CmdOptions.StringOptionDeclaration optExample =\n    CmdOptions.createStringOption(\"NAME\", false, 'e', \"example\", null,\n                                  \"name of built-in example script to execute\");\n  private static final CmdOptions.StringOptionDeclaration optFile =\n    CmdOptions.createStringOption(\"PATH\", false, 'f', \"file\", null,\n                                  \"path of monitor script file to execute\");\n  private static final CmdOptions.BooleanOptionDeclaration optDryRun =\n    CmdOptions.createBooleanOption(false, 'd', \"dry-run\", true,\n                                   \"dry-run the script commands rather than \" +\n                                   \"actually executing them\");\n\n  public Script(final PrintStream console, final CommandRegistry commands)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optList, optShow, optExample, optFile, optDryRun });\n    if (commands == null) {\n      throw new NullPointerException(\"commands\");\n    }\n    this.commands = commands;\n  }\n\n  private LineNumberReader getReaderForResourcePath(final String resourcePath)\n    throws IOException\n  {\n    final InputStream in = IOUtils.getStreamForResourcePath(resourcePath);\n    return new LineNumberReader(new InputStreamReader(in));\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    int count = 0;\n    if (optListValue) count++;\n    if (optShowValue != null) count++;\n    if (optExampleValue != null) count++;\n    if (optFileValue != null) count++;\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (count == 0) {\n        throw new CmdOptions.\n          ParseException(\"at least one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                         \"and \\\"-f\\\" must be specified\");\n      }\n    }\n    if (count > 1) {\n      throw new CmdOptions.\n        ParseException(\"at most one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                       \"and \\\"-f\\\" may be specified at the same time\");\n    }\n  }\n\n\n\n\n  private void listExampleScriptGroup(final Map<String, ScriptInfo>\n                                      scriptsGroupInfo,\n                                      final String groupName)\n  {\n    final String groupTitle = String.format(\"%s:\", groupName);\n    console.printf(\"(pio*:sm*) %s%n\", groupTitle);\n    for (final String scriptId : scriptsGroupInfo.keySet()) {\n      final ScriptInfo scriptInfo = scriptsGroupInfo.get(scriptId);\n      console.printf(\"(pio*:sm*)     %s%n\", scriptInfo.getScriptId());\n    }\n  }\n\n  private void listExampleScripts() throws IOException\n  {\n    final Map<String, Map<String, ScriptInfo>> scriptsInfo =\n      ScriptInfo.createScriptsInfo();\n    Map<String, ScriptInfo> defaultScriptsGroupInfo = null;\n    for (final String groupName : scriptsInfo.keySet()) {\n      final Map<String, ScriptInfo> scriptsGroupInfo =\n        scriptsInfo.get(groupName);\n      if (groupName != ScriptInfo.DEFAULT_GROUP_NAME) {\n        listExampleScriptGroup(scriptsGroupInfo, groupName);\n      } else {\n        // defer default group to end\n        defaultScriptsGroupInfo = scriptsGroupInfo;\n      }\n    }\n    if (defaultScriptsGroupInfo != null) {\n      listExampleScriptGroup(defaultScriptsGroupInfo,\n                             ScriptInfo.DEFAULT_GROUP_NAME);\n    } else {\n      throw new IOException(\"default script group not found\");\n    }\n  }\n\n  private boolean showScript(final LineNumberReader in, final String scriptId)\n    throws IOException\n  {\n    console.printf(\"(pio*:sm*) [script %s]%n\", scriptId);\n    while (true) {\n      final String line = in.readLine();\n      if (line == null) break;\n      console.printf(\"(pio*:sm*) %3d: %s%n\", in.getLineNumber(), line);\n    }\n    console.printf(\"(pio*:sm*) [end of script %s]%n\", scriptId);\n    return true;\n  }\n\n  private int executeScript(final LineNumberReader in,\n                            final String scriptId,\n                            final boolean dryRun,\n                            final boolean localEcho,\n                            final String prompt)\n  {\n    final String action = dryRun ? \"dry-running\" : \"running\";\n    console.printf(\"(pio*:sm*) %s script %s%n\", action, scriptId);\n    while (true) {\n      console.print(prompt);\n      try {\n        final String line = in.readLine();\n        if (line == null) break;\n        if (localEcho) console.println(line);\n        if (commands.parseAndExecute(line, dryRun)) break;\n      } catch (final Panic | IOException e) {\n        console.println(e.getMessage());\n        if (e instanceof Panic) {\n          console.printf(Command.panicNotes);\n          console.println();\n        }\n        return -1;\n      }\n    }\n    return 0;\n  }\n\n  private boolean executeScript(final LineNumberReader in,\n                                final String scriptId, final boolean dryRun)\n    throws IOException\n  {\n    final int exitStatus =\n      executeScript(in, scriptId, dryRun, true, \"script> \");\n    console.printf(\"(pio*:sm*) script %s exited with status %d%n\",\n                   scriptId, exitStatus);\n    return true;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    final boolean dryRun = options.getValue(optDryRun);\n    if (optListValue) {\n      listExampleScripts();\n    } else if (optShowValue != null) {\n      final String resourcePath =\n        String.format(\"/examples/%s.mon\", optShowValue);\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(resourcePath);\n      return showScript(reader, optShowValue);\n    } else if (optExampleValue != null) {\n      final String resourcePath =\n        String.format(\"/examples/%s.mon\", optExampleValue);\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(resourcePath);\n      return executeScript(reader, optExampleValue, dryRun);\n    } else if (optFileValue != null) {\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(optFileValue);\n      return executeScript(reader, optFileValue, dryRun);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/SideSet.java",
    "content": "/*\n * @(#)SideSet.java 1.00 21/04/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"side-set\" provides the same functionality like the\n * PIOASM directive \".side_set\".\n */\npublic class SideSet extends Command\n{\n  private static final String fullName = \"side-set\";\n  private static final String singleLineDescription =\n    \"display or control a state machine's side-set configuration\";\n  private static final String notes =\n    \"Options -p and -s select the state machine that this command%n\" +\n    \"applies to.  Default is PIO0 and SM0.%n\" +\n    \"%n\" +\n    \"If none of the options -c, -b, ±o, ±d is specified, the currently%n\" +\n    \"configured side-set of the selected state machine will be%n\" +\n    \"displayed.  If at least one of the options -c, -b, ±o, ±d is%n\" +\n    \"specified, the corresponding settings will be adjusted, while for%n\" +\n    \"those not specified the corresponding settings will keep unmodified.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optCount =\n    CmdOptions.createIntegerOption(\"COUNT\", false, 'c', \"count\", null,\n                                   \"number of side-set bits to be \" +\n                                   \"used (0…5)\");\n  private static final CmdOptions.IntegerOptionDeclaration optBase =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'b', \"base\", null,\n                                   \"base GPIO pin (0…31) number \"+\n                                   \"of side-set\");\n  protected static final CmdOptions.BooleanOptionDeclaration optOpt =\n    CmdOptions.createBooleanOption(false, 'o', \"opt\", null,\n                                   \"make side-set values optional for \" +\n                                   \"instructions\");\n  protected static final CmdOptions.BooleanOptionDeclaration optPinDirs =\n    CmdOptions.createBooleanOption(false, 'd', \"pindirs\", null,\n                                   \"apply side-set values to the PINDIRs and \" +\n                                   \"not the PINs\");\n\n  private final SDK sdk;\n\n  public SideSet(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optCount, optBase, optOpt, optPinDirs });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      final Integer optCountValue = options.getValue(optCount);\n      if (optCountValue != null) {\n        final int count = optCountValue;\n        if ((count < 0) || (count > 5)) {\n          throw new CmdOptions.\n            ParseException(\"count must be in the range 0…5\");\n        }\n      }\n      final Integer optBaseValue = options.getValue(optBase);\n      if (optBaseValue != null) {\n        final int base = optBaseValue;\n        if ((base < 0) || (base > 31)) {\n          throw new CmdOptions.\n            ParseException(\"base must be in the range 0…31\");\n        }\n      }\n    }\n  }\n\n  private void displaySideSet(final int pioNum, final int smNum,\n                              final SDK sdk)\n    throws IOException\n  {\n    final int pinCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    final int pinCtrl = sdk.readAddress(pinCtrlAddress);\n    final int count =\n      (pinCtrl & Constants.SM0_PINCTRL_SIDESET_COUNT_BITS) >>>\n      Constants.SM0_PINCTRL_SIDESET_COUNT_LSB;\n    final int base =\n      (pinCtrl & Constants.SM0_PINCTRL_SIDESET_BASE_BITS) >>>\n      Constants.SM0_PINCTRL_SIDESET_BASE_LSB;\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(execCtrlAddress);\n    final boolean opt =\n      ((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>>\n       Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0;\n    final boolean pinDirs =\n      ((execCtrl & Constants.SM0_EXECCTRL_SIDE_PINDIR_BITS) >>>\n       Constants.SM0_EXECCTRL_SIDE_PINDIR_LSB) != 0x0;\n    final int nettoCount = opt ? count - 1 : count;\n    console.printf(\"(pio%d:sm%d) count=%d, base=%d, opt=%s, pindirs=%s%n\",\n                   pioNum, smNum, nettoCount, base, opt, pinDirs);\n  }\n\n  private void setSideSetCount(final int pioNum, final int smNum,\n                               final SDK sdk, final int nettoCount)\n    throws IOException\n  {\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(execCtrlAddress);\n    final boolean opt =\n      ((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>>\n       Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0;\n    if (opt && (nettoCount == 5)) {\n      console.printf(\"(pio%d:sm%d) ERROR: can not set count to 5, \" +\n                     \"since set side-set opt is set%n\",\n                     pioNum, smNum);\n    } else {\n      final int count = nettoCount + (opt ? 1 : 0);\n      final int pinCtrlAddress =\n        PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n      final int writeMask = Constants.SM0_PINCTRL_SIDESET_COUNT_BITS;\n      final int values = count << Constants.SM0_PINCTRL_SIDESET_COUNT_LSB;\n      sdk.hwWriteMasked(pinCtrlAddress, values, writeMask);\n      console.printf(\"(pio%d:sm%d) set side-set count to %d%n\",\n                     pioNum, smNum, nettoCount);\n    }\n  }\n\n  private void setSideSetBase(final int pioNum, final int smNum,\n                              final SDK sdk, final int base)\n    throws IOException\n  {\n    final int pinCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    final int writeMask = Constants.SM0_PINCTRL_SIDESET_BASE_BITS;\n    final int values =\n      base << Constants.SM0_PINCTRL_SIDESET_BASE_LSB;\n    sdk.hwWriteMasked(pinCtrlAddress, values, writeMask);\n    console.printf(\"(pio%d:sm%d) set side-set base GPIO pin to %d%n\",\n                   pioNum, smNum, base);\n  }\n\n  private void setSideSetOpt(final int pioNum, final int smNum,\n                             final SDK sdk, final boolean opt)\n    throws IOException\n  {\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(execCtrlAddress);\n    if (opt !=\n        (((execCtrl & Constants.SM0_EXECCTRL_SIDE_EN_BITS) >>>\n          Constants.SM0_EXECCTRL_SIDE_EN_LSB) != 0x0)) {\n      /*\n       * Note: From this command's perspective, it is a RP2040's\n       * design flaw that the SMx_PINCTRL_SIDESET_COUNT is inclusive\n       * of the enable bit.  When the user changes side-set opt only,\n       * they do not expect to change the available pin bits.  For\n       * example, pioasm will allocate *three* side-set bits when\n       * declaring \".side_set 2 opt\", but only two, when the \"opt\" is\n       * dropped.  Consequently, when changing side-set opt *only*, we\n       * have also have to update the number of side-set bits.\n       *\n       * TODO: Need to lock this block (and any other writer to\n       * side-set configuration) as critical section to avoid\n       * corruption, if there are concurrent writers, such as another\n       * Monitor instance.\n       */\n      final int pinCtrlAddress =\n        PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n      final int pinCtrl = sdk.readAddress(pinCtrlAddress);\n      final int count =\n        (pinCtrl & Constants.SM0_PINCTRL_SIDESET_COUNT_BITS) >>>\n        Constants.SM0_PINCTRL_SIDESET_COUNT_LSB;\n      if (opt && (count == 5)) {\n        console.printf(\"(pio%d:sm%d) ERROR: can not set opt, since side-set \" +\n                       \"count is set to 5.%n\",\n                       pioNum, smNum);\n      } else {\n        final int optBits =\n          opt ? 0x1 << Constants.SM0_EXECCTRL_SIDE_EN_LSB : 0x0;\n        sdk.hwWriteMasked(execCtrlAddress, optBits,\n                          Constants.SM0_EXECCTRL_SIDE_EN_BITS);\n        final int newCount = count + (opt ? 1 : (count > 0 ? - 1 : 0));\n        final int countBits =\n          newCount << Constants.SM0_PINCTRL_SIDESET_COUNT_LSB;\n        sdk.hwWriteMasked(pinCtrlAddress, countBits,\n                          Constants.SM0_PINCTRL_SIDESET_COUNT_BITS);\n        console.printf(\"(pio%d:sm%d) set side-set opt=%s%n\",\n                       pioNum, smNum, opt);\n      }\n    } else {\n      console.printf(\"(pio%d:sm%d) set side-set opt=%s%n\",\n                     pioNum, smNum, opt);\n    }\n  }\n\n  private void setSideSetPinDirs(final int pioNum, final int smNum,\n                                 final SDK sdk, final boolean pinDirs)\n    throws IOException\n  {\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int writeMask = Constants.SM0_EXECCTRL_SIDE_PINDIR_BITS;\n    final int values =\n      pinDirs ? 0x1 << Constants.SM0_EXECCTRL_SIDE_PINDIR_LSB : 0x0;\n    sdk.hwWriteMasked(execCtrlAddress, values, writeMask);\n    console.printf(\"(pio%d:sm%d) set side-set pindirs=%s%n\",\n                   pioNum, smNum, pinDirs);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optCountValue = options.getValue(optCount);\n    final Integer optBaseValue = options.getValue(optBase);\n    final Boolean optOptValue = options.getValue(optOpt);\n    final Boolean optPinDirsValue = options.getValue(optPinDirs);\n    if ((optCountValue == null) && (optBaseValue == null) &&\n        (optOptValue == null) && (optPinDirsValue == null)) {\n      displaySideSet(pioNum, smNum, sdk);\n    } else {\n      if (optCountValue != null) {\n        setSideSetCount(pioNum, smNum, sdk, optCountValue);\n      }\n      if (optBaseValue != null) {\n        setSideSetBase(pioNum, smNum, sdk, optBaseValue);\n      }\n      if (optOptValue != null) {\n        setSideSetOpt(pioNum, smNum, sdk, optOptValue);\n      }\n      if (optPinDirsValue != null) {\n        setSideSetPinDirs(pioNum, smNum, sdk, optPinDirsValue);\n      }\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Sm.java",
    "content": "/*\n * @(#)Sm.java 1.00 21/04/01\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Decoder;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"sm\" enables or disables a PIO's state machine\n * or shows if it is enabled.\n */\npublic class Sm extends Command\n{\n  private static final String fullName = \"sm\";\n  private static final String singleLineDescription =\n    \"enable or disable or restart state machine(s) or show if enabled\";\n  private static final String notes =\n    \"Use options \\\"-p\\\" and \\\"-s\\\" to select a state machine.%n\" +\n    \"Enable or disable the selected state machine with option%n\" +\n    \"\\\"+e\\\" or \\\"-e\\\", respectively.  Restart the selected%n\" +\n    \"state machine with option \\\"+r\\\".%n\" +\n    \"If none of options \\\"+e\\\", \\\"-e\\\", \\\"-r\\\" is specified,%n\" +\n    \"show if the state machine is currently enabled.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.BooleanOptionDeclaration optEnable =\n    CmdOptions.createBooleanOption(false, 'e', \"enable\", null,\n                                   \"enable or disable the selected \" +\n                                   \"state machine\");\n  private static final CmdOptions.FlagOptionDeclaration optRestart =\n    CmdOptions.createFlagOption(false, 'r', \"restart\", CmdOptions.Flag.OFF,\n                                \"restart the selected state machine\");\n\n  private final SDK sdk;\n\n  public Sm(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optEnable, optRestart });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n    }\n  }\n\n  private void displaySmStatus(final int pioNum, final int smNum)\n    throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final boolean enabled = pioSdk.smGetEnabled(smNum);\n    console.printf(\"(pio%d:sm%d) %s%n\", pioNum, smNum,\n                   enabled ? \"enabled\" : \"disabled\");\n  }\n\n  private void setEnableStatus(final int pioNum, final int smNum,\n                               final boolean enable)\n    throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.smSetEnabled(smNum, enable);\n    console.printf(\"(pio%d:sm%d) set %s%n\", pioNum, smNum,\n                   enable ? \"enabled\" : \"disabled\");\n  }\n\n  private void restart(final int pioNum, final int smNum) throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.smRestart(smNum);\n    console.printf(\"(pio%d:sm%d) restarted%n\", pioNum, smNum);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Boolean optEnableValue = options.getValue(optEnable);\n    final boolean optRestartValue = options.getValue(optRestart).isOn();\n    final boolean haveModOp = (optEnableValue != null) || optRestartValue;\n    if (!haveModOp) {\n      displaySmStatus(pioNum, smNum);\n    }\n    if (optEnableValue != null) {\n      setEnableStatus(pioNum, smNum, optEnableValue);\n    }\n    if (optRestartValue) {\n      restart(pioNum, smNum);\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Trace.java",
    "content": "/*\n * @(#)Trace.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Direction;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PinState;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.MonitorUtils;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"trace\" lets the emulator execute clock cycles step\n * by step.\n */\npublic class Trace extends Command\n{\n  private static final String fullName = \"trace\";\n  private static final String singleLineDescription =\n    \"trace program by performing a number of clock cycles\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optCycles =\n    CmdOptions.createIntegerOption(\"COUNT\", false, 'c', \"cycles\", 1,\n                                   \"number of cycles to apply\");\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", null,\n                                   \"limit options -l and -i to PIO number, \" +\n                                   \"either 0 or 1 or both, if undefined\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", null,\n                                   \"limit option -i to SM number, one of \" +\n                                   \"0, 1, 2 or 3, or all, if undefined\");\n  private static final CmdOptions.FlagOptionDeclaration optPc =\n    CmdOptions.createFlagOption(false, 'i', \"show-instr\", CmdOptions.Flag.OFF,\n                                \"show address of instruction pointer (aka \" +\n                                \"PC reg) for selected SMs of selected PIOs\");\n  private static final CmdOptions.FlagOptionDeclaration optGpio =\n    CmdOptions.createFlagOption(false, 'g', \"show-gpio\", CmdOptions.Flag.OFF,\n                                \"show status of (global) GPIO pins\");\n  private static final CmdOptions.FlagOptionDeclaration optPioGpio =\n    CmdOptions.createFlagOption(false, 'l', \"show-local-gpio\",\n                                CmdOptions.Flag.OFF,\n                                \"show status of (local PIO's) GPIO pins\");\n  private static final CmdOptions.FlagOptionDeclaration optBefore =\n    CmdOptions.createFlagOption(false, null, \"before\", CmdOptions.Flag.OFF,\n                                \"when displaying global GPIO status, show \" +\n                                \"status before rather than after override\");\n  private static final CmdOptions.IntegerOptionDeclaration optWait =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'w', \"wait\", 0,\n                                   \"before each cycle, sleep for the \" +\n                                   \"specified time [ms] or until interrupted\");\n\n  private static final int[][] addressPioSmPc = {{\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM0_PC),\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM1_PC),\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM2_PC),\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM3_PC)\n    }, {\n      PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM0_PC),\n      PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM1_PC),\n      PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM2_PC),\n      PIOEmuRegisters.getAddress(1, PIOEmuRegisters.Regs.SM3_PC)\n    }};\n\n  private final SDK sdk;\n\n  public Trace(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optCycles, optPc,\n              optPioGpio, optGpio, optBefore, optWait });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final Integer optPioValue = options.getValue(optPio);\n    if (optPioValue != null) {\n      final int pioNum = optPioValue;\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1, if defined\");\n      }\n    }\n    final Integer optSmValue = options.getValue(optSm);\n    if (optSmValue != null) {\n      final int smNum = optSmValue;\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3, if defined\");\n      }\n    }\n    final int cycles = options.getValue(optCycles);\n    if (cycles < 0) {\n      throw new CmdOptions.\n        ParseException(\"COUNT must be a non-negative value\", optCycles);\n    }\n    final int wait = options.getValue(optWait);\n    if (wait < 0) {\n      throw new CmdOptions.\n        ParseException(\"NUMBER must be a non-negative value\", optWait);\n    }\n    if (options.getValue(optBefore) == CmdOptions.Flag.ON) {\n      if (!options.isDefined(optGpio)) {\n        throw new CmdOptions.\n          ParseException(\"option \\\"before\\\" may be specified when option \"+\n                         \"option \\\"-g\\\" is specified\");\n      }\n    }\n  }\n\n  private void displayPcValues(final int pioNumFirst,\n                               final int pioNumLast,\n                               final int smNumFirst,\n                               final int smNumLast)\n    throws IOException\n  {\n    for (int pioNum = pioNumFirst; pioNum <= pioNumLast; pioNum++) {\n      for (int smNum = smNumFirst; smNum <= smNumLast; smNum++) {\n        console.printf(\"(pio%d:sm%d) PC=%02x%n\", pioNum, smNum,\n                       sdk.readAddress(addressPioSmPc[pioNum][smNum]));\n      }\n    }\n  }\n\n  private void displayGpioValues(final boolean before) throws IOException\n  {\n    final GPIOSDK.Override override =\n      before ? GPIOSDK.Override.BEFORE : GPIOSDK.Override.AFTER;\n    final String gpioDisplay = MonitorUtils.gpioDisplay(sdk, override);\n    console.printf(gpioDisplay);\n  }\n\n  private void displayGpioValues(final int pioNumFirst, final int pioNumLast)\n    throws IOException\n  {\n    for (int pioNum = pioNumFirst; pioNum <= pioNumLast; pioNum++) {\n      console.printf(MonitorUtils.gpioDisplay(sdk, pioNum));\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final Integer optPioValue = options.getValue(optPio);\n    final Integer optSmValue = options.getValue(optSm);\n    final int pioNumFirst = optPioValue != null ? optPioValue : 0;\n    final int pioNumLast =\n      optPioValue != null ? optPioValue : Constants.PIO_NUM - 1;\n    final int smNumFirst = optSmValue != null ? optSmValue : 0;\n    final int smNumLast =\n      optSmValue != null ? optSmValue : Constants.SM_COUNT - 1;\n    final int cycles = options.getValue(optCycles);\n    final int wait = options.getValue(optWait);\n    final int wait0 = wait / 2;\n    final int wait1 = wait - wait0;\n    for (int i = 0; i < cycles; i++) {\n      if (wait0 > 0) {\n        try {\n          Thread.sleep(wait0);\n        } catch (final InterruptedException e) {\n          console.printf(\"(pio*:sm*) Interrupted: %s%n\", e.getMessage());\n        }\n      }\n      sdk.triggerCyclePhase0(true);\n      if (wait1 > 0) {\n        try {\n          Thread.sleep(wait1);\n        } catch (final InterruptedException e) {\n          console.printf(\"(pio*:sm*) Interrupted: %s%n\", e.getMessage());\n        }\n      }\n      sdk.triggerCyclePhase1(true);\n      if (options.getValue(optPc).isOn()) {\n        displayPcValues(pioNumFirst, pioNumLast, smNumFirst, smNumLast);\n      }\n      if (options.getValue(optPioGpio).isOn()) {\n        displayGpioValues(pioNumFirst, pioNumLast);\n      }\n      if (options.getValue(optGpio).isOn()) {\n        final boolean before = options.getValue(optBefore) == CmdOptions.Flag.ON;\n        displayGpioValues(before);\n      }\n    }\n    console.println(cycles + \" clock cycle\" + (cycles != 1 ? \"s\" : \"\") +\n                    \" executed.\");\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Unassemble.java",
    "content": "/*\n * @(#)Unassemble.java 1.00 21/03/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Decoder;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"unassemble\" displays instructions of a PIO's\n * instruction memory in a human-readable form.\n */\npublic class Unassemble extends Command\n{\n  private static final String fullName = \"unassemble\";\n  private static final String singleLineDescription =\n    \"unassemble program memory\";\n  private static final String notes =\n    \"Memory locations marked as allocated are prefixed with leading 'X'.%n\" +\n    \"%n\" +\n    \"Note that tracking memory allocation is not a feature of the%n\" +\n    \"RP2040, but local to this monitor instance, just to avoid%n\" +\n    \"accidentally overwriting your own PIO programs.  Other applications%n\" +\n    \"that concurrently access the RP2040 will therefore ignore%n\" +\n    \"this instance's allocation tracking and may arbitrarily%n\" +\n    \"overwrite allocated PIO memory, using their own allocation scheme.%n\" +\n    \"%n\" +\n    \"Note that the same PIO program may unassemble to differently%n\" +\n    \"displayed instructions for different state machines, since%n\" +\n    \"some settings specific to a particular state machine, such as%n\" +\n    \"side-set count, will affect interpretation of op-codes.%n\" +\n    \"Therefore, the unassemble command supports the \\\"sm\\\" argument%n\" +\n    \"for displaying the instructions as interpreted by the selected%n\" +\n    \"state machine, according to its current settings.\";\n  private static final String lockedSymbol = \"🔒\";\n  private static final String unlockedSymbol = \"  \";\n  private static final String wrapSymbol = \"← \";\n  private static final String wrapTargetSymbol = \"→ \";\n  private static final String selfWrapSymbol = \"↔ \";\n  private static final String noWrapSymbol = \"  \";\n  private static final String breakPointSymbol = \"🛑\";\n  private static final String noBreakPointSymbol = \"  \";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optStart =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", 0,\n                                   \"start address (0x00…0x1f)\");\n  private static final CmdOptions.IntegerOptionDeclaration optCount =\n    CmdOptions.createIntegerOption(\"COUNT\", false, 'c', \"count\",\n                                   Constants.MEMORY_SIZE,\n                                   \"number of instructions to unassemble\");\n\n  private final SDK sdk;\n\n  public Unassemble(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optStart, optCount });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final int count = options.getValue(optCount);\n    if (count == 0) return true;\n    final int startAddress =\n      options.getValue(optStart) & (Constants.MEMORY_SIZE - 1);\n    final int stopAddress =\n      (startAddress + count) & (Constants.MEMORY_SIZE - 1);\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final int addressAddr =\n      PIORegisters.getSMAddress(pioNum, smNum,\n                                PIORegisters.Regs.SM0_ADDR);\n    final int addrValue =\n      sdk.readAddress(addressAddr) & (Constants.MEMORY_SIZE - 1);\n    final int memoryAllocation = pioSdk.getMemoryAllocation();\n    final int addressExecCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(addressExecCtrl);\n    final int wrap =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_TOP_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_TOP_LSB;\n    final int wrapTarget =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n    final int addressBreakPoints =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_BREAKPOINTS);\n    final int breakPoints = sdk.readAddress(addressBreakPoints);\n    int address = startAddress;\n    do {\n      final boolean isCurrentAddr = address == addrValue;\n      final PIOSDK.InstructionInfo instructionInfo =\n        pioSdk.getMemoryInstruction(smNum, address, true, true);\n      final boolean isAllocated = ((memoryAllocation >>> address) & 0x1) != 0x0;\n      final boolean isWrap = address == wrap;\n      final boolean isWrapTarget = address == wrapTarget;\n      final String displayWrap =\n        isWrap ?\n        (isWrapTarget ? selfWrapSymbol : wrapSymbol) :\n        (isWrapTarget ? wrapTargetSymbol : noWrapSymbol);\n      final boolean isBreakPoint = ((breakPoints >>> address) & 0x1) != 0x0;\n      if (isCurrentAddr) {\n        console.printf(\"\\u001b[38;5;196m\");\n      }\n      console.printf(\"(pio%d:sm%d) %s %s  %s%s%n\",\n                     pioNum, smNum,\n                     (isBreakPoint ? breakPointSymbol : noBreakPointSymbol),\n                     (isAllocated ? lockedSymbol : unlockedSymbol),\n                     displayWrap,\n                     instructionInfo.getToolTipText());\n      if (isCurrentAddr) {\n        console.printf(\"\\u001b[0m\");\n      }\n      address = (address + 1) & (Constants.MEMORY_SIZE - 1);\n    } while (address != stopAddress);\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Unload.java",
    "content": "/*\n * @(#)Unload.java 1.00 21/04/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.LineNumberReader;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.monitor.MonitorUtils;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"unload\" removes a program from a PIO's memory.\n */\npublic class Unload extends Command\n{\n  private static final String fullName = \"unload\";\n  private static final String singleLineDescription =\n    \"zero PIO memory area for the specified program and unmark it as allocated\";\n  private static final String notes =\n    \"The \\\"unload\\\" command first reads in the specified hex dump in order%n\" +\n    \"to determine the program length of the corresponding PIO program.%n\" +\n    \"Then, the identified instruction memory area that is associated with%n\" +\n    \"the PIO program in the specified PIO will be zeroed, and any memory%n\" +\n    \"allocation marks found in this memory area will be removed.%n\" +\n    \"%n\" +\n    \"Built-in example hex dumps are available that can be listed with%n\" +\n    \"the \\\"-l\\\" option.  To select any of the example hex dumps, use the%n\" +\n    \"\\\"-e\\\" option and pass to this option the hex dump's name as shown%n\" +\n    \"in the list of available built-in hex dumps.  To view a built-in hex%n\" +\n    \"dump prior to unloading it, use the \\\"-s\\\" option.%n\" +\n    \"For user-provided hex dumps, use the \\\"-f\\\" option to specify the%n\" +\n    \"file path of the hex dump, including the \\\".hex\\\" file name suffix.\" +\n    \"%n\" +\n    \"Note that tracking memory allocation is not a feature of the%n\" +\n    \"RP2040, but local to this monitor instance, just to avoid%n\" +\n    \"accidentally overwriting your own PIO programs.  Other applications%n\" +\n    \"that concurrently access the RP2040 will therefore ignore%n\" +\n    \"this instance's allocation tracking and may arbitrarily%n\" +\n    \"overwrite allocated PIO memory, using their own allocation scheme.%n\" +\n    \"%n\" +\n    \"For information about the expected file format, enter the command%n\" +\n    \"\\\"load -h\\\" to view the help information of the \\\"load\\\" command.\";\n\n  private final SDK sdk;\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.FlagOptionDeclaration optList =\n    CmdOptions.createFlagOption(false, 'l', \"list\", CmdOptions.Flag.OFF,\n                                \"list names of available example hex dumps\");\n  private static final CmdOptions.StringOptionDeclaration optShow =\n    CmdOptions.createStringOption(\"NAME\", false, 's', \"show\", null,\n                                  \"name of example hex dump to show\");\n  private static final CmdOptions.StringOptionDeclaration optExample =\n    CmdOptions.createStringOption(\"NAME\", false, 'e', \"example\", null,\n                                  \"name of example hex dump to unload\");\n  private static final CmdOptions.StringOptionDeclaration optFile =\n    CmdOptions.createStringOption(\"STRING\", false, 'f', \"file\", null,\n                                  \"path of hex dump file to unload\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"start address (0x00…0x1f) of the area \" +\n                                   \"to free\");\n\n  public Unload(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optList, optShow, optExample, optFile, optAddress });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int pioNum = options.getValue(optPio);\n    if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n      throw new CmdOptions.\n        ParseException(\"PIO number must be either 0 or 1\");\n    }\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    int count = 0;\n    if (optListValue) count++;\n    if (optShowValue != null) count++;\n    if (optExampleValue != null) count++;\n    if (optFileValue != null) count++;\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (count == 0) {\n        throw new CmdOptions.\n          ParseException(\"at least one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                         \"and \\\"-f\\\" must be specified\");\n      }\n    }\n    if (count > 1) {\n      throw new CmdOptions.\n        ParseException(\"at most one of options \\\"-l\\\", \\\"-s\\\", \\\"-e\\\" \" +\n                       \"and \\\"-f\\\" may be specified at the same time\");\n    }\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (options.isDefined(optExample) ||\n          options.isDefined(optFile)) {\n        if (!options.isDefined(optAddress)) {\n          throw new CmdOptions.\n            ParseException(\"option not specified: \" + optAddress);\n        }\n      }\n    }\n  }\n\n  private boolean unloadHexDump(final int pioNum,\n                                final BufferedReader reader,\n                                final String hexDumpId,\n                                final int loadedOffset)\n    throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    pioSdk.removeProgram(hexDumpId, reader, loadedOffset);\n    console.printf(\"removed program %s from PIO %d, address 0x%02x%n\",\n                   hexDumpId, pioNum, loadedOffset);\n    return true;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final boolean optListValue =\n      options.getValue(optList) == CmdOptions.Flag.ON;\n    final String optShowValue = options.getValue(optShow);\n    final String optExampleValue = options.getValue(optExample);\n    final String optFileValue = options.getValue(optFile);\n    final Integer optAddressValue = options.getValue(optAddress);\n    if (optListValue) {\n      return MonitorUtils.listExampleHexDumps(console);\n    } else if (optShowValue != null) {\n      return MonitorUtils.showExampleHexDump(console, optShowValue);\n    } else if (optExampleValue != null) {\n      final String resourcePath =\n        String.format(\"/examples/%s.hex\", optExampleValue);\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(resourcePath);\n      return unloadHexDump(pioNum, reader, optExampleValue, optAddressValue);\n    } else if (optFileValue != null) {\n      final LineNumberReader reader =\n        IOUtils.getReaderForResourcePath(optFileValue);\n      return unloadHexDump(pioNum, reader, optFileValue, optAddressValue);\n    }\n    return false;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Version.java",
    "content": "/*\n * @(#)Version.java 1.00 21/03/29\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"version\" prints the emulator's version identifier.\n */\npublic class Version extends Command\n{\n  private static final String fullName = \"version\";\n  private static final String singleLineDescription = \"print emulator version\";\n\n  private final SDK sdk;\n  private final String appFullName;\n\n  public Version(final PrintStream console, final SDK sdk,\n                 final String appFullName)\n  {\n    super(console, fullName, singleLineDescription);\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n    this.appFullName = appFullName;\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    console.printf(\"%s%n%s%n%s%n\",\n                   appFullName,\n                   sdk.getEmulatorInfo(),\n                   Constants.getCmdLineCopyrightNotice());\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Wait.java",
    "content": "/*\n * @(#)Wait.java 1.00 21/03/30\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"wait\" observes a register's bits and will not\n * return until the register's value matches an expected bit pattern,\n * or when a timeout has occurred.\n *\n * TODO: There is currently no way to cancel an ongoing wait command\n * other than killing the monitor application.\n */\npublic class Wait extends Command\n{\n  private static final String fullName = \"wait\";\n  private static final String singleLineDescription =\n    \"wait for a register's bits to match an expected value\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"address (0x00000000…0xffffffff) of the \" +\n                                   \"register to observe\");\n  private static final CmdOptions.IntegerOptionDeclaration optExpectedValue =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'v', \"value\", null,\n                                   \"expected value to match\");\n  private static final CmdOptions.IntegerOptionDeclaration optMask =\n    CmdOptions.createIntegerOption(\"MASK\", false, 'm', \"mask\", 0xffffffff,\n                                   \"bit mask to select bits to match\");\n  private static final CmdOptions.IntegerOptionDeclaration optCycles =\n    CmdOptions.createIntegerOption(\"COUNT\", false, 'c', \"cycles\", 0,\n                                   \"timeout after <COUNT> cycles or no timeout, if 0\");\n  private static final CmdOptions.IntegerOptionDeclaration optTime =\n    CmdOptions.createIntegerOption(\"COUNT\", false, 't', \"time\", 100000,\n                                   \"timeout after <COUNT> millis or no timeout, if 0\");\n\n  private final SDK sdk;\n\n  public Wait(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[] {\n            optAddress, optExpectedValue, optMask, optCycles, optTime });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (!options.isDefined(optAddress)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified\", optAddress);\n      }\n      if (!options.isDefined(optExpectedValue)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified\", optExpectedValue);\n      }\n    }\n    final int cycles = options.getValue(optCycles);\n    if (cycles < 0) {\n      throw new CmdOptions.\n        ParseException(\"COUNT must be a non-negative value\", optCycles);\n    }\n    final int time = options.getValue(optTime);\n    if (time < 0) {\n      throw new CmdOptions.\n        ParseException(\"COUNT must be a non-negative value\", optTime);\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int address = options.getValue(optAddress);\n    final int expectedValue = options.getValue(optExpectedValue);\n    final boolean validAddress = sdk.providesAddress(address);\n    if (!validAddress) {\n      final String message =\n        String.format(\"wait on unsupported address: 0x%08x\", address);\n      throw new IOException(message);\n    }\n    final int mask = options.getValue(optMask);\n    final int cycles = options.getValue(optCycles);\n    final int time = options.getValue(optTime);\n    final int result = sdk.wait(address, expectedValue, mask, cycles, time);\n    console.printf(\"wait on 0x%08x for 0x%08x returned 0x%08x%n\",\n                   address, expectedValue, result);\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Wrap.java",
    "content": "/*\n * @(#)Wrap.java 1.00 21/04/04\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"wrap\" provides functionality comparable to the\n * PIOASM directives \".wrap_target\" and \".wrap\".\n */\npublic class Wrap extends Command\n{\n  private static final String fullName = \"wrap\";\n  private static final String singleLineDescription =\n    \"display or control a state machine's wrap and wrap target configuration\";\n  private static final String notes =\n    \"Options -p and -s select the state machine that this command%n\" +\n    \"applies to.  Default is PIO0 and SM0.%n\" +\n    \"%n\" +\n    \"If none of the options -w, -t is specified, the currently%n\" +\n    \"configured wrap and wrap target of the selected state machine will be%n\" +\n    \"displayed.  If at least one of the options -w, -t is%n\" +\n    \"specified, the corresponding settings will be adjusted, while for%n\" +\n    \"those not specified the corresponding settings will keep unmodified.\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optPio =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 'p', \"pio\", 0,\n                                   \"PIO number, either 0 or 1\");\n  private static final CmdOptions.IntegerOptionDeclaration optSm =\n    CmdOptions.createIntegerOption(\"NUMBER\", false, 's', \"sm\", 0,\n                                   \"SM number, one of 0, 1, 2 or 3\");\n  private static final CmdOptions.IntegerOptionDeclaration optWrap =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'w', \"wrap\", null,\n                                   \"wrap (WRAP_TOP) address (0x00…0x1f)\");\n  private static final CmdOptions.IntegerOptionDeclaration optWrapTarget =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 't', \"target\", null,\n                                   \"wrap target (WRAP_BOTTOM) address \" +\n                                   \"(0x00…0x1f)\");\n\n  private final SDK sdk;\n\n  public Wrap(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription, notes,\n          new CmdOptions.OptionDeclaration<?>[]\n          { optPio, optSm, optWrap, optWrapTarget });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      final int pioNum = options.getValue(optPio);\n      if ((pioNum < 0) || (pioNum > Constants.PIO_NUM - 1)) {\n        throw new CmdOptions.\n          ParseException(\"PIO number must be either 0 or 1\");\n      }\n      final int smNum = options.getValue(optSm);\n      if ((smNum < 0) || (smNum > Constants.SM_COUNT - 1)) {\n        throw new CmdOptions.\n          ParseException(\"SM number must be one of 0, 1, 2 or 3\");\n      }\n      final Integer optWrapValue = options.getValue(optWrap);\n      if (optWrapValue != null) {\n        final int wrap = optWrapValue;\n        if ((wrap < 0) || (wrap > Constants.MEMORY_SIZE - 1)) {\n          final String message =\n            String.format(\"wrap address must be in the range 0x00…0x%02x\",\n                          Constants.MEMORY_SIZE - 1);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n      final Integer optWrapTargetValue = options.getValue(optWrapTarget);\n      if (optWrapTargetValue != null) {\n        final int wrapTarget = optWrapTargetValue;\n        if ((wrapTarget < 0) || (wrapTarget > Constants.MEMORY_SIZE - 1)) {\n          final String message =\n            String.format(\"wrap target address must be in the range \" +\n                          \"0x00…0x%02x\",\n                          Constants.MEMORY_SIZE - 1);\n          throw new CmdOptions.ParseException(message);\n        }\n      }\n    }\n  }\n\n  private void displayWrap(final int pioNum, final int smNum,\n                           final SDK sdk)\n    throws IOException\n  {\n    final int execCtrlAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(execCtrlAddress);\n    final int wrap =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_TOP_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_TOP_LSB;\n    final int wrapTarget =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n    console.printf(\"(pio%d:sm%d) wrap=%d, wrap_target=%d%n\",\n                   pioNum, smNum, wrap, wrapTarget);\n  }\n\n  private void setWrap(final int pioNum, final int smNum,\n                       final SDK sdk, final int wrap)\n    throws IOException\n  {\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int writeMask = Constants.SM0_EXECCTRL_WRAP_TOP_BITS;\n    final int values = wrap << Constants.SM0_EXECCTRL_WRAP_TOP_LSB;\n    sdk.hwWriteMasked(address, values, writeMask);\n    console.printf(\"(pio%d:sm%d) set wrap=%d%n\", pioNum, smNum, wrap);\n  }\n\n  private void setWrapTarget(final int pioNum, final int smNum,\n                             final SDK sdk, final int wrapTarget)\n    throws IOException\n  {\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int writeMask = Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS;\n    final int values = wrapTarget << Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n    sdk.hwWriteMasked(address, values, writeMask);\n    console.printf(\"(pio%d:sm%d) set wrap_target=%d%n\",\n                   pioNum, smNum, wrapTarget);\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int pioNum = options.getValue(optPio);\n    final int smNum = options.getValue(optSm);\n    final Integer optWrapValue = options.getValue(optWrap);\n    final Integer optWrapTargetValue = options.getValue(optWrapTarget);\n    if ((optWrapValue == null) && (optWrapTargetValue == null)) {\n      displayWrap(pioNum, smNum, sdk);\n    } else {\n      if (optWrapValue != null) {\n        setWrap(pioNum, smNum, sdk, optWrapValue);\n      }\n      if (optWrapTargetValue != null) {\n        setWrapTarget(pioNum, smNum, sdk, optWrapTargetValue);\n      }\n    }\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/monitor/commands/Write.java",
    "content": "/*\n * @(#)Write.java 1.00 21/03/30\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.monitor.commands;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.monitor.Command;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Monitor command \"write\" provides low-level write access to a\n * register.\n */\npublic class Write extends Command\n{\n  private static final String fullName = \"write\";\n  private static final String singleLineDescription =\n    \"low-level write access to a register\";\n\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"address (0x00000000…0xffffffff) of the \" +\n                                   \"register to access\");\n  private static final CmdOptions.IntegerOptionDeclaration optValue =\n    CmdOptions.createIntegerOption(\"VALUE\", false, 'v', \"value\", null,\n                                   \"value to write\");\n\n  private final SDK sdk;\n\n  public Write(final PrintStream console, final SDK sdk)\n  {\n    super(console, fullName, singleLineDescription,\n          new CmdOptions.OptionDeclaration<?>[] { optAddress, optValue });\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.sdk = sdk;\n  }\n\n  @Override\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (!options.isDefined(optAddress)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optAddress);\n      }\n      if (!options.isDefined(optValue)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified: \" + optValue);\n      }\n    }\n  }\n\n  /**\n   * Returns true if no error occurred and the command has been\n   * executed.\n   */\n  @Override\n  protected boolean execute(final CmdOptions options) throws IOException\n  {\n    final int address = options.getValue(optAddress);\n    final int value = options.getValue(optValue);\n    final boolean validAddress = sdk.providesAddress(address);\n    if (!validAddress) {\n      final String message =\n        String.format(\"write to unsupported address: 0x%08x\", address);\n      throw new IOException(message);\n    }\n    sdk.writeAddress(address, value);\n    final String label = sdk.getLabelForAddress(address);\n    console.printf(\"wrote 0x%04x to %s (0x%08x)%n\", value, label, address);\n    return true;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/ActionPanel.java",
    "content": "/*\n * @(#)ActionPanel.java 1.00 21/04/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JToolBar;\nimport org.soundpaint.rp2040pio.SwingUtils;\n\npublic class ActionPanel<T extends GUIObserver> extends JToolBar\n{\n  private static final long serialVersionUID = -3674776627922144842L;\n  private static final ImageIcon iconClose;\n\n  static {\n    try {\n      iconClose = SwingUtils.createImageIcon(\"quit16x16.png\", \"Quit\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed loading icon: %s\", e.getMessage());\n      System.out.println(message);\n      throw new InternalError(message, e);\n    }\n  }\n\n  public ActionPanel(final T observer)\n  {\n    Objects.requireNonNull(observer);\n    addAdditionalButtons(observer);\n    add(Box.createHorizontalGlue());\n    final JButton btClose = new JButton(iconClose);\n    btClose.setToolTipText(\"Quit Application\");\n    btClose.addActionListener((event) -> { observer.close(); });\n    add(btClose);\n  }\n\n  /**\n   * Override this method to add additional buttons to appear to the\n   * left side of the close button.  The default implementation of\n   * this method is empty.\n   */\n  protected void addAdditionalButtons(final T observer)\n  {\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/ConnectDialog.java",
    "content": "/*\n * @(#)ConnectDialog.java 1.00 21/05/30\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.awt.BorderLayout;\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JTextField;\nimport javax.swing.border.Border;\nimport javax.swing.border.EtchedBorder;\nimport javax.swing.border.TitledBorder;\n\npublic class ConnectDialog extends JDialog\n{\n  private static final long serialVersionUID = -8971812083742002912L;\n\n  private class ActionPanel extends Box\n  {\n    private static final long serialVersionUID = 2187278488162670119L;\n\n    private final JButton btOk;\n    private final JButton btCancel;\n\n    public ActionPanel()\n    {\n      super(BoxLayout.LINE_AXIS);\n      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n      btOk = new JButton(\"Ok\");\n      btOk.setMnemonic(KeyEvent.VK_O);\n      btOk.addActionListener((event) -> {\n          if (apply()) {\n            ConnectDialog.this.setVisible(false);\n          }\n        });\n      add(btOk);\n      add(Box.createHorizontalGlue());\n      btCancel = new JButton(\"Cancel\");\n      btCancel.setMnemonic(KeyEvent.VK_C);\n      btCancel.addActionListener((event) ->\n                                 ConnectDialog.this.setVisible(false));\n      add(btCancel);\n    }\n  }\n\n  private final GUIObserver observer;\n  private final JTextField tfPort;\n  private int savedPort;\n\n  private ConnectDialog()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public ConnectDialog(final GUIObserver observer, final int defaultPort)\n  {\n    super(observer, \"Connect to Emulation Server\");\n    Objects.requireNonNull(observer);\n    this.observer = observer;\n    savedPort = defaultPort;\n    tfPort = new JTextField(String.valueOf(savedPort));\n    getContentPane().add(createConnectionDetails());\n    getContentPane().add(new ActionPanel(), BorderLayout.SOUTH);\n    pack();\n  }\n\n  public void makeVisible()\n  {\n    tfPort.setText(String.valueOf(savedPort));\n    setVisible(true);\n  }\n\n  private Box createConnectionDetails()\n  {\n    final Box vBox = new Box(BoxLayout.PAGE_AXIS);\n    final Border loweredEtched =\n      BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);\n    final TitledBorder titled =\n      BorderFactory.createTitledBorder(loweredEtched, \"Connection Details\");\n    titled.setTitleJustification(TitledBorder.CENTER);\n    vBox.setBorder(titled);\n    vBox.add(createServerPortLine());\n    vBox.add(Box.createVerticalGlue());\n    return vBox;\n  }\n\n  private Box createServerPortLine()\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(new JLabel(\"Server\"));\n    final JTextField tfServer = new JTextField(\"localhost\");\n    tfServer.setEnabled(false);\n    hBox.add(tfServer);\n    hBox.add(Box.createHorizontalStrut(20));\n    final JLabel lbPort = new JLabel(\"Port\");\n    hBox.add(lbPort);\n    hBox.add(tfPort);\n    hBox.add(Box.createHorizontalGlue());\n    return hBox;\n  }\n\n  private boolean apply()\n  {\n    try {\n      final int port = Integer.parseInt(tfPort.getText().trim());\n      if ((port <= 0) || (port > 65535)) {\n        JOptionPane.\n          showMessageDialog(this,\n                            \"Port number must be in the range 1…65535.\",\n                            \"Invalid Port Number\",\n                            JOptionPane.ERROR_MESSAGE);\n        return false;\n      }\n      observer.connect(port);\n      savedPort = port;\n      return true;\n    } catch (final NumberFormatException e) {\n      JOptionPane.\n        showMessageDialog(this,\n                          \"Port is not a valid integer number.\",\n                          \"Invalid Port\",\n                          JOptionPane.ERROR_MESSAGE);\n      return false;\n    } catch (final IOException e) {\n      JOptionPane.\n        showMessageDialog(this,\n                          e.getMessage(),\n                          \"Failed Connecting\",\n                          JOptionPane.ERROR_MESSAGE);\n      return false;\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/GUIObserver.java",
    "content": "/*\n * @(#)GUIObserver.java 1.00 21/04/01\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.awt.BorderLayout;\nimport java.awt.Color;\nimport java.awt.event.WindowEvent;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JFrame;\nimport javax.swing.JLabel;\nimport javax.swing.JMenuBar;\nimport javax.swing.SwingUtilities;\nimport javax.swing.UIManager;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\nimport org.soundpaint.rp2040pio.RemoteAddressSpaceClient;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Common abstract super class for all Swing-GUI based observer\n * implementations.\n */\npublic abstract class GUIObserver extends JFrame\n{\n  private static final long serialVersionUID = 3771005056052179959L;\n\n  private static final String DEFAULT_APP_FULL_NAME =\n    \"Emulation Observer Version 0.1\";\n  private static final CmdOptions.FlagOptionDeclaration optVersion =\n    CmdOptions.createFlagOption(false, 'V', \"version\", CmdOptions.Flag.OFF,\n                                \"display version information and exit\");\n  private static final CmdOptions.FlagOptionDeclaration optHelp =\n    CmdOptions.createFlagOption(false, 'h', \"help\", CmdOptions.Flag.OFF,\n                                \"display this help text and exit\");\n  private static final CmdOptions.IntegerOptionDeclaration optPort =\n    CmdOptions.createIntegerOption(\"PORT\", false, 'p', \"port\",\n                                   Constants.\n                                   REGISTER_SERVER_DEFAULT_PORT_NUMBER,\n                                   \"use PORT as server port number\");\n  private static final CmdOptions.IntegerOptionDeclaration optRefresh =\n    CmdOptions.createIntegerOption(\"TIME\", false, 'r', \"refresh\", 1000,\n                                   \"autorefresh after <TIME> millis or \" +\n                                   \"no autorefresh, if 0\");\n  private static final List<CmdOptions.OptionDeclaration<?>>\n    optionDeclarations =\n    Arrays.asList(new CmdOptions.OptionDeclaration<?>[]\n                  { optVersion, optHelp, optPort, optRefresh });\n\n  static\n  {\n    UIManager.put(\"OptionPane.okButtonMnemonic\", \"79\"); // 'O' as mnemonic\n    UIManager.put(\"OptionPane.cancelButtonMnemonic\", \"67\"); // 'C' as mnemonic\n  }\n\n  private final String appTitle;\n  private final String appFullName;\n  private final PrintStream console;\n  private final ConnectDialog connectDialog;\n  private final JLabel lbStatus;\n  private final CmdOptions options;\n  private final SDK sdk;\n  private final RemoteAddressSpaceClient sdkClient;\n  private final RemoteAddressSpaceClient updateLoopClient;\n  private final ActionPanel<? extends GUIObserver> actionPanel;\n\n  private GUIObserver()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public GUIObserver(final String appTitle, final String appFullName,\n                     final PrintStream console, final String[] argv)\n    throws IOException\n  {\n    super(appTitle);\n    Objects.requireNonNull(appTitle);\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(argv);\n    this.appTitle = appTitle;\n    this.appFullName =\n      appFullName != null ? appFullName : DEFAULT_APP_FULL_NAME;\n    this.console = console;\n    options = parseArgs(argv);\n    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\n    connectDialog = new ConnectDialog(this, getPort());\n    lbStatus = new JLabel();\n    sdkClient = createRemoteAddressSpace(\"GUI event thread\");\n    sdk = new SDK(console, sdkClient);\n    updateLoopClient = createRemoteAddressSpace(\"update loop thread\");\n    connect(null, getPort());\n    add(actionPanel = createActionPanel(), BorderLayout.NORTH);\n    add(createStatusLine(), BorderLayout.SOUTH);\n    setJMenuBar(createMenuBar());\n    printAbout();\n  }\n\n  private Box createStatusLine()\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(lbStatus);\n    hBox.add(Box.createHorizontalGlue());\n    return hBox;\n  }\n\n  protected void setStatus(final String status)\n  {\n    setStatus(status, false);\n  }\n\n  protected void setStatus(final String status, final boolean alert)\n  {\n    lbStatus.setText(status);\n    lbStatus.setForeground(alert ? Color.RED : Color.BLACK);\n  }\n\n  /**\n   * Override this method to provide a custom action panel.  The\n   * default implementation of this method uses the default\n   * ActionPanel implementation in the java package of this class.\n   */\n  protected ActionPanel<? extends GUIObserver> createActionPanel()\n  {\n    return new ActionPanel<GUIObserver>(this);\n  }\n\n  /**\n   * Override this method to provide a custom menu bar.  The default\n   * implementation of this method uses the default MenuBar\n   * implementation in the java package of this class.\n   */\n  protected MenuBar<? extends GUIObserver> createMenuBar()\n  {\n    return new MenuBar<GUIObserver>(this);\n  }\n\n  public String getAppTitle()\n  {\n    return appTitle;\n  }\n\n  public String getAppFullName()\n  {\n    return appFullName;\n  }\n\n  public PrintStream getConsole()\n  {\n    return console;\n  }\n\n  protected SDK getSDK()\n  {\n    return sdk;\n  }\n\n  protected ActionPanel<? extends GUIObserver> getActionPanel()\n  {\n    return actionPanel;\n  }\n\n  protected int getPort()\n  {\n    return options.getValue(optPort);\n  }\n\n  /**\n   * Override this method to add additional option declarations.  The\n   * default implementation returns &lt;code&gt;null&lt;/code&gt;.\n   */\n  protected List<CmdOptions.OptionDeclaration<?>>\n    getAdditionalOptionDeclarations()\n  {\n    return null;\n  }\n\n  private List<CmdOptions.OptionDeclaration<?>> collectOptionDeclarations()\n  {\n    final List<CmdOptions.OptionDeclaration<?>> additionalOptionDeclarations =\n      getAdditionalOptionDeclarations();\n    if (additionalOptionDeclarations != null) {\n      optionDeclarations.addAll(additionalOptionDeclarations);\n    }\n    return optionDeclarations;\n  }\n\n  private CmdOptions parseArgs(final String argv[])\n  {\n    final CmdOptions options;\n    try {\n      options = new CmdOptions(appTitle, appFullName, null,\n                               collectOptionDeclarations());\n      options.parse(argv);\n      checkValidity0(options);\n    } catch (final CmdOptions.ParseException e) {\n      console.printf(\"parsing command line args failed: %s%n\", e.getMessage());\n      System.exit(-1);\n      throw new InternalError();\n    }\n    if (options.getValue(optVersion) == CmdOptions.Flag.ON) {\n      printAbout();\n      System.exit(0);\n      throw new InternalError();\n    }\n    if (options.getValue(optHelp) == CmdOptions.Flag.ON) {\n      console.println(options.getFullInfo());\n      System.exit(0);\n      throw new InternalError();\n    }\n    return options;\n  }\n\n  /**\n   * Override this method to add additional checks for command-line\n   * option values.  If an error is spotted, the code should throw a\n   * &lt;code&gt;CmdOptions.ParseException&lt;/code&gt; with detailed\n   * error reason as message text.  The default implementation is\n   * empty.\n   */\n  protected void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n  }\n\n  private void checkValidity0(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int port = options.getValue(optPort);\n    if ((port <= 0) || (port > 65535)) {\n      throw new CmdOptions.\n        ParseException(\"PORT must be in the range 0…65535\", optPort);\n    }\n    final int refresh = options.getValue(optRefresh);\n    if (refresh < 0) {\n      throw new CmdOptions.\n        ParseException(\"TIME must be a non-negative value\", optRefresh);\n    }\n    checkValidity(options);\n  }\n\n  private void printAbout()\n  {\n    console.printf(\"%s%n%s for%n%s%n%s\",\n                   appTitle, appFullName,\n                   Constants.getEmulatorIdAndVersionWithOs(),\n                   Constants.getGuiCopyrightNotice());\n  }\n\n  private RemoteAddressSpaceClient\n    createRemoteAddressSpace(final String threadName)\n  {\n    final int port = getPort();\n    try {\n      console.printf(\"%s: connecting to emulation server at port %d…%n\",\n                     threadName, port);\n      return new RemoteAddressSpaceClient(console);\n    } catch (final IOException e) {\n      console.println(\"failed to connect to emulation server: \" +\n                      e.getMessage());\n      console.println(\"check that emulation server runs at port address \" +\n                      port);\n      System.exit(-1);\n      throw new InternalError();\n    }\n  }\n\n  public void openConnectDialog()\n  {\n    connectDialog.makeVisible();\n  }\n\n  public void connect() throws IOException\n  {\n    connect(sdkClient.getPort());\n  }\n\n  public void connect(final int port) throws IOException\n  {\n    connect(sdkClient.getHost(), port);\n  }\n\n  public void connect(final String host, final int port) throws IOException\n  {\n    sdkClient.connect(host, port);\n    updateLoopClient.connect(host, port);\n    final String status =\n      String.format(\"Connected to emulation server at port %d.\", port);\n    setStatus(status);\n  }\n\n  /**\n   * Convenience method.  Call this method, if the application is to\n   * be closed in response to user action, e.g. when clicking on a\n   * \"Close\" button for closing the application.\n   */\n  public void close()\n  {\n    final WindowEvent closeEvent =\n      new WindowEvent(this, WindowEvent.WINDOW_CLOSING);\n    dispatchEvent(closeEvent);\n  }\n\n  protected void startUpdating()\n  {\n    new Thread(() -> updateLoop()).start();\n  }\n\n  /**\n   * This method is regularly called.  The observer implementation\n   * should check if the RP2040 Emulator's data, that it displays,\n   * has changed, and if so, properly update its view.\n   */\n  protected abstract void updateView();\n\n  private void updateLoop()\n  {\n    final int addressPhase0 =\n      PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.\n                                  MASTERCLK_TRIGGER_PHASE0);\n    final int addressPhase1 =\n      PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.\n                                  MASTERCLK_TRIGGER_PHASE1);\n    final int expectedValue = 0x1; // update upon stable cycle phase 1\n    final int mask = 0xffffffff;\n    final int cyclesTimeout = 0;\n    final int refresh = options.getValue(optRefresh);\n    final int millisTimeoutPhase0 = refresh / 2;\n    final int millisTimeoutPhase1 = refresh - millisTimeoutPhase0;\n    while (true) {\n      try {\n        while (true) {\n          updateLoopClient.waitAddress(addressPhase1, expectedValue, mask,\n                                       cyclesTimeout, millisTimeoutPhase1);\n          updateView();\n          SwingUtilities.invokeLater(() -> repaint());\n          updateLoopClient.waitAddress(addressPhase0, expectedValue, mask,\n                                       cyclesTimeout, millisTimeoutPhase0);\n        }\n      } catch (final IOException e) {\n        final String message = String.format(\"Error: %s\", e.getMessage());\n        setStatus(message, true);\n        console.println(message);\n        boolean recovered = false;\n        while (!recovered) {\n          try {\n            Thread.sleep(1000); // limit CPU load in case of\n                                // persisting error\n          } catch (final InterruptedException e2) {\n            // ignore\n          }\n          try {\n            connect();\n            recovered = true;\n          } catch (final IOException e3) {\n            // inherited error; ignore\n          }\n        }\n      }\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/LicenseView.java",
    "content": "/*\n * @(#)LicenseView.java 1.00 21/05/22\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.awt.Dimension;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.net.URL;\nimport javax.swing.JEditorPane;\nimport javax.swing.JOptionPane;\nimport javax.swing.JScrollPane;\n\npublic class LicenseView extends JOptionPane\n{\n  private static final long serialVersionUID = 6794879115024130934L;\n  private static final String TITLE = \"License\";\n\n  public LicenseView(final PrintStream console)\n  {\n    setMessageType(INFORMATION_MESSAGE);\n    final JEditorPane htmlPane = new JEditorPane();\n    htmlPane.setEditable(false);\n\n    final String resourcePath = \"/media/gpl-2.0-standalone.html\";\n    final URL resourceURL = LicenseView.class.getResource(resourcePath);\n    if (resourceURL == null) {\n      console.println(\"failed opening license file: \" + resourcePath);\n    }\n    try {\n      if (resourceURL != null) {\n        htmlPane.setPage(resourceURL);\n      } else {\n        htmlPane.setText(\"license not found\");\n      }\n    } catch (final IOException e) {\n      console.println(\"attempted to read a bad URL: \" + resourceURL);\n    }\n    final JScrollPane htmlView = new JScrollPane(htmlPane);\n    final Dimension preferredSize = new Dimension(600, 480);\n    htmlView.setPreferredSize(preferredSize);\n    setMessage(htmlView);\n  }\n\n  public String getTitle()\n  {\n    return TITLE;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/MenuBar.java",
    "content": "/*\n * @(#)MenuBar.java 1.00 21/05/22\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.KeyStroke;\nimport javax.swing.JDialog;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuBar;\nimport javax.swing.JMenuItem;\nimport javax.swing.JOptionPane;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.SwingUtils;\n\npublic class MenuBar<T extends GUIObserver> extends JMenuBar\n{\n  private static final long serialVersionUID = -5235397033202919401L;\n\n  private final T observer;\n  private final JDialog aboutDialog;\n  private final JDialog licenseDialog;\n\n  private MenuBar()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public MenuBar(final T observer)\n  {\n    Objects.requireNonNull(observer);\n    this.observer = observer;\n    aboutDialog = createAboutDialog();\n    final LicenseView licenseView = new LicenseView(observer.getConsole());\n    licenseDialog = licenseView.createDialog(this, licenseView.getTitle());\n    licenseDialog.setModal(false);\n    add(createFileMenu());\n    addAdditionalMenus(observer);\n    add(createHelpMenu());\n  }\n\n  private JDialog createAboutDialog()\n  {\n    final JOptionPane aboutPane = new JOptionPane();\n    final String message =\n      String.format(\"%s%n%s for%n%s%n%s\",\n                    observer.getAppTitle(), observer.getAppFullName(),\n                    Constants.getEmulatorIdAndVersionWithOs(),\n                    Constants.getGuiCopyrightNotice());\n    aboutPane.setMessage(message);\n    aboutPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);\n    final JDialog aboutDialog = aboutPane.createDialog(this, \"About\");\n    aboutDialog.setModal(false);\n    return aboutDialog;\n  }\n\n  /**\n   * Override this method to add additional menus to appear between\n   * the file and the help menus that this class already provides.\n   * The default implementation of this method is empty.\n   */\n  protected void addAdditionalMenus(final T observer)\n  {\n  }\n\n  private JMenu createFileMenu()\n  {\n    final JMenu fileMenu = new JMenu(\"File\");\n    fileMenu.setMnemonic(KeyEvent.VK_F);\n\n    addAdditionalFileMenuItems(fileMenu, observer);\n\n    final JMenuItem connect = new JMenuItem(\"Connect…\");\n    connect.setMnemonic(KeyEvent.VK_N);\n    connect.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,\n                                                  ActionEvent.ALT_MASK));\n    connect.getAccessibleContext().\n      setAccessibleDescription(\"Connect to Emulation Server\");\n    connect.addActionListener((event) -> observer.openConnectDialog());\n    fileMenu.add(connect);\n\n    final JMenuItem quit =\n      SwingUtils.createIconMenuItem(\"quit16x16.png\", \"Quit\");\n    quit.setMnemonic(KeyEvent.VK_Q);\n    quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,\n                                               ActionEvent.ALT_MASK));\n    quit.getAccessibleContext().setAccessibleDescription(\"Exit Application\");\n    quit.addActionListener((event) -> observer.close());\n    fileMenu.add(quit);\n    return fileMenu;\n  }\n\n  /**\n   * Override this method to add additional file menu items to appear\n   * before the \"Quit\" item in the file menu that this class provides.\n   * The default implementation of this method is empty.\n   */\n  protected void addAdditionalFileMenuItems(final JMenu fileMenue,\n                                            final T observer)\n  {\n  }\n\n  private JMenu createHelpMenu()\n  {\n    final JMenu helpMenu = new JMenu(\"Help\");\n    helpMenu.setMnemonic(KeyEvent.VK_H);\n\n    final String appTitle = observer.getAppTitle();\n    final JMenuItem about = new JMenuItem(String.format(\"About %s…\", appTitle));\n    about.setMnemonic(KeyEvent.VK_A);\n    about.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,\n                                                ActionEvent.ALT_MASK));\n    about.getAccessibleContext().setAccessibleDescription(\"About this app\");\n    about.addActionListener((event) -> aboutDialog.setVisible(true));\n    helpMenu.add(about);\n\n    final JMenuItem license = new JMenuItem(\"License…\");\n    license.setMnemonic(KeyEvent.VK_L);\n    license.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,\n                                                  ActionEvent.ALT_MASK));\n    license.getAccessibleContext().\n      setAccessibleDescription(\"Show Copying License\");\n    license.addActionListener((event) -> licenseDialog.setVisible(true));\n    helpMenu.add(license);\n\n    return helpMenu;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/Observer.java",
    "content": "/*\n * @(#)Observer.java 1.00 21/04/01\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.CmdOptions;\nimport org.soundpaint.rp2040pio.RemoteAddressSpaceClient;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Emulation Register Status Observation (Command-Line Version)\n */\npublic class Observer\n{\n  private static final String APP_TITLE = \"Observer\";\n  private static final String APP_FULL_NAME = \"Emulation Observer Version 0.1\";\n\n  private static final CmdOptions.FlagOptionDeclaration optVersion =\n    CmdOptions.createFlagOption(false, 'V', \"version\", CmdOptions.Flag.OFF,\n                                \"display version information and exit\");\n  private static final CmdOptions.FlagOptionDeclaration optHelp =\n    CmdOptions.createFlagOption(false, 'h', \"help\", CmdOptions.Flag.OFF,\n                                \"display this help text and exit\");\n  private static final CmdOptions.IntegerOptionDeclaration optPort =\n    CmdOptions.createIntegerOption(\"PORT\", false, 'p', \"port\",\n                                   Constants.\n                                   REGISTER_SERVER_DEFAULT_PORT_NUMBER,\n                                   \"use PORT as server port number\");\n  private static final CmdOptions.IntegerOptionDeclaration optAddress =\n    CmdOptions.createIntegerOption(\"ADDRESS\", false, 'a', \"address\", null,\n                                   \"address of the register to observe\");\n  private static final CmdOptions.IntegerOptionDeclaration optMask =\n    CmdOptions.createIntegerOption(\"MASK\", false, 'm', \"mask\", 0xffffffff,\n                                   \"bit mask to select bits to observe\");\n  private static final CmdOptions.IntegerOptionDeclaration optRefresh =\n    CmdOptions.createIntegerOption(\"TIME\", false, 'r', \"refresh\", 10000,\n                                   \"autorefresh after <TIME> millis or no autorefresh, if 0\");\n  private static final List<CmdOptions.OptionDeclaration<?>>\n    optionDeclarations =\n    Arrays.asList(new CmdOptions.OptionDeclaration<?>[]\n                  { optVersion, optHelp, optPort, optAddress,\n                    optMask, optRefresh });\n\n  private final PrintStream console;\n  private final CmdOptions options;\n  private final SDK sdk;\n\n  private Observer()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public Observer(final PrintStream console, final String[] argv)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    options = parseArgs(argv);\n    printAbout();\n    final AddressSpace memory = connect();\n    sdk = new SDK(console, memory);\n  }\n\n  private CmdOptions parseArgs(final String argv[])\n  {\n    final CmdOptions options;\n    try {\n      options = new CmdOptions(APP_TITLE, APP_FULL_NAME, null,\n                               optionDeclarations);\n      options.parse(argv);\n      checkValidity(options);\n    } catch (final CmdOptions.ParseException e) {\n      console.println(e.getMessage());\n      System.exit(-1);\n      throw new InternalError();\n    }\n    if (options.getValue(optVersion) == CmdOptions.Flag.ON) {\n      printAbout();\n      System.exit(0);\n      throw new InternalError();\n    }\n    if (options.getValue(optHelp) == CmdOptions.Flag.ON) {\n      console.println(options.getFullInfo());\n      System.exit(0);\n      throw new InternalError();\n    }\n    return options;\n  }\n\n  private void checkValidity(final CmdOptions options)\n    throws CmdOptions.ParseException\n  {\n    final int port = options.getValue(optPort);\n    if ((port < 0) || (port > 65535)) {\n      throw new CmdOptions.\n        ParseException(\"PORT must be in the range 0…65535\", optPort);\n    }\n    if (options.getValue(optHelp) != CmdOptions.Flag.ON) {\n      if (!options.isDefined(optAddress)) {\n        throw new CmdOptions.\n          ParseException(\"option not specified\", optAddress);\n      }\n    }\n    final int refresh = options.getValue(optRefresh);\n    if (refresh < 0) {\n      throw new CmdOptions.\n        ParseException(\"TIME must be a non-negative value\", optRefresh);\n    }\n  }\n\n  private void printAbout()\n  {\n    console.printf(\"%s for%n%s%n%s%n\",\n                   APP_FULL_NAME,\n                   Constants.getEmulatorIdAndVersionWithOs(),\n                   Constants.getCmdLineCopyrightNotice());\n  }\n\n  private AddressSpace connect()\n  {\n    final int port = options.getValue(optPort);\n    try {\n      console.printf(\"connecting to emulation server at port %d…%n\", port);\n      return new RemoteAddressSpaceClient(console, null, port);\n    } catch (final IOException e) {\n      console.println(\"failed to connect to emulation server: \" +\n                      e.getMessage());\n      console.println(\"check that emulation server runs at port address \" +\n                      port);\n      System.exit(-1);\n      throw new InternalError();\n    }\n  }\n\n  private void run()\n  {\n    try {\n      final int address = options.getValue(optAddress);\n      final boolean validAddress = sdk.providesAddress(address);\n      if (!validAddress) {\n        final String message =\n          String.format(\"unsupported address: 0x%08x\", address);\n        console.println(message);\n        System.exit(-1);\n        throw new InternalError();\n      }\n      final int mask = options.getValue(optMask);\n      final String label = sdk.getLabelForAddress(address);\n      final int refresh = options.getValue(optRefresh);\n      update(address, label, sdk.readAddress(address));\n      while (true) {\n        update(address, label, sdk.wait(address, 0xffffffff, 0x0, 1, refresh));\n        update(address, label, sdk.readAddress(address));\n      }\n    } catch (final IOException e) {\n      console.println(e.getMessage());\n      System.exit(-1);\n    }\n    System.exit(0);\n  }\n\n  private void update(final int address, final String label, final int value)\n  {\n    console.printf(\"wait on %s (0x%08x) returned 0x%08x%n\",\n                   label, address, value);\n  }\n\n  public static void main(final String argv[])\n  {\n    new Observer(System.out, argv).run();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/code/ActionPanel.java",
    "content": "/*\n * @(#)ActionPanel.java 1.00 21/04/24\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.code;\n\nimport java.awt.event.KeyEvent;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\n\npublic class ActionPanel extends Box\n{\n  private static final long serialVersionUID = 3591144049290778809L;\n\n  public ActionPanel(final CodeObserver codeObserver)\n  {\n    super(BoxLayout.LINE_AXIS);\n    Objects.requireNonNull(codeObserver);\n    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n    add(Box.createHorizontalGlue());\n    final JButton btClose = new JButton(\"Close\");\n    btClose.setMnemonic(KeyEvent.VK_C);\n    btClose.addActionListener((event) -> { codeObserver.close(); });\n    add(btClose);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/code/CodeObserver.java",
    "content": "/*\n * @(#)CodeObserver.java 1.00 21/04/24\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.code;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.observer.GUIObserver;\n\n/**\n * Emulation Code Status Observation\n */\npublic class CodeObserver extends GUIObserver\n{\n  private static final long serialVersionUID = -8266192991979578046L;\n  private static final String APP_TITLE = \"Code Observer\";\n  private static final String APP_FULL_NAME =\n    \"Emulation Code Observer Version 0.1\";\n\n  private final CodeViewPanel codeViewPanel;\n\n  private CodeObserver(final PrintStream console, final String[] argv)\n    throws IOException\n  {\n    super(APP_TITLE, APP_FULL_NAME, console, argv);\n    add(codeViewPanel = new CodeViewPanel(console, getSDK(), APP_TITLE));\n    pack();\n    setVisible(true);\n    startUpdating();\n  }\n\n  @Override\n  protected void updateView()\n  {\n    codeViewPanel.updateView();\n  }\n\n  public static void main(final String argv[])\n  {\n    final PrintStream console = System.out;\n    try {\n      new CodeObserver(console, argv);\n    } catch (final IOException e) {\n      console.printf(\"initialization failed: %s%n\", e.getMessage());\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/code/CodeSmViewPanel.java",
    "content": "/*\n * @(#)CodeSmViewPanel.java 1.00 21/04/24\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.code;\n\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BoxLayout;\nimport javax.swing.DefaultListCellRenderer;\nimport javax.swing.DefaultListModel;\nimport javax.swing.DefaultListSelectionModel;\nimport javax.swing.JList;\nimport javax.swing.JPanel;\nimport javax.swing.JProgressBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTextField;\nimport javax.swing.SwingConstants;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class CodeSmViewPanel extends JPanel\n{\n  private static final long serialVersionUID = 1576266124601249894L;\n\n  private static final String lockedSymbol = \"\"; //\"🔒 \";\n  private static final String unlockedSymbol = \"\"; //\"   \";\n  private static final String wrapSymbol = \"← \";\n  private static final String wrapTargetSymbol = \"→ \";\n  private static final String selfWrapSymbol = \"↔ \";\n  private static final String noWrapSymbol = \"  \";\n  private static final String breakPointSymbol = \"\"; //\"🛑\";\n  private static final String noBreakPointSymbol = \"\"; //\" \";\n  private static final String errorText = \"error: connection to server lost\";\n\n  private static final Color fgDefault = Color.BLACK;\n  private static final Color bgDefault = Color.WHITE;\n  private static final Color fgCurrent = Color.WHITE;\n  private static final Color bgCurrent = Color.RED;\n  private static final Color fgCurrentInactive = new Color(0x9f9f9f);\n  private static final Color bgCurrentInactive = new Color(0xb04f4f);\n\n  public static final Font codeFont = new Font(Font.MONOSPACED, Font.PLAIN, 12);\n\n  private static class EmptySelectionModel extends DefaultListSelectionModel\n  {\n    private static final long serialVersionUID = -2981078454514535370L;\n\n    private EmptySelectionModel()\n    {\n      setSelectionMode(SINGLE_SELECTION);\n    }\n\n    @Override\n    public void setAnchorSelectionIndex(final int anchorIndex) {}\n\n    @Override\n    public void setLeadAnchorNotificationEnabled(final boolean flag) {}\n\n    @Override\n    public void setLeadSelectionIndex(final int leadIndex) {}\n\n    @Override\n    public void setSelectionInterval(final int index0, final int index1) {}\n  }\n\n  private static class Instruction\n  {\n    public boolean isCurrentAddress;\n    public boolean isActive;\n    public String text;\n  }\n\n  private static class InstructionRenderer extends DefaultListCellRenderer\n  {\n    private static final long serialVersionUID = -1347539595980280795L;\n\n    public InstructionRenderer()\n    {\n      setHorizontalAlignment(SwingConstants.LEFT);\n    }\n\n    @Override\n    public Component\n      getListCellRendererComponent(final JList<?> list,\n                                   final Object value,\n                                   final int index,\n                                   final boolean isSelected,\n                                   final boolean cellHasFocus)\n    {\n      final Instruction instruction = (Instruction)value;\n      super.getListCellRendererComponent(list, value, index,\n                                         isSelected, cellHasFocus);\n      setText(instruction.text);\n      if (instruction.isCurrentAddress) {\n        if (instruction.isActive && list.isEnabled()) {\n          setForeground(fgCurrent);\n          setBackground(bgCurrent);\n        } else {\n          setForeground(fgCurrentInactive);\n          setBackground(bgCurrentInactive);\n        }\n      }\n      setFont(codeFont);\n      return this;\n    }\n  }\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final JProgressBar pbDelay;\n  private final JTextField taForcedOrExecdInstruction;\n  private final DefaultListModel<Instruction> instructions;\n  private final JList<Instruction> lsInstructions;\n  private int pioNum;\n  private int smNum;\n  private int lastPC;\n\n  private CodeSmViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public CodeSmViewPanel(final PrintStream console, final SDK sdk,\n                         final JProgressBar pbDelay,\n                         final JTextField taForcedOrExecdInstruction)\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(pbDelay);\n    Objects.requireNonNull(taForcedOrExecdInstruction);\n    this.console = console;\n    this.sdk = sdk;\n    this.pbDelay = pbDelay;\n    this.taForcedOrExecdInstruction = taForcedOrExecdInstruction;\n    instructions = new DefaultListModel<Instruction>();\n    for (int address = 0; address < Constants.MEMORY_SIZE; address++) {\n      instructions.addElement(new Instruction());\n    }\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    lsInstructions = new JList<Instruction>(instructions);\n    lsInstructions.setSelectionModel(new EmptySelectionModel());\n    lsInstructions.setCellRenderer(new InstructionRenderer());\n    add(new JScrollPane(lsInstructions));\n    setPreferredSize(new Dimension(300, 200));\n    lastPC = -1;\n  }\n\n  private boolean isClkEnabled() throws IOException\n  {\n    final int clkEnableAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_NEXT_CLK_ENABLE);\n    final int clkEnable = sdk.readAddress(clkEnableAddress) & 0x1;\n    return clkEnable != 0x0;\n  }\n\n  private int getPC() throws IOException\n  {\n    final int addressAddr =\n      PIORegisters.getSMAddress(pioNum, smNum,\n                                PIORegisters.Regs.SM0_ADDR);\n    return\n      sdk.readAddress(addressAddr) & (Constants.MEMORY_SIZE - 1);\n  }\n\n  private int getBreakPoints() throws IOException\n  {\n    final int addressBreakPoints =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_BREAKPOINTS);\n    return sdk.readAddress(addressBreakPoints);\n  }\n\n  private int getPendingDelay() throws IOException\n  {\n    final int addressPendingDelay =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_PENDING_DELAY);\n    final int pendingDelay = sdk.readAddress(addressPendingDelay);\n    return pendingDelay & 0x1f;\n  }\n\n  private void updateInstructions() throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final int pc = getPC();\n    final int memoryAllocation = pioSdk.getMemoryAllocation();\n\n    final int addressExecCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = sdk.readAddress(addressExecCtrl);\n    final int wrap =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_TOP_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_TOP_LSB;\n    final int wrapTarget =\n      (execCtrl & Constants.SM0_EXECCTRL_WRAP_BOTTOM_BITS) >>>\n      Constants.SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n\n    final int breakPoints = getBreakPoints();\n    final int pendingDelay = getPendingDelay();\n\n    final int forcedInstrAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_FORCED_INSTR);\n    final int forcedInstr = sdk.readAddress(forcedInstrAddress);\n    final boolean haveForced = (forcedInstr & 0x00010000) != 0x0;\n    final int forcedOpCode = haveForced ? forcedInstr & 0xffff : 0x0;\n\n    final int execdInstrAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_EXECD_INSTR);\n    final int execdInstr = sdk.readAddress(execdInstrAddress);\n    final boolean haveExecd = (execdInstr & 0x00010000) != 0x0;\n    final int execdOpCode = haveExecd ? execdInstr & 0xffff : 0x0;\n\n    for (int address = 0; address < Constants.MEMORY_SIZE; address++) {\n      final boolean isCurrentAddress = address == pc;\n      final PIOSDK.InstructionInfo instructionInfo =\n        pioSdk.getMemoryInstruction(smNum, address, true, true);\n      final boolean isAllocated = ((memoryAllocation >>> address) & 0x1) != 0x0;\n      final boolean isWrap = address == wrap;\n      final boolean isWrapTarget = address == wrapTarget;\n      final String displayWrap =\n        isWrap ?\n        (isWrapTarget ? selfWrapSymbol : wrapSymbol) :\n        (isWrapTarget ? wrapTargetSymbol : noWrapSymbol);\n      final boolean isBreakPoint = ((breakPoints >>> address) & 0x1) != 0x0;\n      final String instructionText =\n        String.format(\"%s%s%s%s%n\",\n                      (isBreakPoint ? breakPointSymbol : noBreakPointSymbol),\n                      (isAllocated ? lockedSymbol : unlockedSymbol),\n                      displayWrap,\n                      instructionInfo.getFullStatement());\n      final Instruction instruction = instructions.getElementAt(address);\n      instruction.text = instructionText;\n      instruction.isCurrentAddress = isCurrentAddress;\n      instruction.isActive = (pendingDelay == 0) && !haveForced && !haveExecd;\n    }\n    final PIOSDK.InstructionInfo currentInstructionInfo =\n      pioSdk.getCurrentInstruction(smNum, true, true);\n    updateDelayDisplay(currentInstructionInfo, pendingDelay);\n    final boolean isActive = pioSdk.smGetEnabled(smNum) && isClkEnabled();\n    updateForcedOrExecdInstructionDisplay(pioSdk, haveForced, forcedOpCode,\n                                          haveExecd, execdOpCode, isActive);\n    lsInstructions.setEnabled(isActive);\n    if (pc != lastPC) {\n      lsInstructions.ensureIndexIsVisible(pc);\n      lastPC = pc;\n    }\n  }\n\n  private void updateForcedOrExecdInstructionDisplay(final PIOSDK pioSdk,\n                                                     final boolean haveForced,\n                                                     final int forcedOpCode,\n                                                     final boolean haveExecd,\n                                                     final int execdOpCode,\n                                                     final boolean isActive)\n    throws IOException\n  {\n    if (haveForced) {\n      final PIOSDK.InstructionInfo forcedInstrInfo =\n        pioSdk.getInstructionFromOpCode(smNum,\n                                        Constants.INSTR_ORIGIN_FORCED,\n                                        \"\", forcedOpCode, true, false, 0);\n      taForcedOrExecdInstruction.setForeground(fgCurrent);\n      taForcedOrExecdInstruction.setBackground(bgCurrent);\n      taForcedOrExecdInstruction.setOpaque(true);\n      final String displayText =\n        String.format(\"  [f] %04x %s\",\n                      forcedOpCode, forcedInstrInfo.getFullStatement());\n      taForcedOrExecdInstruction.setText(displayText);\n    } else if (haveExecd) {\n      final PIOSDK.InstructionInfo execdInstrInfo =\n        pioSdk.getInstructionFromOpCode(smNum,\n                                        Constants.INSTR_ORIGIN_EXECD,\n                                        \"\", execdOpCode, true, false, 0);\n      if (isActive) {\n        taForcedOrExecdInstruction.setForeground(fgCurrent);\n        taForcedOrExecdInstruction.setBackground(bgCurrent);\n      } else {\n        taForcedOrExecdInstruction.setForeground(fgCurrentInactive);\n        taForcedOrExecdInstruction.setBackground(bgCurrentInactive);\n      }\n      taForcedOrExecdInstruction.setOpaque(true);\n      final String displayText =\n        String.format(\"  [x] %04x %s\",\n                      execdOpCode, execdInstrInfo.getFullStatement());\n      taForcedOrExecdInstruction.setText(displayText);\n    } else {\n      taForcedOrExecdInstruction.setForeground(fgDefault);\n      taForcedOrExecdInstruction.setBackground(bgDefault);\n      taForcedOrExecdInstruction.setOpaque(false);\n      taForcedOrExecdInstruction.setText(\"\");\n    }\n  }\n\n  private void updateDelayDisplay(final PIOSDK.InstructionInfo instructionInfo,\n                                  final int pendingDelay)\n    throws IOException\n  {\n    final int totalDelay = instructionInfo.getDelay();\n    final int completedDelay = totalDelay - pendingDelay;\n    final float progress;\n    final String progressText;\n    if (totalDelay == 0) {\n      progress = 0.0f;\n      progressText = \"\";\n    } else {\n      progress = ((float)completedDelay) / totalDelay;\n      progressText =\n        String.format(\"delay: %d of %d %s done\", completedDelay, totalDelay,\n                      totalDelay == 1 ? \"cycle\" : \"cycles\");\n    }\n    final int progressValue = Math.round(progress * 1000.0f);\n    pbDelay.setString(progressText);\n    pbDelay.setValue(progressValue);\n  }\n\n  private void checkedUpdateInstructions()\n  {\n    try {\n      updateInstructions();\n    } catch (final IOException e) {\n      for (int address = 0; address < Constants.MEMORY_SIZE; address++) {\n        instructions.getElementAt(address).text = errorText;\n      }\n    }\n  }\n\n  public void smChanged(final int pioNum, final int smNum)\n  {\n    this.pioNum = pioNum;\n    this.smNum = smNum;\n    final String toolTipText =\n      String.format(\"code view for PIO%d, SM%d\", pioNum, smNum);\n    lsInstructions.setToolTipText(toolTipText);\n    checkedUpdateInstructions();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/code/CodeViewPanel.java",
    "content": "/*\n * @(#)CodeViewPanel.java 1.00 21/04/24\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.code;\n\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JProgressBar;\nimport javax.swing.JRadioButton;\nimport javax.swing.JTextField;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class CodeViewPanel extends JPanel\n{\n  private static final long serialVersionUID = -2394791187467829359L;\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final CodeSmViewPanel codeSmViewPanel;\n  private final JTextField taForcedOrExecdInstruction;\n  private final JProgressBar pbDelay;\n  private int pioNum;\n  private int smNum;\n\n  private CodeViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public CodeViewPanel(final PrintStream console, final SDK sdk,\n                       final String borderTitle)\n    throws IOException\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(borderTitle));\n    pbDelay = new JProgressBar(0, 1000);\n    taForcedOrExecdInstruction = new JTextField();\n    codeSmViewPanel =\n      new CodeSmViewPanel(console, sdk, pbDelay, taForcedOrExecdInstruction);\n\n    final Box pioSelection = new Box(BoxLayout.LINE_AXIS);\n    add(pioSelection);\n    final JLabel lbPio = new JLabel(\"PIO\");\n    pioSelection.add(lbPio);\n    pioSelection.add(Box.createHorizontalStrut(15));\n    addPioButtons(pioSelection);\n    pioSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(pioSelection);\n\n    final Box smSelection = new Box(BoxLayout.LINE_AXIS);\n    add(smSelection);\n    final JLabel lbSm = new JLabel(\"SM\");\n    lbSm.setMinimumSize(lbPio.getPreferredSize());\n    lbSm.setPreferredSize(lbPio.getPreferredSize());\n    smSelection.add(lbSm);\n    smSelection.add(Box.createHorizontalStrut(15));\n    addSmButtons(smSelection);\n    smSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(smSelection);\n\n    taForcedOrExecdInstruction.setEditable(false);\n    taForcedOrExecdInstruction.setFont(CodeSmViewPanel.codeFont);\n    SwingUtils.setPreferredHeightAsMaximum(taForcedOrExecdInstruction);\n    add(taForcedOrExecdInstruction);\n\n    add(codeSmViewPanel);\n\n    pbDelay.setStringPainted(true);\n    add(pbDelay);\n\n    codeSmViewPanel.smChanged(pioNum, smNum);\n  }\n\n  private void addPioButtons(final Box pioSelection)\n  {\n    final ButtonGroup bgPio = new ButtonGroup();\n    for (int pioNum = 0; pioNum < Constants.PIO_NUM; pioNum++) {\n      if (pioNum != 0) pioSelection.add(Box.createHorizontalStrut(10));\n      final JRadioButton rbPio = new JRadioButton(String.valueOf(pioNum));\n      rbPio.setSelected(pioNum == 0);\n      final int finalPioNum = pioNum;\n      rbPio.addActionListener((event) -> {\n          this.pioNum = finalPioNum;\n          codeSmViewPanel.smChanged(finalPioNum, smNum);\n        });\n      bgPio.add(rbPio);\n      pioSelection.add(rbPio);\n    }\n  }\n\n  private void addSmButtons(final Box smSelection)\n  {\n    final ButtonGroup bgSm = new ButtonGroup();\n    for (int smNum = 0; smNum < Constants.SM_COUNT; smNum++) {\n      if (smNum != 0) smSelection.add(Box.createHorizontalStrut(10));\n      final JRadioButton rbSm = new JRadioButton(String.valueOf(smNum));\n      rbSm.setSelected(smNum == 0);\n      rbSm.setMnemonic(KeyEvent.VK_0 + smNum);\n      final int finalSmNum = smNum;\n      rbSm.addActionListener((event) -> {\n          this.smNum = finalSmNum;\n          codeSmViewPanel.smChanged(pioNum, finalSmNum);\n        });\n      bgSm.add(rbSm);\n      smSelection.add(rbSm);\n    }\n  }\n\n  public void updateView()\n  {\n    codeSmViewPanel.smChanged(pioNum, smNum);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/AbstractSignal.java",
    "content": "/*\n * @(#)AbstractSignal.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\npublic abstract class AbstractSignal<T> implements Signal\n{\n  private static class SignalRecord<T>\n  {\n    private T value;\n    private int notChangedSince;\n\n    private SignalRecord()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    private SignalRecord(final T value, final int notChangedSince)\n    {\n      this.value = value;\n      this.notChangedSince = notChangedSince;\n    }\n\n    @Override\n    public String toString()\n    {\n      return String.format(\"SignalRecord(value=%s,notChangedSince=%d\",\n                           value, notChangedSince);\n    }\n  }\n\n  private final List<SignalRecord<T>> signalRecords;\n  private final SignalRendering.SignalParams signalParams;\n  private boolean visible;\n\n  private AbstractSignal()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public AbstractSignal(final SignalRendering.SignalParams signalParams)\n  {\n    Objects.requireNonNull(signalParams);\n    this.signalRecords = new ArrayList<SignalRecord<T>>();\n    this.signalParams = signalParams;\n    visible = false;\n  }\n\n  public SignalRendering.SignalParams getSignalParams()\n  {\n    return signalParams;\n  }\n\n  @Override\n  public void reset() {\n    signalRecords.clear();\n    // keep visibility unmodified\n  }\n\n  @Override\n  public String getLabel() { return signalParams.getLabel(); }\n\n  protected void record(final T value, final boolean enforceChanged)\n  {\n    final int size = signalRecords.size();\n    final T previousValue =\n      size > 0 ? signalRecords.get(size - 1).value : null;\n    final int previousNotChangedSince =\n      size > 0 ? signalRecords.get(size - 1).notChangedSince : 0;\n    final int notChangedSince =\n      enforceChanged ||\n      ((value == null) && (previousValue != null)) ||\n      ((value != null) && !value.equals(previousValue)) ?\n      0 : previousNotChangedSince + 1;\n    final SignalRecord<T> signalRecord =\n      new SignalRecord<T>(value, notChangedSince);\n    signalRecords.add(signalRecord);\n  }\n\n  @Override\n  public int size()\n  {\n    return signalRecords.size();\n  }\n\n  public boolean next(final int cycle)\n  {\n    return cycle < signalRecords.size() - 1;\n  }\n\n  public T getValue(final int index)\n  {\n    return\n      index < 0 ?\n      null :\n      (index >= size() ? null : signalRecords.get(index).value);\n  }\n\n  @Override\n  public int getNotChangedSince(final int cycle)\n  {\n    return\n      cycle >= 0 ? signalRecords.get(cycle).notChangedSince : 0;\n  }\n\n  public boolean changed(final int cycle)\n  {\n    return getNotChangedSince(cycle) == 0;\n  }\n\n  @Override\n  public String getToolTipText(final int cycle)\n  {\n    return null;\n  }\n\n  abstract protected double getSignalHeight();\n\n  @Override\n  public void createToolTip(final List<ToolTip> toolTips,\n                            final int cycle,\n                            final boolean isFirstCycle,\n                            final boolean isLastCycle,\n                            final double zoom,\n                            final double xStart,\n                            final double yBottom)\n  {\n    final String previousToolTipText = getToolTipText(cycle - 1);\n    if (previousToolTipText != null) {\n      final int previousCycles = getNotChangedSince(cycle - 1) + 1;\n      toolTips.add(new ToolTip((int)(xStart - previousCycles * zoom),\n                               (int)(yBottom - getSignalHeight()),\n                               (int)xStart - 1, (int)yBottom,\n                               previousToolTipText));\n    }\n    if (isLastCycle) {\n      // print label as preview for not yet finished value\n      final String toolTipText = getToolTipText(cycle);\n      if (toolTipText != null) {\n        final int cycles = getNotChangedSince(cycle) - 1;\n        toolTips.add(new ToolTip((int)(xStart - cycles * zoom),\n                                 (int)(yBottom - getSignalHeight()),\n                                 (int)xStart - 1, (int)yBottom,\n                                 toolTipText));\n      }\n    }\n  }\n\n  @Override\n  public void setVisible(final boolean visible)\n  {\n    this.visible = visible;\n  }\n\n  @Override\n  public boolean getVisible()\n  {\n    return visible;\n  }\n\n  @Override\n  public String toString()\n  {\n    final StringBuffer values = new StringBuffer();\n    for (final SignalRecord<T> record : signalRecords) {\n      if (values.length() > 0) values.append(\", \");\n      values.append(record.value);\n    }\n    return String.format(\"Signal[label=%s, values={%s}]\", getLabel(), values);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ActionPanel.java",
    "content": "/*\n * @(#)ActionPanel.java 1.00 21/04/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Dimension;\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport javax.swing.Box;\nimport javax.swing.ImageIcon;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JSlider;\nimport javax.swing.JSpinner;\nimport javax.swing.SpinnerModel;\nimport javax.swing.SpinnerNumberModel;\nimport org.soundpaint.rp2040pio.SwingUtils;\n\npublic class ActionPanel\n  extends org.soundpaint.rp2040pio.observer.ActionPanel<Diagram>\n  implements Constants\n{\n  private static final long serialVersionUID = -4136799373128393432L;\n  private static final int defaultCycles = 1;\n  private static final ImageIcon iconEmulate;\n  private static final ImageIcon iconClear;\n  private static final ImageIcon iconScript;\n\n  private /*final*/ JSpinner spCycles;\n\n  static {\n    try {\n      iconEmulate = SwingUtils.createImageIcon(\"cycle16x16.png\", \"Emulate\");\n      iconClear = SwingUtils.createImageIcon(\"trash16x16.png\", \"Clear\");\n      iconScript = SwingUtils.createImageIcon(\"floppy-blue16x16.png\", \"Load…\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed loading icon: %s\", e.getMessage());\n      System.out.println(message);\n      throw new InternalError(message, e);\n    }\n  }\n\n  public ActionPanel(final Diagram diagram)\n  {\n    super(diagram);\n  }\n\n  public int getCycles()\n  {\n    return (Integer)spCycles.getValue();\n  }\n\n  @Override\n  protected void addAdditionalButtons(final Diagram diagram)\n  {\n    addButtonLoad(diagram);\n    add(Box.createHorizontalStrut(15));\n    add(Box.createHorizontalGlue());\n    addCyclesControl();\n    add(Box.createHorizontalStrut(5));\n    final JButton btEmulate = new JButton(iconEmulate);\n    btEmulate.setToolTipText(TOOLTIP_TEXT_EMULATE);\n    add(btEmulate);\n    add(Box.createHorizontalStrut(15));\n    add(Box.createHorizontalGlue());\n    btEmulate.addActionListener((event) -> diagram.applyCycles());\n    addButtonClear(diagram);\n    add(Box.createHorizontalStrut(15));\n    add(Box.createHorizontalGlue());\n    addZoomControl(diagram);\n    add(Box.createHorizontalStrut(15));\n    add(Box.createHorizontalGlue());\n  }\n\n  private void addButtonLoad(final Diagram diagram)\n  {\n    final JButton btScript = new JButton(iconScript);\n    btScript.setToolTipText(TOOLTIP_TEXT_LOAD);\n    btScript.addActionListener((event) -> { diagram.showScriptDialog(); });\n    add(btScript);\n  }\n\n  private void addCyclesControl()\n  {\n    final JLabel lbCycles = new JLabel(\"Cycles\");\n    lbCycles.setDisplayedMnemonic(KeyEvent.VK_Y);\n    lbCycles.setToolTipText(TOOLTIP_TEXT_CYCLES);\n    add(lbCycles);\n    add(Box.createHorizontalStrut(5));\n    final SpinnerModel cyclesModel =\n      new SpinnerNumberModel(defaultCycles, 1, 999, 1);\n    spCycles = new JSpinner(cyclesModel);\n    final JSpinner.DefaultEditor editor =\n      (JSpinner.DefaultEditor)spCycles.getEditor();\n    editor.getTextField().setColumns(3);\n    final int spCyclesHeight = spCycles.getPreferredSize().height;\n    spCycles.setMaximumSize(new Dimension(100, spCyclesHeight));\n    spCycles.setToolTipText(TOOLTIP_TEXT_CYCLES);\n    lbCycles.setLabelFor(spCycles);\n    add(spCycles);\n  }\n\n  private void addButtonClear(final Diagram diagram)\n  {\n    final JButton btClear = new JButton(iconClear);\n    btClear.setToolTipText(TOOLTIP_TEXT_CLEAR);\n    btClear.addActionListener((event) -> diagram.clear());\n    add(btClear);\n  }\n\n  private void addZoomControl(final Diagram diagram)\n  {\n    final JLabel lbZoom = new JLabel(\"Zoom\");\n    lbZoom.setDisplayedMnemonic(KeyEvent.VK_Z);\n    lbZoom.setToolTipText(TOOLTIP_TEXT_ZOOM);\n    add(lbZoom);\n    add(Box.createHorizontalStrut(5));\n    final JSlider slZoom =\n      new JSlider(SignalPanel.ZOOM_MIN, SignalPanel.ZOOM_MAX,\n                  SignalPanel.ZOOM_DEFAULT);\n    lbZoom.setLabelFor(slZoom);\n    slZoom.setMajorTickSpacing(SignalPanel.ZOOM_MIN);\n    slZoom.setPaintTicks(true);\n    slZoom.setLabelTable(slZoom.createStandardLabels(SignalPanel.ZOOM_MIN));\n    slZoom.setPaintLabels(true);\n    slZoom.addChangeListener((event) -> diagram.setZoom(slZoom.getValue()));\n    slZoom.setToolTipText(TOOLTIP_TEXT_ZOOM);\n    add(slZoom);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ClockSignal.java",
    "content": "/*\n * @(#)ClockSignal.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Graphics2D;\nimport java.awt.geom.Line2D;\n\npublic class ClockSignal extends AbstractSignal<Void>\n{\n  private static final double SIGNAL_HEIGHT = 16.0;\n\n  public ClockSignal()\n  {\n    this(\"clock\");\n  }\n\n  public ClockSignal(final String label)\n  {\n    super(new SignalRendering.SignalParams(label));\n  }\n\n  @Override\n  public void record()\n  {\n    record(null, false);\n  }\n\n  @Override\n  protected double getSignalHeight() { return SIGNAL_HEIGHT; }\n\n  @Override\n  public double getDisplayHeight()\n  {\n    return SIGNAL_HEIGHT + 16.0;\n  }\n\n  private void drawUpArrow(final Graphics2D g, final double x, final double y)\n  {\n    final double arrowWidth = 0.3 * SIGNAL_HEIGHT;\n    final double arrowHeight = 0.3 * SIGNAL_HEIGHT;\n    g.draw(new Line2D.Double(x, y, x - 0.5 * arrowWidth, y + arrowHeight));\n    g.draw(new Line2D.Double(x, y, x + 0.5 * arrowWidth, y + arrowHeight));\n  }\n\n  @Override\n  public void paintCycle(final Graphics2D g, final double zoom,\n                         final double xStart, final double yBottom,\n                         final int cycle,\n                         final boolean firstCycle, final boolean lastCycle)\n  {\n    if (!next(cycle - 1)) return;\n    final double xFallingEdge = xStart + 0.5 * zoom;\n    final double xStop = xStart + zoom;\n    final double yTop = yBottom - SIGNAL_HEIGHT;\n    drawUpArrow(g, xStart, yTop);\n    g.draw(new Line2D.Double(xStart, yBottom, xStart, yTop));\n    g.draw(new Line2D.Double(xStart, yTop, xFallingEdge, yTop));\n    g.draw(new Line2D.Double(xFallingEdge, yTop, xFallingEdge, yBottom));\n    g.draw(new Line2D.Double(xFallingEdge, yBottom, xStop, yBottom));\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/Constants.java",
    "content": "/*\n * @(#)Constants.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Dimension;\nimport java.awt.Font;\n\npublic interface Constants\n{\n  static final String TOOLTIP_TEXT_LOAD =\n    \"load and run script for setting up emulation…\";\n  static final String TOOLTIP_TEXT_CYCLES =\n    \"number of cycles to emulate in one go\";\n  static final String TOOLTIP_TEXT_EMULATE =\n    \"emulate specified number of cycles in one go\";\n  static final String TOOLTIP_TEXT_CLEAR =\n    \"clear recorded cycles data\";\n  static final String TOOLTIP_TEXT_ZOOM =\n    \"change horizontal display scale\";\n  static final int ZOOM_MIN = 16;\n  static final int ZOOM_MAX = 112;\n  static final int ZOOM_DEFAULT = 32;\n  static final double TOP_MARGIN = 0.0;\n  static final double BOTTOM_MARGIN = 16.0;\n  static final double SIGNAL_SETUP_X = 4.0;\n  static final Font DEFAULT_FONT = Font.decode(null);\n  static final Font LABEL_FONT = DEFAULT_FONT.deriveFont(10.0f);\n  static final Dimension PREFERRED_LABEL_SIZE = new Dimension(120, 32);\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/CycleRuler.java",
    "content": "/*\n * @(#)CycleRuler.java 1.00 21/06/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.FontMetrics;\nimport java.awt.Graphics2D;\nimport java.awt.geom.Line2D;\n\npublic class CycleRuler extends AbstractSignal<Void>\n{\n  private static final double SIGNAL_HEIGHT = 16.0;\n  private static final int PADDING = 1;\n\n  public CycleRuler()\n  {\n    this(\"cycle count\");\n  }\n\n  public CycleRuler(final String label)\n  {\n    super(new SignalRendering.SignalParams(label));\n  }\n\n  @Override\n  public void record()\n  {\n    record(null, false);\n  }\n\n  @Override\n  protected double getSignalHeight() { return SIGNAL_HEIGHT; }\n\n  @Override\n  public double getDisplayHeight()\n  {\n    return SIGNAL_HEIGHT + 16.0;\n  }\n\n  @Override\n  public String getToolTipText(final int cycle)\n  {\n    return \"cycle #\" + cycle;\n  }\n\n  @Override\n  public void paintCycle(final Graphics2D g, final double zoom,\n                         final double xStart, final double yBottom,\n                         final int cycle,\n                         final boolean firstCycle, final boolean lastCycle)\n  {\n    if (!next(cycle - 1)) return;\n    final double tickYBottom = yBottom;\n    final double tickYTop = yBottom - 0.3 * SIGNAL_HEIGHT;\n    g.draw(new Line2D.Double(xStart, tickYBottom, xStart, tickYTop));\n    if ((cycle % 5) == 0) {\n      final double labelYBottom = yBottom - 0.5 * SIGNAL_HEIGHT;\n      final String label = String.format(\"%d\", cycle);\n      g.setFont(Constants.DEFAULT_FONT);\n      final FontMetrics fm = g.getFontMetrics(g.getFont());\n      final int width = fm.stringWidth(label) - PADDING;\n      g.drawString(label, (float)(xStart - 0.5 * width), (float)labelYBottom);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/Diagram.java",
    "content": "/*\n * @(#)Diagram.java 1.00 21/01/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.SwingUtilities;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.observer.GUIObserver;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Draws a timing diagram of a selected set of PIO signals and\n * generated from a specific PIO program.  By now, most of the\n * configuration is hard-wired in order to make it running out of the\n * box.\n *\n * TODO: Ellipsis, see e.g. Fig. 55.\n *\n * TODO: Labelled external data via GPIO or DMA (e.g. data bits \"D0\",\n * \"D1\", \"D2\", …).\n *\n * Syntax:\n * CLK=SIGNAL\n * DMA.SIGNAL_NAME=(SIGNAL|BIT)\n * SMx.SIGNAL_NAME=(SIGNAL|BIT)\n * GPIOx=(SIGNAL|BIT)\n */\npublic class Diagram extends GUIObserver\n{\n  private static final long serialVersionUID = -2547071637413332775L;\n\n  private static final String APP_TITLE = \"Diagram Creator\";\n  private static final String APP_FULL_NAME =\n    \"Timing Diagram Creator Version 0.1\";\n\n  private final DiagramModel model;\n  private final DiagramViewPanel diagramPanel;\n  private final TelemetryPanel telemetryPanel;\n  private final ScriptDialog scriptDialog;\n\n  private Diagram(final PrintStream console, final String[] argv)\n    throws IOException\n  {\n    super(APP_TITLE, APP_FULL_NAME, console, argv);\n    model = new DiagramModel(console, getSDK());\n    diagramPanel = new DiagramViewPanel(model);\n    telemetryPanel =\n      new TelemetryPanel(model, () -> diagramPanel.getLeftMostVisibleCycle());\n    configureModel();\n    modelChanged();\n    add(createView());\n    scriptDialog = new ScriptDialog(this, console);\n    pack();\n    setVisible(true);\n    startUpdating();\n  }\n\n  private JPanel createView()\n  {\n    final JPanel view = new JPanel();\n    view.setLayout(new BoxLayout(view, BoxLayout.PAGE_AXIS));\n    final JScrollPane scrollPane =\n      new JScrollPane(diagramPanel,\n                      ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,\n                      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n    view.add(scrollPane);\n    view.add(telemetryPanel);\n    return view;\n  }\n\n  public DiagramModel getModel()\n  {\n    return model;\n  }\n\n  @Override\n  protected ActionPanel createActionPanel()\n  {\n    return new ActionPanel(this);\n  }\n\n  @Override\n  protected MenuBar createMenuBar()\n  {\n    return new MenuBar(this, getSDK());\n  }\n\n  private void modelChanged()\n  {\n    diagramPanel.modelChanged();\n    telemetryPanel.modelChanged();\n  }\n\n  @Override\n  protected void updateView()\n  {\n    modelChanged();\n  }\n\n  public void showScriptDialog()\n  {\n    scriptDialog.setVisible(true);\n  }\n\n  /**\n   * Add pseudo signals that are not directly displayed, but provided\n   * for shared use for instruction rendering.\n   */\n  private void createInternalSignals() throws IOException\n  {\n    for (int pioNum = 0; pioNum < Constants.PIO_NUM; pioNum++) {\n      for (int smNum = 0; smNum < Constants.SM_COUNT; smNum++) {\n        final String labelPrefix = String.format(\"_PIO%d_SM%d_\", pioNum, smNum);\n        final int addressPinCtrl =\n          PIORegisters.getSMAddress(pioNum, smNum,\n                                    PIORegisters.Regs.SM0_PINCTRL);\n        final String labelPinCtrl = labelPrefix + \"PINCTRL\";\n        model.addInternalSignal(this, labelPinCtrl, addressPinCtrl);\n        final int addressExecCtrl =\n          PIORegisters.getSMAddress(pioNum, smNum,\n                                    PIORegisters.Regs.SM0_EXECCTRL);\n        final String labelExecCtrl = labelPrefix + \"EXECCTRL\";\n        model.addInternalSignal(this, labelExecCtrl, addressExecCtrl);\n        final int addressDelayCycle =\n          PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                       PIOEmuRegisters.Regs.SM0_DELAY_CYCLE);\n        final String labelDelayCycle = labelPrefix + \"DELAY_CYCLE\";\n        model.addInternalSignal(this, labelDelayCycle, addressDelayCycle);\n        final int addressDelay =\n          PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                       PIOEmuRegisters.Regs.SM0_DELAY);\n        final String labelDelay = labelPrefix + \"DELAY\";\n        model.addInternalSignal(this, labelDelay, addressDelay);\n        final int addressInstrOrigin =\n          PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                       PIOEmuRegisters.Regs.SM0_INSTR_ORIGIN);\n        final String labelInstrOrigin = labelPrefix + \"INSTR_ORIGIN\";\n        model.addInternalSignal(this, labelInstrOrigin, addressInstrOrigin);\n      }\n    }\n  }\n\n  private void configureModel() throws IOException\n  {\n    createInternalSignals();\n    model.addSignal(SignalFactory.createRuler(\"cycle#\")).setVisible(true);\n    model.addSignal(SignalFactory.createClockSignal(\"clock\")).setVisible(true);\n    model.addSignal(this, \"SM0_CLK_ENABLE\", PIOEmuRegisters.\n                    getAddress(0, PIOEmuRegisters.Regs.SM0_CLK_ENABLE), 0).\n      setVisible(true);\n    final GPIOIOBank0Registers.Regs regGpio0Status =\n      GPIOIOBank0Registers.Regs.GPIO0_STATUS;\n    for (int gpioNum = 0; gpioNum < 30; gpioNum++) {\n      final String label = \"GPIO\" + gpioNum + \" (out from peri)\";\n      final int address =\n        GPIOIOBank0Registers.getGPIOAddress(gpioNum, regGpio0Status);\n      model.addSignal(this, label + \" Value\", address, 8, 8).\n        setVisible(gpioNum < 2);\n      model.addSignal(this, label + \" Level\", address, 8, null, -1, -1);\n    }\n    final List<SignalFilter> noDelayFilter =\n      ValueFilterPanel.createFilters(true, false);\n    final int addrSm0Pc =\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM0_PC);\n    model.addSignal(this, \"SM0_PC\", addrSm0Pc);\n    model.addSignal(this, \"SM0_PC (hidden delay)\", addrSm0Pc,\n                    noDelayFilter, 0, 0);\n    final int instrAddr =\n      PIORegisters.getAddress(0, PIORegisters.Regs.SM0_INSTR);\n    final List<SignalFilter> displayFilters =\n      ValueFilterPanel.createFilters(true, true);\n    model.addSignal(SignalFactory.\n                    createFromRegister(this, getSDK(), \"PIO0_SM0_INSTR\",\n                                       instrAddr, 15, 0,\n                                       SignalRendering.Mnemonic,\n                                       displayFilters, 0, 0)).\n      setVisible(true);\n    final int addrSm0RegX =\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM0_REGX);\n    model.addSignal(this, \"SM0_REGX\", addrSm0RegX).setVisible(true);\n    final int addrSm0RegY =\n      PIOEmuRegisters.getAddress(0, PIOEmuRegisters.Regs.SM0_REGY);\n    model.addSignal(this, \"SM0_REGY\", addrSm0RegY);\n  }\n\n  public void clear()\n  {\n    model.resetSignals();\n    modelChanged();\n  }\n\n  private void applyCycles(final int count) throws IOException\n  {\n    model.applyCycles(count);\n    modelChanged();\n    diagramPanel.ensureCycleIsVisible(model.getSignalSize() - 1);\n    diagramPanel.rebuildToolTips();\n  }\n\n  public void applyCycles()\n  {\n    final int cycles = ((ActionPanel)getActionPanel()).getCycles();\n    try {\n      applyCycles(cycles);\n    } catch (final IOException e) {\n      final String title = \"Emulation Failed\";\n      final String message = \"I/O error: \" + e.getMessage();\n      JOptionPane.showMessageDialog(this, message, title,\n                                    JOptionPane.WARNING_MESSAGE);\n      clear();\n    }\n  }\n\n  public void setZoom(final int zoom)\n  {\n    diagramPanel.setZoom(zoom);\n  }\n\n  public RegisterIntSignal getInternalSignalByAddress(final int address)\n  {\n    return model.getInternalSignalByAddress(address);\n  }\n\n  public void pullSignals(final List<Signal> signals)\n  {\n    model.pullSignals(signals);\n    modelChanged();\n  }\n\n  public void pushSignals(final List<Signal> signals)\n  {\n    model.pushSignals(signals);\n    modelChanged();\n  }\n\n  public static void main(final String argv[])\n  {\n    final PrintStream console = System.out;\n    try {\n      new Diagram(System.out, argv);\n    } catch (final IOException e) {\n      console.println(e.getMessage());\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/DiagramModel.java",
    "content": "/*\n * @(#)DiagramModel.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class DiagramModel implements Iterable<Signal>\n{\n  private final PrintStream console;\n  private final SDK sdk;\n  private final HashMap<Integer, RegisterIntSignal> address2internalSignal;\n  private final List<Signal> signals;\n  private long wallClock;\n  private int signalSize;\n\n  private DiagramModel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public DiagramModel(final PrintStream console, final SDK sdk)\n    throws IOException\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    if (sdk == null) {\n      throw new NullPointerException(\"sdk\");\n    }\n    this.console = console;\n    this.sdk = sdk;\n    address2internalSignal = new HashMap<Integer, RegisterIntSignal>();\n    signals = new ArrayList<Signal>();\n    wallClock = -1;\n    signalSize = 0;\n  }\n\n  public Iterator<Signal> iterator()\n  {\n    return signals.iterator();\n  }\n\n  public Collection<RegisterIntSignal> getInternalSignals()\n  {\n    return address2internalSignal.values();\n  }\n\n  public Signal addInternalSignal(final Diagram diagram,\n                                  final String label, final int address)\n    throws IOException\n  {\n    final RegisterIntSignal signal =\n      SignalFactory.createInternal(diagram, sdk, label, address);\n    address2internalSignal.put(address, signal);\n    return signal;\n  }\n\n  public RegisterIntSignal getInternalSignalByAddress(final int address)\n  {\n    return address2internalSignal.get(address);\n  }\n\n  public Signal addSignal(final Signal signal)\n  {\n    if (signal == null) {\n      throw new NullPointerException(\"signal\");\n    }\n    signals.add(signal);\n    return signal;\n  }\n\n  public Signal addSignal(final Diagram diagram,\n                          final String label, final int address,\n                          final int msb, final int lsb,\n                          final List<SignalFilter> displayFilters,\n                          final int pioNum, final int smNum)\n    throws IOException\n  {\n    final Signal signal =\n      SignalFactory.\n      createFromRegister(diagram, sdk, label, address, msb, lsb,\n                         SignalRendering.Hex, displayFilters, pioNum, smNum);\n    return addSignal(signal);\n  }\n\n  public Signal addSignal(final Diagram diagram,\n                          final String label, final int address,\n                          final int msb, final int lsb)\n    throws IOException\n  {\n    return addSignal(diagram, label, address, msb, lsb, null, -1, -1);\n  }\n\n  public Signal addSignal(final Diagram diagram,\n                          final String label, final int address, final int bit,\n                          final List<SignalFilter> displayFilters,\n                          final int pioNum, final int smNum)\n    throws IOException\n  {\n    final RegisterBitSignal signal =\n      SignalFactory.createFromRegister(diagram, sdk, label, address, bit,\n                                       displayFilters, pioNum, smNum);\n    return addSignal(signal);\n  }\n\n  public Signal addSignal(final Diagram diagram, final String label,\n                          final int address, final int bit)\n    throws IOException\n  {\n    return addSignal(diagram, label, address, bit, null, -1, -1);\n  }\n\n  public Signal addSignal(final Diagram diagram,\n                          final String label, final int address)\n    throws IOException\n  {\n    return addSignal(diagram, label, address, 31, 0);\n  }\n\n  public Signal addSignal(final Diagram diagram,\n                          final String label, final int address,\n                          final List<SignalFilter> displayFilters,\n                          final int pioNum, final int smNum)\n    throws IOException\n  {\n    return addSignal(diagram, label, address, 31, 0,\n                     displayFilters, pioNum, smNum);\n  }\n\n  public Signal addSignal(final Diagram diagram, final int address)\n    throws IOException\n  {\n    return addSignal(diagram, null, address);\n  }\n\n  public Signal addSignal(final Diagram diagram, final int address,\n                          final List<SignalFilter> displayFilters,\n                          final int pioNum, final int smNum)\n    throws IOException\n  {\n    return addSignal(diagram, null, address, displayFilters, pioNum, smNum);\n  }\n\n  public void resetSignals()\n  {\n    for (final Signal signal : address2internalSignal.values()) {\n      signal.reset();\n    }\n    for (final Signal signal : signals) {\n      signal.reset();\n    }\n    signalSize = 0;\n  }\n\n  private void appendRecordToSignals() throws IOException\n  {\n    for (final Signal signal : address2internalSignal.values()) {\n      signal.record();\n    }\n    for (final Signal signal : signals) {\n      if (signal.getVisible()) {\n        signal.record();\n      }\n    }\n    signalSize++;\n  }\n\n  private void checkForUpdate() throws IOException\n  {\n    try {\n      final long wallClock = sdk.getWallClock();\n      if (wallClock == this.wallClock) {\n        // nothing to update\n      } else {\n        if (wallClock != this.wallClock + 1) {\n          // discontinuity in time => restart view\n          resetSignals();\n        }\n        appendRecordToSignals();\n      }\n      this.wallClock = wallClock;\n    } catch (final IOException e) {\n      console.println(\"error: failed reading wall clock: \" + e.getMessage());\n    }\n  }\n\n  public void applyCycles(final int count) throws IOException\n  {\n    if (count < 0) {\n      throw new IllegalArgumentException(\"count < 0: \" + count);\n    }\n    for (int cycle = 0; cycle < count; cycle++) {\n      sdk.triggerCyclePhase0(true);\n      checkForUpdate();\n      sdk.triggerCyclePhase1(true);\n    }\n  }\n\n  public int getSignalSize()\n  {\n    return signalSize;\n  }\n\n  public void pullSignals(final List<Signal> targetSignals)\n  {\n    targetSignals.clear();\n    for (final Signal signal : signals) {\n      targetSignals.add(signal);\n    }\n  }\n\n  public void pushSignals(final List<Signal> newSignals)\n  {\n    signals.clear();\n    for (final Signal signal : newSignals) {\n      signals.add(signal);\n    }\n    resetSignals();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/DiagramViewPanel.java",
    "content": "/*\n * @(#)DiagramViewPanel.java 1.00 21/06/26\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Dimension;\nimport java.awt.Rectangle;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollBar;\nimport javax.swing.JScrollPane;\nimport javax.swing.JViewport;\nimport javax.swing.Scrollable;\nimport javax.swing.ScrollPaneConstants;\nimport javax.swing.SwingUtilities;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class DiagramViewPanel extends JPanel implements Scrollable\n{\n  private static final long serialVersionUID = 6691239292598254146L;\n\n  private final DiagramModel model;\n  private final LegendPanel legendPanel;\n  private final SignalPanel signalPanel;\n  private final JScrollPane scrollPane;\n  private final JViewport viewport;\n  private final Dimension preferredViewportSize;\n\n  private DiagramViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public DiagramViewPanel(final DiagramModel model)\n    throws IOException\n  {\n    Objects.requireNonNull(model);\n    this.model = model;\n    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\n    legendPanel = new LegendPanel(model);\n    add(legendPanel);\n    add(Box.createHorizontalStrut(5));\n    signalPanel = new SignalPanel(model);\n    scrollPane =\n      new JScrollPane(signalPanel,\n                      ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,\n                      ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);\n    viewport = scrollPane.getViewport();\n    viewport.addChangeListener((event) -> rebuildToolTips());\n    add(scrollPane);\n    preferredViewportSize = new Dimension(720, 360);\n  }\n\n  @Override\n  public Dimension getPreferredScrollableViewportSize()\n  {\n    return preferredViewportSize;\n  }\n\n  @Override\n  public int getScrollableBlockIncrement(final Rectangle visibleRect,\n                                         final int orientation,\n                                         final int direction)\n  {\n    return 1;\n  }\n\n  @Override\n  public int getScrollableUnitIncrement(final Rectangle visibleRect,\n                                        final int orientation,\n                                        final int direction)\n  {\n    return 1;\n  }\n\n  @Override\n  public boolean getScrollableTracksViewportWidth()\n  {\n    return true;\n  }\n\n  @Override\n  public boolean getScrollableTracksViewportHeight()\n  {\n    return false;\n  }\n\n  public void modelChanged()\n  {\n    legendPanel.updatePreferredSize();\n    signalPanel.updatePreferredSize();\n    SwingUtilities.invokeLater(() -> signalPanel.repaint());\n  }\n\n  public void ensureCycleIsVisible(final int cycle)\n  {\n    scrollPane.validate(); // ensure scrollbar max value is up to date\n    final double leftMostVisibleCycle = getLeftMostVisibleCycle();\n    final double rightMostVisibleCycle = getRightMostVisibleCycle();\n    if ((cycle >= leftMostVisibleCycle) &&\n        (cycle < (int)rightMostVisibleCycle))\n      return;\n    final double newLeftMostVisibleCycle;\n    if (cycle < leftMostVisibleCycle) {\n      newLeftMostVisibleCycle = cycle;\n    } else /* (cycle >= (int)rightMostVisibleCycle) */ {\n      newLeftMostVisibleCycle =\n        cycle + leftMostVisibleCycle - rightMostVisibleCycle + 1;\n    }\n    setLeftMostVisibleCycle(newLeftMostVisibleCycle);\n  }\n\n  public double getRightMostVisibleCycle()\n  {\n    final JScrollBar scrollBar = scrollPane.getHorizontalScrollBar();\n    final int scrollBarValue = scrollBar.getValue();\n    final int viewPortWidth = scrollPane.getViewportBorderBounds().width;\n    return signalPanel.x2cycle(scrollBarValue + viewPortWidth);\n  }\n\n  public double getLeftMostVisibleCycle()\n  {\n    final JScrollBar scrollBar = scrollPane.getHorizontalScrollBar();\n    final int scrollBarValue = scrollBar.getValue();\n    return signalPanel.x2cycle(scrollBarValue);\n  }\n\n  private void setLeftMostVisibleCycle(final double cycle)\n  {\n    final JScrollBar scrollBar = scrollPane.getHorizontalScrollBar();\n    final double scrollBarValue = signalPanel.cycle2x(cycle);\n    scrollBar.setValue((int)Math.round(scrollBarValue));\n    SwingUtilities.invokeLater(() -> {\n        rebuildToolTips();\n        signalPanel.repaint();\n      });\n  }\n\n  public void setZoom(final int zoom)\n  {\n    final double leftMostCycle = getLeftMostVisibleCycle();\n    signalPanel.setZoom(zoom);\n    setLeftMostVisibleCycle(leftMostCycle);\n  }\n\n  /**\n   * While redraw of viewport is done via paintComponent() methods,\n   * rebuild of tooltips is triggered separately and executed via this\n   * method, such that partial repaint (e.g. when a displayed tooltip\n   * disappears) does not retrigger a complete rebuild of all\n   * tooltips.\n   */\n  public void rebuildToolTips()\n  {\n    final Rectangle viewRect = viewport.getViewRect();\n    signalPanel.rebuildToolTips(viewRect);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/LegendPanel.java",
    "content": "/*\n * @(#)LegendPanel.java 1.00 21/06/26\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Dimension;\nimport java.awt.FontMetrics;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JComponent;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class LegendPanel extends JComponent\n{\n  private static final long serialVersionUID = 6634499718877657461L;\n\n  private static final int LEGEND_WIDTH = 200;\n  private static final double LABEL_MARGIN_BOTTOM = 4.0;\n  private static final double LABEL_MARGIN_RIGHT = 10.0;\n\n  private final DiagramModel model;\n  private final Dimension preferredSize;\n\n  private LegendPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public LegendPanel(final DiagramModel model)\n  {\n    Objects.requireNonNull(model);\n    this.model = model;\n    setMinimumSize(new Dimension(LEGEND_WIDTH, 0));\n    setMaximumSize(new Dimension(LEGEND_WIDTH, Integer.MAX_VALUE));\n    preferredSize = new Dimension(LEGEND_WIDTH, 0);\n    updatePreferredSize();\n  }\n\n  private void updatePreferredHeight()\n  {\n    double preferredHeight = Constants.TOP_MARGIN + Constants.BOTTOM_MARGIN;\n    for (final Signal signal : model) {\n      if (signal.getVisible()) {\n        preferredHeight += signal.getDisplayHeight();\n      }\n    }\n    preferredSize.setSize(preferredSize.getWidth(), (int)preferredHeight);\n  }\n\n  public void updatePreferredSize()\n  {\n    updatePreferredHeight();\n    revalidate();\n  }\n\n  @Override\n  public Dimension getPreferredSize()\n  {\n    return preferredSize;\n  }\n\n  private void paintLabel(final Graphics2D g,\n                          final double xStart, final double yBottom,\n                          final String label)\n  {\n    final FontMetrics fm = g.getFontMetrics(g.getFont());\n    final int width = fm.stringWidth(label);\n    g.drawString(label,\n                 (float)(xStart - width - LABEL_MARGIN_RIGHT),\n                 (float)(yBottom - LABEL_MARGIN_BOTTOM));\n  }\n\n  private void paintLabels(final Graphics2D g)\n  {\n    g.setFont(Constants.DEFAULT_FONT);\n    double y = Constants.TOP_MARGIN;\n    for (final Signal signal : model) {\n      if (signal.getVisible()) {\n        final String label = signal.getLabel();\n        final double height = signal.getDisplayHeight();\n        paintLabel(g, LEGEND_WIDTH, y += height, label);\n      }\n    }\n  }\n\n  @Override\n  public void paintComponent(final Graphics g)\n  {\n    super.paintComponent(g);\n    paintLabels((Graphics2D)g);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/MenuBar.java",
    "content": "/*\n * @(#)MenuBar.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.io.PrintStream;\nimport javax.swing.KeyStroke;\nimport javax.swing.JMenu;\nimport javax.swing.JMenuItem;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class MenuBar\n  extends org.soundpaint.rp2040pio.observer.MenuBar<Diagram>\n  implements Constants\n{\n  private static final long serialVersionUID = -5984414867480448181L;\n\n  private final Diagram diagram;\n  private final SignalsDialog signalsDialog;\n\n  public MenuBar(final Diagram diagram, final SDK sdk)\n  {\n    super(diagram);\n    if (diagram == null) {\n      throw new NullPointerException(\"diagram\");\n    }\n    this.diagram = diagram;\n    signalsDialog = new SignalsDialog(diagram, sdk);\n  }\n\n  @Override\n  protected void addAdditionalFileMenuItems(final JMenu fileMenu,\n                                            final Diagram diagram)\n  {\n    final JMenuItem script =\n      SwingUtils.createIconMenuItem(\"floppy-blue16x16.png\", \"Open…\");\n    script.setMnemonic(KeyEvent.VK_O);\n    script.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,\n                                                 ActionEvent.ALT_MASK));\n    script.getAccessibleContext().\n      setAccessibleDescription(\"Open Script for Execution\");\n    script.addActionListener((event) -> diagram.showScriptDialog());\n    fileMenu.add(script);\n  }\n\n  @Override\n  protected void addAdditionalMenus(final Diagram diagram)\n  {\n    add(createEditMenu());\n    add(createEmulationMenu());\n  }\n\n  private JMenu createEditMenu()\n  {\n    final JMenu edit = new JMenu(\"Edit\");\n    edit.setMnemonic(KeyEvent.VK_E);\n\n    final JMenuItem properties = new JMenuItem(\"Signals…\");\n    properties.setMnemonic(KeyEvent.VK_P);\n    properties.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P,\n                                                     ActionEvent.ALT_MASK));\n    properties.getAccessibleContext().\n      setAccessibleDescription(\"Open Properties Dialog\");\n    properties.addActionListener((event) -> signalsDialog.open());\n    edit.add(properties);\n\n    return edit;\n  }\n\n  private JMenu createEmulationMenu()\n  {\n    final JMenu emulation = new JMenu(\"Emulation\");\n    emulation.setMnemonic(KeyEvent.VK_M);\n\n    final JMenuItem emulate =\n      SwingUtils.createIconMenuItem(\"cycle16x16.png\", \"Emulate Cycles\");\n    emulate.setMnemonic(KeyEvent.VK_E);\n    emulate.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U,\n                                                  ActionEvent.ALT_MASK));\n    emulate.getAccessibleContext().\n      setAccessibleDescription(TOOLTIP_TEXT_EMULATE);\n    emulate.addActionListener((event) -> diagram.applyCycles());\n    emulation.add(emulate);\n\n    final JMenuItem clear =\n      SwingUtils.createIconMenuItem(\"trash16x16.png\", \"Clear Recorded Cycles\");\n    clear.setMnemonic(KeyEvent.VK_C);\n    clear.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,\n                                                ActionEvent.ALT_MASK));\n    clear.getAccessibleContext().\n      setAccessibleDescription(TOOLTIP_TEXT_CLEAR);\n    clear.addActionListener((event) -> diagram.clear());\n    emulation.add(clear);\n\n    return emulation;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/RegisterBitSignal.java",
    "content": "/*\n * @(#)RegisterBitSignal.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Graphics2D;\nimport java.awt.geom.Line2D;\nimport java.awt.geom.Rectangle2D;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class RegisterBitSignal extends ValuedSignal<Bit>\n{\n  private static final double SIGNAL_HEIGHT = 16.0;\n\n  public RegisterBitSignal(final SignalRendering.SignalParams signalParams)\n  {\n    this(signalParams, null);\n  }\n\n  public RegisterBitSignal(final SignalRendering.SignalParams signalParams,\n                           final Supplier<Boolean> changeInfoGetter)\n  {\n    super(SignalRendering.Bit, signalParams, changeInfoGetter);\n    final int msb = signalParams.getMsb();\n    final int lsb = signalParams.getLsb();\n    if (msb != lsb) {\n      final String message =\n        String.format(\"RegisterBitSignal: msb != lsb: %d != %d\",\n                      msb, lsb);\n      throw new IllegalArgumentException(message);\n    }\n  }\n\n  public int getBit()\n  {\n    return getSignalParams().getMsb();\n  }\n\n  @Override\n  public double getDisplayHeight()\n  {\n    return SIGNAL_HEIGHT + 16.0;\n  }\n\n  @Override\n  protected Bit sampleValue() throws IOException\n  {\n    final SignalRendering.SignalParams signalParams = getSignalParams();\n    final SDK sdk = signalParams.getSDK();\n    final int address = signalParams.getAddress();\n    final int bit = getBit();\n    return Bit.fromValue(sdk.readAddress(address, bit, bit));\n  }\n\n  @Override\n  public String getToolTipText(final int cycle)\n  {\n    final Bit bit = getValue(cycle);\n    return bit != null ? bit.getLevel() : null;\n  }\n\n  @Override\n  public void paintCycle(final Graphics2D g, final double zoom,\n                         final double xStart, final double yBottom,\n                         final int cycle,\n                         final boolean firstCycle, final boolean lastCycle)\n  {\n    if (!next(cycle - 1)) return;\n    final double xStable = xStart + Constants.SIGNAL_SETUP_X;\n    final double xStop = xStart + zoom;\n\n    final Bit prevBit = getValue(cycle - 1);\n    final Bit bit = getValue(cycle);\n    if (bit != null) {\n      final double yStable =\n        yBottom - (bit == Bit.HIGH ? SIGNAL_HEIGHT : 0.0);\n      g.draw(new Line2D.Double(xStable, yStable, xStop, yStable));\n      if (prevBit != null) {\n        final double yPrev =\n          firstCycle ?\n          yStable :\n          yBottom - (prevBit == Bit.HIGH ? SIGNAL_HEIGHT : 0.0);\n        g.draw(new Line2D.Double(xStart, yPrev, xStable, yStable));\n      }\n    } else {\n      final double xPatternStart = changed(cycle) ? xStable : xStart;\n      final Graphics2D fillG = (Graphics2D)g.create();\n      final double yTop = yBottom - SIGNAL_HEIGHT;\n      final Rectangle2D.Double rectangle =\n        new Rectangle2D.Double(xPatternStart, yTop + 1,\n                               xStop - xPatternStart + 1, yBottom - yTop - 1);\n      fillG.setPaint(FILL_PAINT);\n      fillG.fill(rectangle);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/RegisterIntSignal.java",
    "content": "/*\n * @(#)RegisterIntSignal.java 1.00 21/07/23\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.FontMetrics;\nimport java.awt.Graphics2D;\nimport java.awt.TexturePaint;\nimport java.awt.geom.Line2D;\nimport java.awt.geom.Rectangle2D;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Supplier;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class RegisterIntSignal extends ValuedSignal<Integer>\n{\n  private static final double VALUE_LABEL_MARGIN_BOTTOM = 8.0;\n\n  public RegisterIntSignal(final SignalRendering valueRendering,\n                           final SignalRendering.SignalParams signalParams)\n  {\n    this(valueRendering, signalParams, null);\n  }\n\n  public RegisterIntSignal(final SignalRendering valueRendering,\n                           final SignalRendering.SignalParams signalParams,\n                           final Supplier<Boolean> changeInfoGetter)\n  {\n    super(valueRendering, signalParams, changeInfoGetter);\n    Objects.requireNonNull(valueRendering.getToolTipRenderer());\n    if (valueRendering == SignalRendering.Bit) {\n      final String message = \"this class does not support bit rendering\";\n      throw new IllegalArgumentException(message);\n    }\n  }\n\n  @Override\n  public double getDisplayHeight()\n  {\n    return SIGNAL_HEIGHT + 16.0;\n  }\n\n  @Override\n  protected Integer sampleValue() throws IOException\n  {\n    final SignalRendering.SignalParams signalParams = getSignalParams();\n    final SDK sdk = signalParams.getSDK();\n    final int address = signalParams.getAddress();\n    final int msb = signalParams.getMsb();\n    final int lsb = signalParams.getLsb();\n    return sdk.readAddress(address, msb, lsb);\n  }\n\n  @Override\n  public String getToolTipText(final int cycle)\n  {\n    final Integer value = getValue(cycle);\n    if (value == null)\n      return null;\n    final SignalRendering.ValueRenderer toolTipRenderer =\n      getValueRendering().getToolTipRenderer();\n    final SignalRendering.SignalParams signalParams = getSignalParams();\n    return\n      toolTipRenderer != null ?\n      toolTipRenderer.renderValue(signalParams, cycle, value) : null;\n  }\n\n  private String getRenderedValue(final int cycle)\n  {\n    final Integer value = getValue(cycle);\n    if (value == null)\n      return null;\n    final SignalRendering.ValueRenderer lifeLineRenderer =\n      getValueRendering().getLifeLineRenderer();\n    return\n      lifeLineRenderer != null ?\n      lifeLineRenderer.renderValue(getSignalParams(), cycle, value) :\n      String.valueOf(value);\n  }\n\n  private static void paintValuedLabel(final Graphics2D g,\n                                       final double zoom,\n                                       final double xStart,\n                                       final double yBottom,\n                                       final String label,\n                                       final int cycles)\n  {\n    if (label != null) {\n      g.setFont(Constants.LABEL_FONT);\n      final FontMetrics fm = g.getFontMetrics(g.getFont());\n      final int width = fm.stringWidth(label);\n      final double xLabelStart =\n        xStart - 0.5 * (cycles * zoom - Constants.SIGNAL_SETUP_X + width);\n\n      final double yTextBottom = yBottom - VALUE_LABEL_MARGIN_BOTTOM;\n      g.drawString(label, (float)xLabelStart, (float)yTextBottom);\n    }\n  }\n\n  @Override\n  public void paintCycle(final Graphics2D g, final double zoom,\n                         final double xStart, final double yBottom,\n                         final int cycle,\n                         final boolean firstCycle, final boolean lastCycle)\n  {\n    // Draw previous value only if finished, since current value may\n    // be still ongoing such that centered display of text is not yet\n    // reached.  However, if this is the last cycle for that a value\n    // has been recorded, then draw it anyway, since we can not forsee\n    // the future signal and thus print the current state.\n    if (!next(cycle) && !lastCycle) return;\n\n    if (changed(cycle) && !firstCycle) {\n      // signal changed => print label of previous, now finished\n      // value; but exclude first cycle, as it will be handled on next\n      // turn\n      paintValuedLabel(g, zoom, xStart, yBottom,\n                       getRenderedValue(cycle - 1),\n                       getNotChangedSince(cycle - 1) + 1);\n    }\n\n    // draw lines for current value\n    final double yTop = yBottom - SIGNAL_HEIGHT;\n    final double xStable = xStart + Constants.SIGNAL_SETUP_X;\n    final double xStop = xStart + zoom;\n    if (changed(cycle) && !firstCycle) {\n      g.draw(new Line2D.Double(xStart, yTop, xStable, yBottom));\n      g.draw(new Line2D.Double(xStart, yBottom, xStable, yTop));\n    } else {\n      g.draw(new Line2D.Double(xStart, yBottom, xStable, yBottom));\n      g.draw(new Line2D.Double(xStart, yTop, xStable, yTop));\n    }\n    g.draw(new Line2D.Double(xStable, yTop, xStop, yTop));\n    g.draw(new Line2D.Double(xStable, yBottom, xStop, yBottom));\n    if (getValue(cycle) == null) {\n      final double xPatternStart = changed(cycle) ? xStable : xStart;\n      final Graphics2D fillG = (Graphics2D)g.create();\n      final Rectangle2D.Double rectangle =\n        new Rectangle2D.Double(xPatternStart, yTop + 1,\n                               xStop - xPatternStart + 1, yBottom - yTop - 1);\n      fillG.setPaint(FILL_PAINT);\n      fillG.fill(rectangle);\n    }\n\n    if (lastCycle) {\n      // print label as preview for not yet finished value\n      paintValuedLabel(g, zoom, xStart, yBottom,\n                       getRenderedValue(cycle), getNotChangedSince(cycle) - 1);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ScriptDialog.java",
    "content": "/*\n * @(#)ScriptDialog.java 1.00 21/04/09\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.BorderLayout;\nimport java.awt.Frame;\nimport java.awt.event.KeyEvent;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\n\npublic class ScriptDialog extends JDialog\n{\n  private static final long serialVersionUID = 5065109349974149543L;\n\n  private class ActionPanel extends Box\n  {\n    private static final long serialVersionUID = -3909730642902134887L;\n\n    private final JButton btExecute;\n    private final JButton btClose;\n\n    public ActionPanel()\n    {\n      super(BoxLayout.LINE_AXIS);\n      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n      btExecute = new JButton(\"Execute\");\n      btExecute.setMnemonic(KeyEvent.VK_E);\n      btExecute.addActionListener((event) -> {\n          scriptSelectionPanel.execute();\n        });\n      add(btExecute);\n      add(Box.createHorizontalGlue());\n      btClose = new JButton(\"Close\");\n      btClose.setMnemonic(KeyEvent.VK_C);\n      btClose.addActionListener((event) -> {\n          ScriptDialog.this.setVisible(false);\n        });\n      add(btClose);\n    }\n  }\n\n  private final ScriptSelectionPanel scriptSelectionPanel;\n\n  private ScriptDialog()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public ScriptDialog(final Frame owner, final PrintStream console)\n  {\n    super(owner, \"Load\");\n    Objects.requireNonNull(console);\n    scriptSelectionPanel = new ScriptSelectionPanel(console);\n    getContentPane().add(scriptSelectionPanel);\n    getContentPane().add(new ActionPanel(), BorderLayout.SOUTH);\n    pack();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ScriptSelectionPanel.java",
    "content": "/*\n * @(#)ScriptSelectionPanel.java 1.00 21/04/09\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.event.KeyEvent;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Collectors;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JComboBox;\nimport javax.swing.JFileChooser;\nimport javax.swing.JLabel;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JTabbedPane;\nimport javax.swing.JTextArea;\nimport javax.swing.JTextField;\nimport javax.swing.border.Border;\nimport javax.swing.border.EtchedBorder;\nimport javax.swing.border.TitledBorder;\nimport javax.swing.filechooser.FileNameExtensionFilter;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.monitor.Monitor;\n\npublic class ScriptSelectionPanel extends Box\n{\n  private static final long serialVersionUID = 3945227462166267605L;\n  private static final Dimension PREFERRED_SHOW_EXAMPLE_SIZE =\n    new Dimension(480, 320);\n\n  private final PrintStream console;\n  private final JFileChooser scriptFileChooser;\n  private final JTabbedPane tabbedPane;\n  private final ExampleSelectionPanel exampleSelectionPanel;\n  private final FileSelectionPanel fileSelectionPanel;\n\n  public ScriptSelectionPanel(final PrintStream console)\n  {\n    super(BoxLayout.PAGE_AXIS);\n    Objects.requireNonNull(console);\n    this.console = console;\n    scriptFileChooser = createScriptFileChooser();\n    tabbedPane = new JTabbedPane();\n    add(tabbedPane);\n    exampleSelectionPanel = new ExampleSelectionPanel();\n    tabbedPane.addTab(\"Example Scripts\", null, exampleSelectionPanel,\n                      \"Load & configure emulator by running an example script\");\n    tabbedPane.setMnemonicAt(0, KeyEvent.VK_X);\n    fileSelectionPanel = new FileSelectionPanel();\n    tabbedPane.addTab(\"User Scripts\", null, fileSelectionPanel,\n                      \"Load & configure emulator by running a user script\");\n    tabbedPane.setMnemonicAt(1, KeyEvent.VK_U);\n  }\n\n  private JFileChooser createScriptFileChooser()\n  {\n    final JFileChooser scriptFileChooser = new JFileChooser();\n    scriptFileChooser.setDialogTitle(\"Select Monitor Script\");\n    scriptFileChooser.setAcceptAllFileFilterUsed(true);\n    final FileNameExtensionFilter filter =\n      new FileNameExtensionFilter(\"Monitor script file (*.mon)\", \"MON\", \"mon\");\n    scriptFileChooser.setFileFilter(filter);\n    return scriptFileChooser;\n  }\n\n  private String[] getExampleScripts()\n  {\n    final String suffix = \".mon\";\n    final List<String> examples;\n    try {\n      examples =\n        IOUtils.list(\"examples\").stream().\n        filter(s -> s.endsWith(suffix)).\n        map(s -> { return s.substring(0, s.length() - suffix.length()); }).\n        collect(Collectors.toList());\n    } catch (final IOException e) {\n      console.println(e.getMessage());\n      return new String[0];\n    }\n    return examples.stream().toArray(String[]::new);\n  }\n\n  private InputStream getExampleScriptStream(final String scriptName)\n  {\n    final InputStream in;\n    try {\n      in = IOUtils.getStreamForResourcePath(\"/examples/\" + scriptName + \".mon\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"Built-in script \\\"%s\\\" not found: %s\",\n                      scriptName, e.getMessage());\n      JOptionPane.showMessageDialog(this, message,\n                                    \"Internal Error\",\n                                    JOptionPane.ERROR_MESSAGE);\n      return null;\n    }\n    return in;\n  }\n\n  private void showExampleScript(final String scriptName)\n  {\n    final InputStream in = getExampleScriptStream(scriptName);\n    if (in == null) return;\n    final BufferedReader reader = new BufferedReader(new InputStreamReader(in));\n    final String title = String.format(\"Script %s\", scriptName);\n    final JTextArea taScript = new JTextArea(10, 10);\n    taScript.setEditable(false);\n    try {\n      taScript.read(reader, scriptName);\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed reading script %s: %s\",\n                      scriptName, e.getMessage());\n      JOptionPane.showMessageDialog(this, message, title,\n                                    JOptionPane.ERROR_MESSAGE);\n    }\n    final JScrollPane spScript = new JScrollPane(taScript);\n    spScript.setPreferredSize(PREFERRED_SHOW_EXAMPLE_SIZE);\n    JOptionPane.showMessageDialog(this, spScript, title,\n                                  JOptionPane.INFORMATION_MESSAGE);\n  }\n\n  private void executeScript(final String scriptId,\n                             final InputStream in,\n                             final PrintStream console)\n  {\n    console.printf(\"%s: executing…%n\", scriptId);\n    final int exitCode = Monitor.main(new String[0], in, console, true);\n    final String message;\n    final String title;\n    final int messageType;\n    if (exitCode == 0) {\n      message =\n        String.format(\"%s: execution successfully completed%n\", scriptId);\n      title = \"Script Completed\";\n      messageType = JOptionPane.INFORMATION_MESSAGE;\n    } else {\n      message =\n        String.format(\"%s: execution failed with exit code %d%n\",\n                      scriptId, exitCode);\n      title = \"Script Failed\";\n      messageType = JOptionPane.ERROR_MESSAGE;\n    }\n    console.printf(message);\n    JOptionPane.showMessageDialog(this, message, title, messageType);\n  }\n\n  private void executeExampleScript(final String scriptName)\n  {\n    final InputStream in = getExampleScriptStream(scriptName);\n    if (in == null) return;\n    executeScript(scriptName, in, console);\n  }\n\n  private void executeFileScript(final String scriptFilePath)\n  {\n    final InputStream in;\n    try {\n      in = IOUtils.getStreamForResourcePath(scriptFilePath);\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"Script file \\\"%s\\\" not found: %s\",\n                      scriptFilePath, e.getMessage());\n      JOptionPane.showMessageDialog(this, message,\n                                    \"I/O Error\",\n                                    JOptionPane.ERROR_MESSAGE);\n      return;\n    }\n    executeScript(scriptFilePath, in, console);\n  }\n\n  public void execute()\n  {\n    final Component selectedComponent = tabbedPane.getSelectedComponent();\n    if (selectedComponent == exampleSelectionPanel) {\n      exampleSelectionPanel.execute();\n    } else if (selectedComponent == fileSelectionPanel) {\n      fileSelectionPanel.execute();\n    } else {\n      console.printf(\"warning: unexpected tab: %s%n\", selectedComponent);\n    }\n  }\n\n  private class ExampleSelectionPanel extends JPanel\n  {\n    private static final long serialVersionUID = -7249218754281104584L;\n\n    private final JComboBox<String> cbExamples;\n\n    private void execute()\n    {\n      executeExampleScript((String)cbExamples.getSelectedItem());\n    }\n\n    private ExampleSelectionPanel()\n    {\n      setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n      final Border loweredEtched =\n        BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);\n      final TitledBorder titled =\n        BorderFactory.createTitledBorder(loweredEtched,\n                                         \"Select built-in example\");\n      titled.setTitleJustification(TitledBorder.CENTER);\n      final Box selectionLine = new Box(BoxLayout.LINE_AXIS);\n      setBorder(titled);\n      add(selectionLine);\n      final JLabel lbExampleScript = new JLabel(\"Examples:\");\n      lbExampleScript.setDisplayedMnemonic(KeyEvent.VK_X);\n      selectionLine.add(lbExampleScript);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      cbExamples = new JComboBox<String>(getExampleScripts());\n      cbExamples.setMaximumSize(cbExamples.getPreferredSize());\n      lbExampleScript.setLabelFor(cbExamples);\n      selectionLine.add(cbExamples);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      final JButton btShow = new JButton(\"Show\");\n      btShow.setMnemonic(KeyEvent.VK_S);\n      btShow.addActionListener((event) -> {\n          showExampleScript((String)cbExamples.getSelectedItem());\n        });\n      selectionLine.add(btShow);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      selectionLine.add(Box.createHorizontalGlue());\n      SwingUtils.setPreferredHeightAsMaximum(selectionLine);\n      add(Box.createVerticalGlue());\n    }\n  }\n\n  private class FileSelectionPanel extends JPanel\n  {\n    private static final long serialVersionUID = -6631139141176608724L;\n\n    private final JTextField tfFileName;\n\n    private void execute()\n    {\n      executeFileScript(tfFileName.getText());\n    }\n\n    private FileSelectionPanel()\n    {\n      setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n      final Border loweredEtched =\n        BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);\n      final TitledBorder titled =\n        BorderFactory.createTitledBorder(loweredEtched, \"Choose file\");\n      titled.setTitleJustification(TitledBorder.CENTER);\n      setBorder(titled);\n      final Box selectionLine = new Box(BoxLayout.LINE_AXIS);\n      add(selectionLine);\n      final JLabel lbScriptFilePath = new JLabel(\"File path:\");\n      lbScriptFilePath.setDisplayedMnemonic(KeyEvent.VK_F);\n      selectionLine.add(lbScriptFilePath);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      tfFileName = new JTextField();\n      tfFileName.setColumns(15);\n      tfFileName.setMaximumSize(tfFileName.getPreferredSize());\n      lbScriptFilePath.setLabelFor(tfFileName);\n      selectionLine.add(tfFileName);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      final JButton btOpen = new JButton(\"Browse…\");\n      btOpen.setMnemonic(KeyEvent.VK_B);\n      btOpen.addActionListener((event) -> {\n          final int result = scriptFileChooser.showOpenDialog(this);\n          if (result == JFileChooser.APPROVE_OPTION) {\n            final File file = scriptFileChooser.getSelectedFile();\n            tfFileName.setText(file.getPath());\n          }\n        });\n      selectionLine.add(btOpen);\n      selectionLine.add(Box.createHorizontalStrut(5));\n      selectionLine.add(Box.createHorizontalGlue());\n      SwingUtils.setPreferredHeightAsMaximum(selectionLine);\n      add(Box.createVerticalGlue());\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/Signal.java",
    "content": "/*\n * @(#)Signal.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Graphics2D;\nimport java.io.IOException;\nimport java.util.List;\n\npublic interface Signal\n{\n  void reset();\n  String getLabel();\n  double getDisplayHeight();\n  int size();\n  String getToolTipText(final int cycle);\n  void record() throws IOException;\n  int getNotChangedSince(final int cycle);\n  void setVisible(final boolean visible);\n  boolean getVisible();\n  void paintCycle(final Graphics2D g, final double zoom,\n                  final double xStart, final double yBottom,\n                  final int cycle,\n                  final boolean isFirstCycle, final boolean isLastCycle);\n  void createToolTip(final List<ToolTip> toolTips,\n                     final int cycle,\n                     final boolean isFirstCycle,\n                     final boolean isLastCycle,\n                     final double zoom,\n                     final double xStart,\n                     final double yBottom);\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalDialog.java",
    "content": "/*\n * @(#)SignalDialog.java 1.00 21/06/29\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dialog;\nimport java.awt.event.KeyEvent;\nimport java.util.Objects;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SignalDialog extends JDialog\n{\n  private static final long serialVersionUID = 4433806198970312268L;\n\n  @FunctionalInterface\n  public static interface SignalConsumer\n  {\n    void accept(final Integer index, final Signal signal,\n                final Boolean add);\n  }\n\n  private class ActionPanel extends Box\n  {\n    private static final long serialVersionUID = 7200614607584132864L;\n\n    private final JButton btApply;\n    private final JButton btCancel;\n\n    public ActionPanel()\n    {\n      super(BoxLayout.LINE_AXIS);\n      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n      btApply = new JButton();\n      btApply.addActionListener((event) -> {\n          if (SignalDialog.this.apply()) {\n            SignalDialog.this.setVisible(false);\n          }\n        });\n      add(btApply);\n      add(Box.createHorizontalGlue());\n      btCancel = new JButton(\"Cancel\");\n      btCancel.setMnemonic(KeyEvent.VK_C);\n      btCancel.addActionListener((event) -> {\n          SignalDialog.this.setVisible(false);\n        });\n      add(btCancel);\n    }\n  }\n\n  private final Diagram diagram;\n  private final SignalConsumer signalConsumer;\n  private final SignalFactoryPanel signalFactoryPanel;\n  private final ActionPanel actionPanel;\n  private int index;\n  private Signal editSignal;\n\n  private SignalDialog()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SignalDialog(final Diagram diagram, final SDK sdk,\n                      final SignalConsumer signalConsumer,\n                      final BiFunction<String, Signal, String> labelChecker)\n  {\n    super(diagram, \"Signal\", Dialog.ModalityType.DOCUMENT_MODAL);\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(signalConsumer);\n    Objects.requireNonNull(labelChecker);\n    this.diagram = diagram;\n    this.signalConsumer = signalConsumer;\n    getContentPane().add(signalFactoryPanel =\n                         new SignalFactoryPanel(diagram, sdk, labelChecker));\n    getContentPane().add(actionPanel = new ActionPanel(), BorderLayout.SOUTH);\n  }\n\n  private boolean apply()\n  {\n    final Signal signal = signalFactoryPanel.createSignal(editSignal);\n    if (signal != null) {\n      if (editSignal != null) {\n        signalConsumer.accept(index, signal, false);\n      } else {\n        signal.setVisible(true);\n        signalConsumer.accept(index, signal, true);\n      }\n      return true;\n    }\n    return false;\n  }\n\n  public void open(final int index, final Signal editSignal)\n  {\n    if (editSignal != null) {\n      actionPanel.btApply.setText(\"Apply\");\n      actionPanel.btApply.setMnemonic(KeyEvent.VK_A);\n      signalFactoryPanel.load(editSignal);\n      setTitle(String.format(\"Edit Signal %s\", editSignal.getLabel()));\n    } else {\n      actionPanel.btApply.setText(\"Add\");\n      actionPanel.btApply.setMnemonic(KeyEvent.VK_A);\n      signalFactoryPanel.load(null);\n      setTitle(String.format(\"Insert New Signal After Signal #%d\", index - 1));\n    }\n    this.editSignal = editSignal;\n    this.index = index;\n    pack();\n    setVisible(true);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalFactory.java",
    "content": "/*\n * @(#)SignalFactory.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Objects;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\n/**\n * Configuration of a timing diagram.\n */\npublic class SignalFactory\n{\n  public static CycleRuler createRuler(final String label)\n  {\n    return new CycleRuler(label);\n  }\n\n  public static ClockSignal createClockSignal(final String label)\n  {\n    return new ClockSignal(label);\n  }\n\n  private static String createSignalLabel(final SDK sdk, final String label,\n                                          final int address,\n                                          final String bitRange)\n    throws IOException\n  {\n    return\n      (label != null) ? label : sdk.getLabelForAddress(address) + bitRange;\n  }\n\n  private static String createSignalLabel(final SDK sdk, final String label,\n                                          final int address, final int bit)\n    throws IOException\n  {\n    return createSignalLabel(sdk, label, address, \"_\" + bit);\n  }\n\n  private static String createSignalLabel(final SDK sdk, final String label,\n                                          final int address,\n                                          final int msb, final int lsb)\n    throws IOException\n  {\n    return createSignalLabel(sdk, label, address,\n                             ((lsb == 0) && (msb == 31) ? \"\" :\n                              (\"_\" + msb +\n                               (lsb != msb ? \":\" + lsb : \"\"))));\n\n  }\n\n  public static RegisterBitSignal\n    createFromRegister(final Diagram diagram, final SDK sdk, final String label,\n                       final int address, final int bit,\n                       final List<SignalFilter> displayFilters,\n                       final int pioNum, final int smNum)\n    throws IOException\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(label);\n    Constants.checkBit(bit);\n    final String signalLabel = createSignalLabel(sdk, label, address, bit);\n    final SignalRendering.SignalParams signalParams =\n      new SignalRendering.SignalParams(diagram, sdk, label, address,\n                                       bit, bit, displayFilters, pioNum, smNum);\n    return new RegisterBitSignal(signalParams);\n  }\n\n  public static RegisterIntSignal\n    createInternal(final Diagram diagram, final SDK sdk,\n                   final String label, final int address)\n    throws IOException\n  {\n    final SignalRendering.SignalParams signalParams =\n      new SignalRendering.SignalParams(diagram, sdk, label, address, 31, 0,\n                                       null, -1, -1);\n    return new RegisterIntSignal(SignalRendering.Unsigned, signalParams);\n  }\n\n  public static RegisterIntSignal\n    createFromRegister(final Diagram diagram, final SDK sdk, final String label,\n                       final int address, final int msb, final int lsb,\n                       final SignalRendering valueRendering,\n                       final List<SignalFilter> displayFilters,\n                       final int pioNum, final int smNum)\n    throws IOException\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(label);\n    final String signalLabel = createSignalLabel(sdk, label, address, msb, lsb);\n    final SignalRendering.SignalParams signalParams =\n      new SignalRendering.SignalParams(diagram, sdk, signalLabel, address,\n                                       msb, lsb, displayFilters, pioNum, smNum);\n    return new RegisterIntSignal(valueRendering, signalParams);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalFactoryPanel.java",
    "content": "/*\n * @(#)SignalFactoryPanel.java 1.00 21/07/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SignalFactoryPanel extends JPanel\n{\n  private static final long serialVersionUID = 4492836175968992560L;\n\n  private final Diagram diagram;\n  private final BiFunction<String, Signal, String> labelChecker;\n  private final SignalLabelPanel signalLabelPanel;\n  private final SignalTypePanel signalTypePanel;\n\n  private SignalFactoryPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SignalFactoryPanel(final Diagram diagram, final SDK sdk,\n                            final BiFunction<String, Signal, String> labelChecker)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(labelChecker);\n    this.diagram = diagram;\n    this.labelChecker = labelChecker;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    signalLabelPanel = new SignalLabelPanel(diagram);\n    add(signalLabelPanel);\n    signalTypePanel =\n      new SignalTypePanel(diagram, sdk,\n                          (label) -> signalLabelPanel.setSuggestedText(label));\n    add(signalTypePanel);\n    add(Box.createVerticalGlue());\n  }\n\n  public Signal createSignal(final Signal ignoreSignal)\n  {\n    final String label = signalLabelPanel.getText();\n    final String message = labelChecker.apply(label, ignoreSignal);\n    if (message != null) {\n      JOptionPane.showMessageDialog(this, message, \"Invalid Signal Label\",\n                                    JOptionPane.ERROR_MESSAGE);\n      return null;\n    }\n    return signalTypePanel.createSignal(label);\n  }\n\n  public void load(final Signal signal)\n  {\n    signalLabelPanel.load(signal);\n    signalTypePanel.load(signal);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalFilter.java",
    "content": "/*\n * @(#)SignalFilter.java 1.00 21/07/26\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.util.Objects;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic enum SignalFilter\n{\n  NO_DELAY(\"Cycle is a delay cycle on below target state machine.\",\n           (sdk, pioNum, smNum) -> {\n             final PIOEmuRegisters.Regs sm0DelayCycle =\n               PIOEmuRegisters.Regs.SM0_DELAY_CYCLE;\n             final int smDelayCycleAddress =\n               PIOEmuRegisters.getSMAddress(pioNum, smNum, sm0DelayCycle);\n             final boolean isDelayCycle =\n               sdk.readAddress(smDelayCycleAddress) != 0x0;\n             return !isDelayCycle;\n           }),\n  CLK_ENABLED(\"CLK enable signal is false for below target state machine.\",\n              (sdk, pioNum, smNum) -> {\n                final PIOEmuRegisters.Regs sm0ClkEnable =\n                  PIOEmuRegisters.Regs.SM0_CLK_ENABLE;\n                final int clkEnableAddress =\n                  PIOEmuRegisters.getSMAddress(pioNum, smNum, sm0ClkEnable);\n                final int clkEnable = sdk.readAddress(clkEnableAddress) & 0x1;\n                return clkEnable != 0x0;\n              });\n\n  @FunctionalInterface\n  private static interface FilterFunction\n  {\n    boolean acceptCurrentSignalValue(final SDK sdk,\n                                     final int pioNum, final int smNum)\n      throws IOException;\n  }\n\n  private final String description;\n  private final FilterFunction filterFunction;\n\n  private SignalFilter(final String description,\n                       final FilterFunction filterFunction)\n  {\n    Objects.requireNonNull(description);\n    this.description = description;\n    this.filterFunction = filterFunction;\n  }\n\n  public boolean acceptCurrentSignalValue(final SDK sdk,\n                                          final int pioNum, final int smNum)\n    throws IOException\n  {\n    return filterFunction.acceptCurrentSignalValue(sdk, pioNum, smNum);\n  }\n\n  @Override\n  public String toString() { return description; }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalLabelPanel.java",
    "content": "/*\n * @(#)SignalLabelPanel.java 1.00 21/07/03\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JTextField;\nimport org.soundpaint.rp2040pio.SwingUtils;\n\npublic class SignalLabelPanel extends JPanel\n{\n  private static final long serialVersionUID = 5522534111472664050L;\n\n  private final Diagram diagram;\n  private final JTextField tfLabel;\n  private final JTextField tfSuggestedLabel;\n\n  private SignalLabelPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SignalLabelPanel(final Diagram diagram)\n  {\n    Objects.requireNonNull(diagram);\n    this.diagram = diagram;\n    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(\"Signal Label\"));\n    tfLabel = new JTextField(20);\n    add(tfLabel);\n    add(Box.createHorizontalStrut(20));\n    tfSuggestedLabel = new JTextField(20);\n    tfSuggestedLabel.setEditable(false);\n    final JButton btApply = new JButton(\"←\");\n    btApply.addActionListener((action) ->\n                              tfLabel.setText(tfSuggestedLabel.getText()));\n    add(btApply);\n    add(Box.createHorizontalStrut(20));\n    add(new JLabel(\"Suggested Label\"));\n    add(Box.createHorizontalStrut(5));\n    add(tfSuggestedLabel);\n    add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredWidthAsMaximum(tfLabel);\n    SwingUtils.setPreferredHeightAsMaximum(this);\n  }\n\n  public String getText()\n  {\n    return tfLabel.getText();\n  }\n\n  public void setSuggestedText(final String label)\n  {\n    if (label == null) {\n      throw new NullPointerException(\"label\");\n    }\n    tfSuggestedLabel.setText(label);\n  }\n\n  public void load(final Signal signal)\n  {\n    final String label = signal != null ? signal.getLabel() : null;\n    tfLabel.setText(label);\n    if (label != null) tfSuggestedLabel.setText(label);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalPanel.java",
    "content": "/*\n * @(#)SignalPanel.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.BasicStroke;\nimport java.awt.Color;\nimport java.awt.Dimension;\nimport java.awt.Graphics;\nimport java.awt.Graphics2D;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.Stroke;\nimport java.awt.event.MouseEvent;\nimport java.awt.geom.Line2D;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport javax.swing.JComponent;\n\n/**\n * Panel for drawing the view of the signals.\n */\npublic class SignalPanel extends JComponent implements Constants\n{\n  private static final long serialVersionUID = 6327282160532117231L;\n  private static final double LEFT_MARGIN = 2.0; // for clock arrow\n  private static final double RIGHT_MARGIN = 0.0;\n  private static final Stroke PLAIN_STROKE =\n    new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f);\n  private static final Stroke DOTTED_STROKE =\n    new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL, 0.0f,\n                    new float[]{2.0f}, 0.0f);\n\n  private final DiagramModel model;\n  private final List<ToolTip> toolTips;\n  private final Dimension preferredSize;\n  private double zoom;\n  private Rectangle clipBounds;\n\n  private SignalPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public SignalPanel(final DiagramModel model) throws IOException\n  {\n    if (model == null) {\n      throw new NullPointerException(\"model\");\n    }\n    this.model = model;\n    toolTips = new ArrayList<ToolTip>();\n    setToolTipText(\"\");\n    preferredSize = new Dimension();\n    zoom = ZOOM_DEFAULT;\n    clipBounds = new Rectangle();\n    updatePreferredSize();\n  }\n\n  @Override\n  public String getToolTipText(final MouseEvent event)\n  {\n    final Point p = event.getPoint();\n    for (final ToolTip toolTip: toolTips) {\n      if ((p.x >= toolTip.x0) && (p.x <= toolTip.x1) &&\n          (p.y >= toolTip.y0) && (p.y <= toolTip.y1)) {\n        return toolTip.text;\n      }\n    }\n    return null;\n  }\n\n  private void updatePreferredHeight()\n  {\n    double preferredHeight = TOP_MARGIN + BOTTOM_MARGIN;\n    for (final Signal signal : model) {\n      if (signal.getVisible()) {\n        preferredHeight += signal.getDisplayHeight();\n      }\n    }\n    preferredSize.setSize((int)preferredSize.getWidth(), (int)preferredHeight);\n  }\n\n  private void updatePreferredWidth()\n  {\n    final double preferredWidth =\n      Math.round(LEFT_MARGIN + zoom * model.getSignalSize() + RIGHT_MARGIN);\n    preferredSize.setSize((int)preferredWidth, (int)preferredSize.getHeight());\n  }\n\n  public void updatePreferredSize()\n  {\n    updatePreferredHeight();\n    updatePreferredWidth();\n    revalidate();\n  }\n\n  @Override\n  public Dimension getPreferredSize()\n  {\n    return preferredSize;\n  }\n\n  public void setZoom(final int zoom)\n  {\n    this.zoom =\n      zoom < ZOOM_MIN ? ZOOM_MIN : (zoom > ZOOM_MAX ? ZOOM_MAX : zoom);\n    updatePreferredWidth();\n    revalidate();\n  }\n\n  public double getZoom()\n  {\n    return zoom;\n  }\n\n  private void paintGridLine(final Graphics2D g, final double x,\n                             final double height)\n  {\n    g.setColor(Color.LIGHT_GRAY);\n    g.setStroke(DOTTED_STROKE);\n    g.draw(new Line2D.Double(x, TOP_MARGIN, x, height - BOTTOM_MARGIN));\n  }\n\n  private void paintSignalsCycle(final Graphics2D g,\n                                 final double xStart, final int cycle,\n                                 final boolean firstCycle,\n                                 final boolean lastCycle)\n  {\n    g.setColor(Color.BLACK);\n    g.setStroke(PLAIN_STROKE);\n    double y = TOP_MARGIN;\n    for (final Signal signal : model) {\n      if (signal.getVisible()) {\n        final double height = signal.getDisplayHeight();\n        signal.paintCycle(g, zoom, xStart, y += height, cycle,\n                          firstCycle, lastCycle);\n      }\n    }\n  }\n\n  public int x2cycle(final double x)\n  {\n    return (int)((x - LEFT_MARGIN) / zoom);\n  }\n\n  public double cycle2x(final double cycle)\n  {\n    return cycle * zoom + LEFT_MARGIN;\n  }\n\n  private void paintDiagram(final Graphics2D g,\n                            final int width, final int height)\n    throws IOException\n  {\n    g.setStroke(PLAIN_STROKE);\n    g.getClipBounds(clipBounds);\n    final int cycles = model.getSignalSize();\n    final int leftMostCycle =\n      Math.min(model.getSignalSize(),\n               x2cycle(clipBounds.x));\n    final int rightMostCycle =\n      Math.min(model.getSignalSize(),\n               x2cycle(clipBounds.x + clipBounds.width - 1) + 1);\n    for (int cycle = leftMostCycle; cycle < rightMostCycle; cycle++) {\n      final double x = LEFT_MARGIN + cycle * zoom;\n      final boolean firstCycle = cycle == 0;\n      final boolean lastCycle = cycle == cycles - 1;\n      paintGridLine(g, x, height);\n      paintSignalsCycle(g, x, cycle, firstCycle, lastCycle);\n    }\n    paintGridLine(g, LEFT_MARGIN + rightMostCycle * zoom, height);\n  }\n\n  private void paintError(final Graphics2D g,\n                          final int width, final int height,\n                          final IOException exception)\n  {\n    g.setStroke(PLAIN_STROKE);\n    g.setFont(LABEL_FONT);\n    g.drawString(exception.getMessage(), 10.0f, 10.0f);\n  }\n\n  @Override\n  public void paintComponent(final Graphics g)\n  {\n    super.paintComponent(g);\n    try {\n      paintDiagram((Graphics2D)g, getWidth(), getHeight());\n    } catch (final IOException e) {\n      paintError((Graphics2D)g, getWidth(), getHeight(), e);\n    }\n  }\n\n  private final void createToolTips(final int cycle,\n                                    final boolean firstCycle,\n                                    final boolean lastCycle,\n                                    final double xStart)\n  {\n    double y = TOP_MARGIN;\n    for (final Signal signal : model) {\n      if (signal.getVisible()) {\n        final double height = signal.getDisplayHeight();\n        signal.createToolTip(toolTips, cycle, firstCycle, lastCycle,\n                             zoom, xStart, y += height);\n      }\n    }\n  }\n\n  public void rebuildToolTips(final Rectangle clipBounds)\n  {\n    toolTips.clear();\n    final int cycles = model.getSignalSize();\n    final int leftMostCycle =\n      Math.min(model.getSignalSize(),\n               x2cycle(clipBounds.x));\n    final int rightMostCycle =\n      Math.min(model.getSignalSize(),\n               x2cycle(clipBounds.x + clipBounds.width - 1) + 1);\n    for (int cycle = leftMostCycle; cycle <= rightMostCycle; cycle++) {\n      final double x = LEFT_MARGIN + cycle * zoom;\n      final boolean firstCycle = cycle == 0;\n      final boolean lastCycle = cycle == cycles - 1;\n      createToolTips(cycle, firstCycle, lastCycle, x);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalRendering.java",
    "content": "/*\n * @(#)SignalRendering.java 1.00 21/07/25\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Objects;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic enum SignalRendering implements Constants\n{\n  Bit(\"bit signal shape\",\n      \"single bit value visualized by upper or lower signal pulse\",\n      null, null),\n  Binary(\"binary digits\", \"binary rendering of unsigned integer value\",\n         (signalParams, cycle, value) ->\n         formatBinary(value, 1),\n         (signalParams, cycle, value) ->\n         \"0b\" + formatBinary(value, signalParams.bitSize)),\n  Unsigned(\"unsigned decimal\",\n           \"decimal rendering of unsigned integer value\",\n           (signalParams, cycle, value) -> formatUnsigned(value),\n           (signalParams, cycle, value) -> formatUnsigned(value)),\n  Signed(\"signed decimal\",\n         \"decimal rendering of signed integer value\",\n         (signalParams, cycle, value) -> formatSigned(value),\n         (signalParams, cycle, value) -> formatSigned(value)),\n  Hex(\"hexadecimal\",\n      \"hexadecimal rendering of unsigned integer value\",\n      (signalParams, cycle, value) -> formatHex(value, 1),\n      (signalParams, cycle, value) ->\n      \"0x\" + formatHex(value, signalParams.bitSize)),\n  Octal(\"octal\",\n        \"octal rendering of unsigned integer value\",\n        (signalParams, cycle, value) ->\n        formatOctal(value, 1),\n        (signalParams, cycle, value) ->\n        \"0o\" + formatOctal(value, signalParams.bitSize)),\n  Mnemonic(\"PIO instruction with side-set for below target state machine\",\n           \"mnemonic of PIO instruction with op-code that equals the value\",\n           (signalParams, cycle, value) ->\n           formatShortMnemonic(cycle, value, signalParams),\n           (signalParams, cycle, value) ->\n           formatFullMnemonic(cycle, value, signalParams));\n\n  @FunctionalInterface\n  public static interface ValueRenderer\n  {\n    String renderValue(final SignalParams signalParams,\n                       final int cycle, final int value);\n  }\n\n  // TODO: Make private again when removing demo signals from Diagram class\n  public static class SignalParams\n  {\n    private final Diagram diagram;\n    private final SDK sdk;\n    private final String label;\n    private final int address;\n    private final int msb;\n    private final int lsb;\n    private final int bitSize;\n    // TODO: Make private again when removing demo signals from Diagram class\n    public final List<SignalFilter> displayFilters;\n    private final int pioNum;\n    private final int smNum;\n    private final boolean isSmInstr;\n\n    private SignalParams()\n    {\n      throw new UnsupportedOperationException(\"unsupported default constructor\");\n    }\n\n    public SignalParams(final String label)\n    {\n      Objects.requireNonNull(label);\n      diagram = null;\n      sdk = null;\n      this.label = label;\n      address = -1;\n      msb = -1;\n      lsb = -1;\n      bitSize = -1;\n      displayFilters = null;\n      pioNum = -1;\n      smNum = -1;\n      isSmInstr = false;\n    }\n\n    // TODO: Make private again when removing demo signals from Diagram class\n    public SignalParams(final Diagram diagram,\n                        final SDK sdk,\n                        final String label,\n                        final int address,\n                        final int msb,\n                        final int lsb,\n                        final List<SignalFilter> displayFilters,\n                        final int pioNum,\n                        final int smNum)\n      throws IOException\n    {\n      Objects.requireNonNull(label);\n      if ((msb != -1) || (lsb != -1)) {\n        Constants.checkMSBLSB(msb, lsb);\n      }\n      this.diagram = diagram;\n      this.sdk = sdk;\n      this.label = label;\n      this.address = address;\n      this.msb = msb;\n      this.lsb = lsb;\n      bitSize = (msb != -1) || (lsb != -1) ? msb - lsb + 1 : -1;\n      this.displayFilters = displayFilters;\n      this.pioNum = pioNum;\n      this.smNum = smNum;\n      isSmInstr =\n        sdk.getFullLabelForAddress(address).matches(\"PIO\\\\d_SM\\\\d_INSTR\");\n    }\n\n    public Diagram getDiagram() { return diagram; }\n\n    public SDK getSDK() { return sdk; }\n\n    public String getLabel() { return label; }\n\n    public int getAddress() { return address; }\n\n    public int getMsb() { return msb; }\n\n    public int getLsb() { return lsb; }\n\n    public int getBitSize() { return bitSize; }\n\n    public List<SignalFilter> getDisplayFilters() { return displayFilters; }\n\n    public int getPioNum() { return pioNum; }\n\n    public int getSmNum() { return smNum; }\n\n    public boolean isSmInstr() { return isSmInstr; }\n\n    /*\n     * TODO: Performance: This method is typically called twice with\n     * identical arguments (once for value rendering, once for tooltip\n     * rendering).  To avoid creating two equal instances of\n     * InstructionInfo, maybe cache the instance of the most recent\n     * call and return it again, if arguments cycle and value match\n     * those of the previous call?\n     */\n    private PIOSDK.InstructionInfo getInstructionFromOpCode(final int cycle,\n                                                            final int value)\n    {\n      final int pinCtrlSidesetCount;\n      final boolean execCtrlSideEn;\n      final boolean isDelayCycle;\n      final int delay;\n      final int origin;\n      final String addressLabel;\n      final int signalSize = diagram.getModel().getSignalSize();\n      if ((pioNum >= 0) && (smNum >= 0) && (cycle >= 0)) {\n        final int smPinCtrlSidesetCountAddress =\n          PIORegisters.getSMAddress(pioNum, smNum,\n                                    PIORegisters.Regs.SM0_PINCTRL);\n        pinCtrlSidesetCount =\n          (diagram.getInternalSignalByAddress(smPinCtrlSidesetCountAddress).\n           getValue(cycle) &\n           SM0_PINCTRL_SIDESET_COUNT_BITS) >>> SM0_PINCTRL_SIDESET_COUNT_LSB;\n        final int smExecCtrlSideEnAddress =\n          PIORegisters.getSMAddress(pioNum, smNum,\n                                    PIORegisters.Regs.SM0_EXECCTRL);\n        execCtrlSideEn =\n          (diagram.getInternalSignalByAddress(smExecCtrlSideEnAddress).\n           getValue(cycle) &\n           SM0_EXECCTRL_SIDE_EN_BITS) != 0x0;\n        if (isSmInstr) {\n          final int smDelayCycleAddress =\n            PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                         PIOEmuRegisters.Regs.SM0_DELAY_CYCLE);\n          isDelayCycle =\n            diagram.getInternalSignalByAddress(smDelayCycleAddress).\n            getValue(cycle) == 0x1;\n\n          final int smDelayAddress =\n            PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                         PIOEmuRegisters.Regs.SM0_DELAY);\n          delay =\n            diagram.getInternalSignalByAddress(smDelayAddress).\n            getValue(cycle);\n          final int instrOriginAddress =\n            PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                         PIOEmuRegisters.Regs.SM0_INSTR_ORIGIN);\n          final int instrOrigin =\n            diagram.getInternalSignalByAddress(instrOriginAddress).\n            getValue(cycle);\n          origin = PIOSDK.decodeInstrOrigin(instrOrigin);\n          addressLabel = PIOSDK.renderOrigin(origin) + \": \";\n        } else {\n          isDelayCycle = false;\n          delay = 0;\n          origin = INSTR_ORIGIN_UNKNOWN;\n          addressLabel = \"\";\n        }\n      } else {\n        pinCtrlSidesetCount = 0;\n        execCtrlSideEn = false;\n        isDelayCycle = false;\n        delay = 0;\n        origin = INSTR_ORIGIN_UNKNOWN;\n        addressLabel = \"\";\n      }\n      final boolean format = false;\n      return\n        PIOSDK.getInstructionFromOpCode(pinCtrlSidesetCount, execCtrlSideEn,\n                                        origin, addressLabel, value, format,\n                                        isDelayCycle, delay);\n    }\n  }\n\n  private static String formatBinary(final int value, final int bitSize)\n  {\n    final String digits = Integer.toBinaryString(value);\n    return String.format(\"%\" + bitSize + \"s\", digits).replace(' ', '0');\n  }\n\n  private static String formatUnsigned(final int value)\n  {\n    return Long.toString(Integer.toUnsignedLong(value));\n  }\n\n  private static String formatSigned(final int value)\n  {\n    return Integer.toString(value);\n  }\n\n  private static String formatHex(final int value, final int bitSize)\n  {\n    final String digits = Integer.toHexString(value);\n    return\n      String.format(\"%\" + ((bitSize - 1) / 4 + 1) + \"s\", digits).\n      replace(' ', '0');\n  }\n\n  private static String formatOctal(final int value, final int bitSize)\n  {\n    final String digits = Integer.toOctalString(value);\n    return\n      String.format(\"%\" + ((bitSize - 1) / 3 + 1) + \"s\", digits).\n      replace(' ', '0');\n  }\n\n  // TODO: Make private again when removing demo signals from Diagram class\n  public static String formatShortMnemonic(final int cycle,\n                                           final int value,\n                                           final SignalParams signalParams)\n  {\n    return signalParams.getInstructionFromOpCode(cycle, value).toString();\n  }\n\n  // TODO: Make private again when removing demo signals from Diagram class\n  public static String formatFullMnemonic(final int cycle,\n                                          final int value,\n                                          final SignalParams signalParams)\n  {\n    return\n      signalParams.getInstructionFromOpCode(cycle, value).getToolTipText();\n  }\n\n  private final String label;\n  private final String description;\n  private final ValueRenderer lifeLineRenderer;\n  private final ValueRenderer toolTipRenderer;\n\n  private SignalRendering(final String label, final String description,\n                          final ValueRenderer lifeLineRenderer,\n                          final ValueRenderer toolTipRenderer)\n  {\n    Objects.requireNonNull(label);\n    Objects.requireNonNull(description);\n    this.label = label;\n    this.description = description;\n    this.lifeLineRenderer = lifeLineRenderer;\n    this.toolTipRenderer = toolTipRenderer;\n  }\n\n  public ValueRenderer getLifeLineRenderer() { return lifeLineRenderer; }\n\n  public ValueRenderer getToolTipRenderer() { return toolTipRenderer; }\n\n  public Signal createSignal(final Diagram diagram,\n                             final SDK sdk,\n                             final String label,\n                             final int address,\n                             final int msb,\n                             final int lsb,\n                             final List<SignalFilter> displayFilters,\n                             final int pioNum,\n                             final int smNum)\n    throws IOException\n  {\n    if (this == Bit) {\n      return\n        SignalFactory.createFromRegister(diagram, sdk, label, address, msb,\n                                         displayFilters, pioNum, smNum);\n    } else {\n      return\n        SignalFactory.createFromRegister(diagram, sdk, label, address, msb, lsb,\n                                         this, displayFilters, pioNum, smNum);\n    }\n  }\n\n  @Override\n  public String toString() { return label; }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalTypePanel.java",
    "content": "/*\n * @(#)SignalTypePanel.java 1.00 21/06/30\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.event.KeyEvent;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.List;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.ButtonModel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.JTabbedPane;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SignalTypePanel extends JPanel\n{\n  private static final long serialVersionUID = -3023263223044270621L;\n\n  private final Diagram diagram;\n  private final Consumer<String> suggestedLabelSetter;\n  private final ButtonGroup signalType;\n  private final JRadioButton rbCycleRuler;\n  private final JRadioButton rbClock;\n  private final JRadioButton rbValued;\n  private final JTabbedPane valueTabs;\n  private final ValueSourcePanel valueSourcePanel;\n  private final ValueRenderingPanel valueRenderingPanel;\n  private final ValueFilterPanel valueFilterPanel;\n  private final SmSelectionPanel smSelectionPanel;\n  private boolean visible;\n\n  private SignalTypePanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SignalTypePanel(final Diagram diagram, final SDK sdk,\n                         final Consumer<String> suggestedLabelSetter)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(suggestedLabelSetter);\n    this.diagram = diagram;\n    this.suggestedLabelSetter = suggestedLabelSetter;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(\"Signal Type\"));\n    signalType = new ButtonGroup();\n    rbCycleRuler = new JRadioButton(\"Cycle Ruler\");\n    rbClock = new JRadioButton(\"Clock\");\n    rbValued = new JRadioButton(\"Valued Signal\", true);\n    valueTabs = new JTabbedPane();\n    valueSourcePanel =\n      new ValueSourcePanel(diagram, sdk, suggestedLabelSetter,\n                           (dummy) -> updateSmSelectionInfo());\n    valueRenderingPanel =\n      new ValueRenderingPanel(diagram, sdk,\n                              (dummy) -> updateSmSelectionEnableStatus());\n    valueFilterPanel =\n      new ValueFilterPanel(diagram, sdk,\n                           (dummy) -> updateSmSelectionEnableStatus());\n    createAndAddCycleRulerRadio();\n    createAndAddClockRadio();\n    createAndAddValuedRadio();\n    createAndAddValuePanels();\n    smSelectionPanel = new SmSelectionPanel(diagram, sdk);\n    createAndAddSmSelectionPanel();\n    valueSourcePanel.initRegistersForSelectedRegisterSet();\n    selectValued();\n    visible = false;\n  }\n\n  private void createAndAddCycleRulerRadio()\n  {\n    final JPanel cycleRulerRadio = new JPanel();\n    cycleRulerRadio.\n      setLayout(new BoxLayout(cycleRulerRadio, BoxLayout.LINE_AXIS));\n    rbCycleRuler.addActionListener((action) -> selectCycleRuler());\n    signalType.add(rbCycleRuler);\n    cycleRulerRadio.add(rbCycleRuler);\n    cycleRulerRadio.add(Box.createHorizontalGlue());\n    add(cycleRulerRadio);\n  }\n\n  private void createAndAddClockRadio()\n  {\n    final JPanel clockRadio = new JPanel();\n    clockRadio.setLayout(new BoxLayout(clockRadio, BoxLayout.LINE_AXIS));\n    rbClock.addActionListener((action) -> selectClock());\n    signalType.add(rbClock);\n    clockRadio.add(rbClock);\n    clockRadio.add(Box.createHorizontalGlue());\n    add(clockRadio);\n  }\n\n  private void createAndAddValuedRadio()\n  {\n    final JPanel row = new JPanel();\n    row.setLayout(new BoxLayout(row, BoxLayout.LINE_AXIS));\n    rbValued.addActionListener((action) -> selectValued());\n    signalType.add(rbValued);\n    row.add(rbValued);\n    row.add(Box.createHorizontalGlue());\n    add(row);\n  }\n\n  private void createAndAddValuePanels()\n  {\n    final JPanel row = new JPanel();\n    row.setLayout(new BoxLayout(row, BoxLayout.LINE_AXIS));\n    row.add(Box.createHorizontalStrut(20));\n    createAndAddValueSourcePanel(valueTabs);\n    createAndAddValueRenderingPanel(valueTabs);\n    createAndAddValueFilterPanel(valueTabs);\n    row.add(valueTabs);\n    SwingUtils.setPreferredHeightAsMaximum(row);\n    add(row);\n  }\n\n  private void createAndAddValueSourcePanel(final JTabbedPane valueTabs)\n  {\n    valueTabs.addTab(\"Source\", null, valueSourcePanel,\n                     \"specify how to get values of this signal\");\n    final int tabIndex = valueTabs.indexOfComponent(valueSourcePanel);\n    valueTabs.setMnemonicAt(tabIndex, KeyEvent.VK_S);\n  }\n\n  private void createAndAddValueRenderingPanel(final JTabbedPane valueTabs)\n  {\n    valueTabs.addTab(\"Rendering\", null, valueRenderingPanel,\n                     \"specify how to render values of this signal\");\n    final int tabIndex = valueTabs.indexOfComponent(valueRenderingPanel);\n    valueTabs.setMnemonicAt(tabIndex, KeyEvent.VK_R);\n  }\n\n  private void createAndAddValueFilterPanel(final JTabbedPane valueTabs)\n  {\n    valueTabs.addTab(\"Filter\", null, valueFilterPanel,\n                     \"specify which filters to apply to decide if signal \" +\n                     \"value is defined\");\n    final int tabIndex = valueTabs.indexOfComponent(valueFilterPanel);\n    valueTabs.setMnemonicAt(tabIndex, KeyEvent.VK_F);\n  }\n\n  private void createAndAddSmSelectionPanel()\n  {\n    final JPanel row = new JPanel();\n    row.setLayout(new BoxLayout(row, BoxLayout.LINE_AXIS));\n    row.add(Box.createHorizontalStrut(20));\n    row.add(smSelectionPanel);\n    SwingUtils.setPreferredHeightAsMaximum(row);\n    add(row);\n  }\n\n  private void selectCycleRuler()\n  {\n    setValueEnabled(false);\n    suggestedLabelSetter.accept(\"cycle#\");\n  }\n\n  private void selectClock()\n  {\n    setValueEnabled(false);\n    suggestedLabelSetter.accept(\"clock\");\n  }\n\n  private void selectValued()\n  {\n    setValueEnabled(true);\n    valueSourcePanel.updateSuggestedLabel();\n  }\n\n  private void setValueEnabled(final boolean enabled)\n  {\n    valueSourcePanel.setEnabled(enabled);\n    valueRenderingPanel.setEnabled(enabled);\n    valueFilterPanel.setEnabled(enabled);\n    valueTabs.setEnabled(enabled);\n    updateSmSelectionEnableStatus();\n  }\n\n  private void updateSmSelectionEnableStatus()\n  {\n    final boolean enabled =\n      valueTabs.isEnabled() &&\n      (valueRenderingPanel.isSmSelectionRelevant() ||\n       valueFilterPanel.isSmSelectionRelevant());\n    smSelectionPanel.setEnabled(enabled);\n  }\n\n  private void updateSmSelectionInfo()\n  {\n    final int sourcePioNum = valueSourcePanel.getSelectedRegisterSetPio();\n    final int sourceSmNum = valueSourcePanel.getSelectedRegisterSm();\n    smSelectionPanel.updateSourceInfo(sourcePioNum, sourceSmNum);\n  }\n\n  public Signal createSignal(final String label)\n  {\n    final ButtonModel button = signalType.getSelection();\n    if (button == rbCycleRuler.getModel()) {\n      return SignalFactory.createRuler(label);\n    }\n    if (button == rbClock.getModel()) {\n      return SignalFactory.createClockSignal(label);\n    }\n    if (button == rbValued.getModel()) {\n      final int address = valueSourcePanel.getSelectedRegisterAddress();\n      final int msb = valueSourcePanel.getSelectedRegisterMsb();\n      final int lsb = valueSourcePanel.getSelectedRegisterLsb();\n      final int sourcePioNum = valueSourcePanel.getSelectedRegisterSetPio();\n      final int sourceSmNum = valueSourcePanel.getSelectedRegisterSm();\n      final int pioNum = smSelectionPanel.getPioNum(sourcePioNum);\n      final int smNum = smSelectionPanel.getSmNum(sourceSmNum);\n      final List<SignalFilter> displayFilters =\n        valueFilterPanel.createFilters();\n      return\n        valueRenderingPanel.createSignal(label, pioNum, smNum,\n                                         address, msb, lsb, displayFilters,\n                                         visible);\n    }\n    return null;\n  }\n\n  public void load(final Signal signal)\n  {\n    visible = signal != null ? signal.getVisible() : false;\n    valueTabs.setSelectedIndex(0);\n    final ValuedSignal<?> valuedSignal =\n      signal instanceof ValuedSignal ? (ValuedSignal<?>)signal : null;\n    valueSourcePanel.load(valuedSignal);\n    final int pioNum =\n      valuedSignal != null ? valueSourcePanel.getSelectedRegisterSetPio() : -1;\n    final int smNum =\n      valuedSignal != null ? valueSourcePanel.getSelectedRegisterSm() : -1;\n    valueRenderingPanel.load(valuedSignal);\n    valueFilterPanel.load(valuedSignal);\n    smSelectionPanel.load(valuedSignal, pioNum, smNum);\n    if ((signal == null) ||\n        (signal instanceof ValuedSignal)) {\n      rbValued.setSelected(true);\n      selectValued();\n    } else if (signal instanceof CycleRuler) {\n      rbCycleRuler.setSelected(true);\n      selectCycleRuler();\n    } else if (signal instanceof ClockSignal) {\n      rbClock.setSelected(true);\n      selectClock();\n    } else {\n      final String message =\n        String.format(\"warning: failed loading signal preload values: \" +\n                      \"unknown signal type: %s\", signal.getClass());\n      diagram.getConsole().println(message);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalsDialog.java",
    "content": "/*\n * @(#)SignalsDialog.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.BorderLayout;\nimport java.awt.Dialog;\nimport java.awt.Dimension;\nimport java.awt.event.KeyEvent;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JDialog;\nimport javax.swing.JScrollPane;\nimport javax.swing.border.Border;\nimport javax.swing.border.EtchedBorder;\nimport javax.swing.border.TitledBorder;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SignalsDialog extends JDialog\n{\n  private static final long serialVersionUID = 8248679860337463934L;\n\n  private class ActionPanel extends Box\n  {\n    private static final long serialVersionUID = -4136799373128393432L;\n\n    private final JButton btOk;\n    private final JButton btApply;\n    private final JButton btCancel;\n\n    public ActionPanel()\n    {\n      super(BoxLayout.LINE_AXIS);\n      setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n      btOk = new JButton(\"Ok\");\n      btOk.setMnemonic(KeyEvent.VK_O);\n      btOk.addActionListener((event) -> {\n          applyChanges();\n          SignalsDialog.this.setVisible(false);\n        });\n      add(btOk);\n      add(Box.createHorizontalGlue());\n      btApply = new JButton(\"Apply\");\n      btApply.setMnemonic(KeyEvent.VK_A);\n      btApply.addActionListener((event) -> {\n          applyChanges();\n        });\n      add(btApply);\n      add(Box.createHorizontalGlue());\n      btCancel = new JButton(\"Cancel\");\n      btCancel.setMnemonic(KeyEvent.VK_C);\n      btCancel.addActionListener((event) -> {\n          SignalsDialog.this.setVisible(false);\n        });\n      add(btCancel);\n    }\n  }\n\n  private final SignalsPropertiesPanel signalsPropertiesPanel;\n\n  private SignalsDialog()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SignalsDialog(final Diagram diagram, final SDK sdk)\n  {\n    super(diagram, \"Signals\", Dialog.ModalityType.DOCUMENT_MODAL);\n    Objects.requireNonNull(diagram);\n    signalsPropertiesPanel = new SignalsPropertiesPanel(diagram, sdk);\n    final JScrollPane scrollPane = new JScrollPane(signalsPropertiesPanel);\n    final Border loweredEtched =\n      BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);\n    final TitledBorder titled =\n      BorderFactory.createTitledBorder(loweredEtched, \"Signals\");\n    titled.setTitleJustification(TitledBorder.CENTER);\n    scrollPane.setBorder(titled);\n    getContentPane().add(scrollPane);\n    getContentPane().add(new ActionPanel(), BorderLayout.SOUTH);\n    setPreferredSize(new Dimension(400, 400));\n  }\n\n  private void applyChanges()\n  {\n    signalsPropertiesPanel.applyChanges();\n  }\n\n  public void open()\n  {\n    signalsPropertiesPanel.rebuild();\n    pack();\n    setVisible(true);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SignalsPropertiesPanel.java",
    "content": "/*\n * @(#)SignalsPropertiesPanel.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JPopupMenu;\nimport javax.swing.JTextField;\nimport javax.swing.border.Border;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SignalsPropertiesPanel extends Box\n{\n  private static final long serialVersionUID = -8921092912489516701L;\n\n  private static class IndexedButton extends JButton\n  {\n    private static final long serialVersionUID = -7131638926376704356L;\n\n    private final int index;\n\n    public IndexedButton(final String text, final int index)\n    {\n      super(text);\n      this.index = index;\n    }\n\n    public int getIndex() { return index; }\n  }\n\n  private final Diagram diagram;\n  private final List<Signal> signals;\n  private final List<JTextField> signalIndices;\n  private final List<JTextField> signalLabels;\n  private final List<JCheckBox> signalVisibilities;\n  private final List<IndexedButton> signalActions;\n  private final SignalDialog signalDialog;\n  private final JPopupMenu pmActions;\n\n  public SignalsPropertiesPanel(final Diagram diagram, final SDK sdk)\n  {\n    super(BoxLayout.PAGE_AXIS);\n    Objects.requireNonNull(diagram);\n    this.diagram = diagram;\n    signals = new ArrayList<Signal>();\n    signalIndices = new ArrayList<JTextField>();\n    signalLabels = new ArrayList<JTextField>();\n    signalVisibilities = new ArrayList<JCheckBox>();\n    signalActions = new ArrayList<IndexedButton>();\n    signalDialog =\n      new SignalDialog(diagram, sdk,\n                       (index, signal, add) ->\n                       addOrSetSignal(index, signal, add),\n                       (label, signal) -> checkLabel(label, signal));\n    pmActions = createActions();\n  }\n\n  private JPopupMenu createActions()\n  {\n    final JPopupMenu pmActions = new JPopupMenu(\"Actions\");\n    pmActions.add(\"Add Signal…\").addActionListener((action) -> addSignal());\n    pmActions.add(\"Edit…\").addActionListener((action) -> editSignal());\n    pmActions.add(\"Delete\").addActionListener((action) -> deleteSignal());\n    return pmActions;\n  }\n\n  private void addSignal()\n  {\n    final IndexedButton btActions = (IndexedButton)pmActions.getInvoker();\n    final int index = btActions.getIndex();\n    signalDialog.open(index + 1, null);\n  }\n\n  private void editSignal()\n  {\n    final IndexedButton btActions = (IndexedButton)pmActions.getInvoker();\n    final int index = btActions.getIndex();\n    signalDialog.open(index, signals.get(index));\n  }\n\n  private void deleteSignal()\n  {\n    final IndexedButton btActions = (IndexedButton)pmActions.getInvoker();\n    final int index = btActions.getIndex();\n    signals.remove(index);\n    rebuildSignals();\n    rebuildGUI();\n  }\n\n  public void applyChanges()\n  {\n    int index = 0;\n    for (final Signal signal : signals) {\n      signal.setVisible(signalVisibilities.get(index).isSelected());\n      index++;\n    }\n    diagram.pushSignals(signals);\n  }\n\n  private void swapSignals(final int index)\n  {\n    Collections.swap(signals, index, index + 1);\n    final Signal signal1 = signals.get(index);\n    final Signal signal2 = signals.get(index + 1);\n    final JTextField tfLabel1 = signalLabels.get(index);\n    tfLabel1.setText(signal1.getLabel());\n    final JTextField tfLabel2 = signalLabels.get(index + 1);\n    tfLabel2.setText(signal2.getLabel());\n    final JCheckBox cbVisible1 = signalVisibilities.get(index);\n    final JCheckBox cbVisible2 = signalVisibilities.get(index + 1);\n    final boolean selected = cbVisible1.isSelected();\n    cbVisible1.setSelected(cbVisible2.isSelected());\n    cbVisible2.setSelected(selected);\n    revalidate();\n  }\n\n  private void addOrSetSignal(final int index, final Signal signal,\n                              final boolean add)\n  {\n    if (add) {\n      signals.add(index, signal);\n    } else {\n      signals.set(index, signal);\n    }\n    rebuildSignals();\n    rebuildGUI();\n  }\n\n  private String checkLabel(final String label, final Signal ignoreSignal)\n  {\n    if (label == null) {\n      return \"Signal label must not be null.\";\n    }\n    if (label.isEmpty()) {\n      return \"Signal label must not be empty.\";\n    }\n    for (final Signal signal : signals) {\n      if (signal != ignoreSignal) {\n        if (label.equals(signal.getLabel())) {\n          return \"Signal label is already in use.\";\n        }\n      }\n    }\n    return null;\n  }\n\n  private void rebuildGUI()\n  {\n    removeAll();\n    boolean firstLine = true;\n    final Box headerLine = new Box(BoxLayout.LINE_AXIS);\n    add(headerLine);\n    headerLine.add(Box.createHorizontalStrut(5));\n    headerLine.add(new JLabel(\"# Label\"));\n    headerLine.add(Box.createHorizontalGlue());\n    headerLine.add(new JLabel(\"Show\"));\n    headerLine.add(Box.createHorizontalStrut(5));\n    headerLine.add(new JLabel(\"Actions\"));\n    headerLine.add(Box.createHorizontalStrut(5));\n    SwingUtils.setPreferredHeightAsMaximum(headerLine);\n    add(Box.createVerticalStrut(15));\n    int index = 0;\n    for (final Signal signal : signals) {\n      if (index > 0) {\n        final Box infixLine = new Box(BoxLayout.LINE_AXIS);\n        add(infixLine);\n\n        final JButton btSwap =\n          SwingUtils.createIconButton(\"swapv12x12.png\", \"⬍\");\n        final int swapIndex = index - 1;\n        btSwap.addActionListener((event) -> swapSignals(swapIndex));\n        btSwap.setBorderPainted(false);\n        btSwap.setContentAreaFilled(false);\n        infixLine.add(btSwap);\n\n        infixLine.add(Box.createHorizontalGlue());\n        SwingUtils.setPreferredHeightAsMaximum(infixLine);\n      }\n      final Box signalLine = new Box(BoxLayout.LINE_AXIS);\n      add(signalLine);\n      signalLine.add(signalIndices.get(index));\n      signalLine.add(Box.createHorizontalStrut(5));\n      signalLine.add(signalLabels.get(index));\n      signalLine.add(Box.createHorizontalGlue());\n      signalLine.add(signalVisibilities.get(index));\n      signalLine.add(Box.createHorizontalStrut(12));\n      signalLine.add(signalActions.get(index));\n      signalLine.add(Box.createHorizontalStrut(5));\n      SwingUtils.setPreferredHeightAsMaximum(signalLine);\n      index++;\n    }\n    add(Box.createVerticalGlue());\n    revalidate();\n  }\n\n  private void rebuildSignals()\n  {\n    signalIndices.clear();\n    signalLabels.clear();\n    signalVisibilities.clear();\n    signalActions.clear();\n    int index = 0;\n    for (final Signal signal : signals) {\n      final JTextField tfIndex = new JTextField() {\n          @Override\n          public void setBorder(final Border border) {}\n        };\n      tfIndex.setText(String.format(\"#%d\", index));\n      tfIndex.setEditable(false);\n      SwingUtils.setPreferredWidthAsMaximum(tfIndex);\n      signalIndices.add(tfIndex);\n\n      final JTextField tfLabel = new JTextField() {\n          @Override\n          public void setBorder(final Border border) {}\n        };\n      tfLabel.setText(signal.getLabel());\n      tfLabel.setEditable(false);\n      SwingUtils.setPreferredWidthAsMaximum(tfLabel);\n      signalLabels.add(tfLabel);\n\n      final JCheckBox cbVisible = new JCheckBox();\n      cbVisible.setSelected(signal.getVisible());\n      signalVisibilities.add(cbVisible);\n\n      final int actionsIndex = index;\n      final IndexedButton btActions = new IndexedButton(\"…\", actionsIndex);\n      btActions.setBorderPainted(false);\n      btActions.setContentAreaFilled(false);\n      btActions.addActionListener((action) -> popupActions(btActions));\n      signalActions.add(btActions);\n\n      index++;\n    }\n  }\n\n  private void popupActions(final JButton btActions)\n  {\n    pmActions.show(btActions, 10, 10);\n  }\n\n  public void rebuild()\n  {\n    diagram.pullSignals(signals);\n    rebuildSignals();\n    rebuildGUI();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/SmSelectionPanel.java",
    "content": "/*\n * @(#)SmSelectionPanel.java 1.00 21/07/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.Enumeration;\nimport java.util.Objects;\nimport javax.swing.AbstractButton;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class SmSelectionPanel extends JPanel\n  implements org.soundpaint.rp2040pio.observer.diagram.Constants\n{\n  private static final long serialVersionUID = -4932796542558706478L;\n\n  private final Diagram diagram;\n  private final SDK sdk;\n  private final JLabel lbPio;\n  private final ButtonGroup pioButtons;\n  private final JCheckBox cbUseSourcePio;\n  private final JLabel lbSourcePioLabel;\n  private final JLabel lbSourcePioNum;\n  private final JLabel lbSm;\n  private final ButtonGroup smButtons;\n  private final JCheckBox cbUseSourceSm;\n  private final JLabel lbSourceSmLabel;\n  private final JLabel lbSourceSmNum;\n  private int selectedPio;\n  private int selectedSm;\n\n  private SmSelectionPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public SmSelectionPanel(final Diagram diagram, final SDK sdk)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    this.diagram = diagram;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.\n              createTitledBorder(\"Select Target State Machine\"));\n    lbPio = new JLabel(\"PIO\");\n    cbUseSourcePio = new JCheckBox(\"Prefer PIO from currently \" +\n                                   \"selected source, if available\", true);\n    pioButtons = new ButtonGroup();\n    lbSourcePioLabel = new JLabel(\"Current value:\");\n    lbSourcePioNum = new JLabel();\n    addPioSelection();\n    lbSm = new JLabel(\"SM\");\n    cbUseSourceSm = new JCheckBox(\"Prefer SM from currently \" +\n                                  \"selected source, if available\", true);\n    smButtons = new ButtonGroup();\n    lbSourceSmLabel = new JLabel(\"Current value:\");\n    lbSourceSmNum = new JLabel();\n    addSmSelection();\n    SwingUtils.setPreferredHeightAsMaximum(this);\n  }\n\n  private void addPioSelection()\n  {\n    final JPanel pioLine = new JPanel();\n    pioLine.setLayout(new BoxLayout(pioLine, BoxLayout.LINE_AXIS));\n    lbPio.setPreferredSize(PREFERRED_LABEL_SIZE);\n    pioLine.add(lbPio);\n    for (int pioNum = 0; pioNum < Constants.PIO_NUM; pioNum++) {\n      final JRadioButton rbPio = new JRadioButton(\"PIO\" + pioNum, pioNum == 0);\n      pioButtons.add(rbPio);\n      final int pio = pioNum;\n      rbPio.addActionListener((action) -> selectedPio = pio);\n      pioLine.add(Box.createHorizontalStrut(20));\n      pioLine.add(rbPio);\n    }\n    pioLine.add(Box.createHorizontalGlue());\n    selectedPio = 0;\n    SwingUtils.setPreferredHeightAsMaximum(pioLine);\n    add(pioLine);\n    final JPanel sourcePioLine = new JPanel();\n    sourcePioLine.setLayout(new BoxLayout(sourcePioLine, BoxLayout.LINE_AXIS));\n    sourcePioLine.add(Box.createHorizontalStrut(PREFERRED_LABEL_SIZE.width));\n    sourcePioLine.add(Box.createHorizontalStrut(20));\n    sourcePioLine.add(cbUseSourcePio);\n    sourcePioLine.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(sourcePioLine);\n    add(sourcePioLine);\n    final JPanel sourcePioValueLine = new JPanel();\n    sourcePioValueLine.\n      setLayout(new BoxLayout(sourcePioValueLine, BoxLayout.LINE_AXIS));\n    sourcePioValueLine.\n      add(Box.createHorizontalStrut(PREFERRED_LABEL_SIZE.width));\n    sourcePioValueLine.add(Box.createHorizontalStrut(40));\n    sourcePioValueLine.add(lbSourcePioLabel);\n    sourcePioValueLine.add(Box.createHorizontalStrut(5));\n    sourcePioValueLine.add(lbSourcePioNum);\n    SwingUtils.setPreferredHeightAsMaximum(sourcePioValueLine);\n    add(sourcePioValueLine);\n  }\n\n  private void addSmSelection()\n  {\n    final JPanel smLine = new JPanel();\n    smLine.setLayout(new BoxLayout(smLine, BoxLayout.LINE_AXIS));\n    lbSm.setPreferredSize(PREFERRED_LABEL_SIZE);\n    smLine.add(lbSm);\n    for (int smNum = 0; smNum < Constants.SM_COUNT; smNum++) {\n      final JRadioButton rbSm = new JRadioButton(\"SM\" + smNum, smNum == 0);\n      smButtons.add(rbSm);\n      final int sm = smNum;\n      rbSm.addActionListener((action) -> selectedSm = sm);\n      smLine.add(Box.createHorizontalStrut(20));\n      smLine.add(rbSm);\n    }\n    smLine.add(Box.createHorizontalGlue());\n    selectedPio = 0;\n    SwingUtils.setPreferredHeightAsMaximum(smLine);\n    add(smLine);\n    final JPanel sourceSmLine = new JPanel();\n    sourceSmLine.setLayout(new BoxLayout(sourceSmLine, BoxLayout.LINE_AXIS));\n    sourceSmLine.add(Box.createHorizontalStrut(PREFERRED_LABEL_SIZE.width));\n    sourceSmLine.add(Box.createHorizontalStrut(20));\n    sourceSmLine.add(cbUseSourceSm);\n    sourceSmLine.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(sourceSmLine);\n    add(sourceSmLine);\n    final JPanel sourceSmValueLine = new JPanel();\n    sourceSmValueLine.\n      setLayout(new BoxLayout(sourceSmValueLine, BoxLayout.LINE_AXIS));\n    sourceSmValueLine.\n      add(Box.createHorizontalStrut(PREFERRED_LABEL_SIZE.width));\n    sourceSmValueLine.add(Box.createHorizontalStrut(40));\n    sourceSmValueLine.add(lbSourceSmLabel);\n    sourceSmValueLine.add(Box.createHorizontalStrut(5));\n    sourceSmValueLine.add(lbSourceSmNum);\n    SwingUtils.setPreferredHeightAsMaximum(sourceSmValueLine);\n    add(sourceSmValueLine);\n  }\n\n  public int getPioNum(final int sourcePioNum)\n  {\n    final boolean useSourcePio = cbUseSourcePio.isSelected();\n    return useSourcePio && sourcePioNum >= 0 ? sourcePioNum : selectedPio;\n  }\n\n  public int getSmNum(final int sourceSmNum)\n  {\n    final boolean useSourceSm = cbUseSourceSm.isSelected();\n    return useSourceSm && sourceSmNum >= 0 ? sourceSmNum : selectedSm;\n  }\n\n  private void setButtonsEnabled(final ButtonGroup buttons,\n                                 final boolean enabled)\n  {\n    final Enumeration<AbstractButton> elements = buttons.getElements();\n    while (elements.hasMoreElements()) {\n      elements.nextElement().setEnabled(enabled);\n    }\n  }\n\n  public void updateSourceInfo(final int sourcePioNum, final int sourceSmNum)\n  {\n    final String sourcePioText =\n      sourcePioNum >= 0 ? \"PIO\" + sourcePioNum : \"not available\";\n    lbSourcePioNum.setText(sourcePioText);\n    final String sourceSmText =\n      sourceSmNum >= 0 ? \"SM\" + sourceSmNum : \"not available\";\n    lbSourceSmNum.setText(sourceSmText);\n  }\n\n  @Override\n  public void setEnabled(final boolean enabled)\n  {\n    super.setEnabled(enabled);\n    lbPio.setEnabled(enabled);\n    setButtonsEnabled(pioButtons, enabled);\n    cbUseSourcePio.setEnabled(enabled);\n    lbSourcePioLabel.setEnabled(enabled);\n    lbSourcePioNum.setEnabled(enabled);\n    lbSm.setEnabled(enabled);\n    setButtonsEnabled(smButtons, enabled);\n    cbUseSourceSm.setEnabled(enabled);\n    lbSourceSmLabel.setEnabled(enabled);\n    lbSourceSmNum.setEnabled(enabled);\n  }\n\n  private void selectPio(final int selectedPio)\n  {\n    this.selectedPio = selectedPio;\n    final Enumeration<AbstractButton> buttons = pioButtons.getElements();\n    int pioNum = 0;\n    while (buttons.hasMoreElements()) {\n      final JRadioButton button = (JRadioButton)buttons.nextElement();\n      button.setSelected(pioNum++ == selectedPio);\n    }\n  }\n\n  private void selectSm(final int selectedSm)\n  {\n    this.selectedSm = selectedSm;\n    final Enumeration<AbstractButton> buttons = smButtons.getElements();\n    int smNum = 0;\n    while (buttons.hasMoreElements()) {\n      final JRadioButton button = (JRadioButton)buttons.nextElement();\n      button.setSelected(smNum++ == selectedSm);\n    }\n  }\n\n  public void load(final ValuedSignal<?> signal,\n                   final int sourcePioNum, final int sourceSmNum)\n  {\n    final int signalPioNum;\n    final int signalSmNum;\n    if (signal != null) {\n      final SignalRendering.SignalParams signalParams =\n        signal.getSignalParams();\n      signalPioNum = signalParams.getPioNum();\n      signalSmNum = signalParams.getSmNum();\n    } else {\n      signalPioNum = -1;\n      signalSmNum = -1;\n    }\n    final int pioNum = signalPioNum >= 0 ? signalPioNum : sourcePioNum;\n    final int smNum = signalSmNum >= 0 ? signalSmNum : sourceSmNum;\n    selectPio(pioNum >= 0 ? pioNum : 0);\n    selectSm(smNum >= 0 ? smNum : 0);\n    cbUseSourcePio.setSelected(pioNum == sourcePioNum);\n    cbUseSourceSm.setSelected(smNum == sourceSmNum);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/TelemetryPanel.java",
    "content": "/*\n * @(#)TelemetryPanel.java 1.00 21/06/27\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.util.Objects;\nimport java.util.function.DoubleSupplier;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport org.soundpaint.rp2040pio.SwingUtils;\n\npublic class TelemetryPanel extends JPanel\n{\n  private static final long serialVersionUID = -2133401042831474028L;\n\n  private final DiagramModel model;\n  private final DoubleSupplier leftMostVisibleCycleGetter;\n  private final JLabel lbCycles;\n  private final JLabel lbPosition;\n\n  private TelemetryPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public TelemetryPanel(final DiagramModel model,\n                        final DoubleSupplier leftMostVisibleCycleGetter)\n  {\n    Objects.requireNonNull(model);\n    this.model = model;\n    Objects.requireNonNull(leftMostVisibleCycleGetter);\n    this.leftMostVisibleCycleGetter = leftMostVisibleCycleGetter;\n    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));\n    add(new JLabel(\"Cycles Recorded:\"));\n    add(Box.createHorizontalStrut(5));\n    lbCycles = new JLabel();\n    add(lbCycles);\n    add(Box.createHorizontalStrut(15));\n    add(new JLabel(\"Position:\"));\n    add(Box.createHorizontalStrut(5));\n    lbPosition = new JLabel();\n    add(lbPosition);\n    add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(this);\n  }\n\n  public void modelChanged()\n  {\n    lbCycles.setText(String.format(\"%d\", model.getSignalSize()));\n    final double leftMostVisibleCycle =\n      leftMostVisibleCycleGetter.getAsDouble();\n    lbPosition.setText(String.format(\"%d\", (int)leftMostVisibleCycle));\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ToolTip.java",
    "content": "/*\n * @(#)ToolTip.java 1.00 21/04/07\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\npublic class ToolTip\n{\n  final int x0;\n  final int y0;\n  final int x1;\n  final int y1;\n  final String text;\n\n  private ToolTip()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public ToolTip(final int x0, final int y0, final int x1, final int y1,\n                 final String text)\n  {\n    if (text == null) {\n      throw new NullPointerException(\"text\");\n    }\n    this.x0 = x0;\n    this.y0 = y0;\n    this.x1 = x1;\n    this.y1 = y1;\n    this.text = text;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ValueFilterPanel.java",
    "content": "/*\n * @(#)ValueFilterPanel.java 1.00 21/07/11\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JCheckBox;\nimport javax.swing.JPanel;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class ValueFilterPanel extends JPanel\n{\n  private static final long serialVersionUID = -5391629852386365778L;\n\n  private final Diagram diagram;\n  private final SDK sdk;\n  private final Consumer<Void> filterChangedListener;\n  private final JCheckBox cbNoDelayFilter;\n  private final JCheckBox cbClkEnabledFilter;\n\n  private ValueFilterPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public ValueFilterPanel(final Diagram diagram, final SDK sdk,\n                          final Consumer<Void> filterChangedListener)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(filterChangedListener);\n    this.diagram = diagram;\n    this.sdk = sdk;\n    this.filterChangedListener = filterChangedListener;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.\n              createTitledBorder(\"Gray Out Cycle As Undefined When\"));\n    cbNoDelayFilter = new JCheckBox(\"Cycle is a delay cycle on \" +\n                                    \"below target state machine.\");\n    cbNoDelayFilter.\n      addChangeListener((event) -> filterChangedListener.accept(null));\n    addNoDelayFilter();\n    cbClkEnabledFilter = new JCheckBox(\"CLK enable signal is false for \" +\n                                       \"below target state machine.\");\n    cbClkEnabledFilter.\n      addChangeListener((event) -> filterChangedListener.accept(null));\n    addClkEnabledFilter();\n  }\n\n  private void addNoDelayFilter()\n  {\n    final JPanel line = new JPanel();\n    line.setLayout(new BoxLayout(line, BoxLayout.LINE_AXIS));\n    line.add(cbNoDelayFilter);\n    line.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(line);\n    add(line);\n  }\n\n  private void addClkEnabledFilter()\n  {\n    final JPanel line = new JPanel();\n    line.setLayout(new BoxLayout(line, BoxLayout.LINE_AXIS));\n    line.add(cbClkEnabledFilter);\n    line.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(line);\n    add(line);\n  }\n\n  public boolean isSmSelectionRelevant()\n  {\n    return\n      isEnabled() &&\n      (cbNoDelayFilter.isSelected() || cbClkEnabledFilter.isSelected());\n  }\n\n  /**\n   * Filter returns true, if current signal value passes the filter's\n   * condition(s) for value display, and false otherwise.\n   */\n  public List<SignalFilter> createFilters()\n  {\n    return createFilters(cbNoDelayFilter.isSelected(),\n                         cbClkEnabledFilter.isSelected());\n  }\n\n  // TODO: Make private again when removing demo signals from Diagram class\n  public static List<SignalFilter> createFilters(final boolean createNoDelay,\n                                                 final boolean createClkEnabled)\n  {\n    final List<SignalFilter> filters = new ArrayList<SignalFilter>();\n    if (createNoDelay) {\n      filters.add(SignalFilter.NO_DELAY);\n    }\n    if (createClkEnabled) {\n      filters.add(SignalFilter.CLK_ENABLED);\n    }\n    return filters;\n  }\n\n  @Override\n  public void setEnabled(final boolean enabled)\n  {\n    super.setEnabled(enabled);\n    cbNoDelayFilter.setEnabled(enabled);\n    cbClkEnabledFilter.setEnabled(enabled);\n  }\n\n  public void load(final ValuedSignal<?> signal)\n  {\n    final List<SignalFilter> displayFilters;\n    if (signal != null) {\n      final SignalRendering.SignalParams signalParams =\n        signal.getSignalParams();\n      displayFilters = signalParams.getDisplayFilters();\n    } else {\n      displayFilters = null;\n    }\n    if (displayFilters != null) {\n      boolean selectNoDelay = false;\n      boolean selectClkenabled = false;\n      for (final SignalFilter filter : displayFilters) {\n        selectNoDelay |= filter == SignalFilter.NO_DELAY;\n        selectClkenabled |= filter == SignalFilter.CLK_ENABLED;\n      }\n      cbNoDelayFilter.setSelected(selectNoDelay);\n      cbClkEnabledFilter.setSelected(selectClkenabled);\n    } else {\n      cbNoDelayFilter.setSelected(false);\n      cbClkEnabledFilter.setSelected(false);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ValueRenderingPanel.java",
    "content": "/*\n * @(#)ValueRenderingPanel.java 1.00 21/07/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport javax.swing.AbstractButton;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class ValueRenderingPanel extends JPanel\n{\n  private static final long serialVersionUID = -5251622302191656176L;\n\n  private final Diagram diagram;\n  private final SDK sdk;\n  private final Consumer<Void> renderingChangedListener;\n  private final ButtonGroup buttonGroup;\n  private SignalRendering selectedRendering;\n\n  private ValueRenderingPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public ValueRenderingPanel(final Diagram diagram, final SDK sdk,\n                             final Consumer<Void> renderingChangedListener)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(renderingChangedListener);\n    this.diagram = diagram;\n    this.sdk = sdk;\n    this.renderingChangedListener = renderingChangedListener;\n    selectedRendering = null;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(\"Render Value As\"));\n    buttonGroup = new ButtonGroup();\n    for (final SignalRendering rendering : SignalRendering.values()) {\n      createAndAddRendering(rendering);\n    }\n  }\n\n  private void createAndAddRendering(final SignalRendering rendering)\n  {\n    final JPanel line = new JPanel();\n    line.setLayout(new BoxLayout(line, BoxLayout.LINE_AXIS));\n\n    final boolean selected = rendering == SignalRendering.Signed;\n    final JRadioButton rbRendering =\n      new JRadioButton(rendering.toString(), selected);\n    if (selected) {\n      selectedRendering = rendering;\n    }\n    rbRendering.addActionListener((action) -> renderingSelected(rendering));\n    buttonGroup.add(rbRendering);\n    line.add(rbRendering);\n    line.add(Box.createHorizontalGlue());\n    add(line);\n  }\n\n  private void renderingSelected(final SignalRendering rendering)\n  {\n    selectedRendering = rendering;\n    renderingChangedListener.accept(null);\n  }\n\n  public boolean isSmSelectionRelevant()\n  {\n    return selectedRendering == SignalRendering.Mnemonic;\n  }\n\n  public Signal createSignal(final String label,\n                             final int pioNum,\n                             final int smNum,\n                             final int address,\n                             final int msb,\n                             final int lsb,\n                             final List<SignalFilter> displayFilters,\n                             final boolean visible)\n  {\n    if ((msb < 0) || (lsb < 0)) {\n      JOptionPane.showMessageDialog(this,\n                                    \"Please select a contiguous range of bits.\",\n                                    \"No Bits Range Selected\",\n                                    JOptionPane.ERROR_MESSAGE);\n      return null;\n    }\n    try {\n      final Signal signal =\n        selectedRendering.createSignal(diagram, sdk, label, address,\n                                       msb, lsb, displayFilters, pioNum, smNum);\n      signal.setVisible(visible);\n      return signal;\n    } catch (final IOException e) {\n      JOptionPane.showMessageDialog(this, e.getMessage(),\n                                    \"I/O Exception\",\n                                    JOptionPane.ERROR_MESSAGE);\n      return null;\n    }\n  }\n\n  @Override\n  public void setEnabled(final boolean enabled)\n  {\n    super.setEnabled(enabled);\n    final Enumeration<AbstractButton> buttons = buttonGroup.getElements();\n    while (buttons.hasMoreElements()) {\n      buttons.nextElement().setEnabled(enabled);\n    }\n  }\n\n  private void selectRendering(final SignalRendering signalRendering)\n  {\n    selectedRendering =\n      signalRendering != null ? signalRendering : SignalRendering.Signed;\n    final Enumeration<AbstractButton> buttons = buttonGroup.getElements();\n    while (buttons.hasMoreElements()) {\n      final JRadioButton button = (JRadioButton)buttons.nextElement();\n      final boolean isSelected =\n        /*\n         * TODO: Better use a hash map rather than relying on button\n         * label.\n         */\n        button.getText() == selectedRendering.toString();\n      button.setSelected(isSelected);\n    }\n  }\n\n  public void load(final ValuedSignal<?> signal)\n  {\n    selectRendering(signal != null ? signal.getValueRendering() : null);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ValueSourcePanel.java",
    "content": "/*\n * @(#)ValueSourcePanel.java 1.00 21/06/30\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Component;\nimport java.awt.Point;\nimport java.awt.Rectangle;\nimport java.awt.event.ItemEvent;\nimport java.io.IOException;\nimport java.util.Objects;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JComboBox;\nimport javax.swing.JLabel;\nimport javax.swing.JTable;\nimport javax.swing.JOptionPane;\nimport javax.swing.JPanel;\nimport javax.swing.JScrollPane;\nimport javax.swing.JViewport;\nimport javax.swing.ListSelectionModel;\nimport javax.swing.event.ListSelectionEvent;\nimport javax.swing.table.DefaultTableCellRenderer;\nimport javax.swing.table.DefaultTableModel;\nimport javax.swing.table.TableColumn;\nimport javax.swing.table.TableColumnModel;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.GPIOPadsBank0Registers;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs.BitsInfo;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs.BitsRange;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs.BitsType;\nimport org.soundpaint.rp2040pio.doctool.RegistersDocs.RegisterDetails;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class ValueSourcePanel extends JPanel\n  implements org.soundpaint.rp2040pio.observer.diagram.Constants\n{\n  private static final long serialVersionUID = -726756788602613552L;\n  private static final String COLUMN_BITS_RANGE = \"Bits\";\n  private static final int COLUMN_BITS_RANGE_IDX = 0;\n  private static final String COLUMN_NAME = \"Name\";\n  private static final int COLUMN_NAME_IDX = 1;\n  private static final String COLUMN_DESCRIPTION = \"Description\";\n  private static final int COLUMN_DESCRIPTION_IDX = 2;\n  private static final String COLUMN_TYPE = \"Type\";\n  private static final int COLUMN_TYPE_IDX = 3;\n  private static final String COLUMN_RESET_VALUE = \"Reset Value\";\n  private static final int COLUMN_RESET_VALUE_IDX = 4;\n\n  private static class BitsRangeCellRenderer extends DefaultTableCellRenderer\n  {\n    private static final long serialVersionUID = 8594096152113741258L;\n\n    @Override\n    protected void setValue(final Object value)\n    {\n      setText((value == null) ? \"\" : ((BitsRange)value).toShortString());\n    }\n  }\n\n  private enum RegistersSet\n  {\n    PIO0_REGS(\"PIO0 Registers\", \"PIO0_\",\n              PIORegisters.Regs.getRegisterSetLabel(),\n              PIORegisters.Regs.getRegisterSetDescription(),\n              PIORegisters.Regs.values(),\n              Constants.PIO0_BASE, 0),\n    PIO0_ADD_ON_REGS(\"PIO0 Add-on Registers\", \"PIO0_\",\n                     PIOEmuRegisters.Regs.getRegisterSetLabel(),\n                     PIOEmuRegisters.Regs.getRegisterSetDescription(),\n                     PIOEmuRegisters.Regs.values(),\n                     Constants.PIO0_EMU_BASE, 0),\n    PIO1_REGS(\"PIO1 Registers\", \"PIO1_\",\n              PIORegisters.Regs.getRegisterSetLabel(),\n              PIORegisters.Regs.getRegisterSetDescription(),\n              PIORegisters.Regs.values(),\n              Constants.PIO1_BASE, 1),\n    PIO1_ADD_ON_REGS(\"PIO1 Add-on Registers\", \"PIO1_\",\n                     PIOEmuRegisters.Regs.getRegisterSetLabel(),\n                     PIOEmuRegisters.Regs.getRegisterSetDescription(),\n                     PIOEmuRegisters.Regs.values(),\n                     Constants.PIO1_EMU_BASE, 1),\n    GPIO_IO_BANK0_REGS(\"GPIO IO Bank0 Registers\", \"\",\n                       GPIOIOBank0Registers.Regs.getRegisterSetLabel(),\n                       GPIOIOBank0Registers.Regs.getRegisterSetDescription(),\n                       GPIOIOBank0Registers.Regs.values(),\n                       Constants.IO_BANK0_BASE, -1),\n    GPIO_PADS_BANK0_REGS(\"GPIO Pads Bank0 Registers\", \"\",\n                         GPIOPadsBank0Registers.Regs.getRegisterSetLabel(),\n                         GPIOPadsBank0Registers.Regs.getRegisterSetDescription(),\n                         GPIOPadsBank0Registers.Regs.values(),\n                         Constants.PADS_BANK0_BASE, -1),\n    PICO_ADD_ON_REGS(\"Global Add-on Registers\", \"\",\n                     PicoEmuRegisters.Regs.getRegisterSetLabel(),\n                     PicoEmuRegisters.Regs.getRegisterSetDescription(),\n                     PicoEmuRegisters.Regs.values(),\n                     Constants.EMULATOR_BASE, -1);\n\n    private static RegistersSet fromAddress(final int address)\n    {\n      final int baseAddress = address & 0xffffc000;\n      for (final RegistersSet registersSet : RegistersSet.values()) {\n        if (registersSet.baseAddress == baseAddress)\n          return registersSet;\n      }\n      return null;\n    }\n\n    private final String id;\n    private final String label;\n    private final String description;\n    private final RegistersDocs<? extends Enum<?>>[] regs;\n    private final int baseAddress;\n    private final int pioNum;\n    private final String suggestedLabelPrefix;\n\n    private RegistersSet(final String id,\n                         final String suggestedLabelPrefix,\n                         final String label,\n                         final String description,\n                         final RegistersDocs<? extends Enum<?>>[] regs,\n                         final int baseAddress,\n                         final int pioNum)\n    {\n      this.id = id;\n      this.label = label;\n      this.description = description;\n      this.regs = regs;\n      this.baseAddress = baseAddress;\n      this.pioNum = pioNum;\n      this.suggestedLabelPrefix = suggestedLabelPrefix;\n    }\n\n    @Override\n    public String toString() { return id; }\n  }\n\n  private final Diagram diagram;\n  private final SDK sdk;\n  private final Consumer<String> suggestedLabelSetter;\n  private final Consumer<Void> sourceChangedListener;\n  private final JLabel lbRegistersSet;\n  private final JComboBox<RegistersSet> cbRegistersSet;\n  private final JLabel lbRegistersSetInfo;\n  private final JLabel lbRegister;\n  private final JComboBox<RegistersDocs<? extends Enum<?>>> cbRegister;\n  private final JLabel lbRegisterInfo;\n  private final JLabel lbRegisterBitsInfos;\n  private final JLabel lbRegisterBits;\n  private final DefaultTableModel bitsInfos;\n  private final JTable tbBitsInfos;\n  private final JScrollPane tbBitsInfosScroll;\n\n  private ValueSourcePanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported default constructor\");\n  }\n\n  public ValueSourcePanel(final Diagram diagram, final SDK sdk,\n                          final Consumer<String> suggestedLabelSetter,\n                          final Consumer<Void> sourceChangedListener)\n  {\n    Objects.requireNonNull(diagram);\n    Objects.requireNonNull(sdk);\n    Objects.requireNonNull(suggestedLabelSetter);\n    Objects.requireNonNull(sourceChangedListener);\n    this.diagram = diagram;\n    this.sdk = sdk;\n    this.suggestedLabelSetter = suggestedLabelSetter;\n    this.sourceChangedListener = sourceChangedListener;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(\"Use Value From Register\"));\n    bitsInfos = createTableModel();\n    tbBitsInfos = new JTable(bitsInfos);\n    setupTableColumnRenderers();\n    tbBitsInfos.setColumnSelectionAllowed(false);\n    tbBitsInfos.getSelectionModel().\n      addListSelectionListener((selection) -> selectionChanged(selection));\n    tbBitsInfosScroll = new JScrollPane(tbBitsInfos);\n    lbRegistersSet = new JLabel(\"Register Set\");\n    lbRegistersSetInfo = new JLabel();\n    lbRegistersSet.setPreferredSize(PREFERRED_LABEL_SIZE);\n    lbRegisterBitsInfos = new JLabel(\"Bits Range\");\n    lbRegisterBitsInfos.setPreferredSize(PREFERRED_LABEL_SIZE);\n    lbRegisterBits = new JLabel();\n    lbRegisterBits.setPreferredSize(PREFERRED_LABEL_SIZE);\n    cbRegistersSet = addRegistersSetSelection();\n    add(Box.createVerticalStrut(5));\n    lbRegister = new JLabel(\"Register\");\n    lbRegister.setPreferredSize(PREFERRED_LABEL_SIZE);\n    lbRegisterInfo = new JLabel();\n    cbRegister = addRegisterSelection();\n    add(Box.createVerticalStrut(5));\n    addBitsSelection();\n    SwingUtils.setPreferredHeightAsMaximum(this);\n  }\n\n  public void initRegistersForSelectedRegisterSet()\n  {\n    registersSetSelected((RegistersSet)cbRegistersSet.getSelectedItem());\n  }\n\n  private void setupTableColumnRenderers()\n  {\n    final TableColumnModel model = tbBitsInfos.getColumnModel();\n    final TableColumn bitsRangeColumn = model.getColumn(COLUMN_BITS_RANGE_IDX);\n    bitsRangeColumn.setCellRenderer(new BitsRangeCellRenderer());\n  }\n\n  private DefaultTableModel createTableModel()\n  {\n    final DefaultTableModel model = new DefaultTableModel() {\n        @Override\n        public boolean isCellEditable(final int rowIndex, final int columnIndex)\n        {\n          return false;\n        }\n      };\n    model.addColumn(COLUMN_BITS_RANGE);\n    model.addColumn(COLUMN_NAME);\n    model.addColumn(COLUMN_DESCRIPTION);\n    model.addColumn(COLUMN_TYPE);\n    model.addColumn(COLUMN_RESET_VALUE);\n    return model;\n  }\n\n  private JComboBox<RegistersSet> addRegistersSetSelection()\n  {\n    final JPanel registersSetSelection = new JPanel();\n    registersSetSelection.\n      setLayout(new BoxLayout(registersSetSelection, BoxLayout.LINE_AXIS));\n    registersSetSelection.add(lbRegistersSet);\n    registersSetSelection.add(Box.createHorizontalStrut(5));\n    final JComboBox<RegistersSet> cbRegistersSet =\n      new JComboBox<RegistersSet>(RegistersSet.values());\n    cbRegistersSet.addItemListener((event) -> {\n        if (event.getStateChange() == ItemEvent.SELECTED) {\n          registersSetSelected((RegistersSet)event.getItem());\n        }\n      });\n    cbRegistersSet.setMaximumSize(cbRegistersSet.getPreferredSize());\n    registersSetSelection.add(cbRegistersSet);\n    registersSetSelection.add(Box.createHorizontalGlue());\n    add(registersSetSelection);\n    final JPanel registersSetInfo = new JPanel();\n    registersSetInfo.\n      setLayout(new BoxLayout(registersSetInfo, BoxLayout.LINE_AXIS));\n    final JLabel lbFiller = new JLabel();\n    lbFiller.setPreferredSize(PREFERRED_LABEL_SIZE);\n    registersSetInfo.add(lbFiller);\n    registersSetInfo.add(Box.createHorizontalStrut(5));\n    registersSetInfo.add(lbRegistersSetInfo);\n    registersSetInfo.add(Box.createHorizontalGlue());\n    add(registersSetInfo);\n    return cbRegistersSet;\n  }\n\n  private JComboBox<RegistersDocs<? extends Enum<?>>> addRegisterSelection()\n  {\n    final JPanel registerSelection = new JPanel();\n    registerSelection.\n      setLayout(new BoxLayout(registerSelection, BoxLayout.LINE_AXIS));\n    registerSelection.add(lbRegister);\n    registerSelection.add(Box.createHorizontalStrut(5));\n    final JComboBox<RegistersDocs<? extends Enum<?>>> cbRegister =\n      new JComboBox<RegistersDocs<? extends Enum<?>>>();\n    cbRegister.addItemListener((event) -> {\n        if (event.getStateChange() == ItemEvent.SELECTED) {\n          @SuppressWarnings(\"unchecked\")\n          final RegistersDocs<? extends Enum<?>> docs =\n            (RegistersDocs<? extends Enum<?>>)event.getItem();\n          registerSelected(docs);\n        }\n      });\n    registerSelection.add(cbRegister);\n    registerSelection.add(Box.createHorizontalGlue());\n    add(registerSelection);\n    final JPanel registerInfo = new JPanel();\n    registerInfo.setLayout(new BoxLayout(registerInfo, BoxLayout.LINE_AXIS));\n    final JLabel lbFiller = new JLabel();\n    lbFiller.setPreferredSize(PREFERRED_LABEL_SIZE);\n    registerInfo.add(lbFiller);\n    registerInfo.add(Box.createHorizontalStrut(5));\n    registerInfo.add(lbRegisterInfo);\n    registerInfo.add(Box.createHorizontalGlue());\n    add(registerInfo);\n    return cbRegister;\n  }\n\n  private void addBitsSelection()\n  {\n    final JPanel bitsSelection = new JPanel();\n    bitsSelection.setLayout(new BoxLayout(bitsSelection, BoxLayout.LINE_AXIS));\n    final JPanel labelPanel = new JPanel();\n    labelPanel.setLayout(new BoxLayout(labelPanel, BoxLayout.PAGE_AXIS));\n    labelPanel.add(lbRegisterBitsInfos);\n    labelPanel.add(lbRegisterBits);\n    bitsSelection.add(labelPanel);\n    bitsSelection.add(Box.createHorizontalStrut(5));\n    tbBitsInfos.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);\n    bitsSelection.add(tbBitsInfosScroll);\n    bitsSelection.add(Box.createHorizontalGlue());\n    add(bitsSelection);\n  }\n\n  private void registersSetSelected(final RegistersSet registersSet)\n  {\n    lbRegistersSetInfo.setText(String.format(\"%s @ 0x%08x\",\n                                             registersSet.label,\n                                             registersSet.baseAddress));\n    cbRegister.removeAllItems();\n    for (RegistersDocs<? extends Enum<?>> register : registersSet.regs) {\n      cbRegister.addItem(register);\n    }\n    cbRegister.setMaximumSize(cbRegister.getPreferredSize());\n    registerSelected(cbRegister.getItemAt(0));\n  }\n\n  private String getSuggestedBitsRange()\n  {\n    if (bitsInfos.getRowCount() == 0) {\n      return null;\n    }\n    final int minSelectionRow = tbBitsInfos.getSelectedRow();\n    final int maxSelectionRow =\n      minSelectionRow + tbBitsInfos.getSelectedRowCount() - 1;\n    if ((minSelectionRow < 0) || (maxSelectionRow < 0)) {\n      return null;\n    }\n    final int msb =\n      ((BitsRange)bitsInfos.getValueAt(minSelectionRow, COLUMN_BITS_RANGE_IDX)).\n      getMsb();\n    final int lsb =\n      ((BitsRange)bitsInfos.getValueAt(maxSelectionRow, COLUMN_BITS_RANGE_IDX)).\n      getLsb();\n    return String.format(\"[%s]\", msb != lsb ? msb + \":\" + lsb : msb);\n  }\n\n  private String getSuggestedBitsLabel()\n  {\n    if (bitsInfos.getRowCount() <= 1) {\n      return \"\";\n    }\n    boolean haveUnselectedRelevantBits = false;\n    for (int row = 0; row < bitsInfos.getRowCount(); row++) {\n      if (!tbBitsInfos.isRowSelected(row)) {\n        if (((BitsType)bitsInfos.getValueAt(row, COLUMN_TYPE_IDX)).\n            isRelevant()) {\n          haveUnselectedRelevantBits = true;\n          break;\n        }\n      }\n    }\n    if (!haveUnselectedRelevantBits) {\n      return \"\";\n    }\n    final int minSelectionRow = tbBitsInfos.getSelectedRow();\n    final int maxSelectionRow =\n      minSelectionRow + tbBitsInfos.getSelectedRowCount() - 1;\n    if ((minSelectionRow < 0) || (maxSelectionRow < 0)) {\n      // empty range\n      return null;\n    }\n    if (minSelectionRow == maxSelectionRow) {\n      final String name =\n        (String)bitsInfos.getValueAt(minSelectionRow, COLUMN_NAME_IDX);\n      if (name != null) {\n        return String.format(\"_%s\", name);\n      }\n    }\n    final int msb =\n      ((BitsRange)bitsInfos.getValueAt(minSelectionRow, COLUMN_BITS_RANGE_IDX)).\n      getMsb();\n    final int lsb =\n      ((BitsRange)bitsInfos.getValueAt(maxSelectionRow, COLUMN_BITS_RANGE_IDX)).\n      getLsb();\n    return String.format(\"[%s]\", msb != lsb ? msb + \":\" + lsb : msb);\n  }\n\n  private RegistersDocs<? extends Enum<?>> getSelectedRegister()\n  {\n    @SuppressWarnings(\"unchecked\")\n    final RegistersDocs<? extends Enum<?>> register =\n      (RegistersDocs<? extends Enum<?>>)cbRegister.getSelectedItem();\n    return register;\n  }\n\n  private String getSuggestedLabel(final String suggestedBitsLabel)\n  {\n    final RegistersSet registersSet =\n      (RegistersSet)cbRegistersSet.getSelectedItem();\n    final String suggestedLabelPrefix = registersSet.suggestedLabelPrefix;\n    final RegistersDocs<? extends Enum<?>> register = getSelectedRegister();\n    return String.format(\"%s%s%s\",\n                         suggestedLabelPrefix, register, suggestedBitsLabel);\n  }\n\n  private int chooseBitsInfosRow()\n  {\n    for (int row = bitsInfos.getRowCount() - 1; row >= 0; row--) {\n      final BitsType type =\n        (BitsType)bitsInfos.getValueAt(row, COLUMN_TYPE_IDX);\n      if ((type != BitsType.RESERVED) && (type != BitsType.UNUSED))\n        return row;\n    }\n    return -1;\n  }\n\n  public void updateSuggestedLabel()\n  {\n    final String suggestedBitsRange = getSuggestedBitsRange();\n    if (suggestedBitsRange != null) {\n      lbRegisterBits.setText(suggestedBitsRange);\n    } else {\n      lbRegisterBits.setText(\"\");\n    }\n    final String suggestedBitsLabel = getSuggestedBitsLabel();\n    if (suggestedBitsLabel != null) {\n      final String suggestedLabel = getSuggestedLabel(suggestedBitsLabel);\n      suggestedLabelSetter.accept(suggestedLabel);\n    }\n  }\n\n  private void sourceChanged()\n  {\n    updateSuggestedLabel();\n    sourceChangedListener.accept(null);\n  }\n\n  private void ensureCellIsVisible(final int row, final int column)\n  {\n    if (tbBitsInfos.getParent() instanceof JViewport) {\n      final JViewport viewport = (JViewport)tbBitsInfos.getParent();\n      final Rectangle cellRect = tbBitsInfos.getCellRect(row, column, true);\n      final Point viewPosition = viewport.getViewPosition();\n      cellRect.translate(-viewPosition.x, -viewPosition.y);\n      tbBitsInfos.scrollRectToVisible(cellRect);\n    }\n  }\n\n  private void registerSelected(final RegistersDocs<? extends Enum<?>> register)\n  {\n    lbRegisterInfo.setText(String.format(\"%s\", register.getInfo()));\n    bitsInfos.setRowCount(0);\n    final RegisterDetails registerDetails = register.getRegisterDetails();\n    for (final BitsInfo bitsInfo : registerDetails.getBitsInfos()) {\n      final Object[] rowData = new Object[5];\n      rowData[COLUMN_BITS_RANGE_IDX] = bitsInfo.getBitsRange();\n      rowData[COLUMN_NAME_IDX] = bitsInfo.getName();\n      rowData[COLUMN_DESCRIPTION_IDX] = bitsInfo.getDescription();\n      rowData[COLUMN_TYPE_IDX] = bitsInfo.getType();\n      rowData[COLUMN_RESET_VALUE_IDX] = bitsInfo.getResetValue();\n      bitsInfos.addRow(rowData);\n    }\n    tbBitsInfosScroll.setMaximumSize(tbBitsInfosScroll.getPreferredSize());\n    tbBitsInfosScroll.revalidate();\n    final int row = chooseBitsInfosRow();\n    if (row >= 0) {\n      tbBitsInfos.setRowSelectionInterval(row, row);\n      ensureCellIsVisible(row, 0);\n    }\n    sourceChanged();\n  }\n\n  /**\n   * @return The number of the associated PIO (0…1), or -1, if the\n   * selected register set is not related to any specific PIO.\n   */\n  public int getSelectedRegisterSetPio()\n  {\n    return ((RegistersSet)cbRegistersSet.getSelectedItem()).pioNum;\n  }\n\n  /**\n   * @return The number of the associated SM (0…3), or -1, if the\n   * selected register is not related to any specific SM.\n   */\n  public int getSelectedRegisterSm()\n  {\n    final RegistersDocs<? extends Enum<?>> register = getSelectedRegister();\n    return register.getRegisterDetails().getSmNum();\n  }\n\n  public int getSelectedRegisterAddress()\n  {\n    final int baseAddress =\n      ((RegistersSet)cbRegistersSet.getSelectedItem()).baseAddress;\n    return baseAddress + 0x4 * cbRegister.getSelectedIndex();\n  }\n\n  public int getSelectedRegisterMsb()\n  {\n    final int minSelectionRow = tbBitsInfos.getSelectedRow();\n    if (minSelectionRow < 0) return -1;\n    final BitsRange upperMostBitsRange =\n      (BitsRange)bitsInfos.getValueAt(minSelectionRow, COLUMN_BITS_RANGE_IDX);\n    return upperMostBitsRange.getMsb();\n  }\n\n  public int getSelectedRegisterLsb()\n  {\n    final int maxSelectionRow =\n      tbBitsInfos.getSelectedRow() + tbBitsInfos.getSelectedRowCount() - 1;\n    if (maxSelectionRow < 0) return -1;\n    final BitsRange lowerMostBitsRange =\n      (BitsRange)bitsInfos.getValueAt(maxSelectionRow, COLUMN_BITS_RANGE_IDX);\n    return lowerMostBitsRange.getLsb();\n  }\n\n  @Override\n  public void setEnabled(final boolean enabled)\n  {\n    super.setEnabled(enabled);\n    lbRegistersSet.setEnabled(enabled);\n    cbRegistersSet.setEnabled(enabled);\n    lbRegistersSetInfo.setEnabled(enabled);\n    lbRegister.setEnabled(enabled);\n    cbRegister.setEnabled(enabled);\n    lbRegisterInfo.setEnabled(enabled);\n    lbRegisterBitsInfos.setEnabled(enabled);\n    lbRegisterBits.setEnabled(enabled);\n    tbBitsInfos.setEnabled(enabled);\n    tbBitsInfos.setRowSelectionAllowed(enabled);\n    tbBitsInfosScroll.getHorizontalScrollBar().setEnabled(enabled);\n    tbBitsInfosScroll.getVerticalScrollBar().setEnabled(enabled);\n  }\n\n  private void selectionChanged(final ListSelectionEvent selection)\n  {\n    if (!selection.getValueIsAdjusting()) {\n      sourceChanged();\n    }\n  }\n\n  private void resetInputs()\n  {\n    cbRegistersSet.setSelectedIndex(0);\n    initRegistersForSelectedRegisterSet();\n  }\n\n  private void selectRegisterByAddress(final int address)\n  {\n    final RegistersSet registersSet = RegistersSet.fromAddress(address);\n    if (registersSet != null) {\n      cbRegistersSet.setSelectedItem(registersSet);\n      registersSetSelected(registersSet);\n      final int registerIndex = (address - registersSet.baseAddress) >> 2;\n      if (registerIndex < cbRegister.getItemCount()) {\n        cbRegister.setSelectedIndex(registerIndex);\n        registerSelected(cbRegister.getItemAt(registerIndex));\n      } else {\n        final String message =\n          String.format(\"warning: registers set %s: \" +\n                        \"register not found for signal address 0x%8x\",\n                        registersSet, address);\n        diagram.getConsole().println(message);\n      }\n    } else {\n      final String message =\n        String.format(\"warning: \" +\n                      \"no registers set found for signal address 0x%8x\",\n                      address);\n      diagram.getConsole().println(message);\n      resetInputs();\n    }\n  }\n\n  private void selectBitsRange(final int msb, final int lsb)\n  {\n    final int rowCount = tbBitsInfos.getRowCount();\n    for (int row = 0; row < rowCount; row++) {\n      final BitsRange bitsRange =\n        (BitsRange)bitsInfos.getValueAt(row, COLUMN_BITS_RANGE_IDX);\n      final int rangeMsb = bitsRange.getMsb();\n      final int rangeLsb = bitsRange.getLsb();\n      if ((rangeMsb <= msb) && (rangeLsb >= lsb)) {\n        tbBitsInfos.addRowSelectionInterval(row, row);\n      } else if (((rangeMsb <= msb) && (rangeMsb >= lsb)) ||\n                 ((rangeLsb <= msb) && (rangeLsb >= lsb))) {\n        final String message =\n          String.format(\"warning: signal specifies bit range [%d:%d] not \" +\n                        \"describable by register bit ranges; \" +\n                        \"ignoring bit range [%d:%d]\",\n                        msb, lsb, rangeMsb, rangeLsb);\n        diagram.getConsole().println(message);\n      }\n    }\n  }\n\n  public void load(final ValuedSignal<?> signal)\n  {\n    if (signal != null) {\n      final SignalRendering.SignalParams signalParams =\n        signal.getSignalParams();\n      selectRegisterByAddress(signalParams.getAddress());\n      if (signal instanceof RegisterBitSignal) {\n        final int bit = ((RegisterBitSignal)signal).getBit();\n        selectBitsRange(bit, bit);\n      } else {\n        selectBitsRange(signalParams.getMsb(), signalParams.getLsb());\n      }\n    } else {\n      resetInputs();\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/diagram/ValuedSignal.java",
    "content": "/*\n * @(#)ValuedSignal.java 1.00 21/02/12\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.diagram;\n\nimport java.awt.Graphics2D;\nimport java.awt.TexturePaint;\nimport java.awt.geom.Rectangle2D;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.Supplier;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic abstract class ValuedSignal<T> extends AbstractSignal<T>\n{\n  private static final BufferedImage FILL_IMAGE =\n    ((Supplier<BufferedImage>)(() -> {\n        final BufferedImage image =\n          new BufferedImage(12, 12, BufferedImage.TYPE_INT_RGB);\n        final Graphics2D g = (Graphics2D)image.getGraphics();\n        for (int x = -12; x < 12; x += 2) {\n          g.drawLine(x, 12, x + 12, 0);\n        }\n        return image;\n      })).get();\n  protected static final TexturePaint FILL_PAINT =\n    new TexturePaint(FILL_IMAGE, new Rectangle2D.Double(0.0, 0.0, 12.0, 12.0));\n  protected static final double SIGNAL_HEIGHT = 24.0;\n\n  private final SignalRendering valueRendering;\n  private final Supplier<Boolean> changeInfoGetter;\n\n  /**\n   * @param changeInfoGetter If set to &lt;code&gt;null&lt;/code&gt;,\n   * then a change is assumed only when the updated value changes.\n   */\n  protected ValuedSignal(final SignalRendering valueRendering,\n                         final SignalRendering.SignalParams signalParams,\n                         final Supplier<Boolean> changeInfoGetter)\n  {\n    super(signalParams);\n    Objects.requireNonNull(signalParams);\n    Objects.requireNonNull(signalParams.getDiagram());\n    Objects.requireNonNull(signalParams.getSDK());\n    this.valueRendering = valueRendering;\n    this.changeInfoGetter = changeInfoGetter;\n  }\n\n  public SignalRendering getValueRendering()\n  {\n    return valueRendering;\n  }\n\n  @Override\n  protected double getSignalHeight() { return SIGNAL_HEIGHT; }\n\n  abstract protected T sampleValue() throws IOException;\n\n  private boolean passesAllFilters(final List<SignalFilter> displayFilters)\n    throws IOException\n  {\n    final SignalRendering.SignalParams signalParams = getSignalParams();\n    final SDK sdk = signalParams.getSDK();\n    final int pioNum = signalParams.getPioNum();\n    final int smNum = signalParams.getSmNum();\n    for (final SignalFilter displayFilter : displayFilters) {\n      if (!displayFilter.acceptCurrentSignalValue(sdk, pioNum, smNum))\n        return false;\n    }\n    return true;\n  }\n\n  @Override\n  public void record() throws IOException\n  {\n    final boolean enforceChanged =\n      changeInfoGetter != null ? changeInfoGetter.get() : false;\n    final SignalRendering.SignalParams signalParams = getSignalParams();\n    final List<SignalFilter> displayFilters = signalParams.getDisplayFilters();\n    final boolean passes;\n    if (displayFilters != null) {\n      passes = passesAllFilters(displayFilters);\n    } else {\n      passes = true;\n    }\n    record(passes ? sampleValue() : null, enforceChanged);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/fifo/ActionPanel.java",
    "content": "/*\n * @(#)ActionPanel.java 1.00 21/04/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.fifo;\n\nimport java.awt.event.KeyEvent;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.JButton;\n\npublic class ActionPanel extends Box\n{\n  private static final long serialVersionUID = -7011298643093703317L;\n\n  public ActionPanel(final FifoObserver fifoObserver)\n  {\n    super(BoxLayout.LINE_AXIS);\n    Objects.requireNonNull(fifoObserver);\n    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n    add(Box.createHorizontalGlue());\n    final JButton btClose = new JButton(\"Close\");\n    btClose.setMnemonic(KeyEvent.VK_C);\n    btClose.addActionListener((event) -> { fifoObserver.close(); });\n    add(btClose);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/fifo/FifoEntriesViewPanel.java",
    "content": "/*\n * @(#)FifoEntriesViewPanel.java 1.00 21/04/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.fifo;\n\nimport java.awt.Color;\nimport java.awt.Component;\nimport java.awt.Dimension;\nimport java.awt.Font;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport java.util.function.BiFunction;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.DefaultListCellRenderer;\nimport javax.swing.DefaultListModel;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.SwingConstants;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PIO;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class FifoEntriesViewPanel extends JPanel\n{\n  private static final long serialVersionUID = 2577996075333519551L;\n  private static final Font codeFont = new Font(Font.MONOSPACED,\n                                                Font.PLAIN, 12);\n  private static final String errorText = \"error: connection to server lost\";\n  private static final String\n    txtFJoinNone =\n    \"┌───────────────── TX ─────────────────┐┌───────────────── RX ─────────────────┐\";\n  private static final String\n    txtFJoinTX =\n    \"┌───────────────────────────────────── TX ─────────────────────────────────────┐\";\n  private static final String\n    txtFJoinRX =\n    \"┌───────────────────────────────────── RX ─────────────────────────────────────┐\";\n  private static final String\n    txtFJoinBoth =\n    \"┌─────────────────────────────────── (NONE) ───────────────────────────────────┐\";\n  private static final String\n    txtBottomUnjoined =\n    \"└──────────────────────────────────────┘└──────────────────────────────────────┘\";\n  private static final String\n    txtBottomJoined =\n    \"└──────────────────────────────────────────────────────────────────────────────┘\";\n  private static final String ARROW_LEFT = \"←\";\n  private static final String ARROW_RIGHT = \"→\";\n\n  private static enum ColorScheme\n  {\n    RX, TX;\n  }\n\n  private static enum StateColor\n  {\n    QUEUED(Color.WHITE, Color.RED, Color.WHITE, Color.GREEN),\n    QUEUED_READ_PTR(Color.BLACK, Color.RED, Color.BLACK, Color.GREEN),\n    READ_PTR(Color.BLACK, Color.GRAY, Color.BLACK, Color.GRAY),\n    DEQUEUED(Color.LIGHT_GRAY, Color.GRAY, Color.LIGHT_GRAY, Color.GRAY);\n\n    private final Color fgTX;\n    private final Color bgTX;\n    private final Color fgRX;\n    private final Color bgRX;\n\n    private StateColor(final Color fgTX, final Color bgTX,\n                       final Color fgRX, final Color bgRX)\n    {\n      this.fgTX = fgTX;\n      this.bgTX = bgTX;\n      this.fgRX = fgRX;\n      this.bgRX = bgRX;\n    }\n\n    public Color getFgColor(final ColorScheme colorScheme)\n    {\n      return colorScheme == ColorScheme.RX ? fgRX : fgTX;\n    }\n\n    public Color getBgColor(final ColorScheme colorScheme)\n    {\n      return colorScheme == ColorScheme.RX ? bgRX : bgTX;\n    }\n  }\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final JLabel lbTopLine;\n  private final JLabel lbBottomLine;\n  private final JLabel[] lbEntries;\n  private final JLabel[] lbSeparators;\n  private final Integer[] buffer;\n  private final JCheckBox cbFDebugTxStall;\n  private final JCheckBox cbFDebugTxOver;\n  private final JCheckBox cbFDebugRxUnder;\n  private final JCheckBox cbFDebugRxStall;\n  private final JLabel lbOsrLeftHandArrow;\n  private final JLabel lbOsrRightHandArrow;\n  private final JLabel lbIsrLeftHandArrow;\n  private final JLabel lbIsrRightHandArrow;\n  private final JLabel[] lbOsrBits;\n  private final JLabel[] lbIsrBits;\n  private final JCheckBox cbAutoPull;\n  private final JCheckBox cbAutoPush;\n  private int pioNum;\n  private int smNum;\n  private int rxLevel;\n  private int txLevel;\n  private boolean joinRx;\n  private boolean joinTx;\n  private boolean autoScroll;\n\n  private FifoEntriesViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public FifoEntriesViewPanel(final PrintStream console, final SDK sdk,\n                              final boolean initialAutoScroll)\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n\n    lbTopLine = new JLabel();\n    add(createTopLine());\n\n    buffer = new Integer[2 * Constants.FIFO_DEPTH];\n    lbSeparators = new JLabel[2 * Constants.FIFO_DEPTH + 1];\n    lbEntries = new JLabel[2 * Constants.FIFO_DEPTH];\n    add(createEntriesLine());\n\n    lbBottomLine = new JLabel();\n    add(createBottomLine());\n    add(Box.createVerticalStrut(10));\n\n    cbFDebugTxStall = new JCheckBox();\n    cbFDebugTxOver = new JCheckBox();\n    cbFDebugRxUnder = new JCheckBox();\n    cbFDebugRxStall = new JCheckBox();\n    add(createFDebugLine());\n    add(Box.createVerticalStrut(10));\n\n    lbOsrLeftHandArrow = new JLabel();\n    lbOsrRightHandArrow = new JLabel();\n    lbIsrLeftHandArrow = new JLabel();\n    lbIsrRightHandArrow = new JLabel();\n    lbOsrBits = new JLabel[33];\n    lbIsrBits = new JLabel[33];\n    cbAutoPull = new JCheckBox();\n    cbAutoPush = new JCheckBox();\n    add(createShiftRegisters());\n\n    autoScroll = initialAutoScroll;\n    SwingUtils.setPreferredWidthAsMaximum(this);\n  }\n\n  private Box createTopLine()\n  {\n    final Box topLine = new Box(BoxLayout.LINE_AXIS);\n    lbTopLine.setFont(codeFont);\n    topLine.add(lbTopLine);\n    topLine.add(Box.createHorizontalGlue());\n    return topLine;\n  }\n\n  private Box createEntriesLine()\n  {\n    final Box entriesLine = new Box(BoxLayout.LINE_AXIS);\n    for (int entryNum = 0; entryNum < 2 * Constants.FIFO_DEPTH; entryNum++) {\n      final JLabel lbSeparator = new JLabel(entryNum == 0 ? \"│\" : \"  \");\n      lbSeparator.setOpaque(true);\n      lbSeparator.setFont(codeFont);\n      lbSeparators[entryNum] = lbSeparator;\n      entriesLine.add(lbSeparator);\n      final JLabel lbEntry = new JLabel();\n      lbEntry.setOpaque(true);\n      lbEntry.setFont(codeFont);\n      lbEntries[entryNum] = lbEntry;\n      entriesLine.add(lbEntry);\n    }\n    final JLabel lbSeparator = new JLabel(\"│\");\n    lbSeparator.setOpaque(true);\n    lbSeparator.setFont(codeFont);\n    lbSeparators[2 * Constants.FIFO_DEPTH] = lbSeparator;\n    entriesLine.add(lbSeparator);\n    entriesLine.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(entriesLine);\n    return entriesLine;\n  }\n\n  private Box createBottomLine()\n  {\n    final Box bottomLine = new Box(BoxLayout.LINE_AXIS);\n    lbBottomLine.setFont(codeFont);\n    bottomLine.add(lbBottomLine);\n    bottomLine.add(Box.createHorizontalGlue());\n    return bottomLine;\n  }\n\n  private Box createFDebugLine()\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(new JLabel(\"FDEBUG:\"));\n    hBox.add(Box.createHorizontalStrut(20));\n    createFDebugLineCheck(hBox, \"TX Stall\", cbFDebugTxStall);\n    createFDebugLineCheck(hBox, \"TX Over\", cbFDebugTxOver);\n    createFDebugLineCheck(hBox, \"RX Under\", cbFDebugRxUnder);\n    createFDebugLineCheck(hBox, \"RX Stall\", cbFDebugRxStall);\n    return hBox;\n  }\n\n  private void createFDebugLineCheck(final Box hBox, final String flagName,\n                                     final JCheckBox cbFDebugFlag)\n  {\n    final JLabel lbFDebugFlag = new JLabel(flagName);\n    lbFDebugFlag.setLabelFor(cbFDebugFlag);\n    cbFDebugFlag.setEnabled(false);\n    hBox.add(cbFDebugFlag);\n    hBox.add(lbFDebugFlag);\n    hBox.add(Box.createHorizontalGlue());\n  }\n\n  private Box createShiftRegisters()\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(createShiftRegister(\"OSR Register\",\n                                 lbOsrLeftHandArrow, lbOsrRightHandArrow,\n                                 lbOsrBits, \"Pull\",\n                                 cbAutoPull));\n    hBox.add(Box.createHorizontalGlue());\n    hBox.add(createShiftRegister(\"ISR Register\",\n                                 lbIsrLeftHandArrow, lbIsrRightHandArrow,\n                                 lbIsrBits, \"Push\",\n                                 cbAutoPush));\n    hBox.add(Box.createHorizontalGlue());\n    return hBox;\n  }\n\n  private Box createShiftRegister(final String registerName,\n                                  final JLabel lbLeftHandArrow,\n                                  final JLabel lbRightHandArrow,\n                                  final JLabel[] lbBits,\n                                  final String actionName,\n                                  final JCheckBox cbAuto)\n  {\n    final Box vBox = new Box(BoxLayout.PAGE_AXIS);\n    vBox.setBorder(BorderFactory.createTitledBorder(registerName));\n    vBox.add(createShiftRegisterContents(lbLeftHandArrow, lbRightHandArrow,\n                                         lbBits));\n    vBox.add(createAutoInfo(actionName, cbAuto));\n    return vBox;\n  }\n\n  private Box createAutoInfo(final String actionName,\n                             final JCheckBox cbAuto)\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    final JLabel lbAuto = new JLabel(\"Auto \" + actionName);\n    lbAuto.setLabelFor(cbAuto);\n    cbAuto.setEnabled(false);\n    hBox.add(cbAuto);\n    hBox.add(lbAuto);\n    hBox.add(Box.createHorizontalGlue());\n    return hBox;\n  }\n\n  private Box createShiftRegisterContents(final JLabel lbLeftHandArrow,\n                                          final JLabel lbRightHandArrow,\n                                          final JLabel[] lbBits)\n  {\n    final Box hBox = new Box(BoxLayout.LINE_AXIS);\n    hBox.add(lbLeftHandArrow);\n    for (int index = 0; index < lbBits.length; index++) {\n      final JLabel lbBit = new JLabel();\n      lbBit.setFont(codeFont);\n      lbBits[index] = lbBit;\n      hBox.add(lbBit);\n    }\n    hBox.add(lbRightHandArrow);\n    unsetShiftReg(lbLeftHandArrow, lbRightHandArrow, lbBits);\n    return hBox;\n  }\n\n  private int getShiftCtrl() throws IOException\n  {\n    final int addressShiftCtrl =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    return sdk.readAddress(addressShiftCtrl);\n  }\n\n  private int getSMFReadPtr() throws IOException\n  {\n    final int addressFReadPtr =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.FREAD_PTR);\n    final int fReadPtr = sdk.readAddress(addressFReadPtr);\n    return (fReadPtr >>> (smNum << 3)) & 0xff;\n  }\n\n  private int getSMFLevel() throws IOException\n  {\n    final int addressFLevel =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FLEVEL);\n    final int fLevel = sdk.readAddress(addressFLevel);\n    return (fLevel >>> (smNum << 3)) & 0xff;\n  }\n\n  private int getSMFStat() throws IOException\n  {\n    final int addressFStat =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FSTAT);\n    final int fStat = sdk.readAddress(addressFStat);\n    return fStat >>> smNum;\n  }\n\n  private void updateFifoContents() throws IOException\n  {\n    final int addressFifo =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_FIFO_MEM0);\n    for (int entryNum = 0; entryNum < 2 * Constants.FIFO_DEPTH; entryNum++) {\n      buffer[entryNum] = sdk.readAddress(addressFifo + (entryNum << 2));\n    }\n  }\n\n  private void updateEntries(final int entryOffs, final int entryCount,\n                             final int readPtr, final int level,\n                             final ColorScheme colorScheme)\n  {\n    int entryPtr = autoScroll ? readPtr : entryOffs;\n    for (int displayedEntry = 0; displayedEntry < entryCount; displayedEntry++) {\n      final boolean isQueued =\n        autoScroll ?\n        displayedEntry < level :\n        level > 0 &&\n        (((entryCount + entryPtr - readPtr) & (entryCount - 1)) < level);\n      final boolean isReadPtr =\n        autoScroll ?\n        (readPtr >= 0) && (displayedEntry == 0) :\n        entryPtr == readPtr;\n      final Integer data = entryPtr >= 0 ? buffer[entryPtr] : null;\n      final JLabel lbEntry = lbEntries[entryOffs + displayedEntry];\n      lbEntry.setText(data != null ? String.format(\"%08x\", data) : \"  ????  \");\n      final StateColor stateColor =\n        isQueued ?\n        (isReadPtr ? StateColor.QUEUED_READ_PTR : StateColor.QUEUED) :\n        (isReadPtr ? StateColor.READ_PTR : StateColor.DEQUEUED);\n      lbEntry.setOpaque(isQueued);\n      lbEntry.setForeground(stateColor.getFgColor(colorScheme));\n      lbEntry.setBackground(stateColor.getBgColor(colorScheme));\n      if (entryPtr >= 0) {\n        entryPtr = ((entryPtr + 1) & (entryCount - 1)) + entryOffs;\n      }\n    }\n  }\n\n  private void updateEntries(final int shiftCtrl) throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    updateFifoContents();\n    final int smJoin =\n      (shiftCtrl >>> Constants.SM0_SHIFTCTRL_FJOIN_TX_LSB) & 0x3;\n    final boolean fJoinTX = (smJoin & 0x1) != 0x0;\n    final boolean fJoinRX = (smJoin & 0x2) != 0x0;\n    final int smfReadPtr = getSMFReadPtr();\n    final int txReadPtr = smfReadPtr & 0xf;\n    final int rxReadPtr = (smfReadPtr & 0xf0) >> 4;\n    final int smfLevel = getSMFLevel();\n    final int txLevel = smfLevel & 0xf;\n    final int rxLevel = (smfLevel >>> 4) & 0xf;\n    lbBottomLine.setText(fJoinTX || fJoinRX ?\n                         txtBottomJoined : txtBottomUnjoined);\n    lbSeparators[Constants.FIFO_DEPTH].setText(fJoinTX || fJoinRX ? \"  \" : \"||\");\n    if (fJoinTX) {\n      lbTopLine.setText(fJoinRX ? txtFJoinBoth : txtFJoinTX);\n    } else {\n      lbTopLine.setText(fJoinRX ? txtFJoinRX : txtFJoinNone);\n    }\n    if (fJoinTX) {\n      if (fJoinRX) {\n        updateEntries(0, 2 * Constants.FIFO_DEPTH,\n                      -1, 0, ColorScheme.RX);\n      } else {\n        updateEntries(0, 2 * Constants.FIFO_DEPTH,\n                      txReadPtr, txLevel, ColorScheme.TX);\n      }\n    } else {\n      if (fJoinRX) {\n        updateEntries(0, 2 * Constants.FIFO_DEPTH,\n                      rxReadPtr, rxLevel, ColorScheme.RX);\n      } else {\n        updateEntries(0, Constants.FIFO_DEPTH,\n                      txReadPtr, txLevel, ColorScheme.TX);\n        updateEntries(Constants.FIFO_DEPTH, Constants.FIFO_DEPTH,\n                      rxReadPtr, rxLevel, ColorScheme.RX);\n      }\n    }\n  }\n\n  private boolean getFDebug(final int smNum, final int fDebugValue,\n                            final int lsb, final int bits)\n  {\n    return (((fDebugValue & bits) >>> (lsb + smNum)) & 0x01) != 0x0;\n  }\n\n  private void updateFDebugStatus() throws IOException\n  {\n    final int addressFDebug =\n      PIORegisters.getAddress(pioNum, PIORegisters.Regs.FDEBUG);\n    final int fDebugValue = sdk.readAddress(addressFDebug);\n    cbFDebugTxStall.\n      setSelected(getFDebug(smNum, fDebugValue,\n                            Constants.FDEBUG_TXSTALL_LSB,\n                            Constants.FDEBUG_TXSTALL_BITS));\n    cbFDebugTxOver.\n      setSelected(getFDebug(smNum, fDebugValue,\n                            Constants.FDEBUG_TXOVER_LSB,\n                            Constants.FDEBUG_TXOVER_BITS));\n    cbFDebugRxUnder.\n      setSelected(getFDebug(smNum, fDebugValue,\n                            Constants.FDEBUG_RXUNDER_LSB,\n                            Constants.FDEBUG_RXUNDER_BITS));\n    cbFDebugRxStall.\n      setSelected(getFDebug(smNum, fDebugValue,\n                            Constants.FDEBUG_RXSTALL_LSB,\n                            Constants.FDEBUG_RXSTALL_BITS));\n  }\n\n  private void unsetShiftReg(final JLabel lbLeftHandArrow,\n                             final JLabel lbRightHandArrow,\n                             final JLabel[] lbBits)\n  {\n    lbLeftHandArrow.setText(\"?\");\n    lbRightHandArrow.setText(\"?\");\n    for (int index = 0; index < lbBits.length; index++) {\n      final JLabel lbBit = lbBits[index];\n      lbBit.setText(\"?\");\n      lbBit.setForeground(Color.LIGHT_GRAY);\n      lbBit.setBackground(Color.GRAY);\n      lbBit.setOpaque(false);\n    }\n  }\n\n  private void updateShiftReg(final BiFunction<Integer, Integer, Boolean>\n                              levelComparator,\n                              final Color activeColor,\n                              final JLabel lbLeftHandArrow,\n                              final JLabel lbRightHandArrow,\n                              final JLabel[] lbBits,\n                              final PIOEmuRegisters.Regs regShiftReg,\n                              final PIOEmuRegisters.Regs levelReg,\n                              final int threshold,\n                              final PIO.ShiftDir shiftDir)\n    throws IOException\n  {\n    final int addressShiftReg =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum, regShiftReg);\n    final int value = sdk.readAddress(addressShiftReg);\n    final int addressLevel =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum, levelReg);\n    final int level = sdk.readAddress(addressLevel);\n    final int labelNum = lbBits.length;\n    final int bitNum = labelNum - 1;\n    for (int bitIndex = 0; bitIndex < bitNum; bitIndex++) {\n      final int bitValue = (value >>> bitIndex) & 0x1;\n      final int bitIndexAfterShiftDir;\n      final int thresholdAfterShiftDir;\n      switch (shiftDir) {\n      case SHIFT_LEFT:\n        thresholdAfterShiftDir = threshold;\n        bitIndexAfterShiftDir = bitIndex;\n        break;\n      default:\n        thresholdAfterShiftDir = bitNum -threshold;\n        bitIndexAfterShiftDir = bitNum - bitIndex - 1;\n        break;\n      }\n      final JLabel lbBit =\n        lbBits[bitNum - bitIndex -\n               (bitIndex >= thresholdAfterShiftDir ? 1 : 0)];\n      lbBit.setText(bitValue == 0x1 ? \"1\" : \"0\");\n      if (levelComparator.apply(bitIndexAfterShiftDir, level)) {\n        lbBit.setForeground(Color.BLACK);\n        lbBit.setBackground(activeColor);\n        lbBit.setOpaque(true);\n      } else {\n        lbBit.setForeground(Color.LIGHT_GRAY);\n        lbBit.setBackground(Color.GRAY);\n        lbBit.setOpaque(false);\n      }\n    }\n    final JLabel lbThreshold;\n    switch (shiftDir) {\n    case SHIFT_LEFT:\n      lbThreshold = lbBits[labelNum - threshold - 1];\n      break;\n    default:\n      lbThreshold = lbBits[threshold];\n      break;\n    }\n    lbThreshold.setText(\"|\");\n    lbThreshold.setForeground(Color.BLACK);\n    lbThreshold.setBackground(Color.GRAY);\n    lbThreshold.setOpaque(false);\n    switch (shiftDir) {\n    case SHIFT_LEFT:\n      lbLeftHandArrow.setText(ARROW_LEFT);\n      lbRightHandArrow.setText(ARROW_LEFT);\n      break;\n    case SHIFT_RIGHT:\n      lbLeftHandArrow.setText(ARROW_RIGHT);\n      lbRightHandArrow.setText(ARROW_RIGHT);\n      break;\n    default:\n      lbLeftHandArrow.setText(\"?\");\n      lbRightHandArrow.setText(\"?\");\n      break;\n    }\n  }\n\n  private int getThreshold(final int shiftCtrl, final int bits, final int lsb)\n  {\n    final int thresholdValue = (shiftCtrl & bits) >>> lsb;\n    return thresholdValue == 0 ? 32 : thresholdValue;\n  }\n\n  private void updateShiftRegs(final int shiftCtrl) throws IOException\n  {\n    final int outShiftDir =\n      (shiftCtrl & Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS) >>>\n      Constants.SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB;\n    updateShiftReg((bit, level) -> bit >= level, Color.RED,\n                   lbOsrLeftHandArrow, lbOsrRightHandArrow, lbOsrBits,\n                   PIOEmuRegisters.Regs.SM0_OSR,\n                   PIOEmuRegisters.Regs.SM0_OSR_SHIFT_COUNT,\n                   getThreshold(shiftCtrl,\n                                Constants.SM0_SHIFTCTRL_PULL_THRESH_BITS,\n                                Constants.SM0_SHIFTCTRL_PULL_THRESH_LSB),\n                   PIO.ShiftDir.fromValue(outShiftDir));\n    cbAutoPull.setSelected((shiftCtrl &\n                            Constants.SM0_SHIFTCTRL_AUTOPULL_BITS) != 0x0);\n    final int inShiftDir =\n      (shiftCtrl & Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_BITS) >>>\n      Constants.SM0_SHIFTCTRL_IN_SHIFTDIR_LSB;\n    updateShiftReg((bit, level) -> bit < level, Color.GREEN,\n                   lbIsrLeftHandArrow, lbIsrRightHandArrow, lbIsrBits,\n                   PIOEmuRegisters.Regs.SM0_ISR,\n                   PIOEmuRegisters.Regs.SM0_ISR_SHIFT_COUNT,\n                   getThreshold(shiftCtrl,\n                                Constants.SM0_SHIFTCTRL_PUSH_THRESH_BITS,\n                                Constants.SM0_SHIFTCTRL_PUSH_THRESH_LSB),\n                   PIO.ShiftDir.fromValue(inShiftDir));\n    cbAutoPush.setSelected((shiftCtrl &\n                            Constants.SM0_SHIFTCTRL_AUTOPUSH_BITS) != 0x0);\n  }\n\n  private void checkedUpdate()\n  {\n    try {\n      final int shiftCtrl = getShiftCtrl();\n      updateEntries(shiftCtrl);\n      updateFDebugStatus();\n      updateShiftRegs(shiftCtrl);\n    } catch (final IOException e) {\n      for (int entryNum = 0; entryNum < Constants.SM_COUNT; entryNum++) {\n        buffer[entryNum] = null;\n      }\n      unsetShiftReg(lbOsrLeftHandArrow, lbOsrRightHandArrow, lbOsrBits);\n      unsetShiftReg(lbIsrLeftHandArrow, lbIsrRightHandArrow, lbIsrBits);\n    }\n  }\n\n  public void smChanged(final int pioNum, final int smNum)\n  {\n    this.pioNum = pioNum;\n    this.smNum = smNum;\n    final String toolTipText =\n      String.format(\"FIFO registers view for PIO%d, SM%d\", pioNum, smNum);\n    setToolTipText(toolTipText);\n    checkedUpdate();\n  }\n\n  public void setAutoScroll(final boolean autoScroll)\n  {\n    this.autoScroll = autoScroll;\n    checkedUpdate();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/fifo/FifoObserver.java",
    "content": "/*\n * @(#)FifoObserver.java 1.00 21/04/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.fifo;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.observer.GUIObserver;\n\n/**\n * Emulation FIFO Status Observation\n */\npublic class FifoObserver extends GUIObserver\n{\n  private static final long serialVersionUID = 5938495055310591092L;\n  private static final String APP_TITLE = \"FIFO Observer\";\n  private static final String APP_FULL_NAME =\n    \"Emulation FIFO Observer Version 0.1\";\n\n  private final FifoViewPanel fifoViewPanel;\n\n  private FifoObserver(final PrintStream console, final String[] argv)\n    throws IOException\n  {\n    super(APP_TITLE, APP_FULL_NAME, console, argv);\n    add(fifoViewPanel = new FifoViewPanel(console, getSDK(), APP_TITLE));\n    pack();\n    setVisible(true);\n    startUpdating();\n  }\n\n  @Override\n  protected void updateView()\n  {\n    fifoViewPanel.updateView();\n  }\n\n  public static void main(final String argv[])\n  {\n    final PrintStream console = System.out;\n    try {\n      new FifoObserver(console, argv);\n    } catch (final IOException e) {\n      console.printf(\"initialization failed: %s%n\", e.getMessage());\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/fifo/FifoViewPanel.java",
    "content": "/*\n * @(#)FifoViewPanel.java 1.00 21/04/28\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.fifo;\n\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JCheckBox;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class FifoViewPanel extends JPanel\n{\n  private static final long serialVersionUID = -2782955870967022886L;\n  private static final boolean initialAutoScroll = true;\n  private static final String autoScrollToolTipText =\n    \"automatically rotates display of FIFO cyclic buffer such that the \" +\n    \"next entry to be dequeued is displayed leftmost\";\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final FifoEntriesViewPanel fifoEntriesViewPanel;\n  private final JCheckBox cbAutoScroll;\n  private int pioNum;\n  private int smNum;\n\n  private FifoViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public FifoViewPanel(final PrintStream console, final SDK sdk,\n                       final String borderTitle)\n    throws IOException\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createTitledBorder(borderTitle));\n    fifoEntriesViewPanel = new FifoEntriesViewPanel(console, sdk,\n                                                    initialAutoScroll);\n\n    final Box pioSelection = new Box(BoxLayout.LINE_AXIS);\n    add(pioSelection);\n    final JLabel lbPio = new JLabel(\"PIO\");\n    pioSelection.add(lbPio);\n    pioSelection.add(Box.createHorizontalStrut(15));\n    addPioButtons(pioSelection);\n    pioSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(pioSelection);\n\n    final Box smSelection = new Box(BoxLayout.LINE_AXIS);\n    add(smSelection);\n    final JLabel lbSm = new JLabel(\"SM\");\n    lbSm.setMinimumSize(lbPio.getPreferredSize());\n    lbSm.setPreferredSize(lbPio.getPreferredSize());\n    smSelection.add(lbSm);\n    smSelection.add(Box.createHorizontalStrut(15));\n    addSmButtons(smSelection);\n    smSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(smSelection);\n\n    final Box scrollSelectionLine = new Box(BoxLayout.LINE_AXIS);\n    add(scrollSelectionLine);\n    cbAutoScroll = new JCheckBox(\"Auto-scroll to front entry\");\n    cbAutoScroll.setMnemonic('a');\n    cbAutoScroll.setToolTipText(autoScrollToolTipText);\n    cbAutoScroll.setSelected(initialAutoScroll);\n    cbAutoScroll.addActionListener((event) ->\n                                   fifoEntriesViewPanel.\n                                   setAutoScroll(cbAutoScroll.isSelected()));\n    scrollSelectionLine.add(cbAutoScroll);\n    scrollSelectionLine.add(Box.createHorizontalGlue());\n\n    final Box fifoEntriesViewBox = new Box(BoxLayout.LINE_AXIS);\n    add(fifoEntriesViewBox);\n    fifoEntriesViewBox.add(fifoEntriesViewPanel);\n    fifoEntriesViewBox.add(Box.createHorizontalGlue());\n    fifoEntriesViewPanel.smChanged(pioNum, smNum);\n  }\n\n  private void addPioButtons(final Box pioSelection)\n  {\n    final ButtonGroup bgPio = new ButtonGroup();\n    for (int pioNum = 0; pioNum < Constants.PIO_NUM; pioNum++) {\n      if (pioNum != 0) pioSelection.add(Box.createHorizontalStrut(10));\n      final JRadioButton rbPio = new JRadioButton(String.valueOf(pioNum));\n      rbPio.setSelected(pioNum == 0);\n      final int finalPioNum = pioNum;\n      rbPio.addActionListener((event) -> {\n          this.pioNum = finalPioNum;\n          fifoEntriesViewPanel.smChanged(finalPioNum, smNum);\n        });\n      bgPio.add(rbPio);\n      pioSelection.add(rbPio);\n    }\n  }\n\n  private void addSmButtons(final Box smSelection)\n  {\n    final ButtonGroup bgSm = new ButtonGroup();\n    for (int smNum = 0; smNum < Constants.SM_COUNT; smNum++) {\n      if (smNum != 0) smSelection.add(Box.createHorizontalStrut(10));\n      final JRadioButton rbSm = new JRadioButton(String.valueOf(smNum));\n      rbSm.setSelected(smNum == 0);\n      rbSm.setMnemonic(KeyEvent.VK_0 + smNum);\n      final int finalSmNum = smNum;\n      rbSm.addActionListener((event) -> {\n          this.smNum = finalSmNum;\n          fifoEntriesViewPanel.smChanged(pioNum, finalSmNum);\n        });\n      bgSm.add(rbSm);\n      smSelection.add(rbSm);\n    }\n  }\n\n  public void updateView()\n  {\n    fifoEntriesViewPanel.smChanged(pioNum, smNum);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/GPIOArrayPanel.java",
    "content": "/*\n * @(#)GPIOArrayPanel.java 1.00 21/04/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.border.EtchedBorder;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PinState;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class GPIOArrayPanel extends JPanel\n{\n  private static final long serialVersionUID = -2035403823264488596L;\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final GPIOPanel[] panels;\n  private GPIOSDK.Override override;\n\n  private GPIOArrayPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public GPIOArrayPanel(final PrintStream console, final SDK sdk)\n    throws IOException\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));\n\n    final Box overrideSelection = new Box(BoxLayout.LINE_AXIS);\n    add(overrideSelection);\n    final JLabel lbOverride = new JLabel(\"Override\");\n    overrideSelection.add(lbOverride);\n    overrideSelection.add(Box.createHorizontalStrut(15));\n    addOverrideButtons(overrideSelection);\n    overrideSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(overrideSelection);\n\n    final Box box = new Box(BoxLayout.LINE_AXIS);\n    add(box);\n    box.add(Box.createHorizontalStrut(15));\n    panels = new GPIOPanel[Constants.GPIO_NUM];\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final GPIOPanel panel = new GPIOPanel(console, sdk, gpioNum);\n      panels[gpioNum] = panel;\n      box.add(panel);\n      box.add(Box.createHorizontalStrut((gpioNum & 0x7) == 0x7 ? 15 : 5));\n    }\n    box.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(box);\n    add(Box.createVerticalStrut(5));\n    add(Box.createVerticalGlue());\n  }\n\n  private void addOverrideButtons(final Box overrideSelection)\n  {\n    final ButtonGroup bgOverride = new ButtonGroup();\n    final JRadioButton rbBefore = new JRadioButton(\"Before\");\n    rbBefore.addActionListener((event) -> overrideChanged(GPIOSDK.Override.BEFORE));\n    bgOverride.add(rbBefore);\n    overrideSelection.add(rbBefore);\n    overrideSelection.add(Box.createHorizontalStrut(10));\n    final JRadioButton rbAfter = new JRadioButton(\"After\");\n    rbAfter.setSelected(true);\n    rbAfter.addActionListener((event) -> overrideChanged(GPIOSDK.Override.AFTER));\n    bgOverride.add(rbAfter);\n    overrideSelection.add(rbAfter);\n    override = GPIOSDK.Override.AFTER;\n  }\n\n  public void updateStatus() throws IOException\n  {\n    final GPIOSDK gpioSdk = sdk.getGPIOSDK();\n    final PinState[] pinStates = gpioSdk.getPinStates(override);\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final PinState pinState = pinStates[gpioNum];\n      final GPIOPanel panel = panels[gpioNum];\n      panel.updateStatus(pinState.getDirection(), pinState.getLevel());\n    }\n  }\n\n  public void checkedUpdate()\n  {\n    try {\n      updateStatus();\n    } catch (final IOException e) {\n      for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n        panels[gpioNum].markAsUnknown();\n      }\n    }\n  }\n\n  private void overrideChanged(final GPIOSDK.Override override)\n  {\n    this.override = override;\n    checkedUpdate();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/GPIOObserver.java",
    "content": "/*\n * @(#)GPIOObserver.java 1.00 21/04/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.observer.GUIObserver;\n\n/**\n * Emulation GPIO Status Observation\n */\npublic class GPIOObserver extends GUIObserver\n{\n  private static final long serialVersionUID = -4777618004050203269L;\n  private static final String APP_TITLE = \"GPIO Observer\";\n  private static final String APP_FULL_NAME =\n    \"Emulation GPIO Observer Version 0.1\";\n\n  private final GPIOViewPanel gpioViewPanel;\n\n  public GPIOObserver(final PrintStream console, final String[] argv)\n    throws IOException\n  {\n    super(APP_TITLE, APP_FULL_NAME, console, argv);\n    add(gpioViewPanel = new GPIOViewPanel(console, getSDK()));\n    pack();\n    setVisible(true);\n    startUpdating();\n  }\n\n  @Override\n  protected void updateView()\n  {\n    gpioViewPanel.updateView();\n  }\n\n  public static void main(final String argv[])\n  {\n    final PrintStream console = System.out;\n    try {\n      new GPIOObserver(console, argv);\n    } catch (final IOException e) {\n      console.printf(\"initialization failed: %s%n\", e.getMessage());\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/GPIOPanel.java",
    "content": "/*\n * @(#)GPIOPanel.java 1.00 21/04/10\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.SwingUtilities;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Direction;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.sdk.GPIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class GPIOPanel extends JPanel\n{\n  private static final long serialVersionUID = 2863884535718464586L;\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final int gpioNum;\n  private final JLabel lbStatus;\n\n  private GPIOPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public GPIOPanel(final PrintStream console, final SDK sdk, final int gpioNum)\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    this.gpioNum = gpioNum;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    add(Box.createVerticalStrut(5));\n    final Box gpioNumBox = new Box(BoxLayout.LINE_AXIS);\n    gpioNumBox.add(Box.createHorizontalGlue());\n    gpioNumBox.add(new JLabel(String.format(\"%d\", gpioNum)));\n    gpioNumBox.add(Box.createHorizontalGlue());\n    add(gpioNumBox);\n    add(Box.createVerticalStrut(5));\n    final Box ledBox = new Box(BoxLayout.LINE_AXIS);\n    ledBox.add(Box.createHorizontalGlue());\n    ledBox.add(lbStatus = new JLabel(GPIOViewPanel.ledInLow));\n    ledBox.add(Box.createHorizontalGlue());\n    add(ledBox);\n    add(Box.createVerticalGlue());\n    setMaximumSize(getPreferredSize());\n  }\n\n  public void updateStatus(final Direction direction, final Bit level)\n  {\n    final ImageIcon icon =\n      direction == Direction.IN ?\n      (level == Bit.HIGH ? GPIOViewPanel.ledInHigh : GPIOViewPanel.ledInLow) :\n      (level == Bit.HIGH ? GPIOViewPanel.ledOutHigh : GPIOViewPanel.ledOutLow);\n    if (icon != lbStatus.getIcon()) {\n      lbStatus.setIcon(icon);\n    }\n  }\n\n  public void markAsUnknown()\n  {\n    final ImageIcon icon = GPIOViewPanel.ledUnknown;\n    if (icon != lbStatus.getIcon()) {\n      lbStatus.setIcon(icon);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/GPIOViewPanel.java",
    "content": "/*\n * @(#)GPIOViewPanel.java 1.00 21/05/17\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JPanel;\nimport org.soundpaint.rp2040pio.CollapsiblePanel;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class GPIOViewPanel extends JPanel\n{\n  private static final long serialVersionUID = -14152871236331492L;\n\n  public static final ImageIcon ledInLow;\n  public static final ImageIcon ledInHigh;\n  public static final ImageIcon ledOutLow;\n  public static final ImageIcon ledOutHigh;\n  public static final ImageIcon ledUnknown;\n\n  static {\n    try {\n      ledInLow = SwingUtils.createImageIcon(\"led-green-off16x16.png\", \"in: 0\");\n      ledInHigh = SwingUtils.createImageIcon(\"led-green-on16x16.png\", \"in: 1\");\n      ledOutLow = SwingUtils.createImageIcon(\"led-red-off16x16.png\", \"out: 0\");\n      ledOutHigh = SwingUtils.createImageIcon(\"led-red-on16x16.png\", \"out: 1\");\n      ledUnknown = SwingUtils.createImageIcon(\"led-gray16x16.png\", \"unknown\");\n    } catch (final IOException e) {\n      final String message =\n        String.format(\"failed loading icon: %s\", e.getMessage());\n      System.out.println(message);\n      throw new InternalError(message, e);\n    }\n  }\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final PIOGPIOArrayPanel pioGpioArrayPanel;\n  private final GPIOArrayPanel gpioArrayPanel;\n\n  private GPIOViewPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public GPIOViewPanel(final PrintStream console, final SDK sdk)\n    throws IOException\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n\n    pioGpioArrayPanel = new PIOGPIOArrayPanel(console, sdk);\n    SwingUtils.setPreferredHeightAsMaximum(pioGpioArrayPanel);\n    add(new CollapsiblePanel(pioGpioArrayPanel, \"PIO View of GPIO Pins\", true));\n\n    gpioArrayPanel = new GPIOArrayPanel(console, sdk);\n    SwingUtils.setPreferredHeightAsMaximum(gpioArrayPanel);\n    add(new CollapsiblePanel(gpioArrayPanel, \"GPIO View of GPIO Pins\", true));\n\n    add(Box.createVerticalGlue());\n  }\n\n  public void updateView()\n  {\n    pioGpioArrayPanel.checkedUpdate();\n    gpioArrayPanel.checkedUpdate();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/PIOGPIOArrayPanel.java",
    "content": "/*\n * @(#)PIOGPIOArrayPanel.java 1.00 21/05/16\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.BorderFactory;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ButtonGroup;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.JRadioButton;\nimport javax.swing.border.EtchedBorder;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.PinState;\nimport org.soundpaint.rp2040pio.SwingUtils;\nimport org.soundpaint.rp2040pio.sdk.PIOSDK;\nimport org.soundpaint.rp2040pio.sdk.SDK;\n\npublic class PIOGPIOArrayPanel extends JPanel\n{\n  private static final long serialVersionUID = 7074300168406892457L;\n\n  private final PrintStream console;\n  private final SDK sdk;\n  private final PIOGPIOPanel[] panels;\n  private int pioNum;\n\n  private PIOGPIOArrayPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PIOGPIOArrayPanel(final PrintStream console, final SDK sdk)\n    throws IOException\n  {\n    Objects.requireNonNull(console);\n    Objects.requireNonNull(sdk);\n    this.console = console;\n    this.sdk = sdk;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));\n\n    final Box pioSelection = new Box(BoxLayout.LINE_AXIS);\n    add(pioSelection);\n    final JLabel lbPio = new JLabel(\"PIO\");\n    pioSelection.add(lbPio);\n    pioSelection.add(Box.createHorizontalStrut(15));\n    addPioButtons(pioSelection);\n    pioSelection.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(pioSelection);\n\n    final Box box = new Box(BoxLayout.LINE_AXIS);\n    add(box);\n    box.add(Box.createHorizontalStrut(15));\n    panels = new PIOGPIOPanel[Constants.GPIO_NUM];\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final PIOGPIOPanel panel = new PIOGPIOPanel(console, gpioNum);\n      panels[gpioNum] = panel;\n      box.add(panel);\n      box.add(Box.createHorizontalStrut((gpioNum & 0x7) == 0x7 ? 15 : 5));\n    }\n    box.add(Box.createHorizontalGlue());\n    SwingUtils.setPreferredHeightAsMaximum(box);\n    add(Box.createVerticalStrut(5));\n    add(Box.createVerticalGlue());\n  }\n\n  private void addPioButtons(final Box pioSelection)\n  {\n    final ButtonGroup bgPio = new ButtonGroup();\n    for (int pioNum = 0; pioNum < Constants.PIO_NUM; pioNum++) {\n      if (pioNum != 0) pioSelection.add(Box.createHorizontalStrut(10));\n      final JRadioButton rbPio = new JRadioButton(String.valueOf(pioNum));\n      rbPio.setSelected(pioNum == 0);\n      final int finalPioNum = pioNum;\n      rbPio.addActionListener((event) -> pioChanged(finalPioNum));\n      bgPio.add(rbPio);\n      pioSelection.add(rbPio);\n    }\n  }\n\n  public void updateStatus() throws IOException\n  {\n    final PIOSDK pioSdk = pioNum == 0 ? sdk.getPIO0SDK() : sdk.getPIO1SDK();\n    final PinState[] pinStates = pioSdk.getPinStates();\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final PinState pinState = pinStates[gpioNum];\n      final PIOGPIOPanel panel = panels[gpioNum];\n      panel.updateStatus(pinState.getDirection(), pinState.getLevel());\n    }\n  }\n\n  public void checkedUpdate()\n  {\n    try {\n      updateStatus();\n    } catch (final IOException e) {\n      for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n        panels[gpioNum].markAsUnknown();\n      }\n    }\n  }\n\n  private void pioChanged(final int pioNum)\n  {\n    this.pioNum = pioNum;\n    checkedUpdate();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/observer/gpio/PIOGPIOPanel.java",
    "content": "/*\n * @(#)PIOGPIOPanel.java 1.00 21/05/16\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.observer.gpio;\n\nimport java.io.PrintStream;\nimport java.util.Objects;\nimport javax.swing.Box;\nimport javax.swing.BoxLayout;\nimport javax.swing.ImageIcon;\nimport javax.swing.JLabel;\nimport javax.swing.JPanel;\nimport javax.swing.SwingUtilities;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.Direction;\n\npublic class PIOGPIOPanel extends JPanel\n{\n  private static final long serialVersionUID = -4710787733361262765L;\n\n  private final PrintStream console;\n  private final int gpioNum;\n  private final JLabel lbStatus;\n\n  private PIOGPIOPanel()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PIOGPIOPanel(final PrintStream console, final int gpioNum)\n  {\n    Objects.requireNonNull(console);\n    this.console = console;\n    this.gpioNum = gpioNum;\n    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));\n    add(Box.createVerticalStrut(5));\n    final Box gpioNumBox = new Box(BoxLayout.LINE_AXIS);\n    gpioNumBox.add(Box.createHorizontalGlue());\n    gpioNumBox.add(new JLabel(String.format(\"%d\", gpioNum)));\n    gpioNumBox.add(Box.createHorizontalGlue());\n    add(gpioNumBox);\n    add(Box.createVerticalStrut(5));\n    final Box ledBox = new Box(BoxLayout.LINE_AXIS);\n    ledBox.add(Box.createHorizontalGlue());\n    ledBox.add(lbStatus = new JLabel(GPIOViewPanel.ledInLow));\n    ledBox.add(Box.createHorizontalGlue());\n    add(ledBox);\n    add(Box.createVerticalGlue());\n    setMaximumSize(getPreferredSize());\n  }\n\n  public void updateStatus(final Direction direction, final Bit level)\n  {\n    final ImageIcon icon =\n      direction == Direction.IN ?\n      (level == Bit.HIGH ? GPIOViewPanel.ledInHigh : GPIOViewPanel.ledInLow) :\n      (level == Bit.HIGH ? GPIOViewPanel.ledOutHigh : GPIOViewPanel.ledOutLow);\n    if (icon != lbStatus.getIcon()) {\n      lbStatus.setIcon(icon);\n    }\n  }\n\n  public void markAsUnknown()\n  {\n    final ImageIcon icon = GPIOViewPanel.ledUnknown;\n    if (icon != lbStatus.getIcon()) {\n      lbStatus.setIcon(icon);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/GPIOSDK.java",
    "content": "/*\n * @(#)GPIOSDK.java 1.00 21/03/02\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport java.io.IOException;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.Direction;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.GPIOPadsBank0Registers;\nimport org.soundpaint.rp2040pio.PinState;\n\n/**\n * Minimal subset of GPIO SDK Interface, just enough to provide all\n * required GPIO-related functionality for PIO SDK.\n */\npublic class GPIOSDK implements Constants\n{\n  public enum Override\n  {\n    BEFORE, AFTER\n  }\n\n  private final AddressSpace memory;\n\n  public GPIOSDK(final AddressSpace memory)\n  {\n    if (memory == null) {\n      throw new NullPointerException(\"memory\");\n    }\n    this.memory = memory;\n  }\n\n  public void setFunction(final int gpioNum, final GPIO_Function fn)\n    throws IOException\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n\n    final int padsGpioAddress =\n      GPIOPadsBank0Registers.getGPIOAddress(gpioNum);\n    final int padsValues = Bit.LOW.getValue() << PADS_BANK0_GPIO0_IE_LSB;\n    final int padsWriteMask = PADS_BANK0_GPIO0_IE_BITS;\n    memory.hwWriteMasked(padsGpioAddress, padsValues, padsWriteMask);\n\n    final GPIOIOBank0Registers.Regs ioBank0Reg =\n      GPIOIOBank0Registers.Regs.GPIO0_CTRL;\n    final int ioGpioAddress =\n      GPIOIOBank0Registers.getGPIOAddress(gpioNum, ioBank0Reg);\n    final int ioValues = fn.getValue() << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB;\n    final int ioWriteMask = IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS;\n    memory.hwWriteMasked(ioGpioAddress, ioValues, ioWriteMask);\n  }\n\n  private static Direction getDirectionFromStatus(final int statusValue,\n                                                  final Override override)\n  {\n    if (override == null) {\n      throw new NullPointerException(\"override\");\n    }\n    final int gpioOe = override == Override.BEFORE ?\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_OEFROMPERI_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB :\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_OETOPAD_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_OETOPAD_LSB;\n    return Direction.fromValue(gpioOe);\n  }\n\n  private static Bit getOutputLevelFromStatus(final int statusValue,\n                                              final Override override)\n  {\n    if (override == null) {\n      throw new NullPointerException(\"override\");\n    }\n    final int gpioOut = override == Override.BEFORE ?\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_OUTFROMPERI_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_OUTFROMPERI_LSB :\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_OUTTOPAD_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_OUTTOPAD_LSB;\n    return Bit.fromValue(gpioOut);\n  }\n\n  private static Bit getInputLevelFromStatus(final int statusValue,\n                                             final Override override)\n  {\n    if (override == null) {\n      throw new NullPointerException(\"override\");\n    }\n    final int gpioIn = override == Override.BEFORE ?\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB :\n      (statusValue & Constants.IO_BANK0_GPIO0_STATUS_INTOPERI_BITS) >>>\n      Constants.IO_BANK0_GPIO0_STATUS_INTOPERI_LSB;\n    return Bit.fromValue(gpioIn);\n  }\n\n  public Bit getInputLevel(final int gpioNum, final Override override)\n    throws IOException\n  {\n    Constants.checkGpioPin(gpioNum, \"GPIO pin number\");\n    final int gpioStatusAddress =\n      GPIOIOBank0Registers.\n      getGPIOAddress(gpioNum, GPIOIOBank0Registers.Regs.GPIO0_STATUS);\n    final int gpioStatusValue = memory.readAddress(gpioStatusAddress);\n    return getInputLevelFromStatus(gpioStatusValue, override);\n  }\n\n  public PinState[] getPinStates(final Override override) throws IOException\n  {\n    final PinState[] pinStates = new PinState[Constants.GPIO_NUM];\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final int gpioStatusAddress =\n        GPIOIOBank0Registers.\n        getGPIOAddress(gpioNum, GPIOIOBank0Registers.Regs.GPIO0_STATUS);\n      final int gpioStatusValue = memory.readAddress(gpioStatusAddress);\n      final int gpioOeFromPeri =\n        (gpioStatusValue & Constants.IO_BANK0_GPIO0_STATUS_OEFROMPERI_BITS) >>>\n        Constants.IO_BANK0_GPIO0_STATUS_OEFROMPERI_LSB;\n      final Direction direction = Direction.fromValue(gpioOeFromPeri);\n      final Bit level;\n      if (direction == Direction.OUT) {\n        level = getOutputLevelFromStatus(gpioStatusValue, override);\n      } else {\n        level = getInputLevelFromStatus(gpioStatusValue, override);\n      }\n      pinStates[gpioNum] = PinState.fromValues(direction, level);\n    }\n    return pinStates;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/PIOSDK.java",
    "content": "/*\n * @(#)PIOSDK.java 1.00 21/02/25\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Bit;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Decoder;\nimport org.soundpaint.rp2040pio.Direction;\nimport org.soundpaint.rp2040pio.GPIOIOBank0Registers;\nimport org.soundpaint.rp2040pio.Instruction;\nimport org.soundpaint.rp2040pio.PinState;\nimport org.soundpaint.rp2040pio.PIOEmuRegisters;\nimport org.soundpaint.rp2040pio.PIORegisters;\n\n/**\n * PIO SDK Interface\n */\npublic class PIOSDK implements Constants\n{\n  private static final Decoder decoder = new Decoder();\n\n  private final int pioNum;\n  private final AddressSpace memory;\n  private final GPIOSDK gpioSdk;\n\n  private PIOSDK()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public PIOSDK(final int pioNum, final AddressSpace memory,\n                final GPIOSDK gpioSdk)\n  {\n    Constants.checkPioNum(pioNum, \"PIO index number\");\n    if (memory == null) {\n      throw new NullPointerException(\"memory\");\n    }\n    if (gpioSdk == null) {\n      throw new NullPointerException(\"gpio sdk\");\n    }\n    this.pioNum = pioNum;\n    this.memory = memory;\n    this.gpioSdk = gpioSdk;\n  }\n\n  /**\n   * Holds a copy of all info of a specific Instruction during a\n   * specific cycle that is relevant for the timing diagram.\n   */\n  public static class InstructionInfo\n  {\n    private final int origin;\n    private final String mnemonic;\n    private final String fullStatement;\n    private final boolean isDelayCycle;\n    private final int delay;\n\n    private InstructionInfo()\n    {\n      throw new UnsupportedOperationException(\"unsupported empty constructor\");\n    }\n\n    /**\n     * @param origin Either memory address (0…31), or\n     * INSTR_ORIGIN_FORCED for an enforced instruction, or\n     * INSTR_ORIGIN_EXECD for an EXEC'd instruction, or\n     * INSTR_ORIGIN_UNKNOWN if the origin is not available.\n     */\n    public InstructionInfo(final int origin,\n                           final String mnemonic, final String fullStatement,\n                           final boolean isDelayCycle, final int delay)\n    {\n      // instruction & state machine will change, hence save snapshot\n      // of relevant info\n      if (origin < INSTR_ORIGIN_UNKNOWN) {\n        final String message =\n          String.format(\"origin < %d: %d\", INSTR_ORIGIN_UNKNOWN, origin);\n        throw new IllegalArgumentException(message);\n      }\n      if (origin >= MEMORY_SIZE) {\n        final String message =\n          String.format(\"origin >= %d: %d\", MEMORY_SIZE, origin);\n        throw new IllegalArgumentException(message);\n      }\n      this.origin = origin;\n      this.mnemonic = mnemonic;\n      this.fullStatement = fullStatement;\n      this.isDelayCycle = isDelayCycle;\n      this.delay = delay;\n    }\n\n    public InstructionInfo(final Exception e)\n    {\n      this.origin = INSTR_ORIGIN_UNKNOWN;\n      this.mnemonic = \"err\";\n      this.fullStatement = e.getMessage();\n      this.isDelayCycle = false;\n      this.delay = 0;\n    }\n\n    public int getOrigin() { return origin; }\n\n    public String getMnemnonic() { return mnemonic; }\n\n    public String getFullStatement() { return fullStatement; }\n\n    public boolean isDelayCycle() { return isDelayCycle; }\n\n    public int getDelay() { return delay; }\n\n    @Override\n    public boolean equals(final Object obj)\n    {\n      if (!(obj instanceof InstructionInfo)) return false;\n      final InstructionInfo other = (InstructionInfo)obj;\n      if (isDelayCycle && other.isDelayCycle) return true;\n      return this == other;\n    }\n\n    @Override\n    public int hashCode()\n    {\n      return isDelayCycle ? 0 : super.hashCode();\n    }\n\n    public String getToolTipText()\n    {\n      return isDelayCycle ? \"[delay]\" : fullStatement;\n    }\n\n    @Override\n    public String toString()\n    {\n      return isDelayCycle ? \"[\" + delay + \"]\" : mnemonic;\n    }\n  }\n\n  public InstructionInfo\n    getInstructionFromOpCode(final int smNum, final int origin,\n                             final String addressLabel, final int opCode,\n                             final boolean format,\n                             final boolean isDelayCycle, final int delay)\n      throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int smPinCtrlSidesetCountAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    final int pinCtrlSidesetCount =\n      (memory.readAddress(smPinCtrlSidesetCountAddress) &\n       SM0_PINCTRL_SIDESET_COUNT_BITS) >>> SM0_PINCTRL_SIDESET_COUNT_LSB;\n\n    final int smExecCtrlSideEnAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final boolean execCtrlSideEn =\n      (memory.readAddress(smExecCtrlSideEnAddress) &\n       SM0_EXECCTRL_SIDE_EN_BITS) != 0x0;\n    return getInstructionFromOpCode(pinCtrlSidesetCount, execCtrlSideEn,\n                                    origin, addressLabel, opCode,\n                                    format, isDelayCycle, delay);\n  }\n\n  /**\n   * Note: This method is synchronized since we have only a single\n   * instance of a decoder, with a single instance of each\n   * instruction.  Therefore, use of this decoder and access to\n   * instances of Instruction objects must be serialized.\n   */\n  public static synchronized InstructionInfo\n    getInstructionFromOpCode(final int pinCtrlSidesetCount,\n                             final boolean execCtrlSideEn,\n                             final int origin,\n                             final String addressLabel, final int opCode,\n                             final boolean format,\n                             final boolean isDelayCycle, final int delay)\n  {\n    /*final*/ Instruction instruction;\n    try {\n      instruction =\n        decoder.decode((short)opCode, pinCtrlSidesetCount, execCtrlSideEn);\n    } catch (final Decoder.DecodeException e) {\n      instruction = null;\n    }\n    final String mnemonic;\n    final String fullStatement;\n    if (instruction != null) {\n      mnemonic = instruction.getMnemonic();\n      fullStatement = addressLabel + instruction.toString();\n    } else {\n      mnemonic = \"???\";\n      fullStatement = addressLabel + \"???\";\n    }\n    final String formattedFullStatement =\n      format ? fullStatement : fullStatement.replaceAll(\"\\\\s{2,}\", \" \");\n\n    return new InstructionInfo(origin, mnemonic, formattedFullStatement,\n                               isDelayCycle, delay);\n  }\n\n  public static int decodeInstrOrigin(final int encoded)\n  {\n    final int originMode = (encoded >>> 5) & 0x3;\n    return\n      originMode == INSTR_ORIGIN_MEMORY ?\n      encoded & (MEMORY_SIZE - 1) :\n      ~((~originMode) & 0x3);\n  }\n\n  private int getInstrOrigin(final int smNum) throws IOException\n  {\n    final int instrOriginAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_INSTR_ORIGIN);\n    final int instrOrigin = memory.readAddress(instrOriginAddress);\n    return decodeInstrOrigin(instrOrigin);\n  }\n\n  public static String renderOrigin(final int origin)\n  {\n    if (origin >= 0) {\n      return String.format(\"%02x\", origin);\n    }\n    switch (origin) {\n    case INSTR_ORIGIN_UNKNOWN:\n      return \"??\";\n    case INSTR_ORIGIN_FORCED:\n      return \"[forced]\";\n    case INSTR_ORIGIN_EXECD:\n      return \"[EXEC'd]\";\n    default:\n      throw new InternalError(\"unexpected case fall-through\");\n    }\n  }\n\n  public InstructionInfo getCurrentInstruction(final int smNum,\n                                               final boolean showOrigin,\n                                               final boolean format)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int smInstrAddress =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_INSTR);\n    final int opCode = memory.readAddress(smInstrAddress) & 0xffff;\n    final int origin = getInstrOrigin(smNum);\n    final String addressLabel =\n      showOrigin ? renderOrigin(origin) + \": \" : \"\";\n\n    final int smDelayCycleAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_DELAY_CYCLE);\n    final boolean isDelayCycle = memory.readAddress(smDelayCycleAddress) != 0x0;\n\n    final int smDelayAddress =\n      PIOEmuRegisters.getSMAddress(pioNum, smNum,\n                                   PIOEmuRegisters.Regs.SM0_DELAY);\n    final int delay = memory.readAddress(smDelayAddress);\n\n    return getInstructionFromOpCode(smNum, origin, addressLabel, opCode, format,\n                                    isDelayCycle, delay);\n  }\n\n  /**\n   * @param smNum Index of state machine.  For decoding an\n   * instruction, it is essential to know for which state machine the\n   * instruction applies, since the state machine's configuration of\n   * the side set / delay settings has an impact on interpretation of\n   * the instruction even for disassembling.\n   */\n  public InstructionInfo getMemoryInstruction(final int smNum,\n                                              final int address,\n                                              final boolean showAddress,\n                                              final boolean format)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkSmMemAddr(address, \"memory address\");\n    final int instrAddress = PIOEmuRegisters.getMemoryAddress(pioNum, address);\n    final int opCode = memory.readAddress(instrAddress) & 0xffff;\n    final String formattedOpCode = String.format(\"%04x \", opCode);\n    final String addressLabel =\n      (showAddress ? String.format(\"%02x: \", address) : \"\") + formattedOpCode;\n    final boolean isDelayCycle = false;\n    final int delay = 0;\n    return\n      getInstructionFromOpCode(smNum, address, addressLabel, opCode, format,\n                               isDelayCycle, delay);\n  }\n\n  // ---- Functions for compatibility with the Pico SDK, SM Config Group ----\n\n  public static SMConfig getDefaultSmConfig()\n  {\n    return SMConfig.getDefault();\n  }\n\n  public void smSetOutPins(final int smNum,\n                           final int outBase, final int outCount)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkGpioPin(outBase, \"GPIO out base\");\n    Constants.checkGpioPinsCount(outCount, \"GPIO out count\");\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    memory.hwWriteMasked(address,\n                         (outCount << SM0_PINCTRL_OUT_COUNT_LSB) |\n                         (outBase << SM0_PINCTRL_OUT_BASE_LSB),\n                         SM0_PINCTRL_OUT_COUNT_BITS &\n                         SM0_PINCTRL_OUT_BASE_BITS);\n  }\n\n  public void smSetSetPins(final int smNum,\n                           final int setBase, final int setCount)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkGpioPin(setBase, \"GPIO set base\");\n    if (setCount < 0) {\n      throw new IllegalArgumentException(\"setCount < 0: \" + setCount);\n    }\n    if (setCount > 5) {\n      throw new IllegalArgumentException(\"setCount > 5: \" + setCount);\n    }\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    memory.hwWriteMasked(address,\n                         (setCount << SM0_PINCTRL_SET_COUNT_LSB) |\n                         (setBase << SM0_PINCTRL_SET_BASE_LSB),\n                         SM0_PINCTRL_SET_COUNT_BITS &\n                         SM0_PINCTRL_SET_BASE_BITS);\n  }\n\n  public void smSetInPins(final int smNum, final int inBase) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkGpioPin(inBase, \"GPIO in base\");\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    memory.hwWriteMasked(address, inBase << SM0_PINCTRL_IN_BASE_LSB,\n                         SM0_PINCTRL_IN_BASE_BITS);\n  }\n\n  public void smSetSideSetPins(final int smNum, final int sideSetBase)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkGpioPin(sideSetBase, \"GPIO side set base\");\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    memory.hwWriteMasked(address, sideSetBase << SM0_PINCTRL_SIDESET_BASE_LSB,\n                         SM0_PINCTRL_SIDESET_BASE_BITS);\n  }\n\n  // ---- Functions for compatibility with the Pico SDK, PIO Group ----\n\n  /**\n   * Tracking allocation of instruction memory is not a feature of the\n   * RP2040 itself, but a feature of the SDK.  This is, why we do not\n   * put this stuff into the memory class.\n   */\n  private Integer memoryAllocation = 0x0;\n\n  /**\n   * Tracking claim of state machines is not a feature of the RP2040\n   * itself, but a feature of the SDK.  This is, why we do not put\n   * this stuff into the state machine class.\n   */\n  private Integer stateMachineClaimed = 0x0;\n\n  public void reset()\n  {\n    memoryAllocation = 0;\n    stateMachineClaimed = 0;\n  }\n\n  public void smSetConfig(final int smNum, final SMConfig smConfig)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    if (smConfig == null) {\n      throw new NullPointerException(\"smConfig\");\n    }\n    synchronized(memory) {\n      final int smClkDivAddr =\n        PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV);\n      memory.writeAddress(smClkDivAddr, smConfig.getClkDiv());\n      final int smExecCtrlAddr =\n        PIORegisters.getSMAddress(pioNum, smNum,\n                                  PIORegisters.Regs.SM0_EXECCTRL);\n      memory.writeAddress(smExecCtrlAddr, smConfig.getExecCtrl());\n      final int smShiftCtrlAddr =\n        PIORegisters.getSMAddress(pioNum, smNum,\n                                  PIORegisters.Regs.SM0_SHIFTCTRL);\n      memory.writeAddress(smShiftCtrlAddr, smConfig.getShiftCtrl());\n      final int smPinCtrlAddr =\n        PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n      memory.writeAddress(smPinCtrlAddr, smConfig.getPinCtrl());\n    }\n  }\n\n  public int getIndex()\n  {\n    return pioNum;\n  }\n\n  public void gpioInit(final int pin) throws IOException\n  {\n    Constants.checkGpioPin(pin, \"GPIO pin number\");\n    final GPIO_Function function =\n      getIndex() == 0 ? GPIO_Function.PIO0 : GPIO_Function.PIO1;\n    gpioSdk.setFunction(pin, function);\n  }\n\n  /**\n   * Given one of the 8 DMA channels (RX and TX for each state\n   * machine) between DMA and this PIO, return the corresponding DREQ\n   * number, as specified in Table 120, Sect. 2.5 (\"DMA\") of the\n   * RP2040 data sheet.\n   */\n  public int getDREQ(final int smNum, final boolean isTX)\n  {\n    Constants.checkSmNum(smNum);\n    return (getIndex() << 3) | (isTX ? 0 : SM_COUNT) | smNum;\n  }\n\n  /**\n   * Tries to allocate memory for the specified allocation mask and\n   * origin.  Returns address (0…31) where the allocation is\n   * performed.\n   * @param allocationMask Bit mask of instruction addresses (0…31) to\n   * allocate.\n   * @param origin Address where to allocate, or -1, if any address is\n   * acceptable.\n   * @param checkOnly If true, allocation is only checked for, but not\n   * performed.  Also, if allocation is not possible, -1 is returned\n   * rather than throwing an exception.\n   */\n  private int allocateMemory(final int allocationMask, final int origin,\n                             final boolean checkOnly)\n  {\n    synchronized(memoryAllocation) {\n      if (origin >= 0) {\n        if ((memoryAllocation & allocationMask) == 0x0) {\n          if (!checkOnly) memoryAllocation |= allocationMask;\n          return origin;\n        }\n        if (checkOnly) return -1;\n        final String message =\n          String.format(\"allocation at %02x failed\", origin);\n        throw new Panic(message);\n      }\n      for (int offset = 0; offset < MEMORY_SIZE; offset++) {\n        final int allocationMaskForOffset =\n          (allocationMask << offset) |\n          (allocationMask << (offset - MEMORY_SIZE));\n        if ((memoryAllocation & allocationMaskForOffset) == 0x0) {\n          if (!checkOnly) memoryAllocation |= allocationMaskForOffset;\n          return offset;\n        }\n      }\n    }\n    if (checkOnly) return -1;\n    final String message =\n      String.format(\"allocation at %02x failed\", origin);\n    throw new Panic(message);\n  }\n\n  public int getMemoryAllocation() { return memoryAllocation; }\n\n  public boolean canAddProgram(final Program program)\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    final int allocationMask = program.getAllocationMask();\n    final int origin = program.getOrigin();\n    return allocateMemory(allocationMask, origin, true) >= 0;\n  }\n\n  public boolean canAddProgramAtOffset(final Program program, final int offset)\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    Constants.checkSmMemAddr(offset, \"offset\");\n    final int origin = program.getOrigin();\n    if (origin >= 0) {\n      // do not allocate program with fixed origin at different offset\n      if (origin != offset) return false;\n    }\n    final int allocationMask = program.getAllocationMask();\n    final int allocationMaskForOffset =\n      origin >= 0 ?\n      allocationMask :\n      (allocationMask << offset) | (allocationMask << (offset - MEMORY_SIZE));\n    return allocateMemory(allocationMaskForOffset, offset, true) >= 0;\n  }\n\n  private void writeProgram(final Program program, final int addressOffset)\n    throws IOException\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    Constants.checkSmMemAddr(addressOffset, \"address offset\");\n    final int length = program.getLength();\n    synchronized(memory) {\n      for (int index = 0; index < length; index++) {\n        final short instruction = program.getInstruction(index);\n        final int memoryAddress = (addressOffset + index) & 0x1f;\n        // TODO: FIXME: Code relocation: When (addressOffset != 0),\n        // JMP commands need their absolute target address to be\n        // adjusted according to the offset.\n        memory.writeAddress(PIORegisters.\n                            getMemoryAddress(pioNum, memoryAddress),\n                            instruction);\n      }\n    }\n  }\n\n  public int addProgram(final String resourceId, final BufferedReader reader)\n    throws IOException\n  {\n    return addProgram(ProgramParser.parse(resourceId, reader));\n  }\n\n  public int addProgram(final Program program) throws IOException\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    final int allocationMask = program.getAllocationMask();\n    final int origin = program.getOrigin();\n    final int address = allocateMemory(allocationMask, origin, false);\n    writeProgram(program, address);\n    return address;\n  }\n\n  public int addProgramAtOffset(final String resourceId,\n                                final BufferedReader reader, final int offset)\n    throws IOException\n  {\n    return addProgramAtOffset(ProgramParser.parse(resourceId, reader), offset);\n  }\n\n  public int addProgramAtOffset(final Program program, final int offset)\n    throws IOException\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    Constants.checkSmMemAddr(offset, \"offset\");\n    final int origin = program.getOrigin();\n    if (origin >= 0) {\n      // do not allocate program with fixed origin at different offset\n      if (origin != offset) {\n        final String message =\n          String.format(\"allocation at %02x failed for program %s: \" +\n                        \"conflicting origin: %02x\",\n                        offset, program, origin);\n        throw new Panic(message);\n      }\n    }\n    final int allocationMask = program.getAllocationMask();\n    final int allocationMaskForOffset =\n      origin >= 0 ?\n      allocationMask :\n      (allocationMask << offset) |\n      (allocationMask << (offset - MEMORY_SIZE));\n    final int address = allocateMemory(allocationMaskForOffset, offset, false);\n    writeProgram(program, address);\n    return address;\n  }\n\n  public void removeProgram(final String resourceId,\n                            final BufferedReader reader, final int loadedOffset)\n    throws IOException\n  {\n    removeProgram(ProgramParser.parse(resourceId, reader), loadedOffset);\n  }\n\n  public void removeProgram(final Program program, final int loadedOffset)\n    throws IOException\n  {\n    if (program == null) {\n      throw new NullPointerException(\"program\");\n    }\n    Constants.checkSmMemAddr(loadedOffset, \"loaded offset\");\n    final int origin = program.getOrigin();\n    if (origin >= 0) {\n      // can not remove program from offset it is not designed for\n      if (origin != loadedOffset) {\n        final String message =\n          String.format(\"can not remove program %s from offset %02x: \" +\n                        \"program has conflicting origin: %02x\",\n                        program, loadedOffset, origin);\n        throw new Panic(message);\n      }\n    }\n    final int allocationMask = program.getAllocationMask();\n    final int allocationMaskForOffset =\n      origin >= 0 ?\n      allocationMask :\n      (allocationMask << loadedOffset) |\n      (allocationMask << (loadedOffset - MEMORY_SIZE));\n    synchronized(memoryAllocation) {\n      if ((memoryAllocation & allocationMaskForOffset) !=\n          allocationMaskForOffset) {\n        final String message =\n          String.format(\"deallocation at %02x failed for program %s: \" +\n                        \"allocation bits corrupted\",\n                        loadedOffset, program);\n        throw new Panic(message);\n      }\n      memoryAllocation &= ~allocationMaskForOffset;\n      synchronized(memory) {\n        for (int index = 0; index < program.getLength(); index++) {\n          final int memoryAddress = (loadedOffset + index) & 0x1f;\n          memory.writeAddress(PIORegisters.getMemoryAddress(pioNum,\n                                                            memoryAddress),\n                              0);\n        }\n      }\n    }\n  }\n\n  public void clearInstructionMemory() throws IOException\n  {\n    synchronized(memoryAllocation) {\n      memoryAllocation = 0;\n      synchronized(memory) {\n        for (int memoryAddress = 0; memoryAddress < MEMORY_SIZE;\n             memoryAddress++) {\n          memory.writeAddress(PIORegisters.getMemoryAddress(pioNum,\n                                                            memoryAddress),\n                              0);\n        }\n      }\n    }\n  }\n\n  public void smInit(final int smNum, final int initialPC,\n                     final SMConfig config)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    smSetEnabled(smNum, false);\n    smSetConfig(smNum, config != null ? config : getDefaultSmConfig());\n    smClearFIFOs(smNum);\n    final int fDebug =\n      ((0x1 << FDEBUG_TXSTALL_LSB) |\n       (0x1 << FDEBUG_TXOVER_LSB) |\n       (0x1 << FDEBUG_RXUNDER_LSB) |\n       (0x1 << FDEBUG_RXSTALL_LSB)) << smNum;\n    memory.writeAddress(PIORegisters.getAddress(pioNum,\n                                                PIORegisters.Regs.FDEBUG),\n                        fDebug);\n    smRestart(smNum);\n    smClkDivRestart(smNum);\n    final int jmpInstruction =\n      initialPC & 0x001f; // no sideset/delay => all other bits are 0\n    smExec(smNum, (short)jmpInstruction);\n  }\n\n  public boolean smGetEnabled(final int smNum)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    final int ctrl = memory.readAddress(address);\n    return (ctrl & (0x1 << smNum)) != 0x0;\n  }\n\n  public void smSetEnabled(final int smNum, final boolean enabled)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    setSmMaskEnabled(0x1 << smNum, enabled);\n  }\n\n  public void setSmMaskEnabled(final int mask, final boolean enabled)\n    throws IOException\n  {\n    final int address = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    memory.hwWriteMasked(address, enabled ? mask : 0, mask);\n  }\n\n  public void smRestart(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    restartSmMask(0x1 << smNum);\n  }\n\n  public void restartSmMask(final int mask) throws IOException\n  {\n    if (mask < 0) {\n      throw new IllegalArgumentException(\"mask < 0: \" + mask);\n    }\n    if (mask > (0x1 << SM_COUNT) - 1) {\n      throw new IllegalArgumentException(\"mask > \" +\n                                         ((0x1 << SM_COUNT) - 1) + \": \" +\n                                         mask);\n    }\n    final int address = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    memory.hwSetBits(address,\n                     (mask << CTRL_SM_RESTART_LSB) & CTRL_SM_RESTART_BITS);\n  }\n\n  public void smClkDivRestart(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    clkDivRestartSmMask(0x1 << smNum);\n  }\n\n  public void clkDivRestartSmMask(final int mask) throws IOException\n  {\n    if (mask < 0) {\n      throw new IllegalArgumentException(\"mask < 0: \" + mask);\n    }\n    if (mask > (0x1 << SM_COUNT) - 1) {\n      throw new IllegalArgumentException(\"mask > \" +\n                                         ((0x1 << SM_COUNT) - 1) + \": \" +\n                                         mask);\n    }\n    final int address = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    memory.hwWriteMasked(address, mask << CTRL_CLKDIV_RESTART_LSB,\n                         CTRL_CLKDIV_RESTART_BITS);\n  }\n\n  public void enableSmMaskInSync(final int mask) throws IOException\n  {\n    if (mask < 0) {\n      throw new IllegalArgumentException(\"mask < 0: \" + mask);\n    }\n    if (mask > (0x1 << SM_COUNT) - 1) {\n      throw new IllegalArgumentException(\"mask > \" +\n                                         ((0x1 << SM_COUNT) - 1) + \": \" +\n                                         mask);\n    }\n    final int address = PIORegisters.getAddress(pioNum, PIORegisters.Regs.CTRL);\n    memory.hwWriteMasked(address,\n                         (mask << CTRL_CLKDIV_RESTART_LSB) |\n                         (mask << CTRL_SM_ENABLE_LSB),\n                         CTRL_CLKDIV_RESTART_BITS | CTRL_SM_ENABLE_BITS);\n  }\n\n  public int smGetPC(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_ADDR);\n    return memory.readAddress(address);\n  }\n\n  public void smExec(final int smNum, final short instr) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_ADDR);\n    memory.writeAddress(address, instr & 0xffff);\n  }\n\n  public boolean smIsExecStalled(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    final int execCtrl = memory.readAddress(address);\n    return (execCtrl & SM0_EXECCTRL_EXEC_STALLED_BITS) != 0x0;\n  }\n\n  public void smExecWaitBlocking(final int smNum, final short instr)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    smExec(smNum, instr);\n    while (smIsExecStalled(smNum)) Thread.yield();\n  }\n\n  public void smSetWrap(final int smNum, final int wrapTarget,\n                        final int wrap)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkSmMemAddr(wrapTarget, \"wrap target\");\n    Constants.checkSmMemAddr(wrap, \"wrap\");\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_EXECCTRL);\n    memory.hwWriteMasked(address,\n                         (wrap << SM0_EXECCTRL_WRAP_TOP_LSB) |\n                         (wrapTarget << SM0_EXECCTRL_WRAP_BOTTOM_LSB),\n                         SM0_EXECCTRL_WRAP_TOP_BITS |\n                         SM0_EXECCTRL_WRAP_BOTTOM_BITS);\n  }\n\n  public void smPut(final int smNum, final int data) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    memory.writeAddress(PIORegisters.getTXFAddress(pioNum, smNum), data);\n  }\n\n  public int smGet(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    return memory.readAddress(PIORegisters.getRXFAddress(pioNum, smNum));\n  }\n\n  public boolean smIsRXFIFOFull(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int fStat =\n      memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                 PIORegisters.Regs.FSTAT));\n    return (fStat & (0x1 << (FSTAT_RXFULL_LSB + smNum))) != 0x0;\n  }\n\n  public boolean smIsRXFIFOEmpty(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int fStat =\n      memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                 PIORegisters.Regs.FSTAT));\n    return (fStat & (0x1 << (FSTAT_RXEMPTY_LSB + smNum))) != 0x0;\n  }\n\n  public int smGetRXFIFOLevel(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int shiftCount =\n      FLEVEL_RX0_LSB + smNum * (FLEVEL_RX1_LSB - FLEVEL_RX0_LSB);\n    final int mask = FLEVEL_RX0_BITS >> FLEVEL_RX0_LSB;\n    return\n      (memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                  PIORegisters.Regs.FLEVEL)) >>\n       shiftCount) & mask;\n  }\n\n  public boolean smIsTXFIFOFull(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int fStat =\n      memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                 PIORegisters.Regs.FSTAT));\n    return (fStat & (0x1 << (FSTAT_TXFULL_LSB + smNum))) != 0x0;\n  }\n\n  public boolean smIsTXFIFOEmpty(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int fStat =\n      memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                 PIORegisters.Regs.FSTAT));\n    return (fStat & (0x1 << (FSTAT_TXEMPTY_LSB + smNum))) != 0x0;\n  }\n\n  public int smGetTXFIFOLevel(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int shiftCount =\n      FLEVEL_TX0_LSB + smNum * (FLEVEL_TX1_LSB - FLEVEL_TX0_LSB);\n    final int mask = FLEVEL_TX0_BITS >> FLEVEL_TX0_LSB;\n    return\n      (memory.readAddress(PIORegisters.getAddress(pioNum,\n                                                  PIORegisters.Regs.FLEVEL)) >>\n       shiftCount) & mask;\n  }\n\n  public void smPutBlocking(final int smNum, final int data) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    while (smIsTXFIFOFull(smNum)) {\n      Thread.yield();\n    }\n    smPut(smNum, data);\n  }\n\n  public int smGetBlocking(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    while (smIsRXFIFOEmpty(smNum)) {\n      Thread.yield();\n    }\n    return smGet(smNum);\n  }\n\n  public void smDrainTXFIFO(final int smNum) throws IOException\n  {\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    final boolean autoPull =\n      (memory.readAddress(address) & SM0_SHIFTCTRL_AUTOPULL_BITS) != 0x0;\n    final int instruction =\n      autoPull ? 0x6060 : 0x8080;\n    while (smIsTXFIFOEmpty(smNum)) {\n      smExec(smNum, (short)instruction);\n      // TODO: Wait for completion of inserted instruction?\n    }\n  }\n\n  public void smSetClkDiv(final int smNum, final float div) throws IOException\n  {\n    if (div < 1.0f) {\n      throw new IllegalArgumentException(\"div < 1: \" + div);\n    }\n    if (div > 65536.0f) {\n      throw new IllegalArgumentException(\"div > 65536: \" + div);\n    }\n    final int divInt = (int)div;\n    final int divFrac;\n    if (divInt == 0) {\n      divFrac = 0;\n    } else {\n      divFrac = (int)((div - divInt) * 256.0);\n    }\n    smSetClkDivIntFrac(smNum, divInt & 0xffff, divFrac);\n  }\n\n  public void smSetClkDivIntFrac(final int smNum,\n                                 final int divInt, final int divFrac)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    if (divInt < 0) {\n      throw new IllegalArgumentException(\"div integer bits < 0: \" +\n                                         divInt);\n    }\n    if (divInt > 0xffff) {\n      throw new IllegalArgumentException(\"div integer bits > 65535: \" +\n                                         divInt);\n    }\n    if (divFrac < 0) {\n      throw new IllegalArgumentException(\"div fractional bits < 0: \" +\n                                         divFrac);\n    }\n    if (divFrac > 0xff) {\n      throw new IllegalArgumentException(\"div fractional bits > 255: \" +\n                                         divFrac);\n    }\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_CLKDIV);\n    final int clkDiv =\n      divInt << SM0_CLKDIV_INT_LSB | divFrac << SM0_CLKDIV_FRAC_LSB;\n    memory.writeAddress(address, clkDiv);\n  }\n\n  public void smClearFIFOs(final int smNum) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_SHIFTCTRL);\n    synchronized(memory) {\n      // toggle RX join bit to force clearance of both, RX and TX\n      memory.hwXorBits(address, SM0_SHIFTCTRL_FJOIN_RX_BITS);\n      // toggle once again to restore previous value\n      memory.hwXorBits(address, SM0_SHIFTCTRL_FJOIN_RX_BITS);\n    }\n  }\n\n  public void smSetPins(final int smNum, int pins) throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    synchronized(memory) {\n      final int pinCtrlSaved = memory.readAddress(address);\n      int remaining = 32;\n      int base = 0;\n      while (remaining > 0) {\n        final int decrement = remaining > 5 ? 5 : remaining;\n        memory.writeAddress(address,\n                            (decrement << SM0_PINCTRL_SET_COUNT_LSB) |\n                            (base << SM0_PINCTRL_SET_BASE_LSB));\n        final int setInstruction =\n          0xe000 | (pins & 0x1f); // no sideset/delay => all other bits are 0\n        smExec(smNum, (short)setInstruction);\n        remaining -= decrement;\n        base += decrement;\n        pins >>>= 5;\n      }\n      memory.writeAddress(address, pinCtrlSaved);\n    }\n  }\n\n  public void smSetPinsWithMask(final int smNum, final int pinValues,\n                                int pinMask)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    synchronized(memory) {\n      final int pinCtrlSaved = memory.readAddress(address);\n      while (pinMask > 0) {\n        final int base = Constants.ctz(pinMask);\n        memory.writeAddress(address,\n                            (0x1 << SM0_PINCTRL_SET_COUNT_LSB) |\n                            (base << SM0_PINCTRL_SET_BASE_LSB));\n        final int setInstruction =\n          0xe000 | ((pinValues >> base) & 0x1); // no sideset/delay =>\n        // all other bits are 0\n        smExec(smNum, (short)setInstruction);\n        pinMask &= pinMask - 1;\n      }\n      memory.writeAddress(address, pinCtrlSaved);\n    }\n  }\n\n  public void smSetPinDirsWithMask(final int smNum, final int pinDirs,\n                                   int pinMask)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    synchronized(memory) {\n      final int pinCtrlSaved = memory.readAddress(address);\n      while (pinMask > 0) {\n        final int base = Constants.ctz(pinMask);\n        memory.writeAddress(address,\n                            (0x1 << SM0_PINCTRL_SET_COUNT_LSB) |\n                            (base << SM0_PINCTRL_SET_BASE_LSB));\n        final int setInstruction =\n          0xe080 | ((pinDirs >> base) & 0x1); // no sideset/delay =>\n                                              // all other bits are 0\n        smExec(smNum, (short)setInstruction);\n        pinMask &= pinMask - 1;\n      }\n      memory.writeAddress(address, pinCtrlSaved);\n    }\n  }\n\n  public void smSetConsecutivePinDirs(final int smNum,\n                                      int pinBase, int pinCount,\n                                      final boolean isOut)\n    throws IOException\n  {\n    Constants.checkSmNum(smNum);\n    Constants.checkGpioPin(pinBase, \"GPIO pin base\");\n    Constants.checkGpioPin(pinCount, \"GPIO pin count\");\n    final int address =\n      PIORegisters.getSMAddress(pioNum, smNum, PIORegisters.Regs.SM0_PINCTRL);\n    synchronized(memory) {\n      final int pinCtrlSaved = memory.readAddress(address);\n      final int pinDirValue = isOut ? 0x1f : 0x0;\n      while (pinCount > 5) {\n        memory.writeAddress(address,\n                            (0x5 << SM0_PINCTRL_SET_COUNT_LSB) |\n                            (pinBase << SM0_PINCTRL_SET_BASE_LSB));\n        final int setInstruction =\n          0xe080 | pinDirValue; // no sideset/delay => all other bits\n                                // are 0\n        smExec(smNum, (short)setInstruction);\n        pinCount -= 5;\n        pinBase = (pinBase + 5) & 0x1f;\n      }\n      memory.writeAddress(address,\n                          (pinCount << SM0_PINCTRL_SET_COUNT_LSB) |\n                             (pinBase << SM0_PINCTRL_SET_BASE_LSB));\n      final int setInstruction =\n        0xe080 | pinDirValue; // no sideset/delay => all other bits\n                              // are 0\n      smExec(smNum, (short)setInstruction);\n      memory.writeAddress(address, pinCtrlSaved);\n    }\n  }\n\n  public void smClaim(final int smNum)\n  {\n    Constants.checkSmNum(smNum);\n    claimSmMask(0x1 << smNum);\n  }\n\n  private String listMaskBits(final int mask) {\n    final StringBuffer s = new StringBuffer();\n    for (int count = 0; count < 32; count++) {\n      if ((mask & (0x1 << count)) != 0x0) {\n        if (s.length() > 0) s.append(\", \");\n        s.append(count);\n      }\n    }\n    return s.toString();\n  }\n\n  public void claimSmMask(final int mask)\n  {\n    if (mask < 0) {\n      throw new IllegalArgumentException(\"mask < 0: \" + mask);\n    }\n    if (mask > (0x1 << SM_COUNT) - 1) {\n      throw new IllegalArgumentException(\"mask > \" +\n                                         ((0x1 << SM_COUNT) - 1) + \": \" +\n                                         mask);\n    }\n    synchronized(stateMachineClaimed) {\n      if ((stateMachineClaimed & mask) != 0x0) {\n        final String message =\n          String.format(\"claim failed: state machine(s) already in use: %s\",\n                        listMaskBits(mask));\n        throw new Panic(message);\n      }\n      stateMachineClaimed |= mask;\n    }\n  }\n\n  public void smUnclaim(final int smNum)\n  {\n    Constants.checkSmNum(smNum);\n    final int mask = 0x1 << smNum;\n    synchronized(stateMachineClaimed) {\n      stateMachineClaimed &= ~mask;\n    }\n  }\n\n  public int claimUnusedSm(final boolean required)\n  {\n    synchronized(stateMachineClaimed) {\n      final int unclaimed = ~stateMachineClaimed & ((0x1 << SM_COUNT) - 1);\n      if (unclaimed == 0x0) {\n        if (required) {\n          final String message =\n            \"claim failed: all state machines already in use\";\n          throw new Panic(message);\n        }\n        return -1;\n      }\n      for (int smNum = 0; smNum < SM_COUNT; smNum++) {\n        if ((unclaimed & (0x1 << smNum)) != 0x0) {\n          return smNum;\n        }\n      }\n      throw new InternalError(\"unexpected fall-through\");\n    }\n  }\n\n  public PinState[] getPinStates() throws IOException\n  {\n    final PinState[] pinStates = new PinState[Constants.GPIO_NUM];\n    final int pinsAddress =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINS);\n    final int pinDirsAddress =\n      PIOEmuRegisters.getAddress(pioNum, PIOEmuRegisters.Regs.GPIO_PINDIRS);\n    final int pins = memory.readAddress(pinsAddress);\n    final int pinDirs = memory.readAddress(pinDirsAddress);\n    for (int gpioNum = 0; gpioNum < Constants.GPIO_NUM; gpioNum++) {\n      final Direction direction =\n        Direction.fromValue((pinDirs >>> gpioNum) & 0x1);\n      final Bit level;\n      if (direction == Direction.OUT) {\n        level = Bit.fromValue((pins >>> gpioNum) & 0x1);\n      } else {\n        level = gpioSdk.getInputLevel(gpioNum, GPIOSDK.Override.AFTER);\n      }\n      pinStates[gpioNum] = PinState.fromValues(direction, level);\n    }\n    return pinStates;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/Panic.java",
    "content": "/*\n * @(#)Panic.java 1.00 21/03/31\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\npublic class Panic extends RuntimeException\n{\n  private static final long serialVersionUID = 1796831772857837603L;\n  private static final String PANIC = \"*** PANIC ***\";\n\n  public Panic(final String message)\n  {\n    super(message);\n  }\n\n  public Panic(final String message, final Throwable cause)\n  {\n    super(message, cause);\n  }\n\n  public Panic(final String message, final Throwable cause,\n               final boolean enableSupression, final boolean writableStackTrace)\n  {\n    super(message, cause, enableSupression, writableStackTrace);\n  }\n\n  public Panic(final Throwable cause)\n  {\n    super(cause);\n  }\n\n  @Override\n  public String getMessage()\n  {\n    final String message = super.getMessage();\n    if (message != null) {\n      return String.format(\"%s%n%s%n\", PANIC, message);\n    } else {\n      return String.format(\"%s%n\", PANIC);\n    }\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/Program.java",
    "content": "/*\n * @(#)Program.java 1.00 21/02/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport java.util.Arrays;\nimport java.io.InputStream;\nimport org.soundpaint.rp2040pio.Constants;\n\n/**\n * This class is for compatibility with the Raspberry Pi Pico SDK.\n * It represents C struct pio_program_t and all functions that\n * manipulate this struct.\n */\npublic class Program implements Constants\n{\n  private final String id;\n  private final int origin;\n  private final int wrap;\n  private final int wrapTarget;\n  private final int sideSetCount;\n  private final boolean sideSetOpt;\n  private final boolean sideSetPinDirs;\n  private final short[] instructions;\n  private final int allocationMask;\n\n  private Program()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  /**\n   * @param origin Either the fixed origin of the program (values\n   * 0…31), or -1, if the program is relocatable, such that it does\n   * not matter where it will be loaded into memory.\n   * @param instructions Array with all instructions of the program.\n   * If the array is null or the length of the array is greater than\n   * 32, an exception is thrown.\n   *\n   * The program id is optional.  Set to\n   * &lt;code&gt;null&lt;/code&gt;, if program id is undefined.\n   * However, if defined, it must be a non-empty string.\n   */\n  public Program(final String id, final int origin, final int wrap,\n                 final int wrapTarget, final int sideSetCount,\n                 final boolean sideSetOpt, final boolean sideSetPinDirs,\n                 final short[] instructions)\n  {\n    if (id != null) {\n      checkId(id);\n    }\n    if (origin < -1) {\n      throw new IllegalArgumentException(\"origin < -1: \" + origin);\n    }\n    if (origin > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"origin > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         origin);\n    }\n    if (wrap < 0) {\n      throw new IllegalArgumentException(\"wrap < 0: \" + wrap);\n    }\n    if (wrap > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"wrap > \" + (MEMORY_SIZE - 1) + \": \" +\n                                         wrap);\n    }\n    if (wrapTarget < 0) {\n      throw new IllegalArgumentException(\"wrap_target < 0: \" + wrapTarget);\n    }\n    if (wrapTarget > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"wrap_target > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         wrapTarget);\n    }\n    if (sideSetCount < 0) {\n      throw new IllegalArgumentException(\"side_set count < 0: \" + sideSetCount);\n    }\n    if (sideSetCount > 5) {\n      throw new IllegalArgumentException(\"side_set count > 5: \" + sideSetCount);\n    }\n    if (sideSetOpt && ((sideSetCount > 4))) {\n      throw new IllegalArgumentException(\"max. side-set count is 4, \" +\n                                         \"if opt is set\");\n    }\n    if (instructions == null) {\n      throw new NullPointerException(\"instructions\");\n    }\n    final int length = instructions.length;\n    if (length > MEMORY_SIZE) {\n      throw new IllegalArgumentException(\"instructions length > \" +\n                                         MEMORY_SIZE + \": \" +\n                                         length);\n    }\n    this.id = id;\n    this.instructions = Arrays.copyOf(instructions, length);\n    this.origin = origin;\n    this.wrap = wrap;\n    this.wrapTarget = wrapTarget;\n    this.sideSetCount = sideSetCount;\n    this.sideSetOpt = sideSetOpt;\n    this.sideSetPinDirs = sideSetPinDirs;\n    final int mask = (length < 32 ? (0x1 << length) : 0) - 1;\n    allocationMask =\n      origin >= 0 ?\n      mask << origin | (mask << (origin - MEMORY_SIZE)) :\n      mask;\n  }\n\n  private void checkId(final String id)\n  {\n    if (id.isEmpty()) {\n      throw new IllegalArgumentException(\"invalid program id: <empty>\");\n    }\n  }\n\n  /**\n   * Optional program identifier, or &lt;code&gt;null&lt;/code&gt;, if\n   * undefined.\n   */\n  public String getId()\n  {\n    return id;\n  }\n\n  public int getLength()\n  {\n    return instructions.length;\n  }\n\n  public short getInstruction(final int index)\n  {\n    if (index < 0) {\n      throw new IllegalArgumentException(\"index < 0: \" + index);\n    }\n    if (index > instructions.length) {\n      throw new IllegalArgumentException(\"index > instructions length: \" +\n                                         index);\n    }\n    return instructions[index];\n  }\n\n  /**\n   * @return The origin of the program.  Either a fixed values in the\n   * range 0…31, or -1, if the program is relocatable, such that it\n   * does not matter where it will be loaded into memory.\n   */\n  public int getOrigin()\n  {\n    return origin;\n  }\n\n  /**\n   * Helper function that returns an allocation mask for the program.\n   * If the program origin is -1, the allocation mask is 0-based.\n   */\n  public int getAllocationMask()\n  {\n    return allocationMask;\n  }\n\n  public SMConfig getDefaultConfig(final int offset)\n  {\n    final SMConfig smConfig = SMConfig.getDefault();\n    smConfig.setWrap(wrapTarget, wrap);\n    if (sideSetCount > 0) {\n      final int nettoSideSetCount = sideSetCount - (sideSetOpt ? 1 : 0);\n      smConfig.setSideSet(nettoSideSetCount, sideSetOpt, sideSetPinDirs);\n    }\n    return smConfig;\n  }\n\n  @Override\n  public String toString()\n  {\n    return\n      String.format(\"Program{origin=%02x,length=%02x}\",\n                    origin, instructions.length);\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/ProgramParser.java",
    "content": "/*\n * @(#)ProgramParser.java 1.00 21/02/16\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.IOException;\nimport java.util.function.Function;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.IOUtils;\nimport org.soundpaint.rp2040pio.ParseException;\n\npublic class ProgramParser implements Constants\n{\n  private static final String DIRECTIVE_PROGRAM = \".program\";\n  private static final String DIRECTIVE_ORIGIN = \".origin\";\n  private static final String DIRECTIVE_WRAP = \".wrap\";\n  private static final String DIRECTIVE_WRAP_TARGET = \".wrap_target\";\n  private static final String DIRECTIVE_SIDE_SET = \".side_set\";\n  private static final String DIRECTIVE_WORD = \".word\";\n  private static final String ARG_OPT = \"opt\";\n  private static final String ARG_PINDIRS = \"pindirs\";\n\n  private final String resourceId;\n  private final BufferedReader reader;\n  private final short[] instructions;\n  private boolean firstInstructionParsed;\n  private int lineIndex;\n  private int address;\n  private String id;\n  private boolean idParsed;\n  private int origin;\n  private boolean originParsed;\n  private int wrap;\n  private boolean wrapParsed;\n  private int wrapTarget;\n  private boolean wrapTargetParsed;\n  private int sideSetCount;\n  private boolean sideSetCountParsed;\n  private boolean sideSetOptParsed;\n  private boolean sideSetPinDirsParsed;\n\n  private ProgramParser()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  private ProgramParser(final String resourceId, final BufferedReader reader)\n    throws IOException\n  {\n    if (resourceId == null) {\n      throw new NullPointerException(\"resourceId\");\n    }\n    if (reader == null) {\n      throw new NullPointerException(\"reader\");\n    }\n    this.resourceId = resourceId;\n    this.reader = reader;\n    address = -1;\n    instructions = new short[MEMORY_SIZE];\n    lineIndex = 0;\n    address = 0;\n    id = null;\n    origin = -1;\n    wrap = MEMORY_SIZE - 1;\n    wrapTarget = 0;\n    sideSetCount = 0;\n  }\n\n  private ParseException parseException(final String message)\n  {\n    return parseException(message, null);\n  }\n\n  private ParseException parseException(final String message,\n                                        final Throwable cause)\n  {\n    return ParseException.create(message, resourceId, lineIndex, cause);\n  }\n\n  private int parseDecimalInt(final String decInt) throws ParseException\n  {\n    try {\n      return Integer.parseInt(decInt);\n    } catch (final NumberFormatException e) {\n      throw parseException(\"expected decimal integer: \" + decInt, e);\n    }\n  }\n\n  private void parseInstruction(final String word,\n                                final Function<String, Integer> wordParser)\n    throws ParseException\n  {\n    if (address >= MEMORY_SIZE) {\n      throw parseException(\"program too large: \" +\n                           \"get more than \" + MEMORY_SIZE + \" words\");\n    }\n    final int value;\n    try {\n      value = wordParser.apply(word);\n    } catch (final NumberFormatException e) {\n      throw parseException(\"invalid 16 bit word: \" + word, e);\n    }\n    if (value < 0x0000) {\n      throw parseException(\"instruction word < 0x0000: \" + value);\n    }\n    if (value > 0xffff) {\n      throw parseException(\"instruction word > 0xffff: \" + value);\n    }\n    instructions[address++] = (short)value;\n    firstInstructionParsed = true;\n  }\n\n  private void checkProgramId(final String id) throws ParseException\n  {\n    final String tail = id.replaceFirst(\"[_A-Za-z][_A-Za-z0-9]*\", \"\");\n    if (!tail.isEmpty()) {\n      final int position = id.indexOf(tail.charAt(0));\n      throw parseException(DIRECTIVE_PROGRAM + \": \" +\n                           \"id contains invalid character at position \" +\n                           position + \": \" +\n                           String.format(\"%n  %s%n%\" + (position + 2) + \"s^\",\n                                         id, \"\"));\n    }\n  }\n\n  private void parseProgramDrct(final String unparsed) throws ParseException\n  {\n    if (idParsed) {\n      throw parseException(DIRECTIVE_PROGRAM + \" already declared\");\n    }\n    final String id = unparsed.trim();\n    if (id.isEmpty()) {\n      throw parseException(DIRECTIVE_PROGRAM + \": id expected\");\n    }\n    checkProgramId(id);\n    this.id = id;\n    idParsed = true;\n  }\n\n  private void parseOriginDrct(final String unparsed) throws ParseException\n  {\n    if (originParsed) {\n      throw parseException(DIRECTIVE_ORIGIN + \" already declared\");\n    }\n    final int origin = parseDecimalInt(unparsed);\n    if (origin < -1) {\n      throw parseException(DIRECTIVE_ORIGIN + \": origin < -1: \" + origin);\n    }\n    if (origin > MEMORY_SIZE - 1) {\n      throw parseException(DIRECTIVE_ORIGIN + \": \" +\n                           \"origin > \" + (MEMORY_SIZE - 1) + \": \" + origin);\n    }\n    this.origin = origin;\n    originParsed = true;\n  }\n\n  private void parseWrapDrct(final String unparsed) throws ParseException\n  {\n    if (wrapParsed) {\n      throw parseException(DIRECTIVE_WRAP + \" already declared\");\n    }\n    final int wrap = parseDecimalInt(unparsed);\n    if (wrap < 0) {\n      throw parseException(DIRECTIVE_WRAP + \": wrap < 0: \" + wrap);\n    }\n    if (wrap > MEMORY_SIZE - 1) {\n      throw parseException(DIRECTIVE_WRAP + \": \" +\n                           \"wrap > \" + (MEMORY_SIZE - 1) + \": \" + wrap);\n    }\n    this.wrap = wrap;\n    wrapParsed = true;\n  }\n\n  private void parseWrapTargetDrct(final String unparsed) throws ParseException\n  {\n    if (wrapTargetParsed) {\n      throw parseException(DIRECTIVE_WRAP_TARGET + \" already declared\");\n    }\n    final int wrapTarget = parseDecimalInt(unparsed);\n    if (wrapTarget < 0) {\n      throw parseException(DIRECTIVE_WRAP_TARGET + \": \" +\n                           \"wrap_target < 0: \" + wrapTarget);\n    }\n    if (wrapTarget > MEMORY_SIZE - 1) {\n      throw parseException(DIRECTIVE_WRAP_TARGET + \": \" +\n                           \"wrap_target > \" + (MEMORY_SIZE - 1) + \": \" +\n                           wrapTarget);\n    }\n    this.wrapTarget = wrapTarget;\n    wrapTargetParsed = true;\n  }\n\n  private void parseSideSetArg(final String arg) throws ParseException\n  {\n    if (ARG_OPT.equals(arg)) {\n      if (sideSetOptParsed) {\n        throw parseException(DIRECTIVE_SIDE_SET + \" \" + ARG_OPT +\n                             \" already declared\");\n      }\n      sideSetOptParsed = true;\n    }\n    if (ARG_PINDIRS.equals(arg)) {\n      if (sideSetPinDirsParsed) {\n        throw parseException(DIRECTIVE_SIDE_SET + \" \" + ARG_PINDIRS +\n                             \" already declared\");\n      }\n      sideSetPinDirsParsed = true;\n    }\n  }\n\n  private void parseSideSetDrct(final String unparsed) throws ParseException\n  {\n    if (sideSetCountParsed) {\n      throw parseException(DIRECTIVE_SIDE_SET + \" already declared\");\n    }\n    if (firstInstructionParsed) {\n      throw parseException(DIRECTIVE_SIDE_SET + \": \" +\n                           \"this directive is only valid before the \" +\n                           \"first instruction\");\n    }\n    final String[] tokens = unparsed.split(\"[\\\\p{javaWhitespace}]*\");\n    if (tokens.length == 0) {\n      throw parseException(DIRECTIVE_SIDE_SET + \": <count> expected\");\n    }\n    final int sideSetCount = parseDecimalInt(tokens[0]);\n    if (sideSetCount < 0) {\n      throw parseException(DIRECTIVE_SIDE_SET + \": \" +\n                           \"side_set count < 0: \" + sideSetCount);\n    }\n    if (sideSetCount > 5) {\n      throw parseException(DIRECTIVE_SIDE_SET + \": \" +\n                           \"side_set count > 5: \" + sideSetCount);\n    }\n    if (tokens.length >= 2) {\n      parseSideSetArg(tokens[1].trim());\n    }\n    if (tokens.length >= 3) {\n      parseSideSetArg(tokens[2].trim());\n    }\n    if (tokens.length >= 4) {\n      throw parseException(DIRECTIVE_SIDE_SET + \": \" +\n                           \"unexpected extra argument: \" + tokens[3]);\n    }\n    if (sideSetOptParsed && ((sideSetCount > 4))) {\n      throw parseException(\"max. side-set count is 4, if opt is set\");\n    }\n    this.sideSetCount = sideSetCount;\n    sideSetCountParsed = true;\n  }\n\n  private void parseDirective(final String directive) throws ParseException\n  {\n    if (directive.startsWith(DIRECTIVE_PROGRAM)) {\n      parseProgramDrct(directive.substring(DIRECTIVE_PROGRAM.length()));\n    } else if (directive.startsWith(DIRECTIVE_ORIGIN)) {\n      parseOriginDrct(directive.substring(DIRECTIVE_ORIGIN.length()));\n    } else if (directive.startsWith(DIRECTIVE_WRAP)) {\n      parseWrapDrct(directive.substring(DIRECTIVE_WRAP.length()));\n    } else if (directive.startsWith(DIRECTIVE_WRAP_TARGET)) {\n      parseWrapTargetDrct(directive.substring(DIRECTIVE_WRAP_TARGET.length()));\n    } else if (directive.startsWith(DIRECTIVE_SIDE_SET)) {\n      parseSideSetDrct(directive.substring(DIRECTIVE_SIDE_SET.length()));\n    } else if (directive.startsWith(DIRECTIVE_WORD)) {\n      parseInstruction(directive.substring(DIRECTIVE_WORD.length()),\n                       (unparsed) -> Integer.parseInt(unparsed));\n    } else {\n      throw parseException(\"unsupported directive: \" + directive);\n    }\n  }\n\n  private void parseComment(final String comment) throws ParseException\n  {\n    if (comment.startsWith(\";\")) {\n      // ignore user comments\n      return;\n    } else {\n      parseDirective(comment);\n    }\n  }\n\n  private Program parse() throws IOException\n  {\n    String line;\n    while ((line = reader.readLine()) != null) {\n      lineIndex++;\n      final String trimmedLine = line.trim();\n      if (trimmedLine.startsWith(\"#\")) {\n        parseComment(trimmedLine.substring(1).trim());\n      } else if (!trimmedLine.isEmpty()) {\n        parseInstruction(trimmedLine,\n                         (unparsed) -> Integer.parseInt(unparsed, 16));\n      } else {\n        // ignore empty lines\n      }\n    }\n    reader.close();\n    if (address == 0) {\n      throw parseException(\"program does not contain any instruction\");\n    }\n    if (!wrapTargetParsed) {\n      wrapTarget = origin >= 0 ? origin : 0;\n    }\n    if (!wrapParsed) {\n      wrapTarget =\n        origin >= 0 ? ((origin + address - 1) % MEMORY_SIZE) : address - 1;\n    }\n    final short[] trimmedInstructions = new short[address];\n    System.arraycopy(instructions, 0, trimmedInstructions, 0, address);\n    final Program program =\n      new Program(id, origin, wrap, wrapTarget, sideSetCount,\n                  sideSetOptParsed, sideSetPinDirsParsed, trimmedInstructions);\n    final String programDisplay =\n      id != null ? \"program \\\"\" + id + \"\\\"\" : \"unnamed program\";\n    final String message =\n      \"parsed \" + programDisplay + \" with \" +\n      address + \" PIO SM instructions\" +\n      (origin >= 0 ? \" @\" + origin : \"\");\n    System.out.println(message);\n    return program;\n  }\n\n  public static Program parse(final String resourceId,\n                              final BufferedReader reader)\n    throws IOException\n  {\n    final ProgramParser parser = new ProgramParser(resourceId, reader);\n    return parser.parse();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/SDK.java",
    "content": "/*\n * @(#)SDK.java 1.00 21/02/02\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport org.soundpaint.rp2040pio.AddressSpace;\nimport org.soundpaint.rp2040pio.Constants;\nimport org.soundpaint.rp2040pio.Emulator;\nimport org.soundpaint.rp2040pio.PicoEmuRegisters;\n\npublic class SDK implements Constants\n{\n  private final PrintStream console;\n  private final AddressSpace memory;\n\n  /*\n   * TODO: There is only a single GPIO, but each of the two PIOs has\n   * its own GPIO input / output latches. Therefore, some GPIO\n   * functionality is shared between both PIOs, while other GPIO\n   * functionality is instantiated per PIO.  This difference should be\n   * made more explicit in the overall architecture.\n   */\n  private final GPIOSDK gpioSdk;\n  private final PIOSDK pio0Sdk;\n  private final PIOSDK pio1Sdk;\n\n  private SDK()\n  {\n    throw new UnsupportedOperationException(\"unsupported empty constructor\");\n  }\n\n  public SDK(final PrintStream console, final AddressSpace memory)\n  {\n    if (console == null) {\n      throw new NullPointerException(\"console\");\n    }\n    this.console = console;\n    if (memory == null) {\n      throw new NullPointerException(\"memory\");\n    }\n    this.memory = memory;\n    gpioSdk = new GPIOSDK(memory);\n    pio0Sdk = new PIOSDK(0, memory, gpioSdk);\n    pio1Sdk = new PIOSDK(1, memory, gpioSdk);\n  }\n\n  public PrintStream getConsole() { return console; }\n  public GPIOSDK getGPIOSDK() { return gpioSdk; }\n  public PIOSDK getPIO0SDK() { return pio0Sdk; }\n  public PIOSDK getPIO1SDK() { return pio1Sdk; }\n\n  public int readAddress(final int address) throws IOException\n  {\n    return memory.readAddress(address);\n  }\n\n  public int readAddress(final int address, final int msb, final int lsb)\n    throws IOException\n  {\n    Constants.checkMSBLSB(msb, lsb);\n    final int value = readAddress(address);\n    return\n      (msb - lsb == 31) ?\n      value :\n      (value >>> lsb) & ((0x1 << (msb - lsb + 1)) - 1);\n  }\n\n  public void writeAddress(final int address, final int value)\n    throws IOException\n  {\n    memory.writeAddress(address, value);\n  }\n\n  public void writeAddressMasked(final int address, final int bits,\n                                 final int mask, final boolean xor)\n    throws IOException\n  {\n    memory.writeAddressMasked(address, bits, mask, xor);\n  }\n\n  public int wait(final int address, final int expectedValue)\n    throws IOException\n  {\n    return wait(address, expectedValue, 0xffffffff);\n  }\n\n  public int wait(final int address, final int expectedValue, final int mask)\n    throws IOException\n  {\n    return wait(address, expectedValue, mask, 0x0);\n  }\n\n  public int wait(final int address, final int expectedValue, final int mask,\n                  final long cyclesTimeout)\n    throws IOException\n  {\n    return wait(address, expectedValue, mask, cyclesTimeout, 0x0);\n  }\n\n  public int wait(final int address, final int expectedValue, final int mask,\n                  final long cyclesTimeout, final long millisTimeout)\n    throws IOException\n  {\n    return\n      memory.waitAddress(address, expectedValue, mask,\n                         cyclesTimeout, millisTimeout);\n  }\n\n  public void awaitNextCycle() throws IOException\n  {\n    memory.waitAddress(EMULATOR_BASE, 0xffffffff, 0x0, 1, 0);\n  }\n\n  public void hwSetBits(final int address, final int mask) throws IOException\n  {\n    memory.hwSetBits(address, mask);\n  }\n\n  public void hwClearBits(final int address, final int mask) throws IOException\n  {\n    memory.hwClearBits(address, mask);\n  }\n\n  public void hwXorBits(final int address, final int mask) throws IOException\n  {\n    memory.hwXorBits(address, mask);\n  }\n\n  public void hwWriteMasked(final int address, final int values,\n                            final int writeMask)\n    throws IOException\n  {\n    memory.hwWriteMasked(address, values, writeMask);\n  }\n\n  // -------- address helpers --------\n\n  public String getEmulatorInfo() throws IOException\n  {\n    return memory.getEmulatorInfo();\n  }\n\n  public boolean providesAddress(final int address) throws IOException\n  {\n    return memory.providesAddress(address);\n  }\n\n  public String getRegisterSetId(final int address) throws IOException\n  {\n    return memory.getRegisterSetId(address);\n  }\n\n  public String getLabelForAddress(final int address) throws IOException\n  {\n    return memory.getAddressLabel(address);\n  }\n\n  public String getFullLabelForAddress(final int address) throws IOException\n  {\n    return getRegisterSetId(address) + \"_\" + getLabelForAddress(address);\n  }\n\n  // -------- PicoEmuRegisters convenience methods --------\n\n  public void reset() throws IOException\n  {\n    final int address =\n      PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.PWR_UP);\n    synchronized(memory) {\n      memory.writeAddress(address, PICO_PWR_UP_VALUE);\n      pio0Sdk.reset();\n      pio1Sdk.reset();\n    }\n  }\n\n  private void triggerCyclePhaseX(final PicoEmuRegisters.Regs trigger,\n                                  final boolean await)\n    throws IOException\n  {\n    final int triggerAddress = PicoEmuRegisters.getAddress(trigger);\n    synchronized(memory) {\n      memory.writeAddress(triggerAddress, 0);\n      while (await && (memory.readAddress(triggerAddress) == 0)) {\n        Thread.yield();\n      }\n    }\n  }\n\n  public void triggerCyclePhase0(final boolean await) throws IOException\n  {\n    triggerCyclePhaseX(PicoEmuRegisters.Regs.MASTERCLK_TRIGGER_PHASE0, await);\n  }\n\n  public void triggerCyclePhase1(final boolean await) throws IOException\n  {\n    triggerCyclePhaseX(PicoEmuRegisters.Regs.MASTERCLK_TRIGGER_PHASE1, await);\n  }\n\n  public long getWallClock() throws IOException\n  {\n    final int addressWallClockLsb =\n      PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.WALLCLOCK_LSB);\n    final int addressWallClockMsb =\n      PicoEmuRegisters.getAddress(PicoEmuRegisters.Regs.WALLCLOCK_MSB);\n    final int wallClockLsb = memory.readAddress(addressWallClockLsb);\n    final int wallClockMsb = memory.readAddress(addressWallClockMsb);\n    return ((long)wallClockMsb << 32) | wallClockLsb;\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  },
  {
    "path": "java/org/soundpaint/rp2040pio/sdk/SMConfig.java",
    "content": "/*\n * @(#)SMConfig.java 1.00 21/02/06\n *\n * Copyright (C) 2021 Jürgen Reuter\n *\n * This program is free software; you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation; either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program; if not, write to the Free Software\n * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n *\n * For updates and more info or contacting the author, visit:\n * <https://github.com/soundpaint/rp2040pio>\n *\n * Author's web site: www.juergen-reuter.de\n */\npackage org.soundpaint.rp2040pio.sdk;\n\nimport org.soundpaint.rp2040pio.Constants;\n\n/**\n * This class is for compatibility with the Raspberry Pi Pico SDK.\n * It represents C struct pio_sm_config and all functions that\n * manipulate this struct.\n */\npublic class SMConfig implements Constants\n{\n  public enum FIFOJoin {\n    NONE, TX, RX;\n  };\n\n  public enum MoveStatusType {\n    STATUS_TX_LESSTHAN, STATUS_RX_LESSTHAN;\n  };\n\n  private int clkDiv;\n  private int execCtrl;\n  private int shiftCtrl;\n  private int pinCtrl;\n\n  private SMConfig()\n  {\n    reset();\n  }\n\n  private void reset()\n  {\n    /*\n     * See values in \"Reset\" column of tables 392, 393, 394, 397 of\n     * RP2040 datasheet, Sect. 3.7.\n     */\n    clkDiv = 0x00010000;\n    execCtrl = 0x0001f000;\n    shiftCtrl = 0x000c0000;\n    pinCtrl = 0x14000000;\n  }\n\n  public int getClkDiv() { return clkDiv; }\n  public int getExecCtrl() { return execCtrl; }\n  public int getShiftCtrl() { return shiftCtrl; }\n  public int getPinCtrl() { return pinCtrl; }\n\n  // ---- Functions for compatibility with the Pico SDK, SM Config Group ----\n\n  public void setOutPins(final int outBase, final int outCount)\n  {\n    if (outBase < 0) {\n      throw new IllegalArgumentException(\"outBase < 0: \" + outBase);\n    }\n    if (outBase > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"outBase > \" + (GPIO_NUM - 1) + \": \" +\n                                         outBase);\n    }\n    if (outCount < 0) {\n      throw new IllegalArgumentException(\"outCount < 0: \" + outCount);\n    }\n    if (outCount > GPIO_NUM) {\n      throw new IllegalArgumentException(\"outCount > \" + GPIO_NUM + \": \" +\n                                         outCount);\n    }\n    pinCtrl &= ~(SM0_PINCTRL_OUT_COUNT_BITS | SM0_PINCTRL_OUT_BASE_BITS);\n    pinCtrl |= outCount << SM0_PINCTRL_OUT_COUNT_LSB;\n    pinCtrl |= outBase << SM0_PINCTRL_OUT_BASE_LSB;\n  }\n\n  public void setSetPins(final int setBase, final int setCount)\n  {\n    if (setBase < 0) {\n      throw new IllegalArgumentException(\"setBase < 0: \" + setBase);\n    }\n    if (setBase > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"setBase > \" + (GPIO_NUM - 1) + \": \" +\n                                         setBase);\n    }\n    if (setCount < 0) {\n      throw new IllegalArgumentException(\"setCount < 0: \" + setCount);\n    }\n    if (setCount > 5) {\n      throw new IllegalArgumentException(\"setCount > 5: \" + setCount);\n    }\n    pinCtrl &= ~(SM0_PINCTRL_SET_COUNT_BITS | SM0_PINCTRL_SET_BASE_BITS);\n    pinCtrl |= setCount << SM0_PINCTRL_SET_COUNT_LSB;\n    pinCtrl |= setBase << SM0_PINCTRL_SET_BASE_LSB;\n  }\n\n  public void setInPins(final int inBase)\n  {\n    if (inBase < 0) {\n      throw new IllegalArgumentException(\"inBase < 0: \" + inBase);\n    }\n    if (inBase > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"inBase > \" + (GPIO_NUM - 1) + \": \" +\n                                         inBase);\n    }\n    pinCtrl &= ~SM0_PINCTRL_IN_BASE_BITS;\n    pinCtrl |= inBase << SM0_PINCTRL_IN_BASE_LSB;\n  }\n\n  public void setSideSetPins(final int sideSetBase)\n  {\n    if (sideSetBase < 0) {\n      throw new IllegalArgumentException(\"sideSetBase < 0: \" + sideSetBase);\n    }\n    if (sideSetBase > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"sideSetBase > \" +\n                                         (GPIO_NUM - 1) + \": \" +\n                                         sideSetBase);\n    }\n    pinCtrl &= ~SM0_PINCTRL_SIDESET_BASE_BITS;\n    pinCtrl |= sideSetBase << SM0_PINCTRL_SIDESET_BASE_LSB;\n  }\n\n  public void setSideSet(final int bitCount,\n                         final boolean optional, final boolean pinDirs)\n  {\n    if (bitCount < 0) {\n      throw new IllegalArgumentException(\"bitCount < 0: \" + bitCount);\n    }\n    if (bitCount > 7) {\n      throw new IllegalArgumentException(\"bitCount > 7: \" + bitCount);\n    }\n    pinCtrl &= ~SM0_PINCTRL_SIDESET_COUNT_BITS;\n    pinCtrl |= bitCount << SM0_PINCTRL_SIDESET_COUNT_LSB;\n    execCtrl &= ~(SM0_EXECCTRL_SIDE_EN_BITS | SM0_EXECCTRL_SIDE_PINDIR_BITS);\n    execCtrl |= (optional ? 1 : 0) << SM0_EXECCTRL_SIDE_EN_LSB;\n    execCtrl |= (pinDirs ? 1 : 0) << SM0_EXECCTRL_SIDE_PINDIR_LSB;\n  }\n\n  public void setClkDiv(final float div)\n  {\n    if (div < 0.0f) {\n      throw new IllegalArgumentException(\"div < 0: \" + div);\n    }\n    if (div >= 65536.0f) {\n      throw new IllegalArgumentException(\"div >= 65536: \" + div);\n    }\n    final int divInt = (int)div;\n    final int divFrac = (int)((div - divInt) * 256.0);\n    setClkDivIntFrac(divInt, divFrac);\n  }\n\n  public void setClkDivIntFrac(final int divInt, final int divFrac)\n  {\n    if (divInt < 0) {\n      throw new IllegalArgumentException(\"div int < 0: \" + divInt);\n    }\n    if (divInt > 65535) {\n      throw new IllegalArgumentException(\"div int > 65535: \" + divInt);\n    }\n    if (divFrac < 0) {\n      throw new IllegalArgumentException(\"div int < 0: \" + divFrac);\n    }\n    if (divFrac > 255) {\n      throw new IllegalArgumentException(\"div int > 255: \" + divFrac);\n    }\n    clkDiv &= ~(SM0_CLKDIV_INT_BITS | SM0_CLKDIV_FRAC_BITS);\n    clkDiv |= divInt << SM0_CLKDIV_INT_LSB;\n    clkDiv |= divFrac << SM0_CLKDIV_FRAC_LSB;\n  }\n\n  public void setWrap(final int wrapTarget, final int wrap)\n  {\n    if (wrapTarget < 0) {\n      throw new IllegalArgumentException(\"wrap target < 0: \" + wrapTarget);\n    }\n    if (wrapTarget > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"wrap target > \" +\n                                         (MEMORY_SIZE - 1) + \": \" +\n                                         wrapTarget);\n    }\n    if (wrap < 0) {\n      throw new IllegalArgumentException(\"wrap < 0: \" + wrap);\n    }\n    if (wrap > MEMORY_SIZE - 1) {\n      throw new IllegalArgumentException(\"wrap > \" + (MEMORY_SIZE - 1) + \": \" +\n                                         wrap);\n    }\n    execCtrl &= ~(SM0_EXECCTRL_WRAP_TOP_BITS | SM0_EXECCTRL_WRAP_BOTTOM_BITS);\n    execCtrl |= wrap << SM0_EXECCTRL_WRAP_TOP_LSB;\n    execCtrl |= wrapTarget << SM0_EXECCTRL_WRAP_BOTTOM_LSB;\n  }\n\n  public void setJmpPin(final int pin)\n  {\n    if (pin < 0) {\n      throw new IllegalArgumentException(\"jmp pin < 0: \" + pin);\n    }\n    if (pin > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"jmp pin > \" + (GPIO_NUM - 1) + \": \" +\n                                         pin);\n    }\n    execCtrl &= ~SM0_EXECCTRL_JMP_PIN_BITS;\n    execCtrl |= pin << SM0_EXECCTRL_JMP_PIN_LSB;\n  }\n\n  public void setInShift(final boolean shiftRight,\n                         final boolean autoPush,\n                         final int pushThreshold)\n  {\n    if (pushThreshold < 0) {\n      throw new IllegalArgumentException(\"push threshold < 0: \" +\n                                         pushThreshold);\n    }\n    if (pushThreshold > 31) {\n      throw new IllegalArgumentException(\"push threshold > 31: \" +\n                                         pushThreshold);\n    }\n    shiftCtrl &= ~(SM0_SHIFTCTRL_PUSH_THRESH_BITS |\n                   SM0_SHIFTCTRL_IN_SHIFTDIR_BITS |\n                   SM0_SHIFTCTRL_AUTOPUSH_BITS);\n    shiftCtrl |= pushThreshold << SM0_SHIFTCTRL_PUSH_THRESH_LSB;\n    shiftCtrl |= (autoPush ? 1 : 0) << SM0_SHIFTCTRL_AUTOPUSH_LSB;\n    shiftCtrl |= (shiftRight ? 1 : 0) << SM0_SHIFTCTRL_IN_SHIFTDIR_LSB;\n  }\n\n  public void setOutShift(final boolean shiftRight,\n                          final boolean autoPull,\n                          final int pullThreshold)\n  {\n    if (pullThreshold < 0) {\n      throw new IllegalArgumentException(\"pull threshold < 0: \" +\n                                         pullThreshold);\n    }\n    if (pullThreshold > 31) {\n      throw new IllegalArgumentException(\"pull threshold > 31: \" +\n                                         pullThreshold);\n    }\n    shiftCtrl &= ~(SM0_SHIFTCTRL_PULL_THRESH_BITS |\n                   SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS |\n                   SM0_SHIFTCTRL_AUTOPULL_BITS);\n    shiftCtrl |= pullThreshold << SM0_SHIFTCTRL_PULL_THRESH_LSB;\n    shiftCtrl |= (shiftRight ? 1 : 0) << SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB;\n    shiftCtrl |= (autoPull ? 1 : 0) << SM0_SHIFTCTRL_AUTOPULL_LSB;\n  }\n\n  public void setFIFOJoin(final FIFOJoin join)\n  {\n    shiftCtrl &= ~(SM0_SHIFTCTRL_FJOIN_RX_BITS | SM0_SHIFTCTRL_FJOIN_TX_BITS);\n    shiftCtrl |= join.ordinal() << SM0_SHIFTCTRL_FJOIN_TX_LSB;\n  }\n\n  public void setOutSpecial(final boolean sticky, final boolean hasEnablePin,\n                            final int enablePinIndex)\n  {\n    if (enablePinIndex < 0) {\n      throw new IllegalArgumentException(\"enable pin index < 0: \" +\n                                         enablePinIndex);\n    }\n    if (enablePinIndex > GPIO_NUM - 1) {\n      throw new IllegalArgumentException(\"enable pin index > \" +\n                                         (GPIO_NUM - 1) + \": \" +\n                                         enablePinIndex);\n    }\n    execCtrl &= ~(SM0_EXECCTRL_OUT_EN_SEL_BITS |\n                  SM0_EXECCTRL_INLINE_OUT_EN_BITS |\n                  SM0_EXECCTRL_OUT_STICKY_BITS);\n    execCtrl |= enablePinIndex << SM0_EXECCTRL_OUT_EN_SEL_LSB;\n    execCtrl |= (hasEnablePin ? 1 : 0) << SM0_EXECCTRL_INLINE_OUT_EN_LSB;\n    execCtrl |= (sticky ? 1 : 0) << SM0_EXECCTRL_OUT_STICKY_LSB;\n  }\n\n  public void setMoveStatus(final MoveStatusType statusSel,\n                            final int statusN)\n  {\n    if (statusN < 0) {\n      throw new IllegalArgumentException(\"status n < 0: \" +\n                                         statusN);\n    }\n    if (statusN > 15) {\n      throw new IllegalArgumentException(\"status n > 15: \" +\n                                         statusN);\n    }\n    execCtrl &= ~(SM0_EXECCTRL_STATUS_SEL_BITS | SM0_EXECCTRL_STATUS_N_BITS);\n    execCtrl |= statusSel.ordinal() << SM0_EXECCTRL_STATUS_SEL_LSB;\n    execCtrl |= statusN << SM0_EXECCTRL_STATUS_N_LSB;\n  }\n\n  public static SMConfig getDefault()\n  {\n    return new SMConfig();\n  }\n}\n\n/*\n * Local Variables:\n *   coding:utf-8\n *   mode:Java\n * End:\n */\n"
  }
]