diff options
author | Ian C <ianc@noddybox.co.uk> | 2021-06-25 19:38:44 +0000 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2021-06-25 19:38:44 +0000 |
commit | abad1c81839420fa60d8923d2332ea003cd73a06 (patch) | |
tree | 508732c8310ed93897689eeafce8e6dbaed87755 | |
parent | 99c00f06c19308c222e123671d2d0f521dd62e05 (diff) |
Added files and got initial support working. ZX81 display needed.
-rw-r--r-- | LICENSE | 674 | ||||
-rw-r--r-- | Makefile | 209 | ||||
-rw-r--r-- | data/cpatrol.bin | bin | 0 -> 9198 bytes | |||
-rw-r--r-- | data/maze.bin | bin | 0 -> 10553 bytes | |||
-rw-r--r-- | data/mazogs.bin | bin | 0 -> 12939 bytes | |||
-rw-r--r-- | data/sabotage.bin | bin | 0 -> 8492 bytes | |||
-rw-r--r-- | data/zx81.bin | bin | 0 -> 8192 bytes | |||
-rw-r--r-- | gfx/cpatrol_inlay.png | bin | 0 -> 51234 bytes | |||
-rw-r--r-- | gfx/keyb.png | bin | 0 -> 4736 bytes | |||
-rw-r--r-- | gfx/maze_inlay.png | bin | 0 -> 45597 bytes | |||
-rw-r--r-- | gfx/mazogs_inlay.png | bin | 0 -> 63379 bytes | |||
-rw-r--r-- | gfx/sabotage_inlay.png | bin | 0 -> 49343 bytes | |||
-rw-r--r-- | gfx/splashimg.png | bin | 0 -> 177511 bytes | |||
-rw-r--r-- | include/config.h | 53 | ||||
-rw-r--r-- | include/framebuffer.h | 124 | ||||
-rw-r--r-- | include/gui.h | 32 | ||||
-rw-r--r-- | include/keyboard.h | 135 | ||||
-rw-r--r-- | include/snapshot.h | 36 | ||||
-rw-r--r-- | include/stream.h | 34 | ||||
-rw-r--r-- | include/tapes.h | 26 | ||||
-rw-r--r-- | include/z80.h | 256 | ||||
-rw-r--r-- | include/z80_config.h | 58 | ||||
-rw-r--r-- | include/z80_private.h | 275 | ||||
-rw-r--r-- | include/zx81.h | 82 | ||||
-rw-r--r-- | source/config.c | 143 | ||||
-rw-r--r-- | source/framebuffer.c | 433 | ||||
-rw-r--r-- | source/gui.c | 919 | ||||
-rw-r--r-- | source/keyboard.c | 428 | ||||
-rw-r--r-- | source/main.c | 358 | ||||
-rw-r--r-- | source/snapshot.c | 192 | ||||
-rw-r--r-- | source/stream.c | 85 | ||||
-rw-r--r-- | source/tapes.c | 258 | ||||
-rw-r--r-- | source/z80.c | 389 | ||||
-rw-r--r-- | source/z80_decode.c | 2530 | ||||
-rw-r--r-- | source/z80_dis.c | 2491 | ||||
-rw-r--r-- | source/zx81.c | 868 |
36 files changed, 11088 insertions, 0 deletions
@@ -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 Binary files differnew file mode 100644 index 0000000..05dda79 --- /dev/null +++ b/data/cpatrol.bin diff --git a/data/maze.bin b/data/maze.bin Binary files differnew file mode 100644 index 0000000..2882186 --- /dev/null +++ b/data/maze.bin diff --git a/data/mazogs.bin b/data/mazogs.bin Binary files differnew file mode 100644 index 0000000..be3effb --- /dev/null +++ b/data/mazogs.bin diff --git a/data/sabotage.bin b/data/sabotage.bin Binary files differnew file mode 100644 index 0000000..eb9c19d --- /dev/null +++ b/data/sabotage.bin diff --git a/data/zx81.bin b/data/zx81.bin Binary files differnew file mode 100644 index 0000000..557ddcb --- /dev/null +++ b/data/zx81.bin diff --git a/gfx/cpatrol_inlay.png b/gfx/cpatrol_inlay.png Binary files differnew file mode 100644 index 0000000..e22ff4f --- /dev/null +++ b/gfx/cpatrol_inlay.png diff --git a/gfx/keyb.png b/gfx/keyb.png Binary files differnew file mode 100644 index 0000000..c2043d1 --- /dev/null +++ b/gfx/keyb.png diff --git a/gfx/maze_inlay.png b/gfx/maze_inlay.png Binary files differnew file mode 100644 index 0000000..e2eefd2 --- /dev/null +++ b/gfx/maze_inlay.png diff --git a/gfx/mazogs_inlay.png b/gfx/mazogs_inlay.png Binary files differnew file mode 100644 index 0000000..a7af40a --- /dev/null +++ b/gfx/mazogs_inlay.png diff --git a/gfx/sabotage_inlay.png b/gfx/sabotage_inlay.png Binary files differnew file mode 100644 index 0000000..837e994 --- /dev/null +++ b/gfx/sabotage_inlay.png diff --git a/gfx/splashimg.png b/gfx/splashimg.png Binary files differnew file mode 100644 index 0000000..eeaa651 --- /dev/null +++ b/gfx/splashimg.png 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 */ |