summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2021-06-25 19:38:44 +0000
committerIan C <ianc@noddybox.co.uk>2021-06-25 19:38:44 +0000
commitabad1c81839420fa60d8923d2332ea003cd73a06 (patch)
tree508732c8310ed93897689eeafce8e6dbaed87755
parent99c00f06c19308c222e123671d2d0f521dd62e05 (diff)
Added files and got initial support working. ZX81 display needed.
-rw-r--r--LICENSE674
-rw-r--r--Makefile209
-rw-r--r--data/cpatrol.binbin0 -> 9198 bytes
-rw-r--r--data/maze.binbin0 -> 10553 bytes
-rw-r--r--data/mazogs.binbin0 -> 12939 bytes
-rw-r--r--data/sabotage.binbin0 -> 8492 bytes
-rw-r--r--data/zx81.binbin0 -> 8192 bytes
-rw-r--r--gfx/cpatrol_inlay.pngbin0 -> 51234 bytes
-rw-r--r--gfx/keyb.pngbin0 -> 4736 bytes
-rw-r--r--gfx/maze_inlay.pngbin0 -> 45597 bytes
-rw-r--r--gfx/mazogs_inlay.pngbin0 -> 63379 bytes
-rw-r--r--gfx/sabotage_inlay.pngbin0 -> 49343 bytes
-rw-r--r--gfx/splashimg.pngbin0 -> 177511 bytes
-rw-r--r--include/config.h53
-rw-r--r--include/framebuffer.h124
-rw-r--r--include/gui.h32
-rw-r--r--include/keyboard.h135
-rw-r--r--include/snapshot.h36
-rw-r--r--include/stream.h34
-rw-r--r--include/tapes.h26
-rw-r--r--include/z80.h256
-rw-r--r--include/z80_config.h58
-rw-r--r--include/z80_private.h275
-rw-r--r--include/zx81.h82
-rw-r--r--source/config.c143
-rw-r--r--source/framebuffer.c433
-rw-r--r--source/gui.c919
-rw-r--r--source/keyboard.c428
-rw-r--r--source/main.c358
-rw-r--r--source/snapshot.c192
-rw-r--r--source/stream.c85
-rw-r--r--source/tapes.c258
-rw-r--r--source/z80.c389
-rw-r--r--source/z80_decode.c2530
-rw-r--r--source/z80_dis.c2491
-rw-r--r--source/zx81.c868
36 files changed, 11088 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9cecc1d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4fd0cc9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,209 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
+endif
+
+TOPDIR ?= $(CURDIR)
+include $(DEVKITARM)/3ds_rules
+
+#---------------------------------------------------------------------------------
+# TARGET is the name of the output
+# BUILD is the directory where object files & intermediate files will be placed
+# SOURCES is a list of directories containing source code
+# DATA is a list of directories containing data files
+# INCLUDES is a list of directories containing header files
+#
+# NO_SMDH: if set to anything, no SMDH file is generated.
+# APP_TITLE is the name of the app stored in the SMDH file (Optional)
+# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional)
+# APP_AUTHOR is the author of the app stored in the SMDH file (Optional)
+# ICON is the filename of the icon (.png), relative to the project folder.
+# If not set, it attempts to use one of the following (in this order):
+# - <Project name>.png
+# - icon.png
+# - <libctru folder>/default_icon.png
+#---------------------------------------------------------------------------------
+TARGET := $(notdir $(CURDIR))
+BUILD := build
+SOURCES := source
+DATA := data
+INCLUDES := include
+GRAPHICS := gfx
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
+
+CFLAGS := -g -Wall -O2 -mword-relocations \
+ -ffunction-sections \
+ $(ARCH)
+
+CFLAGS += $(INCLUDE) -DARM11 -D_3DS
+
+CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
+
+ASFLAGS := -g $(ARCH)
+LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
+
+LIBS := -lctru -lm
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS := $(CTRULIB)
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT := $(CURDIR)/$(TARGET)
+export TOPDIR := $(CURDIR)
+
+export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
+ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
+ $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
+
+export DEPSDIR := $(CURDIR)/$(BUILD)
+
+CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
+PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+ export LD := $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+ export LD := $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \
+ $(PNGFILES:.png=.bgr.o) \
+
+export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
+
+export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(PNGFILES:.png=_bgr.h)
+
+export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+ -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+ifeq ($(strip $(ICON)),)
+ icons := $(wildcard *.png)
+ ifneq (,$(findstring $(TARGET).png,$(icons)))
+ export APP_ICON := $(TOPDIR)/$(TARGET).png
+ else
+ ifneq (,$(findstring icon.png,$(icons)))
+ export APP_ICON := $(TOPDIR)/icon.png
+ endif
+ endif
+else
+ export APP_ICON := $(TOPDIR)/$(ICON)
+endif
+
+IMAGEMAGICK := $(shell which convert)
+
+ifeq ($(strip $(NO_SMDH)),)
+ export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh
+endif
+
+.PHONY: $(BUILD) clean all
+
+#---------------------------------------------------------------------------------
+ifneq ($(strip $(IMAGEMAGICK)),)
+ifeq ($(findstring System32,$(IMAGEMAGICK)),)
+
+HAVE_CONVERT := yes
+
+endif
+endif
+
+ifeq ($(strip $(HAVE_CONVERT)),yes)
+
+all: $(BUILD)
+
+else
+
+all:
+ @echo "Image Magick not found!"
+ @echo
+ @echo "Please install Image Magick from http://www.imagemagick.org/ to build this example"
+
+endif
+
+#---------------------------------------------------------------------------------
+$(BUILD):
+ @[ -d $@ ] || mkdir -p $@
+ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+#---------------------------------------------------------------------------------
+clean:
+ @echo clean ...
+ @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf
+
+
+#---------------------------------------------------------------------------------
+else
+
+DEPENDS := $(OFILES:.o=.d)
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(NO_SMDH)),)
+$(OUTPUT).3dsx : $(OUTPUT).elf $(OUTPUT).smdh
+else
+$(OUTPUT).3dsx : $(OUTPUT).elf
+endif
+
+$(OFILES_SOURCES) : $(HFILES)
+
+$(OUTPUT).elf : $(OFILES)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o : %.bin
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+
+
+#---------------------------------------------------------------------------------
+%_bgr.h %.bgr.o: %.bgr
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @$(bin2o)
+
+#---------------------------------------------------------------------------------
+%.bgr: %.png
+#---------------------------------------------------------------------------------
+ @echo $(notdir $<)
+ @convert $< $@
+
+-include $(DEPENDS)
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/data/cpatrol.bin b/data/cpatrol.bin
new file mode 100644
index 0000000..05dda79
--- /dev/null
+++ b/data/cpatrol.bin
Binary files differ
diff --git a/data/maze.bin b/data/maze.bin
new file mode 100644
index 0000000..2882186
--- /dev/null
+++ b/data/maze.bin
Binary files differ
diff --git a/data/mazogs.bin b/data/mazogs.bin
new file mode 100644
index 0000000..be3effb
--- /dev/null
+++ b/data/mazogs.bin
Binary files differ
diff --git a/data/sabotage.bin b/data/sabotage.bin
new file mode 100644
index 0000000..eb9c19d
--- /dev/null
+++ b/data/sabotage.bin
Binary files differ
diff --git a/data/zx81.bin b/data/zx81.bin
new file mode 100644
index 0000000..557ddcb
--- /dev/null
+++ b/data/zx81.bin
Binary files differ
diff --git a/gfx/cpatrol_inlay.png b/gfx/cpatrol_inlay.png
new file mode 100644
index 0000000..e22ff4f
--- /dev/null
+++ b/gfx/cpatrol_inlay.png
Binary files differ
diff --git a/gfx/keyb.png b/gfx/keyb.png
new file mode 100644
index 0000000..c2043d1
--- /dev/null
+++ b/gfx/keyb.png
Binary files differ
diff --git a/gfx/maze_inlay.png b/gfx/maze_inlay.png
new file mode 100644
index 0000000..e2eefd2
--- /dev/null
+++ b/gfx/maze_inlay.png
Binary files differ
diff --git a/gfx/mazogs_inlay.png b/gfx/mazogs_inlay.png
new file mode 100644
index 0000000..a7af40a
--- /dev/null
+++ b/gfx/mazogs_inlay.png
Binary files differ
diff --git a/gfx/sabotage_inlay.png b/gfx/sabotage_inlay.png
new file mode 100644
index 0000000..837e994
--- /dev/null
+++ b/gfx/sabotage_inlay.png
Binary files differ
diff --git a/gfx/splashimg.png b/gfx/splashimg.png
new file mode 100644
index 0000000..eeaa651
--- /dev/null
+++ b/gfx/splashimg.png
Binary files differ
diff --git a/include/config.h b/include/config.h
new file mode 100644
index 0000000..303f5c7
--- /dev/null
+++ b/include/config.h
@@ -0,0 +1,53 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ $Id: config.h 65 2008-12-12 00:19:08Z ianc $
+*/
+#ifndef DS81_CONFIG_H
+#define DS81_CONFIG_H
+
+/* Default snapshot dir
+*/
+#define DEFAULT_SNAPDIR "/ZX81SNAP/"
+
+typedef enum
+{
+ DS81_STICKY_SHIFT,
+ DS81_STATIC_RAM_AT_0x2000,
+ DS81_ALLOW_TAPE_SAVE,
+ DS81_LOAD_DEFAULT_SNAPSHOT,
+ DS81_NUM_CONFIG_ITEMS
+} DS81_ConfigItem;
+
+/* Returns TRUE if config loaded from FAT device
+*/
+int LoadConfig(void);
+
+/* Returns TRUE if config saved to FAT device
+*/
+int SaveConfig(void);
+
+/* Gets a description for a config item.
+*/
+const char *ConfigDesc(DS81_ConfigItem item);
+
+/* Table of configs. Done like this for simple performance reasons.
+*/
+extern int DS81_Config[/*DS81_ConfigItem item*/];
+
+#endif /* DS81_CONFIG_H */
diff --git a/include/framebuffer.h b/include/framebuffer.h
new file mode 100644
index 0000000..871bd85
--- /dev/null
+++ b/include/framebuffer.h
@@ -0,0 +1,124 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ $Id: framebuffer.h 43 2007-03-12 00:59:51Z ianc $
+*/
+#ifndef DS81_FRAMEBUFFER_H
+#define DS81_FRAMEBUFFER_H
+
+#include <3ds.h>
+
+/* Predefined colours.
+*/
+typedef enum
+{
+ COL_TRANSPARENT = -1,
+ COL_BLACK = 0,
+ COL_WHITE = 1,
+ COL_RED = 2,
+ COL_GREEN = 3,
+ COL_BLUE = 4,
+ COL_GUISELECT = 5,
+ COL_GREY = 6,
+ COL_LIGHTGREY = 7,
+ COL_DARKGREY = 8,
+ COL_YELLOW = 9
+} FB_Colour;
+
+/* Predefined images
+*/
+typedef enum
+{
+ IMG_CITY_PATROL_INLAY,
+ IMG_3D_MONSTER_MAZE_INLAY,
+ IMG_SABOTAGE_INLAY,
+ IMG_MAZOGS_INLAY,
+ IMG_KEYBOARD,
+ IMG_SPLASH
+} FB_Image;
+
+/* A framebuffer or image
+*/
+typedef struct
+{
+ u16 *buffer;
+ u16 width;
+ u16 height;
+} Framebuffer;
+
+/* Macro to get the address of a framebuffer pixel
+*/
+#define FB_ADDR(fb,x,y) (fb)->buffer[(x) * (fb)->height + (y)]
+
+/* Macro to plot a framebuffer pixel safely clippped
+*/
+#define FB_PLOT_CLIPPED(fb,x,y,c) \
+ do \
+ { \
+ if ((x) >= 0 && (x) < (fb)->width &&\
+ (y) >= 0 && (y) < (fb)->height) \
+ { \
+ FB_ADDR(fb, x, y) = c; \
+ } \
+ } while(0)
+
+/* Initialise framebuffer code.
+*/
+void FB_Init(void);
+
+/* Start a frame and setup the framebuffers. This can be called multiple times
+ per frame. Either pointer can be NULL if you're not interested in it.
+*/
+void FB_StartFrame(Framebuffer *upper, Framebuffer *lower);
+
+/* Convenience function to flush the framebuffers, wait for a vsync and scan
+ HID input
+*/
+void FB_EndFrame(void);
+
+/* Get the encoded value for a colour
+*/
+u16 FB_GetColour(FB_Colour col);
+
+/* Print the text into the framebuffer. The text is inverted if the character
+ '\001' is found.
+*/
+void FB_Print(Framebuffer *fb, const char *text, int x, int y,
+ FB_Colour colour, FB_Colour paper);
+void FB_Centre(Framebuffer *fb, const char *text, int y,
+ FB_Colour colour, FB_Colour paper);
+void FB_printf(Framebuffer *fb, int x, int y,
+ FB_Colour colour, FB_Colour paper, const char *format, ...);
+
+/* Lines and boxes.
+*/
+void FB_HLine(Framebuffer *fb, u16 x1, u16 x2, u16 y, FB_Colour colour);
+void FB_VLine(Framebuffer *fb, u16 x, u16 y1, u16 y2, FB_Colour colour);
+void FB_Box(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h, FB_Colour colour);
+void FB_FillBox(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h,
+ FB_Colour colour);
+
+/* Clear to colour
+*/
+void FB_Clear(Framebuffer *fb, FB_Colour col);
+
+/* Draw the image.
+*/
+void FB_Blit(Framebuffer *fb, FB_Image img, u16 x, u16 y);
+
+#endif /* DS81_FRAMEBUFFER_H */
diff --git a/include/gui.h b/include/gui.h
new file mode 100644
index 0000000..3dd3fe2
--- /dev/null
+++ b/include/gui.h
@@ -0,0 +1,32 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ $Id: gui.h 64 2008-12-05 00:37:26Z ianc $
+*/
+#ifndef DS81_GUI_H
+#define DS81_GUI_H
+
+int GUI_Menu(const char *opts[]);
+void GUI_Alert(int fatal, const char *text);
+void GUI_Config(void);
+int GUI_FileSelect(char pwd[], char selected_file[],
+ const char *filter);
+int GUI_InputName(const char *prompt, const char *ext,
+ char name[], int maxlen);
+
+#endif /* DS81_GUI_H */
diff --git a/include/keyboard.h b/include/keyboard.h
new file mode 100644
index 0000000..f0671f0
--- /dev/null
+++ b/include/keyboard.h
@@ -0,0 +1,135 @@
+/*
+ 3ds81 - Nintendo ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+ $Id: keyboard.h 61 2008-11-03 17:07:32Z ianc $
+*/
+#ifndef DS81_KEYBOARD_H
+#define DS81_KEYBOARD_H
+
+#include <stdio.h>
+
+/* Note that the first 40 values purposefully are the keyboard matrix keys.
+ Note also that they are in display order, not matrix order.
+*/
+typedef enum
+{
+ SK_1,
+ SK_2,
+ SK_3,
+ SK_4,
+ SK_5,
+
+ SK_6,
+ SK_7,
+ SK_8,
+ SK_9,
+ SK_0,
+
+ SK_Q,
+ SK_W,
+ SK_E,
+ SK_R,
+ SK_T,
+
+ SK_Y,
+ SK_U,
+ SK_I,
+ SK_O,
+ SK_P,
+
+ SK_A,
+ SK_S,
+ SK_D,
+ SK_F,
+ SK_G,
+
+ SK_H,
+ SK_J,
+ SK_K,
+ SK_L,
+ SK_NEWLINE,
+
+ SK_SHIFT,
+ SK_Z,
+ SK_X,
+ SK_C,
+ SK_V,
+
+ SK_B,
+ SK_N,
+ SK_M,
+ SK_PERIOD,
+ SK_SPACE,
+
+ SK_ABOUT,
+ SK_CONFIG,
+ SK_PAD_UP,
+ SK_PAD_DOWN,
+ SK_PAD_LEFT,
+ SK_PAD_RIGHT,
+ SK_PAD_A,
+ SK_PAD_B,
+ SK_PAD_X,
+ SK_PAD_Y,
+ SK_PAD_R,
+ SK_PAD_L,
+ SK_PAD_START,
+ SK_PAD_SELECT,
+
+ NUM_SOFT_KEYS
+} SoftKey;
+
+typedef struct
+{
+ SoftKey key;
+ int pressed;
+} SoftKeyEvent;
+
+
+/* Display the soft keyboard.
+*/
+void SK_DisplayKeyboard(void);
+
+/* Returns TRUE while there are still key events for this cycle
+*/
+int SK_GetEvent(SoftKeyEvent *ev);
+
+/* Returns TRUE while there are still key events for this cycle. Unlike
+ SK_GetEvent this does not do joypad mappings.
+*/
+int SK_GetBareEvent(SoftKeyEvent *ev);
+
+/* Sets a key to be 'sticky'.
+*/
+void SK_SetSticky(SoftKey key, int is_sticky);
+
+/* Map the joypad to keys. Note that when mapped that both the key and the
+ joypad code will be generated.
+*/
+void SK_DefinePad(SoftKey pad, SoftKey key);
+
+/* Returns a name for key symbols.
+*/
+const char *SK_KeyName(SoftKey pad);
+
+/* Allows the keyboard to save/restore its state from a stream
+*/
+void SK_SaveSnapshot(FILE *fp);
+void SK_LoadSnapshot(FILE *fp);
+
+#endif /* DS81_KEYBOARD_H */
diff --git a/include/snapshot.h b/include/snapshot.h
new file mode 100644
index 0000000..c21829d
--- /dev/null
+++ b/include/snapshot.h
@@ -0,0 +1,36 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: snapshot.h 65 2008-12-12 00:19:08Z ianc $
+*/
+#ifndef DS81_SNAPSHOT_H
+#define DS81_SNAPSHOT_H
+
+#include "z80.h"
+
+typedef enum
+{
+ SNAP_TYPE_FULL,
+ SNAP_TYPE_KEYBOARD
+} SnapshotType;
+
+void SNAP_Enable(int enable);
+void SNAP_Save(Z80 *cpu, SnapshotType type);
+void SNAP_Load(Z80 *cpu, const char *optional_name, SnapshotType type);
+
+#endif /* DS81_SNAPSHOT_H */
diff --git a/include/stream.h b/include/stream.h
new file mode 100644
index 0000000..bbbf80f
--- /dev/null
+++ b/include/stream.h
@@ -0,0 +1,34 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: stream.h 64 2008-12-05 00:37:26Z ianc $
+*/
+#ifndef DS81_STREAM_H
+#define DS81_STREAM_H
+
+#include <stdio.h>
+
+void PUT_Byte(FILE *fp, unsigned char c);
+void PUT_Long(FILE *fp, long l);
+void PUT_ULong(FILE *fp, unsigned long l);
+
+unsigned char GET_Byte(FILE *fp);
+long GET_Long(FILE *fp);
+unsigned long GET_ULong(FILE *fp);
+
+#endif /* DS81_STREAM_H */
diff --git a/include/tapes.h b/include/tapes.h
new file mode 100644
index 0000000..c96d9e9
--- /dev/null
+++ b/include/tapes.h
@@ -0,0 +1,26 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: tapes.h 13 2006-10-12 16:38:57Z ianc $
+*/
+#ifndef DS81_TAPES_H
+#define DS81_TAPES_H
+
+void SelectTape(void);
+
+#endif /* DS81_TAPES_H */
diff --git a/include/z80.h b/include/z80.h
new file mode 100644
index 0000000..ef24fd0
--- /dev/null
+++ b/include/z80.h
@@ -0,0 +1,256 @@
+/*
+
+ z80 - Z80 emulation
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80.h 61 2008-11-03 17:07:32Z ianc $
+
+*/
+
+#ifndef Z80_H
+#define Z80_H "$Id: z80.h 61 2008-11-03 17:07:32Z ianc $"
+
+#include <stdio.h>
+
+/* Configuration
+*/
+#include "z80_config.h"
+
+
+/* ---------------------------------------- TYPES
+*/
+
+/* Large unsigned type
+*/
+typedef unsigned long Z80Val;
+
+
+/* 8-bit type. The emulation will exit with code 2 if this isn't 8 bits.
+*/
+typedef unsigned char Z80Byte;
+
+
+/* 8-bit signed type. The emulation will exit with code 2 if this isn't 8 bits.
+*/
+typedef signed char Z80Relative;
+
+
+/* 16-bit type. The emulation will exit with code 2 if this isn't 16 bits.
+*/
+typedef unsigned short Z80Word;
+
+
+/* A Z80 16-bit register. To access the HI/LO component use the indexes
+ Z80_HI_WORD and Z80_LO_WORD which will be initialised once Z80Init has been
+ called.
+*/
+typedef union
+{
+ Z80Word w;
+ Z80Byte b[2];
+} Z80Reg;
+
+extern int Z80_HI_WORD;
+extern int Z80_LO_WORD;
+
+
+/* The processor
+*/
+struct Z80Private;
+
+typedef struct
+{
+ Z80Word PC;
+
+ Z80Reg AF;
+ Z80Reg BC;
+ Z80Reg DE;
+ Z80Reg HL;
+
+ Z80Word AF_;
+ Z80Word BC_;
+ Z80Word DE_;
+ Z80Word HL_;
+
+ Z80Reg IX;
+ Z80Reg IY;
+
+ Z80Word SP;
+
+ Z80Byte IFF1;
+ Z80Byte IFF2;
+ Z80Byte IM;
+ Z80Byte I;
+ Z80Byte R;
+
+ struct Z80Private *priv;
+} Z80;
+
+
+/* Interfaces used to handle memory
+*/
+typedef Z80Byte (*Z80ReadMemory)(Z80 *cpu, Z80Word address);
+typedef void (*Z80WriteMemory)(Z80 *cpu, Z80Word address, Z80Byte value);
+
+
+/* Interfaces needed to handle ports (IN/OUT commands)
+*/
+typedef Z80Byte (*Z80ReadPort)(Z80 *cpu, Z80Word address);
+typedef void (*Z80WritePort)(Z80 *cpu, Z80Word address, Z80Byte value);
+
+
+/* Callback. Callback should return TRUE for processing to continue.
+*/
+typedef int (*Z80Callback)(Z80 *cpu, Z80Val data);
+
+
+/* Callback reasons
+
+ eZ80_Instruction Called before the initial fetch for an instruction
+ (called just to once no matter how many bytes the
+ instruction is made up of).
+
+ eZ80_EDHook Called when an undefined ED opcode is executed.
+
+ eZ80_Halt Called when the HALT instruction is hit and released.
+
+ eZ80_RETI Called when the RETI instruction is executed
+*/
+typedef enum
+{
+ eZ80_Instruction, /* data = no cycles since reset */
+ eZ80_EDHook, /* data = byte after ED opcode (only for NOP opcodes) */
+ eZ80_Halt, /* data = 1 halt raised, 0 halt cleared by int */
+ eZ80_RETI, /* data = ignored */
+ eZ80_NO_CALLBACK /* leave at end */
+} Z80CallbackReason;
+
+
+/* Flags in the F register
+*/
+typedef enum
+{
+ eZ80_Carry =0x01,
+ eZ80_Neg =0x02,
+ eZ80_PV =0x04,
+ eZ80_Hidden3 =0x08,
+ eZ80_HalfCarry =0x10,
+ eZ80_Hidden5 =0x20,
+ eZ80_Zero =0x40,
+ eZ80_Sign =0x80
+} Z80FlagRegister;
+
+
+/* Disassembly label -- only useful if ENABLE_DISASSEMBLER is set.
+ Labels are stored as an array, where a NULL in the label field marks
+ the end of the list.
+*/
+typedef struct
+{
+ Z80Word address;
+ const char *label;
+} Z80Label;
+
+
+/* ---------------------------------------- INTERFACES
+*/
+
+
+/* Initialises the processor.
+*/
+#ifdef ENABLE_ARRAY_MEMORY
+Z80 *Z80Init(Z80ReadPort read_port,
+ Z80WritePort write_port);
+#else
+Z80 *Z80Init(Z80ReadMemory read_memory,
+ Z80WriteMemory write_memory,
+ Z80ReadPort read_port,
+ Z80WritePort write_port,
+ Z80ReadMemory read_for_disassem);
+#endif
+
+
+/* Resets the processor.
+*/
+void Z80Reset(Z80 *cpu);
+
+
+/* Lodge a callback to be invoked after special events. Returns FALSE
+ if the callback couldn't be lodged (there is a max of 10 callbacks per
+ reason).
+*/
+int Z80LodgeCallback(Z80 *cpu,
+ Z80CallbackReason reason,
+ Z80Callback callback);
+
+
+/* Remove a callback. Does nothing if reason was not lodged with
+ Z80LodgeCallback()
+*/
+void Z80RemoveCallback(Z80 *cpu,
+ Z80CallbackReason reason,
+ Z80Callback callback);
+
+
+/* Cause an interrupt before the next opcode.
+ devbyte is the byte generated by the device (if any).
+*/
+void Z80Interrupt(Z80 *cpu, Z80Byte devbyte);
+
+
+/* Cause an NMI
+*/
+void Z80NMI(Z80 *cpu);
+
+
+/* Execute a single instruction. Returns FALSE if any callback returned
+ FALSE.
+*/
+int Z80SingleStep(Z80 *cpu);
+
+
+/* Executes until a callback returns FALSE (never returns otherwise)
+*/
+void Z80Exec(Z80 *cpu);
+
+
+/* Manipulate the cylce count of the Z80
+*/
+Z80Val Z80Cycles(Z80 *cpu);
+void Z80ResetCycles(Z80 *cpu, Z80Val cycles);
+
+
+/* Set address to label mappings for the disassembler
+*/
+void Z80SetLabels(Z80Label labels[]);
+
+
+/* Simple disassembly of memory accessed through read_for_disassem, or
+ Z80_MEMORY as appropriate. addr is updated on exit.
+*/
+const char *Z80Disassemble(Z80 *cpu, Z80Word *addr);
+
+/* Allows the CPU state to be saved/loaded from a stream
+*/
+void Z80SaveSnapshot(Z80 *cpu, FILE *fp);
+void Z80LoadSnapshot(Z80 *cpu, FILE *fp);
+
+#endif
+
+/* END OF FILE */
diff --git a/include/z80_config.h b/include/z80_config.h
new file mode 100644
index 0000000..8d9ee79
--- /dev/null
+++ b/include/z80_config.h
@@ -0,0 +1,58 @@
+/*
+
+ z80 - Z80 emulation
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80_config.h 41 2007-03-01 00:36:54Z ianc $
+
+*/
+
+#ifndef Z80_CONFIG_H
+#define Z80_CONFIG_H "$Id: z80_config.h 41 2007-03-01 00:36:54Z ianc $"
+
+
+/* This file defines various compile-time configuration options
+ for the Z80 emulation
+*/
+
+
+/* Define this to enable the disassembly interface
+*/
+#define ENABLE_DISASSEM
+
+
+/* Define this to enable the array-based memory model. In this mode
+ an externally visible Z80Byte array called Z80_MEMORY must be
+ defined. The macros RAMBOT and RAMTOP define the writable area of
+ memory and must be changed accordingly.
+
+ In this mode the signature of Z80Init changes so that the memory functions
+ are not passed. ALL processor instances share the same memory.
+#define ENABLE_ARRAY_MEMORY
+*/
+
+#ifdef ENABLE_ARRAY_MEMORY
+#define RAMBOT 0x0000
+#define RAMTOP 0xffff
+#endif
+
+
+#endif
+
+/* END OF FILE */
diff --git a/include/z80_private.h b/include/z80_private.h
new file mode 100644
index 0000000..10cf97e
--- /dev/null
+++ b/include/z80_private.h
@@ -0,0 +1,275 @@
+/*
+
+ z80 - Z80 emulation
+
+ Copyright (C) 2021 Ian Cowburn (ianc@noddybox.co.uk)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80_private.h 13 2006-10-12 16:38:57Z ianc $
+
+ Private macros for Z80
+
+*/
+
+#ifndef Z80_PRIVATE_H
+#define Z80_PRIVATE_H "$Id: z80_private.h 13 2006-10-12 16:38:57Z ianc $"
+
+#include "z80_config.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define MAX_PER_CALLBACK 10
+
+
+/* ---------------------------------------- TYPES
+*/
+
+struct Z80Private
+{
+ Z80Val cycle;
+
+ int halt;
+
+ Z80Byte shift;
+
+ int raise;
+ Z80Byte devbyte;
+ int nmi;
+
+#ifndef ENABLE_ARRAY_MEMORY
+ Z80ReadMemory disread;
+
+ Z80ReadMemory mread;
+ Z80WriteMemory mwrite;
+#endif
+
+ Z80ReadPort pread;
+ Z80WritePort pwrite;
+
+ Z80Callback callback[eZ80_NO_CALLBACK][MAX_PER_CALLBACK];
+
+ int last_cb;
+};
+
+#define PRIV cpu->priv
+
+
+/* ---------------------------------------- ARRAY MEMORY
+*/
+
+#ifdef ENABLE_ARRAY_MEMORY
+extern Z80Byte Z80_MEMORY[];
+#endif
+
+
+/* ---------------------------------------- MACROS
+*/
+
+/* NOTE: A lot of these macros assume you have a variable called 'cpu'
+ which is a pointer to Z80
+*/
+
+
+/* Invoke a callback class
+*/
+#define CALLBACK(r,d) do \
+ { \
+ int f; \
+ \
+ for(f=0;f<MAX_PER_CALLBACK;f++) \
+ if (PRIV->callback[r][f]) \
+ PRIV->last_cb &= \
+ PRIV->callback[r][f](cpu,d);\
+ } while(0)
+
+/* Flag register
+*/
+#define C_Z80 0x01
+#define N_Z80 0x02
+#define P_Z80 0x04
+#define V_Z80 P_Z80
+#define H_Z80 0x10
+#define Z_Z80 0x40
+#define S_Z80 0x80
+
+#define B3_Z80 0x08
+#define B5_Z80 0x20
+
+
+#define SET(v,b) (v)|=b
+#define CLR(v,b) (v)&=~(b)
+
+#define SETFLAG(f) SET(cpu->AF.b[LO],f)
+#define CLRFLAG(f) CLR(cpu->AF.b[LO],f)
+
+#ifdef ENABLE_ARRAY_MEMORY
+
+#define PEEK(addr) Z80_MEMORY[addr]
+
+static inline Z80Word PEEKW(Z80Word addr)
+{
+ return (PEEK(addr) | (Z80Word)PEEK(addr+1)<<8);
+}
+
+#define POKE(addr,val) do \
+ { \
+ Z80Word ba=addr; \
+ if (ba>=RAMBOT && ba<=RAMTOP) \
+ Z80_MEMORY[ba]=val; \
+ } while(0)
+
+#define POKEW(addr,val) do \
+ { \
+ Z80Word wa=addr; \
+ Z80Word wv=val; \
+ POKE(wa,wv); \
+ POKE(wa+1,wv>>8); \
+ } while(0)
+
+
+#define FETCH_BYTE (Z80_MEMORY[cpu->PC++])
+#define FETCH_WORD (cpu->PC+=2, \
+ Z80_MEMORY[cpu->PC-2]| \
+ ((Z80Word)Z80_MEMORY[cpu->PC-1]<<8))
+
+#else
+
+#define PEEK(addr) (PRIV->mread(cpu,addr))
+#define PEEKW(addr) FPEEKW(cpu,addr)
+
+#define POKE(addr,val) PRIV->mwrite(cpu,addr,val)
+#define POKEW(addr,val) FPOKEW(cpu,addr,val)
+
+#define FETCH_BYTE (PRIV->mread(cpu,cpu->PC++))
+#define FETCH_WORD (cpu->PC+=2,FPEEKW(cpu,cpu->PC-2))
+
+#endif
+
+
+#define IS_C (cpu->AF.b[LO]&C_Z80)
+#define IS_N (cpu->AF.b[LO]&N_Z80)
+#define IS_P (cpu->AF.b[LO]&P_Z80)
+#define IS_H (cpu->AF.b[LO]&H_Z80)
+#define IS_Z (cpu->AF.b[LO]&Z_Z80)
+#define IS_S (cpu->AF.b[LO]&S_Z80)
+
+#define CARRY IS_C
+
+#define IS_IX_IY (PRIV->shift==0xdd || PRIV->shift==0xfd)
+#define OFFSET(off) off=(IS_IX_IY ? (Z80Relative)FETCH_BYTE:0)
+
+#define TSTATE(n) PRIV->cycle+=n
+
+#define ADD_R(v) cpu->R=((cpu->R&0x80)|((cpu->R+(v))&0x7f))
+#define INC_R ADD_R(1)
+
+#ifdef ENABLE_ARRAY_MEMORY
+
+#define PUSH(REG) do \
+ { \
+ Z80Word pv=REG; \
+ cpu->SP-=2; \
+ POKE(cpu->SP,pv); \
+ POKE(cpu->SP+1,pv>>8); \
+ } while(0)
+
+#else
+
+#define PUSH(REG) do \
+ { \
+ Z80Word pushv=REG; \
+ cpu->SP-=2; \
+ PRIV->mwrite(cpu,cpu->SP,pushv); \
+ PRIV->mwrite(cpu,cpu->SP+1,pushv>>8);\
+ } while(0)
+#endif
+
+#define POP(REG) do \
+ { \
+ REG=PEEK(cpu->SP) | \
+ (Z80Word)PEEK(cpu->SP+1)<<8; \
+ cpu->SP+=2; \
+ } while(0)
+
+#define SETHIDDEN(res) cpu->AF.b[LO]=(cpu->AF.b[LO]&~(B3_Z80|B5_Z80))|\
+ ((res)&(B3_Z80|B5_Z80))
+
+#define CALL do \
+ { \
+ PUSH(cpu->PC+2); \
+ cpu->PC=PEEKW(cpu->PC); \
+ } while(0)
+
+#define NOCALL cpu->PC+=2
+#define JP cpu->PC=PEEKW(cpu->PC)
+#define NOJP cpu->PC+=2
+#define JR cpu->PC+=(Z80Relative)PEEK(cpu->PC)+1
+#define NOJR cpu->PC++
+
+#define OUT(P,V) do \
+ { \
+ if (PRIV->pwrite) \
+ PRIV->pwrite(cpu,P,V); \
+ } while(0)
+
+#define IN(P) (PRIV->pread?PRIV->pread(cpu,P):0)
+
+
+
+/* ---------------------------------------- LABELS
+*/
+extern Z80Label *z80_labels;
+
+
+/* ---------------------------------------- GLOBAL GENERAL OPCODES/ROUTINES
+*/
+void Z80_Decode(Z80 *cpu, Z80Byte opcode);
+void Z80_InitialiseInternals(void);
+
+
+/* ---------------------------------------- DISASSEMBLY
+*/
+#ifdef ENABLE_DISASSEM
+typedef void (*DIS_OP_CALLBACK)(Z80 *z80, Z80Byte op, Z80Word *pc);
+
+extern DIS_OP_CALLBACK dis_CB_opcode[];
+extern DIS_OP_CALLBACK dis_DD_opcode[];
+extern DIS_OP_CALLBACK dis_DD_CB_opcode[];
+extern DIS_OP_CALLBACK dis_ED_opcode[];
+extern DIS_OP_CALLBACK dis_FD_opcode[];
+extern DIS_OP_CALLBACK dis_FD_CB_opcode[];
+extern DIS_OP_CALLBACK dis_opcode_z80[];
+
+const char *Z80_Dis_Printf(const char *format, ...);
+
+Z80Byte Z80_Dis_FetchByte(Z80 *cpu, Z80Word *pc);
+Z80Word Z80_Dis_FetchWord(Z80 *cpu, Z80Word *pc);
+
+void Z80_Dis_Set(const char *op, const char *arg);
+const char *Z80_Dis_GetOp(void);
+const char *Z80_Dis_GetArg(void);
+#endif /* ENABLE_DISASSEM */
+
+#endif /* Z80_PRIVATE_H */
+
+/* END OF FILE */
diff --git a/include/zx81.h b/include/zx81.h
new file mode 100644
index 0000000..770a132
--- /dev/null
+++ b/include/zx81.h
@@ -0,0 +1,82 @@
+/*
+
+ 3ds81 - Nintendo 3DS ZX81 emulator
+
+ Copyright (C) 2021 Ian Cowburn (ianc@noddybox.co.uk)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ Provides the emulation for the ZX81
+
+*/
+
+#ifndef DS81_ZX81_H
+#define DS81_ZX81_H
+
+#include <stdio.h>
+
+#include "framebuffer.h"
+#include "z80.h"
+#include "keyboard.h"
+
+
+/* Initialise the ZX81
+*/
+void ZX81Init(Z80 *z80);
+
+/* Render the display
+*/
+void ZX81RenderDisplay(Framebuffer *fb);
+
+/* Handle keypresses
+*/
+void ZX81HandleKey(SoftKey k, int is_pressed);
+
+/* Enable fopen() loading of tape files
+*/
+void ZX81EnableFileSystem(int enable);
+
+/* Set a file to load from tape
+*/
+void ZX81SetTape(const Z80Byte *image, int len);
+
+/* Reset the 81
+*/
+void ZX81Reset(Z80 *z80);
+
+/* Tell the 81 that config may have changed.
+*/
+void ZX81Reconfigure(void);
+
+/* Interfaces for the Z80
+*/
+Z80Byte ZX81ReadMem(Z80 *z80, Z80Word addr);
+void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val);
+Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port);
+void ZX81WritePort(Z80 *z80, Z80Word port, Z80Byte val);
+
+#define ZX81ReadDisassem ZX81ReadMem
+
+/* Interfaces to allows the ZX81 to save/load itself as a snapshot to/from
+ a stream.
+*/
+void ZX81SaveSnapshot(FILE *fp);
+void ZX81LoadSnapshot(FILE *fp);
+
+#endif
+
+
+/* END OF FILE */
diff --git a/source/config.c b/source/config.c
new file mode 100644
index 0000000..c5635e3
--- /dev/null
+++ b/source/config.c
@@ -0,0 +1,143 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: config.c 65 2008-12-12 00:19:08Z ianc $
+*/
+
+#include <3ds.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* ---------------------------------------- PRIVATE DATA
+*/
+const char *conf_filename = "DS81.CFG";
+
+const char *conf_entry[DS81_NUM_CONFIG_ITEMS]=
+{
+ "sticky_shift",
+ "static_ram_at_0x2000",
+ "allow_tape_save",
+ "load_default_snapshot"
+};
+
+
+/* ---------------------------------------- GLOBAL DATA
+*/
+int DS81_Config[DS81_NUM_CONFIG_ITEMS]=
+{
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE
+};
+
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+int LoadConfig(void)
+{
+ FILE *fp = NULL;
+
+ fp=fopen(conf_filename,"r");
+
+ if (fp)
+ {
+ char line[80];
+
+ while(fgets(line, sizeof line, fp))
+ {
+ char *p;
+
+ if ((p = strchr(line, '=')))
+ {
+ int f;
+
+ for(f=0;f<DS81_NUM_CONFIG_ITEMS;f++)
+ {
+ if (strncmp(line, conf_entry[f],
+ strlen(conf_entry[f])) == 0)
+ {
+ DS81_Config[f] = (*(p+1) == '1');
+ }
+ }
+
+ }
+ }
+
+ fclose(fp);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int SaveConfig(void)
+{
+ FILE *fp = NULL;
+
+ fp=fopen(conf_filename,"w");
+
+ if (fp)
+ {
+ int f;
+
+ for(f=0;f<DS81_NUM_CONFIG_ITEMS;f++)
+ {
+ fprintf(fp,"%s=%d\n",conf_entry[f],DS81_Config[f]);
+ }
+
+ fclose(fp);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const char *ConfigDesc(DS81_ConfigItem item)
+{
+ switch(item)
+ {
+ case DS81_STICKY_SHIFT:
+ return "STICKY SHIFT";
+
+ case DS81_STATIC_RAM_AT_0x2000:
+ return "RAM AT 8192";
+
+ case DS81_LOAD_DEFAULT_SNAPSHOT:
+ return "LOAD DEFAULT SNAPSHOT";
+
+ case DS81_ALLOW_TAPE_SAVE:
+ return "ALLOW TAPE SAVING";
+
+ default:
+ return "UNKNOWN";
+ }
+}
+
diff --git a/source/framebuffer.c b/source/framebuffer.c
new file mode 100644
index 0000000..efdd27b
--- /dev/null
+++ b/source/framebuffer.c
@@ -0,0 +1,433 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: framebuffer.c 43 2007-03-12 00:59:51Z ianc $
+*/
+
+#include <3ds.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "framebuffer.h"
+
+#include "cpatrol_inlay_bgr.h"
+#include "mazogs_inlay_bgr.h"
+#include "keyb_bgr.h"
+#include "sabotage_inlay_bgr.h"
+#include "maze_inlay_bgr.h"
+#include "splashimg_bgr.h"
+
+/* ---------------------------------------- STATIC DATA
+*/
+static u16 pal[COL_YELLOW + 1];
+
+static Framebuffer image[IMG_SPLASH + 1];
+
+static u8 font[]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00,
+ 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00,
+ 0x00, 0x10, 0x7c, 0x14, 0x7c, 0x50, 0x7c, 0x10,
+ 0x00, 0x46, 0x26, 0x10, 0x08, 0x64, 0x62, 0x00,
+ 0x00, 0x08, 0x14, 0x08, 0x54, 0x22, 0x5c, 0x00,
+ 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00,
+ 0x00, 0x04, 0x08, 0x08, 0x08, 0x08, 0x04, 0x00,
+ 0x00, 0x00, 0x28, 0x10, 0x7c, 0x10, 0x28, 0x00,
+ 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
+ 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00,
+ 0x00, 0x3c, 0x62, 0x52, 0x4a, 0x46, 0x3c, 0x00,
+ 0x00, 0x18, 0x14, 0x10, 0x10, 0x10, 0x7c, 0x00,
+ 0x00, 0x3c, 0x42, 0x40, 0x3c, 0x02, 0x7e, 0x00,
+ 0x00, 0x3c, 0x42, 0x30, 0x40, 0x42, 0x3c, 0x00,
+ 0x00, 0x10, 0x18, 0x14, 0x12, 0x7e, 0x10, 0x00,
+ 0x00, 0x7e, 0x02, 0x3e, 0x40, 0x42, 0x3c, 0x00,
+ 0x00, 0x3c, 0x02, 0x3e, 0x42, 0x42, 0x3c, 0x00,
+ 0x00, 0x7e, 0x40, 0x20, 0x10, 0x08, 0x08, 0x00,
+ 0x00, 0x3c, 0x42, 0x3c, 0x42, 0x42, 0x3c, 0x00,
+ 0x00, 0x3c, 0x42, 0x42, 0x7c, 0x40, 0x3c, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x04,
+ 0x00, 0x00, 0x20, 0x10, 0x08, 0x10, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x10, 0x20, 0x10, 0x08, 0x00,
+ 0x00, 0x3c, 0x42, 0x20, 0x10, 0x00, 0x10, 0x00,
+ 0x00, 0x3c, 0x52, 0x6a, 0x7a, 0x02, 0x3c, 0x00,
+ 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x00,
+ 0x00, 0x3e, 0x42, 0x3e, 0x42, 0x42, 0x3e, 0x00,
+ 0x00, 0x3c, 0x42, 0x02, 0x02, 0x42, 0x3c, 0x00,
+ 0x00, 0x1e, 0x22, 0x42, 0x42, 0x22, 0x1e, 0x00,
+ 0x00, 0x7e, 0x02, 0x3e, 0x02, 0x02, 0x7e, 0x00,
+ 0x00, 0x7e, 0x02, 0x3e, 0x02, 0x02, 0x02, 0x00,
+ 0x00, 0x3c, 0x42, 0x02, 0x72, 0x42, 0x3c, 0x00,
+ 0x00, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x00,
+ 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x00,
+ 0x00, 0x40, 0x40, 0x40, 0x42, 0x42, 0x3c, 0x00,
+ 0x00, 0x22, 0x12, 0x0e, 0x12, 0x22, 0x42, 0x00,
+ 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00,
+ 0x00, 0x42, 0x66, 0x5a, 0x42, 0x42, 0x42, 0x00,
+ 0x00, 0x42, 0x46, 0x4a, 0x52, 0x62, 0x42, 0x00,
+ 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00,
+ 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x00,
+ 0x00, 0x3c, 0x42, 0x42, 0x4a, 0x52, 0x3c, 0x00,
+ 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x22, 0x42, 0x00,
+ 0x00, 0x3c, 0x02, 0x3c, 0x40, 0x42, 0x3c, 0x00,
+ 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
+ 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00,
+ 0x00, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00,
+ 0x00, 0x42, 0x42, 0x42, 0x42, 0x5a, 0x24, 0x00,
+ 0x00, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x00,
+ 0x00, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x00,
+ 0x00, 0x7e, 0x20, 0x10, 0x08, 0x04, 0x7e, 0x00,
+ 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00,
+ 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
+ 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00,
+ 0x00, 0x08, 0x1c, 0x2a, 0x08, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x38, 0x44, 0x1e, 0x04, 0x04, 0x7e, 0x00,
+ 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3c, 0x00,
+ 0x00, 0x04, 0x04, 0x3c, 0x44, 0x44, 0x3c, 0x00,
+ 0x00, 0x00, 0x38, 0x04, 0x04, 0x04, 0x38, 0x00,
+ 0x00, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x3c, 0x00,
+ 0x00, 0x00, 0x1c, 0x22, 0x1e, 0x02, 0x3c, 0x00,
+ 0x00, 0x30, 0x08, 0x18, 0x08, 0x08, 0x08, 0x00,
+ 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x1c,
+ 0x00, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x00,
+ 0x00, 0x08, 0x00, 0x0c, 0x08, 0x08, 0x1c, 0x00,
+ 0x00, 0x20, 0x00, 0x20, 0x20, 0x20, 0x24, 0x18,
+ 0x00, 0x04, 0x14, 0x0c, 0x0c, 0x14, 0x24, 0x00,
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x30, 0x00,
+ 0x00, 0x00, 0x16, 0x2a, 0x2a, 0x2a, 0x2a, 0x00,
+ 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00,
+ 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00,
+ 0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02,
+ 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x60,
+ 0x00, 0x00, 0x38, 0x04, 0x04, 0x04, 0x04, 0x00,
+ 0x00, 0x00, 0x1c, 0x02, 0x1c, 0x20, 0x1e, 0x00,
+ 0x00, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x30, 0x00,
+ 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00,
+ 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x00,
+ 0x00, 0x00, 0x22, 0x2a, 0x2a, 0x2a, 0x14, 0x00,
+ 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00,
+ 0x00, 0x00, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c,
+ 0x00, 0x00, 0x3e, 0x10, 0x08, 0x04, 0x3e, 0x00,
+ 0x00, 0x70, 0x10, 0x0c, 0x10, 0x10, 0x70, 0x00,
+ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00,
+ 0x00, 0x0e, 0x08, 0x30, 0x08, 0x08, 0x0e, 0x00,
+ 0x00, 0x28, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3c, 0x42, 0x99, 0x85, 0x85, 0x99, 0x42, 0x3c
+};
+
+
+/* ---------------------------------------- PRIVATE INTERFACES
+*/
+static void LoadImage(Framebuffer *fb, const u8* img, u16 w, u16 h)
+{
+ int f;
+ int x,y;
+
+ fb->width = w;
+ fb->height = h;
+
+ fb->buffer = malloc(sizeof *fb->buffer * w * h);
+
+ x = 0;
+ y = h - 1;
+
+ for(f = 0; f < w * h; f++)
+ {
+ u8 b = *img++;
+ u8 g = *img++;
+ u8 r = *img++;
+
+ FB_ADDR(fb, x, y) = RGB8_to_565(r, g, b);
+
+ if (++x == w)
+ {
+ x = 0;
+ y--;
+ }
+ }
+}
+
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+void FB_Init(void)
+{
+ pal[COL_BLACK] = RGB8_to_565(0,0,0);
+ pal[COL_WHITE] = RGB8_to_565(255,255,255);
+ pal[COL_RED] = RGB8_to_565(255,0,0);
+ pal[COL_GREEN] = RGB8_to_565(0,255,0);
+ pal[COL_BLUE] = RGB8_to_565(0,0,255);
+ pal[COL_GUISELECT] = RGB8_to_565(128,128,255);
+ pal[COL_GREY] = RGB8_to_565(128,128,128);
+ pal[COL_LIGHTGREY] = RGB8_to_565(200,200,200);
+ pal[COL_DARKGREY] = RGB8_to_565(64,64,64);
+ pal[COL_YELLOW] = RGB8_to_565(255,255,0);
+
+ LoadImage(image + IMG_CITY_PATROL_INLAY, cpatrol_inlay_bgr, 126, 192);
+ LoadImage(image + IMG_3D_MONSTER_MAZE_INLAY, cpatrol_inlay_bgr, 126, 192);
+ LoadImage(image + IMG_SABOTAGE_INLAY, sabotage_inlay_bgr, 126, 192);
+ LoadImage(image + IMG_MAZOGS_INLAY, mazogs_inlay_bgr, 126, 192);
+ LoadImage(image + IMG_KEYBOARD, keyb_bgr, 320, 240);
+ LoadImage(image + IMG_SPLASH, splashimg_bgr, 400, 240);
+}
+
+
+void FB_StartFrame(Framebuffer *upper, Framebuffer *lower)
+{
+ if (upper)
+ {
+ upper->buffer = (u16*)gfxGetFramebuffer(GFX_TOP, GFX_LEFT,
+ &upper->height, &upper->width);
+ }
+
+ if (lower)
+ {
+ lower->buffer = (u16*)gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT,
+ &lower->height, &lower->width);
+ }
+}
+
+
+void FB_EndFrame(void)
+{
+ gfxFlushBuffers();
+ gfxSwapBuffers();
+ gspWaitForVBlank();
+ hidScanInput();
+}
+
+
+void FB_Print(Framebuffer *fb, const char *text, int x, int y,
+ FB_Colour colour, FB_Colour paper)
+{
+ int cx,cy;
+ int ch;
+ u16 fg,bg;
+ u16 fg_trans,bg_trans;
+
+ fg = pal[colour];
+ bg = pal[paper];
+ fg_trans = colour == COL_TRANSPARENT;
+ bg_trans = paper == COL_TRANSPARENT;
+
+ while(*text)
+ {
+ if (*text == 1)
+ {
+ u16 t;
+
+ t = fg;
+ fg = bg;
+ bg = t;
+
+ t = fg_trans;
+ fg_trans = bg_trans;
+ bg_trans = t;
+
+ text++;
+ }
+ else if (*text == '\n')
+ {
+ x = 0;
+ y -= 8;
+
+ text++;
+
+ if (y < -7)
+ {
+ return;
+ }
+ }
+ else
+ {
+ ch=((*text)-32)*8;
+
+ for(cy=0;cy<8;cy++)
+ {
+ for(cx=0;cx<8;cx++)
+ {
+ if (font[ch]&(1<<cx))
+ {
+ if (!fg_trans)
+ {
+ FB_PLOT_CLIPPED(fb, x + cx, y - cy, fg);
+ }
+ }
+ else
+ {
+ if (!bg_trans)
+ {
+ FB_PLOT_CLIPPED(fb, x + cx, y - cy, bg);
+ }
+ }
+ }
+
+ ch++;
+ }
+
+ x+=8;
+ text++;
+
+ if (x > fb->width)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+void FB_Centre(Framebuffer *fb, const char *text, int y,
+ FB_Colour colour, FB_Colour paper)
+{
+ FB_Print(fb, text, fb->width / 2 - strlen(text) * 4, y, colour, paper);
+}
+
+
+void FB_printf(Framebuffer *fb, int x, int y,
+ FB_Colour colour, FB_Colour paper, const char *format, ...)
+{
+ char buff[256];
+ va_list va;
+
+ va_start(va,format);
+ vsnprintf(buff,sizeof buff,format,va);
+ va_end(va);
+
+ FB_Print(fb, buff, x, y, colour, paper);
+}
+
+
+void FB_HLine(Framebuffer *fb, u16 x1, u16 x2, u16 y, FB_Colour colour)
+{
+ if (x1 > x2)
+ {
+ u16 t;
+
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+
+ while(x1<=x2)
+ {
+ FB_PLOT_CLIPPED(fb, x1, y, pal[colour]);
+ x1++;
+ }
+}
+
+
+void FB_VLine(Framebuffer *fb, u16 x, u16 y1, u16 y2, FB_Colour colour)
+{
+ if (y1 > y2)
+ {
+ u16 t;
+
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ while(y1<=y2)
+ {
+ FB_PLOT_CLIPPED(fb, x, y1, pal[colour]);
+ y1++;
+ }
+}
+
+
+void FB_Box(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h, FB_Colour colour)
+{
+ FB_HLine(fb, x, x + w - 1, y, colour);
+ FB_HLine(fb, x, x + w - 1, y - h + 1, colour);
+ FB_VLine(fb, x, y, y - h + 1, colour);
+ FB_VLine(fb, x + w - 1, y, y - h + 1, colour);
+}
+
+
+void FB_FillBox(Framebuffer *fb, u16 x, u16 y, u16 w, u16 h, FB_Colour colour)
+{
+ while(h--)
+ {
+ FB_HLine(fb, x, x + w - 1, y--, colour);
+ }
+}
+
+
+void FB_Clear(Framebuffer *fb, FB_Colour col)
+{
+ u16 *p;
+ int f;
+ u16 c;
+
+ f = fb->width * fb->height;
+ p = fb->buffer;
+ c = pal[col];
+
+ while(f--)
+ {
+ *p++ = c;
+ }
+}
+
+
+void FB_Blit(Framebuffer *fb, FB_Image img, u16 x, u16 y)
+{
+ Framebuffer *src;
+ u16 px,py;
+
+ src = image + img;
+
+ if (fb->width == src->width && fb->height == src->height &&
+ x == 0 && y == 0)
+ {
+ memcpy(fb->buffer, src->buffer, src->width * src->height * 2);
+ }
+ else
+ {
+ for(px = 0; px < src->width; px++)
+ {
+ u16 dx;
+
+ dx = x + px;
+
+ if (dx < fb->width)
+ {
+ u16 dy;
+
+ dy = y;
+
+ for(py = 0; dy < fb->height && py < src->height; py++, dy++)
+ {
+ dy = y + py;
+ FB_ADDR(fb, dx, dy) = FB_ADDR(src, px, py);
+ }
+ }
+ }
+ }
+}
diff --git a/source/gui.c b/source/gui.c
new file mode 100644
index 0000000..eee82f4
--- /dev/null
+++ b/source/gui.c
@@ -0,0 +1,919 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: gui.c 75 2010-11-19 14:52:49Z ianc $
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <3ds.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "framebuffer.h"
+#include "zx81.h"
+#include "keyboard.h"
+#include "config.h"
+
+
+/* ---------------------------------------- PRIVATE INTERFACES - PATH HANDLING
+*/
+#define FSEL_FILENAME_LEN 20
+#define FSEL_LINES 24
+#define FSEL_MAX_FILES 1024
+
+#define FSEL_LIST_Y 10
+#define FSEL_LIST_H FSEL_LINES*8
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct
+{
+ char name[FSEL_FILENAME_LEN+1];
+ int is_dir;
+ int size;
+} FSEL_File;
+
+static FSEL_File fsel[FSEL_MAX_FILES];
+
+
+static void CheckPath(char *path)
+{
+ size_t l;
+
+ l = strlen(path);
+
+ if (l == 1)
+ {
+ path[0] = '/';
+ }
+ else
+ {
+ if (path[l-1] != '/')
+ {
+ path[l] = '/';
+ path[l+1] = 0;
+ }
+ }
+}
+
+
+static void AddPath(char *path, const char *dir)
+{
+ if (strcmp(dir,"..") == 0)
+ {
+ size_t l;
+
+ l = strlen(path);
+
+ if (l > 1)
+ {
+ path[--l] = 0;
+
+ while(l && path[l] != '/')
+ {
+ path[l--] = 0;
+ }
+ }
+ }
+ else
+ {
+ strcat(path,dir);
+ strcat(path,"/");
+ }
+}
+
+
+
+
+static int SortFiles(const void *a, const void *b)
+{
+ const FSEL_File *f1;
+ const FSEL_File *f2;
+
+ f1 = (const FSEL_File *)a;
+ f2 = (const FSEL_File *)b;
+
+ if (strcmp(f1->name, "..") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp(f2->name, "..") == 0)
+ {
+ return 1;
+ }
+ else if (f1->is_dir == f2->is_dir)
+ {
+ return strcasecmp(f1->name, f2->name);
+ }
+ else if (f1->is_dir)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+
+static int ValidFilename(const char *name, int is_dir, const char *filter)
+{
+ size_t l;
+ size_t f;
+
+ l = strlen(name);
+
+ if (l > FSEL_FILENAME_LEN)
+ return 0;
+
+ if (strcmp(name,".") == 0)
+ return 0;
+
+ if (is_dir || !filter)
+ return 1;
+
+ f = strlen(filter);
+
+ if (l > f)
+ {
+ if (strcasecmp(name+l-f,filter) == 0)
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int LoadDir(const char *path, const char *filter)
+{
+ DIR *dir;
+ struct dirent *ent;
+ struct stat st;
+ int no = 0;
+ char whole_path[FILENAME_MAX];
+
+ if ((dir = opendir(path)))
+ {
+ if (strcmp(path, "/") != 0)
+ {
+ strcpy(fsel[no].name, "..");
+ fsel[no].is_dir = TRUE;
+ fsel[no].size = 0;
+ no++;
+ }
+
+ while(no < FSEL_MAX_FILES && (ent = readdir(dir)))
+ {
+ strcpy(whole_path, path);
+ strcat(whole_path, "/");
+ strcat(whole_path, ent->d_name);
+
+ stat(whole_path, &st);
+
+ if (ValidFilename(ent->d_name, (st.st_mode & S_IFDIR), filter))
+ {
+ strcpy(fsel[no].name,ent->d_name);
+ fsel[no].is_dir = (st.st_mode & S_IFDIR);
+ fsel[no].size = (int)st.st_size;
+ no++;
+ }
+ }
+
+ closedir(dir);
+
+ qsort(fsel,no,sizeof fsel[0],SortFiles);
+ }
+
+ return no;
+}
+
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+int GUI_Menu(const char *opts[])
+{
+ int x,y;
+ int h;
+ int w;
+ int no;
+ int sel;
+ int f;
+ int done;
+ int defer;
+ Framebuffer lower;
+
+ w=0;
+ h=0;
+ sel=0;
+ done=FALSE;
+ defer=FALSE;
+
+ for(no=0;opts[no];no++)
+ {
+ h+=16;
+
+ if (strlen(opts[no])>w)
+ {
+ w=strlen(opts[no]);
+ }
+ }
+
+ FB_StartFrame(NULL, &lower);
+
+ w=w*8+16;
+
+ x=lower.width/2-w/2;
+ y=lower.height - 3;
+
+ while(!done)
+ {
+ u32 key=0;
+
+ do
+ {
+ FB_StartFrame(NULL, &lower);
+
+ FB_Clear(&lower, COL_BLACK);
+
+ FB_Box(&lower,x,y,w,h,COL_WHITE);
+ FB_FillBox(&lower,x+1,y-sel*16+1,w-2,14,COL_GUISELECT);
+
+ for(f=0;f<no;f++)
+ {
+ FB_Centre(&lower, opts[f],y-4-f*16,COL_WHITE,COL_TRANSPARENT);
+ }
+
+ FB_EndFrame();
+ } while(!defer && !(key=hidKeysDownRepeat()));
+
+ if (defer)
+ {
+ do
+ {
+ gspWaitForVBlank();
+ hidScanInput();
+ } while (hidKeysHeld()&KEY_TOUCH);
+ done=TRUE;
+ }
+ else
+ {
+ if (key & (KEY_A|KEY_B|KEY_X|KEY_Y))
+ {
+ done=TRUE;
+ }
+ else if ((key & KEY_UP) && sel)
+ {
+ sel--;
+ }
+ else if ((key & KEY_DOWN) && sel<no-1)
+ {
+ sel++;
+ }
+ else if (key & KEY_TOUCH)
+ {
+ touchPosition tp;
+
+ hidTouchRead(&tp);
+
+ if (tp.px>=x && tp.px<(x+w) && tp.py>=3 && tp.py<(3+h))
+ {
+ defer=TRUE;
+ sel=(tp.py-3)/16;
+ }
+ }
+ }
+ }
+
+ return sel;
+}
+
+
+void GUI_Alert(int fatal, const char *text)
+{
+ char line[80];
+ int h;
+ const char *p;
+ char *d;
+ Framebuffer lower;
+
+ FB_StartFrame(NULL, &lower);
+
+ h=40;
+ p=text;
+
+ while(*p)
+ {
+ if (*p++=='\n')
+ {
+ h+=8;
+ }
+ }
+
+ FB_Clear(&lower, COL_BLACK);
+ FB_Box(&lower,0,lower.height-1,lower.width-1,lower.height-1,COL_WHITE);
+
+ p=text;
+ h=lower.height-12;
+ d=line;
+
+ while(*p)
+ {
+ if (*p=='\n')
+ {
+ *d++=0;
+ p++;
+ FB_Centre(&lower,line,h,COL_WHITE,COL_TRANSPARENT);
+ h-=8;
+ d=line;
+ }
+ else
+ {
+ *d++=*p++;
+ }
+ }
+
+ if (d>line)
+ {
+ *d=0;
+ FB_Centre(&lower,line,h,COL_WHITE,COL_TRANSPARENT);
+ h-=8;
+ }
+
+ if (!fatal)
+ {
+ FB_Centre(&lower, "PRESS ANY BUTTON OR SCREEN",h-16,
+ COL_YELLOW,COL_TRANSPARENT);
+
+ FB_EndFrame();
+
+ while(!hidKeysDown())
+ {
+ gspWaitForVBlank();
+ hidScanInput();
+ }
+
+ while(hidKeysHeld())
+ {
+ gspWaitForVBlank();
+ hidScanInput();
+ }
+ }
+ else
+ {
+ FB_Centre(&lower,"PLEASE RESET YOUR CONSOLE",h-16,
+ COL_YELLOW,COL_TRANSPARENT);
+
+ FB_EndFrame();
+
+ while(1)
+ {
+ gspWaitForVBlank();
+ }
+ }
+}
+
+
+void GUI_Config(void)
+{
+ int sel;
+ DS81_ConfigItem f;
+ int done;
+ int save;
+ Framebuffer lower;
+
+ sel = 0;
+ done = FALSE;
+ save = FALSE;
+
+ while(!done)
+ {
+ u32 key=0;
+
+ do
+ {
+ FB_StartFrame(NULL, &lower);
+
+ FB_Clear(&lower,COL_BLACK);
+
+ FB_Centre(&lower,"Up/Down to select",40,
+ COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"A to toggle",32,COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"Or use touchscreen",24,
+ COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"START to finish",16,COL_YELLOW,COL_TRANSPARENT);
+
+ FB_Centre(&lower,"SELECT to finish and save",8,
+ COL_YELLOW,COL_TRANSPARENT);
+
+ for(f=0;f<DS81_NUM_CONFIG_ITEMS;f++)
+ {
+ FB_Print(&lower,ConfigDesc(f),14,lower.height-(20+f*14),
+ COL_WHITE,COL_TRANSPARENT);
+ }
+
+
+ for(f=0;f<DS81_NUM_CONFIG_ITEMS;f++)
+ {
+ FB_FillBox(&lower,2,lower.height-(20+f*14-1),10,10,
+ DS81_Config[f] ? COL_WHITE : COL_BLACK);
+
+ FB_Box(&lower,2,lower.height-(20+f*14-1),10,10,COL_GREY);
+ }
+
+ FB_Box(&lower,0,lower.height-(20+sel*14-3),lower.width-1,14,
+ COL_GUISELECT);
+
+ FB_EndFrame();
+ } while(!(key=hidKeysDownRepeat()));
+
+ if (key & KEY_START)
+ {
+ done=TRUE;
+ }
+ else if (key & KEY_SELECT)
+ {
+ done=TRUE;
+ save=TRUE;
+ }
+ else if (key & KEY_A)
+ {
+ DS81_Config[sel] = !DS81_Config[sel];
+ }
+ else if ((key & KEY_UP) && sel)
+ {
+ sel--;
+ }
+ else if ((key & KEY_DOWN) && sel<DS81_NUM_CONFIG_ITEMS-1)
+ {
+ sel++;
+ }
+ else if (key & KEY_TOUCH)
+ {
+ touchPosition tp;
+ int nsel;
+
+ touchRead(&tp);
+
+ nsel = (tp.py-18)/14;
+
+ if (nsel>=0 && nsel<DS81_NUM_CONFIG_ITEMS)
+ {
+ sel = nsel;
+ DS81_Config[sel] = !DS81_Config[sel];
+ }
+ }
+ }
+
+ if (save)
+ {
+ SaveConfig();
+ }
+}
+
+
+int GUI_FileSelect(char pwd[], char selected_file[], const char *filter)
+{
+ int no;
+ int sel;
+ int top;
+ int bar_size;
+ double bar_step;
+ int done;
+ int ret;
+ FB_Colour paper;
+ int off;
+ int f;
+ int drag;
+ int drag_start;
+ Framebuffer lower;
+
+ CheckPath(pwd);
+ no = LoadDir(pwd,filter);
+
+ sel = 0;
+ top = 0;
+ done = FALSE;
+ ret = FALSE;
+ drag = FALSE;
+ drag_start = 0;
+
+ while(!done)
+ {
+ u32 key=0;
+
+ FB_StartFrame(NULL, &lower);
+
+ FB_Clear(&lower,COL_BLACK);
+
+ FB_printf(&lower,0,lower.height-1,COL_BLACK,COL_LIGHTGREY,
+ "%-32.32s",pwd);
+
+ FB_Centre(&lower,"Use pad and A to select",32,
+ COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"L and R to page up/down",24,
+ COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"Or use touchscreen",16,COL_YELLOW,COL_TRANSPARENT);
+ FB_Centre(&lower,"B to cancel",8,COL_YELLOW,COL_TRANSPARENT);
+
+ if (no<=FSEL_LINES)
+ {
+ bar_step = 0;
+ bar_size = FSEL_LIST_H;
+ }
+ else
+ {
+ bar_step = FSEL_LIST_H/(double)no;
+ bar_size = bar_step*FSEL_LINES;
+ }
+
+ for (f=0;f<FSEL_LINES;f++)
+ {
+ off = f + top;
+
+ if (off<no)
+ {
+ if (off == sel)
+ {
+ paper = COL_GUISELECT;
+ }
+ else
+ {
+ paper = COL_BLACK;
+ }
+
+ FB_printf(&lower,8,lower.height-(FSEL_LIST_Y+f*8),
+ COL_WHITE,paper,
+ "%-*s %s",
+ FSEL_FILENAME_LEN,
+ fsel[off].name,
+ fsel[off].is_dir ? "DIR" : " ");
+ }
+ else
+ {
+ FB_printf(&lower,8,lower.height-(FSEL_LIST_Y+f*8),
+ COL_WHITE,COL_BLACK,
+ "%-*s %s",
+ FSEL_FILENAME_LEN,
+ off==0 ? "No Files!" : "",
+ " ");
+ }
+ }
+
+ FB_FillBox(&lower,240,lower.height-(FSEL_LIST_Y),16,FSEL_LIST_H,
+ COL_DARKGREY);
+ FB_FillBox(&lower,240,lower.height-(FSEL_LIST_Y+top*bar_step),16,
+ bar_size,COL_WHITE);
+
+ FB_EndFrame();
+
+ if (drag)
+ {
+ touchPosition tp = {0};
+ int diff = 0;
+
+ while (((key=keysHeld()) & KEY_TOUCH) && diff == 0)
+ {
+ hidScanInput();
+ touchRead(&tp);
+ diff = tp.py - drag_start;
+ gspWaitForVBlank();
+ }
+
+ if (key & KEY_TOUCH)
+ {
+ int new_top;
+
+ new_top = top + diff / bar_step;
+
+ if (new_top > (no - FSEL_LINES))
+ {
+ new_top = no - FSEL_LINES;
+ }
+
+ if (new_top < 0)
+ {
+ new_top = 0;
+ }
+
+ if (new_top != top)
+ {
+ top = new_top;
+ sel = top;
+ drag_start = tp.py;
+ }
+ }
+ else
+ {
+ drag = FALSE;
+ }
+ }
+
+ if (!drag)
+ {
+ int activate = FALSE;
+
+ key=hidKeysDownRepeat();
+
+ if (key & KEY_TOUCH)
+ {
+ touchPosition tp;
+
+ touchRead(&tp);
+
+ if (tp.py >= FSEL_LIST_Y && tp.py <= (FSEL_LIST_Y+FSEL_LIST_H))
+ {
+ if (tp.px > 239)
+ {
+ drag = TRUE;
+ drag_start = tp.py;
+ }
+ else
+ {
+ int new_sel;
+
+ new_sel = top + (tp.py - FSEL_LIST_Y)/8;
+
+ if (new_sel < no)
+ {
+ if (new_sel == sel)
+ {
+ activate = TRUE;
+ }
+ else
+ {
+ sel = new_sel;
+ }
+ }
+ }
+ }
+ }
+ else if (key & KEY_UP)
+ {
+ if (sel)
+ {
+ sel--;
+
+ if (sel<top)
+ {
+ top--;
+ }
+ }
+ }
+ else if (key & KEY_DOWN)
+ {
+ if (sel < (no-1))
+ {
+ sel++;
+
+ if (sel >= (top+FSEL_LINES))
+ {
+ top++;
+ }
+ }
+ }
+ else if (key & KEY_L)
+ {
+ if (sel)
+ {
+ sel-=FSEL_LINES;
+
+ if (sel < 0)
+ {
+ sel = 0;
+ }
+
+ top = sel;
+ }
+ }
+ else if (key & KEY_R)
+ {
+ if (sel < (no-1))
+ {
+ sel+=FSEL_LINES;
+
+ if (sel > (no-1))
+ {
+ sel = no-1;
+ }
+
+ top = sel - FSEL_LINES + 1;
+
+ if (top < 0)
+ {
+ top = 0;
+ }
+ }
+ }
+ else if (key & KEY_A)
+ {
+ activate = TRUE;
+ }
+ else if (key & KEY_B)
+ {
+ done = TRUE;
+ }
+
+ if (activate)
+ {
+ if (fsel[sel].is_dir)
+ {
+ AddPath(pwd,fsel[sel].name);
+
+ no = LoadDir(pwd,filter);
+
+ sel = 0;
+ top = 0;
+
+ if (no<=FSEL_LINES)
+ {
+ bar_step = 0;
+ bar_size = FSEL_LIST_H;
+ }
+ else
+ {
+ bar_step = FSEL_LIST_H/(double)no;
+ bar_size = bar_step*FSEL_LINES;
+ }
+ }
+ else
+ {
+ done = TRUE;
+ ret = TRUE;
+
+ strcpy(selected_file,pwd);
+ strcat(selected_file,fsel[sel].name);
+ }
+ }
+ }
+ }
+
+ while (hidKeysHeld())
+ {
+ hidScanInput();
+ gspWaitForVBlank();
+ }
+
+ return ret;
+}
+
+
+int GUI_InputName(const char *prompt, const char *ext, char name[], int maxlen)
+{
+ struct
+ {
+ SoftKey key;
+ int ascii;
+ } keymap[] =
+ {
+ {SK_1, '1'},
+ {SK_2, '2'},
+ {SK_3, '3'},
+ {SK_4, '4'},
+ {SK_5, '5'},
+ {SK_6, '6'},
+ {SK_7, '7'},
+ {SK_8, '8'},
+ {SK_9, '9'},
+ {SK_0, '0'},
+ {SK_A, 'A'},
+ {SK_B, 'B'},
+ {SK_C, 'C'},
+ {SK_D, 'D'},
+ {SK_E, 'E'},
+ {SK_F, 'F'},
+ {SK_G, 'G'},
+ {SK_H, 'H'},
+ {SK_I, 'I'},
+ {SK_J, 'J'},
+ {SK_K, 'K'},
+ {SK_L, 'L'},
+ {SK_M, 'M'},
+ {SK_N, 'N'},
+ {SK_O, 'O'},
+ {SK_P, 'P'},
+ {SK_Q, 'Q'},
+ {SK_R, 'R'},
+ {SK_S, 'S'},
+ {SK_T, 'T'},
+ {SK_U, 'U'},
+ {SK_V, 'V'},
+ {SK_W, 'W'},
+ {SK_X, 'X'},
+ {SK_Y, 'Y'},
+ {SK_Z, 'Z'},
+ {0, 0}
+ };
+
+ SoftKeyEvent ev;
+ int done = FALSE;
+ int accept = FALSE;
+ Framebuffer upper;
+
+ name[0] = 0;
+
+ while(!done)
+ {
+ FB_StartFrame(&upper, NULL);
+
+ FB_Clear(&upper, COL_WHITE);
+ SK_DisplayKeyboard();
+
+ FB_printf(&upper, 0, 16, COL_BLACK, COL_TRANSPARENT, "%s", prompt);
+ FB_printf(&upper, 0, 8, COL_BLACK, COL_TRANSPARENT, "\"%s\001L\001\"",
+ name);
+
+ FB_Print(&upper, "PRESS ENTER TO ACCEPT", 0, 32,
+ COL_BLACK, COL_TRANSPARENT);
+ FB_Print(&upper, "PRESS PERIOD TO BACKSPACE", 0, 40,
+ COL_BLACK, COL_TRANSPARENT);
+ FB_Print(&upper, "PRESS SPACE/BREAK TO CANCEL", 0, 48,
+ COL_BLACK, COL_TRANSPARENT);
+
+ FB_EndFrame();
+
+ while(SK_GetBareEvent(&ev))
+ {
+ if (!ev.pressed)
+ {
+ size_t l;
+ int f;
+ int ascii;
+
+ l = strlen(name);
+
+ switch(ev.key)
+ {
+ case SK_PERIOD:
+ if (l)
+ {
+ name[--l] = 0;
+ }
+ break;
+
+ case SK_SPACE:
+ done = TRUE;
+ accept = FALSE;
+ break;
+
+ case SK_NEWLINE:
+ done = TRUE;
+ accept = TRUE;
+ break;
+
+ default:
+ if (l < maxlen)
+ {
+ f = 0;
+ ascii = 0;
+
+ while(!ascii && keymap[f].ascii)
+ {
+ if (ev.key == keymap[f].key)
+ {
+ ascii = keymap[f].ascii;
+ }
+
+ f++;
+ }
+
+ if (ascii)
+ {
+ name[l++] = ascii;
+ name[l] = 0;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return accept;
+}
+
diff --git a/source/keyboard.c b/source/keyboard.c
new file mode 100644
index 0000000..a08b72d
--- /dev/null
+++ b/source/keyboard.c
@@ -0,0 +1,428 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: keyboard.c 64 2008-12-05 00:37:26Z ianc $
+*/
+
+#include <3ds.h>
+
+#include "keyboard.h"
+#include "framebuffer.h"
+#include "stream.h"
+
+/* ---------------------------------------- STATIC DATA
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+static int selection_on = COL_WHITE;
+static int selection_off = COL_BLACK;
+
+static struct
+{
+ int state;
+ int new_state;
+ int handled;
+ int is_sticky;
+} key_state[NUM_SOFT_KEYS];
+
+static SoftKey pad_left_key = SK_5;
+static SoftKey pad_right_key = SK_8;
+static SoftKey pad_up_key = SK_7;
+static SoftKey pad_down_key = SK_6;
+static SoftKey pad_A_key = SK_0;
+static SoftKey pad_B_key = SK_NEWLINE;
+static SoftKey pad_X_key = NUM_SOFT_KEYS;
+static SoftKey pad_Y_key = NUM_SOFT_KEYS;
+static SoftKey pad_R_key = NUM_SOFT_KEYS;
+static SoftKey pad_L_key = NUM_SOFT_KEYS;
+static SoftKey pad_start_key = NUM_SOFT_KEYS;
+static SoftKey pad_select_key = NUM_SOFT_KEYS;
+
+#define CLEAR_STATE(SHORTCUT) \
+ do \
+ { \
+ if (SHORTCUT != NUM_SOFT_KEYS && \
+ !key_state[SHORTCUT].handled) \
+ { \
+ key_state[SHORTCUT].new_state = FALSE; \
+ } \
+ } while(0)
+
+#define CHECK_STATE(KEYS,BIT,CODE,SHORTCUT,USE_SHORTCUT) \
+ do \
+ { \
+ key_state[CODE].new_state = (KEYS & BIT); \
+ if (USE_SHORTCUT && SHORTCUT != NUM_SOFT_KEYS && \
+ !key_state[SHORTCUT].handled && (KEYS & BIT)) \
+ { \
+ key_state[SHORTCUT].new_state = TRUE; \
+ } \
+ } while(0)
+
+#define CELL_WIDTH 32
+#define CELL_HEIGHT 48
+
+static const char *keynames[]=
+{
+ "1", "2", "3", "4", "5",
+ "6", "7", "8", "9", "0",
+ "Q", "W", "E", "R", "T",
+ "Y", "U", "I", "O", "P",
+ "A", "S", "D", "F", "G",
+ "H", "J", "K", "L", "NEWLINE",
+ "SHIFT", "Z", "X", "C", "V",
+ "B", "N", "M", "PERIOD", "SPACE",
+
+ "ABOUT",
+ "CONFIG",
+ "JOYPAD UP",
+ "JOYPAD DOWN",
+ "JOYPAD LEFT",
+ "JOYPAD RIGHT",
+ "A BUTTON",
+ "B BUTTON",
+ "X BUTTON",
+ "Y BUTTON",
+ "RIGHT SHOULDER BUTTON",
+ "LEFT SHOULDER BUTTON",
+ "START BUTTON",
+ "SELECT BUTTON"
+};
+
+/* ---------------------------------------- PRIVATE INTERFACES
+*/
+static SoftKey LocatePress(const touchPosition *p)
+{
+ int kx=0,ky=0;
+ int key = NUM_SOFT_KEYS;
+
+ kx = p->px / CELL_WIDTH;
+ ky = p->py / CELL_HEIGHT;
+
+ key = kx + ky * 10;
+
+ if (key>SK_SPACE)
+ {
+ key = NUM_SOFT_KEYS;
+ }
+
+ return key;
+}
+
+
+static void DrawSelect(Framebuffer *fb, int key, int selected)
+{
+ int x,y;
+
+ x = (key % 10) * CELL_WIDTH;
+ y = (key / 10) * CELL_HEIGHT;
+
+ FB_Box(fb, x, fb->height - 1 - y, CELL_WIDTH, CELL_HEIGHT,
+ selected ? selection_on : selection_off);
+}
+
+
+static int GetEvent(SoftKeyEvent *ev, int map)
+{
+ static SoftKey last = NUM_SOFT_KEYS;
+ static int poll_index = -1;
+ Framebuffer lower;
+
+ FB_StartFrame(NULL, &lower);
+
+ /* Read the keys if this is a new loop
+ */
+ if (poll_index == -1)
+ {
+ int f;
+ u32 keys;
+
+ keys = keysHeld();
+
+ /* Clear the non-sticky keys
+ */
+ for(f=SK_1; f<=SK_CONFIG; f++)
+ {
+ key_state[f].handled = FALSE;
+
+ if (key_state[f].is_sticky)
+ {
+ key_state[f].new_state = key_state[f].state;
+ }
+ else
+ {
+ key_state[f].new_state = FALSE;
+ }
+ }
+
+ /* Check the soft keyboard
+ */
+ if (keys & KEY_TOUCH)
+ {
+ touchPosition tp;
+
+ hidTouchRead(&tp);
+
+ if (tp.py>192)
+ {
+ key_state[SK_CONFIG].new_state = TRUE;
+ }
+ else
+ {
+ SoftKey press;
+
+ press = LocatePress(&tp);
+
+ if (press != NUM_SOFT_KEYS)
+ {
+ key_state[press].handled = TRUE;
+
+ if (key_state[press].is_sticky)
+ {
+ if (last != press)
+ {
+ key_state[press].new_state =
+ !key_state[press].state;
+ }
+ }
+ else
+ {
+ key_state[press].new_state = TRUE;
+ }
+
+ last = press;
+ }
+ }
+ }
+ else
+ {
+ last = NUM_SOFT_KEYS;
+ }
+
+ /* Check non soft-keyboard controls
+ */
+ CHECK_STATE(keys, KEY_A, SK_PAD_A, pad_A_key, map);
+ CHECK_STATE(keys, KEY_B, SK_PAD_B, pad_B_key, map);
+ CHECK_STATE(keys, KEY_X, SK_PAD_X, pad_X_key, map);
+ CHECK_STATE(keys, KEY_Y, SK_PAD_Y, pad_Y_key, map);
+ CHECK_STATE(keys, KEY_R, SK_PAD_R, pad_R_key, map);
+ CHECK_STATE(keys, KEY_L, SK_PAD_L, pad_L_key, map);
+ CHECK_STATE(keys, KEY_START, SK_PAD_START, pad_start_key, map);
+ CHECK_STATE(keys, KEY_SELECT, SK_PAD_SELECT, pad_select_key, map);
+ CHECK_STATE(keys, KEY_UP, SK_PAD_UP, pad_up_key, map);
+ CHECK_STATE(keys, KEY_DOWN, SK_PAD_DOWN, pad_down_key, map);
+ CHECK_STATE(keys, KEY_LEFT, SK_PAD_LEFT, pad_left_key, map);
+ CHECK_STATE(keys, KEY_RIGHT, SK_PAD_RIGHT, pad_right_key, map);
+
+ /* Reset key event poll index
+ */
+ poll_index = 0;
+
+ /* Update any on-screen indicators
+ */
+ for(f=SK_1; f<SK_CONFIG; f++)
+ {
+ if (key_state[f].state != key_state[f].new_state)
+ {
+ DrawSelect(&lower, f, key_state[f].new_state);
+ }
+ }
+ }
+
+ while(poll_index < NUM_SOFT_KEYS &&
+ key_state[poll_index].state == key_state[poll_index].new_state)
+ {
+ poll_index++;
+ }
+
+ if (poll_index < NUM_SOFT_KEYS)
+ {
+ key_state[poll_index].state = key_state[poll_index].new_state;
+
+ ev->key = poll_index;
+ ev->pressed = key_state[poll_index].state;
+
+ return TRUE;
+ }
+ else
+ {
+ poll_index = -1;
+ return FALSE;
+ }
+}
+
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+void SK_DisplayKeyboard(void)
+{
+ int f;
+ Framebuffer lower;
+
+ FB_StartFrame(NULL, &lower);
+
+ FB_Blit(&lower,IMG_KEYBOARD,0,0);
+
+ /* Update any on-screen indicators
+ */
+ for(f=SK_1; f<SK_CONFIG; f++)
+ {
+ if (key_state[f].state)
+ {
+ DrawSelect(&lower, f, key_state[f].state);
+ }
+ }
+}
+
+
+int SK_GetEvent(SoftKeyEvent *ev)
+{
+ return GetEvent(ev,TRUE);
+}
+
+
+int SK_GetBareEvent(SoftKeyEvent *ev)
+{
+ return GetEvent(ev,FALSE);
+}
+
+
+void SK_SetSticky(SoftKey key, int is_sticky)
+{
+ key_state[key].is_sticky = is_sticky;
+
+ if (!is_sticky)
+ {
+ key_state[key].new_state = FALSE;
+ }
+}
+
+
+void SK_DefinePad(SoftKey pad, SoftKey key)
+{
+ switch(pad)
+ {
+ case SK_PAD_LEFT:
+ pad_left_key = key;
+ break;
+ case SK_PAD_RIGHT:
+ pad_right_key = key;
+ break;
+ case SK_PAD_UP:
+ pad_up_key = key;
+ break;
+ case SK_PAD_DOWN:
+ pad_down_key = key;
+ break;
+ case SK_PAD_A:
+ pad_A_key = key;
+ break;
+ case SK_PAD_B:
+ pad_B_key = key;
+ break;
+ case SK_PAD_X:
+ pad_X_key = key;
+ break;
+ case SK_PAD_Y:
+ pad_Y_key = key;
+ break;
+ case SK_PAD_R:
+ pad_R_key = key;
+ break;
+ case SK_PAD_L:
+ pad_L_key = key;
+ break;
+ case SK_PAD_START:
+ pad_start_key = key;
+ break;
+ case SK_PAD_SELECT:
+ pad_select_key = key;
+ break;
+ default:
+ break;
+ }
+}
+
+
+const char *SK_KeyName(SoftKey k)
+{
+ return keynames[k];
+}
+
+
+void SK_SaveSnapshot(FILE *fp)
+{
+ int f;
+
+ PUT_Long(fp, pad_left_key);
+ PUT_Long(fp, pad_right_key);
+ PUT_Long(fp, pad_up_key);
+ PUT_Long(fp, pad_down_key);
+ PUT_Long(fp, pad_A_key);
+ PUT_Long(fp, pad_B_key);
+ PUT_Long(fp, pad_X_key);
+ PUT_Long(fp, pad_Y_key);
+ PUT_Long(fp, pad_R_key);
+ PUT_Long(fp, pad_L_key);
+ PUT_Long(fp, pad_start_key);
+ PUT_Long(fp, pad_select_key);
+
+ for(f = 0; f < NUM_SOFT_KEYS; f++)
+ {
+ PUT_Long(fp, key_state[f].state);
+ PUT_Long(fp, key_state[f].new_state);
+ PUT_Long(fp, key_state[f].handled);
+ PUT_Long(fp, key_state[f].is_sticky);
+ }
+}
+
+
+void SK_LoadSnapshot(FILE *fp)
+{
+ int f;
+
+ pad_left_key = GET_Long(fp);
+ pad_right_key = GET_Long(fp);
+ pad_up_key = GET_Long(fp);
+ pad_down_key = GET_Long(fp);
+ pad_A_key = GET_Long(fp);
+ pad_B_key = GET_Long(fp);
+ pad_X_key = GET_Long(fp);
+ pad_Y_key = GET_Long(fp);
+ pad_R_key = GET_Long(fp);
+ pad_L_key = GET_Long(fp);
+ pad_start_key = GET_Long(fp);
+ pad_select_key = GET_Long(fp);
+
+ for(f = 0; f < NUM_SOFT_KEYS; f++)
+ {
+ key_state[f].state = GET_Long(fp);
+ key_state[f].new_state = GET_Long(fp);
+ key_state[f].handled = GET_Long(fp);
+ key_state[f].is_sticky = GET_Long(fp);
+ }
+}
+
+
diff --git a/source/main.c b/source/main.c
new file mode 100644
index 0000000..b41efe6
--- /dev/null
+++ b/source/main.c
@@ -0,0 +1,358 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: main.c 77 2010-11-23 08:10:25Z ianc $
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <3ds.h>
+
+#include "framebuffer.h"
+#include "gui.h"
+#include "keyboard.h"
+#include "z80.h"
+#include "zx81.h"
+#include "tapes.h"
+#include "config.h"
+#include "snapshot.h"
+
+#ifndef DS81_VERSION
+#define DS81_VERSION "DEV " __TIME__ "/" __DATE__
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define DEBUG(x) \
+ do \
+ { \
+ printf(x); \
+ gfxFlushBuffers(); \
+ gfxSwapBuffers(); \
+ } while(0); \
+ do \
+ { \
+ hidScanInput(); \
+ } while(!(hidKeysDown() & KEY_TOUCH)) \
+
+/* ---------------------------------------- STATIC DATA
+*/
+static const char *main_menu[]=
+ {
+ "Reset ZX81",
+ "Select Tape",
+ "Configure",
+ "Map Joypad to Keys",
+ "Save Memory Snapshot",
+ "Load Memory Snapshot",
+ "Save Joypad/Key State",
+ "Load Joypad/Key State",
+ "Exit 3DS81",
+ "Cancel",
+ NULL
+ };
+
+typedef enum
+{
+ MenuReset,
+ MenuSelectTape,
+ MenuConfigure,
+ MenuMapJoypad,
+ MenuSaveSnapshot,
+ MenuLoadSnapshot,
+ MenuSaveMappings,
+ MenuLoadMappings,
+ MenuExit
+} MenuOpt;
+
+
+/* ---------------------------------------- DISPLAY FUNCS
+*/
+static void Splash(void)
+{
+ static char scroller[]=
+ {
+ " "
+ "Welcome to 3DS81, a ZX81 emulator for the Ninetendo 3DS. "
+ "You can safely ignore this message. I was just bored for half an "
+ "hour. And no retro game is complete without a side-scroller... "
+ "Thanks to Slay Radio, Ladytron, the Genki Rockets, the High "
+ "Voltage SID Collection, The Prodigy, Paradise Lost and "
+ "Retro Gamer for coding fuel."
+ };
+
+ static const char *text[]=
+ {
+ "3DS81 \177 2021 Ian Cowburn",
+ " ",
+ "ZX81 ROM \177 1981",
+ "Nine Tiles Networks LTD",
+ " ",
+ "PRESS A TO CONTINUE",
+ " ",
+ "https://noddybox.co.uk/",
+ " ",
+ "If you place .P tape files in",
+ "the top directory or ZX81SNAP",
+ "then you should be able to load",
+ "GAME.P with the command",
+ "LOAD \"GAME\"",
+ NULL
+ };
+
+ int f;
+ int y;
+ int scr_x = 0;
+ int scr_y = 240;
+ Framebuffer upper;
+ Framebuffer lower;
+
+ ZX81EnableFileSystem(TRUE);
+ SNAP_Enable(TRUE);
+
+ while(!(hidKeysDown() & KEY_A))
+ {
+ FB_StartFrame(&upper, &lower);
+
+ FB_Clear(&upper, COL_BLACK);
+ FB_Clear(&lower, COL_BLACK);
+
+ FB_Blit(&upper, IMG_SPLASH, 0, scr_y);
+
+ FB_Print(&upper, "10 REM VERSION \001" DS81_VERSION "\001\n"
+ "20 PRINT \"\001THE ZX81 IS ACE\001\"\n"
+ "30 GOTO 20",
+ 0, 230, COL_WHITE, COL_TRANSPARENT);
+
+ FB_printf(&lower, scr_x, 8, COL_WHITE, COL_TRANSPARENT,
+ "%-42.42s",scroller);
+
+ y = lower.height - 20;
+
+ for(f=0;text[f];f++)
+ {
+ FB_Centre(&lower, text[f], y, COL_WHITE, COL_TRANSPARENT);
+ y -= 8;
+ }
+
+ FB_EndFrame();
+
+ if (scr_y > 0)
+ {
+ scr_y--;
+ }
+
+ if (--scr_x == -8)
+ {
+ size_t l = sizeof scroller;
+ char c;
+
+ scr_x = 0;
+
+ c = scroller[0];
+ memmove(scroller, scroller+1, l-2);
+ scroller[l-2] = c;
+ }
+ }
+}
+
+
+/* ---------------------------------------- JOYPAD MAPPING
+*/
+static void MapJoypad(void)
+{
+ SoftKeyEvent ev;
+ SoftKey pad = NUM_SOFT_KEYS;
+ int done = FALSE;
+ Framebuffer upper;
+ Framebuffer lower;
+
+ while(!done)
+ {
+ FB_StartFrame(&upper, &lower);
+
+ SK_DisplayKeyboard();
+
+ FB_Clear(&upper, COL_WHITE);
+
+ FB_Print(&upper, "Press the joypad button you want\n"
+ "to define and then the ZX81 key\n"
+ "you want to use.\n\n"
+ "Press CONFIG to finish.",
+ 0, upper.height - 40, COL_BLACK,COL_TRANSPARENT);
+
+ if (pad != NUM_SOFT_KEYS)
+ {
+ FB_printf(&upper, 0, 80, COL_BLACK, COL_TRANSPARENT,
+ "defining\n \001%s\001",SK_KeyName(pad));
+ }
+
+ FB_EndFrame();
+
+ while(SK_GetBareEvent(&ev))
+ {
+ if (ev.pressed)
+ {
+ if (ev.key==SK_ABOUT || ev.key==SK_CONFIG)
+ {
+ done = true;
+ }
+ }
+ else
+ {
+ if (ev.key>=SK_PAD_UP && ev.key<=SK_PAD_SELECT)
+ {
+ pad = ev.key;
+ }
+
+ if (ev.key<=SK_SPACE && pad!=NUM_SOFT_KEYS)
+ {
+ SK_DefinePad(pad,ev.key);
+ pad = NUM_SOFT_KEYS;
+ }
+ }
+ }
+ }
+}
+
+
+/* ---------------------------------------- MAIN
+*/
+int main(int argc, char *argv[])
+{
+ Z80 *z80;
+ int quit = FALSE;
+
+ gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, false);
+ consoleInit(GFX_TOP, NULL);
+
+ FB_Init();
+
+ z80 = Z80Init(ZX81ReadMem,
+ ZX81WriteMem,
+ ZX81ReadPort,
+ ZX81WritePort,
+ ZX81ReadDisassem);
+
+ if (!z80)
+ {
+ GUI_Alert(TRUE,"Failed to initialise\nthe Z80 CPU emulation!");
+ }
+
+ ZX81Init(z80);
+
+ Splash();
+
+ LoadConfig();
+ ZX81Reconfigure();
+
+ SK_DisplayKeyboard();
+
+ SK_SetSticky(SK_SHIFT,DS81_Config[DS81_STICKY_SHIFT]);
+
+ if (DS81_Config[DS81_LOAD_DEFAULT_SNAPSHOT])
+ {
+ SNAP_Load(z80, "AUTO", SNAP_TYPE_FULL);
+ }
+
+ while(!quit && aptMainLoop())
+ {
+ SoftKeyEvent ev;
+ Framebuffer upper;
+ Framebuffer lower;
+
+ FB_StartFrame(&upper, &lower);
+
+ SK_DisplayKeyboard();
+
+ Z80Exec(z80);
+
+ ZX81RenderDisplay(&upper);
+
+ FB_EndFrame();
+
+ while(SK_GetEvent(&ev))
+ {
+ switch(ev.key)
+ {
+ case SK_ABOUT:
+ case SK_CONFIG:
+ if (ev.pressed)
+ {
+ switch(GUI_Menu(main_menu))
+ {
+ case MenuReset:
+ ZX81Reset(z80);
+ break;
+
+ case MenuSelectTape:
+ SelectTape();
+ break;
+
+ case MenuConfigure:
+ GUI_Config();
+ SK_SetSticky(SK_SHIFT,
+ DS81_Config[DS81_STICKY_SHIFT]);
+ ZX81Reconfigure();
+ break;
+
+ case MenuMapJoypad:
+ MapJoypad();
+ break;
+
+ case MenuSaveSnapshot:
+ SNAP_Save(z80, SNAP_TYPE_FULL);
+ break;
+
+ case MenuLoadSnapshot:
+ SNAP_Load(z80, NULL, SNAP_TYPE_FULL);
+ break;
+
+ case MenuSaveMappings:
+ SNAP_Save(z80, SNAP_TYPE_KEYBOARD);
+ break;
+
+ case MenuLoadMappings:
+ SNAP_Load(z80, NULL, SNAP_TYPE_KEYBOARD);
+ break;
+
+ case MenuExit:
+ quit = TRUE;
+ break;
+ }
+ }
+ break;
+
+ default:
+ ZX81HandleKey(ev.key,ev.pressed);
+ break;
+ }
+ }
+ }
+
+ gfxExit();
+
+ return 0;
+}
diff --git a/source/snapshot.c b/source/snapshot.c
new file mode 100644
index 0000000..b64d309
--- /dev/null
+++ b/source/snapshot.c
@@ -0,0 +1,192 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ Provides the routines for snapshotting.
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <3ds.h>
+
+#include "snapshot.h"
+#include "zx81.h"
+#include "gui.h"
+
+#include "config.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/* ---------------------------------------- STATICS
+*/
+static int enabled;
+static const char *magic = "V01_DS81";
+static const char *extension[2] = {".D81", ".K81"};
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+static void WriteMagic(FILE *fp, SnapshotType t)
+{
+ const char *p = magic;
+
+ while(*p)
+ {
+ fputc(*p++, fp);
+ }
+
+ fputc(t, fp);
+}
+
+static int CheckMagic(FILE *fp, SnapshotType t)
+{
+ const char *p = magic;
+
+ while(*p)
+ {
+ if (fgetc(fp) != *p++)
+ {
+ return FALSE;
+ }
+ }
+
+ return (fgetc(fp) == t);
+}
+
+
+/* ---------------------------------------- EXPORTED INTERFACES
+*/
+void SNAP_Enable(int enable)
+{
+ enabled = enable;
+}
+
+void SNAP_Save(Z80 *cpu, SnapshotType type)
+{
+ char base[FILENAME_MAX] = "";
+ char file[FILENAME_MAX];
+ FILE *fp = NULL;
+
+ if (!enabled)
+ {
+ return;
+ }
+
+ if(!GUI_InputName("Enter snapshot filename",
+ extension[type], base, 8) || !base[0])
+ {
+ return;
+ }
+
+ strcat(base, extension[type]);
+
+ strcpy(file, DEFAULT_SNAPDIR);
+ strcat(file, base);
+
+ fp = fopen(file, "wb");
+
+ if (!fp)
+ {
+ fp = fopen(base, "wb");
+ }
+
+ if (fp)
+ {
+ WriteMagic(fp, type);
+
+ SK_SaveSnapshot(fp);
+
+ if (type == SNAP_TYPE_FULL)
+ {
+ Z80SaveSnapshot(cpu, fp);
+ ZX81SaveSnapshot(fp);
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ GUI_Alert(FALSE, "Failed to save snapshot");
+ }
+}
+
+void SNAP_Load(Z80 *cpu, const char *optional_name, SnapshotType type)
+{
+ static char last_dir[FILENAME_MAX] = "/";
+ char file[FILENAME_MAX];
+ FILE *fp = NULL;
+
+ if (!enabled)
+ {
+ return;
+ }
+
+ if (optional_name)
+ {
+ strcpy(file, DEFAULT_SNAPDIR);
+ strcat(file, optional_name);
+ strcat(file, extension[type]);
+
+ fp = fopen(file, "rb");
+
+ if (!fp)
+ {
+ strcpy(file, optional_name);
+ strcat(file, extension[type]);
+
+ fp = fopen(file, "rb");
+ }
+ }
+ else
+ {
+ if (GUI_FileSelect(last_dir, file, extension[type]))
+ {
+ fp = fopen(file, "rb");
+ }
+ }
+
+ if (fp)
+ {
+ if (!CheckMagic(fp, type))
+ {
+ GUI_Alert(FALSE, "Not a valid snapshot");
+ }
+ else
+ {
+ SK_LoadSnapshot(fp);
+
+ if (type == SNAP_TYPE_FULL)
+ {
+ Z80LoadSnapshot(cpu, fp);
+ ZX81LoadSnapshot(fp);
+ }
+ }
+
+ fclose(fp);
+ }
+}
diff --git a/source/stream.c b/source/stream.c
new file mode 100644
index 0000000..897fa62
--- /dev/null
+++ b/source/stream.c
@@ -0,0 +1,85 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ Provides the routines for streaming.
+
+*/
+#include "stream.h"
+
+/* The long functions are a tad convuluted, but I'm in a dash.
+*/
+
+void PUT_Byte(FILE *fp, unsigned char c)
+{
+ fputc(c, fp);
+}
+
+void PUT_Long(FILE *fp, long l)
+{
+ union {long l; unsigned char c[4];} u;
+
+ u.l = l;
+
+ fputc(u.c[0], fp);
+ fputc(u.c[1], fp);
+ fputc(u.c[2], fp);
+ fputc(u.c[3], fp);
+}
+
+void PUT_ULong(FILE *fp, unsigned long l)
+{
+ union {unsigned long l; unsigned char c[4];} u;
+
+ u.l = l;
+
+ fputc(u.c[0], fp);
+ fputc(u.c[1], fp);
+ fputc(u.c[2], fp);
+ fputc(u.c[3], fp);
+}
+
+unsigned char GET_Byte(FILE *fp)
+{
+ return (unsigned char)fgetc(fp);
+}
+
+long GET_Long(FILE *fp)
+{
+ union {long l; unsigned char c[4];} u;
+
+ u.c[0] = (unsigned char)fgetc(fp);
+ u.c[1] = (unsigned char)fgetc(fp);
+ u.c[2] = (unsigned char)fgetc(fp);
+ u.c[3] = (unsigned char)fgetc(fp);
+
+ return u.l;
+}
+
+unsigned long GET_ULong(FILE *fp)
+{
+ union {unsigned long l; unsigned char c[4];} u;
+
+ u.c[0] = (unsigned char)fgetc(fp);
+ u.c[1] = (unsigned char)fgetc(fp);
+ u.c[2] = (unsigned char)fgetc(fp);
+ u.c[3] = (unsigned char)fgetc(fp);
+
+ return u.l;
+}
diff --git a/source/tapes.c b/source/tapes.c
new file mode 100644
index 0000000..98652d0
--- /dev/null
+++ b/source/tapes.c
@@ -0,0 +1,258 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator.
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ $Id: tapes.c 61 2008-11-03 17:07:32Z ianc $
+*/
+
+#include <3ds.h>
+
+#include "tapes.h"
+#include "framebuffer.h"
+#include "keyboard.h"
+#include "zx81.h"
+
+#include "maze_bin.h"
+#include "cpatrol_bin.h"
+#include "sabotage_bin.h"
+#include "mazogs_bin.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/* ---------------------------------------- STATIC DATA
+*/
+typedef struct
+{
+ const u8 *tape;
+ const u8 *tape_end;
+ FB_Image img;
+ SoftKey *keys;
+ const char *text;
+} Tape;
+
+#define NO_TAPES 4
+
+static SoftKey maze_keys[]=
+ {
+ SK_PAD_UP, SK_7,
+ SK_PAD_LEFT, SK_5,
+ SK_PAD_RIGHT, SK_8,
+ SK_PAD_START, SK_C,
+ SK_PAD_SELECT, SK_A,
+ NUM_SOFT_KEYS
+ };
+
+static SoftKey cpatrol_keys[]=
+ {
+ SK_PAD_UP, SK_F,
+ SK_PAD_RIGHT, SK_J,
+ SK_PAD_LEFT, SK_N,
+ SK_PAD_DOWN, SK_V,
+ SK_PAD_R, SK_N,
+ SK_PAD_L, SK_J,
+ SK_PAD_A, SK_0,
+ SK_PAD_B, SK_NEWLINE,
+ NUM_SOFT_KEYS
+ };
+
+static SoftKey sabotage_keys[]=
+ {
+ SK_PAD_UP, SK_W,
+ SK_PAD_LEFT, SK_H,
+ SK_PAD_RIGHT, SK_J,
+ SK_PAD_DOWN, SK_S,
+ SK_PAD_A, SK_E,
+ SK_PAD_R, SK_1,
+ SK_PAD_L, SK_2,
+ SK_PAD_START, SK_0,
+ NUM_SOFT_KEYS
+ };
+
+static SoftKey mazogs_keys[]=
+ {
+ SK_PAD_UP, SK_W,
+ SK_PAD_LEFT, SK_H,
+ SK_PAD_RIGHT, SK_J,
+ SK_PAD_DOWN, SK_S,
+ SK_PAD_A, SK_NEWLINE,
+ SK_PAD_R, SK_R,
+ SK_PAD_L, SK_L,
+ SK_PAD_START, SK_Y,
+ SK_PAD_SELECT, SK_V,
+ NUM_SOFT_KEYS
+ };
+
+static Tape tapes[NO_TAPES]=
+ {
+ {
+ maze_bin,
+ maze_bin_end,
+ IMG_3D_MONSTER_MAZE_INLAY,
+ maze_keys,
+ "\0013D Monster maze\001\n"
+ "(c) 1983 Malcolm E. Evans\n\n"
+ "Escape the maze and its T-Rex\n\n"
+ "Use joypad for turning and to\n"
+ "move forward.\n"
+ "\001START\001 to start.\n"
+ "\001SELECT\001 to appeal.\n\n"
+ "\001Note\001 when the screen goes grey\n"
+ "this is not a problem - the game\n"
+ "is creating the maze."
+ },
+ {
+ mazogs_bin,
+ mazogs_bin_end,
+ IMG_MAZOGS_INLAY,
+ mazogs_keys,
+ "\001Mazogs\001\n"
+ "(c) 1981 Don Priestley\n\n"
+ "Find the treasure and\n"
+ "return to the start.\n"
+ "Avoid the \001Mazogs\001 that roam\n"
+ "the maze.\n\n"
+ "Use joypad to move.\n"
+ "\001SELECT\001 to view map.\n"
+ "\001START\001 to quit.\n"
+ "\001L\001 or \001R\001 shoulder to select\n"
+ "direction at start."
+ },
+ {
+ cpatrol_bin,
+ cpatrol_bin_end,
+ IMG_CITY_PATROL_INLAY,
+ cpatrol_keys,
+ "\001City Patrol\001\n"
+ "(c) 1982 Don Priestley\n\n"
+ "Defend the city from the aliens.\n\n"
+ "yes - that parallax city was\n"
+ "done with a text mode and the\n"
+ "equivalent of a 0.8mhz z80\n\n"
+ "the joypad controls the cursor.\n"
+ "hold \001L\001 or \001R\001 shoulder buttons\n"
+ "to move fast when moving in the\n"
+ "same direction.\n\n"
+ "\001A\001 fires when moving.\n"
+ "\001B\001 fires when still.\n\n"
+ "sorry about that, but the keys\n"
+ "are a bit odd in this game."
+ },
+ {
+ sabotage_bin,
+ sabotage_bin_end,
+ IMG_SABOTAGE_INLAY,
+ sabotage_keys,
+ "\001Sabotage\001\n"
+ "(c) 1982 Don Priestley\n\n"
+ "Destroy the boxes before the\n"
+ "guard finds you.\n\n"
+ "Or find the saboteur as the\n"
+ "guard.\n\n"
+ "While this game may not feature\n"
+ "the dazzling graphics of other\n"
+ "ZX81 games it more than makes\n"
+ "up with a simply joyous\n"
+ "gameplay mechanic.\n\n"
+ "The joypad controls the player.\n"
+ "\001A\001 plants a bomb. \001L\001 shoulder\n"
+ "to play as the guard, \001R\001 as\n"
+ "the saboteur."
+ }
+ };
+
+
+static int current=0;
+
+/* ---------------------------------------- PRIVATE INTERFACES
+*/
+static void DisplayTape(Tape *t, Framebuffer *upper, Framebuffer *lower)
+{
+ FB_Clear(upper,COL_WHITE);
+ FB_Clear(lower,COL_BLACK);
+
+ FB_Blit(lower,t->img,lower->width-195,0);
+
+ FB_Print(lower,"LEFT/RIGHT",0,lower->height-1,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"to choose",0,lower->height-10,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"A to select",0,lower->height-30,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"B to cancel",0,lower->height-40,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"REMEMBER TO",0,lower->height-60,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"LOAD \"\"",0,lower->height-70,COL_WHITE,COL_TRANSPARENT);
+ FB_Print(lower,"ON THE ZX81!",0,lower->height-80,COL_WHITE,COL_TRANSPARENT);
+
+ FB_Print(upper, t->text, 0, upper->height-1, COL_BLACK,COL_TRANSPARENT);
+}
+
+/* ---------------------------------------- PUBLIC INTERFACES
+*/
+void SelectTape(void)
+{
+ int done=FALSE;
+ Framebuffer upper;
+ Framebuffer lower;
+
+ while(!done)
+ {
+ u32 key=0;
+
+
+ do
+ {
+ FB_StartFrame(&upper, &lower);
+ DisplayTape(tapes+current, &upper, &lower);
+ FB_EndFrame();
+ } while(!(key=hidKeysDownRepeat()));
+
+ if (key & KEY_LEFT)
+ {
+ if (--current<0)
+ {
+ current=NO_TAPES-1;
+ }
+ }
+ else if (key & KEY_RIGHT)
+ {
+ current=(current+1)%NO_TAPES;
+ }
+ else if (key & KEY_A)
+ {
+ int f;
+
+ done=TRUE;
+ ZX81SetTape(tapes[current].tape,
+ tapes[current].tape_end - tapes[current].tape);
+
+ for(f=0;tapes[current].keys[f]!=NUM_SOFT_KEYS;f+=2)
+ {
+ SK_DefinePad(tapes[current].keys[f],
+ tapes[current].keys[f+1]);
+ }
+ }
+ else if (key & KEY_B)
+ {
+ done=TRUE;
+ }
+ }
+}
+
diff --git a/source/z80.c b/source/z80.c
new file mode 100644
index 0000000..e5f6c92
--- /dev/null
+++ b/source/z80.c
@@ -0,0 +1,389 @@
+/*
+
+ z80 - Z80 Emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80.c 64 2008-12-05 00:37:26Z ianc $
+
+ Z80
+
+*/
+#include <stdlib.h>
+#include <string.h>
+
+#include "z80.h"
+#include "z80_private.h"
+
+#include "stream.h"
+
+Z80Label *z80_labels=NULL;
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+static void InitTables()
+{
+ static int init=FALSE;
+
+ if (init)
+ return;
+
+ init=TRUE;
+
+ Z80_InitialiseInternals();
+}
+
+static void Z80_CheckInterrupt(Z80 *cpu)
+{
+ /* Check interrupts
+ */
+ if (PRIV->raise)
+ {
+ if (PRIV->nmi)
+ {
+ if (PRIV->halt)
+ {
+ PRIV->halt=FALSE;
+ CALLBACK(eZ80_Halt,0);
+ cpu->PC++;
+ }
+
+ TSTATE(2);
+ cpu->IFF1=0;
+ PRIV->nmi=FALSE;
+ PUSH(cpu->PC);
+ cpu->PC=0x66;
+ }
+ else if (cpu->IFF1)
+ {
+ if (PRIV->halt)
+ {
+ PRIV->halt=FALSE;
+ CALLBACK(eZ80_Halt,0);
+ cpu->PC++;
+ }
+
+ TSTATE(2);
+
+ cpu->IFF1=0;
+ cpu->IFF2=0;
+
+ switch(cpu->IM)
+ {
+ default:
+ case 0:
+ INC_R;
+ Z80_Decode(cpu,PRIV->devbyte);
+ return;
+ break;
+
+ case 1:
+ PUSH(cpu->PC);
+ cpu->PC=0x38;
+ break;
+
+ case 2:
+ PUSH(cpu->PC);
+ cpu->PC=(Z80Word)cpu->I*256+PRIV->devbyte;
+ break;
+ }
+ }
+
+ PRIV->raise=FALSE;
+ }
+}
+
+
+/* ---------------------------------------- INTERFACES
+*/
+
+#ifdef ENABLE_ARRAY_MEMORY
+Z80 *Z80Init(Z80ReadPort read_port,
+ Z80WritePort write_port)
+#else
+Z80 *Z80Init(Z80ReadMemory read_memory,
+ Z80WriteMemory write_memory,
+ Z80ReadPort read_port,
+ Z80WritePort write_port,
+ Z80ReadMemory read_for_disassem)
+#endif
+{
+ Z80 *cpu;
+ int f;
+ int r;
+
+ InitTables();
+
+#ifndef ENABLE_ARRAY_MEMORY
+ if (!read_memory || !write_memory)
+ return NULL;
+#endif
+
+ cpu=malloc(sizeof *cpu);
+
+ if (cpu)
+ {
+ cpu->priv=malloc(sizeof *cpu->priv);
+
+ if (cpu->priv)
+ {
+#ifndef ENABLE_ARRAY_MEMORY
+ PRIV->mread=read_memory;
+ PRIV->mwrite=write_memory;
+ PRIV->disread=read_for_disassem;
+#endif
+ PRIV->pread=read_port;
+ PRIV->pwrite=write_port;
+
+ for(f=0;f<eZ80_NO_CALLBACK;f++)
+ for(r=0;r<MAX_PER_CALLBACK;r++)
+ PRIV->callback[f][r]=NULL;
+
+ Z80Reset(cpu);
+ }
+ else
+ {
+ free(cpu);
+ cpu=NULL;
+ }
+ }
+
+ return cpu;
+}
+
+
+void Z80Reset(Z80 *cpu)
+{
+ PRIV->cycle=0;
+ cpu->PC=0;
+
+ cpu->AF.w=0xffff;
+ cpu->BC.w=0xffff;
+ cpu->DE.w=0xffff;
+ cpu->HL.w=0xffff;
+ cpu->AF_=0xffff;
+ cpu->BC_=0xffff;
+ cpu->DE_=0xffff;
+ cpu->HL_=0xffff;
+
+ cpu->IX.w=0xffff;
+ cpu->IY.w=0xffff;
+
+ cpu->SP=0xffff;
+ cpu->IFF1=0;
+ cpu->IFF2=0;
+ cpu->IM=0;
+ cpu->I=0;
+ cpu->R=0;
+ PRIV->halt=0;
+
+ PRIV->raise=FALSE;
+ PRIV->nmi=FALSE;
+}
+
+
+Z80Val Z80Cycles(Z80 *cpu)
+{
+ return PRIV->cycle;
+}
+
+
+void Z80ResetCycles(Z80 *cpu, Z80Val cycles)
+{
+ PRIV->cycle=cycles;
+}
+
+
+int Z80LodgeCallback(Z80 *cpu, Z80CallbackReason reason, Z80Callback callback)
+{
+ int f;
+
+ for(f=0;f<MAX_PER_CALLBACK;f++)
+ {
+ if (!PRIV->callback[reason][f])
+ {
+ PRIV->callback[reason][f]=callback;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+void Z80RemoveCallback(Z80 *cpu, Z80CallbackReason reason, Z80Callback callback)
+{
+ int f;
+
+ for(f=0;f<MAX_PER_CALLBACK;f++)
+ {
+ if (PRIV->callback[reason][f]==callback)
+ {
+ PRIV->callback[reason][f]=NULL;
+ }
+ }
+}
+
+
+void Z80Interrupt(Z80 *cpu, Z80Byte devbyte)
+{
+ PRIV->raise=TRUE;
+ PRIV->devbyte=devbyte;
+ PRIV->nmi=FALSE;
+}
+
+
+void Z80NMI(Z80 *cpu)
+{
+ PRIV->raise=TRUE;
+ PRIV->nmi=TRUE;
+}
+
+
+int Z80SingleStep(Z80 *cpu)
+{
+ Z80Byte opcode;
+
+ PRIV->last_cb=TRUE;
+ PRIV->shift=0;
+
+ Z80_CheckInterrupt(cpu);
+
+ CALLBACK(eZ80_Instruction,PRIV->cycle);
+
+ INC_R;
+
+ opcode=FETCH_BYTE;
+
+ Z80_Decode(cpu,opcode);
+
+ return PRIV->last_cb;
+}
+
+
+void Z80Exec(Z80 *cpu)
+{
+ while (Z80SingleStep(cpu));
+}
+
+
+void Z80SetLabels(Z80Label labels[])
+{
+ z80_labels=labels;
+}
+
+
+const char *Z80Disassemble(Z80 *cpu, Z80Word *pc)
+{
+#ifdef ENABLE_DISASSEM
+ Z80Byte Z80_Dis_FetchByte(Z80 *cpu, Z80Word *pc);
+ static char s[80];
+ Z80Word opc,npc;
+ Z80Byte op;
+ int f;
+
+ opc=*pc;
+ op=Z80_Dis_FetchByte(cpu,pc);
+ dis_opcode_z80[op](cpu,op,pc);
+ npc=*pc;
+
+ strcpy(s,Z80_Dis_Printf("%-5s",Z80_Dis_GetOp()));
+ strcat(s,Z80_Dis_Printf("%-40s ;",Z80_Dis_GetArg()));
+
+ for(f=0;f<5 && opc!=npc;f++)
+ {
+#ifdef ENABLE_ARRAY_MEMORY
+ strcat(s,Z80_Dis_Printf(" %.2x",(int)Z80_MEMORY[opc++]));
+#else
+ strcat(s,Z80_Dis_Printf(" %.2x",(int)PRIV->disread(cpu,opc++)));
+#endif
+ }
+
+ if (opc!=npc)
+ for(f=1;f<3;f++)
+ s[strlen(s)-f]='.';
+
+ return s;
+#else
+ (*pc)+=4;
+ return "NO DISASSEMBLER";
+#endif
+}
+
+
+void Z80SaveSnapshot(Z80 *cpu, FILE *fp)
+{
+ PUT_ULong(fp, cpu->PC);
+ PUT_ULong(fp, cpu->AF.w);
+ PUT_ULong(fp, cpu->BC.w);
+ PUT_ULong(fp, cpu->DE.w);
+ PUT_ULong(fp, cpu->HL.w);
+ PUT_ULong(fp, cpu->AF_);
+ PUT_ULong(fp, cpu->BC_);
+ PUT_ULong(fp, cpu->DE_);
+ PUT_ULong(fp, cpu->HL_);
+ PUT_ULong(fp, cpu->IX.w);
+ PUT_ULong(fp, cpu->IY.w);
+ PUT_ULong(fp, cpu->SP);
+ PUT_Byte(fp, cpu->IFF1);
+ PUT_Byte(fp, cpu->IFF2);
+ PUT_Byte(fp, cpu->IM);
+ PUT_Byte(fp, cpu->I);
+ PUT_Byte(fp, cpu->R);
+ PUT_Byte(fp, cpu->R);
+
+ PUT_ULong(fp, cpu->priv->cycle);
+ PUT_Long(fp, cpu->priv->halt);
+ PUT_Byte(fp, cpu->priv->shift);
+ PUT_Long(fp, cpu->priv->raise);
+ PUT_Byte(fp, cpu->priv->devbyte);
+ PUT_Long(fp, cpu->priv->nmi);
+ PUT_Long(fp, cpu->priv->last_cb);
+}
+
+void Z80LoadSnapshot(Z80 *cpu, FILE *fp)
+{
+ cpu->PC = GET_ULong(fp);
+ cpu->AF.w = GET_ULong(fp);
+ cpu->BC.w = GET_ULong(fp);
+ cpu->DE.w = GET_ULong(fp);
+ cpu->HL.w = GET_ULong(fp);
+ cpu->AF_ = GET_ULong(fp);
+ cpu->BC_ = GET_ULong(fp);
+ cpu->DE_ = GET_ULong(fp);
+ cpu->HL_ = GET_ULong(fp);
+ cpu->IX.w = GET_ULong(fp);
+ cpu->IY.w = GET_ULong(fp);
+ cpu->SP = GET_ULong(fp);
+ cpu->IFF1 = GET_Byte(fp);
+ cpu->IFF2 = GET_Byte(fp);
+ cpu->IM = GET_Byte(fp);
+ cpu->I = GET_Byte(fp);
+ cpu->R = GET_Byte(fp);
+ cpu->R = GET_Byte(fp);
+
+ cpu->priv->cycle = GET_ULong(fp);
+ cpu->priv->halt = GET_Long(fp);
+ cpu->priv->shift = GET_Byte(fp);
+ cpu->priv->raise = GET_Long(fp);
+ cpu->priv->devbyte = GET_Byte(fp);
+ cpu->priv->nmi = GET_Long(fp);
+ cpu->priv->last_cb = GET_Long(fp);
+}
+
+/* END OF FILE */
diff --git a/source/z80_decode.c b/source/z80_decode.c
new file mode 100644
index 0000000..614f384
--- /dev/null
+++ b/source/z80_decode.c
@@ -0,0 +1,2530 @@
+/*
+
+ z80 - Z80 Emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80_decode.c 4 2006-10-04 23:05:43Z ianc $
+
+*/
+#include <stdlib.h>
+#include <limits.h>
+
+#include "z80.h"
+#include "z80_private.h"
+
+/* ---------------------------------------- TABLES AND INIT
+*/
+static Z80Byte PSZtable[512];
+static Z80Byte SZtable[512];
+static Z80Byte Ptable[512];
+static Z80Byte Stable[512];
+static Z80Byte Ztable[512];
+
+
+int Z80_HI_WORD;
+int Z80_LO_WORD;
+
+#define HI Z80_HI_WORD
+#define LO Z80_LO_WORD
+
+/* ---------------------------------------- MISC FUNCTIONS
+*/
+void Z80_InitialiseInternals(void)
+{
+ Z80Word f;
+ Z80Reg r;
+
+ /* Check endianness
+ */
+ r.w=0x1234;
+
+ if (r.b[0] == 0x12)
+ {
+ HI=0;
+ LO=1;
+ }
+ else if (r.b[1] == 0x12)
+ {
+ HI=1;
+ LO=0;
+ }
+ else
+ {
+ exit(1);
+ }
+
+ /* Check variable sizes
+ */
+ if (CHAR_BIT!=8 || sizeof(Z80Word)!=2)
+ {
+ exit(2);
+ }
+
+ /* Initialise flag tables
+ */
+ for(f=0;f<256;f++)
+ {
+ Z80Byte p,z,s;
+ int b;
+
+ p=0;
+
+ for(b=0;b<8;b++)
+ if (f&(1<<b))
+ p++;
+
+ if (p&1)
+ p=0;
+ else
+ p=P_Z80;
+
+ if (f)
+ z=0;
+ else
+ z=Z_Z80;
+
+ if (f&0x80)
+ s=S_Z80;
+ else
+ s=0;
+
+ Ptable[f]=p;
+ Stable[f]=s;
+ Ztable[f]=z;
+ SZtable[f]=z|s;
+ PSZtable[f]=z|s|p;
+
+ Ptable[f+256]=Ptable[f]|C_Z80;
+ Stable[f+256]=Stable[f]|C_Z80;
+ Ztable[f+256]=Ztable[f]|C_Z80;
+ SZtable[f+256]=SZtable[f]|C_Z80;
+ PSZtable[f+256]=PSZtable[f]|C_Z80;
+ }
+}
+
+#ifndef ENABLE_ARRAY_MEMORY
+static Z80Word FPEEKW(Z80 *cpu, Z80Word addr)
+{
+ return (PEEK(addr) | (Z80Word)PEEK(addr+1)<<8);
+}
+
+
+static void FPOKEW(Z80 *cpu, Z80Word addr, Z80Word val)
+{
+ PRIV->mwrite(cpu,addr,val);
+ PRIV->mwrite(cpu,addr+1,val>>8);
+}
+#endif
+
+
+/* ---------------------------------------- GENERAL MACROS
+*/
+#define SWAP(A,B) \
+do { \
+ unsigned swap_tmp; \
+ swap_tmp=A; \
+ A=B; \
+ B=swap_tmp; \
+} while(0)
+
+
+/* ---------------------------------------- ARITHMETIC OPS
+*/
+#define ADD8(ONCE) \
+do { \
+ Z80Byte VAL=ONCE; \
+ unsigned w; \
+ w=cpu->AF.b[HI]+(unsigned)VAL; \
+ cpu->AF.b[LO]=SZtable[w]; \
+ if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; \
+ if ((VAL^cpu->AF.b[HI]^0x80)&(VAL^w)&0x80) cpu->AF.b[LO]|=P_Z80; \
+ SETHIDDEN(w); \
+ cpu->AF.b[HI]=w; \
+} while(0)
+
+
+#define ADC8(ONCE) \
+do { \
+ Z80Byte VAL=ONCE; \
+ unsigned w; \
+ w=(cpu->AF.b[HI]+(unsigned)VAL+CARRY)&0x1ff; \
+ cpu->AF.b[LO]=SZtable[w]; \
+ if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; \
+ if ((VAL^cpu->AF.b[HI]^0x80)&(VAL^w)&0x80) cpu->AF.b[LO]|=P_Z80; \
+ SETHIDDEN(w); \
+ cpu->AF.b[HI]=w; \
+} while(0)
+
+
+#define SUB8(ONCE) \
+do { \
+ Z80Byte VAL=ONCE; \
+ unsigned w; \
+ w=(cpu->AF.b[HI]-(unsigned)VAL)&0x1ff; \
+ cpu->AF.b[LO]=SZtable[w]|N_Z80; \
+ if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; \
+ if ((VAL^cpu->AF.b[HI])&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; \
+ SETHIDDEN(w); \
+ cpu->AF.b[HI]=w; \
+} while(0)
+
+
+#define CMP8(ONCE) \
+do { \
+ Z80Byte VAL=ONCE; \
+ unsigned w; \
+ w=(cpu->AF.b[HI]-(unsigned)VAL)&0x1ff; \
+ cpu->AF.b[LO]=SZtable[w]|N_Z80; \
+ if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; \
+ if ((VAL^cpu->AF.b[HI])&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; \
+ SETHIDDEN(VAL); \
+} while(0)
+
+
+#define SBC8(ONCE) \
+do { \
+ Z80Byte VAL=ONCE; \
+ unsigned w; \
+ w=(cpu->AF.b[HI]-(unsigned)VAL-CARRY)&0x1ff; \
+ cpu->AF.b[LO]=SZtable[w]|N_Z80; \
+ if ((cpu->AF.b[HI]^w^VAL)&H_Z80) cpu->AF.b[LO]|=H_Z80; \
+ if ((VAL^cpu->AF.b[HI])&(cpu->AF.b[HI]^w)&0x80) cpu->AF.b[LO]|=P_Z80; \
+ SETHIDDEN(w); \
+ cpu->AF.b[HI]=w; \
+} while(0)
+
+
+#define ADD16(REG,ONCE) \
+do { \
+ Z80Word VAL=ONCE; \
+ Z80Val w; \
+ w=(REG)+(Z80Val)VAL; \
+ cpu->AF.b[LO]&=(S_Z80|Z_Z80|V_Z80); \
+ if (w>0xffff) cpu->AF.b[LO]|=C_Z80; \
+ if (((REG)^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; \
+ SETHIDDEN(w>>8); \
+ (REG)=w; \
+} while(0)
+
+
+#define ADC16(REG, ONCE) \
+do { \
+ Z80Word VAL=ONCE; \
+ Z80Val w; \
+ w=(REG)+(Z80Val)VAL+CARRY; \
+ cpu->AF.b[LO]=0; \
+ if ((w&0xffff)==0) cpu->AF.b[LO]=Z_Z80; \
+ if (w&0x8000) cpu->AF.b[LO]|=S_Z80; \
+ if (w>0xffff) cpu->AF.b[LO]|=C_Z80; \
+ if ((VAL^(REG)^0x8000)&((REG)^w)&0x8000) cpu->AF.b[LO]|=P_Z80; \
+ if (((REG)^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; \
+ SETHIDDEN(w>>8); \
+ (REG)=w; \
+} while(0)
+
+
+#define SBC16(REG, ONCE) \
+do { \
+ Z80Word VAL=ONCE; \
+ Z80Val w; \
+ w=(REG)-(Z80Val)VAL-CARRY; \
+ cpu->AF.b[LO]=N_Z80; \
+ if (w&0x8000) cpu->AF.b[LO]|=S_Z80; \
+ if ((w&0xffff)==0) cpu->AF.b[LO]|=Z_Z80; \
+ if (w>0xffff) cpu->AF.b[LO]|=C_Z80; \
+ if ((VAL^(REG))&((REG)^w)&0x8000) cpu->AF.b[LO]|=P_Z80; \
+ if (((REG)^w^VAL)&0x1000) cpu->AF.b[LO]|=H_Z80; \
+ SETHIDDEN(w>>8); \
+ (REG)=w; \
+} while(0)
+
+
+#define INC8(REG) \
+do { \
+ (REG)++; \
+ cpu->AF.b[LO]=CARRY|SZtable[(REG)]; \
+ if ((REG)==0x80) cpu->AF.b[LO]|=P_Z80; \
+ if (((REG)&0x0f)==0) cpu->AF.b[LO]|=H_Z80; \
+} while(0)
+
+
+#define DEC8(REG) \
+do { \
+ (REG)--; \
+ cpu->AF.b[LO]=N_Z80|CARRY; \
+ if ((REG)==0x7f) cpu->AF.b[LO]|=P_Z80; \
+ if (((REG)&0x0f)==0x0f) cpu->AF.b[LO]|=H_Z80; \
+ cpu->AF.b[LO]|=SZtable[(REG)]; \
+} while(0)
+
+
+#define OP_ON_MEM(OP,addr) \
+do { \
+ Z80Byte memop=PEEK(addr); \
+ OP(memop); \
+ POKE(addr,memop); \
+} while(0)
+
+
+#define OP_ON_MEM_WITH_ARG(OP,addr,arg) \
+do { \
+ Z80Byte memop=PEEK(addr); \
+ OP(memop,arg); \
+ POKE(addr,memop); \
+} while(0)
+
+
+#define OP_ON_MEM_WITH_COPY(OP,addr,copy) \
+do { \
+ Z80Byte memop=PEEK(addr); \
+ OP(memop); \
+ copy=memop; \
+ POKE(addr,memop); \
+} while(0)
+
+
+#define OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,arg,copy) \
+do { \
+ Z80Byte memop=PEEK(addr); \
+ OP(memop,arg); \
+ copy=memop; \
+ POKE(addr,memop); \
+} while(0)
+
+
+/* ---------------------------------------- ROTATE AND SHIFT OPS
+*/
+#define RRCA \
+do { \
+ cpu->AF.b[LO]=(cpu->AF.b[LO]&(S_Z80|Z_Z80|P_Z80))|(cpu->AF.b[HI]&C_Z80); \
+ cpu->AF.b[HI]=(cpu->AF.b[HI]>>1)|(cpu->AF.b[HI]<<7); \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define RRA \
+do { \
+ Z80Byte c; \
+ c=CARRY; \
+ cpu->AF.b[LO]=(cpu->AF.b[LO]&(S_Z80|Z_Z80|P_Z80))|(cpu->AF.b[HI]&C_Z80); \
+ cpu->AF.b[HI]=(cpu->AF.b[HI]>>1)|(c<<7); \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define RRC(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)&C_Z80; \
+ (REG)=((REG)>>1)|((REG)<<7); \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define RR(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)&C_Z80; \
+ (REG)=((REG)>>1)|(CARRY<<7); \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define RLCA \
+do { \
+ cpu->AF.b[LO]=(cpu->AF.b[LO]&(S_Z80|Z_Z80|P_Z80))|(cpu->AF.b[HI]>>7); \
+ cpu->AF.b[HI]=(cpu->AF.b[HI]<<1)|(cpu->AF.b[HI]>>7); \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define RLA \
+do { \
+ Z80Byte c; \
+ c=CARRY; \
+ cpu->AF.b[LO]=(cpu->AF.b[LO]&(S_Z80|Z_Z80|P_Z80))|(cpu->AF.b[HI]>>7); \
+ cpu->AF.b[HI]=(cpu->AF.b[HI]<<1)|c; \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define RLC(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)>>7; \
+ (REG)=((REG)<<1)|c; \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define RL(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)>>7; \
+ (REG)=((REG)<<1)|CARRY; \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define SRL(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)&C_Z80; \
+ (REG)>>=1; \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define SRA(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)&C_Z80; \
+ (REG)=((REG)>>1)|((REG)&0x80); \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define SLL(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)>>7; \
+ (REG)=((REG)<<1)|1; \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+#define SLA(REG) \
+do { \
+ Z80Byte c; \
+ c=(REG)>>7; \
+ (REG)=(REG)<<1; \
+ cpu->AF.b[LO]=PSZtable[(REG)]|c; \
+ SETHIDDEN(REG); \
+} while(0)
+
+
+/* ---------------------------------------- BOOLEAN OPS
+*/
+#define AND(VAL) \
+do { \
+ cpu->AF.b[HI]&=VAL; \
+ cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]|H_Z80; \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define OR(VAL) \
+do { \
+ cpu->AF.b[HI]|=VAL; \
+ cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]; \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define XOR(VAL) \
+do { \
+ cpu->AF.b[HI]^=VAL; \
+ cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]; \
+ SETHIDDEN(cpu->AF.b[HI]); \
+} while(0)
+
+
+#define BIT(REG,B) \
+do { \
+ cpu->AF.b[LO]=CARRY|H_Z80; \
+ if ((REG)&(1<<B)) \
+ { \
+ if (B==7 && (REG&S_Z80)) cpu->AF.b[LO]|=S_Z80; \
+ if (B==5 && (REG&B5_Z80)) cpu->AF.b[LO]|=B5_Z80; \
+ if (B==3 && (REG&B3_Z80)) cpu->AF.b[LO]|=B3_Z80; \
+ } \
+ else \
+ { \
+ cpu->AF.b[LO]|=Z_Z80; \
+ cpu->AF.b[LO]|=P_Z80; \
+ } \
+} while(0)
+
+#define BIT_SET(REG,B) (REG)|=(1<<B)
+#define BIT_RES(REG,B) (REG)&=~(1<<B)
+
+
+/* ---------------------------------------- JUMP OPERATIONS
+*/
+#define JR_COND(COND) \
+do { \
+ if (COND) \
+ { \
+ TSTATE(12); \
+ JR; \
+ } \
+ else \
+ { \
+ TSTATE(7); \
+ NOJR; \
+ } \
+} while(0)
+
+
+#define JP_COND(COND) \
+do { \
+ TSTATE(10); \
+ if (COND) \
+ { \
+ JP; \
+ } \
+ else \
+ { \
+ NOJP; \
+ } \
+} while(0)
+
+
+#define CALL_COND(COND) \
+do { \
+ if (COND) \
+ { \
+ TSTATE(17); \
+ CALL; \
+ } \
+ else \
+ { \
+ TSTATE(10); \
+ NOCALL; \
+ } \
+} while(0)
+
+
+#define RET_COND(COND) \
+do { \
+ if (COND) \
+ { \
+ TSTATE(11); \
+ POP(cpu->PC); \
+ } \
+ else \
+ { \
+ TSTATE(5); \
+ } \
+} while(0)
+
+
+#define RST(ADDR) \
+ TSTATE(11); \
+ PUSH(cpu->PC); \
+ cpu->PC=ADDR
+
+/* ---------------------------------------- BLOCK OPERATIONS
+*/
+#define LDI \
+do { \
+ Z80Byte b; \
+ \
+ b=PEEK(cpu->HL.w); \
+ POKE(cpu->DE.w,b); \
+ cpu->DE.w++; \
+ cpu->HL.w++; \
+ cpu->BC.w--; \
+ \
+ CLRFLAG(H_Z80); \
+ CLRFLAG(N_Z80); \
+ \
+ if (cpu->BC.w) \
+ SETFLAG(P_Z80); \
+ else \
+ CLRFLAG(P_Z80); \
+ \
+ SETHIDDEN(cpu->AF.b[HI]+b); \
+} while(0)
+
+#define LDD \
+do { \
+ Z80Byte b; \
+ \
+ b=PEEK(cpu->HL.w); \
+ POKE(cpu->DE.w,b); \
+ cpu->DE.w--; \
+ cpu->HL.w--; \
+ cpu->BC.w--; \
+ \
+ CLRFLAG(H_Z80); \
+ CLRFLAG(N_Z80); \
+ \
+ if (cpu->BC.w) \
+ SETFLAG(P_Z80); \
+ else \
+ CLRFLAG(P_Z80); \
+ \
+ SETHIDDEN(cpu->AF.b[HI]+b); \
+} while(0)
+
+#define CPI \
+do { \
+ Z80Byte c,b; \
+ \
+ c=CARRY; \
+ b=PEEK(cpu->HL.w); \
+ \
+ CMP8(b); \
+ \
+ if (c) \
+ SETFLAG(C_Z80); \
+ else \
+ CLRFLAG(C_Z80); \
+ \
+ cpu->HL.w++; \
+ cpu->BC.w--; \
+ \
+ if (cpu->BC.w) \
+ SETFLAG(P_Z80); \
+ else \
+ CLRFLAG(P_Z80); \
+} while(0)
+
+#define CPD \
+do { \
+ Z80Byte c,b; \
+ \
+ c=CARRY; \
+ b=PEEK(cpu->HL.w); \
+ \
+ CMP8(b); \
+ \
+ if (c) \
+ SETFLAG(C_Z80); \
+ else \
+ CLRFLAG(C_Z80); \
+ \
+ cpu->HL.w--; \
+ cpu->BC.w--; \
+ \
+ if (cpu->BC.w) \
+ SETFLAG(P_Z80); \
+ else \
+ CLRFLAG(P_Z80); \
+} while(0)
+
+#define INI \
+do { \
+ Z80Word w; \
+ Z80Byte b; \
+ \
+ b=IN(cpu->BC.w); \
+ POKE(cpu->HL.w,b); \
+ \
+ cpu->BC.b[HI]--; \
+ cpu->HL.w++; \
+ \
+ cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; \
+ SETHIDDEN(cpu->BC.b[HI]); \
+ \
+ w=(((Z80Word)cpu->BC.b[LO])&0xff)+b; \
+ \
+ if (b&0x80) \
+ SETFLAG(N_Z80); \
+ \
+ if (w&0x100) \
+ { \
+ SETFLAG(C_Z80); \
+ SETFLAG(H_Z80); \
+ } \
+ else \
+ { \
+ CLRFLAG(C_Z80); \
+ CLRFLAG(H_Z80); \
+ } \
+} while(0)
+
+#define IND \
+do { \
+ Z80Word w; \
+ Z80Byte b; \
+ \
+ b=IN(cpu->BC.w); \
+ POKE(cpu->HL.w,b); \
+ \
+ cpu->BC.b[HI]--; \
+ cpu->HL.w--; \
+ \
+ cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; \
+ SETHIDDEN(cpu->BC.b[HI]); \
+ \
+ w=(((Z80Word)cpu->BC.b[LO])&0xff)+b; \
+ \
+ if (b&0x80) \
+ SETFLAG(N_Z80); \
+ \
+ if (w&0x100) \
+ { \
+ SETFLAG(C_Z80); \
+ SETFLAG(H_Z80); \
+ } \
+ else \
+ { \
+ CLRFLAG(C_Z80); \
+ CLRFLAG(H_Z80); \
+ } \
+} while(0) \
+
+#define OUTI \
+do { \
+ OUT(cpu->BC.w,PEEK(cpu->HL.w)); \
+ \
+ cpu->HL.w++; \
+ cpu->BC.b[HI]--; \
+ \
+ cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; \
+ SETHIDDEN(cpu->BC.b[HI]); \
+} while(0)
+
+#define OUTD \
+do { \
+ OUT(cpu->BC.w,PEEK(cpu->HL.w)); \
+ \
+ cpu->HL.w--; \
+ cpu->BC.b[HI]--; \
+ \
+ cpu->AF.b[LO]=SZtable[cpu->BC.b[HI]]; \
+ SETFLAG(N_Z80); \
+ SETHIDDEN(cpu->BC.b[HI]); \
+} while(0)
+
+
+/* ---------------------------------------- BASE OPCODE SHORT-HAND BLOCKS
+*/
+
+#define LD_BLOCK(BASE,DEST,DEST2) \
+ case BASE: /* LD DEST,B */ \
+ TSTATE(4); \
+ DEST=cpu->BC.b[HI]; \
+ break; \
+ \
+ case BASE+1: /* LD DEST,C */ \
+ TSTATE(4); \
+ DEST=cpu->BC.b[LO]; \
+ break; \
+ \
+ case BASE+2: /* LD DEST,D */ \
+ TSTATE(4); \
+ DEST=cpu->DE.b[HI]; \
+ break; \
+ \
+ case BASE+3: /* LD DEST,E */ \
+ TSTATE(4); \
+ DEST=cpu->DE.b[LO]; \
+ break; \
+ \
+ case BASE+4: /* LD DEST,H */ \
+ TSTATE(4); \
+ DEST=*H; \
+ break; \
+ \
+ case BASE+5: /* LD DEST,L */ \
+ TSTATE(4); \
+ DEST=*L; \
+ break; \
+ \
+ case BASE+6: /* LD DEST,(HL) */ \
+ TSTATE(7); \
+ OFFSET(off); \
+ DEST2=PEEK(*HL+off); \
+ break; \
+ \
+ case BASE+7: /* LD DEST,A */ \
+ TSTATE(4); \
+ DEST=cpu->AF.b[HI]; \
+ break;
+
+#define ALU_BLOCK(BASE,OP) \
+ case BASE: /* OP A,B */ \
+ TSTATE(4); \
+ OP(cpu->BC.b[HI]); \
+ break; \
+ \
+ case BASE+1: /* OP A,C */ \
+ TSTATE(4); \
+ OP(cpu->BC.b[LO]); \
+ break; \
+ \
+ case BASE+2: /* OP A,D */ \
+ TSTATE(4); \
+ OP(cpu->DE.b[HI]); \
+ break; \
+ \
+ case BASE+3: /* OP A,E */ \
+ TSTATE(4); \
+ OP(cpu->DE.b[LO]); \
+ break; \
+ \
+ case BASE+4: /* OP A,H */ \
+ TSTATE(4); \
+ OP(*H); \
+ break; \
+ \
+ case BASE+5: /* OP A,L */ \
+ TSTATE(4); \
+ OP(*L); \
+ break; \
+ \
+ case BASE+6: /* OP A,(HL) */ \
+ TSTATE(7); \
+ OFFSET(off); \
+ OP_ON_MEM(OP,*HL+off); \
+ break; \
+ \
+ case BASE+7: /* OP A,A */ \
+ TSTATE(4); \
+ OP(cpu->AF.b[HI]); \
+ break;
+
+
+/* ---------------------------------------- CB OPCODE SHORT-HAND BLOCKS
+*/
+
+#define CB_ALU_BLOCK(BASE,OP) \
+ case BASE: /* OP B */ \
+ TSTATE(8); \
+ OP(cpu->BC.b[HI]); \
+ break; \
+ \
+ case BASE+1: /* OP C */ \
+ TSTATE(8); \
+ OP(cpu->BC.b[LO]); \
+ break; \
+ \
+ case BASE+2: /* OP D */ \
+ TSTATE(8); \
+ OP(cpu->DE.b[HI]); \
+ break; \
+ \
+ case BASE+3: /* OP E */ \
+ TSTATE(8); \
+ OP(cpu->DE.b[LO]); \
+ break; \
+ \
+ case BASE+4: /* OP H */ \
+ TSTATE(8); \
+ OP(cpu->HL.b[HI]); \
+ break; \
+ \
+ case BASE+5: /* OP L */ \
+ TSTATE(8); \
+ OP(cpu->HL.b[LO]); \
+ break; \
+ \
+ case BASE+6: /* OP (HL) */ \
+ TSTATE(15); \
+ OP_ON_MEM(OP,cpu->HL.w); \
+ break; \
+ \
+ case BASE+7: /* OP A */ \
+ TSTATE(8); \
+ OP(cpu->AF.b[HI]); \
+ break;
+
+#define CB_BITMANIP_BLOCK(BASE,OP,BIT_NO) \
+ case BASE: /* OP B */ \
+ TSTATE(8); \
+ OP(cpu->BC.b[HI],BIT_NO); \
+ break; \
+ \
+ case BASE+1: /* OP C */ \
+ TSTATE(8); \
+ OP(cpu->BC.b[LO],BIT_NO); \
+ break; \
+ \
+ case BASE+2: /* OP D */ \
+ TSTATE(8); \
+ OP(cpu->DE.b[HI],BIT_NO); \
+ break; \
+ \
+ case BASE+3: /* OP E */ \
+ TSTATE(8); \
+ OP(cpu->DE.b[LO],BIT_NO); \
+ break; \
+ \
+ case BASE+4: /* OP H */ \
+ TSTATE(8); \
+ OP(cpu->HL.b[HI],BIT_NO); \
+ break; \
+ \
+ case BASE+5: /* OP L */ \
+ TSTATE(8); \
+ OP(cpu->HL.b[LO],BIT_NO); \
+ break; \
+ \
+ case BASE+6: /* OP (HL) */ \
+ TSTATE(12); \
+ OP_ON_MEM_WITH_ARG(OP,cpu->HL.w,BIT_NO); \
+ break; \
+ \
+ case BASE+7: /* OP A */ \
+ TSTATE(8); \
+ OP(cpu->AF.b[HI],BIT_NO); \
+ break;
+
+/* ---------------------------------------- SHIFTED CB OPCODE SHORT-HAND BLOCKS
+*/
+
+#define SHIFTED_CB_ALU_BLOCK(BASE,OP) \
+ case BASE: /* OP B */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->BC.b[HI]); \
+ break; \
+ \
+ case BASE+1: /* OP C */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->BC.b[LO]); \
+ break; \
+ \
+ case BASE+2: /* OP D */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->DE.b[HI]); \
+ break; \
+ \
+ case BASE+3: /* OP E */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->DE.b[LO]); \
+ break; \
+ \
+ case BASE+4: /* OP H */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->HL.b[HI]); \
+ break; \
+ \
+ case BASE+5: /* OP L */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->HL.b[LO]); \
+ break; \
+ \
+ case BASE+6: /* OP (HL) */ \
+ TSTATE(15); \
+ OP_ON_MEM(OP,addr); \
+ break; \
+ \
+ case BASE+7: /* OP A */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_COPY(OP,addr,cpu->AF.b[HI]); \
+ break;
+
+#define SHIFTED_CB_BITMANIP_BLOCK(BASE,OP,BIT_NO) \
+ case BASE: /* OP B */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->BC.b[HI]); \
+ break; \
+ \
+ case BASE+1: /* OP C */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->BC.b[LO]); \
+ break; \
+ \
+ case BASE+2: /* OP D */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->DE.b[HI]); \
+ break; \
+ \
+ case BASE+3: /* OP E */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->DE.b[LO]); \
+ break; \
+ \
+ case BASE+4: /* OP H */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->HL.b[HI]); \
+ break; \
+ \
+ case BASE+5: /* OP L */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->HL.b[LO]); \
+ break; \
+ \
+ case BASE+6: /* OP (HL) */ \
+ TSTATE(12); \
+ OP_ON_MEM_WITH_ARG(OP,addr,BIT_NO); \
+ break; \
+ \
+ case BASE+7: /* OP A */ \
+ TSTATE(8); \
+ OP_ON_MEM_WITH_ARG_AND_COPY(OP,addr,BIT_NO,cpu->AF.b[HI]); \
+ break;
+
+/* ---------------------------------------- DAA
+*/
+
+/* This alogrithm is based on info from
+ http://www.worldofspectrum.org/faq/reference/z80reference.htm
+*/
+static void DAA (Z80 *cpu)
+{
+ Z80Byte add=0;
+ Z80Byte carry=0;
+ Z80Byte nf=cpu->AF.b[LO]&N_Z80;
+ Z80Byte acc=cpu->AF.b[HI];
+
+ if (acc>0x99 || IS_C)
+ {
+ add|=0x60;
+ carry=C_Z80;
+ }
+
+ if ((acc&0xf)>0x9 || IS_H)
+ {
+ add|=0x06;
+ }
+
+ if (nf)
+ {
+ cpu->AF.b[HI]-=add;
+ }
+ else
+ {
+ cpu->AF.b[HI]+=add;
+ }
+
+ cpu->AF.b[LO]=PSZtable[cpu->AF.b[HI]]
+ | carry
+ | nf
+ | ((acc^cpu->AF.b[HI])&H_Z80)
+ | (cpu->AF.b[HI]&(B3_Z80|B5_Z80));
+}
+
+/* ---------------------------------------- HANDLERS FOR ED OPCODES
+*/
+static void DecodeED(Z80 *cpu, Z80Byte opcode)
+{
+ switch(opcode)
+ {
+ case 0x40: /* IN B,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->BC.b[HI]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->BC.b[HI]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->BC.b[HI]];
+ SETHIDDEN(cpu->BC.b[HI]);
+ break;
+
+ case 0x41: /* OUT (C),B */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->BC.b[HI]);
+ break;
+
+ case 0x42: /* SBC HL,BC */
+ TSTATE(15);
+ SBC16(cpu->HL.w,cpu->BC.w);
+ break;
+
+ case 0x43: /* LD (nnnn),BC */
+ TSTATE(20);
+ POKEW(FETCH_WORD,cpu->BC.w);
+ break;
+
+ case 0x44: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x45: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x46: /* IM 0 */
+ TSTATE(8);
+ cpu->IM=0;
+ break;
+
+ case 0x47: /* LD I,A */
+ TSTATE(9);
+ cpu->I=cpu->AF.b[HI];
+ break;
+
+ case 0x48: /* IN C,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->BC.b[LO]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->BC.b[LO]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->BC.b[LO]];
+ SETHIDDEN(cpu->BC.b[LO]);
+ break;
+
+ case 0x49: /* OUT (C),C */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->BC.b[LO]);
+ break;
+
+ case 0x4a: /* ADC HL,BC */
+ TSTATE(15);
+ ADC16(cpu->HL.w,cpu->BC.w);
+ break;
+
+ case 0x4b: /* LD BC,(nnnn) */
+ TSTATE(20);
+ cpu->BC.w=PEEKW(FETCH_WORD);
+ break;
+
+ case 0x4c: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x4d: /* RETI */
+ TSTATE(14);
+ CALLBACK(eZ80_RETI,0);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x4e: /* IM 0/1 */
+ TSTATE(8);
+ cpu->IM=0;
+ break;
+
+ case 0x4f: /* LD R,A */
+ TSTATE(9);
+ cpu->R=cpu->AF.b[HI];
+ break;
+
+ case 0x50: /* IN D,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->DE.b[HI]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->DE.b[HI]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->DE.b[HI]];
+ SETHIDDEN(cpu->BC.b[HI]);
+ break;
+
+ case 0x51: /* OUT (C),D */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->DE.b[HI]);
+ break;
+
+ case 0x52: /* SBC HL,DE */
+ TSTATE(15);
+ SBC16(cpu->HL.w,cpu->DE.w);
+ break;
+
+ case 0x53: /* LD (nnnn),DE */
+ TSTATE(20);
+ POKEW(FETCH_WORD,cpu->DE.w);
+ break;
+
+ case 0x54: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x55: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x56: /* IM 1 */
+ TSTATE(8);
+ cpu->IM=1;
+ break;
+
+ case 0x57: /* LD A,I */
+ TSTATE(9);
+ cpu->AF.b[HI]=cpu->I;
+ break;
+
+ case 0x58: /* IN E,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->DE.b[LO]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->BC.b[LO]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->DE.b[LO]];
+ SETHIDDEN(cpu->DE.b[LO]);
+ break;
+
+ case 0x59: /* OUT (C),E */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->DE.b[LO]);
+ break;
+
+ case 0x5a: /* ADC HL,DE */
+ TSTATE(15);
+ ADC16(cpu->HL.w,cpu->DE.w);
+ break;
+
+ case 0x5b: /* LD DE,(nnnn) */
+ TSTATE(20);
+ cpu->DE.w=PEEKW(FETCH_WORD);
+ break;
+
+ case 0x5c: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x5d: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x5e: /* IM 2 */
+ TSTATE(8);
+ cpu->IM=2;
+ break;
+
+ case 0x5f: /* LD A,R */
+ TSTATE(9);
+ cpu->AF.b[HI]=cpu->R;
+ break;
+
+ case 0x60: /* IN H,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->HL.b[HI]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->HL.b[HI]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->HL.b[HI]];
+ SETHIDDEN(cpu->HL.b[HI]);
+ break;
+
+ case 0x61: /* OUT (C),H */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->HL.b[HI]);
+ break;
+
+ case 0x62: /* SBC HL,HL */
+ TSTATE(15);
+ SBC16(cpu->HL.w,cpu->HL.w);
+ break;
+
+ case 0x63: /* LD (nnnn),HL */
+ TSTATE(20);
+ POKEW(FETCH_WORD,cpu->HL.w);
+ break;
+
+ case 0x64: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x65: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x66: /* IM 0 */
+ TSTATE(8);
+ cpu->IM=0;
+ break;
+
+ case 0x67: /* RRD */
+ {
+ Z80Byte b;
+
+ TSTATE(18);
+
+ b=PEEK(cpu->HL.w);
+
+ POKE(cpu->HL.w,(b>>4)|(cpu->AF.b[HI]<<4));
+ cpu->AF.b[HI]=(cpu->AF.b[HI]&0xf0)|(b&0x0f);
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->AF.b[HI]];
+ SETHIDDEN(cpu->AF.b[HI]);
+ break;
+ }
+
+ case 0x68: /* IN L,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->HL.b[LO]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->HL.b[LO]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->HL.b[LO]];
+ SETHIDDEN(cpu->HL.b[LO]);
+ break;
+
+ case 0x69: /* OUT (C),L */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->HL.b[LO]);
+ break;
+
+ case 0x6a: /* ADC HL,HL */
+ TSTATE(15);
+ ADC16(cpu->HL.w,cpu->HL.w);
+ break;
+
+ case 0x6b: /* LD HL,(nnnn) */
+ TSTATE(20);
+ cpu->HL.w=PEEKW(FETCH_WORD);
+ break;
+
+ case 0x6c: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x6d: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x6e: /* IM 0/1 */
+ TSTATE(8);
+ cpu->IM=0;
+ break;
+
+ case 0x6f: /* RLD */
+ {
+ Z80Byte b;
+
+ TSTATE(18);
+
+ b=PEEK(cpu->HL.w);
+
+ POKE(cpu->HL.w,(b<<4)|(cpu->AF.b[HI]&0x0f));
+ cpu->AF.b[HI]=(cpu->AF.b[HI]&0xf0)|(b>>4);
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->AF.b[HI]];
+ SETHIDDEN(cpu->AF.b[HI]);
+ break;
+ }
+
+ case 0x70: /* IN (C) */
+ {
+ Z80Byte b;
+
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ b=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ b=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[b];
+ SETHIDDEN(b);
+ break;
+ }
+
+ case 0x71: /* OUT (C) */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,0);
+ break;
+
+ case 0x72: /* SBC HL,SP */
+ TSTATE(15);
+ SBC16(cpu->HL.w,cpu->SP);
+ break;
+
+ case 0x73: /* LD (nnnn),SP */
+ TSTATE(20);
+ POKEW(FETCH_WORD,cpu->SP);
+ break;
+
+ case 0x74: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x75: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x76: /* IM 1 */
+ TSTATE(8);
+ cpu->IM=1;
+ break;
+
+ case 0x77: /* NOP */
+ TSTATE(8);
+ CALLBACK(eZ80_EDHook,opcode);
+ break;
+
+ case 0x78: /* IN A,(C) */
+ TSTATE(12);
+
+ if (PRIV->pread)
+ {
+ cpu->AF.b[HI]=PRIV->pread(cpu,cpu->BC.w);
+ }
+ else
+ {
+ cpu->AF.b[HI]=0;
+ }
+
+ cpu->AF.b[LO]=CARRY|PSZtable[cpu->AF.b[HI]];
+ SETHIDDEN(cpu->AF.b[HI]);
+ break;
+
+ case 0x79: /* OUT (C),A */
+ TSTATE(12);
+ if (PRIV->pwrite) PRIV->pwrite(cpu,cpu->BC.w,cpu->AF.b[HI]);
+ break;
+
+ case 0x7a: /* ADC HL,SP */
+ TSTATE(15);
+ ADC16(cpu->HL.w,cpu->SP);
+ break;
+
+ case 0x7b: /* LD SP,(nnnn) */
+ TSTATE(20);
+ cpu->SP=PEEKW(FETCH_WORD);
+ break;
+
+ case 0x7c: /* NEG */
+ {
+ Z80Byte b;
+
+ TSTATE(8);
+
+ b=cpu->AF.b[HI];
+ cpu->AF.b[HI]=0;
+ SUB8(b);
+ break;
+ }
+
+ case 0x7d: /* RETN */
+ TSTATE(14);
+ cpu->IFF1=cpu->IFF2;
+ POP(cpu->PC);
+ break;
+
+ case 0x7e: /* IM 2 */
+ TSTATE(8);
+ cpu->IM=2;
+ break;
+
+ case 0x7f: /* NOP */
+ TSTATE(8);
+ CALLBACK(eZ80_EDHook,opcode);
+ break;
+
+ case 0xa0: /* LDI */
+ TSTATE(16);
+ LDI;
+ break;
+
+ case 0xa1: /* CPI */
+ TSTATE(16);
+ CPI;
+ break;
+
+ case 0xa2: /* INI */
+ TSTATE(16);
+ INI;
+ break;
+
+ case 0xa3: /* OUTI */
+ TSTATE(16);
+ OUTI;
+ break;
+
+ case 0xa8: /* LDD */
+ TSTATE(16);
+ LDD;
+ break;
+
+ case 0xa9: /* CPD */
+ TSTATE(16);
+ CPD;
+ break;
+
+ case 0xaa: /* IND */
+ TSTATE(16);
+ IND;
+ break;
+
+ case 0xab: /* OUTD */
+ TSTATE(16);
+ OUTD;
+ break;
+
+ case 0xb0: /* LDIR */
+ TSTATE(16);
+ LDI;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xb1: /* CPIR */
+ TSTATE(16);
+ CPI;
+ if (cpu->BC.w && !IS_Z)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xb2: /* INIR */
+ TSTATE(16);
+ INI;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xb3: /* OTIR */
+ TSTATE(16);
+ OUTI;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xb8: /* LDDR */
+ TSTATE(16);
+ LDD;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xb9: /* CPDR */
+ TSTATE(16);
+ CPD;
+ if (cpu->BC.w && !IS_Z)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xba: /* INDR */
+ TSTATE(16);
+ IND;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ case 0xbb: /* OTDR */
+ TSTATE(16);
+ OUTD;
+ if (cpu->BC.w)
+ {
+ TSTATE(5);
+ cpu->PC-=2;
+ }
+ break;
+
+ /* All the rest are NOP/invalid
+ */
+ default:
+ TSTATE(8);
+ CALLBACK(eZ80_EDHook,opcode);
+ break;
+ }
+}
+
+
+/* ---------------------------------------- HANDLERS FOR CB OPCODES
+*/
+static void DecodeCB(Z80 *cpu, Z80Byte opcode)
+{
+ switch(opcode)
+ {
+ CB_ALU_BLOCK(0x00,RLC)
+ CB_ALU_BLOCK(0x08,RRC)
+ CB_ALU_BLOCK(0x10,RL)
+ CB_ALU_BLOCK(0x18,RR)
+ CB_ALU_BLOCK(0x20,SLA)
+ CB_ALU_BLOCK(0x28,SRA)
+ CB_ALU_BLOCK(0x30,SLL)
+ CB_ALU_BLOCK(0x38,SRL)
+
+ CB_BITMANIP_BLOCK(0x40,BIT,0)
+ CB_BITMANIP_BLOCK(0x48,BIT,1)
+ CB_BITMANIP_BLOCK(0x50,BIT,2)
+ CB_BITMANIP_BLOCK(0x58,BIT,3)
+ CB_BITMANIP_BLOCK(0x60,BIT,4)
+ CB_BITMANIP_BLOCK(0x68,BIT,5)
+ CB_BITMANIP_BLOCK(0x70,BIT,6)
+ CB_BITMANIP_BLOCK(0x78,BIT,7)
+
+ CB_BITMANIP_BLOCK(0x80,BIT_RES,0)
+ CB_BITMANIP_BLOCK(0x88,BIT_RES,1)
+ CB_BITMANIP_BLOCK(0x90,BIT_RES,2)
+ CB_BITMANIP_BLOCK(0x98,BIT_RES,3)
+ CB_BITMANIP_BLOCK(0xa0,BIT_RES,4)
+ CB_BITMANIP_BLOCK(0xa8,BIT_RES,5)
+ CB_BITMANIP_BLOCK(0xb0,BIT_RES,6)
+ CB_BITMANIP_BLOCK(0xb8,BIT_RES,7)
+
+ CB_BITMANIP_BLOCK(0xc0,BIT_SET,0)
+ CB_BITMANIP_BLOCK(0xc8,BIT_SET,1)
+ CB_BITMANIP_BLOCK(0xd0,BIT_SET,2)
+ CB_BITMANIP_BLOCK(0xd8,BIT_SET,3)
+ CB_BITMANIP_BLOCK(0xe0,BIT_SET,4)
+ CB_BITMANIP_BLOCK(0xe8,BIT_SET,5)
+ CB_BITMANIP_BLOCK(0xf0,BIT_SET,6)
+ CB_BITMANIP_BLOCK(0xf8,BIT_SET,7)
+ }
+}
+
+
+static void ShiftedDecodeCB(Z80 *cpu, Z80Byte opcode, Z80Relative offset)
+{
+ Z80Word addr;
+
+ /* See if we've come here from a IX/IY shift.
+ */
+ switch (PRIV->shift)
+ {
+ case 0xdd:
+ addr=cpu->IX.w+offset;
+ break;
+ case 0xfd:
+ addr=cpu->IY.w+offset;
+ break;
+ default:
+ addr=cpu->HL.w; /* Play safe... */
+ break;
+ }
+
+ switch(opcode)
+ {
+ SHIFTED_CB_ALU_BLOCK(0x00,RLC)
+ SHIFTED_CB_ALU_BLOCK(0x08,RRC)
+ SHIFTED_CB_ALU_BLOCK(0x10,RL)
+ SHIFTED_CB_ALU_BLOCK(0x18,RR)
+ SHIFTED_CB_ALU_BLOCK(0x20,SLA)
+ SHIFTED_CB_ALU_BLOCK(0x28,SRA)
+ SHIFTED_CB_ALU_BLOCK(0x30,SLL)
+ SHIFTED_CB_ALU_BLOCK(0x38,SRL)
+
+ SHIFTED_CB_BITMANIP_BLOCK(0x40,BIT,0)
+ SHIFTED_CB_BITMANIP_BLOCK(0x48,BIT,1)
+ SHIFTED_CB_BITMANIP_BLOCK(0x50,BIT,2)
+ SHIFTED_CB_BITMANIP_BLOCK(0x58,BIT,3)
+ SHIFTED_CB_BITMANIP_BLOCK(0x60,BIT,4)
+ SHIFTED_CB_BITMANIP_BLOCK(0x68,BIT,5)
+ SHIFTED_CB_BITMANIP_BLOCK(0x70,BIT,6)
+ SHIFTED_CB_BITMANIP_BLOCK(0x78,BIT,7)
+
+ SHIFTED_CB_BITMANIP_BLOCK(0x80,BIT_RES,0)
+ SHIFTED_CB_BITMANIP_BLOCK(0x88,BIT_RES,1)
+ SHIFTED_CB_BITMANIP_BLOCK(0x90,BIT_RES,2)
+ SHIFTED_CB_BITMANIP_BLOCK(0x98,BIT_RES,3)
+ SHIFTED_CB_BITMANIP_BLOCK(0xa0,BIT_RES,4)
+ SHIFTED_CB_BITMANIP_BLOCK(0xa8,BIT_RES,5)
+ SHIFTED_CB_BITMANIP_BLOCK(0xb0,BIT_RES,6)
+ SHIFTED_CB_BITMANIP_BLOCK(0xb8,BIT_RES,7)
+
+ SHIFTED_CB_BITMANIP_BLOCK(0xc0,BIT_SET,0)
+ SHIFTED_CB_BITMANIP_BLOCK(0xc8,BIT_SET,1)
+ SHIFTED_CB_BITMANIP_BLOCK(0xd0,BIT_SET,2)
+ SHIFTED_CB_BITMANIP_BLOCK(0xd8,BIT_SET,3)
+ SHIFTED_CB_BITMANIP_BLOCK(0xe0,BIT_SET,4)
+ SHIFTED_CB_BITMANIP_BLOCK(0xe8,BIT_SET,5)
+ SHIFTED_CB_BITMANIP_BLOCK(0xf0,BIT_SET,6)
+ SHIFTED_CB_BITMANIP_BLOCK(0xf8,BIT_SET,7)
+ }
+}
+
+
+/* ---------------------------------------- NORMAL OPCODE DECODER
+*/
+void Z80_Decode(Z80 *cpu, Z80Byte opcode)
+{
+ Z80Word *HL;
+ Z80Byte *H;
+ Z80Byte *L;
+ Z80Relative off;
+
+ /* See if we've come here from a IX/IY shift
+ */
+ switch (PRIV->shift)
+ {
+ case 0xdd:
+ HL=&(cpu->IX.w);
+ L=cpu->IX.b+LO;
+ H=cpu->IX.b+HI;
+ break;
+ case 0xfd:
+ HL=&(cpu->IY.w);
+ L=cpu->IY.b+LO;
+ H=cpu->IY.b+HI;
+ break;
+ default:
+ HL=&(cpu->HL.w);
+ L=cpu->HL.b+LO;
+ H=cpu->HL.b+HI;
+ break;
+ }
+
+ switch(opcode)
+ {
+ case 0x00: /* NOP */
+ TSTATE(4);
+ break;
+
+ case 0x01: /* LD BC,nnnn */
+ TSTATE(10);
+ cpu->BC.w=FETCH_WORD;
+ break;
+
+ case 0x02: /* LD (BC),A */
+ TSTATE(7);
+ POKE(cpu->BC.w,cpu->AF.b[HI]);
+ break;
+
+ case 0x03: /* INC BC */
+ TSTATE(6);
+ cpu->BC.w++;
+ break;
+
+ case 0x04: /* INC B */
+ TSTATE(4);
+ INC8(cpu->BC.b[HI]);
+ break;
+
+ case 0x05: /* DEC B */
+ TSTATE(4);
+ DEC8(cpu->BC.b[HI]);
+ break;
+
+ case 0x06: /* LD B,n */
+ TSTATE(7);
+ cpu->BC.b[HI]=FETCH_BYTE;
+ break;
+
+ case 0x07: /* RLCA */
+ TSTATE(4);
+ RLCA;
+ break;
+
+ case 0x08: /* EX AF,AF' */
+ TSTATE(4);
+ SWAP(cpu->AF.w,cpu->AF_);
+ break;
+
+ case 0x09: /* ADD HL,BC */
+ TSTATE(11);
+ ADD16(*HL,cpu->BC.w);
+ break;
+
+ case 0x0a: /* LD A,(BC) */
+ TSTATE(7);
+ cpu->AF.b[HI]=PEEK(cpu->BC.w);
+ break;
+
+ case 0x0b: /* DEC BC */
+ TSTATE(6);
+ cpu->BC.w--;
+ break;
+
+ case 0x0c: /* INC C */
+ TSTATE(4);
+ INC8(cpu->BC.b[LO]);
+ break;
+
+ case 0x0d: /* DEC C */
+ TSTATE(4);
+ DEC8(cpu->BC.b[LO]);
+ break;
+
+ case 0x0e: /* LD C,n */
+ TSTATE(7);
+ cpu->BC.b[LO]=FETCH_BYTE;
+ break;
+
+ case 0x0f: /* RRCA */
+ TSTATE(4);
+ RRCA;
+ break;
+
+ case 0x10: /* DJNZ */
+ if (--(cpu->BC.b[HI]))
+ {
+ TSTATE(13);
+ JR;
+ }
+ else
+ {
+ TSTATE(8);
+ NOJR;
+ }
+ break;
+
+ case 0x11: /* LD DE,nnnn */
+ TSTATE(10);
+ cpu->DE.w=FETCH_WORD;
+ break;
+
+ case 0x12: /* LD (DE),A */
+ TSTATE(7);
+ POKE(cpu->DE.w,cpu->AF.b[HI]);
+ break;
+
+ case 0x13: /* INC DE */
+ TSTATE(6);
+ cpu->DE.w++;
+ break;
+
+ case 0x14: /* INC D */
+ TSTATE(4);
+ INC8(cpu->DE.b[HI]);
+ break;
+
+ case 0x15: /* DEC D */
+ TSTATE(4);
+ DEC8(cpu->DE.b[HI]);
+ break;
+
+ case 0x16: /* LD D,n */
+ TSTATE(7);
+ cpu->DE.b[HI]=FETCH_BYTE;
+ break;
+
+ case 0x17: /* RLA */
+ TSTATE(4);
+ RLA;
+ break;
+
+ case 0x18: /* JR d */
+ TSTATE(12);
+ JR;
+ break;
+
+ case 0x19: /* ADD HL,DE */
+ TSTATE(11);
+ ADD16(*HL,cpu->DE.w);
+ break;
+
+ case 0x1a: /* LD A,(DE) */
+ TSTATE(7);
+ cpu->AF.b[HI]=PEEK(cpu->DE.w);
+ break;
+
+ case 0x1b: /* DEC DE */
+ TSTATE(6);
+ cpu->DE.w--;
+ break;
+
+ case 0x1c: /* INC E */
+ TSTATE(4);
+ INC8(cpu->DE.b[LO]);
+ break;
+
+ case 0x1d: /* DEC E */
+ TSTATE(4);
+ DEC8(cpu->DE.b[LO]);
+ break;
+
+ case 0x1e: /* LD E,n */
+ TSTATE(7);
+ cpu->DE.b[LO]=FETCH_BYTE;
+ break;
+
+ case 0x1f: /* RRA */
+ TSTATE(4);
+ RRA;
+ break;
+
+ case 0x20: /* JR NZ,e */
+ JR_COND(!IS_Z);
+ break;
+
+ case 0x21: /* LD HL,nnnn */
+ TSTATE(10);
+ *HL=FETCH_WORD;
+ break;
+
+ case 0x22: /* LD (nnnn),HL */
+ TSTATE(16);
+ POKEW(FETCH_WORD,*HL);
+ break;
+
+ case 0x23: /* INC HL */
+ TSTATE(6);
+ (*HL)++;
+ break;
+
+ case 0x24: /* INC H */
+ TSTATE(4);
+ INC8(*H);
+ break;
+
+ case 0x25: /* DEC H */
+ TSTATE(4);
+ DEC8(*H);
+ break;
+
+ case 0x26: /* LD H,n */
+ TSTATE(7);
+ *H=FETCH_BYTE;
+ break;
+
+ case 0x27: /* DAA */
+ TSTATE(4);
+ DAA(cpu);
+ break;
+
+ case 0x28: /* JR Z,d */
+ JR_COND(IS_Z);
+ break;
+
+ case 0x29: /* ADD HL,HL */
+ TSTATE(11);
+ ADD16(*HL,*HL);
+ break;
+
+ case 0x2a: /* LD HL,(nnnn) */
+ TSTATE(7);
+ *HL=PEEKW(FETCH_WORD);
+ break;
+
+ case 0x2b: /* DEC HL */
+ TSTATE(6);
+ (*HL)--;
+ break;
+
+ case 0x2c: /* INC L */
+ TSTATE(4);
+ INC8(*L);
+ break;
+
+ case 0x2d: /* DEC L */
+ TSTATE(4);
+ DEC8(*L);
+ break;
+
+ case 0x2e: /* LD L,n */
+ TSTATE(7);
+ *L=FETCH_BYTE;
+ break;
+
+ case 0x2f: /* CPL */
+ TSTATE(4);
+ cpu->AF.b[HI]^=0xff;
+ SETFLAG(H_Z80);
+ SETFLAG(N_Z80);
+ SETHIDDEN(cpu->AF.b[HI]);
+ break;
+
+ case 0x30: /* JR NC,d */
+ JR_COND(!IS_C);
+ break;
+
+ case 0x31: /* LD SP,nnnn */
+ TSTATE(10);
+ cpu->SP=FETCH_WORD;
+ break;
+
+ case 0x32: /* LD (nnnn),A */
+ TSTATE(13);
+ POKE(FETCH_WORD,cpu->AF.b[HI]);
+ break;
+
+ case 0x33: /* INC SP */
+ TSTATE(6);
+ cpu->SP++;
+ break;
+
+ case 0x34: /* INC (HL) */
+ TSTATE(11);
+ OFFSET(off);
+ OP_ON_MEM(INC8,*HL+off);
+ break;
+
+ case 0x35: /* DEC (HL) */
+ TSTATE(11);
+ OFFSET(off);
+ OP_ON_MEM(DEC8,*HL+off);
+ break;
+
+ case 0x36: /* LD (HL),n */
+ TSTATE(10);
+ OFFSET(off);
+ POKE(*HL+off,FETCH_BYTE);
+ break;
+
+ case 0x37: /* SCF */
+ TSTATE(4);
+ cpu->AF.b[LO]=(cpu->AF.b[LO]&(S_Z80|Z_Z80|P_Z80))
+ | C_Z80
+ | (cpu->AF.b[HI]&(B3_Z80|B5_Z80));
+ break;
+
+ case 0x38: /* JR C,d */
+ JR_COND(IS_C);
+ break;
+
+ case 0x39: /* ADD HL,SP */
+ TSTATE(11);
+ ADD16(*HL,cpu->SP);
+ break;
+
+ case 0x3a: /* LD A,(nnnn) */
+ TSTATE(13);
+ cpu->AF.b[HI]=PEEK(FETCH_WORD);
+ break;
+
+ case 0x3b: /* DEC SP */
+ TSTATE(6);
+ cpu->SP--;
+ break;
+
+ case 0x3c: /* INC A */
+ TSTATE(4);
+ INC8(cpu->AF.b[HI]);
+ break;
+
+ case 0x3d: /* DEC A */
+ TSTATE(4);
+ DEC8(cpu->AF.b[HI]);
+ break;
+
+ case 0x3e: /* LD A,n */
+ TSTATE(7);
+ cpu->AF.b[HI]=FETCH_BYTE;
+ break;
+
+ case 0x3f: /* CCF */
+ TSTATE(4);
+
+ if (CARRY)
+ SETFLAG(H_Z80);
+ else
+ CLRFLAG(H_Z80);
+
+ cpu->AF.b[LO]^=C_Z80;
+ SETHIDDEN(cpu->AF.b[HI]);
+ break;
+
+ LD_BLOCK(0x40,cpu->BC.b[HI],cpu->BC.b[HI])
+ LD_BLOCK(0x48,cpu->BC.b[LO],cpu->BC.b[LO])
+ LD_BLOCK(0x50,cpu->DE.b[HI],cpu->DE.b[HI])
+ LD_BLOCK(0x58,cpu->DE.b[LO],cpu->DE.b[LO])
+ LD_BLOCK(0x60,*H,cpu->HL.b[HI])
+ LD_BLOCK(0x68,*L,cpu->HL.b[LO])
+
+ case 0x70: /* LD (HL),B */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->BC.b[HI]);
+ break;
+
+ case 0x71: /* LD (HL),C */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->BC.b[LO]);
+ break;
+
+ case 0x72: /* LD (HL),D */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->DE.b[HI]);
+ break;
+
+ case 0x73: /* LD (HL),E */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->DE.b[LO]);
+ break;
+
+ case 0x74: /* LD (HL),H */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->HL.b[HI]);
+ break;
+
+ case 0x75: /* LD (HL),L */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->HL.b[LO]);
+ break;
+
+ case 0x76: /* HALT */
+ TSTATE(4);
+ cpu->PC--;
+
+ if (!PRIV->halt)
+ CALLBACK(eZ80_Halt,1);
+
+ PRIV->halt=TRUE;
+ break;
+
+ case 0x77: /* LD (HL),A */
+ TSTATE(7);
+ OFFSET(off);
+ POKE(*HL+off,cpu->AF.b[HI]);
+ break;
+
+ LD_BLOCK(0x78,cpu->AF.b[HI],cpu->AF.b[HI])
+
+ ALU_BLOCK(0x80,ADD8)
+ ALU_BLOCK(0x88,ADC8)
+ ALU_BLOCK(0x90,SUB8)
+ ALU_BLOCK(0x98,SBC8)
+ ALU_BLOCK(0xa0,AND)
+ ALU_BLOCK(0xa8,XOR)
+ ALU_BLOCK(0xb0,OR)
+ ALU_BLOCK(0xb8,CMP8)
+
+ case 0xc0: /* RET NZ */
+ RET_COND(!IS_Z);
+ break;
+
+ case 0xc1: /* POP BC */
+ TSTATE(10);
+ POP(cpu->BC.w);
+ break;
+
+ case 0xc2: /* JP NZ,nnnn */
+ JP_COND(!IS_Z);
+ break;
+
+ case 0xc3: /* JP nnnn */
+ JP_COND(1);
+ break;
+
+ case 0xc4: /* CALL NZ,nnnn */
+ CALL_COND(!IS_Z);
+ break;
+
+ case 0xc5: /* PUSH BC */
+ TSTATE(10);
+ PUSH(cpu->BC.w);
+ break;
+
+ case 0xc6: /* ADD A,n */
+ TSTATE(7);
+ ADD8(FETCH_BYTE);
+ break;
+
+ case 0xc7: /* RST 0 */
+ RST(0);
+ break;
+
+ case 0xc8: /* RET Z */
+ RET_COND(IS_Z);
+ break;
+
+ case 0xc9: /* RET */
+ TSTATE(10);
+ POP(cpu->PC);
+ break;
+
+ case 0xca: /* JP Z,nnnn */
+ JP_COND(IS_Z);
+ break;
+
+ case 0xcb: /* CB PREFIX */
+ INC_R;
+
+ /* Check for previous IX/IY shift.
+ */
+ if (PRIV->shift!=0)
+ {
+ Z80Relative cb_offset;
+
+ TSTATE(4); /* Wild stab in the dark! */
+ cb_offset=FETCH_BYTE;
+ ShiftedDecodeCB(cpu,FETCH_BYTE,cb_offset);
+ }
+ else
+ {
+ DecodeCB(cpu,FETCH_BYTE);
+ }
+ break;
+
+ case 0xcc: /* CALL Z,nnnn */
+ CALL_COND(IS_Z);
+ break;
+
+ case 0xcd: /* CALL nnnn */
+ CALL_COND(1);
+ break;
+
+ case 0xce: /* ADC A,n */
+ ADC8(FETCH_BYTE);
+ break;
+
+ case 0xcf: /* RST 8 */
+ RST(8);
+ break;
+
+ case 0xd0: /* RET NC */
+ RET_COND(!IS_C);
+ break;
+
+ case 0xd1: /* POP DE */
+ TSTATE(10);
+ POP(cpu->DE.w);
+ break;
+
+ case 0xd2: /* JP NC,nnnn */
+ JP_COND(!IS_C);
+ break;
+
+ case 0xd3: /* OUT (n),A */
+ TSTATE(11);
+ if (PRIV->pwrite)
+ {
+ Z80Word port;
+
+ port=FETCH_BYTE;
+ port|=(Z80Word)cpu->AF.b[HI]<<8;
+ PRIV->pwrite(cpu,port,cpu->AF.b[HI]);
+ }
+ else
+ cpu->PC++;
+ break;
+
+ case 0xd4: /* CALL NC,nnnn */
+ CALL_COND(!IS_C);
+ break;
+
+ case 0xd5: /* PUSH DE */
+ TSTATE(11);
+ PUSH(cpu->DE.w);
+ break;
+
+ case 0xd6: /* SUB A,n */
+ TSTATE(7);
+ SUB8(FETCH_BYTE);
+ break;
+
+ case 0xd7: /* RST 10 */
+ RST(0x10);
+ break;
+
+ case 0xd8: /* RET C */
+ RET_COND(IS_C);
+ break;
+
+ case 0xd9: /* EXX */
+ TSTATE(4);
+ SWAP(cpu->BC.w,cpu->BC_);
+ SWAP(cpu->DE.w,cpu->DE_);
+ SWAP(cpu->HL.w,cpu->HL_);
+ break;
+
+ case 0xda: /* JP C,nnnn */
+ JP_COND(IS_C);
+ break;
+
+ case 0xdb: /* IN A,(n) */
+ TSTATE(11);
+ if (PRIV->pread)
+ {
+ Z80Word port;
+
+ port=FETCH_BYTE;
+ port|=(Z80Word)cpu->AF.b[HI]<<8;
+ cpu->AF.b[HI]=PRIV->pread(cpu,port);
+ }
+ else
+ cpu->PC++;
+ break;
+
+ case 0xdc: /* CALL C,nnnn */
+ CALL_COND(IS_C);
+ break;
+
+ case 0xdd: /* DD PREFIX */
+ TSTATE(4);
+ INC_R;
+
+ PRIV->shift=opcode;
+ Z80_Decode(cpu,FETCH_BYTE);
+ break;
+
+ case 0xde: /* SBC A,n */
+ TSTATE(7);
+ SBC8(FETCH_BYTE);
+ break;
+
+ case 0xdf: /* RST 18 */
+ RST(0x18);
+ break;
+
+ case 0xe0: /* RET PO */
+ RET_COND(!IS_P);
+ break;
+
+ case 0xe1: /* POP HL */
+ TSTATE(10);
+ POP(*HL);
+ break;
+
+ case 0xe2: /* JP PO,nnnn */
+ JP_COND(!IS_P);
+ break;
+
+ case 0xe3: /* EX (SP),HL */
+ {
+ Z80Word tmp;
+ TSTATE(19);
+ POP(tmp);
+ PUSH(*HL);
+ *HL=tmp;
+ }
+ break;
+
+ case 0xe4: /* CALL PO,nnnn */
+ CALL_COND(!IS_P);
+ break;
+
+ case 0xe5: /* PUSH HL */
+ TSTATE(10);
+ PUSH(*HL);
+ break;
+
+ case 0xe6: /* AND A,n */
+ TSTATE(7);
+ AND(FETCH_BYTE);
+ break;
+
+ case 0xe7: /* RST 20 */
+ RST(0x20);
+ break;
+
+ case 0xe8: /* RET PE */
+ RET_COND(IS_P);
+ break;
+
+ case 0xe9: /* JP (HL) */
+ TSTATE(4);
+ cpu->PC=*HL;
+ break;
+
+ case 0xea: /* JP PE,nnnn */
+ JP_COND(IS_P);
+ break;
+
+ case 0xeb: /* EX DE,HL */
+ TSTATE(4);
+ SWAP(cpu->DE.w,*HL);
+ break;
+
+ case 0xec: /* CALL PE,nnnn */
+ CALL_COND(IS_P);
+ break;
+
+ case 0xed: /* ED PREFIX */
+ INC_R;
+ DecodeED(cpu,FETCH_BYTE);
+ break;
+
+ case 0xee: /* XOR A,n */
+ TSTATE(7);
+ XOR(FETCH_BYTE);
+ break;
+
+ case 0xef: /* RST 28 */
+ RST(0x28);
+ break;
+
+ case 0xf0: /* RET P */
+ RET_COND(!IS_S);
+ break;
+
+ case 0xf1: /* POP AF */
+ TSTATE(10);
+ POP(cpu->AF.w);
+ break;
+
+ case 0xf2: /* JP P,nnnn */
+ JP_COND(!IS_S);
+ break;
+
+ case 0xf3: /* DI */
+ TSTATE(4);
+ cpu->IFF1=0;
+ cpu->IFF2=0;
+ break;
+
+ case 0xf4: /* CALL P,nnnn */
+ CALL_COND(!IS_S);
+ break;
+
+ case 0xf5: /* PUSH AF */
+ TSTATE(10);
+ PUSH(cpu->AF.w);
+ break;
+
+ case 0xf6: /* OR A,n */
+ TSTATE(7);
+ OR(FETCH_BYTE);
+ break;
+
+ case 0xf7: /* RST 30 */
+ RST(0x30);
+ break;
+
+ case 0xf8: /* RET M */
+ RET_COND(IS_S);
+ break;
+
+ case 0xf9: /* LD SP,HL */
+ TSTATE(6);
+ cpu->SP=*HL;
+ break;
+
+ case 0xfa: /* JP M,nnnn */
+ JP_COND(IS_S);
+ break;
+
+ case 0xfb: /* EI */
+ TSTATE(4);
+ cpu->IFF1=1;
+ cpu->IFF2=1;
+ break;
+
+ case 0xfc: /* CALL M,nnnn */
+ CALL_COND(IS_S);
+ break;
+
+ case 0xfd: /* FD PREFIX */
+ TSTATE(4);
+ INC_R;
+
+ PRIV->shift=opcode;
+ Z80_Decode(cpu,FETCH_BYTE);
+ break;
+
+ case 0xfe: /* CP A,n */
+ TSTATE(7);
+ CMP8(FETCH_BYTE);
+ break;
+
+ case 0xff: /* RST 38 */
+ RST(0x38);
+ break;
+
+ }
+}
+
+
+/* END OF FILE */
diff --git a/source/z80_dis.c b/source/z80_dis.c
new file mode 100644
index 0000000..6c01b28
--- /dev/null
+++ b/source/z80_dis.c
@@ -0,0 +1,2491 @@
+/*
+
+ z80 - Z80 Emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ $Id: z80_dis.c 4 2006-10-04 23:05:43Z ianc $
+
+*/
+#include "z80_config.h"
+
+#ifdef ENABLE_DISASSEM
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "z80.h"
+#include "z80_private.h"
+
+static Z80Relative cb_off;
+
+/* ---------------------------------------- SHARED ROUTINES
+*/
+static const char *z80_dis_reg8[]={"b","c","d","e","h","l","(hl)","a"};
+static const char *z80_dis_reg16[]={"bc","de","hl","sp"};
+static const char *z80_dis_condition[]={"nz","z","nc","c","po","pe","p","m"};
+
+static const char *dis_op;
+static const char *dis_arg;
+
+const char *Z80_Dis_Printf(const char *format, ...)
+{
+ static int p=0;
+ static char s[16][80];
+ va_list arg;
+
+ va_start(arg,format);
+ p=(p+1)%16;
+ vsprintf(s[p],format,arg);
+ va_end(arg);
+
+ return s[p];
+}
+
+
+Z80Byte Z80_Dis_FetchByte(Z80 *cpu, Z80Word *pc)
+{
+#ifdef ENABLE_ARRAY_MEMORY
+ return Z80_MEMORY[(*pc)++];
+#else
+ return cpu->priv->disread(cpu,(*pc)++);
+#endif
+}
+
+
+Z80Word Z80_Dis_FetchWord(Z80 *cpu, Z80Word *pc)
+{
+ Z80Byte l,h;
+
+ l=Z80_Dis_FetchByte(cpu,pc);
+ h=Z80_Dis_FetchByte(cpu,pc);
+
+ return ((Z80Word)h<<8)|l;
+}
+
+
+void Z80_Dis_Set(const char *op, const char *arg)
+{
+ dis_op=op;
+ dis_arg=arg;
+}
+
+
+const char *Z80_Dis_GetOp(void)
+{
+ return dis_op ? dis_op : "";
+}
+
+
+const char *Z80_Dis_GetArg(void)
+{
+ return dis_arg ? dis_arg : "";
+}
+
+
+static const char *GetLabel(Z80Word w)
+{
+ if (z80_labels)
+ {
+ int f;
+
+ for(f=0;z80_labels[f].label;f++)
+ {
+ if (z80_labels[f].address==w)
+ {
+ return z80_labels[f].label;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* ---------------------------------------- CB xx BYTE OPCODES
+*/
+static void DIS_RLC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("rlc",reg);
+}
+
+static void DIS_RRC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("rrc",reg);
+}
+
+static void DIS_RL_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("rl",reg);
+}
+
+static void DIS_RR_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("rr",reg);
+}
+
+static void DIS_SLA_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("sla",reg);
+}
+
+static void DIS_SRA_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("sra",reg);
+}
+
+static void DIS_SLL_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("sll",reg);
+}
+
+static void DIS_SRL_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("srl",reg);
+}
+
+static void DIS_BIT_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+ int bit;
+
+ reg=z80_dis_reg8[op%8];
+ bit=(op-0x40)/8;
+ Z80_Dis_Set("bit",Z80_Dis_Printf("%d,%s",bit,reg));
+}
+
+static void DIS_RES_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+ int bit;
+
+ reg=z80_dis_reg8[op%8];
+ bit=(op-0x80)/8;
+ Z80_Dis_Set("res",Z80_Dis_Printf("%d,%s",bit,reg));
+}
+
+static void DIS_SET_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+ int bit;
+
+ reg=z80_dis_reg8[op%8];
+ bit=(op-0xc0)/8;
+ Z80_Dis_Set("set",Z80_Dis_Printf("%d,%s",bit,reg));
+}
+
+/* ---------------------------------------- DD OPCODES
+*/
+
+static const char *IX_RelStr(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ static char s[80];
+ Z80Relative r;
+
+ r=(Z80Relative)Z80_Dis_FetchByte(z80,pc);
+
+ if (r<0)
+ sprintf(s,"(ix-$%.2x)",-r);
+ else
+ sprintf(s,"(ix+$%.2x)",r);
+
+ return(s);
+}
+
+
+static const char *IX_RelStrCB(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ static char s[80];
+ Z80Relative r;
+
+ r=(Z80Relative)cb_off;
+
+ if (r<0)
+ sprintf(s,"(ix-$%.2x)",-r);
+ else
+ sprintf(s,"(ix+$%.2x)",r);
+
+ return(s);
+}
+
+
+static const char *XR8(Z80 *z80, int reg, Z80Word *pc)
+{
+ switch(reg)
+ {
+ case 0:
+ return("b");
+ break;
+ case 1:
+ return("c");
+ break;
+ case 2:
+ return("d");
+ break;
+ case 3:
+ return("e");
+ break;
+ case 4:
+ return("ixh");
+ break;
+ case 5:
+ return("ixl");
+ break;
+ case 6:
+ return(IX_RelStr(z80,0,pc));
+ break;
+ case 7:
+ return("a");
+ break;
+ default:
+ return(Z80_Dis_Printf("BUG %d",reg));
+ break;
+ }
+}
+
+static void DIS_DD_NOP(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ dis_opcode_z80[op](z80,op,pc);
+}
+
+static void DIS_ADD_IX_BC(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","ix,bc");
+}
+
+static void DIS_ADD_IX_DE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","ix,de");
+}
+
+static void DIS_LD_IX_WORD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("ix,$%.4x",Z80_Dis_FetchWord(z80,pc)));
+}
+
+static void DIS_LD_ADDR_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),ix",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("($%.4x),ix",w));
+}
+
+static void DIS_INC_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","ix");
+}
+
+static void DIS_INC_IXH(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","ixh");
+}
+
+static void DIS_DEC_IXH(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","ixh");
+}
+
+static void DIS_LD_IXH_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("ixh,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_ADD_IX_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","ix,ix");
+}
+
+static void DIS_LD_IX_ADDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("ix,(%s)",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("ix,($%.4x)",w));
+}
+
+static void DIS_DEC_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","ix");
+}
+
+static void DIS_INC_IXL(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","ixl");
+}
+
+static void DIS_DEC_IXL(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","ixl");
+}
+
+static void DIS_LD_IXL_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("ixl,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_INC_IIX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc",Z80_Dis_Printf("%s",IX_RelStr(z80,op,pc)));
+}
+
+static void DIS_DEC_IIX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec",Z80_Dis_Printf("%s",IX_RelStr(z80,op,pc)));
+}
+
+static void DIS_LD_IIX_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *rel;
+ int b;
+
+ rel=IX_RelStr(z80,op,pc);
+ b=Z80_Dis_FetchByte(z80,pc);
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,$%.2x",rel,b));
+}
+
+
+static void DIS_ADD_IX_SP(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","ix,sp");
+}
+
+static void DIS_XLD_R8_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int src_r,dest_r;
+ const char *src,*dest;
+
+ dest_r=(op-0x40)/8;
+ src_r=op%8;
+
+ /* IX can't be used as source and destination when reading z80ory
+ */
+ if (dest_r==6)
+ {
+ dest=XR8(z80,dest_r,pc);
+ src=z80_dis_reg8[src_r];
+ }
+ else if (((dest_r==4)||(dest_r==5))&&(src_r==6))
+ {
+ dest=z80_dis_reg8[dest_r];
+ src=XR8(z80,src_r,pc);
+ }
+ else
+ {
+ dest=XR8(z80,dest_r,pc);
+ src=XR8(z80,src_r,pc);
+ }
+
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,%s",dest,src));
+}
+
+static void DIS_XADD_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add",Z80_Dis_Printf("a,%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XADC_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("adc",Z80_Dis_Printf("a,%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XSUB_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sub",Z80_Dis_Printf("a,%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XSBC_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sbc",Z80_Dis_Printf("a,%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XAND_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("and",Z80_Dis_Printf("%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XXOR_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("xor",Z80_Dis_Printf("%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_X_OR_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("or",Z80_Dis_Printf("%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_XCP_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cp",Z80_Dis_Printf("%s",XR8(z80,(op%8),pc)));
+}
+
+static void DIS_POP_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("pop","ix");
+}
+
+static void DIS_EX_ISP_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ex","(sp),ix");
+}
+
+static void DIS_PUSH_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("push","ix");
+}
+
+static void DIS_JP_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("jp","(ix)");
+}
+
+static void DIS_LD_SP_IX(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","sp,ix");
+}
+
+static void DIS_DD_CB_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ cb_off=(Z80Relative)Z80_Dis_FetchByte(z80,pc);
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_DD_CB_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_DD_DD_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_DD_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_DD_ED_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_ED_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_DD_FD_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_FD_opcode[nop](z80,nop,pc);
+}
+
+
+/* ---------------------------------------- DD CB OPCODES
+*/
+
+static void DIS_RLC_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rlc",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ {
+ Z80_Dis_Set("rlc",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+ }
+}
+
+static void DIS_RRC_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rrc",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rrc",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RL_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rl",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rl",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RR_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rr",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rr",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SLA_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sla",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sla",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SRA_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sra",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sra",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SRL_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("srl",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("srl",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SLL_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sll",Z80_Dis_Printf("%s",IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sll",Z80_Dis_Printf("%s[%s]",IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_BIT_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0x40)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("bit",Z80_Dis_Printf("%d,%s",bit,IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("bit",Z80_Dis_Printf("%d,%s[%s]",bit,IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RES_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0x80)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("res",Z80_Dis_Printf("%d,%s",bit,IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("res",Z80_Dis_Printf("%d,%s[%s]",bit,IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SET_IX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0xc0)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("set",Z80_Dis_Printf("%d,%s",bit,IX_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("set",Z80_Dis_Printf("%d,%s[%s]",bit,IX_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+
+/* ---------------------------------------- ED OPCODES
+*/
+
+static const char *ER8(int reg)
+{
+ switch(reg)
+ {
+ case 0:
+ return("b");
+ break;
+ case 1:
+ return("c");
+ break;
+ case 2:
+ return("d");
+ break;
+ case 3:
+ return("e");
+ break;
+ case 4:
+ return("h");
+ break;
+ case 5:
+ return("l");
+ break;
+ case 6:
+ return("0");
+ break;
+ case 7:
+ return("a");
+ break;
+ }
+
+ return "?";
+}
+
+/* Assumes illegal ED ops are being used for break points
+*/
+static void DIS_ED_NOP(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("brk",Z80_Dis_Printf("$%.2x",op));
+}
+
+static void DIS_IN_R8_C(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("in",Z80_Dis_Printf("%s,(c)",ER8((op-0x40)/8)));
+}
+
+static void DIS_OUT_C_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("out",Z80_Dis_Printf("(c),%s",ER8((op-0x40)/8)));
+}
+
+static void DIS_SBC_HL_R16(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sbc",Z80_Dis_Printf("hl,%s",z80_dis_reg16[(op-0x40)/16]));
+}
+
+static void DIS_ED_LD_ADDR_R16(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),%s",p,z80_dis_reg16[(op-0x40)/16]));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("($%.4x),%s",w,z80_dis_reg16[(op-0x40)/16]));
+}
+
+static void DIS_NEG(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("neg",NULL);
+}
+
+static void DIS_RETN(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("retn",NULL);
+}
+
+static void DIS_IM_0(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("im","0");
+}
+
+static void DIS_LD_I_A(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","i,a");
+}
+
+static void DIS_ADC_HL_R16(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("adc",Z80_Dis_Printf("hl,%s",z80_dis_reg16[(op-0x40)/16]));
+}
+
+static void DIS_ED_LD_R16_ADDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,(%s)",z80_dis_reg16[(op-0x40)/16],p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,($%.4x)",z80_dis_reg16[(op-0x40)/16],w));
+}
+
+static void DIS_RETI(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("reti",NULL);
+}
+
+static void DIS_LD_R_A(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","r,a");
+}
+
+static void DIS_IM_1(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("im","1");
+}
+
+static void DIS_LD_A_I(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","a,i");
+}
+
+static void DIS_IM_2(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("im","2");
+}
+
+static void DIS_LD_A_R(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","a,r");
+}
+
+static void DIS_RRD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rrd",NULL);
+}
+
+static void DIS_RLD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rld",NULL);
+}
+
+static void DIS_LDI(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ldi",NULL);
+}
+
+static void DIS_CPI(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cpi",NULL);
+}
+
+static void DIS_INI(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ini",NULL);
+}
+
+static void DIS_OUTI(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("outi",NULL);
+}
+
+static void DIS_LDD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ldd",NULL);
+}
+
+static void DIS_CPD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cpd",NULL);
+}
+
+static void DIS_IND(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ind",NULL);
+}
+
+static void DIS_OUTD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("outd",NULL);
+}
+
+static void DIS_LDIR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ldir",NULL);
+}
+
+static void DIS_CPIR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cpir",NULL);
+}
+
+static void DIS_INIR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inir",NULL);
+}
+
+static void DIS_OTIR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("otir",NULL);
+}
+
+static void DIS_LDDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("lddr",NULL);
+}
+
+static void DIS_CPDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cpdr",NULL);
+}
+
+static void DIS_INDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("indr",NULL);
+}
+
+static void DIS_OTDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("otdr",NULL);
+}
+
+
+/* ---------------------------------------- FD OPCODES
+*/
+
+static const char *IY_RelStr(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ static char s[80];
+ Z80Relative r;
+
+ r=(Z80Relative)Z80_Dis_FetchByte(z80,pc);
+
+ if (r<0)
+ sprintf(s,"(iy-$%.2x)",-r);
+ else
+ sprintf(s,"(iy+$%.2x)",r);
+
+ return(s);
+}
+
+
+static const char *IY_RelStrCB(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ static char s[80];
+ Z80Relative r;
+
+ r=(Z80Relative)cb_off;
+
+ if (r<0)
+ sprintf(s,"(iy-$%.2x)",-r);
+ else
+ sprintf(s,"(iy+$%.2x)",r);
+
+ return(s);
+}
+
+
+static const char *YR8(Z80 *z80, int reg, Z80Word *pc)
+{
+ switch(reg)
+ {
+ case 0:
+ return("b");
+ break;
+ case 1:
+ return("c");
+ break;
+ case 2:
+ return("d");
+ break;
+ case 3:
+ return("e");
+ break;
+ case 4:
+ return("iyh");
+ break;
+ case 5:
+ return("iyl");
+ break;
+ case 6:
+ return(IY_RelStr(z80,0,pc));
+ break;
+ case 7:
+ return("a");
+ break;
+ default:
+ return(Z80_Dis_Printf("BUG %d",reg));
+ break;
+ }
+}
+
+static void DIS_FD_NOP(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ dis_opcode_z80[op](z80,op,pc);
+}
+
+static void DIS_ADD_IY_BC(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","iy,bc");
+}
+
+static void DIS_ADD_IY_DE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","iy,de");
+}
+
+static void DIS_LD_IY_WORD(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("iy,$%.4x",Z80_Dis_FetchWord(z80,pc)));
+}
+
+static void DIS_LD_ADDR_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),iy",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("($%.4x),iy",w));
+}
+
+static void DIS_INC_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","iy");
+}
+
+static void DIS_INC_IYH(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","iyh");
+}
+
+static void DIS_DEC_IYH(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","iyh");
+}
+
+static void DIS_LD_IYH_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("iyh,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_ADD_IY_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","iy,iy");
+}
+
+static void DIS_LD_IY_ADDR(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("iy,(%s)",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("iy,($%.4x)",w));
+}
+
+static void DIS_DEC_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","iy");
+}
+
+static void DIS_INC_IYL(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc","iyl");
+}
+
+static void DIS_DEC_IYL(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec","iyl");
+}
+
+static void DIS_LD_IYL_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld",Z80_Dis_Printf("iyl,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_INC_IIY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("inc",Z80_Dis_Printf("%s",IY_RelStr(z80,op,pc)));
+}
+
+static void DIS_DEC_IIY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("dec",Z80_Dis_Printf("%s",IY_RelStr(z80,op,pc)));
+}
+
+static void DIS_LD_IIY_BYTE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *rel;
+ int b;
+
+ rel=IY_RelStr(z80,op,pc);
+ b=Z80_Dis_FetchByte(z80,pc);
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,$%.2x",rel,b));
+}
+
+
+static void DIS_ADD_IY_SP(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add","iy,sp");
+}
+
+static void DIS_YLD_R8_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int src_r,dest_r;
+ const char *src,*dest;
+
+ dest_r=(op-0x40)/8;
+ src_r=op%8;
+
+ /* IY can't be used as source and destination when reading z80ory
+ */
+ if (dest_r==6)
+ {
+ dest=YR8(z80,dest_r,pc);
+ src=z80_dis_reg8[src_r];
+ }
+ else if (((dest_r==4)||(dest_r==5))&&(src_r==6))
+ {
+ dest=z80_dis_reg8[dest_r];
+ src=YR8(z80,src_r,pc);
+ }
+ else
+ {
+ dest=YR8(z80,dest_r,pc);
+ src=YR8(z80,src_r,pc);
+ }
+
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,%s",dest,src));
+}
+
+static void DIS_YADD_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add",Z80_Dis_Printf("a,%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YADC_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("adc",Z80_Dis_Printf("a,%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YSUB_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sub",Z80_Dis_Printf("a,%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YSBC_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sbc",Z80_Dis_Printf("a,%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YAND_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("and",Z80_Dis_Printf("%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YYOR_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("xor",Z80_Dis_Printf("%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_Y_OR_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("or",Z80_Dis_Printf("%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_YCP_R8(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cp",Z80_Dis_Printf("%s",YR8(z80,(op%8),pc)));
+}
+
+static void DIS_POP_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("pop","iy");
+}
+
+static void DIS_EY_ISP_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ex","(sp),iy");
+}
+
+static void DIS_PUSH_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("push","iy");
+}
+
+static void DIS_JP_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("jp","(iy)");
+}
+
+static void DIS_LD_SP_IY(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","sp,iy");
+}
+
+static void DIS_FD_CB_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ cb_off=(Z80Relative)Z80_Dis_FetchByte(z80,pc);
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_FD_CB_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_FD_DD_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_DD_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_FD_ED_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_ED_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_FD_FD_DECODE(Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_FD_opcode[nop](z80,nop,pc);
+}
+
+
+/* ---------------------------------------- FD CB OPCODES
+*/
+
+static void DIS_RLC_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rlc",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rlc",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RRC_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rrc",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rrc",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RL_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rl",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rl",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RR_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("rr",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("rr",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SLA_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sla",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sla",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SRA_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sra",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sra",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SRL_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("srl",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("srl",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SLL_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+
+ reg=(op%8);
+
+ if (reg==6)
+ Z80_Dis_Set("sll",Z80_Dis_Printf("%s",IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("sll",Z80_Dis_Printf("%s[%s]",IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_BIT_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0x40)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("bit",Z80_Dis_Printf("%d,%s",bit,IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("bit",Z80_Dis_Printf("%d,%s[%s]",bit,IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_RES_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0x80)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("res",Z80_Dis_Printf("%d,%s",bit,IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("res",Z80_Dis_Printf("%d,%s[%s]",bit,IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+static void DIS_SET_IY (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int reg;
+ int bit;
+
+ reg=(op%8);
+ bit=(op-0xc0)/8;
+
+ if (reg==6)
+ Z80_Dis_Set("set",Z80_Dis_Printf("%d,%s",bit,IY_RelStrCB(z80,op,pc)));
+ else
+ Z80_Dis_Set("set",Z80_Dis_Printf("%d,%s[%s]",bit,IY_RelStrCB(z80,op,pc),z80_dis_reg8[reg]));
+}
+
+
+/* ---------------------------------------- SINGLE BYTE OPCODES
+*/
+static void DIS_NOP (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("nop",NULL);
+}
+
+static void DIS_LD_R16_WORD (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,$%.4x",reg,Z80_Dis_FetchWord(z80,pc)));
+}
+
+static void DIS_LD_R16_A (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),a",reg));
+}
+
+static void DIS_INC_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("inc",reg);
+}
+
+static void DIS_INC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[(op&0x38)/0x8];
+ Z80_Dis_Set("inc",reg);
+}
+
+static void DIS_DEC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[(op&0x38)/0x8];
+ Z80_Dis_Set("dec",reg);
+}
+
+static void DIS_LD_R8_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[(op&0x38)/0x8];
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,$%.2x",reg,Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_RLCA (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rlca",NULL);
+}
+
+static void DIS_EX_AF_AF (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ex","af,af'");
+}
+
+static void DIS_ADD_HL_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("add",Z80_Dis_Printf("hl,%s",reg));
+}
+
+static void DIS_LD_A_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("ld",Z80_Dis_Printf("a,(%s)",reg));
+}
+
+static void DIS_DEC_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op&0x30)/0x10];
+ Z80_Dis_Set("dec",reg);
+}
+
+static void DIS_RRCA (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rrca",NULL);
+}
+
+static void DIS_DJNZ (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word new;
+
+#ifdef ENABLE_ARRAY_MEMORY
+ new=*pc+(Z80Relative)Z80_MEMORY[*pc]+1;
+#else
+ new=*pc+(Z80Relative)z80->priv->disread(z80,*pc)+1;
+#endif
+ (*pc)++;
+ Z80_Dis_Set("djnz",Z80_Dis_Printf("$%.4x",new));
+}
+
+static void DIS_RLA (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rla",NULL);
+}
+
+static void DIS_JR (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word new;
+ const char *p;
+
+#ifdef ENABLE_ARRAY_MEMORY
+ new=*pc+(Z80Relative)Z80_MEMORY[*pc]+1;
+#else
+ new=*pc+(Z80Relative)z80->priv->disread(z80,*pc)+1;
+#endif
+ (*pc)++;
+
+ if ((p=GetLabel(new)))
+ Z80_Dis_Set("jr",Z80_Dis_Printf("%s",p));
+ else
+ Z80_Dis_Set("jr",Z80_Dis_Printf("$%.4x",new));
+}
+
+static void DIS_RRA (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("rra",NULL);
+}
+
+static void DIS_JR_CO (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *con;
+ Z80Word new;
+ const char *p;
+
+ con=z80_dis_condition[(op-0x20)/8];
+#ifdef ENABLE_ARRAY_MEMORY
+ new=*pc+(Z80Relative)Z80_MEMORY[*pc]+1;
+#else
+ new=*pc+(Z80Relative)z80->priv->disread(z80,*pc)+1;
+#endif
+ (*pc)++;
+
+ if ((p=GetLabel(new)))
+ Z80_Dis_Set("jr",Z80_Dis_Printf("%s,%s",con,p));
+ else
+ Z80_Dis_Set("jr",Z80_Dis_Printf("%s,$%.4x",con,new));
+}
+
+static void DIS_LD_ADDR_HL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),hl",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("($%.4x),hl",w));
+}
+
+static void DIS_DAA (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("daa",NULL);
+}
+
+static void DIS_LD_HL_ADDR (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("hl,(%s)",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("hl,($%.4x)",w));
+}
+
+static void DIS_CPL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cpl",NULL);
+}
+
+static void DIS_LD_ADDR_A (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("(%s),a",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("($%.4x),a",w));
+}
+
+static void DIS_SCF (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("scf",NULL);
+}
+
+static void DIS_LD_A_ADDR (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("ld",Z80_Dis_Printf("a,(%s)",p));
+ else
+ Z80_Dis_Set("ld",Z80_Dis_Printf("a,($%.4x)",w));
+}
+
+static void DIS_CCF (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ccf",NULL);
+}
+
+static void DIS_LD_R8_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *src,*dest;
+
+ dest=z80_dis_reg8[(op-0x40)/8];
+ src=z80_dis_reg8[op%8];
+ Z80_Dis_Set("ld",Z80_Dis_Printf("%s,%s",dest,src));
+}
+
+static void DIS_HALT (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("halt",NULL);
+}
+
+static void DIS_ADD_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("add",Z80_Dis_Printf("a,%s",reg));
+}
+
+static void DIS_ADC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("adc",Z80_Dis_Printf("a,%s",reg));
+}
+
+static void DIS_SUB_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("sub",Z80_Dis_Printf("a,%s",reg));
+}
+
+static void DIS_SBC_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("sbc",Z80_Dis_Printf("a,%s",reg));
+}
+
+static void DIS_AND_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("and",Z80_Dis_Printf("%s",reg));
+}
+
+static void DIS_XOR_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("xor",Z80_Dis_Printf("%s",reg));
+}
+
+static void DIS_OR_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("or",Z80_Dis_Printf("%s",reg));
+}
+
+static void DIS_CP_R8 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg8[op%8];
+ Z80_Dis_Set("cp",Z80_Dis_Printf("%s",reg));
+}
+
+
+static void DIS_RET_CO (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *con;
+
+ con=z80_dis_condition[(op-0xc0)/8];
+ Z80_Dis_Set("ret",con);
+}
+
+static void DIS_POP_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op-0xc0)/16];
+
+ if (!strcmp(reg,"sp"))
+ reg="af";
+
+ Z80_Dis_Set("pop",reg);
+}
+
+static void DIS_JP (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("jp",Z80_Dis_Printf("%s",p));
+ else
+ Z80_Dis_Set("jp",Z80_Dis_Printf("$%.4x",w));
+}
+
+static void DIS_PUSH_R16 (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *reg;
+
+ reg=z80_dis_reg16[(op-0xc0)/16];
+
+ if (!strcmp(reg,"sp"))
+ reg="af";
+
+ Z80_Dis_Set("push",reg);
+}
+
+static void DIS_ADD_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("add",Z80_Dis_Printf("a,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_RST (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int add;
+
+ add=(op&0x3f)-7;
+ Z80_Dis_Set("rst",Z80_Dis_Printf("%.2xh",add));
+}
+
+static void DIS_RET (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ret",NULL);
+}
+
+static void DIS_JP_CO (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *con;
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+ con=z80_dis_condition[(op-0xc0)/8];
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("jp",Z80_Dis_Printf("%s,%s",con,p));
+ else
+ Z80_Dis_Set("jp",Z80_Dis_Printf("%s,$%.4x",con,w));
+}
+
+static void DIS_CB_DECODE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_CB_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_CALL_CO (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ const char *con;
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+ con=z80_dis_condition[(op-0xc0)/8];
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("call",Z80_Dis_Printf("%s,%s",con,p));
+ else
+ Z80_Dis_Set("call",Z80_Dis_Printf("%s,$%.4x",con,w));
+}
+
+static void DIS_CALL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80Word w;
+ const char *p;
+
+ w=Z80_Dis_FetchWord(z80,pc);
+
+ if ((p=GetLabel(w)))
+ Z80_Dis_Set("call",Z80_Dis_Printf("%s",p));
+ else
+ Z80_Dis_Set("call",Z80_Dis_Printf("$%.4x",w));
+}
+
+static void DIS_ADC_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("adc",Z80_Dis_Printf("a,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_OUT_BYTE_A (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("out",Z80_Dis_Printf("($%.2x),a",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_SUB_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sub",Z80_Dis_Printf("a,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_EXX (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("exx",NULL);
+}
+
+static void DIS_IN_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("in",Z80_Dis_Printf("a,($%.2x)",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_DD_DECODE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_DD_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_SBC_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("sbc",Z80_Dis_Printf("a,$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+
+static void DIS_EX_ISP_HL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ex","(sp),hl");
+}
+
+static void DIS_AND_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("and",Z80_Dis_Printf("$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_JP_HL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("jp","(hl)");
+}
+
+static void DIS_EX_DE_HL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ex","de,hl");
+}
+
+static void DIS_ED_DECODE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_ED_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_XOR_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("xor",Z80_Dis_Printf("$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_DI (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("di",NULL);
+}
+
+static void DIS_OR_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("or",Z80_Dis_Printf("$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+static void DIS_LD_SP_HL (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ld","sp,hl");
+}
+
+static void DIS_EI (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("ei",NULL);
+}
+
+static void DIS_FD_DECODE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ int nop;
+
+ nop=Z80_Dis_FetchByte(z80,pc);
+ dis_FD_opcode[nop](z80,nop,pc);
+}
+
+static void DIS_CP_A_BYTE (Z80 *z80, Z80Byte op, Z80Word *pc)
+{
+ Z80_Dis_Set("cp",Z80_Dis_Printf("$%.2x",Z80_Dis_FetchByte(z80,pc)));
+}
+
+
+/* ---------------------------------------- TABLES
+*/
+
+/* CB opcodes
+*/
+DIS_OP_CALLBACK dis_CB_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_RLC_R8, DIS_RLC_R8, DIS_RLC_R8, DIS_RLC_R8,
+/* 0x04 - 0x07 */ DIS_RLC_R8, DIS_RLC_R8, DIS_RLC_R8, DIS_RLC_R8,
+/* 0x08 - 0x0b */ DIS_RRC_R8, DIS_RRC_R8, DIS_RRC_R8, DIS_RRC_R8,
+/* 0x0c - 0x0f */ DIS_RRC_R8, DIS_RRC_R8, DIS_RRC_R8, DIS_RRC_R8,
+
+/* 0x10 - 0x13 */ DIS_RL_R8, DIS_RL_R8, DIS_RL_R8, DIS_RL_R8,
+/* 0x14 - 0x17 */ DIS_RL_R8, DIS_RL_R8, DIS_RL_R8, DIS_RL_R8,
+/* 0x18 - 0x1b */ DIS_RR_R8, DIS_RR_R8, DIS_RR_R8, DIS_RR_R8,
+/* 0x1c - 0x1f */ DIS_RR_R8, DIS_RR_R8, DIS_RR_R8, DIS_RR_R8,
+
+/* 0x20 - 0x23 */ DIS_SLA_R8, DIS_SLA_R8, DIS_SLA_R8, DIS_SLA_R8,
+/* 0x24 - 0x27 */ DIS_SLA_R8, DIS_SLA_R8, DIS_SLA_R8, DIS_SLA_R8,
+/* 0x28 - 0x2b */ DIS_SRA_R8, DIS_SRA_R8, DIS_SRA_R8, DIS_SRA_R8,
+/* 0x2c - 0x2f */ DIS_SRA_R8, DIS_SRA_R8, DIS_SRA_R8, DIS_SRA_R8,
+
+/* 0x30 - 0x33 */ DIS_SLL_R8, DIS_SLL_R8, DIS_SLL_R8, DIS_SLL_R8,
+/* 0x34 - 0x37 */ DIS_SLL_R8, DIS_SLL_R8, DIS_SLL_R8, DIS_SLL_R8,
+/* 0x38 - 0x3b */ DIS_SRL_R8, DIS_SRL_R8, DIS_SRL_R8, DIS_SRL_R8,
+/* 0x3c - 0x3f */ DIS_SRL_R8, DIS_SRL_R8, DIS_SRL_R8, DIS_SRL_R8,
+
+/* 0x40 - 0x43 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x44 - 0x47 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x48 - 0x4b */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x4c - 0x4f */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+
+/* 0x50 - 0x53 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x54 - 0x57 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x58 - 0x5b */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x5c - 0x5f */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+
+/* 0x60 - 0x63 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x64 - 0x67 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x68 - 0x6b */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x6c - 0x6f */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+
+/* 0x70 - 0x73 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x74 - 0x77 */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x78 - 0x7b */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+/* 0x7c - 0x7f */ DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8, DIS_BIT_R8,
+
+/* 0x80 - 0x83 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x84 - 0x87 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x88 - 0x8b */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x8c - 0x8f */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+
+/* 0x90 - 0x93 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x94 - 0x97 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x98 - 0x9b */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0x9c - 0x9f */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+
+/* 0xa0 - 0xa3 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xa4 - 0xa7 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xa8 - 0xab */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xac - 0xaf */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+
+/* 0xb0 - 0xb3 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xb4 - 0xb7 */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xb8 - 0xbb */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+/* 0xbc - 0xbf */ DIS_RES_R8, DIS_RES_R8, DIS_RES_R8, DIS_RES_R8,
+
+/* 0xc0 - 0xc3 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xc4 - 0xc7 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xc8 - 0xcb */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xcc - 0xcf */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+
+/* 0xd0 - 0xd3 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xd4 - 0xd7 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xd8 - 0xdb */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xdc - 0xdf */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+
+/* 0xe0 - 0xe3 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xe4 - 0xe7 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xe8 - 0xeb */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xec - 0xef */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+
+/* 0xf0 - 0xf3 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xf4 - 0xf7 */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xf8 - 0xfb */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+/* 0xfc - 0xff */ DIS_SET_R8, DIS_SET_R8, DIS_SET_R8, DIS_SET_R8,
+ };
+
+/* DIS_DD opcodes
+*/
+DIS_OP_CALLBACK dis_DD_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x04 - 0x07 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x08 - 0x0b */ DIS_DD_NOP, DIS_ADD_IX_BC, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x0c - 0x0f */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0x10 - 0x13 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x14 - 0x17 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x18 - 0x1b */ DIS_DD_NOP, DIS_ADD_IX_DE, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x1c - 0x1f */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0x20 - 0x23 */ DIS_DD_NOP, DIS_LD_IX_WORD, DIS_LD_ADDR_IX, DIS_INC_IX,
+/* 0x24 - 0x27 */ DIS_INC_IXH, DIS_DEC_IXH, DIS_LD_IXH_BYTE, DIS_DD_NOP,
+/* 0x28 - 0x2b */ DIS_DD_NOP, DIS_ADD_IX_IX, DIS_LD_IX_ADDR, DIS_DEC_IX,
+/* 0x2c - 0x2f */ DIS_INC_IXL, DIS_DEC_IXL, DIS_LD_IXL_BYTE, DIS_DD_NOP,
+
+/* 0x30 - 0x33 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x34 - 0x37 */ DIS_INC_IIX, DIS_DEC_IIX, DIS_LD_IIX_BYTE, DIS_DD_NOP,
+/* 0x38 - 0x3b */ DIS_DD_NOP, DIS_ADD_IX_SP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x3c - 0x3f */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0x40 - 0x43 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x44 - 0x47 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP,
+/* 0x48 - 0x4b */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x4c - 0x4f */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP,
+
+/* 0x50 - 0x53 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x54 - 0x57 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP,
+/* 0x58 - 0x5b */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x5c - 0x5f */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP,
+
+/* 0x60 - 0x63 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8,
+/* 0x64 - 0x67 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8,
+/* 0x68 - 0x6b */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8,
+/* 0x6c - 0x6f */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8,
+
+/* 0x70 - 0x73 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8,
+/* 0x74 - 0x77 */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP, DIS_XLD_R8_R8,
+/* 0x78 - 0x7b */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x7c - 0x7f */ DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_XLD_R8_R8, DIS_DD_NOP,
+
+/* 0x80 - 0x83 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x84 - 0x87 */ DIS_XADD_R8, DIS_XADD_R8, DIS_XADD_R8, DIS_DD_NOP,
+/* 0x88 - 0x8b */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x8c - 0x8f */ DIS_XADC_R8, DIS_XADC_R8, DIS_XADC_R8, DIS_DD_NOP,
+
+/* 0x90 - 0x93 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x94 - 0x97 */ DIS_XSUB_R8, DIS_XSUB_R8, DIS_XSUB_R8, DIS_DD_NOP,
+/* 0x98 - 0x9b */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0x9c - 0x9f */ DIS_XSBC_R8, DIS_XSBC_R8, DIS_XSBC_R8, DIS_DD_NOP,
+
+/* 0xa0 - 0xa3 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xa4 - 0xa7 */ DIS_XAND_R8, DIS_XAND_R8, DIS_XAND_R8, DIS_DD_NOP,
+/* 0xa8 - 0xab */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xac - 0xaf */ DIS_XXOR_R8, DIS_XXOR_R8, DIS_XXOR_R8, DIS_DD_NOP,
+
+/* 0xb0 - 0xb3 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xb4 - 0xb7 */ DIS_X_OR_R8, DIS_X_OR_R8, DIS_X_OR_R8, DIS_DD_NOP,
+/* 0xb8 - 0xbb */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xbc - 0xbf */ DIS_XCP_R8, DIS_XCP_R8, DIS_XCP_R8, DIS_DD_NOP,
+
+/* 0xc0 - 0xc3 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xc4 - 0xc7 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xc8 - 0xcb */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_CB_DECODE,
+/* 0xcc - 0xcf */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0xd0 - 0xd3 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xd4 - 0xd7 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xd8 - 0xdb */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xdc - 0xdf */ DIS_DD_NOP, DIS_DD_DD_DECODE, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0xe0 - 0xe3 */ DIS_DD_NOP, DIS_POP_IX, DIS_DD_NOP, DIS_EX_ISP_IX,
+/* 0xe4 - 0xe7 */ DIS_DD_NOP, DIS_PUSH_IX, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xe8 - 0xeb */ DIS_DD_NOP, DIS_JP_IX, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xec - 0xef */ DIS_DD_NOP, DIS_DD_ED_DECODE, DIS_DD_NOP, DIS_DD_NOP,
+
+/* 0xf0 - 0xf3 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xf4 - 0xf7 */ DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xf8 - 0xfb */ DIS_DD_NOP, DIS_LD_SP_IX, DIS_DD_NOP, DIS_DD_NOP,
+/* 0xfc - 0xff */ DIS_DD_NOP, DIS_DD_FD_DECODE, DIS_DD_NOP, DIS_DD_NOP,
+ };
+
+
+/* DIS_DD DIS_CB opcodes
+*/
+DIS_OP_CALLBACK dis_DD_CB_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_RLC_IX, DIS_RLC_IX, DIS_RLC_IX, DIS_RLC_IX,
+/* 0x04 - 0x07 */ DIS_RLC_IX, DIS_RLC_IX, DIS_RLC_IX, DIS_RLC_IX,
+/* 0x08 - 0x0b */ DIS_RRC_IX, DIS_RRC_IX, DIS_RRC_IX, DIS_RRC_IX,
+/* 0x0c - 0x0f */ DIS_RRC_IX, DIS_RRC_IX, DIS_RRC_IX, DIS_RRC_IX,
+
+/* 0x10 - 0x13 */ DIS_RL_IX, DIS_RL_IX, DIS_RL_IX, DIS_RL_IX,
+/* 0x14 - 0x17 */ DIS_RL_IX, DIS_RL_IX, DIS_RL_IX, DIS_RL_IX,
+/* 0x18 - 0x1b */ DIS_RR_IX, DIS_RR_IX, DIS_RR_IX, DIS_RR_IX,
+/* 0x1c - 0x1f */ DIS_RR_IX, DIS_RR_IX, DIS_RR_IX, DIS_RR_IX,
+
+/* 0x20 - 0x23 */ DIS_SLA_IX, DIS_SLA_IX, DIS_SLA_IX, DIS_SLA_IX,
+/* 0x24 - 0x27 */ DIS_SLA_IX, DIS_SLA_IX, DIS_SLA_IX, DIS_SLA_IX,
+/* 0x28 - 0x2b */ DIS_SRA_IX, DIS_SRA_IX, DIS_SRA_IX, DIS_SRA_IX,
+/* 0x2c - 0x2f */ DIS_SRA_IX, DIS_SRA_IX, DIS_SRA_IX, DIS_SRA_IX,
+
+/* 0x30 - 0x33 */ DIS_SLL_IX, DIS_SLL_IX, DIS_SLL_IX, DIS_SLL_IX,
+/* 0x34 - 0x37 */ DIS_SLL_IX, DIS_SLL_IX, DIS_SLL_IX, DIS_SLL_IX,
+/* 0x38 - 0x3b */ DIS_SRL_IX, DIS_SRL_IX, DIS_SRL_IX, DIS_SRL_IX,
+/* 0x3c - 0x3f */ DIS_SRL_IX, DIS_SRL_IX, DIS_SRL_IX, DIS_SRL_IX,
+
+/* 0x40 - 0x43 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x44 - 0x47 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x48 - 0x4b */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x4c - 0x4f */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+
+/* 0x50 - 0x53 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x54 - 0x57 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x58 - 0x5b */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x5c - 0x5f */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+
+/* 0x60 - 0x63 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x64 - 0x67 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x68 - 0x6b */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x6c - 0x6f */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+
+/* 0x70 - 0x73 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x74 - 0x77 */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x78 - 0x7b */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+/* 0x7c - 0x7f */ DIS_BIT_IX,DIS_BIT_IX, DIS_BIT_IX, DIS_BIT_IX,
+
+/* 0x80 - 0x83 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x84 - 0x87 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x88 - 0x8b */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x8c - 0x8f */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+
+/* 0x90 - 0x93 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x94 - 0x97 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x98 - 0x9b */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0x9c - 0x9f */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+
+/* 0xa0 - 0xa3 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xa4 - 0xa7 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xa8 - 0xab */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xac - 0xaf */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+
+/* 0xb0 - 0xb3 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xb4 - 0xb7 */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xb8 - 0xbb */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+/* 0xbc - 0xbf */ DIS_RES_IX,DIS_RES_IX, DIS_RES_IX, DIS_RES_IX,
+
+/* 0xc0 - 0xc3 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xc4 - 0xc7 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xc8 - 0xcb */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xcc - 0xcf */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+
+/* 0xd0 - 0xd3 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xd4 - 0xd7 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xd8 - 0xdb */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xdc - 0xdf */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+
+/* 0xe0 - 0xe3 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xe4 - 0xe7 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xe8 - 0xeb */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xec - 0xef */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+
+/* 0xf0 - 0xf3 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xf4 - 0xf7 */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xf8 - 0xfb */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+/* 0xfc - 0xff */ DIS_SET_IX,DIS_SET_IX, DIS_SET_IX, DIS_SET_IX,
+ };
+
+/* DIS_ED opcodes
+*/
+DIS_OP_CALLBACK dis_ED_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x04 - 0x07 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x08 - 0x0b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x0c - 0x0f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0x10 - 0x13 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x14 - 0x17 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x18 - 0x1b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x1c - 0x1f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0x20 - 0x23 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x24 - 0x27 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x28 - 0x2b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x2c - 0x2f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0x30 - 0x33 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x34 - 0x37 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x38 - 0x3b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x3c - 0x3f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0x40 - 0x43 */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_SBC_HL_R16, DIS_ED_LD_ADDR_R16,
+/* 0x44 - 0x47 */ DIS_NEG, DIS_RETN, DIS_IM_0, DIS_LD_I_A,
+/* 0x48 - 0x4b */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_ADC_HL_R16, DIS_ED_LD_R16_ADDR,
+/* 0x4c - 0x4f */ DIS_NEG, DIS_RETI, DIS_IM_0, DIS_LD_R_A,
+
+/* 0x50 - 0x53 */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_SBC_HL_R16, DIS_ED_LD_ADDR_R16,
+/* 0x54 - 0x57 */ DIS_NEG, DIS_RETN, DIS_IM_1, DIS_LD_A_I,
+/* 0x58 - 0x5b */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_ADC_HL_R16, DIS_ED_LD_R16_ADDR,
+/* 0x5c - 0x5f */ DIS_NEG, DIS_RETI, DIS_IM_2, DIS_LD_A_R,
+
+/* 0x60 - 0x63 */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_SBC_HL_R16, DIS_ED_LD_ADDR_R16,
+/* 0x64 - 0x67 */ DIS_NEG, DIS_RETN, DIS_IM_0, DIS_RRD,
+/* 0x68 - 0x6b */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_ADC_HL_R16, DIS_ED_LD_R16_ADDR,
+/* 0x6c - 0x6f */ DIS_NEG, DIS_RETI, DIS_IM_0, DIS_RLD,
+
+/* 0x70 - 0x73 */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_SBC_HL_R16, DIS_ED_LD_ADDR_R16,
+/* 0x74 - 0x77 */ DIS_NEG, DIS_RETN, DIS_IM_1, DIS_ED_NOP,
+/* 0x78 - 0x7b */ DIS_IN_R8_C, DIS_OUT_C_R8, DIS_ADC_HL_R16, DIS_ED_LD_R16_ADDR,
+/* 0x7c - 0x7f */ DIS_NEG, DIS_RETI, DIS_IM_2, DIS_ED_NOP,
+
+/* 0x80 - 0x83 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x84 - 0x87 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x88 - 0x8b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x8c - 0x8f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0x90 - 0x93 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x94 - 0x97 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x98 - 0x9b */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0x9c - 0x9f */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xa0 - 0xa3 */ DIS_LDI, DIS_CPI, DIS_INI, DIS_OUTI,
+/* 0xa4 - 0xa7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xa8 - 0xab */ DIS_LDD, DIS_CPD, DIS_IND, DIS_OUTD,
+/* 0xac - 0xaf */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xb0 - 0xb3 */ DIS_LDIR, DIS_CPIR, DIS_INIR, DIS_OTIR,
+/* 0xb4 - 0xb7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xb8 - 0xbb */ DIS_LDDR, DIS_CPDR, DIS_INDR, DIS_OTDR,
+/* 0xbc - 0xbf */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xc0 - 0xc3 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xc4 - 0xc7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xc8 - 0xcb */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xcc - 0xcf */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xd0 - 0xd3 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xd4 - 0xd7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xd8 - 0xdb */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xdc - 0xdf */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xe0 - 0xe3 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xe4 - 0xe7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xe8 - 0xeb */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xec - 0xef */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+
+/* 0xf0 - 0xf3 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xf4 - 0xf7 */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xf8 - 0xfb */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+/* 0xfc - 0xff */ DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP, DIS_ED_NOP,
+ };
+
+/* DIS_FD opcodes
+*/
+DIS_OP_CALLBACK dis_FD_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x04 - 0x07 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x08 - 0x0b */ DIS_FD_NOP, DIS_ADD_IY_BC, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x0c - 0x0f */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0x10 - 0x13 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x14 - 0x17 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x18 - 0x1b */ DIS_FD_NOP, DIS_ADD_IY_DE, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x1c - 0x1f */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0x20 - 0x23 */ DIS_FD_NOP, DIS_LD_IY_WORD, DIS_LD_ADDR_IY, DIS_INC_IY,
+/* 0x24 - 0x27 */ DIS_INC_IYH, DIS_DEC_IYH, DIS_LD_IYH_BYTE, DIS_FD_NOP,
+/* 0x28 - 0x2b */ DIS_FD_NOP, DIS_ADD_IY_IY, DIS_LD_IY_ADDR, DIS_DEC_IY,
+/* 0x2c - 0x2f */ DIS_INC_IYL, DIS_DEC_IYL, DIS_LD_IYL_BYTE, DIS_FD_NOP,
+
+/* 0x30 - 0x33 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x34 - 0x37 */ DIS_INC_IIY, DIS_DEC_IIY, DIS_LD_IIY_BYTE, DIS_FD_NOP,
+/* 0x38 - 0x3b */ DIS_FD_NOP, DIS_ADD_IY_SP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x3c - 0x3f */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0x40 - 0x43 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x44 - 0x47 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP,
+/* 0x48 - 0x4b */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x4c - 0x4f */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP,
+
+/* 0x50 - 0x53 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x54 - 0x57 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP,
+/* 0x58 - 0x5b */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x5c - 0x5f */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP,
+
+/* 0x60 - 0x63 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8,
+/* 0x64 - 0x67 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8,
+/* 0x68 - 0x6b */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8,
+/* 0x6c - 0x6f */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8,
+
+/* 0x70 - 0x73 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8,
+/* 0x74 - 0x77 */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP, DIS_YLD_R8_R8,
+/* 0x78 - 0x7b */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x7c - 0x7f */ DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_YLD_R8_R8, DIS_FD_NOP,
+
+/* 0x80 - 0x83 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x84 - 0x87 */ DIS_YADD_R8, DIS_YADD_R8, DIS_YADD_R8, DIS_FD_NOP,
+/* 0x88 - 0x8b */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x8c - 0x8f */ DIS_YADC_R8, DIS_YADC_R8, DIS_YADC_R8, DIS_FD_NOP,
+
+/* 0x90 - 0x93 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x94 - 0x97 */ DIS_YSUB_R8, DIS_YSUB_R8, DIS_YSUB_R8, DIS_FD_NOP,
+/* 0x98 - 0x9b */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0x9c - 0x9f */ DIS_YSBC_R8, DIS_YSBC_R8, DIS_YSBC_R8, DIS_FD_NOP,
+
+/* 0xa0 - 0xa3 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xa4 - 0xa7 */ DIS_YAND_R8, DIS_YAND_R8, DIS_YAND_R8, DIS_FD_NOP,
+/* 0xa8 - 0xab */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xac - 0xaf */ DIS_YYOR_R8, DIS_YYOR_R8, DIS_YYOR_R8, DIS_FD_NOP,
+
+/* 0xb0 - 0xb3 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xb4 - 0xb7 */ DIS_Y_OR_R8, DIS_Y_OR_R8, DIS_Y_OR_R8, DIS_FD_NOP,
+/* 0xb8 - 0xbb */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xbc - 0xbf */ DIS_YCP_R8, DIS_YCP_R8, DIS_YCP_R8, DIS_FD_NOP,
+
+/* 0xc0 - 0xc3 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xc4 - 0xc7 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xc8 - 0xcb */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_CB_DECODE,
+/* 0xcc - 0xcf */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0xd0 - 0xd3 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xd4 - 0xd7 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xd8 - 0xdb */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xdc - 0xdf */ DIS_FD_NOP, DIS_FD_DD_DECODE, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0xe0 - 0xe3 */ DIS_FD_NOP, DIS_POP_IY, DIS_FD_NOP, DIS_EY_ISP_IY,
+/* 0xe4 - 0xe7 */ DIS_FD_NOP, DIS_PUSH_IY, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xe8 - 0xeb */ DIS_FD_NOP, DIS_JP_IY, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xec - 0xef */ DIS_FD_NOP, DIS_FD_ED_DECODE, DIS_FD_NOP, DIS_FD_NOP,
+
+/* 0xf0 - 0xf3 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xf4 - 0xf7 */ DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xf8 - 0xfb */ DIS_FD_NOP, DIS_LD_SP_IY, DIS_FD_NOP, DIS_FD_NOP,
+/* 0xfc - 0xff */ DIS_FD_NOP, DIS_FD_FD_DECODE, DIS_FD_NOP, DIS_FD_NOP,
+ };
+
+
+/* DIS_FD DIS_CB opcodes
+*/
+DIS_OP_CALLBACK dis_FD_CB_opcode[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_RLC_IY, DIS_RLC_IY, DIS_RLC_IY, DIS_RLC_IY,
+/* 0x04 - 0x07 */ DIS_RLC_IY, DIS_RLC_IY, DIS_RLC_IY, DIS_RLC_IY,
+/* 0x08 - 0x0b */ DIS_RRC_IY, DIS_RRC_IY, DIS_RRC_IY, DIS_RRC_IY,
+/* 0x0c - 0x0f */ DIS_RRC_IY, DIS_RRC_IY, DIS_RRC_IY, DIS_RRC_IY,
+
+/* 0x10 - 0x13 */ DIS_RL_IY, DIS_RL_IY, DIS_RL_IY, DIS_RL_IY,
+/* 0x14 - 0x17 */ DIS_RL_IY, DIS_RL_IY, DIS_RL_IY, DIS_RL_IY,
+/* 0x18 - 0x1b */ DIS_RR_IY, DIS_RR_IY, DIS_RR_IY, DIS_RR_IY,
+/* 0x1c - 0x1f */ DIS_RR_IY, DIS_RR_IY, DIS_RR_IY, DIS_RR_IY,
+
+/* 0x20 - 0x23 */ DIS_SLA_IY, DIS_SLA_IY, DIS_SLA_IY, DIS_SLA_IY,
+/* 0x24 - 0x27 */ DIS_SLA_IY, DIS_SLA_IY, DIS_SLA_IY, DIS_SLA_IY,
+/* 0x28 - 0x2b */ DIS_SRA_IY, DIS_SRA_IY, DIS_SRA_IY, DIS_SRA_IY,
+/* 0x2c - 0x2f */ DIS_SRA_IY, DIS_SRA_IY, DIS_SRA_IY, DIS_SRA_IY,
+
+/* 0x30 - 0x33 */ DIS_SLL_IY, DIS_SLL_IY, DIS_SLL_IY, DIS_SLL_IY,
+/* 0x34 - 0x37 */ DIS_SLL_IY, DIS_SLL_IY, DIS_SLL_IY, DIS_SLL_IY,
+/* 0x38 - 0x3b */ DIS_SRL_IY, DIS_SRL_IY, DIS_SRL_IY, DIS_SRL_IY,
+/* 0x3c - 0x3f */ DIS_SRL_IY, DIS_SRL_IY, DIS_SRL_IY, DIS_SRL_IY,
+
+/* 0x40 - 0x43 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x44 - 0x47 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x48 - 0x4b */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x4c - 0x4f */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+
+/* 0x50 - 0x53 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x54 - 0x57 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x58 - 0x5b */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x5c - 0x5f */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+
+/* 0x60 - 0x63 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x64 - 0x67 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x68 - 0x6b */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x6c - 0x6f */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+
+/* 0x70 - 0x73 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x74 - 0x77 */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x78 - 0x7b */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+/* 0x7c - 0x7f */ DIS_BIT_IY,DIS_BIT_IY, DIS_BIT_IY, DIS_BIT_IY,
+
+/* 0x80 - 0x83 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x84 - 0x87 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x88 - 0x8b */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x8c - 0x8f */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+
+/* 0x90 - 0x93 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x94 - 0x97 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x98 - 0x9b */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0x9c - 0x9f */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+
+/* 0xa0 - 0xa3 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xa4 - 0xa7 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xa8 - 0xab */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xac - 0xaf */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+
+/* 0xb0 - 0xb3 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xb4 - 0xb7 */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xb8 - 0xbb */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+/* 0xbc - 0xbf */ DIS_RES_IY,DIS_RES_IY, DIS_RES_IY, DIS_RES_IY,
+
+/* 0xc0 - 0xc3 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xc4 - 0xc7 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xc8 - 0xcb */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xcc - 0xcf */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+
+/* 0xd0 - 0xd3 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xd4 - 0xd7 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xd8 - 0xdb */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xdc - 0xdf */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+
+/* 0xe0 - 0xe3 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xe4 - 0xe7 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xe8 - 0xeb */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xec - 0xef */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+
+/* 0xf0 - 0xf3 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xf4 - 0xf7 */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xf8 - 0xfb */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+/* 0xfc - 0xff */ DIS_SET_IY,DIS_SET_IY, DIS_SET_IY, DIS_SET_IY,
+ };
+
+/* DIS_First/single byte opcodes
+*/
+DIS_OP_CALLBACK dis_opcode_z80[0x100]=
+ {
+/* 0x00 - 0x03 */ DIS_NOP, DIS_LD_R16_WORD, DIS_LD_R16_A, DIS_INC_R16,
+/* 0x04 - 0x07 */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_RLCA,
+/* 0x08 - 0x0b */ DIS_EX_AF_AF, DIS_ADD_HL_R16, DIS_LD_A_R16, DIS_DEC_R16,
+/* 0x0c - 0x0f */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_RRCA,
+
+/* 0x10 - 0x13 */ DIS_DJNZ, DIS_LD_R16_WORD, DIS_LD_R16_A, DIS_INC_R16,
+/* 0x14 - 0x17 */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_RLA,
+/* 0x18 - 0x1b */ DIS_JR, DIS_ADD_HL_R16, DIS_LD_A_R16, DIS_DEC_R16,
+/* 0x1c - 0x1f */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_RRA,
+
+/* 0x20 - 0x23 */ DIS_JR_CO, DIS_LD_R16_WORD, DIS_LD_ADDR_HL, DIS_INC_R16,
+/* 0x24 - 0x27 */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_DAA,
+/* 0x28 - 0x2b */ DIS_JR_CO, DIS_ADD_HL_R16, DIS_LD_HL_ADDR, DIS_DEC_R16,
+/* 0x2c - 0x2f */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_CPL,
+
+/* 0x30 - 0x33 */ DIS_JR_CO, DIS_LD_R16_WORD, DIS_LD_ADDR_A, DIS_INC_R16,
+/* 0x34 - 0x37 */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_SCF,
+/* 0x38 - 0x3b */ DIS_JR_CO, DIS_ADD_HL_R16, DIS_LD_A_ADDR, DIS_DEC_R16,
+/* 0x3c - 0x3f */ DIS_INC_R8, DIS_DEC_R8, DIS_LD_R8_BYTE, DIS_CCF,
+
+/* 0x40 - 0x43 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x44 - 0x47 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x48 - 0x4b */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x4c - 0x4f */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+
+/* 0x50 - 0x53 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x54 - 0x57 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x58 - 0x5b */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x5c - 0x5f */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+
+/* 0x60 - 0x63 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x64 - 0x67 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x68 - 0x6b */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x6c - 0x6f */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+
+/* 0x70 - 0x73 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x74 - 0x77 */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_HALT, DIS_LD_R8_R8,
+/* 0x78 - 0x7b */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+/* 0x7c - 0x7f */ DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8, DIS_LD_R8_R8,
+
+/* 0x80 - 0x83 */ DIS_ADD_R8, DIS_ADD_R8, DIS_ADD_R8, DIS_ADD_R8,
+/* 0x84 - 0x87 */ DIS_ADD_R8, DIS_ADD_R8, DIS_ADD_R8, DIS_ADD_R8,
+/* 0x88 - 0x8b */ DIS_ADC_R8, DIS_ADC_R8, DIS_ADC_R8, DIS_ADC_R8,
+/* 0x8c - 0x8f */ DIS_ADC_R8, DIS_ADC_R8, DIS_ADC_R8, DIS_ADC_R8,
+
+/* 0x90 - 0x93 */ DIS_SUB_R8, DIS_SUB_R8, DIS_SUB_R8, DIS_SUB_R8,
+/* 0x94 - 0x97 */ DIS_SUB_R8, DIS_SUB_R8, DIS_SUB_R8, DIS_SUB_R8,
+/* 0x98 - 0x9b */ DIS_SBC_R8, DIS_SBC_R8, DIS_SBC_R8, DIS_SBC_R8,
+/* 0x9c - 0x9f */ DIS_SBC_R8, DIS_SBC_R8, DIS_SBC_R8, DIS_SBC_R8,
+
+/* 0xa0 - 0xa3 */ DIS_AND_R8, DIS_AND_R8, DIS_AND_R8, DIS_AND_R8,
+/* 0xa4 - 0xa7 */ DIS_AND_R8, DIS_AND_R8, DIS_AND_R8, DIS_AND_R8,
+/* 0xa8 - 0xab */ DIS_XOR_R8, DIS_XOR_R8, DIS_XOR_R8, DIS_XOR_R8,
+/* 0xac - 0xaf */ DIS_XOR_R8, DIS_XOR_R8, DIS_XOR_R8, DIS_XOR_R8,
+
+/* 0xb0 - 0xb3 */ DIS_OR_R8, DIS_OR_R8, DIS_OR_R8, DIS_OR_R8,
+/* 0xb4 - 0xb7 */ DIS_OR_R8, DIS_OR_R8, DIS_OR_R8, DIS_OR_R8,
+/* 0xb8 - 0xbb */ DIS_CP_R8, DIS_CP_R8, DIS_CP_R8, DIS_CP_R8,
+/* 0xbc - 0xbf */ DIS_CP_R8, DIS_CP_R8, DIS_CP_R8, DIS_CP_R8,
+
+/* 0xc0 - 0xc3 */ DIS_RET_CO, DIS_POP_R16, DIS_JP_CO, DIS_JP,
+/* 0xc4 - 0xc7 */ DIS_CALL_CO, DIS_PUSH_R16, DIS_ADD_A_BYTE, DIS_RST,
+/* 0xc8 - 0xcb */ DIS_RET_CO, DIS_RET, DIS_JP_CO, DIS_CB_DECODE,
+/* 0xcc - 0xcf */ DIS_CALL_CO, DIS_CALL, DIS_ADC_A_BYTE, DIS_RST,
+
+/* 0xd0 - 0xd3 */ DIS_RET_CO, DIS_POP_R16, DIS_JP_CO, DIS_OUT_BYTE_A,
+/* 0xd4 - 0xd7 */ DIS_CALL_CO, DIS_PUSH_R16, DIS_SUB_A_BYTE, DIS_RST,
+/* 0xd8 - 0xdb */ DIS_RET_CO, DIS_EXX, DIS_JP_CO, DIS_IN_A_BYTE,
+/* 0xdc - 0xdf */ DIS_CALL_CO, DIS_DD_DECODE, DIS_SBC_A_BYTE, DIS_RST,
+
+/* 0xe0 - 0xe3 */ DIS_RET_CO, DIS_POP_R16, DIS_JP_CO, DIS_EX_ISP_HL,
+/* 0xe4 - 0xe7 */ DIS_CALL_CO, DIS_PUSH_R16, DIS_AND_A_BYTE, DIS_RST,
+/* 0xe8 - 0xeb */ DIS_RET_CO, DIS_JP_HL, DIS_JP_CO, DIS_EX_DE_HL,
+/* 0xec - 0xef */ DIS_CALL_CO, DIS_ED_DECODE, DIS_XOR_A_BYTE, DIS_RST,
+
+/* 0xf0 - 0xf3 */ DIS_RET_CO, DIS_POP_R16, DIS_JP_CO, DIS_DI,
+/* 0xf4 - 0xf7 */ DIS_CALL_CO, DIS_PUSH_R16, DIS_OR_A_BYTE, DIS_RST,
+/* 0xf8 - 0xfb */ DIS_RET_CO, DIS_LD_SP_HL, DIS_JP_CO, DIS_EI,
+/* 0xfc - 0xff */ DIS_CALL_CO, DIS_FD_DECODE, DIS_CP_A_BYTE, DIS_RST,
+ };
+
+
+#endif
+
+/* END OF FILE */
diff --git a/source/zx81.c b/source/zx81.c
new file mode 100644
index 0000000..b2d85b1
--- /dev/null
+++ b/source/zx81.c
@@ -0,0 +1,868 @@
+/*
+ 3ds81 - Nintendo 3DS ZX81 emulator
+
+ Copyright (C) 2021 Ian Cowburn <ianc@noddybox.co.uk>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>
+
+ -------------------------------------------------------------------------
+
+ Provides the emulation for the ZX81
+
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <3ds.h>
+
+#include "zx81.h"
+#include "gui.h"
+
+#include "stream.h"
+
+#include "config.h"
+
+#include "zx81_bin.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* ---------------------------------------- STATICS
+*/
+#define ROMLEN 0x2000
+#define ROM_SAVE 0x2fc
+#define ROM_LOAD 0x347
+
+#define ED_SAVE 0xf0
+#define ED_LOAD 0xf1
+#define ED_WAITKEY 0xf2
+#define ED_ENDWAITKEY 0xf3
+#define ED_PAUSE 0xf4
+
+#define SLOW_TSTATES 16000
+#define FAST_TSTATES (64000*10)
+
+#define E_LINE 16404
+#define LASTK1 16421
+#define LASTK2 16422
+#define MARGIN 16424
+#define FRAMES 16436
+#define CDFLAG 16443
+
+static Z80Val FRAME_TSTATES=FAST_TSTATES;
+
+/* The ZX81 screen and memory
+*/
+static int waitkey=FALSE;
+static int started=FALSE;
+
+static int hires=FALSE;
+static int hires_dfile;
+static int last_I;
+
+static int fast_mode=FALSE;
+
+static unsigned prev_lk1;
+static unsigned prev_lk2;
+
+#define SCR_W 256
+#define SCR_H 192
+#define TXT_W 32
+#define TXT_H 24
+
+static Z80Byte mem[0x10000];
+
+static Z80Word RAMBOT=0;
+static Z80Word RAMTOP=0;
+
+#define DFILE 0x400c
+
+#define WORD(a) (mem[a] | (Z80Word)mem[a+1]<<8)
+
+/* Tape
+*/
+static int enable_filesystem;
+static int allow_save;
+static const Z80Byte *tape_image;
+static int tape_len;
+
+static char last_dir[FILENAME_MAX] = "/";
+
+/* The keyboard
+*/
+static Z80Byte matrix[8];
+
+static struct
+{
+ int row;
+ int bit;
+} key_matrix[]=
+ {
+ {3,0x01}, {3,0x02}, {3,0x04}, {3,0x08}, {3,0x10}, /* 1 - 5 */
+ {4,0x10}, {4,0x08}, {4,0x04}, {4,0x02}, {4,0x01}, /* 6 - 0 */
+ {2,0x01}, {2,0x02}, {2,0x04}, {2,0x08}, {2,0x10}, /* Q - T */
+ {5,0x10}, {5,0x08}, {5,0x04}, {5,0x02}, {5,0x01}, /* Y - P */
+ {1,0x01}, {1,0x02}, {1,0x04}, {1,0x08}, {1,0x10}, /* A - G */
+ {6,0x10}, {6,0x08}, {6,0x04}, {6,0x02}, {6,0x01}, /* H - NL */
+ {0,0x01}, {0,0x02}, {0,0x04}, {0,0x08}, {0,0x10}, /* CAPS - V */
+ {7,0x10}, {7,0x08}, {7,0x04}, {7,0x02}, {7,0x01} /* B - SPACE */
+ };
+
+
+/* ---------------------------------------- PRIVATE FUNCTIONS
+*/
+#define PEEKW(addr) (mem[addr] | (Z80Word)mem[addr+1]<<8)
+
+#define POKEW(addr,val) do \
+ { \
+ Z80Word wa=addr; \
+ Z80Word wv=val; \
+ mem[wa]=wv; \
+ mem[wa+1]=wv>>8; \
+ } while(0)
+
+static void RomPatch(void)
+{
+ static const Z80Byte save[]=
+ {
+ 0xed, ED_SAVE, /* (SAVE) */
+ 0xc3, 0x07, 0x02, /* JP $0207 */
+ 0xff /* End of patch */
+ };
+
+ static const Z80Byte load[]=
+ {
+ 0xed, ED_LOAD, /* (LOAD) */
+ 0xc3, 0x07, 0x02, /* JP $0207 */
+ 0xff /* End of patch */
+ };
+
+ static const Z80Byte fast_hack[]=
+ {
+ 0xed, ED_WAITKEY, /* (START KEY WAIT) */
+ 0xcb,0x46, /* L: bit 0,(hl) */
+ 0x28,0xfc, /* jr z,L */
+ 0xed, ED_ENDWAITKEY, /* (END KEY WAIT) */
+ 0x00, /* nop */
+ 0xff /* End of patch */
+ };
+
+ static const Z80Byte kbd_hack[]=
+ {
+ 0x2a,0x25,0x40, /* ld hl,(LASTK) */
+ 0xc9, /* ret */
+ 0xff /* End of patch */
+ };
+
+ static const Z80Byte pause_hack[]=
+ {
+ 0xed, ED_PAUSE, /* (PAUSE) */
+ 0x00, /* nop */
+ 0xff /* End of patch */
+ };
+
+ int f;
+
+ for(f=0;save[f]!=0xff;f++)
+ {
+ mem[ROM_SAVE+f]=save[f];
+ }
+
+ for(f=0;load[f]!=0xff;f++)
+ {
+ mem[ROM_LOAD+f]=load[f];
+ }
+
+ for(f=0;fast_hack[f]!=0xff;f++)
+ {
+ mem[0x4ca+f]=fast_hack[f];
+ }
+
+ for(f=0;kbd_hack[f]!=0xff;f++)
+ {
+ mem[0x2bb+f]=kbd_hack[f];
+ }
+
+ for(f=0;pause_hack[f]!=0xff;f++)
+ {
+ mem[0xf3a+f]=pause_hack[f];
+ }
+
+ /* Trust me, we have a ZX81... Honestly.
+ */
+ mem[0x21c]=0x00;
+ mem[0x21d]=0x00;
+
+ /* Remove HALTs as we don't do interrupts
+ */
+ mem[0x0079]=0;
+ mem[0x02ec]=0;
+}
+
+/* Open a tape file the passed address
+*/
+static FILE *OpenTapeFile(Z80Word addr, int *cancelled, const char *mode)
+{
+ static const char zx_chars[] = "\"#$:?()><=+-*/;,."
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ FILE *fp;
+ char full_fn[FILENAME_MAX] = DEFAULT_SNAPDIR;
+ char fn[FILENAME_MAX];
+ int f;
+ int done;
+
+ fp = NULL;
+ f = 0;
+ done = FALSE;
+ *cancelled = FALSE;
+
+ while(f<(FILENAME_MAX-3) && !done)
+ {
+ int ch;
+
+ ch = mem[addr++];
+
+ if (ch&0x80)
+ {
+ done = TRUE;
+ ch &= 0x7f;
+ }
+
+ if (ch>=11 && ch<=63)
+ {
+ fn[f++] = zx_chars[ch-11];
+ }
+ }
+
+ if (fn[0] == '*')
+ {
+ if (GUI_FileSelect(last_dir,fn,".P"))
+ {
+ fp = fopen(fn, mode);
+ }
+ else
+ {
+ *cancelled = TRUE;
+ }
+
+ SK_DisplayKeyboard();
+ }
+ else
+ {
+ fn[f++] = '.';
+ fn[f++] = 'P';
+ fn[f] = 0;
+
+ strcat(full_fn,fn);
+
+ if (!(fp = fopen(full_fn, mode)))
+ {
+ fp = fopen(fn, mode);
+ }
+ }
+
+ return fp;
+}
+
+
+static void LoadInternalTape(Z80 *z80)
+{
+ memcpy(mem+0x4009,tape_image,tape_len);
+}
+
+
+static void LoadExternalTape(FILE *tape, Z80 *z80)
+{
+ int c;
+ Z80Byte *a;
+
+ a=mem+0x4009;
+
+ while((c=getc(tape))!=EOF)
+ {
+ *a++=c;
+ }
+}
+
+
+static void SaveExternalTape(FILE *tape, Z80 *z80)
+{
+ int f;
+ int end;
+
+ f = 0x4009;
+ end = WORD(E_LINE);
+
+ while(f <= end)
+ {
+ fputc(mem[f++], tape);
+ }
+}
+
+
+static void FindHiresDFILE(void)
+{
+ /* Somewhat based on the code from xz81, an X-based ZX81 emulator,
+ (C) 1994 Ian Collier. Search the ZX81's RAM until we find what looks
+ like a hi-res display file.
+
+ Bizarrely the original code used 'f' for a loop counter too... Another
+ poor soul forever damaged by the ZX81's keyword entry system...
+ */
+ int f;
+
+ for(f=0x8000-(33*192); f>0x4000 ; f--)
+ {
+ int v;
+
+ v = mem[f+32];
+
+ if (v&0x40)
+ {
+ int ok = TRUE;
+ int n;
+
+ for(n=0;n<192 && ok;n++)
+ {
+ if (mem[f+33*n]&0x40)
+ {
+ ok = FALSE;
+ }
+
+ if (mem[f+32+33*n] != v)
+ {
+ ok = FALSE;
+ }
+ }
+
+ if (ok)
+ {
+ hires_dfile = f;
+ return;
+ }
+ }
+ }
+
+ /* All else fails, put the hires dfile at 0x4000 -- at least it should be
+ obvious that the hires won't work for whatever is being run.
+ */
+ hires_dfile = 0x4000;
+}
+
+
+/* Perform ZX81 housekeeping functions like updating FRAMES and updating LASTK
+*/
+static void ZX81HouseKeeping(Z80 *z80)
+{
+ unsigned row;
+ unsigned lastk1;
+ unsigned lastk2;
+
+ /* British ZX81
+ */
+ mem[MARGIN]=55;
+
+ /* Update FRAMES
+ */
+ if (FRAME_TSTATES==SLOW_TSTATES)
+ {
+ Z80Word frame=PEEKW(FRAMES)&0x7fff;
+
+ if (frame)
+ {
+ frame--;
+ }
+
+ POKEW(FRAMES,frame|0x8000);
+ }
+
+ if (!started)
+ {
+ prev_lk1=0;
+ prev_lk2=0;
+ return;
+ }
+
+ /* Update LASTK
+ */
+ lastk1=0;
+ lastk2=0;
+
+ for(row=0;row<8;row++)
+ {
+ unsigned b;
+
+ b=(~matrix[row]&0x1f)<<1;
+
+ if (row==0)
+ {
+ unsigned shift;
+
+ shift=b&2;
+ b&=~2;
+ b|=(shift>>1);
+ }
+
+ if (b)
+ {
+ if (b>1)
+ {
+ lastk1|=(1<<row);
+ }
+
+ lastk2|=b;
+ }
+ }
+
+ if (lastk1 && (lastk1!=prev_lk1 || lastk2!=prev_lk2))
+ {
+ mem[CDFLAG]|=1;
+ }
+ else
+ {
+ mem[CDFLAG]&=~1;
+ }
+
+ mem[LASTK1]=lastk1^0xff;
+ mem[LASTK2]=lastk2^0xff;
+
+ prev_lk1=lastk1;
+ prev_lk2=lastk2;
+}
+
+
+static int CheckTimers(Z80 *z80, Z80Val val)
+{
+ if (val>=FRAME_TSTATES)
+ {
+ /* Check for hi-res modes
+ */
+ if (z80->I && z80->I != last_I)
+ {
+ last_I = z80->I;
+
+ if (z80->I == 0x1e)
+ {
+ hires = FALSE;
+ }
+ else
+ {
+ hires = TRUE;
+ FindHiresDFILE();
+ }
+ }
+
+ Z80ResetCycles(z80,val-FRAME_TSTATES);
+
+ /* Kludge warning - We assume that a hires display will not be in
+ FAST mode!
+ */
+ if (started && ((mem[CDFLAG] & 0x80) || waitkey || hires))
+ {
+ fast_mode = FALSE;
+ FRAME_TSTATES=SLOW_TSTATES;
+ }
+ else
+ {
+ fast_mode = TRUE;
+ FRAME_TSTATES=FAST_TSTATES;
+ }
+
+ /* Update FRAMES (if in SLOW) and scan the keyboard. This only happens
+ once we've got to a decent point in the boot cycle (detected with
+ a valid stack pointer).
+ */
+ if (z80->SP<0x8000)
+ {
+ ZX81HouseKeeping(z80);
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+static int EDCallback(Z80 *z80, Z80Val data)
+{
+ Z80Word pause;
+
+ switch((Z80Byte)data)
+ {
+ case ED_SAVE:
+ if (allow_save && z80->DE.w<0x8000)
+ {
+ FILE *fp;
+ int cancel;
+
+ if ((fp=OpenTapeFile(z80->HL.w, &cancel, "wb")))
+ {
+ SaveExternalTape(fp,z80);
+ fclose(fp);
+ }
+ }
+ break;
+
+ case ED_LOAD:
+ /* Try and load the external file if a name given. Otherwise, we
+ try the internal one. Some of this is slightly dodgy -- it was
+ never intended for the emulator to be doing any GUI related
+ nonsense (like the alerts) but simply emulating.
+ */
+ if (enable_filesystem && z80->DE.w<0x8000)
+ {
+ FILE *fp;
+ int cancel;
+
+ if ((fp=OpenTapeFile(z80->DE.w, &cancel, "rb")))
+ {
+ LoadExternalTape(fp,z80);
+ fclose(fp);
+ }
+ else
+ {
+ if (!cancel)
+ {
+ GUI_Alert(FALSE,"Couldn't open tape");
+ SK_DisplayKeyboard();
+ }
+ }
+ }
+ else
+ {
+ if (tape_image)
+ {
+ LoadInternalTape(z80);
+ }
+ else
+ {
+ GUI_Alert(FALSE,"No tape image selected");
+ SK_DisplayKeyboard();
+ }
+ }
+
+ mem[CDFLAG]=0xc0;
+ break;
+
+ case ED_WAITKEY:
+ waitkey=TRUE;
+ started=TRUE;
+ break;
+
+ case ED_ENDWAITKEY:
+ waitkey=FALSE;
+ break;
+
+ case ED_PAUSE:
+ waitkey=TRUE;
+
+ pause=z80->BC.w;
+
+ while(pause-- && !(mem[CDFLAG]&1))
+ {
+ SoftKeyEvent ev;
+
+ while (SK_GetEvent(&ev))
+ {
+ ZX81HandleKey(ev.key,ev.pressed);
+ }
+
+ CheckTimers(z80,FRAME_TSTATES);
+ }
+
+ waitkey=FALSE;
+ break;
+
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+/* ---------------------------------------- EXPORTED INTERFACES
+*/
+void ZX81Init(Z80 *z80)
+{
+ Z80Word f;
+
+ hires = FALSE;
+ hires_dfile = 0;
+ last_I = 0x1e;
+
+ /* Load the ROM
+ */
+ memcpy(mem,zx81_bin,ROMLEN);
+
+ /* Patch the ROM
+ */
+ RomPatch();
+ Z80LodgeCallback(z80,eZ80_EDHook,EDCallback);
+ Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers);
+
+ /* Mirror the ROM
+ */
+ memcpy(mem+ROMLEN,mem,ROMLEN);
+
+ /* Memory size (16K)
+ */
+ RAMBOT=0x4000;
+ RAMTOP=RAMBOT+0x4000;
+
+ for(f = RAMBOT; f <= RAMTOP; f++)
+ {
+ mem[f] = 0;
+ }
+
+ for(f = 0; f < 8; f++)
+ {
+ matrix[f] = 0x1f;
+ }
+
+ /* Fill the upper 32K with RET opcodes -- hopefully by simply returning
+ RET for ULA reads we save a lot of ROM patching shenanigans.
+
+ Note that this check used to be in ZX81ReadMem, but obviously this
+ should cut down on a *lot* of pointless expression evaluation!
+ */
+ for(f = 0x8000; f <= RAMTOP; f++)
+ {
+ mem[f] = 0xc9;
+ }
+
+}
+
+
+void ZX81RenderDisplay(Framebuffer *fb)
+{
+ FB_Clear(fb, COL_BLACK);
+ FB_Centre(fb, "TODO", 10, COL_WHITE, COL_TRANSPARENT);
+}
+
+
+void ZX81HandleKey(SoftKey key, int is_pressed)
+{
+ if (key<SK_CONFIG)
+ {
+ if (is_pressed)
+ {
+ matrix[key_matrix[key].row]&=~key_matrix[key].bit;
+ }
+ else
+ {
+ matrix[key_matrix[key].row]|=key_matrix[key].bit;
+ }
+ }
+ else
+ {
+ /* TODO: Joysticks? Were there any common ones for the 81? */
+ }
+}
+
+
+Z80Byte ZX81ReadMem(Z80 *z80, Z80Word addr)
+{
+ return mem[addr];
+}
+
+
+void ZX81WriteMem(Z80 *z80, Z80Word addr, Z80Byte val)
+{
+ if (addr>=RAMBOT && addr<=RAMTOP)
+ {
+ mem[addr]=val;
+ }
+}
+
+
+Z80Byte ZX81ReadPort(Z80 *z80, Z80Word port)
+{
+ Z80Byte b=0;
+
+ switch(port&0xff)
+ {
+ case 0xfe: /* ULA */
+ /* Key matrix
+ */
+ switch(port&0xff00)
+ {
+ case 0xfe00:
+ b=matrix[0];
+ break;
+ case 0xfd00:
+ b=matrix[1];
+ break;
+ case 0xfb00:
+ b=matrix[2];
+ break;
+ case 0xf700:
+ b=matrix[3];
+ break;
+ case 0xef00:
+ b=matrix[4];
+ break;
+ case 0xdf00:
+ b=matrix[5];
+ break;
+ case 0xbf00:
+ b=matrix[6];
+ break;
+ case 0x7f00:
+ b=matrix[7];
+ break;
+ }
+
+ /* Some code expects some of the top bits set... Of course, whether
+ or not this may be worse as other code doesn't expect the bits,
+ we shall find out!
+ */
+ b |= 0x60;
+
+ break;
+
+ default:
+ b = 0xff; /* Idle bus */
+ break;
+ }
+
+ return b;
+}
+
+
+void ZX81WritePort(Z80 *z80, Z80Word port, Z80Byte val)
+{
+ switch(port&0xff)
+ {
+ case 0xfd:
+ break;
+
+ case 0xfe:
+ break;
+ }
+}
+
+
+void ZX81Reset(Z80 *z80)
+{
+ int f;
+
+ for(f=0;f<8;f++)
+ matrix[f]=0x1f;
+
+ Z80Reset(z80);
+ Z80ResetCycles(z80,0);
+
+ started=FALSE;
+
+ hires = FALSE;
+ hires_dfile = 0;
+ last_I = 0x1e;
+}
+
+
+void ZX81EnableFileSystem(int enable)
+{
+ enable_filesystem=enable;
+}
+
+
+void ZX81SetTape(const Z80Byte *image, int len)
+{
+ tape_image=image;
+ tape_len=len;
+}
+
+
+void ZX81Reconfigure(void)
+{
+ if (DS81_Config[DS81_STATIC_RAM_AT_0x2000])
+ {
+ RAMBOT = 0x2000;
+ }
+ else
+ {
+ RAMBOT = 0x4000;
+ memcpy(mem+ROMLEN,mem,ROMLEN);
+ }
+
+ allow_save = enable_filesystem && DS81_Config[DS81_ALLOW_TAPE_SAVE];
+}
+
+
+void ZX81SaveSnapshot(FILE *fp)
+{
+ int f;
+
+ for(f=0; f<sizeof mem; f++)
+ {
+ PUT_Byte(fp, mem[f]);
+ }
+
+ for(f=0; f<sizeof matrix; f++)
+ {
+ PUT_Byte(fp, matrix[f]);
+ }
+
+ PUT_Long(fp, waitkey);
+ PUT_Long(fp, started);
+
+ PUT_ULong(fp, RAMBOT);
+ PUT_ULong(fp, RAMTOP);
+
+ PUT_ULong(fp, prev_lk1);
+ PUT_ULong(fp, prev_lk2);
+}
+
+
+void ZX81LoadSnapshot(FILE *fp)
+{
+ int f;
+
+ for(f=0; f<sizeof mem; f++)
+ {
+ mem[f] = GET_Byte(fp);
+ }
+
+ for(f=0; f<sizeof matrix; f++)
+ {
+ matrix[f] = GET_Byte(fp);
+ }
+
+ waitkey = GET_Long(fp);
+ started = GET_Long(fp);
+
+ RAMBOT = GET_ULong(fp);
+ RAMTOP = GET_ULong(fp);
+
+ prev_lk1 = GET_ULong(fp);
+ prev_lk2 = GET_ULong(fp);
+
+ /* Reset last_I to force hi/lo res detection
+ */
+ last_I = 0;
+}
+
+
+/* END OF FILE */