# HG changeset patch # User Robert McIntyre # Date 1330893232 21600 # Node ID 5e8e5083da94e0f945bcd93d44abc272db8748bd # Parent ac56489c2ca6a07bc28b147899f8e0c283506d44 brought in common and gba, fixed problems with outdated Makefile.am files in both of these packages diff -r ac56489c2ca6 -r 5e8e5083da94 .hgignore --- a/.hgignore Sat Mar 03 12:06:10 2012 -0600 +++ b/.hgignore Sun Mar 04 14:33:52 2012 -0600 @@ -10,4 +10,5 @@ configure.scan *Makefile.in* config.h.in -build/* \ No newline at end of file +build/* +build-aux/* \ No newline at end of file diff -r ac56489c2ca6 -r 5e8e5083da94 COPYING --- a/COPYING Sat Mar 03 12:06:10 2012 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - 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. - - - Copyright (C) - - 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 . - -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: - - Copyright (C) - 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 -. - - 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 -. diff -r ac56489c2ca6 -r 5e8e5083da94 INSTALL --- a/INSTALL Sat Mar 03 12:06:10 2012 -0600 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,365 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff -r ac56489c2ca6 -r 5e8e5083da94 configure.ac --- a/configure.ac Sat Mar 03 12:06:10 2012 -0600 +++ b/configure.ac Sun Mar 04 14:33:52 2012 -0600 @@ -2,6 +2,10 @@ AC_INIT([vba-rlm], [1.0]) +AC_CONFIG_AUX_DIR([build-aux]) + +AM_INIT_AUTOMAKE([foreign dist-bzip2]) + dnl TODO: change this to gba.cpp or something AC_CONFIG_SRCDIR([src/lua/lopcodes.c]) AC_CONFIG_HEADERS([config.h]) @@ -12,6 +16,17 @@ AC_PROG_CC AC_PROG_RANLIB + + +# SDL stuff +SDL_VERSION=1.2.2 +AM_PATH_SDL($SDL_VERSION,[ +],[ +AC_MSG_ERROR([*** Couldn't find SDL library (version >= $SDL_VERSION).]) +]) + + + # Checks for header files. AC_CHECK_HEADERS([limits.h locale.h stddef.h stdlib.h string.h unistd.h]) @@ -35,9 +50,11 @@ AC_CONFIG_FILES([Makefile src/Makefile src/lua/Makefile - src/gb/Makefile]) + src/gb/Makefile + src/gba/Makefile + src/common/Makefile]) -AM_INIT_AUTOMAKE([dist-bzip2]) + AC_OUTPUT diff -r ac56489c2ca6 -r 5e8e5083da94 src/Makefile.am --- a/src/Makefile.am Sat Mar 03 12:06:10 2012 -0600 +++ b/src/Makefile.am Sun Mar 04 14:33:52 2012 -0600 @@ -1,1 +1,1 @@ -SUBDIRS = lua gb +SUBDIRS = lua gb gba common diff -r ac56489c2ca6 -r 5e8e5083da94 src/NLS.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/NLS.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,52 @@ +#ifndef VBS_NLS_H +#define VBA_NLS_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define N_(String) (String) + +#define MSG_UNSUPPORTED_VBA_SGM 1 +#define MSG_CANNOT_LOAD_SGM 2 +#define MSG_SAVE_GAME_NOT_USING_BIOS 3 +#define MSG_SAVE_GAME_USING_BIOS 4 +#define MSG_UNSUPPORTED_SAVE_TYPE 5 +#define MSG_CANNOT_OPEN_FILE 6 +#define MSG_BAD_ZIP_FILE 7 +#define MSG_NO_IMAGE_ON_ZIP 8 +#define MSG_ERROR_OPENING_IMAGE 9 +#define MSG_ERROR_READING_IMAGE 10 +#define MSG_UNSUPPORTED_BIOS_FUNCTION 11 +#define MSG_INVALID_BIOS_FILE_SIZE 12 +#define MSG_INVALID_CHEAT_CODE 13 +#define MSG_UNKNOWN_ARM_OPCODE 14 +#define MSG_UNKNOWN_THUMB_OPCODE 15 +#define MSG_ERROR_CREATING_FILE 16 +#define MSG_FAILED_TO_READ_SGM 17 +#define MSG_FAILED_TO_READ_RTC 18 +#define MSG_UNSUPPORTED_VB_SGM 19 +#define MSG_CANNOT_LOAD_SGM_FOR 20 +#define MSG_ERROR_OPENING_IMAGE_FROM 21 +#define MSG_ERROR_READING_IMAGE_FROM 22 +#define MSG_UNSUPPORTED_ROM_SIZE 23 +#define MSG_UNSUPPORTED_RAM_SIZE 24 +#define MSG_UNKNOWN_CARTRIDGE_TYPE 25 +#define MSG_MAXIMUM_NUMBER_OF_CHEATS 26 +#define MSG_INVALID_GAMESHARK_CODE 27 +#define MSG_INVALID_GAMEGENIE_CODE 28 +#define MSG_INVALID_CHEAT_TO_REMOVE 29 +#define MSG_INVALID_CHEAT_CODE_ADDRESS 30 +#define MSG_UNSUPPORTED_CHEAT_LIST_VERSION 31 +#define MSG_UNSUPPORTED_CHEAT_LIST_TYPE 32 +#define MSG_INVALID_GSA_CODE 33 +#define MSG_CANNOT_IMPORT_SNAPSHOT_FOR 34 +#define MSG_UNSUPPORTED_SNAPSHOT_FILE 35 +#define MSG_UNSUPPORTED_ARM_MODE 36 +#define MSG_UNSUPPORTED_CODE_FILE 37 +#define MSG_GBA_CODE_WARNING 38 +#define MSG_INVALID_CBA_CODE 39 +#define MSG_CBA_CODE_WARNING 40 +#define MSG_OUT_OF_MEMORY 41 + +#endif // VBA_NLS_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/Port.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Port.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,178 @@ +#ifndef VBA_PORT_H +#define VBA_PORT_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +#ifndef NULL +#define NULL 0 +#endif + +typedef unsigned char bool8; + +#ifdef HAVE_STDINT_H +#include + +typedef int8_t int8; +typedef uint8_t uint8; +typedef int16_t int16; +typedef uint16_t uint16; +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +typedef intptr_t pint; + +#else /* Don't have stdint.h */ + +#ifdef PTR_NOT_INT +typedef long pint; +#else /* pointer is int */ +typedef int pint; +#endif /* PTR_NOT_INT */ + +/* FIXME: Refactor this by moving out the BORLAND part and unifying typedefs */ +#ifndef WIN32 +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef unsigned int uint32; +# ifdef __GNUC__ /* long long is not part of ISO C++ */ +__extension__ typedef long long int64; +__extension__ typedef unsigned long long uint64; +# else +typedef long long int64; +typedef unsigned long long uint64; +# endif +#else /* WIN32 */ + +# ifdef __BORLANDC__ +# include +# else + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed char int8; +typedef short int16; + +# ifndef WSAAPI +/* winsock2.h typedefs int32 as well. */ +typedef long int32; +# endif + +typedef unsigned int uint32; + +# endif /* __BORLANDC__ */ + +typedef __int64 int64; +typedef unsigned __int64 uint64; + +#endif /* WIN32 */ +#endif /* HAVE_STDINT_H */ + +#ifndef WIN32 + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#define _MAX_DIR PATH_MAX +#define _MAX_DRIVE 1 +#define _MAX_FNAME PATH_MAX +#define _MAX_EXT PATH_MAX +#define _MAX_PATH PATH_MAX + +#define ZeroMemory(a, b) memset((a), 0, (b)) + +void _makepath(char *path, const char *drive, const char *dir, + const char *fname, const char *ext); +void _splitpath(const char *path, char *drive, char *dir, char *fname, + char *ext); +#else /* WIN32 */ +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +typedef uint8 u8; +typedef uint16 u16; +typedef uint32 u32; +typedef uint64 u64; +typedef int8 s8; +typedef int16 s16; +typedef int32 s32; +typedef int64 s64; + +// for consistency +static inline u8 swap8(u8 v) +{ + return v; +} + +// swaps a 16-bit value +static inline u16 swap16(u16 v) +{ + return (v<<8)|(v>>8); +} + +// swaps a 32-bit value +static inline u32 swap32(u32 v) +{ + return (v<<24)|((v<<8)&0xff0000)|((v>>8)&0xff00)|(v>>24); +} + +#define READ8LE(x) \ + *((u8 *)x) + +#define WRITE8LE(x, v) \ + *((u8 *)x) = (v) + +#ifdef WORDS_BIGENDIAN +#if defined(__GNUC__) && defined(__ppc__) + +#define READ16LE(base) \ + ({ unsigned short lhbrxResult; \ + __asm__("lhbrx %0, 0, %1" : "=r" (lhbrxResult) : "r" (base) : "memory"); \ + lhbrxResult; }) + +#define READ32LE(base) \ + ({ unsigned long lwbrxResult; \ + __asm__("lwbrx %0, 0, %1" : "=r" (lwbrxResult) : "r" (base) : "memory"); \ + lwbrxResult; }) + +#define WRITE16LE(base, value) \ + __asm__("sthbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#define WRITE32LE(base, value) \ + __asm__("stwbrx %0, 0, %1" : : "r" (value), "r" (base) : "memory") + +#else +#define READ16LE(x) \ + swap16(*((u16 *)(x))) +#define READ32LE(x) \ + swap32(*((u32 *)(x))) +#define WRITE16LE(x, v) \ + *((u16 *)x) = swap16((v)) +#define WRITE32LE(x, v) \ + *((u32 *)x) = swap32((v)) +#endif +#else +#define READ16LE(x) \ + *((u16 *)x) +#define READ32LE(x) \ + *((u32 *)x) +#define WRITE16LE(x, v) \ + *((u16 *)x) = (v) +#define WRITE32LE(x, v) \ + *((u32 *)x) = (v) +#endif + +#ifndef CTASSERT +#define CTASSERT(x) typedef char __assert ## y[(x) ? 1 : -1]; +#endif + +#endif // VBA_PORT_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/CheatSearch.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/CheatSearch.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,371 @@ +#include +#include + +#include "CheatSearch.h" + +CheatSearchBlock cheatSearchBlocks[4]; + +CheatSearchData cheatSearchData = { + 0, + cheatSearchBlocks +}; + +static bool cheatSearchEQ(u32 a, u32 b) +{ + return a == b; +} + +static bool cheatSearchNE(u32 a, u32 b) +{ + return a != b; +} + +static bool cheatSearchLT(u32 a, u32 b) +{ + return a < b; +} + +static bool cheatSearchLE(u32 a, u32 b) +{ + return a <= b; +} + +static bool cheatSearchGT(u32 a, u32 b) +{ + return a > b; +} + +static bool cheatSearchGE(u32 a, u32 b) +{ + return a >= b; +} + +static bool cheatSearchSignedEQ(s32 a, s32 b) +{ + return a == b; +} + +static bool cheatSearchSignedNE(s32 a, s32 b) +{ + return a != b; +} + +static bool cheatSearchSignedLT(s32 a, s32 b) +{ + return a < b; +} + +static bool cheatSearchSignedLE(s32 a, s32 b) +{ + return a <= b; +} + +static bool cheatSearchSignedGT(s32 a, s32 b) +{ + return a > b; +} + +static bool cheatSearchSignedGE(s32 a, s32 b) +{ + return a >= b; +} + +static bool (*cheatSearchFunc[])(u32, u32) = { + cheatSearchEQ, + cheatSearchNE, + cheatSearchLT, + cheatSearchLE, + cheatSearchGT, + cheatSearchGE +}; + +static bool (*cheatSearchSignedFunc[])(s32, s32) = { + cheatSearchSignedEQ, + cheatSearchSignedNE, + cheatSearchSignedLT, + cheatSearchSignedLE, + cheatSearchSignedGT, + cheatSearchSignedGE +}; + +void cheatSearchSetSavedAndBits(CheatSearchBlock *block) +{ + if (!block->saved) + { + block->saved = (u8 *)malloc(block->size); + memcpy(block->saved, block->data, block->size); + } + if (!block->bits) + { + block->bits = (u8 *)malloc(block->size >> 3); + memset(block->bits, 0xff, block->size >> 3); + } +} + +void cheatSearchZeroBlock(CheatSearchBlock *block) +{ + block->data = 0; + block->offset = 0; + block->size = 0; + free(block->saved); + free(block->bits); + block->saved = 0; + block->bits = 0; +} + +void cheatSearchCleanup(CheatSearchData *cs) +{ + int count = cs->count; + + for (int i = 0; i < count; i++) + { + CheatSearchBlock &block = cs->blocks[i]; + free(block.saved); + free(block.bits); + block.saved = 0; + block.bits = 0; + } + cs->count = 0; +} + +void cheatSearchStart(const CheatSearchData *cs) +{ + int count = cs->count; + + for (int i = 0; i < count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + memset(block->bits, 0xff, block->size >> 3); + memcpy(block->saved, block->data, block->size); + } +} + +s32 cheatSearchSignedRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + + switch (size) + { + case BITS_8: + res <<= 24; + return ((s32)res) >> 24; + case BITS_16: + res |= ((u32)data[off++])<<8; + res <<= 16; + return ((s32)res) >> 16; + case BITS_32: + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + return (s32)res; + } + return (s32)res; +} + +u32 cheatSearchRead(u8 *data, int off, int size) +{ + u32 res = data[off++]; + if (size == BITS_16) + res |= ((u32)data[off++])<<8; + else if (size == BITS_32) + { + res |= ((u32)data[off++])<<8; + res |= ((u32)data[off++])<<16; + res |= ((u32)data[off++])<<24; + } + return res; +} + +void cheatSearch(const CheatSearchData *cs, int compare, int size, + bool isSigned) +{ + if (compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if (size == BITS_16) + inc = 2; + else if (size == BITS_32) + inc = 4; + + if (isSigned) + { + bool (*func)(s32, s32) = cheatSearchSignedFunc[compare]; + + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for (int j = 0; j < size2; j += inc) + { + if (IS_BIT_SET(bits, j)) + { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = cheatSearchSignedRead(saved, j, size); + + if (!func(a, b)) + { + CLEAR_BIT(bits, j); + if (size == BITS_16) + CLEAR_BIT(bits, j+1); + if (size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } + else + { + bool (*func)(u32, u32) = cheatSearchFunc[compare]; + + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + u8 *saved = block->saved; + + for (int j = 0; j < size2; j += inc) + { + if (IS_BIT_SET(bits, j)) + { + u32 a = cheatSearchRead(data, j, size); + u32 b = cheatSearchRead(saved, j, size); + + if (!func(a, b)) + { + CLEAR_BIT(bits, j); + if (size == BITS_16) + CLEAR_BIT(bits, j+1); + if (size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +void cheatSearchValue(const CheatSearchData *cs, int compare, int size, + bool isSigned, u32 value) +{ + if (compare < 0 || compare > SEARCH_GE) + return; + int inc = 1; + if (size == BITS_16) + inc = 2; + else if (size == BITS_32) + inc = 4; + + if (isSigned) + { + bool (*func)(s32, s32) = cheatSearchSignedFunc[compare]; + + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for (int j = 0; j < size2; j += inc) + { + if (IS_BIT_SET(bits, j)) + { + s32 a = cheatSearchSignedRead(data, j, size); + s32 b = (s32)value; + + if (!func(a, b)) + { + CLEAR_BIT(bits, j); + if (size == BITS_16) + CLEAR_BIT(bits, j+1); + if (size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } + else + { + bool (*func)(u32, u32) = cheatSearchFunc[compare]; + + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + int size2 = block->size; + u8 *bits = block->bits; + u8 *data = block->data; + + for (int j = 0; j < size2; j += inc) + { + if (IS_BIT_SET(bits, j)) + { + u32 a = cheatSearchRead(data, j, size); + + if (!func(a, value)) + { + CLEAR_BIT(bits, j); + if (size == BITS_16) + CLEAR_BIT(bits, j+1); + if (size == BITS_32) + { + CLEAR_BIT(bits, j+2); + CLEAR_BIT(bits, j+3); + } + } + } + } + } + } +} + +int cheatSearchGetCount(const CheatSearchData *cs, int size) +{ + int res = 0; + int inc = 1; + if (size == BITS_16) + inc = 2; + else if (size == BITS_32) + inc = 4; + + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + int size2 = block->size; + u8 *bits = block->bits; + for (int j = 0; j < size2; j += inc) + { + if (IS_BIT_SET(bits, j)) + res++; + } + } + return res; +} + +void cheatSearchUpdateValues(const CheatSearchData *cs) +{ + for (int i = 0; i < cs->count; i++) + { + CheatSearchBlock *block = &cs->blocks[i]; + + memcpy(block->saved, block->data, block->size); + } +} diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/CheatSearch.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/CheatSearch.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,65 @@ +#ifndef VBA_CHEATSEARCH_H +#define VBA_CHEATSEARCH_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../Port.h" + +struct CheatSearchBlock +{ + u8 *data; + int size; + u32 offset; + u8 *saved; + u8 *bits; +}; + +struct CheatSearchData +{ + int count; + CheatSearchBlock *blocks; +}; + +enum +{ + SEARCH_EQ, + SEARCH_NE, + SEARCH_LT, + SEARCH_LE, + SEARCH_GT, + SEARCH_GE +}; + +enum +{ + BITS_8, + BITS_16, + BITS_32 +}; + +#define SET_BIT(bits, off) \ + (bits)[(off) >> 3] |= (1 << ((off) & 7)) + +#define CLEAR_BIT(bits, off) \ + (bits)[(off) >> 3] &= ~(1 << ((off) & 7)) + +#define IS_BIT_SET(bits, off) \ + (bits)[(off) >> 3] & (1 << ((off) & 7)) + +extern CheatSearchData cheatSearchData; +extern void cheatSearchSetSavedAndBits(CheatSearchBlock *block); +extern void cheatSearchZeroBlock(CheatSearchBlock *block); +extern void cheatSearchCleanup(CheatSearchData *cs); +extern void cheatSearchStart(const CheatSearchData *cs); +extern void cheatSearch(const CheatSearchData *cs, int compare, int size, + bool isSigned); +extern void cheatSearchValue(const CheatSearchData *cs, int compare, int size, + bool isSigned, u32 value); +extern int cheatSearchGetCount(const CheatSearchData *cs, int size); +extern void cheatSearchUpdateValues(const CheatSearchData *cs); +extern s32 cheatSearchSignedRead(u8 *data, int off, int size); +extern u32 cheatSearchRead(u8 *data, int off, int size); + +#endif // VBA_CHEATSEARCH_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/Makefile.am Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,31 @@ +noinst_LIBRARIES = libgbcom.a + +libgbcom_a_SOURCES = \ + CheatSearch.h \ + inputGlobal.h \ + memgzio.h \ + movie.h \ + nesvideos-piece.h \ + System.h \ + Text.h \ + unzip.h \ + Util.h \ + vbalua.h \ + \ + CheatSearch.cpp \ + lua-engine.cpp \ + memgzio.c \ + movie.cpp \ + nesvideos-piece.cpp \ + Text.cpp \ + unzip.cpp \ + Util.cpp + + + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -DSDL \ + -DSYSCONFDIR=\"$(sysconfdir)\" + +AM_CXXFLAGS = -fno-exceptions @SDL_CFLAGS@ diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/System.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/System.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,137 @@ +#ifndef VBA_SYSTEM_H +#define VBA_SYSTEM_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +// c++ lacks a way to implement Smart Referrences or Delphi-Style Properties +// in order to maintain consistency, value-copied things should not be modified too often +struct EmulatedSystem +{ + // main emulation function + void (*emuMain)(int); + // reset emulator + void (*emuReset)(bool); + // clean up memory + void (*emuCleanUp)(); + // load battery file + bool (*emuReadBattery)(const char *); + // write battery file + bool (*emuWriteBattery)(const char *); + // load battery file from stream + bool (*emuReadBatteryFromStream)(gzFile); + // write battery file to stream + bool (*emuWriteBatteryToStream)(gzFile); + // load state + bool (*emuReadState)(const char *); + // save state + bool (*emuWriteState)(const char *); + // load state from stream + bool (*emuReadStateFromStream)(gzFile); + // save state to stream + bool (*emuWriteStateToStream)(gzFile); + // load memory state (rewind) + bool (*emuReadMemState)(char *, int); + // write memory state (rewind) + bool (*emuWriteMemState)(char *, int); + // write PNG file + bool (*emuWritePNG)(const char *); + // write BMP file + bool (*emuWriteBMP)(const char *); + // emulator update CPSR (ARM only) + void (*emuUpdateCPSR)(); + // emulator has debugger + bool emuHasDebugger; + // clock ticks to emulate + int emuCount; +}; + +// why not convert the value type only when doing I/O? +struct EmulatedSystemCounters +{ + int32 frameCount; + int32 lagCount; + int32 extraCount; + bool8 lagged; + bool8 laggedLast; +}; + +extern struct EmulatedSystem theEmulator; +extern struct EmulatedSystemCounters systemCounters; + +extern void log(const char *, ...); + +extern void systemGbPrint(u8 *, int, int, int, int); +extern int systemScreenCapture(int); +extern void systemRefreshScreen(); +extern void systemRenderFrame(); +extern void systemRedrawScreen(); +extern void systemUpdateListeners(); +// updates the joystick data +extern void systemSetSensorX(int32); +extern void systemSetSensorY(int32); +extern void systemResetSensor(); +extern int32 systemGetSensorX(); +extern int32 systemGetSensorY(); +extern void systemUpdateMotionSensor(int); +extern int systemGetDefaultJoypad(); +extern void systemSetDefaultJoypad(int); +extern bool systemReadJoypads(); +// return information about the given joystick, -1 for default joystick... the bool is for if motion sensor should be handled +// too +extern u32 systemGetOriginalJoypad(int, bool); +extern u32 systemGetJoypad(int, bool); +extern void systemSetJoypad(int, u32); +extern void systemClearJoypads(); +extern void systemMessage(int, const char *, ...); +extern void systemScreenMessage(const char *msg, int slot = 0, int duration = 3000, const char *colorList = NULL); +extern bool systemSoundInit(); +extern void systemSoundShutdown(); +extern void systemSoundPause(); +extern void systemSoundResume(); +extern bool systemSoundIsPaused(); +extern void systemSoundReset(); +extern void systemSoundWriteToBuffer(); +extern void systemSoundClearBuffer(); +extern bool systemSoundCanChangeQuality(); +extern bool systemSoundSetQuality(int quality); +extern u32 systemGetClock(); +extern void systemSetTitle(const char *); +extern void systemShowSpeed(int); +extern void systemIncreaseThrottle(); +extern void systemDecreaseThrottle(); +extern void systemSetThrottle(int); +extern int systemGetThrottle(); +extern void systemFrame(); +extern int systemFramesToSkip(); +extern bool systemIsEmulating(); +extern void systemGbBorderOn(); +extern bool systemIsRunningGBA(); +extern bool systemIsSpedUp(); +extern bool systemIsPaused(); +extern void systemSetPause(bool pause); +extern bool systemPauseOnFrame(); + +extern int systemCartridgeType; +extern int systemSpeed; +extern bool systemSoundOn; +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; +extern u16 systemGbPalette[24]; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; +extern int systemColorDepth; +extern int systemDebug; +extern int systemVerbose; +extern int systemFrameSkip; +extern int systemSaveUpdateCounter; + +#define SYSTEM_SAVE_UPDATED 30 +#define SYSTEM_SAVE_NOT_UPDATED 0 + +#endif // VBA_SYSTEM_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/Text.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/Text.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,496 @@ +/* FCE Ultra - NES/Famicom Emulator + * + * Copyright notice for this file: + * Copyright (C) 2002 Ben Parnell + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Code originally from fceu/drawing.h file, adapted by Forgotten + */ +#include "System.h" + +bool outlinedText = true, transparentText = false; +int textColor = 0, textMethod = 1; + +extern u32 RGB_LOW_BITS_MASK; + +static const u8 fontdata2[2048] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e, 0x7e, 0xff, 0xdb, 0xff, + 0xc3, 0xe7, 0xff, 0x7e, 0x36, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, 0x00, 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00, + 0x1c, + 0x3e, 0x1c, 0x7f, 0x7f, 0x3e, 0x1c, 0x3e, 0x08, 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x3e, 0x00, 0x00, 0x18, 0x3c, 0x3c, + 0x18, + 0x00, 0x00, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0xff, 0xc3, + 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xf0, 0xe0, 0xf0, 0xbe, 0x33, 0x33, 0x33, 0x1e, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, + 0x7e, + 0x18, 0xfc, 0xcc, 0xfc, 0x0c, 0x0c, 0x0e, 0x0f, 0x07, 0xfe, 0xc6, 0xfe, 0xc6, 0xc6, 0xe6, 0x67, 0x03, 0x99, 0x5a, 0x3c, + 0xe7, + 0xe7, 0x3c, 0x5a, 0x99, 0x01, 0x07, 0x1f, 0x7f, 0x1f, 0x07, 0x01, 0x00, 0x40, 0x70, 0x7c, 0x7f, 0x7c, 0x70, 0x40, 0x00, + 0x18, + 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 0xfe, 0xdb, 0xdb, 0xde, 0xd8, + 0xd8, + 0xd8, 0x00, 0x7c, 0xc6, 0x1c, 0x36, 0x36, 0x1c, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00, 0x18, 0x3c, + 0x7e, + 0x18, 0x7e, 0x3c, 0x18, 0xff, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, + 0x00, + 0x00, 0x18, 0x30, 0x7f, 0x30, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x7f, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, + 0x03, + 0x7f, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00, 0x00, + 0xff, + 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1e, 0x1e, 0x0c, 0x0c, 0x00, 0x0c, 0x00, 0x36, 0x36, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x0c, 0x3e, 0x03, 0x1e, 0x30, 0x1f, 0x0c, 0x00, + 0x00, + 0x63, 0x33, 0x18, 0x0c, 0x66, 0x63, 0x00, 0x1c, 0x36, 0x1c, 0x6e, 0x3b, 0x33, 0x6e, 0x00, 0x06, 0x06, 0x03, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x18, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x18, 0x00, 0x06, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x66, + 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x3f, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x0c, + 0x06, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x60, 0x30, 0x18, + 0x0c, + 0x06, 0x03, 0x01, 0x00, 0x3e, 0x63, 0x73, 0x7b, 0x6f, 0x67, 0x3e, 0x00, 0x0c, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x3f, 0x00, + 0x1e, + 0x33, 0x30, 0x1c, 0x06, 0x33, 0x3f, 0x00, 0x1e, 0x33, 0x30, 0x1c, 0x30, 0x33, 0x1e, 0x00, 0x38, 0x3c, 0x36, 0x33, 0x7f, + 0x30, + 0x78, 0x00, 0x3f, 0x03, 0x1f, 0x30, 0x30, 0x33, 0x1e, 0x00, 0x1c, 0x06, 0x03, 0x1f, 0x33, 0x33, 0x1e, 0x00, 0x3f, 0x33, + 0x30, + 0x18, 0x0c, 0x0c, 0x0c, 0x00, 0x1e, 0x33, 0x33, 0x1e, 0x33, 0x33, 0x1e, 0x00, 0x1e, 0x33, 0x33, 0x3e, 0x30, 0x18, 0x0e, + 0x00, + 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x06, 0x18, 0x0c, 0x06, 0x03, + 0x06, + 0x0c, 0x18, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x1e, + 0x33, + 0x30, 0x18, 0x0c, 0x00, 0x0c, 0x00, + 0x3e, 0x63, 0x7b, 0x7b, 0x7b, 0x03, 0x1e, 0x00, 0x0c, 0x1e, 0x33, 0x33, 0x3f, 0x33, 0x33, 0x00, 0x3f, 0x66, 0x66, 0x3e, + 0x66, 0x66, 0x3f, 0x00, 0x3c, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3c, 0x00, 0x1f, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00, + 0x7f, + 0x46, 0x16, 0x1e, 0x16, 0x46, 0x7f, 0x00, 0x7f, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x0f, 0x00, 0x3c, 0x66, 0x03, 0x03, 0x73, + 0x66, + 0x7c, 0x00, 0x33, 0x33, 0x33, 0x3f, 0x33, 0x33, 0x33, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x78, 0x30, + 0x30, 0x30, 0x33, 0x33, 0x1e, 0x00, 0x67, 0x66, 0x36, 0x1e, 0x36, 0x66, 0x67, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x46, 0x66, + 0x7f, + 0x00, 0x63, 0x77, 0x7f, 0x7f, 0x6b, 0x63, 0x63, 0x00, 0x63, 0x67, 0x6f, 0x7b, 0x73, 0x63, 0x63, 0x00, 0x1c, 0x36, 0x63, + 0x63, + 0x63, 0x36, 0x1c, 0x00, 0x3f, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00, 0x1e, 0x33, 0x33, 0x33, 0x3b, 0x1e, 0x38, 0x00, + 0x3f, + 0x66, 0x66, 0x3e, 0x36, 0x66, 0x67, 0x00, 0x1e, 0x33, 0x07, 0x0e, 0x38, 0x33, 0x1e, 0x00, 0x3f, 0x2d, 0x0c, 0x0c, 0x0c, + 0x0c, + 0x1e, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3f, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x0c, 0x00, 0x63, 0x63, + 0x63, + 0x6b, 0x7f, 0x77, 0x63, 0x00, 0x63, 0x63, 0x36, 0x1c, 0x1c, 0x36, 0x63, 0x00, 0x33, 0x33, 0x33, 0x1e, 0x0c, 0x0c, 0x1e, + 0x00, + 0x7f, 0x63, 0x31, 0x18, 0x4c, 0x66, 0x7f, 0x00, 0x1e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1e, 0x00, 0x03, 0x06, 0x0c, 0x18, + 0x30, + 0x60, 0x40, 0x00, 0x1e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1e, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x6e, 0x00, 0x07, 0x06, 0x06, 0x3e, + 0x66, 0x66, 0x3b, 0x00, 0x00, 0x00, 0x1e, 0x33, 0x03, 0x33, 0x1e, 0x00, 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6e, 0x00, + 0x00, + 0x00, 0x1e, 0x33, 0x3f, 0x03, 0x1e, 0x00, 0x1c, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, + 0x3e, + 0x30, 0x1f, 0x07, 0x06, 0x36, 0x6e, 0x66, 0x66, 0x67, 0x00, 0x0c, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x30, 0x00, + 0x30, 0x30, 0x30, 0x33, 0x33, 0x1e, 0x07, 0x06, 0x66, 0x36, 0x1e, 0x36, 0x67, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x1e, + 0x00, 0x00, 0x00, 0x33, 0x7f, 0x7f, 0x6b, 0x63, 0x00, 0x00, 0x00, 0x1f, 0x33, 0x33, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, + 0x33, + 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, 0x3e, 0x06, 0x0f, 0x00, 0x00, 0x6e, 0x33, 0x33, 0x3e, 0x30, 0x78, + 0x00, + 0x00, 0x3b, 0x6e, 0x66, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x1e, 0x30, 0x1f, 0x00, 0x08, 0x0c, 0x3e, 0x0c, 0x0c, + 0x2c, + 0x18, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x1e, 0x0c, 0x00, 0x00, 0x00, + 0x63, + 0x6b, 0x7f, 0x7f, 0x36, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x3e, 0x30, + 0x1f, + 0x00, 0x00, 0x3f, 0x19, 0x0c, 0x26, 0x3f, 0x00, 0x38, 0x0c, 0x0c, 0x07, 0x0c, 0x0c, 0x38, 0x00, 0x18, 0x18, 0x18, 0x00, + 0x18, + 0x18, 0x18, 0x00, 0x07, 0x0c, 0x0c, 0x38, 0x0c, 0x0c, 0x07, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, + 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x00, + 0x1e, 0x33, 0x03, 0x33, 0x1e, 0x18, 0x30, 0x1e, 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7e, 0x00, 0x38, 0x00, 0x1e, 0x33, + 0x3f, 0x03, 0x1e, 0x00, 0x7e, 0xc3, 0x3c, 0x60, 0x7c, 0x66, 0xfc, 0x00, 0x33, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x7e, 0x00, + 0x07, + 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x7e, 0x00, 0x0c, 0x0c, 0x1e, 0x30, 0x3e, 0x33, 0x7e, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x03, + 0x1e, + 0x30, 0x1c, 0x7e, 0xc3, 0x3c, 0x66, 0x7e, 0x06, 0x3c, 0x00, 0x33, 0x00, 0x1e, 0x33, 0x3f, 0x03, 0x1e, 0x00, 0x07, 0x00, + 0x1e, 0x33, 0x3f, 0x03, 0x1e, 0x00, 0x33, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x3e, 0x63, 0x1c, 0x18, 0x18, 0x18, + 0x3c, + 0x00, 0x07, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x63, 0x1c, 0x36, 0x63, 0x7f, 0x63, 0x63, 0x00, 0x0c, 0x0c, 0x00, + 0x1e, + 0x33, 0x3f, 0x33, 0x00, 0x38, 0x00, 0x3f, 0x06, 0x1e, 0x06, 0x3f, 0x00, 0x00, 0x00, 0xfe, 0x30, 0xfe, 0x33, 0xfe, 0x00, + 0x7c, + 0x36, 0x33, 0x7f, 0x33, 0x33, 0x73, 0x00, 0x1e, 0x33, 0x00, 0x1e, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x33, 0x00, 0x1e, 0x33, + 0x33, + 0x1e, 0x00, 0x00, 0x07, 0x00, 0x1e, 0x33, 0x33, 0x1e, 0x00, 0x1e, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7e, 0x00, 0x00, 0x07, + 0x00, + 0x33, 0x33, 0x33, 0x7e, 0x00, 0x00, 0x33, 0x00, 0x33, 0x33, 0x3e, 0x30, 0x1f, 0xc3, 0x18, 0x3c, 0x66, 0x66, 0x3c, 0x18, + 0x00, + 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x18, 0x18, 0x7e, 0x03, 0x03, 0x7e, 0x18, 0x18, 0x1c, 0x36, 0x26, 0x0f, + 0x06, + 0x67, 0x3f, 0x00, 0x33, 0x33, 0x1e, 0x3f, 0x0c, 0x3f, 0x0c, 0x0c, 0x1f, 0x33, 0x33, 0x5f, 0x63, 0xf3, 0x63, 0xe3, 0x70, + 0xd8, + 0x18, 0x3c, 0x18, 0x18, 0x1b, 0x0e, + 0x38, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x7e, 0x00, 0x1c, 0x00, 0x0e, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x38, 0x00, 0x1e, + 0x33, 0x33, 0x1e, 0x00, 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7e, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x33, 0x33, 0x33, 0x00, + 0x3f, + 0x00, 0x33, 0x37, 0x3f, 0x3b, 0x33, 0x00, 0x3c, 0x36, 0x36, 0x7c, 0x00, 0x7e, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, + 0x3e, + 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x06, 0x03, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x30, 0x30, 0x00, 0x00, 0xc3, 0x63, 0x33, 0x7b, 0xcc, 0x66, 0x33, 0xf0, 0xc3, 0x63, 0x33, 0xdb, 0xec, 0xf6, + 0xf3, + 0xc0, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0xcc, 0x66, 0x33, 0x66, 0xcc, 0x00, 0x00, 0x00, 0x33, 0x66, + 0xcc, + 0x66, 0x33, 0x00, 0x00, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, + 0xdb, + 0xee, 0xdb, 0x77, 0xdb, 0xee, 0xdb, 0x77, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x6c, 0x6c, 0x6c, 0x00, 0x00, + 0x00, + 0x00, 0x7f, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, + 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, + 0x7f, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x00, 0x00, 0x00, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x18, + 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, + 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, + 0x6c, + 0x6c, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0xff, + 0x00, + 0xff, 0x00, 0x00, 0x00, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, + 0x00, + 0x00, 0x00, 0x00, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, 0x00, 0x00, 0x00, 0x18, 0x18, 0xf8, 0x18, 0xf8, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, + 0x6c, + 0x6c, 0xff, 0x6c, 0x6c, 0x6c, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, + 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, + 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6e, 0x3b, 0x13, 0x3b, 0x6e, 0x00, 0x00, 0x1e, 0x33, 0x1f, 0x33, 0x1f, 0x03, 0x03, 0x00, 0x3f, 0x33, 0x03, + 0x03, 0x03, 0x03, 0x00, 0x00, 0x7f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x3f, 0x33, 0x06, 0x0c, 0x06, 0x33, 0x3f, 0x00, + 0x00, + 0x00, 0x7e, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x03, 0x00, 0x6e, 0x3b, 0x18, 0x18, + 0x18, + 0x18, 0x00, 0x3f, 0x0c, 0x1e, 0x33, 0x33, 0x1e, 0x0c, 0x3f, 0x1c, 0x36, 0x63, 0x7f, 0x63, 0x36, 0x1c, 0x00, 0x1c, 0x36, + 0x63, 0x63, 0x36, 0x36, 0x77, 0x00, 0x38, 0x0c, 0x18, 0x3e, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0x7e, + 0x00, + 0x00, 0x60, 0x30, 0x7e, 0xdb, 0xdb, 0x7e, 0x06, 0x03, 0x1c, 0x06, 0x03, 0x1f, 0x03, 0x06, 0x1c, 0x00, 0x1e, 0x33, 0x33, + 0x33, + 0x33, 0x33, 0x33, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x0c, 0x0c, 0x3f, 0x0c, 0x0c, 0x00, 0x3f, 0x00, + 0x06, + 0x0c, 0x18, 0x0c, 0x06, 0x00, 0x3f, 0x00, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x00, 0x3f, 0x00, 0x70, 0xd8, 0xd8, 0x18, 0x18, + 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x0e, 0x0c, 0x0c, 0x00, 0x3f, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x6e, + 0x3b, + 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xf0, 0x30, 0x30, 0x30, 0x37, 0x36, 0x3c, 0x38, 0x1e, 0x36, 0x36, 0x36, + 0x36, + 0x00, 0x00, 0x00, 0x0e, 0x18, 0x0c, 0x06, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void calcColors(const int colorNum, int & lo, int & hi, int & out) +{ + int redLo, redHi, greenLo, greenHi, blueLo, blueHi; + + if (colorNum == 0 || colorNum == 1 || colorNum == 2 || colorNum == 6) // white, red, yellow, or magenta + redLo = (0xf) << systemRedShift, redHi = (0x1f) << systemRedShift; + else + redLo = redHi = 0; + + if (colorNum == 0 || colorNum == 2 || colorNum == 3 || colorNum == 4) // white, yellow, green, or cyan + greenLo = (0xf) << systemGreenShift, greenHi = (0x1f) << systemGreenShift; + else + greenLo = greenHi = 0; + + if (colorNum == 0 || colorNum == 4 || colorNum == 5 || colorNum == 6) // white, cyan, blue, or magenta + blueLo = (0xf) << systemBlueShift, blueHi = (0x1f) << systemBlueShift; + else + blueLo = blueHi = 0; + + lo = redLo + greenLo + blueLo; + hi = redHi + greenHi + blueHi; + + if (colorNum == 7) // black + out = 0xffffffff; // white border + else + out = 0; // black border +} + +int lastColID = 0; +static void progressColorList(const char *& colorList, int & lo, int & hi, int & out) +{ + if (*colorList) + { + if (*colorList != lastColID) + { + calcColors((int)(*colorList)-1, lo, hi, out); + lastColID = *colorList; + } + colorList++; + } + else + { + colorList = NULL; + } +} + +static void drawTextInternal(u8 *screen, int pitch, int x, int y, + const char *string, bool trans, const char *colorList = NULL) +{ + if (colorList && !*colorList) + colorList = NULL; + + int loCol, hiCol, outCol; + calcColors(textColor, loCol, hiCol, outCol); + + lastColID = 0; + + const static int xd [8] = {-1, 0, 1, 1, 1, 0, -1, -1}; + const static int yd [8] = {-1, -1, -1, 0, 1, 1, 1, 0}; + + screen += y*pitch; + int inc = 2; + switch (systemColorDepth) + { + case 24: + inc = 3; + break; + case 32: + inc = 4; + break; + } + screen += x*inc; + + int xpos = x; + switch (systemColorDepth) + { + case 16: + { + while (*string) + { + char c = *string++; + u8 * scr = screen; + + if (colorList) + progressColorList(colorList, loCol, hiCol, outCol); + + u16 mask = u16(~RGB_LOW_BITS_MASK); + u16 *s = (u16 *)scr; + for (int h = 0-1; h < 8+1; h++) + { + for (int w = 0-1; w < 8+1; w++, s++) + { + int on = (h < 0 || w < 0 || h >= 8 || w >= 8) ? 0 : (fontdata2[(c<<3)+h]>>w)&1; + + int border = 0; + if (outlinedText) + for (int i = 0; i < 8; i++) + { + int h2 = h+yd[i], w2 = w+xd[i]; + border = (h2 < 0 || w2 < 0 || h2 >= 8 || w2 >= 8) ? 0 : (fontdata2[(c<<3)+h2]>>w2)&1; + if (border) + break; + } + + if (trans) + { + if (on) + *s = loCol + + ((*s & mask) >>1); + else if (border) + { + *s = outCol + + ((*s & mask) >>1); + } + } + else + { + if (on) + *s = hiCol; + else if (border) + *s = outCol; + } + } + scr += pitch; + s = (u16 *)scr; + } + screen += inc*8; + + xpos += 8; + if (xpos+8 > pitch>>1) // cut off text at right boundary + break; + } + break; + } + case 24: // TODO: verify this code's correctness + { + while (*string) + { + char c = *string++; + u8 * scr = screen; + + if (colorList) + progressColorList(colorList, loCol, hiCol, outCol); + + int h, w; + u8 *s = (u8 *)scr; + for (h = 0-1; h < 8+1; h++) + { + for (w = 0-1; w < 8+1; w++, s++) + { + int on = (h < 0 || w < 0 || h >= 8 || w >= 8) ? 0 : (fontdata2[(c<<3)+h]>>w)&1; + + int border = 0; + if (outlinedText) + for (int i = 0; i < 8; i++) + { + int h2 = h+yd[i], w2 = w+xd[i]; + border = (h2 < 0 || w2 < 0 || h2 >= 8 || w2 >= 8) ? 0 : (fontdata2[(c<<3)+h2]>>w2)&1; + if (border) + break; + } + + if (trans) + { + if (on) + { + u32 color = hiCol; + *s = ((color & 255)>>1)+(*s>>1); + *(s+1) = (((color >> 8) & 255)>>1)+(*(s+1)>>1); + *(s+2) = (((color >> 16) & 255)>>1)+(*(s+2)>>1); + } + else if (border) + { + u32 color = outCol; + *s = ((color & 255)>>1)+(*s>>1); + *(s+1) = (((color >> 8) & 255)>>1)+(*(s+1)>>1); + *(s+2) = (((color >> 16) & 255)>>1)+(*(s+2)>>1); + } + } + else + { + if (on) + { + u32 color = hiCol; + *s = (color & 255); + *(s+1) = (color >> 8) & 255; + *(s+2) = (color >> 16) & 255; + } + else if (border) + { + u32 color = outCol; + *s = (color & 255); + *(s+1) = (color >> 8) & 255; + *(s+2) = (color >> 16) & 255; + } + } + } + scr += pitch; + s = (u8 *)scr; + } + screen += inc*8; + + xpos += 8; + if (xpos+8 > pitch/3) // cut off text at right boundary + break; + } + break; + } + case 32: + { + while (*string) + { + char c = *string++; + u8 * scr = screen; + + if (colorList) + progressColorList(colorList, loCol, hiCol, outCol); + + int h, w; + u32 mask = 0xfefefe; + u32 *s = (u32 *)scr; + for (h = 0-1; h < 8+1; h++) + { + for (w = 0-1; w < 8+1; w++, s++) + { + int on = (h < 0 || w < 0 || h >= 8 || w >= 8) ? 0 : (fontdata2[(c<<3)+h]>>w)&1; + + int border = 0; + if (outlinedText) + for (int i = 0; i < 8; i++) + { + int h2 = h+yd[i], w2 = w+xd[i]; + border = (h2 < 0 || w2 < 0 || h2 >= 8 || w2 >= 8) ? 0 : (fontdata2[(c<<3)+h2]>>w2)&1; + if (border) + break; + } + + if (trans) + { + if (on) + *s = loCol + + ((*s & mask)>>1); + else if (border) + { + *s = outCol + + ((*s & mask)>>1); + } + } + else + { + if (on) + *s = hiCol; + else if (border) + *s = outCol; + } + } + scr += pitch; + s = (u32 *)scr; + } + screen += inc*8; + + xpos += 8; + if (xpos+8 > pitch>>2) // cut off text at right boundary + break; + } + break; + } + } +} + +void drawText(u8 *screen, int pitch, int x, int y, const char *string, const char *colorList) +{ + drawTextInternal(screen, pitch, x, y, string, transparentText, colorList); +} + +void drawTextTransp(u8 *screen, int pitch, int x, int y, const char *string, const char *colorList) +{ + drawTextInternal(screen, pitch, x, y, string, true, colorList); +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/Text.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/Text.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,14 @@ +#ifndef VBA_TEXT_H +#define VBA_TEXT_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +extern void drawText(u8 *, int, int, int, const char *, const char*cl = NULL); +extern void drawTextTransp(u8 *, int, int, int, const char *, const char*cl = NULL); + +extern bool outlinedText, transparentText; +extern int textColor, textMethod; + +#endif // VBA_TEXT_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/Util.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/Util.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1400 @@ +#include +#include +#include +#include + +extern "C" { +#include +} + +#if 0 +#include "unrarlib.h" +#endif + +#include "unzip.h" + +#include "../NLS.h" +#include "System.h" +#include "Util.h" +#include "../gba/Flash.h" +#include "../gba/RTC.h" + +extern "C" { +#include "memgzio.h" +} + +#ifndef _MSC_VER +#define _stricmp strcasecmp +#endif // ! _MSC_VER + +extern int32 cpuSaveType; + +extern int systemColorDepth; +extern int systemRedShift; +extern int systemGreenShift; +extern int systemBlueShift; + +extern u16 systemColorMap16[0x10000]; +extern u32 systemColorMap32[0x10000]; + +static int (ZEXPORT *utilGzWriteFunc)(gzFile, voidp, unsigned int) = NULL; +static int (ZEXPORT *utilGzReadFunc)(gzFile, voidp, unsigned int) = NULL; +static int (ZEXPORT *utilGzCloseFunc)(gzFile) = NULL; +static z_off_t (ZEXPORT *utilGzSeekFunc)(gzFile, z_off_t, int) = NULL; +static z_off_t (ZEXPORT *utilGzTellFunc)(gzFile) = NULL; + +//Kludge to get it to compile in Linux, GCC cannot convert +//gzwrite function pointer to the type of utilGzWriteFunc +//due to void* and const void* differences +//--Felipe +int gzWrite(gzFile file, void* buf, unsigned len){ + return gzwrite(file,buf,len); +} + +void utilPutDword(u8 *p, u32 value) +{ + *p++ = value & 255; + *p++ = (value >> 8) & 255; + *p++ = (value >> 16) & 255; + *p = (value >> 24) & 255; +} + +void utilPutWord(u8 *p, u16 value) +{ + *p++ = value & 255; + *p = (value >> 8) & 255; +} + +void utilWriteBMP(u8 *b, int w, int h, int dstDepth, u8 *pix) +{ + int sizeX = w; + int sizeY = h; + + switch (dstDepth > 0 ? dstDepth : systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix + (w + 2) * (h) * 2); // skip first black line + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + p -= 2 * (w + 2); + } + break; + } + case 24: + { + u8 *pixU8 = (u8 *)pix + 3 * w * (h - 1); + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + if (systemRedShift > systemBlueShift) + { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + else + { + int red = *pixU8++; + int green = *pixU8++; + int blue = *pixU8++; + + *b++ = blue; + *b++ = green; + *b++ = red; + } + } + pixU8 -= 2 * 3 * w; + } + break; + } + case 32: + { + u32 *pixU32 = (u32 *)(pix + 4 * (w + 1) * (h)); + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + pixU32++; + pixU32 -= 2 * (w + 1); + } + break; + } + } +} + +bool utilWriteBMPFile(const char *fileName, int w, int h, u8 *pix) +{ + u8 writeBuffer[256 * 3]; + + FILE *fp = fopen(fileName, "wb"); + + if (!fp) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + struct + { + u8 ident[2]; + u8 filesize[4]; + u8 reserved[4]; + u8 dataoffset[4]; + u8 headersize[4]; + u8 width[4]; + u8 height[4]; + u8 planes[2]; + u8 bitsperpixel[2]; + u8 compression[4]; + u8 datasize[4]; + u8 hres[4]; + u8 vres[4]; + u8 colors[4]; + u8 importantcolors[4]; + // u8 pad[2]; + } bmpheader; + memset(&bmpheader, 0, sizeof(bmpheader)); + + bmpheader.ident[0] = 'B'; + bmpheader.ident[1] = 'M'; + + u32 fsz = sizeof(bmpheader) + w * h * 3; + utilPutDword(bmpheader.filesize, fsz); + utilPutDword(bmpheader.dataoffset, 0x36); + utilPutDword(bmpheader.headersize, 0x28); + utilPutDword(bmpheader.width, w); + utilPutDword(bmpheader.height, h); + utilPutDword(bmpheader.planes, 1); + utilPutDword(bmpheader.bitsperpixel, 24); + utilPutDword(bmpheader.datasize, 3 * w * h); + + fwrite(&bmpheader, 1, sizeof(bmpheader), fp); + +#if 0 + // FIXME: need sufficient buffer + utilWriteBMP(writeBuffer, w, h, systemColorDepth, pix); +#else + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch (systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix + (w + 2) * (h) * 2); // skip first black line + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + p -= 2 * (w + 2); + fwrite(writeBuffer, 1, 3 * w, fp); + + b = writeBuffer; + } + break; + } + case 24: + { + u8 *pixU8 = (u8 *)pix + 3 * w * (h - 1); + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + if (systemRedShift > systemBlueShift) + { + *b++ = *pixU8++; // B + *b++ = *pixU8++; // G + *b++ = *pixU8++; // R + } + else + { + int red = *pixU8++; + int green = *pixU8++; + int blue = *pixU8++; + + *b++ = blue; + *b++ = green; + *b++ = red; + } + } + pixU8 -= 2 * 3 * w; + fwrite(writeBuffer, 1, 3 * w, fp); + + b = writeBuffer; + } + break; + } + case 32: + { + u32 *pixU32 = (u32 *)(pix + 4 * (w + 1) * (h)); + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + } + pixU32++; + pixU32 -= 2 * (w + 1); + + fwrite(writeBuffer, 1, 3 * w, fp); + + b = writeBuffer; + } + break; + } + } +#endif + + fclose(fp); + + return true; +} + +bool utilWritePNGFile(const char *fileName, int w, int h, u8 *pix) +{ + u8 writeBuffer[256 * 3]; + + FILE *fp = fopen(fileName, "wb"); + + if (!fp) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), fileName); + return false; + } + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, + NULL, + NULL); + if (!png_ptr) + { + fclose(fp); + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, NULL); + fclose(fp); + return false; + } + + if (setjmp(png_ptr->jmpbuf)) + { + png_destroy_write_struct(&png_ptr, NULL); + fclose(fp); + return false; + } + + png_init_io(png_ptr, fp); + + png_set_IHDR(png_ptr, + info_ptr, + w, + h, + 8, + PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_write_info(png_ptr, info_ptr); + + u8 *b = writeBuffer; + + int sizeX = w; + int sizeY = h; + + switch (systemColorDepth) + { + case 16: + { + u16 *p = (u16 *)(pix + (w + 2) * 2); // skip first black line + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u16 v = *p++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x01f) << 3; // B + } + p++; // skip black pixel for filters + p++; // skip black pixel for filters + png_write_row(png_ptr, writeBuffer); + + b = writeBuffer; + } + break; + } + case 24: + { + u8 *pixU8 = (u8 *)pix; + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + if (systemRedShift < systemBlueShift) + { + *b++ = *pixU8++; // R + *b++ = *pixU8++; // G + *b++ = *pixU8++; // B + } + else + { + int blue = *pixU8++; + int green = *pixU8++; + int red = *pixU8++; + + *b++ = red; + *b++ = green; + *b++ = blue; + } + } + png_write_row(png_ptr, writeBuffer); + + b = writeBuffer; + } + break; + } + case 32: + { + u32 *pixU32 = (u32 *)(pix + 4 * (w + 1)); + for (int y = 0; y < sizeY; y++) + { + for (int x = 0; x < sizeX; x++) + { + u32 v = *pixU32++; + + *b++ = ((v >> systemRedShift) & 0x001f) << 3; // R + *b++ = ((v >> systemGreenShift) & 0x001f) << 3; // G + *b++ = ((v >> systemBlueShift) & 0x001f) << 3; // B + } + pixU32++; + + png_write_row(png_ptr, writeBuffer); + + b = writeBuffer; + } + break; + } + } + + png_write_end(png_ptr, info_ptr); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + + return true; +} + +static int utilReadInt2(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if (c == EOF) + return -1; + res = c; + c = fgetc(f); + if (c == EOF) + return -1; + return c + (res << 8); +} + +static int utilReadInt3(FILE *f) +{ + int res = 0; + int c = fgetc(f); + if (c == EOF) + return -1; + res = c; + c = fgetc(f); + if (c == EOF) + return -1; + res = c + (res << 8); + c = fgetc(f); + if (c == EOF) + return -1; + return c + (res << 8); +} + +void utilApplyIPS(const char *ips, u8 * *r, int *s) +{ + // from the IPS spec at http://zerosoft.zophar.net/ips.htm + FILE *f = fopen(ips, "rb"); + if (!f) + return; + u8 *rom = *r; + int size = *s; + if (fgetc(f) == 'P' && + fgetc(f) == 'A' && + fgetc(f) == 'T' && + fgetc(f) == 'C' && + fgetc(f) == 'H') + { + int b; + int offset; + int len; + for (;; ) + { + // read offset + offset = utilReadInt3(f); + // if offset == EOF, end of patch + if (offset == 0x454f46) + break; + // read length + len = utilReadInt2(f); + if (!len) + { + // len == 0, RLE block + len = utilReadInt2(f); + // byte to fill + int c = fgetc(f); + if (c == -1) + break; + b = (u8)c; + } + else + b = -1; + // check if we need to reallocate our ROM + if ((offset + len) >= size) + { + size *= 2; + rom = (u8 *)realloc(rom, size); + *r = rom; + *s = size; + } + if (b == -1) + { + // normal block, just read the data + if (fread(&rom[offset], 1, len, f) != (size_t)len) + break; + } + else + { + // fill the region with the given byte + while (len--) + { + rom[offset++] = b; + } + } + } + } + // close the file + fclose(f); +} + +extern bool8 cpuIsMultiBoot; + +bool utilIsGBAImage(const char *file) +{ + cpuIsMultiBoot = false; + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".gba") == 0) + return true; + if (_stricmp(p, ".agb") == 0) + return true; + if (_stricmp(p, ".bin") == 0) + return true; + if (_stricmp(p, ".elf") == 0) + return true; + if (_stricmp(p, ".mb") == 0) + { + cpuIsMultiBoot = true; + return true; + } + } + } + + return false; +} + +bool utilIsGBImage(const char *file) +{ + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".gb") == 0) + return true; + if (_stricmp(p, ".gbc") == 0) + return true; + if (_stricmp(p, ".cgb") == 0) + return true; + if (_stricmp(p, ".sgb") == 0) + return true; + } + } + + return false; +} + +bool utilIsGBABios(const char *file) +{ + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".gba") == 0) + return true; + if (_stricmp(p, ".agb") == 0) + return true; + if (_stricmp(p, ".bin") == 0) + return true; + if (_stricmp(p, ".bios") == 0) + return true; + if (_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +bool utilIsGBBios(const char *file) +{ + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".gb") == 0) + return true; + if (_stricmp(p, ".bin") == 0) + return true; + if (_stricmp(p, ".bios") == 0) + return true; + if (_stricmp(p, ".rom") == 0) + return true; + } + } + + return false; +} + +bool utilIsELF(const char *file) +{ + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".elf") == 0) + return true; + } + } + return false; +} + +bool utilIsZipFile(const char *file) +{ + if (strlen(file) > 4) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".zip") == 0) + return true; + } + } + + return false; +} + +#if 0 +bool utilIsRarFile(const char *file) +{ + if (strlen(file) > 4) + { + char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".rar") == 0) + return true; + } + } + + return false; +} + +#endif + +bool utilIsGzipFile(const char *file) +{ + if (strlen(file) > 3) + { + const char *p = strrchr(file, '.'); + + if (p != NULL) + { + if (_stricmp(p, ".gz") == 0) + return true; + if (_stricmp(p, ".z") == 0) + return true; + } + } + + return false; +} + +void utilGetBaseName(const char *file, char *buffer) +{ + strcpy(buffer, file); + + if (utilIsGzipFile(file)) + { + char *p = strrchr(buffer, '.'); + + if (p) + *p = 0; + } +} + +IMAGE_TYPE utilFindType(const char *file) +{ + char buffer[2048]; + + if (utilIsZipFile(file)) + { + unzFile unz = unzOpen(file); + + if (unz == NULL) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), file); + return IMAGE_UNKNOWN; + } + + int r = unzGoToFirstFile(unz); + + if (r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return IMAGE_UNKNOWN; + } + + IMAGE_TYPE found = IMAGE_UNKNOWN; + + unz_file_info info; + + while (true) + { + r = unzGetCurrentFileInfo(unz, + &info, + buffer, + sizeof(buffer), + NULL, + 0, + NULL, + 0); + + if (r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return IMAGE_UNKNOWN; + } + + if (utilIsGBAImage(buffer)) + { + found = IMAGE_GBA; + break; + } + + if (utilIsGBImage(buffer)) + { + found = IMAGE_GB; + break; + } + + r = unzGoToNextFile(unz); + + if (r != UNZ_OK) + break; + } + unzClose(unz); + + if (found == IMAGE_UNKNOWN) + { + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on ZIP file %s"), file); + return found; + } + return found; +#if 0 + } + else if (utilIsRarFile(file)) + { + IMAGE_TYPE found = IMAGE_UNKNOWN; + + ArchiveList_struct *rarList = NULL; + if (urarlib_list((void *)file, (ArchiveList_struct *)&rarList)) + { + ArchiveList_struct *p = rarList; + + while (p) + { + if (utilIsGBAImage(p->item.Name)) + { + found = IMAGE_GBA; + break; + } + + if (utilIsGBImage(p->item.Name)) + { + found = IMAGE_GB; + break; + } + p = p->next; + } + + urarlib_freelist(rarList); + } + return found; +#endif + } + else + { + if (utilIsGzipFile(file)) + utilGetBaseName(file, buffer); + else + strcpy(buffer, file); + + if (utilIsGBAImage(buffer)) + return IMAGE_GBA; + if (utilIsGBImage(buffer)) + return IMAGE_GB; + } + return IMAGE_UNKNOWN; +} + +static int utilGetSize(int size) +{ + int res = 1; + while (res < size) + res <<= 1; + return res; +} + +static u8 *utilLoadFromZip(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + char buffer[2048]; + + unzFile unz = unzOpen(file); + + if (unz == NULL) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), file); + return NULL; + } + int r = unzGoToFirstFile(unz); + + if (r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return NULL; + } + + bool found = false; + + unz_file_info info; + + while (true) + { + r = unzGetCurrentFileInfo(unz, + &info, + buffer, + sizeof(buffer), + NULL, + 0, + NULL, + 0); + + if (r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_BAD_ZIP_FILE, N_("Bad ZIP file %s"), file); + return NULL; + } + + if (accept(buffer)) + { + found = true; + break; + } + + r = unzGoToNextFile(unz); + + if (r != UNZ_OK) + break; + } + + if (!found) + { + unzClose(unz); + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on ZIP file %s"), file); + return NULL; + } + + int fileSize = info.uncompressed_size; + if (size == 0) + size = fileSize; + r = unzOpenCurrentFile(unz); + + if (r != UNZ_OK) + { + unzClose(unz); + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), buffer); + return NULL; + } + + u8 *image = data; + + if (image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if (image == NULL) + { + unzCloseCurrentFile(unz); + unzClose(unz); + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + r = unzReadCurrentFile(unz, + image, + read); + + unzCloseCurrentFile(unz); + unzClose(unz); + + if (r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), buffer); + if (data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +static u8 *utilLoadGzipFile(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + FILE *f = fopen(file, "rb"); + + if (f == NULL) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + fseek(f, -4, SEEK_END); + int fileSize = fgetc(f) | (fgetc(f) << 8) | (fgetc(f) << 16) | (fgetc(f) << 24); + fclose(f); + if (size == 0) + size = fileSize; + + gzFile gz = gzopen(file, "rb"); + + if (gz == NULL) + { + // should not happen, but who knows? + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + u8 *image = data; + + if (image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if (image == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + fclose(f); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + int r = gzread(gz, image, read); + gzclose(gz); + + if (r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), file); + if (data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +#if 0 +static u8 *utilLoadRarFile(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + char buffer[2048]; + + ArchiveList_struct *rarList = NULL; + if (urarlib_list((void *)file, (ArchiveList_struct *)&rarList)) + { + ArchiveList_struct *p = rarList; + + bool found = false; + while (p) + { + if (accept(p->item.Name)) + { + strcpy(buffer, p->item.Name); + found = true; + break; + } + p = p->next; + } + if (found) + { + void *memory = NULL; + unsigned long lsize = 0; + size = p->item.UnpSize; + int r = urarlib_get((void *)&memory, &lsize, buffer, (void *)file, ""); + if (!r) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), buffer); + urarlib_freelist(rarList); + return NULL; + } + u8 *image = (u8 *)memory; + if (data != NULL) + { + memcpy(image, data, size); + } + urarlib_freelist(rarList); + return image; + } + systemMessage(MSG_NO_IMAGE_ON_ZIP, + N_("No image found on RAR file %s"), file); + urarlib_freelist(rarList); + return NULL; + } + // nothing found + return NULL; +} + +#endif + +// the caller is responsible for caling free(return value) to release the memory +u8 *utilLoad(const char *file, + bool (*accept)(const char *), + u8 *data, + int &size) +{ + if (utilIsZipFile(file)) + { + return utilLoadFromZip(file, accept, data, size); + } + if (utilIsGzipFile(file)) + { + return utilLoadGzipFile(file, accept, data, size); + } +#if 0 + if (utilIsRarFile(file)) + { + return utilLoadRarFile(file, accept, data, size); + } +#endif + + u8 *image = data; + + FILE *f = fopen(file, "rb"); + + if (!f) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), file); + return NULL; + } + + fseek(f, 0, SEEK_END); + int fileSize = ftell(f); + fseek(f, 0, SEEK_SET); + if (size == 0) + size = fileSize; + + if (image == NULL) + { + image = (u8 *)malloc(utilGetSize(size)); + if (image == NULL) + { + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), + "data"); + fclose(f); + return NULL; + } + size = fileSize; + } + int read = fileSize <= size ? fileSize : size; + int r = fread(image, 1, read, f); + fclose(f); + + if (r != (int)read) + { + systemMessage(MSG_ERROR_READING_IMAGE, + N_("Error reading image %s"), file); + if (data == NULL) + free(image); + return NULL; + } + + size = fileSize; + + return image; +} + +void utilWriteInt(gzFile gzFile, int32 i) +{ + utilGzWrite(gzFile, &i, sizeof(int32)); +} + +int32 utilReadInt(gzFile gzFile) +{ + int32 i = 0; + utilGzRead(gzFile, &i, sizeof(int32)); + return i; +} + +void utilReadData(gzFile gzFile, variable_desc *data) +{ + while (data->address) + { + utilGzRead(gzFile, data->address, data->size); + data++; + } +} + +void utilWriteData(gzFile gzFile, variable_desc *data) +{ + while (data->address) + { + utilGzWrite(gzFile, data->address, data->size); + data++; + } +} + +gzFile utilGzOpen(const char *file, const char *mode) +{ + utilGzWriteFunc = gzWrite; + utilGzReadFunc = gzread; + utilGzCloseFunc = gzclose; + utilGzSeekFunc = gzseek; + utilGzTellFunc = gztell; + + return gzopen(file, mode); +} + +gzFile utilGzReopen(int id, const char *mode) +{ + utilGzWriteFunc = gzWrite; + utilGzReadFunc = gzread; + utilGzCloseFunc = gzclose; + utilGzSeekFunc = gzseek; + utilGzTellFunc = gztell; + + return gzdopen(id, mode); +} + +gzFile utilMemGzOpen(char *memory, int available, char *mode) +{ + utilGzWriteFunc = memgzwrite; + utilGzReadFunc = memgzread; + utilGzCloseFunc = memgzclose; + utilGzSeekFunc = NULL; // FIXME: not implemented... + utilGzTellFunc = memtell; + + return memgzopen(memory, available, mode); +} + +int utilGzWrite(gzFile file, voidp buffer, unsigned int len) +{ + return utilGzWriteFunc(file, buffer, len); +} + +int utilGzRead(gzFile file, voidp buffer, unsigned int len) +{ + return utilGzReadFunc(file, buffer, len); +} + +int utilGzClose(gzFile file) +{ + return utilGzCloseFunc(file); +} + +z_off_t utilGzSeek(gzFile file, z_off_t offset, int whence) +{ + return utilGzSeekFunc(file, offset, whence); +} + +z_off_t utilGzTell(gzFile file) +{ + return utilGzTellFunc(file); +} + +void utilGBAFindSave(const u8 *data, const int size) +{ + u32 *p = (u32 *)data; + u32 *end = (u32 *)(data + size); + int saveType = 0; + int flashSize = 0x10000; + bool rtcFound = false; + + while (p < end) + { + u32 d = READ32LE(p); + + if (d == 0x52504545) + { + if (memcmp(p, "EEPROM_", 7) == 0) + { + if (saveType == 0) + saveType = 1; + } + } + else if (d == 0x4D415253) + { + if (memcmp(p, "SRAM_", 5) == 0) + { + if (saveType == 0) + saveType = 2; + } + } + else if (d == 0x53414C46) + { + if (memcmp(p, "FLASH1M_", 8) == 0) + { + if (saveType == 0) + { + saveType = 3; + flashSize = 0x20000; + } + } + else if (memcmp(p, "FLASH", 5) == 0) + { + if (saveType == 0) + { + saveType = 3; + flashSize = 0x10000; + } + } + } + else if (d == 0x52494953) + { + if (memcmp(p, "SIIRTC_V", 8) == 0) + rtcFound = true; + } + p++; + } + // if no matches found, then set it to NONE + if (saveType == 0) + { + saveType = 5; + } + rtcEnable(rtcFound); + cpuSaveType = saveType; + flashSetSize(flashSize); +} + +void utilUpdateSystemColorMaps() +{ + switch (systemColorDepth) + { + case 16: + { + for (int i = 0; i < 0x10000; i++) + { + systemColorMap16[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + break; + } + case 24: + case 32: + { + for (int i = 0; i < 0x10000; i++) + { + systemColorMap32[i] = ((i & 0x1f) << systemRedShift) | + (((i & 0x3e0) >> 5) << systemGreenShift) | + (((i & 0x7c00) >> 10) << systemBlueShift); + } + break; + } + } +} + +//// BIOS stuff +// systemType uses the same enum values as gbEmulatorType does + +bool utilLoadBIOS(u8 *bios, const char *biosFileName, int systemType) +{ + if (bios == NULL || strlen(biosFileName) == 0) + return false; + + if (systemType == 4) + { + int biosSize = 0x4000; + if (utilLoad(biosFileName, utilIsGBABios, bios, biosSize)) + { + if (biosSize == 0x4000) + return true; + } + } + else + { + int biosSize = 0x100; + if (utilLoad(biosFileName, utilIsGBBios, bios, biosSize)) + { + if (biosSize == 0x100) + return true; + } + } + + return false; +} + +bool utilCheckBIOS(const char *biosFileName, int systemType) +{ + if (strlen(biosFileName) == 0) + return false; + + u8 * tempBIOS = (u8 *)malloc(systemType == 4 ? 0x4000 : 0x100); + bool result = utilLoadBIOS(tempBIOS, biosFileName, systemType); + free(tempBIOS); + + return result; +} + +#if 0 +// returns the checksum of the BIOS that will be loaded after the next restart +u16 utilCalcBIOSChecksum(const u8 *bios, int systemType) +{ + u32 biosChecksum = 0; + if (bios) + { + int biosSize = (systemType == 4 ? 0x4000 : 0x100); + const u16 *data = reinterpret_cast(bios); + for (int i = biosSize; i > 0; i -= 2) + biosChecksum += *data++; + } + + while ((biosChecksum >> 16) & 0xFFFF) + biosChecksum = (biosChecksum &0xFFFF) + ((biosChecksum >> 16) & 0xFFFF); + + return biosChecksum & 0xFFFF; +} +#else +// returns the checksum of the BIOS that will be loaded after the next restart +u16 utilCalcBIOSChecksum(const u8 *bios, int systemType) +{ + u32 biosChecksum = 0; + if (bios) + { + int biosSize = (systemType == 4 ? 0x4000 : 0x100); + const u32 *data = reinterpret_cast(bios); + for (int i = biosSize; i > 0; i -= 4) + biosChecksum += *data++; + } + + return biosChecksum & 0xFFFF; +} +#endif + +// returns the checksum of the BIOS file +u16 utilCalcBIOSFileChecksum(const char *biosFileName, int systemType) +{ + if (strlen(biosFileName) == 0) + return 0; + + u16 biosChecksum = 0; + const int biosSize = (systemType == 4 ? 0x4000 : 0x100); + u8 * tempBIOS = (u8 *)malloc(biosSize); + bool hasBIOS = utilLoadBIOS(tempBIOS, biosFileName, systemType); + if (hasBIOS) + { + biosChecksum = utilCalcBIOSChecksum(tempBIOS, systemType); + } + free(tempBIOS); + + return biosChecksum; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/Util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/Util.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,64 @@ +#ifndef VBA_UTIL_H +#define VBA_UTIL_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +enum IMAGE_TYPE +{ + IMAGE_UNKNOWN = -1, + IMAGE_GBA = 0, + IMAGE_GB = 1 +}; + +// save game + +typedef struct +{ + void *address; + int size; +} variable_desc; + +extern void utilWriteBMP(u8 *out, int w, int h, int dstDepth, u8 *in); +extern bool utilWriteBMPFile(const char *, int, int, u8 *); +extern bool utilWritePNGFile(const char *, int, int, u8 *); +extern void utilApplyIPS(const char *ips, u8 * *rom, int *size); +extern bool utilIsGBAImage(const char *); +extern bool utilIsGBABios(const char *file); +extern bool utilIsELF(const char *file); +extern bool utilIsGBImage(const char *); +extern bool utilIsGBBios(const char *file); +extern bool utilIsZipFile(const char *); +extern bool utilIsGzipFile(const char *); +extern bool utilIsRarFile(const char *); +extern void utilGetBaseName(const char *, char *); +extern IMAGE_TYPE utilFindType(const char *); +extern u8 * utilLoad(const char *, bool (*)(const char *), u8 *, int &); +extern void utilPutDword(u8 *, u32); +extern void utilPutWord(u8 *, u16); +extern void utilWriteData(gzFile, variable_desc *); +extern void utilReadData(gzFile, variable_desc *); +extern int32 utilReadInt(gzFile); +extern void utilWriteInt(gzFile, int32); +extern gzFile utilGzOpen(const char *file, const char *mode); +extern gzFile utilGzReopen(int id, const char *mode); +extern gzFile utilMemGzOpen(char *memory, int available, char *mode); +extern int utilGzWrite(gzFile file, voidp buffer, unsigned int len); +extern int utilGzRead(gzFile file, voidp buffer, unsigned int len); +extern int utilGzClose(gzFile file); +extern z_off_t utilGzSeek(gzFile file, z_off_t offset, int whence); +extern z_off_t utilGzTell(gzFile file); +extern void utilGBAFindSave(const u8 *, const int); +extern void utilUpdateSystemColorMaps(); +extern bool utilLoadBIOS(u8 *bios, const char *biosFileName, int systemType); +extern bool utilCheckBIOS(const char *biosFileName, int systemType); +extern u16 utilCalcBIOSChecksum(const u8 *bios, int systemType); +extern u16 utilCalcBIOSFileChecksum(const char *biosFileName, int systemType); + +extern int gzWrite(gzFile file, void* buf, unsigned len); + +#endif // VBA_UTIL_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/inputGlobal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/inputGlobal.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,48 @@ +#ifndef VBA_INPUT_GLOBAL_H +#define VBA_INPUT_GLOBAL_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +enum +{ + KEY_BUTTON_A, KEY_BUTTON_B, + KEY_BUTTON_SELECT, KEY_BUTTON_START, + KEY_RIGHT, KEY_LEFT, + KEY_UP, KEY_DOWN, + KEY_BUTTON_R, KEY_BUTTON_L, + KEY_BUTTON_SPEED, KEY_BUTTON_CAPTURE, + KEY_BUTTON_GS +}; + +#define BUTTON_MASK_A (0x0001) +#define BUTTON_MASK_B (0x0002) +#define BUTTON_MASK_SELECT (0x0004) +#define BUTTON_MASK_START (0x0008) +#define BUTTON_MASK_RIGHT (0x0010) +#define BUTTON_MASK_LEFT (0x0020) +#define BUTTON_MASK_UP (0x0040) +#define BUTTON_MASK_DOWN (0x0080) +#define BUTTON_MASK_R (0x0100) +#define BUTTON_MASK_L (0x0200) +#define BUTTON_GB_MASK (BUTTON_MASK_A|BUTTON_MASK_B|BUTTON_MASK_SELECT|BUTTON_MASK_START| \ + BUTTON_MASK_RIGHT|BUTTON_MASK_LEFT|BUTTON_MASK_UP|BUTTON_MASK_DOWN) +#define BUTTON_GBA_ONLY (BUTTON_MASK_R|BUTTON_MASK_L) +#define BUTTON_REGULAR_MASK (BUTTON_GB_MASK|BUTTON_GBA_ONLY) +#define BUTTON_MASK_OLD_RESET (0x0400) +#define BUTTON_MASK_NEW_RESET (0x0800) +#define BUTTON_MASK_LEFT_MOTION (0x1000) +#define BUTTON_MASK_RIGHT_MOTION (0x2000) +#define BUTTON_MASK_DOWN_MOTION (0x4000) +#define BUTTON_MASK_UP_MOTION (0x8000) +#define BUTTON_MOTION_MASK (BUTTON_MASK_LEFT_MOTION|BUTTON_MASK_RIGHT_MOTION|BUTTON_MASK_DOWN_MOTION| \ + BUTTON_MASK_UP_MOTION) +#define BUTTON_RECORDINGONLY_MASK (BUTTON_MASK_OLD_RESET|BUTTON_MASK_NEW_RESET|BUTTON_MOTION_MASK) +#define BUTTON_REGULAR_RECORDING_MASK (BUTTON_REGULAR_MASK|BUTTON_RECORDINGONLY_MASK) +#define BUTTON_MASK_SPEED (0x040000) +#define BUTTON_MASK_CAPTURE (0x080000) +#define BUTTON_MASK_GAMESHARK (0x100000) +#define BUTTON_NONRECORDINGONLY_MASK (BUTTON_MASK_SPEED|BUTTON_MASK_CAPTURE|BUTTON_MASK_GAMESHARK) + +#endif // VBA_INPUT_GLOBAL_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/lua-engine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/lua-engine.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,5102 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; + +#ifdef __linux + #include // for unlink + #include + #include +#endif +#if (defined(WIN32) && !defined(SDL)) + #include + #include "../win32/stdafx.h" + #include "../win32/Input.h" + #include "../win32/MainWnd.h" + #include "../win32/VBA.h" + #include "../win32/LuaOpenDialog.h" +#else + #define stricmp strcasecmp + #define strnicmp strncasecmp +#endif + +#include "../Port.h" +#include "System.h" +#include "movie.h" +#include "../gba/GBA.h" +#include "../gba/GBAGlobals.h" +#include "../gb/GB.h" +#include "../gb/gbGlobals.h" +#include "../gba/GBASound.h" + +#ifdef _WIN32 +#include "../win32/Sound.h" +//#include "../win32/WinMiscUtil.h" +extern CString winGetSavestateFilename(const CString &LogicalRomName, int nID); +#else +#endif + +extern "C" +{ +#include "../lua/src/lua.h" +#include "../lua/src/lauxlib.h" +#include "../lua/src/lualib.h" +#include "../lua/src/lstate.h" +} +#include "vbalua.h" + +#include "../SFMT/SFMT.c" + +static void (*info_print)(int uid, const char *str); +static void (*info_onstart)(int uid); +static void (*info_onstop)(int uid); +static int info_uid; + +#ifndef countof + #define countof(a) (sizeof(a) / sizeof(a[0])) +#endif + +static lua_State *LUA; + +// Are we running any code right now? +static char *luaScriptName = NULL; + +// Are we running any code right now? +static bool8 luaRunning = false; + +// True at the frame boundary, false otherwise. +static bool8 frameBoundary = false; + +// The execution speed we're running at. +static enum { SPEED_NORMAL, SPEED_NOTHROTTLE, SPEED_TURBO, SPEED_MAXIMUM } speedmode = SPEED_NORMAL; + +// Rerecord count skip mode +static bool8 skipRerecords = false; + +// Used by the registry to find our functions +static const char *frameAdvanceThread = "VBA.FrameAdvance"; +static const char *guiCallbackTable = "VBA.GUI"; + +// True if there's a thread waiting to run after a run of frame-advance. +static bool8 frameAdvanceWaiting = false; + +// We save our pause status in the case of a natural death. +//static bool8 wasPaused = false; + +// Transparency strength. 255=opaque, 0=so transparent it's invisible +static int transparencyModifier = 255; + +// Our joypads. +static uint32 lua_joypads[4]; +static uint8 lua_joypads_used = 0; + +static bool8 gui_used = false; +static uint8 *gui_data = NULL; // BGRA + +// Protects Lua calls from going nuts. +// We set this to a big number like 1000 and decrement it +// over time. The script gets knifed once this reaches zero. +static int numTries; + +// number of registered memory functions (1 per hooked byte) +static unsigned int numMemHooks; + +// Look in inputglobal.h for macros named like BUTTON_MASK_UP to determine the order. +static const char *button_mappings[] = { + "A", "B", "select", "start", "right", "left", "up", "down", "R", "L" +}; + +#ifdef _MSC_VER + #define snprintf _snprintf + #define vscprintf _vscprintf +#else + #define stricmp strcasecmp + #define strnicmp strncasecmp + #define __forceinline __attribute__((always_inline)) +#endif + +static const char *luaCallIDStrings[] = +{ + "CALL_BEFOREEMULATION", + "CALL_AFTEREMULATION", + "CALL_BEFOREEXIT" +}; + +//make sure we have the right number of strings +CTASSERT(sizeof(luaCallIDStrings) / sizeof(*luaCallIDStrings) == LUACALL_COUNT) + +static const char *luaMemHookTypeStrings [] = +{ + "MEMHOOK_WRITE", + "MEMHOOK_READ", + "MEMHOOK_EXEC", + + "MEMHOOK_WRITE_SUB", + "MEMHOOK_READ_SUB", + "MEMHOOK_EXEC_SUB", +}; + +//make sure we have the right number of strings +CTASSERT(sizeof(luaMemHookTypeStrings) / sizeof(*luaMemHookTypeStrings) == LUAMEMHOOK_COUNT) + +static char *rawToCString(lua_State * L, int idx = 0); +static const char *toCString(lua_State *L, int idx = 0); + +// GBA memory I/O functions copied from win32/MemoryViewerDlg.cpp +static inline u8 CPUReadByteQuick(u32 addr) +{ + return ::map[addr >> 24].address[addr & ::map[addr >> 24].mask]; +} + +static inline void CPUWriteByteQuick(u32 addr, u8 b) +{ + ::map[addr >> 24].address[addr & ::map[addr >> 24].mask] = b; +} + +static inline u16 CPUReadHalfWordQuick(u32 addr) +{ + return *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); +} + +static inline void CPUWriteHalfWordQuick(u32 addr, u16 b) +{ + *((u16 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; +} + +static inline u32 CPUReadMemoryQuick(u32 addr) +{ + return *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]); +} + +static inline void CPUWriteMemoryQuick(u32 addr, u32 b) +{ + *((u32 *) &::map[addr >> 24].address[addr & ::map[addr >> 24].mask]) = b; +} + +// GB +static inline u8 gbReadMemoryQuick8(u16 addr) +{ + return gbReadMemoryQuick(addr); +} + +static inline void gbWriteMemoryQuick8(u16 addr, u8 b) +{ + gbWriteMemoryQuick(addr, b); +} + +static inline u16 gbReadMemoryQuick16(u16 addr) +{ + return (gbReadMemoryQuick(addr + 1) << 8) | gbReadMemoryQuick(addr); +} + +static inline void gbWriteMemoryQuick16(u16 addr, u16 b) +{ + gbWriteMemoryQuick(addr, b & 0xff); + gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); +} + +static inline u32 gbReadMemoryQuick32(u16 addr) +{ + return (gbReadMemoryQuick(addr + 3) << 24) | + (gbReadMemoryQuick(addr + 2) << 16) | + (gbReadMemoryQuick(addr + 1) << 8) | + gbReadMemoryQuick(addr); +} + +static inline void gbWriteMemoryQuick32(u16 addr, u32 b) +{ + gbWriteMemoryQuick(addr, b & 0xff); + gbWriteMemoryQuick(addr + 1, (b >> 8) & 0xff); + gbWriteMemoryQuick(addr + 2, (b >> 16) & 0xff); + gbWriteMemoryQuick(addr + 1, (b >> 24) & 0xff); +} + +static inline u8 gbReadROMQuick8(u32 addr) +{ + return gbReadROMQuick(addr & gbRomSizeMask); +} + +static inline u8 gbReadROMQuick16(u32 addr) +{ + return (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | gbReadROMQuick(addr & gbRomSizeMask); +} + +static inline u8 gbReadROMQuick32(u32 addr) +{ + return (gbReadROMQuick(addr+3 & gbRomSizeMask) << 24) | + (gbReadROMQuick(addr+2 & gbRomSizeMask) << 16) | + (gbReadROMQuick(addr+1 & gbRomSizeMask) << 8) | + gbReadROMQuick(addr & gbRomSizeMask); +} + +typedef void (*GetColorFunc)(const uint8 *, uint8 *, uint8 *, uint8 *); +typedef void (*SetColorFunc)(uint8 *, uint8, uint8, uint8); + +static void getColor16(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) +{ + u16 v = *(const uint16 *)s; + *r = ((v >> systemBlueShift) & 0x001f) << 3; + *g = ((v >> systemGreenShift) & 0x001f) << 3; + *b = ((v >> systemRedShift) & 0x001f) << 3; +} + +static void getColor24(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) +{ + if (systemRedShift > systemBlueShift) + *b = s[0], *g = s[1], *r = s[2]; + else + *r = s[0], *g = s[1], *b = s[2]; +} + +static void getColor32(const uint8 *s, uint8 *r, uint8 *g, uint8 *b) +{ + u32 v = *(const uint32 *)s; + *b = ((v >> systemBlueShift) & 0x001f) << 3; + *g = ((v >> systemGreenShift) & 0x001f) << 3; + *r = ((v >> systemRedShift) & 0x001f) << 3; +} + +static void setColor16(uint8 *s, uint8 r, uint8 g, uint8 b) +{ + *(uint16 *)s = ((b >> 3) & 0x01f) << + systemBlueShift | + ((g >> 3) & 0x01f) << + systemGreenShift | + ((r >> 3) & 0x01f) << + systemRedShift; +} + +static void setColor24(uint8 *s, uint8 r, uint8 g, uint8 b) +{ + if (systemRedShift > systemBlueShift) + s[0] = b, s[1] = g, s[2] = r; + else + s[0] = r, s[1] = g, s[2] = b; +} + +static void setColor32(uint8 *s, uint8 r, uint8 g, uint8 b) +{ + *(uint32 *)s = ((b >> 3) & 0x01f) << + systemBlueShift | + ((g >> 3) & 0x01f) << + systemGreenShift | + ((r >> 3) & 0x01f) << + systemRedShift; +} + +static bool getColorIOFunc(int depth, GetColorFunc *getColor, SetColorFunc *setColor) +{ + switch (depth) + { + case 16: + if (getColor) + *getColor = getColor16; + if (setColor) + *setColor = setColor16; + return true; + case 24: + if (getColor) + *getColor = getColor24; + if (setColor) + *setColor = setColor24; + return true; + case 32: + if (getColor) + *getColor = getColor32; + if (setColor) + *setColor = setColor32; + return true; + default: + return false; + } +} + +/** + * Resets emulator speed / pause states after script exit. + */ +static void VBALuaOnStop(void) +{ + luaRunning = false; + lua_joypads_used = 0; + gui_used = false; + //if (wasPaused) + // systemSetPause(true); +} + +/** + * Asks Lua if it wants control of the emulator's speed. + * Returns 0 if no, 1 if yes. If yes, we also tamper with the + * IPPU's settings for speed ourselves, so the calling code + * need not do anything. + */ +int VBALuaSpeed(void) +{ + if (!LUA || !luaRunning) + return 0; + + //printf("%d\n", speedmode); + switch (speedmode) + { + /* + case SPEED_NORMAL: + return 0; + case SPEED_NOTHROTTLE: + IPPU.RenderThisFrame = true; + return 1; + + case SPEED_TURBO: + IPPU.SkippedFrames++; + if (IPPU.SkippedFrames >= 40) { + IPPU.SkippedFrames = 0; + IPPU.RenderThisFrame = true; + } + else + IPPU.RenderThisFrame = false; + return 1; + + // In mode 3, SkippedFrames is set to zero so that the frame + // skipping code doesn't try anything funny. + case SPEED_MAXIMUM: + IPPU.SkippedFrames=0; + IPPU.RenderThisFrame = false; + return 1; + */ + case 0: // FIXME: to get rid of the warning + default: + assert(false); + return 0; + } +} + +/////////////////////////// +// vba.speedmode(string mode) +// +// Takes control of the emulation speed +// of the system. Normal is normal speed (60fps, 50 for PAL), +// nothrottle disables speed control but renders every frame, +// turbo renders only a few frames in order to speed up emulation, + +// maximum renders no frames +static int vba_speedmode(lua_State *L) +{ + const char *mode = luaL_checkstring(L, 1); + + if (strcasecmp(mode, "normal") == 0) + { + speedmode = SPEED_NORMAL; + } + else if (strcasecmp(mode, "nothrottle") == 0) + { + speedmode = SPEED_NOTHROTTLE; + } + else if (strcasecmp(mode, "turbo") == 0) + { + speedmode = SPEED_TURBO; + } + else if (strcasecmp(mode, "maximum") == 0) + { + speedmode = SPEED_MAXIMUM; + } + else + luaL_error(L, "Invalid mode %s to vba.speedmode", mode); + + //printf("new speed mode: %d\n", speedmode); + return 0; +} + +// vba.frameadvnace() +// +// Executes a frame advance. Occurs by yielding the coroutine, then re-running + +// when we break out. +static int vba_frameadvance(lua_State *L) +{ + // We're going to sleep for a frame-advance. Take notes. + if (frameAdvanceWaiting) + return luaL_error(L, "can't call vba.frameadvance() from here"); + + frameAdvanceWaiting = true; + + // Don't do this! The user won't like us sending their emulator out of control! + // Settings.FrameAdvance = true; + // Now we can yield to the main + return lua_yield(L, 0); + + // It's actually rather disappointing... +} + +// vba.pause() +// +// Pauses the emulator, function "waits" until the user unpauses. +// This function MAY be called from a non-frame boundary, but the frame + +// finishes executing anwyays. In this case, the function returns immediately. +static int vba_pause(lua_State *L) +{ + systemSetPause(true); + speedmode = SPEED_NORMAL; + + // Return control if we're midway through a frame. We can't pause here. + if (frameAdvanceWaiting) + { + return 0; + } + + // If it's on a frame boundary, we also yield. + frameAdvanceWaiting = true; + return lua_yield(L, 0); +} + +static int vba_registerbefore(lua_State *L) +{ + if (!lua_isnil(L, 1)) + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); + lua_insert(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEMULATION]); + + //StopScriptIfFinished(luaStateToUIDMap[L]); + return 1; +} + +static int vba_registerafter(lua_State *L) +{ + if (!lua_isnil(L, 1)) + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); + lua_insert(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_AFTEREMULATION]); + + //StopScriptIfFinished(luaStateToUIDMap[L]); + return 1; +} + +static int vba_registerexit(lua_State *L) +{ + if (!lua_isnil(L, 1)) + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); + lua_insert(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); + + //StopScriptIfFinished(luaStateToUIDMap[L]); + return 1; +} + +static inline bool isalphaorunderscore(char c) +{ + return isalpha(c) || c == '_'; +} + +static std::vector s_tableAddressStack; // prevents infinite recursion of a table within a table (when cycle is + // found, print something like table:parent) +static std::vector s_metacallStack; // prevents infinite recursion if something's __tostring returns another table + // that contains that something (when cycle is found, print the inner result + // without using __tostring) + +#define APPENDPRINT { int _n = snprintf(ptr, remaining, +#define END ); if (_n >= 0) { ptr += _n; remaining -= _n; } else { remaining = 0; } } +static void toCStringConverter(lua_State *L, int i, char * &ptr, int &remaining) +{ + if (remaining <= 0) + return; + + const char *str = ptr; // for debugging + + // if there is a __tostring metamethod then call it + int usedMeta = luaL_callmeta(L, i, "__tostring"); + if (usedMeta) + { + std::vector::const_iterator foundCycleIter = std::find(s_metacallStack.begin(), s_metacallStack.end(), lua_topointer(L, i)); + if (foundCycleIter != s_metacallStack.end()) + { + lua_pop(L, 1); + usedMeta = false; + } + else + { + s_metacallStack.push_back(lua_topointer(L, i)); + i = lua_gettop(L); + } + } + + switch (lua_type(L, i)) + { + case LUA_TNONE: + break; + case LUA_TNIL: + APPENDPRINT "nil" END break; + case LUA_TBOOLEAN: + APPENDPRINT lua_toboolean(L, i) ? "true" : "false" END break; + case LUA_TSTRING : APPENDPRINT "%s", lua_tostring(L, i) END break; + case LUA_TNUMBER: + APPENDPRINT "%.12Lg", lua_tonumber(L, i) END break; + case LUA_TFUNCTION: + if ((L->base + i - 1)->value.gc->cl.c.isC) + { + //lua_CFunction func = lua_tocfunction(L, i); + //std::map::iterator iter = s_cFuncInfoMap.find(func); + //if(iter == s_cFuncInfoMap.end()) + goto defcase; + //APPENDPRINT "function(%s)", iter->second END + } + else + { + APPENDPRINT "function(" END + Proto * p = (L->base + i - 1)->value.gc->cl.l.p; + int numParams = p->numparams + (p->is_vararg ? 1 : 0); + for (int n = 0; n < p->numparams; n++) + { + APPENDPRINT "%s", getstr(p->locvars[n].varname) END + if (n != numParams - 1) + APPENDPRINT "," END + } + if (p->is_vararg) + APPENDPRINT "..." END + APPENDPRINT ")" END + } + break; +defcase: default: + APPENDPRINT "%s:%p", luaL_typename(L, i), lua_topointer(L, i) END break; + case LUA_TTABLE: + { + // first make sure there's enough stack space + if (!lua_checkstack(L, 4)) + { + // note that even if lua_checkstack never returns false, + // that doesn't mean we didn't need to call it, + // because calling it retrieves stack space past LUA_MINSTACK + goto defcase; + } + + std::vector::const_iterator foundCycleIter = + std::find(s_tableAddressStack.begin(), s_tableAddressStack.end(), lua_topointer(L, i)); + if (foundCycleIter != s_tableAddressStack.end()) + { + int parentNum = s_tableAddressStack.end() - foundCycleIter; + if (parentNum > 1) + APPENDPRINT "%s:parent^%d", luaL_typename(L, i), parentNum END + else + APPENDPRINT "%s:parent", luaL_typename(L, i) END + } + else + { + s_tableAddressStack.push_back(lua_topointer(L, i)); + struct Scope { ~Scope(){ s_tableAddressStack. pop_back(); } } scope; + + APPENDPRINT "{" END + + lua_pushnil(L); // first key + int keyIndex = lua_gettop(L); + int valueIndex = keyIndex + 1; + bool first = true; + bool skipKey = true; // true if we're still in the "array part" of the table + lua_Number arrayIndex = (lua_Number)0; + while (lua_next(L, i)) + { + if (first) + first = false; + else + APPENDPRINT ", " END + if (skipKey) + { + arrayIndex += (lua_Number)1; + bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); + skipKey = keyIsNumber && (lua_tonumber(L, keyIndex) == arrayIndex); + } + if (!skipKey) + { + bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); + bool invalidLuaIdentifier = (!keyIsString || !isalphaorunderscore(*lua_tostring(L, keyIndex))); + if (invalidLuaIdentifier) + if (keyIsString) + APPENDPRINT "['" END + else + APPENDPRINT "[" END + + toCStringConverter(L, keyIndex, ptr, remaining); + // key + + if (invalidLuaIdentifier) + if (keyIsString) + APPENDPRINT "']=" END + else + APPENDPRINT "]=" END + else + APPENDPRINT "=" END + } + + bool valueIsString = (lua_type(L, valueIndex) == LUA_TSTRING); + if (valueIsString) + APPENDPRINT "'" END + + toCStringConverter(L, valueIndex, ptr, remaining); // value + + if (valueIsString) + APPENDPRINT "'" END + + lua_pop(L, 1); + + if (remaining <= 0) + { + lua_settop(L, keyIndex - 1); // stack might not be clean yet if we're breaking + // early + break; + } + } + APPENDPRINT "}" END + } + } + break; + } + + if (usedMeta) + { + s_metacallStack.pop_back(); + lua_pop(L, 1); + } + } + + static const int s_tempStrMaxLen = 64 * 1024; + static char s_tempStr [s_tempStrMaxLen]; + + static char *rawToCString(lua_State *L, int idx) + { + int a = idx > 0 ? idx : 1; + int n = idx > 0 ? idx : lua_gettop(L); + + char *ptr = s_tempStr; + *ptr = 0; + + int remaining = s_tempStrMaxLen; + for (int i = a; i <= n; i++) + { + toCStringConverter(L, i, ptr, remaining); + if (i != n) + APPENDPRINT " " END + } + + if (remaining < 3) + { + while (remaining < 6) + remaining++, ptr--; + APPENDPRINT "..." END + } + APPENDPRINT "\r\n" END + // the trailing newline is so print() can avoid having to do wasteful things to print its newline + // (string copying would be wasteful and calling info.print() twice can be extremely slow) + // at the cost of functions that don't want the newline needing to trim off the last two characters + // (which is a very fast operation and thus acceptable in this case) + + return s_tempStr; + } +#undef APPENDPRINT +#undef END + +// replacement for luaB_tostring() that is able to show the contents of tables (and formats numbers better, and show function +// prototypes) +// can be called directly from lua via tostring(), assuming tostring hasn't been reassigned + static int tostring(lua_State *L) + { + char *str = rawToCString(L); + str[strlen(str) - 2] = 0; // hack: trim off the \r\n (which is there to simplify the print function's + // task) + lua_pushstring(L, str); + return 1; + } + +// like rawToCString, but will check if the global Lua function tostring() +// has been replaced with a custom function, and call that instead if so + static const char *toCString(lua_State *L, int idx) + { + int a = idx > 0 ? idx : 1; + int n = idx > 0 ? idx : lua_gettop(L); + lua_getglobal(L, "tostring"); + lua_CFunction cf = lua_tocfunction(L, -1); + if (cf == tostring || lua_isnil(L, -1)) // optimization: if using our own C tostring function, we can + // bypass the call through Lua and all the string object + // allocation that would entail + { + lua_pop(L, 1); + return rawToCString(L, idx); + } + else // if the user overrided the tostring function, we have to actually call it and store the + // temporarily allocated string it returns + { + lua_pushstring(L, ""); + for (int i = a; i <= n; i++) + { + lua_pushvalue(L, -2); // function to be called + lua_pushvalue(L, i); // value to print + lua_call(L, 1, 1); + if (lua_tostring(L, -1) == NULL) + luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); + lua_pushstring(L, (i < n) ? " " : "\r\n"); + lua_concat(L, 3); + } + const char *str = lua_tostring(L, -1); + strncpy(s_tempStr, str, s_tempStrMaxLen); + s_tempStr[s_tempStrMaxLen - 1] = 0; + lua_pop(L, 2); + return s_tempStr; + } + } + +// replacement for luaB_print() that goes to the appropriate textbox instead of stdout + static int print(lua_State *L) + { + const char *str = toCString(L); + + int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; + //LuaContextInfo& info = GetCurrentInfo(); + + if (info_print) + info_print(uid, str); + else + puts(str); + + //worry(L, 100); + return 0; + } + + static int printerror(lua_State *L, int idx) + { + lua_checkstack(L, lua_gettop(L) + 4); + + if (idx < 0) + idx = lua_gettop(L) + 1 + idx; + + const char *str = rawToCString(L, idx); + + int uid = info_uid; //luaStateToUIDMap[L->l_G->mainthread]; + //LuaContextInfo& info = GetCurrentInfo(); + + if (info_print) + info_print(uid, str); + else + fputs(str, stderr); + + //worry(L, 100); + return 0; + } + +// vba.message(string msg) +// +// Displays the given message on the screen. + static int vba_message(lua_State *L) + { + const char *msg = luaL_checkstring(L, 1); + systemScreenMessage(msg); + + return 0; + } + +// provides an easy way to copy a table from Lua +// (simple assignment only makes an alias, but sometimes an independent table is desired) +// currently this function only performs a shallow copy, +// but I think it should be changed to do a deep copy (possibly of configurable depth?) +// that maintains the internal table reference structure + static int copytable(lua_State *L) + { + int origIndex = 1; // we only care about the first argument + int origType = lua_type(L, origIndex); + if (origType == LUA_TNIL) + { + lua_pushnil(L); + return 1; + } + if (origType != LUA_TTABLE) + { + luaL_typerror(L, 1, lua_typename(L, LUA_TTABLE)); + lua_pushnil(L); + return 1; + } + + lua_createtable(L, lua_objlen(L, 1), 0); + int copyIndex = lua_gettop(L); + + lua_pushnil(L); // first key + int keyIndex = lua_gettop(L); + int valueIndex = keyIndex + 1; + + while (lua_next(L, origIndex)) + { + lua_pushvalue(L, keyIndex); + lua_pushvalue(L, valueIndex); + lua_rawset(L, copyIndex); // copytable[key] = value + lua_pop(L, 1); + } + + // copy the reference to the metatable as well, if any + if (lua_getmetatable(L, origIndex)) + lua_setmetatable(L, copyIndex); + + return 1; // return the new table + } + +// because print traditionally shows the address of tables, +// and the print function I provide instead shows the contents of tables, +// I also provide this function +// (otherwise there would be no way to see a table's address, AFAICT) + static int addressof(lua_State *L) + { + const void *ptr = lua_topointer(L, -1); + lua_pushinteger(L, (lua_Integer)ptr); + return 1; + } + + struct registerPointerMap + { + const char * registerName; + unsigned int *pointer; + int dataSize; + }; + +#define RPM_ENTRY(name, var) \ + { name, (unsigned int *)&var, sizeof(var) \ + } \ + , + + extern gbRegister AF; + extern gbRegister BC; + extern gbRegister DE; + extern gbRegister HL; + extern gbRegister SP; + extern gbRegister PC; + extern u16 IFF; + + registerPointerMap regPointerMap [] = { + // gba registers + RPM_ENTRY("r0", reg[0].I) + RPM_ENTRY("r1", reg[1].I) + RPM_ENTRY("r2", reg[2].I) + RPM_ENTRY("r3", reg[3].I) + RPM_ENTRY("r4", reg[4].I) + RPM_ENTRY("r5", reg[5].I) + RPM_ENTRY("r6", reg[6].I) + RPM_ENTRY("r7", reg[7].I) + RPM_ENTRY("r8", reg[8].I) + RPM_ENTRY("r9", reg[9].I) + RPM_ENTRY("r10", reg[10].I) + RPM_ENTRY("r11", reg[11].I) + RPM_ENTRY("r12", reg[12].I) + RPM_ENTRY("r13", reg[13].I) + RPM_ENTRY("r14", reg[14].I) + RPM_ENTRY("r15", reg[15].I) + RPM_ENTRY("cpsr", reg[16].I) + RPM_ENTRY("spsr", reg[17].I) + // gb registers + RPM_ENTRY("a", AF.B.B1) + RPM_ENTRY("f", AF.B.B0) + RPM_ENTRY("b", BC.B.B1) + RPM_ENTRY("c", BC.B.B0) + RPM_ENTRY("d", DE.B.B1) + RPM_ENTRY("e", DE.B.B0) + RPM_ENTRY("h", HL.B.B1) + RPM_ENTRY("l", HL.B.B0) + RPM_ENTRY("af", AF.W) + RPM_ENTRY("bc", BC.W) + RPM_ENTRY("de", DE.W) + RPM_ENTRY("hl", HL.W) + RPM_ENTRY("sp", SP.W) + RPM_ENTRY("pc", PC.W) + {} + }; + + struct cpuToRegisterMap + { + const char *cpuName; + registerPointerMap *rpmap; + } + cpuToRegisterMaps [] = + { + { "", regPointerMap }, + }; + +//DEFINE_LUA_FUNCTION(memory_getregister, "cpu_dot_registername_string") + static int memory_getregister(lua_State *L) + { + const char *qualifiedRegisterName = luaL_checkstring(L, 1); + lua_settop(L, 0); + for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) + { + cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; + int cpuNameLen = strlen(ctrm.cpuName); + if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) + { + qualifiedRegisterName += cpuNameLen; + for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) + { + registerPointerMap rpm = ctrm.rpmap[reg]; + if (!stricmp(qualifiedRegisterName, rpm.registerName)) + { + switch (rpm.dataSize) + { + default: + case 1: + lua_pushinteger(L, *(unsigned char *)rpm.pointer); break; + case 2: + lua_pushinteger(L, *(unsigned short *)rpm.pointer); break; + case 4: + lua_pushinteger(L, *(unsigned long *)rpm.pointer); break; + } + return 1; + } + } + lua_pushnil(L); + return 1; + } + } + lua_pushnil(L); + return 1; + } + +//DEFINE_LUA_FUNCTION(memory_setregister, "cpu_dot_registername_string,value") + static int memory_setregister(lua_State *L) + { + const char * qualifiedRegisterName = luaL_checkstring(L, 1); + unsigned long value = (unsigned long)(luaL_checkinteger(L, 2)); + lua_settop(L, 0); + for (int cpu = 0; cpu < sizeof(cpuToRegisterMaps) / sizeof(*cpuToRegisterMaps); cpu++) + { + cpuToRegisterMap ctrm = cpuToRegisterMaps[cpu]; + int cpuNameLen = strlen(ctrm.cpuName); + if (!strnicmp(qualifiedRegisterName, ctrm.cpuName, cpuNameLen)) + { + qualifiedRegisterName += cpuNameLen; + for (int reg = 0; ctrm.rpmap[reg].dataSize; reg++) + { + registerPointerMap rpm = ctrm.rpmap[reg]; + if (!stricmp(qualifiedRegisterName, rpm.registerName)) + { + switch (rpm.dataSize) + { + default: + case 1: + *(unsigned char *)rpm.pointer = (unsigned char)(value & 0xFF); break; + case 2: + *(unsigned short *)rpm.pointer = (unsigned short)(value & 0xFFFF); break; + case 4: + *(unsigned long *)rpm.pointer = value; break; + } + return 0; + } + } + return 0; + } + } + return 0; + } + + void HandleCallbackError(lua_State *L) + { + if (L->errfunc || L->errorJmp) + luaL_error(L, "%s", lua_tostring(L, -1)); + else + { + lua_pushnil(LUA); + lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); + + // Error? +//#if (defined(WIN32) && !defined(SDL)) +// info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); +// AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua run error", MB_OK | MB_ICONSTOP); +//#else +// fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(LUA, -1)); +//#endif + printerror(LUA, -1); + VBALuaStop(); + } + } + + void CallRegisteredLuaFunctions(LuaCallID calltype) + { + assert((unsigned int)calltype < (unsigned int)LUACALL_COUNT); + + const char *idstring = luaCallIDStrings[calltype]; + + if (!LUA) + return; + + lua_settop(LUA, 0); + lua_getfield(LUA, LUA_REGISTRYINDEX, idstring); + + int errorcode = 0; + if (lua_isfunction(LUA, -1)) + { + errorcode = lua_pcall(LUA, 0, 0, 0); + if (errorcode) + HandleCallbackError(LUA); + } + else + { + lua_pop(LUA, 1); + } + } + +// the purpose of this structure is to provide a way of +// QUICKLY determining whether a memory address range has a hook associated with it, +// with a bias toward fast rejection because the majority of addresses will not be hooked. +// (it must not use any part of Lua or perform any per-script operations, +// otherwise it would definitely be too slow.) +// calculating the regions when a hook is added/removed may be slow, +// but this is an intentional tradeoff to obtain a high speed of checking during later execution + struct TieredRegion + { + template + struct Region + { + struct Island + { + unsigned int start; + unsigned int end; + __forceinline bool Contains(unsigned int address, int size) const { return address < end && address + size > start; } + }; + std::vector islands; + + void Calculate(const std::vector &bytes) + { + islands. clear(); + + unsigned int lastEnd = ~0; + + std::vector::const_iterator iter = bytes.begin(); + std::vector::const_iterator end = bytes.end(); + for (; iter != end; ++iter) + { + unsigned int addr = *iter; + if (addr < lastEnd || addr > lastEnd + (long long)maxGap) + { + islands. push_back(Island()); + islands. back().start = addr; + } + islands.back(). end = addr + 1; + lastEnd = addr + 1; + } + } + + bool Contains(unsigned int address, int size) const + { + for (size_t i = 0; i != islands.size(); ++i) + { + if (islands[i].Contains(address, size)) + return true; + } + return false; + } + }; + + Region<0xFFFFFFFF> broad; + Region<0x1000> mid; + Region<0> narrow; + + void Calculate(std::vector &bytes) + { + std:: sort(bytes.begin(), bytes.end()); + + broad. Calculate(bytes); + mid. Calculate(bytes); + narrow. Calculate(bytes); + } + + TieredRegion() + { + std::vector temp; + Calculate(temp); + } + + __forceinline int NotEmpty() + { + return broad.islands.size(); + } + + // note: it is illegal to call this if NotEmpty() returns 0 + __forceinline bool Contains(unsigned int address, int size) + { + return broad.islands[0].Contains(address, size) && + mid.Contains(address, size) && + narrow.Contains(address, size); + } + }; + TieredRegion hookedRegions [LUAMEMHOOK_COUNT]; + + static void CalculateMemHookRegions(LuaMemHookType hookType) + { + std::vector hookedBytes; +// std::map::iterator iter = luaContextInfo.begin(); +// std::map::iterator end = luaContextInfo.end(); +// while(iter != end) +// { +// LuaContextInfo& info = *iter->second; + if (/*info.*/ numMemHooks) + { + lua_State *L = LUA /*info.L*/; + if (L) + { + lua_settop(L, 0); + lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); + lua_pushnil(L); + while (lua_next(L, -2)) + { + if (lua_isfunction(L, -1)) + { + unsigned int addr = lua_tointeger(L, -2); + hookedBytes.push_back(addr); + } + lua_pop(L, 1); + } + lua_settop(L, 0); + } + } +// ++iter; +// } + hookedRegions[hookType].Calculate(hookedBytes); + } + + static void CallRegisteredLuaMemHook_LuaMatch(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) + { +// std::map::iterator iter = luaContextInfo.begin(); +// std::map::iterator end = luaContextInfo.end(); +// while(iter != end) +// { +// LuaContextInfo& info = *iter->second; + if (/*info.*/ numMemHooks) + { + lua_State *L = LUA /*info.L*/; + if (L /* && !info.panic*/) + { +#ifdef USE_INFO_STACK + infoStack.insert(infoStack.begin(), &info); + struct Scope { ~Scope(){ infoStack. erase(infoStack.begin()); } } scope; +#endif + lua_settop(L, 0); + lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); + for (int i = address; i != address + size; i++) + { + lua_rawgeti(L, -1, i); + if (lua_isfunction(L, -1)) + { + bool wasRunning = (luaRunning != 0) /*info.running*/; + luaRunning /*info.running*/ = true; + //RefreshScriptSpeedStatus(); + lua_pushinteger(L, address); + lua_pushinteger(L, size); + int errorcode = lua_pcall(L, 2, 0, 0); + luaRunning /*info.running*/ = wasRunning; + //RefreshScriptSpeedStatus(); + if (errorcode) + { + HandleCallbackError(L); + //int uid = iter->first; + //HandleCallbackError(L,info,uid,true); + } + break; + } + else + { + lua_pop(L, 1); + } + } + lua_settop(L, 0); + } + } +// ++iter; +// } + } + + void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType) + { + // performance critical! (called VERY frequently) + // I suggest timing a large number of calls to this function in Release if you change anything in here, + // before and after, because even the most innocent change can make it become 30% to 400% slower. + // a good amount to test is: 100000000 calls with no hook set, and another 100000000 with a hook set. + // (on my system that consistently took 200 ms total in the former case and 350 ms total in the latter + // case) + if (hookedRegions[hookType].NotEmpty()) + { + //if((hookType <= LUAMEMHOOK_EXEC) && (address >= 0xE00000)) + // address |= 0xFF0000; // account for mirroring of RAM + if (hookedRegions[hookType].Contains(address, size)) + CallRegisteredLuaMemHook_LuaMatch(address, size, value, hookType); // something has hooked this + // specific address + } + } + + static int memory_registerHook(lua_State *L, LuaMemHookType hookType, int defaultSize) + { + // get first argument: address + unsigned int addr = luaL_checkinteger(L, 1); + //if((addr & ~0xFFFFFF) == ~0xFFFFFF) + // addr &= 0xFFFFFF; + + // get optional second argument: size + int size = defaultSize; + int funcIdx = 2; + if (lua_isnumber(L, 2)) + { + size = luaL_checkinteger(L, 2); + if (size < 0) + { + size = -size; + addr -= size; + } + funcIdx++; + } + + // check last argument: callback function + bool clearing = lua_isnil(L, funcIdx); + if (!clearing) + luaL_checktype(L, funcIdx, LUA_TFUNCTION); + lua_settop(L, funcIdx); + + // get the address-to-callback table for this hook type of the current script + lua_getfield(L, LUA_REGISTRYINDEX, luaMemHookTypeStrings[hookType]); + + // count how many callback functions we'll be displacing + int numFuncsAfter = clearing ? 0 : size; + int numFuncsBefore = 0; + for (unsigned int i = addr; i != addr + size; i++) + { + lua_rawgeti(L, -1, i); + if (lua_isfunction(L, -1)) + numFuncsBefore++; + lua_pop(L, 1); + } + + // put the callback function in the address slots + for (unsigned int i = addr; i != addr + size; i++) + { + lua_pushvalue(L, -2); + lua_rawseti(L, -2, i); + } + + // adjust the count of active hooks + //LuaContextInfo& info = GetCurrentInfo(); + /*info.*/ numMemHooks += numFuncsAfter - numFuncsBefore; + + // re-cache regions of hooked memory across all scripts + CalculateMemHookRegions(hookType); + + //StopScriptIfFinished(luaStateToUIDMap[L]); + return 0; + } + + LuaMemHookType MatchHookTypeToCPU(lua_State *L, LuaMemHookType hookType) + { + int cpuID = 0; + + int cpunameIndex = 0; + if (lua_type(L, 2) == LUA_TSTRING) + cpunameIndex = 2; + else if (lua_type(L, 3) == LUA_TSTRING) + cpunameIndex = 3; + + if (cpunameIndex) + { + const char *cpuName = lua_tostring(L, cpunameIndex); + if (!stricmp(cpuName, "sub")) + cpuID = 1; + lua_remove(L, cpunameIndex); + } + + switch (cpuID) + { + case 0: + return hookType; + + case 1: + switch (hookType) + { + case LUAMEMHOOK_WRITE: + return LUAMEMHOOK_WRITE_SUB; + case LUAMEMHOOK_READ: + return LUAMEMHOOK_READ_SUB; + case LUAMEMHOOK_EXEC: + return LUAMEMHOOK_EXEC_SUB; + } + } + return hookType; + } + + static int memory_registerwrite(lua_State *L) + { + return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_WRITE), 1); + } + + static int memory_registerread(lua_State *L) + { + return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_READ), 1); + } + + static int memory_registerexec(lua_State *L) + { + return memory_registerHook(L, MatchHookTypeToCPU(L, LUAMEMHOOK_EXEC), 1); + } + +//int vba.lagcount +// + +//Returns the lagcounter variable + static int vba_getlagcount(lua_State *L) + { + lua_pushinteger(L, systemCounters.lagCount); + return 1; + } + +//int vba.lagged +// +//Returns true if the current frame is a lag frame + static int vba_lagged(lua_State *L) + { + lua_pushboolean(L, systemCounters.laggedLast); + return 1; + } + +// boolean vba.emulating() + int vba_emulating(lua_State *L) + { + lua_pushboolean(L, systemIsEmulating()); + return 1; + } + + int movie_isactive(lua_State *L) + { + lua_pushboolean(L, VBAMovieActive()); + return 1; + } + + int movie_isrecording(lua_State *L) + { + lua_pushboolean(L, VBAMovieRecording()); + return 1; + } + + int movie_isplaying(lua_State *L) + { + lua_pushboolean(L, VBAMoviePlaying()); + return 1; + } + + int movie_getlength(lua_State *L) + { + if (VBAMovieActive()) + lua_pushinteger(L, VBAMovieGetLength()); + else + lua_pushinteger(L, 0); + return 1; + } + + static int memory_readbyte(lua_State *L) + { + u32 addr; + u8 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = CPUReadByteQuick(addr); + } + else + { + val = gbReadMemoryQuick8(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_readbytesigned(lua_State *L) + { + u32 addr; + s8 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = (s8) CPUReadByteQuick(addr); + } + else + { + val = (s8) gbReadMemoryQuick8(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_readword(lua_State *L) + { + u32 addr; + u16 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = CPUReadHalfWordQuick(addr); + } + else + { + val = gbReadMemoryQuick16(addr & 0x0000FFFF); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_readwordsigned(lua_State *L) + { + u32 addr; + s16 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = (s16) CPUReadHalfWordQuick(addr); + } + else + { + val = (s16) gbReadMemoryQuick16(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_readdword(lua_State *L) + { + u32 addr; + u32 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = CPUReadMemoryQuick(addr); + } + else + { + val = gbReadMemoryQuick32(addr & 0x0000FFFF); + } + + // lua_pushinteger doesn't work properly for 32bit system, does it? + if (val >= 0x80000000 && sizeof(int) <= 4) + lua_pushnumber(L, val); + else + lua_pushinteger(L, val); + return 1; + } + + static int memory_readdwordsigned(lua_State *L) + { + u32 addr; + s32 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + val = (s32) CPUReadMemoryQuick(addr); + } + else + { + val = (s32) gbReadMemoryQuick32(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_readbyterange(lua_State *L) + { + uint32 address = luaL_checkinteger(L, 1); + int length = luaL_checkinteger(L, 2); + + if (length < 0) + { + address += length; + length = -length; + } + + // push the array + lua_createtable(L, abs(length), 0); + + // put all the values into the (1-based) array + for (int a = address, n = 1; n <= length; a++, n++) + { + unsigned char value; + + if (systemIsRunningGBA()) + { + value = CPUReadByteQuick(a); + } + else + { + value = gbReadMemoryQuick8(a); + } + + lua_pushinteger(L, value); + lua_rawseti(L, -2, n); + } + + return 1; + } + + static int memory_writebyte(lua_State *L) + { + u32 addr; + int val; + + addr = luaL_checkinteger(L, 1); + val = luaL_checkinteger(L, 2); + if (systemIsRunningGBA()) + { + CPUWriteByteQuick(addr, val); + } + else + { + gbWriteMemoryQuick8(addr, val); + } + + CallRegisteredLuaMemHook(addr, 1, val, LUAMEMHOOK_WRITE); + return 0; + } + + static int memory_writeword(lua_State *L) + { + u32 addr; + int val; + + addr = luaL_checkinteger(L, 1); + val = luaL_checkinteger(L, 2); + if (systemIsRunningGBA()) + { + CPUWriteHalfWordQuick(addr, val); + } + else + { + gbWriteMemoryQuick16(addr, val); + } + + CallRegisteredLuaMemHook(addr, 2, val, LUAMEMHOOK_WRITE); + return 0; + } + + static int memory_writedword(lua_State *L) + { + u32 addr; + int val; + + addr = luaL_checkinteger(L, 1); + val = luaL_checkinteger(L, 2); + if (systemIsRunningGBA()) + { + CPUWriteMemoryQuick(addr, val); + } + else + { + gbWriteMemoryQuick32(addr, val); + } + + CallRegisteredLuaMemHook(addr, 4, val, LUAMEMHOOK_WRITE); + return 0; + } + + static int memory_gbromreadbyte(lua_State *L) + { + u32 addr; + u8 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = gbReadROMQuick8(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreadbytesigned(lua_State *L) + { + u32 addr; + s8 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = (s8) gbReadROMQuick8(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreadword(lua_State *L) + { + u32 addr; + u16 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = gbReadROMQuick16(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreadwordsigned(lua_State *L) + { + u32 addr; + s16 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = (s16) gbReadROMQuick16(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreaddword(lua_State *L) + { + u32 addr; + u32 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = gbReadROMQuick32(addr); + } + + // lua_pushinteger doesn't work properly for 32bit system, does it? + if (val >= 0x80000000 && sizeof(int) <= 4) + lua_pushnumber(L, val); + else + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreaddwordsigned(lua_State *L) + { + u32 addr; + s32 val; + + addr = luaL_checkinteger(L, 1); + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + val = (s32) gbReadROMQuick32(addr); + } + + lua_pushinteger(L, val); + return 1; + } + + static int memory_gbromreadbyterange(lua_State *L) + { + uint32 address = luaL_checkinteger(L, 1); + int length = luaL_checkinteger(L, 2); + + if (length < 0) + { + address += length; + length = -length; + } + + // push the array + lua_createtable(L, abs(length), 0); + + // put all the values into the (1-based) array + for (int a = address, n = 1; n <= length; a++, n++) + { + unsigned char value; + + if (systemIsRunningGBA()) + { + lua_pushnil(L); + return 1; + } + else + { + value = gbReadROMQuick8(a); + } + + lua_pushinteger(L, value); + lua_rawseti(L, -2, n); + } + + return 1; + } + +// table joypad.get(int which = 1) +// +// Reads the joypads as inputted by the user. + static int joy_get_internal(lua_State *L, bool reportUp, bool reportDown) + { + // Reads the joypads as inputted by the user + int which = luaL_checkinteger(L, 1); + + if (which < 0 || which > 4) + { + luaL_error(L, "Invalid input port (valid range 0-4, specified %d)", which); + } + + uint32 buttons = systemGetOriginalJoypad(which - 1, false); + + lua_newtable(L); + + int i; + for (i = 0; i < 10; i++) + { + bool pressed = (buttons & (1 << i)) != 0; + if ((pressed && reportDown) || (!pressed && reportUp)) + { + lua_pushboolean(L, pressed); + lua_setfield(L, -2, button_mappings[i]); + } + } + + return 1; + } + +// joypad.get(which) +// returns a table of every game button, +// true meaning currently-held and false meaning not-currently-held +// (as of last frame boundary) +// this WILL read input from a currently-playing movie + static int joypad_get(lua_State *L) + { + return joy_get_internal(L, true, true); + } + +// joypad.getdown(which) +// returns a table of every game button that is currently held + static int joypad_getdown(lua_State *L) + { + return joy_get_internal(L, false, true); + } + +// joypad.getup(which) +// returns a table of every game button that is not currently held + static int joypad_getup(lua_State *L) + { + return joy_get_internal(L, true, false); + } + +// joypad.set(int which, table buttons) +// +// Sets the given buttons to be pressed during the next +// frame advance. The table should have the right + +// keys (no pun intended) set. + static int joypad_set(lua_State *L) + { + // Which joypad we're tampering with + int which = luaL_checkinteger(L, 1); + if (which < 0 || which > 4) + { + luaL_error(L, "Invalid output port (valid range 0-4, specified %d)", which); + } + + if (which == 0) + which = systemGetDefaultJoypad(); + + // And the table of buttons. + luaL_checktype(L, 2, LUA_TTABLE); + + // Set up for taking control of the indicated controller + lua_joypads_used |= 1 << (which - 1); + lua_joypads[which - 1] = 0; + + for (int i = 0; i < 10; i++) + { + const char *name = button_mappings[i]; + lua_getfield(L, 2, name); + if (!lua_isnil(L, -1)) + { + bool pressed = lua_toboolean(L, -1) != 0; + if (pressed) + lua_joypads[which - 1] |= 1 << i; + else + lua_joypads[which - 1] &= ~(1 << i); + } + lua_pop(L, 1); + } + + return 0; + } + +// Helper function to convert a savestate object to the filename it represents. + static const char *savestateobj2filename(lua_State *L, int offset) + { + // First we get the metatable of the indicated object + int result = lua_getmetatable(L, offset); + + if (!result) + luaL_error(L, "object not a savestate object"); + + // Also check that the type entry is set + lua_getfield(L, -1, "__metatable"); + if (strcmp(lua_tostring(L, -1), "vba Savestate") != 0) + luaL_error(L, "object not a savestate object"); + lua_pop(L, 1); + + // Now, get the field we want + lua_getfield(L, -1, "filename"); + + // Return it + return lua_tostring(L, -1); + } + +// Helper function for garbage collection. + static int savestate_gc(lua_State *L) + { + // The object we're collecting is on top of the stack + lua_getmetatable(L, 1); + + // Get the filename + const char *filename; + lua_getfield(L, -1, "filename"); + filename = lua_tostring(L, -1); + + // Delete the file + remove(filename); + + // We exit, and the garbage collector takes care of the rest. + // Edit: Visual Studio needs a return value anyway, so returns 0. + return 0; + } + +// object savestate.create(int which = nil) +// +// Creates an object used for savestates. +// The object can be associated with a player-accessible savestate + +// ("which" between 1 and 12) or not (which == nil). + static int savestate_create(lua_State *L) + { + int which = -1; + if (lua_gettop(L) >= 1) + { + which = luaL_checkinteger(L, 1); + if (which < 1 || which > 12) + { + luaL_error(L, "invalid player's savestate %d", which); + } + } + + char stateName[2048]; + + if (which > 0) + { + // Find an appropriate filename. This is OS specific, unfortunately. +#if (defined(WIN32) && !defined(SDL)) + CString stateName = winGetSavestateFilename(theApp.gameFilename, which); +#else + extern char saveDir[2048]; + extern char filename[2048]; + extern char *sdlGetFilename(char *name); + + if (saveDir[0]) + sprintf(stateName, "%s/%s%d.sgm", saveDir, sdlGetFilename(filename), which); + else + sprintf(stateName, "%s%d.sgm", filename, which); +#endif + } + else + { + char *stateNameTemp = tempnam(NULL, "snlua"); + strcpy(stateName, stateNameTemp); + if (stateNameTemp) + free(stateNameTemp); + } + + // Our "object". We don't care about the type, we just need the memory and GC services. + lua_newuserdata(L, 1); + + // The metatable we use, protected from Lua and contains garbage collection info and stuff. + lua_newtable(L); + + // First, we must protect it + lua_pushstring(L, "vba Savestate"); + lua_setfield(L, -2, "__metatable"); + + // Now we need to save the file itself. + lua_pushstring(L, stateName); + lua_setfield(L, -2, "filename"); + + // If it's an anonymous savestate, we must delete the file from disk should it be gargage collected + if (which < 0) + { + lua_pushcfunction(L, savestate_gc); + lua_setfield(L, -2, "__gc"); + } + + // Set the metatable + lua_setmetatable(L, -2); + + // Awesome. Return the object + return 1; + } + +// savestate.save(object state) +// + +// Saves a state to the given object. + static int savestate_save(lua_State *L) + { + const char *filename = savestateobj2filename(L, 1); + + // printf("saving %s\n", filename); + // Save states are very expensive. They take time. + numTries--; + + bool8 retvalue = theEmulator.emuWriteState ? theEmulator.emuWriteState(filename) : false; + if (!retvalue) + { + // Uh oh + luaL_error(L, "savestate failed"); + } + + return 0; + } + +// savestate.load(object state) +// + +// Loads the given state + static int savestate_load(lua_State *L) + { + const char *filename = savestateobj2filename(L, 1); + + numTries--; + + // printf("loading %s\n", filename); + bool8 retvalue = theEmulator.emuReadState ? theEmulator.emuReadState(filename) : false; + if (!retvalue) + { + // Uh oh + luaL_error(L, "loadstate failed"); + } + + return 0; + } + +// int vba.framecount() +// + +// Gets the frame counter for the movie, or the number of frames since last reset. + int vba_framecount(lua_State *L) + { + if (!VBAMovieActive()) + { + lua_pushinteger(L, systemCounters.frameCount); + } + else + { + lua_pushinteger(L, VBAMovieGetFrameCounter()); + } + + return 1; + } + +//string movie.getauthor +// + +// returns author info field of .vbm file + int movie_getauthor(lua_State *L) + { + if (!VBAMovieActive()) + { + //lua_pushnil(L); + lua_pushstring(L, ""); + return 1; + } + + lua_pushstring(L, VBAMovieGetAuthorInfo().c_str()); + return 1; + } + +//string movie.filename + int movie_getfilename(lua_State *L) + { + if (!VBAMovieActive()) + { + //lua_pushnil(L); + lua_pushstring(L, ""); + return 1; + } + + lua_pushstring(L, VBAMovieGetFilename().c_str()); + return 1; + } + +// string movie.mode() +// + +// "record", "playback" or nil + int movie_getmode(lua_State *L) + { + assert(!VBAMovieLoading()); + if (!VBAMovieActive()) + { + lua_pushnil(L); + return 1; + } + + if (VBAMovieRecording()) + lua_pushstring(L, "record"); + else + lua_pushstring(L, "playback"); + return 1; + } + + static int movie_rerecordcount(lua_State *L) + { + if (VBAMovieActive()) + lua_pushinteger(L, VBAMovieGetRerecordCount()); + else + lua_pushinteger(L, 0); + return 1; + } + + static int movie_setrerecordcount(lua_State *L) + { + if (VBAMovieActive()) + VBAMovieSetRerecordCount(luaL_checkinteger(L, 1)); + return 0; + } + + static int movie_rerecordcounting(lua_State *L) + { + if (lua_gettop(L) == 0) + luaL_error(L, "no parameters specified"); + + skipRerecords = lua_toboolean(L, 1); + return 0; + } + +// movie.stop() +// + +// Stops movie playback/recording. Bombs out if movie is not running. + static int movie_stop(lua_State *L) + { + if (!VBAMovieActive()) + luaL_error(L, "no movie"); + + VBAMovieStop(false); + return 0; + } + +#define LUA_SCREEN_WIDTH 256 +#define LUA_SCREEN_HEIGHT 239 + +// Common code by the gui library: make sure the screen array is ready + static void gui_prepare(void) + { + if (!gui_data) + gui_data = (uint8 *)malloc(LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); + if (!gui_used) + memset(gui_data, 0, LUA_SCREEN_WIDTH * LUA_SCREEN_HEIGHT * 4); + gui_used = true; + } + +// pixform for lua graphics +#define BUILD_PIXEL_ARGB8888(A, R, G, B) (((int)(A) << 24) | ((int)(R) << 16) | ((int)(G) << 8) | (int)(B)) +#define DECOMPOSE_PIXEL_ARGB8888(PIX, A, R, G, B) \ + { \ + (A) = ((PIX) >> 24) & 0xff; \ + (R) = ((PIX) >> 16) & 0xff; \ + (G) = ((PIX) >> 8) & 0xff; \ + (B) = (PIX) & 0xff; \ + } +#define LUA_BUILD_PIXEL BUILD_PIXEL_ARGB8888 +#define LUA_DECOMPOSE_PIXEL DECOMPOSE_PIXEL_ARGB8888 +#define LUA_PIXEL_A(PIX) (((PIX) >> 24) & 0xff) +#define LUA_PIXEL_R(PIX) (((PIX) >> 16) & 0xff) +#define LUA_PIXEL_G(PIX) (((PIX) >> 8) & 0xff) +#define LUA_PIXEL_B(PIX) ((PIX) & 0xff) + + template + static void swap(T &one, T &two) + { + T temp = one; + one = two; + two = temp; + } + +// write a pixel to buffer + static inline void blend32(uint32 *dstPixel, uint32 colour) + { + uint8 *dst = (uint8 *)dstPixel; + int a, r, g, b; + LUA_DECOMPOSE_PIXEL(colour, a, r, g, b); + + if (a == 255 || dst[3] == 0) + { + // direct copy + *(uint32 *) (dst) = colour; + } + else if (a == 0) + { + // do not copy + } + else + { + // alpha-blending + int a_dst = ((255 - a) * dst[3] + 128) / 255; + int a_new = a + a_dst; + + dst[0] = (uint8) (((dst[0] * a_dst + b * a) + (a_new / 2)) / a_new); + dst[1] = (uint8) (((dst[1] * a_dst + g * a) + (a_new / 2)) / a_new); + dst[2] = (uint8) (((dst[2] * a_dst + r * a) + (a_new / 2)) / a_new); + dst[3] = (uint8) a_new; + } + } + +// check if a pixel is in the lua canvas + static inline bool gui_check_boundary(int x, int y) + { + return !(x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= LUA_SCREEN_HEIGHT); + } + +// check if any part of a box is in the lua canvas + static inline bool gui_checkbox(int x1, int y1, int x2, int y2) + { + if ((x1 < 0 && x2 < 0) + || (x1 >= LUA_SCREEN_WIDTH && x2 >= LUA_SCREEN_WIDTH) + || (y1 < 0 && y2 < 0) + || (y1 >= LUA_SCREEN_HEIGHT && y2 >= LUA_SCREEN_HEIGHT)) + return false; + return true; + } + +// write a pixel to gui_data (do not check boundaries for speedup) + static inline void gui_drawpixel_fast(int x, int y, uint32 colour) + { + //gui_prepare(); + blend32((uint32 *) &gui_data[(y * LUA_SCREEN_WIDTH + x) * 4], colour); + } + +// write a pixel to gui_data (check boundaries) + static inline void gui_drawpixel_internal(int x, int y, uint32 colour) + { + //gui_prepare(); + if (gui_check_boundary(x, y)) + gui_drawpixel_fast(x, y, colour); + } + +// draw a line on gui_data (checks boundaries) + static void gui_drawline_internal(int x1, int y1, int x2, int y2, bool lastPixel, uint32 colour) + { + //gui_prepare(); + // Note: New version of Bresenham's Line Algorithm + // + // + // http://groups.google.co.jp/group/rec.games.roguelike.development/browse_thread/thread/345f4c42c3b25858/29e07a3af3a450e6?show_docid=29e07a3af3a450e6 + int swappedx = 0; + int swappedy = 0; + + int xtemp = x1 - x2; + int ytemp = y1 - y2; + if (xtemp == 0 && ytemp == 0) + { + gui_drawpixel_internal(x1, y1, colour); + return; + } + + if (xtemp < 0) + { + xtemp = -xtemp; + swappedx = 1; + } + + if (ytemp < 0) + { + ytemp = -ytemp; + swappedy = 1; + } + + int delta_x = xtemp << 1; + int delta_y = ytemp << 1; + + signed char ix = x1 > x2 ? 1 : -1; + signed char iy = y1 > y2 ? 1 : -1; + + if (lastPixel) + gui_drawpixel_internal(x2, y2, colour); + + if (delta_x >= delta_y) + { + int error = delta_y - (delta_x >> 1); + + while (x2 != x1) + { + if (error == 0 && !swappedx) + gui_drawpixel_internal(x2 + ix, y2, colour); + if (error >= 0) + { + if (error || (ix > 0)) + { + y2 += iy; + error -= delta_x; + } + } + + x2 += ix; + gui_drawpixel_internal(x2, y2, colour); + if (error == 0 && swappedx) + gui_drawpixel_internal(x2, y2 + iy, colour); + error += delta_y; + } + } + else + { + int error = delta_x - (delta_y >> 1); + + while (y2 != y1) + { + if (error == 0 && !swappedy) + gui_drawpixel_internal(x2, y2 + iy, colour); + if (error >= 0) + { + if (error || (iy > 0)) + { + x2 += ix; + error -= delta_y; + } + } + + y2 += iy; + gui_drawpixel_internal(x2, y2, colour); + if (error == 0 && swappedy) + gui_drawpixel_internal(x2 + ix, y2, colour); + error += delta_x; + } + } + } + +// draw a rect on gui_data + static void gui_drawbox_internal(int x1, int y1, int x2, int y2, uint32 colour) + { + if (x1 > x2) + std::swap(x1, x2); + if (y1 > y2) + std::swap(y1, y2); + if (x1 < 0) + x1 = -1; + if (y1 < 0) + y1 = -1; + if (x2 >= LUA_SCREEN_WIDTH) + x2 = LUA_SCREEN_WIDTH; + if (y2 >= LUA_SCREEN_HEIGHT) + y2 = LUA_SCREEN_HEIGHT; + + if (!gui_checkbox(x1, y1, x2, y2)) + return; + + //gui_prepare(); + gui_drawline_internal(x1, y1, x2, y1, true, colour); + gui_drawline_internal(x1, y2, x2, y2, true, colour); + gui_drawline_internal(x1, y1, x1, y2, true, colour); + gui_drawline_internal(x2, y1, x2, y2, true, colour); + } + +// draw a circle on gui_data + static void gui_drawcircle_internal(int x0, int y0, int radius, uint32 colour) + { + //gui_prepare(); + if (radius < 0) + radius = -radius; + if (radius == 0) + return; + if (radius == 1) + { + gui_drawpixel_internal(x0, y0, colour); + return; + } + + // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int x = 0; + int y = radius; + + if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) + return; + + gui_drawpixel_internal(x0, y0 + radius, colour); + gui_drawpixel_internal(x0, y0 - radius, colour); + gui_drawpixel_internal(x0 + radius, y0, colour); + gui_drawpixel_internal(x0 - radius, y0, colour); + + // same pixel shouldn't be drawed twice, + // because each pixel has opacity. + // so now the routine gets ugly. + while (true) + { + assert(ddF_x == 2 * x + 1); + assert(ddF_y == -2 * y); + assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); + if (f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + + x++; + ddF_x += 2; + f += ddF_x; + if (x < y) + { + gui_drawpixel_internal(x0 + x, y0 + y, colour); + gui_drawpixel_internal(x0 - x, y0 + y, colour); + gui_drawpixel_internal(x0 + x, y0 - y, colour); + gui_drawpixel_internal(x0 - x, y0 - y, colour); + gui_drawpixel_internal(x0 + y, y0 + x, colour); + gui_drawpixel_internal(x0 - y, y0 + x, colour); + gui_drawpixel_internal(x0 + y, y0 - x, colour); + gui_drawpixel_internal(x0 - y, y0 - x, colour); + } + else if (x == y) + { + gui_drawpixel_internal(x0 + x, y0 + y, colour); + gui_drawpixel_internal(x0 - x, y0 + y, colour); + gui_drawpixel_internal(x0 + x, y0 - y, colour); + gui_drawpixel_internal(x0 - x, y0 - y, colour); + break; + } + else + break; + } + } + +// draw fill rect on gui_data + static void gui_fillbox_internal(int x1, int y1, int x2, int y2, uint32 colour) + { + if (x1 > x2) + std::swap(x1, x2); + if (y1 > y2) + std::swap(y1, y2); + if (x1 < 0) + x1 = 0; + if (y1 < 0) + y1 = 0; + if (x2 >= LUA_SCREEN_WIDTH) + x2 = LUA_SCREEN_WIDTH - 1; + if (y2 >= LUA_SCREEN_HEIGHT) + y2 = LUA_SCREEN_HEIGHT - 1; + + //gui_prepare(); + int ix, iy; + for (iy = y1; iy <= y2; iy++) + { + for (ix = x1; ix <= x2; ix++) + { + gui_drawpixel_fast(ix, iy, colour); + } + } + } + +// fill a circle on gui_data + static void gui_fillcircle_internal(int x0, int y0, int radius, uint32 colour) + { + //gui_prepare(); + if (radius < 0) + radius = -radius; + if (radius == 0) + return; + if (radius == 1) + { + gui_drawpixel_internal(x0, y0, colour); + return; + } + + // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm + int f = 1 - radius; + int ddF_x = 1; + int ddF_y = -2 * radius; + int x = 0; + int y = radius; + + if (!gui_checkbox(x0 - radius, y0 - radius, x0 + radius, y0 + radius)) + return; + + gui_drawline_internal(x0, y0 - radius, x0, y0 + radius, true, colour); + + while (true) + { + assert(ddF_x == 2 * x + 1); + assert(ddF_y == -2 * y); + assert(f == x * x + y * y - radius * radius + 2 * x - y + 1); + if (f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + + x++; + ddF_x += 2; + f += ddF_x; + + if (x < y) + { + gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); + gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); + if (f >= 0) + { + gui_drawline_internal(x0 + y, y0 - x, x0 + y, y0 + x, true, colour); + gui_drawline_internal(x0 - y, y0 - x, x0 - y, y0 + x, true, colour); + } + } + else if (x == y) + { + gui_drawline_internal(x0 + x, y0 - y, x0 + x, y0 + y, true, colour); + gui_drawline_internal(x0 - x, y0 - y, x0 - x, y0 + y, true, colour); + break; + } + else + break; + } + } + +// Helper for a simple hex parser + static int hex2int(lua_State *L, char c) + { + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return luaL_error(L, "invalid hex in colour"); + } + + static const struct ColorMapping + { + const char *name; + int value; + } + s_colorMapping[] = + { + { "white", 0xFFFFFFFF }, + { "black", 0x000000FF }, + { "clear", 0x00000000 }, + { "gray", 0x7F7F7FFF }, + { "grey", 0x7F7F7FFF }, + { "red", 0xFF0000FF }, + { "orange", 0xFF7F00FF }, + { "yellow", 0xFFFF00FF }, + { "chartreuse", 0x7FFF00FF }, + { "green", 0x00FF00FF }, + { "teal", 0x00FF7FFF }, + { "cyan", 0x00FFFFFF }, + { "blue", 0x0000FFFF }, + { "purple", 0x7F00FFFF }, + { "magenta", 0xFF00FFFF }, + }; + +/** + * Converts an integer or a string on the stack at the given + * offset to a RGB32 colour. Several encodings are supported. + * The user may construct their own RGB value, given a simple colour name, + * or an HTML-style "#09abcd" colour. 16 bit reduction doesn't occur at this time. + */ + static inline bool str2colour(uint32 *colour, lua_State *L, const char *str) + { + if (str[0] == '#') + { + int color; + sscanf(str + 1, "%X", &color); + + int len = strlen(str + 1); + int missing = max(0, 8 - len); + color <<= missing << 2; + if (missing >= 2) + color |= 0xFF; + *colour = color; + return true; + } + else + { + if (!strnicmp(str, "rand", 4)) + { + *colour = gen_rand32() | 0xFF; //((rand()*255/RAND_MAX) << 8) | ((rand()*255/RAND_MAX) << 16) | + // ((rand()*255/RAND_MAX) << 24) | 0xFF; + return true; + } + + for (int i = 0; i < sizeof(s_colorMapping) / sizeof(*s_colorMapping); i++) + { + if (!stricmp(str, s_colorMapping[i].name)) + { + *colour = s_colorMapping[i].value; + return true; + } + } + } + + return false; + } + + static inline uint32 gui_getcolour_wrapped(lua_State *L, int offset, bool hasDefaultValue, uint32 defaultColour) + { + switch (lua_type(L, offset)) + { + case LUA_TSTRING: + { + const char *str = lua_tostring(L, offset); + uint32 colour; + + if (str2colour(&colour, L, str)) + return colour; + else + { + if (hasDefaultValue) + return defaultColour; + else + return luaL_error(L, "unknown colour %s", str); + } + } + + case LUA_TNUMBER: + { + uint32 colour = (uint32) lua_tointeger(L, offset); + return colour; + } + + case LUA_TTABLE: + { + int color = 0xFF; + lua_pushnil(L); // first key + int keyIndex = lua_gettop(L); + int valueIndex = keyIndex + 1; + bool first = true; + while (lua_next(L, offset)) + { + bool keyIsString = (lua_type(L, keyIndex) == LUA_TSTRING); + bool keyIsNumber = (lua_type(L, keyIndex) == LUA_TNUMBER); + int key = keyIsString ? tolower(*lua_tostring(L, keyIndex)) : (keyIsNumber ? lua_tointeger(L, keyIndex) : 0); + int value = lua_tointeger(L, valueIndex); + if (value < 0) value = 0; + if (value > 255) value = 255; + switch (key) + { + case 1: + case 'r': + color |= value << 24; break; + case 2: + case 'g': + color |= value << 16; break; + case 3: + case 'b': + color |= value << 8; break; + case 4: + case 'a': + color = (color & ~0xFF) | value; break; + } + lua_pop(L, 1); + } + return color; + } break; + + case LUA_TFUNCTION: + luaL_error(L, "invalid colour"); // NYI + return 0; + + default: + if (hasDefaultValue) + return defaultColour; + else + return luaL_error(L, "invalid colour"); + } + } + + static uint32 gui_getcolour(lua_State *L, int offset) + { + uint32 colour; + int a, r, g, b; + + colour = gui_getcolour_wrapped(L, offset, false, 0); + a = ((colour & 0xff) * transparencyModifier) / 255; + if (a > 255) + a = 255; + b = (colour >> 8) & 0xff; + g = (colour >> 16) & 0xff; + r = (colour >> 24) & 0xff; + return LUA_BUILD_PIXEL(a, r, g, b); + } + + static uint32 gui_optcolour(lua_State *L, int offset, uint32 defaultColour) + { + uint32 colour; + int a, r, g, b; + uint8 defA, defB, defG, defR; + + LUA_DECOMPOSE_PIXEL(defaultColour, defA, defR, defG, defB); + defaultColour = (defR << 24) | (defG << 16) | (defB << 8) | defA; + + colour = gui_getcolour_wrapped(L, offset, true, defaultColour); + a = ((colour & 0xff) * transparencyModifier) / 255; + if (a > 255) + a = 255; + b = (colour >> 8) & 0xff; + g = (colour >> 16) & 0xff; + r = (colour >> 24) & 0xff; + return LUA_BUILD_PIXEL(a, r, g, b); + } + +// gui.drawpixel(x,y,colour) + static int gui_drawpixel(lua_State *L) + { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + + uint32 colour = gui_getcolour(L, 3); + + // if (!gui_check_boundary(x, y)) + // luaL_error(L,"bad coordinates"); + gui_prepare(); + + gui_drawpixel_internal(x, y, colour); + + return 0; + } + +// gui.drawline(x1,y1,x2,y2,color,skipFirst) + static int gui_drawline(lua_State *L) + { + int x1, y1, x2, y2; + uint32 color; + x1 = luaL_checkinteger(L, 1); + y1 = luaL_checkinteger(L, 2); + x2 = luaL_checkinteger(L, 3); + y2 = luaL_checkinteger(L, 4); + color = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 255, 255, 255)); + int skipFirst = lua_toboolean(L, 6); + + gui_prepare(); + + gui_drawline_internal(x2, y2, x1, y1, !skipFirst, color); + + return 0; + } + +// gui.drawbox(x1, y1, x2, y2, fillcolor, outlinecolor) + static int gui_drawbox(lua_State *L) + { + int x1, y1, x2, y2; + uint32 fillcolor; + uint32 outlinecolor; + + x1 = luaL_checkinteger(L, 1); + y1 = luaL_checkinteger(L, 2); + x2 = luaL_checkinteger(L, 3); + y2 = luaL_checkinteger(L, 4); + fillcolor = gui_optcolour(L, 5, LUA_BUILD_PIXEL(63, 255, 255, 255)); + outlinecolor = gui_optcolour(L, 6, LUA_BUILD_PIXEL(255, LUA_PIXEL_R(fillcolor), LUA_PIXEL_G(fillcolor), LUA_PIXEL_B(fillcolor))); + + if (x1 > x2) + std::swap(x1, x2); + if (y1 > y2) + std::swap(y1, y2); + + gui_prepare(); + + gui_drawbox_internal(x1, y1, x2, y2, outlinecolor); + if ((x2 - x1) >= 2 && (y2 - y1) >= 2) + gui_fillbox_internal(x1 + 1, y1 + 1, x2 - 1, y2 - 1, fillcolor); + + return 0; + } + +// gui.drawcircle(x0, y0, radius, colour) + static int gui_drawcircle(lua_State *L) + { + int x, y, r; + uint32 colour; + + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + r = luaL_checkinteger(L, 3); + colour = gui_getcolour(L, 4); + + gui_prepare(); + + gui_drawcircle_internal(x, y, r, colour); + + return 0; + } + +// gui.fillbox(x1, y1, x2, y2, colour) + static int gui_fillbox(lua_State *L) + { + int x1, y1, x2, y2; + uint32 colour; + + x1 = luaL_checkinteger(L, 1); + y1 = luaL_checkinteger(L, 2); + x2 = luaL_checkinteger(L, 3); + y2 = luaL_checkinteger(L, 4); + colour = gui_getcolour(L, 5); + + // if (!gui_check_boundary(x1, y1)) + // luaL_error(L,"bad coordinates"); + // + // if (!gui_check_boundary(x2, y2)) + // luaL_error(L,"bad coordinates"); + gui_prepare(); + + if (!gui_checkbox(x1, y1, x2, y2)) + return 0; + + gui_fillbox_internal(x1, y1, x2, y2, colour); + + return 0; + } + +// gui.fillcircle(x0, y0, radius, colour) + static int gui_fillcircle(lua_State *L) + { + int x, y, r; + uint32 colour; + + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + r = luaL_checkinteger(L, 3); + colour = gui_getcolour(L, 4); + + gui_prepare(); + + gui_fillcircle_internal(x, y, r, colour); + + return 0; + } + + static int gui_getpixel(lua_State *L) + { + int x = luaL_checkinteger(L, 1); + int y = luaL_checkinteger(L, 2); + + int pixWidth = 240, pixHeight = 160; + int scrWidth = 240, scrHeight = 160; + int scrOffsetX = 0, scrOffsetY = 0; + int pitch; + if (!systemIsRunningGBA()) + { + if (gbBorderOn) + { + pixWidth = 256, pixHeight = 224; + scrOffsetX = 48, scrOffsetY = 40; + } + else + { + pixWidth = 160, pixHeight = 144; + } + scrWidth = 160, scrHeight = 144; + } + pitch = pixWidth * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); + scrOffsetY++; // don't know why it's needed + + if (!(x >= 0 && y >= 0 && x < scrWidth && y < scrHeight) /*!gui_check_boundary(x,y)*/) + { + lua_pushinteger(L, 0); + lua_pushinteger(L, 0); + lua_pushinteger(L, 0); + } + else + { + switch (systemColorDepth) + { + case 16: + { + uint16 *screen = (uint16 *) (&pix[scrOffsetY * pitch + scrOffsetX * 2]); + uint16 pixColor = screen[y * pitch / 2 + x]; + lua_pushinteger(L, (pixColor >> 8) & 0xF8); // red + lua_pushinteger(L, (pixColor >> 3) & 0xFC); // green + lua_pushinteger(L, (pixColor << 3) & 0xF8); // blue + } + break; + case 24: + { + uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 3]; + lua_pushinteger(L, screen[y * pitch + x * 3 + 2]); // red + lua_pushinteger(L, screen[y * pitch + x * 3 + 1]); // green + lua_pushinteger(L, screen[y * pitch + x * 3 + 0]); // blue + } + break; + case 32: + { + uint8 *screen = &pix[scrOffsetY * pitch + scrOffsetX * 4]; + lua_pushinteger(L, screen[y * pitch + x * 4 + 2]); // red + lua_pushinteger(L, screen[y * pitch + x * 4 + 1]); // green + lua_pushinteger(L, screen[y * pitch + x * 4 + 0]); // blue + } + break; + default: + lua_pushinteger(L, 0); + lua_pushinteger(L, 0); + lua_pushinteger(L, 0); + break; + } + } + return 3; + } + + static int gui_parsecolor(lua_State *L) + { + int r, g, b, a; + uint32 color = gui_getcolour(L, 1); + LUA_DECOMPOSE_PIXEL(color, a, r, g, b); + lua_pushinteger(L, r); + lua_pushinteger(L, g); + lua_pushinteger(L, b); + lua_pushinteger(L, a); + return 4; + } + +// gui.gdscreenshot() +// +// Returns a screen shot as a string in gd's v1 file format. +// This allows us to make screen shots available without gd installed locally. +// Users can also just grab pixels via substring selection. +// +// I think... Does lua support grabbing byte values from a string? // yes, string.byte(str,offset) +// Well, either way, just install gd and do what you like with it. +// It really is easier that way. + +// example: gd.createFromGdStr(gui.gdscreenshot()):png("outputimage.png") + static int gui_gdscreenshot(lua_State *L) + { + int xofs = 0, yofs = 0, ppl = 240, width = 240, height = 160; + if (!systemIsRunningGBA()) + { + if (gbBorderOn) + xofs = 48, yofs = 40, ppl = 256; + else + ppl = 160; + width = 160, height = 144; + } + + yofs++; + + //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; + int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); + uint8 *screen = &pix[yofs * pitch + xofs * (systemColorDepth / 8)]; + + int size = 11 + width * height * 4; + char *str = new char[size + 1]; + str[size] = 0; + + unsigned char *ptr = (unsigned char *)str; + + // GD format header for truecolor image (11 bytes) + *ptr++ = (65534 >> 8) & 0xFF; + *ptr++ = (65534) & 0xFF; + *ptr++ = (width >> 8) & 0xFF; + *ptr++ = (width) & 0xFF; + *ptr++ = (height >> 8) & 0xFF; + *ptr++ = (height) & 0xFF; + *ptr++ = 1; + *ptr++ = 255; + *ptr++ = 255; + *ptr++ = 255; + *ptr++ = 255; + + GetColorFunc getColor; + getColorIOFunc(systemColorDepth, &getColor, NULL); + + int x, y; + for (y = 0; y < height; y++) + { + uint8 *s = &screen[y * pitch]; + for (x = 0; x < width; x++, s += systemColorDepth / 8) + { + uint8 r, g, b; + getColor(s, &r, &g, &b); + + *ptr++ = 0; + *ptr++ = r; + *ptr++ = g; + *ptr++ = b; + } + } + + lua_pushlstring(L, str, size); + delete[] str; + return 1; + } + +// gui.opacity(number alphaValue) +// sets the transparency of subsequent draw calls +// 0.0 is completely transparent, 1.0 is completely opaque +// non-integer values are supported and meaningful, as are values greater than 1.0 +// it is not necessary to use this function to get transparency (or the less-recommended gui.transparency() either), +// because you can provide an alpha value in the color argument of each draw call. + +// however, it can be convenient to be able to globally modify the drawing transparency + static int gui_setopacity(lua_State *L) + { + double opacF = luaL_checknumber(L, 1); + transparencyModifier = (int)(opacF * 255); + if (transparencyModifier < 0) + transparencyModifier = 0; + return 0; + } + +// gui.transparency(int strength) +// + +// 0 = solid, + static int gui_transparency(lua_State *L) + { + double trans = luaL_checknumber(L, 1); + transparencyModifier = (int)((4.0 - trans) / 4.0 * 255); + if (transparencyModifier < 0) + transparencyModifier = 0; + return 0; + } + + static const uint32 Small_Font_Data[] = + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 32 + 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 33 ! + 0x00000000, 0x00040002, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 34 " + 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 35 # + 0x00000000, 0x00040300, 0x00000403, 0x00000500, 0x00070600, 0x00000706, 0x00000000, // 36 $ + 0x00000000, 0x00000002, 0x00050000, 0x00000500, 0x00000005, 0x00080000, 0x00000000, // 37 % + 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00080700, 0x00000000, // 38 & + 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 39 ' + 0x00000000, 0x00000300, 0x00000003, 0x00000004, 0x00000005, 0x00000700, 0x00000000, // 40 ( + 0x00000000, 0x00000300, 0x00050000, 0x00060000, 0x00070000, 0x00000700, 0x00000000, // 41 ) + 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00080006, 0x00000000, // 42 * + 0x00000000, 0x00000000, 0x00000400, 0x00060504, 0x00000600, 0x00000000, 0x00000000, // 43 + + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000600, 0x00000700, 0x00000007, // 44 , + 0x00000000, 0x00000000, 0x00000000, 0x00060504, 0x00000000, 0x00000000, 0x00000000, // 45 - + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 46 . + 0x00030000, 0x00040000, 0x00000400, 0x00000500, 0x00000005, 0x00000006, 0x00000000, // 47 / + 0x00000000, 0x00000300, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 48 0 + 0x00000000, 0x00000300, 0x00000403, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 49 1 + 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 50 2 + 0x00000000, 0x00000302, 0x00050000, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 51 3 + 0x00000000, 0x00000300, 0x00000003, 0x00060004, 0x00070605, 0x00080000, 0x00000000, // 52 4 + 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00070000, 0x00000706, 0x00000000, // 53 5 + 0x00000000, 0x00000300, 0x00000003, 0x00000504, 0x00070005, 0x00000700, 0x00000000, // 54 6 + 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 55 7 + 0x00000000, 0x00000300, 0x00050003, 0x00000500, 0x00070005, 0x00000700, 0x00000000, // 56 8 + 0x00000000, 0x00000300, 0x00050003, 0x00060500, 0x00070000, 0x00000700, 0x00000000, // 57 9 + 0x00000000, 0x00000000, 0x00000400, 0x00000000, 0x00000000, 0x00000700, 0x00000000, // 58 : + 0x00000000, 0x00000000, 0x00000000, 0x00000500, 0x00000000, 0x00000700, 0x00000007, // 59 ; + 0x00000000, 0x00040000, 0x00000400, 0x00000004, 0x00000600, 0x00080000, 0x00000000, // 60 < + 0x00000000, 0x00000000, 0x00050403, 0x00000000, 0x00070605, 0x00000000, 0x00000000, // 61 = + 0x00000000, 0x00000002, 0x00000400, 0x00060000, 0x00000600, 0x00000006, 0x00000000, // 62 > + 0x00000000, 0x00000302, 0x00050000, 0x00000500, 0x00000000, 0x00000700, 0x00000000, // 63 ? + 0x00000000, 0x00000300, 0x00050400, 0x00060004, 0x00070600, 0x00000000, 0x00000000, // 64 @ + 0x00000000, 0x00000300, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 65 A + 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 66 B + 0x00000000, 0x00040300, 0x00000003, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 67 C + 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00000706, 0x00000000, // 68 D + 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00080706, 0x00000000, // 69 E + 0x00000000, 0x00040302, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 70 F + 0x00000000, 0x00040300, 0x00000003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 71 G + 0x00000000, 0x00040002, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 72 H + 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 73 I + 0x00000000, 0x00040000, 0x00050000, 0x00060000, 0x00070005, 0x00000700, 0x00000000, // 74 J + 0x00000000, 0x00040002, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 75 K + 0x00000000, 0x00000002, 0x00000003, 0x00000004, 0x00000005, 0x00080706, 0x00000000, // 76 l + 0x00000000, 0x00040002, 0x00050403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 77 M + 0x00000000, 0x00000302, 0x00050003, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 78 N + 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 79 O + 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 80 P + 0x00000000, 0x00040302, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00090000, // 81 Q + 0x00000000, 0x00000302, 0x00050003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 82 R + 0x00000000, 0x00040300, 0x00000003, 0x00000500, 0x00070000, 0x00000706, 0x00000000, // 83 S + 0x00000000, 0x00040302, 0x00000400, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 84 T + 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070005, 0x00080706, 0x00000000, // 85 U + 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000000, // 86 V + 0x00000000, 0x00040002, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 87 W + 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 88 X + 0x00000000, 0x00040002, 0x00050003, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 89 Y + 0x00000000, 0x00040302, 0x00050000, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 90 Z + 0x00000000, 0x00040300, 0x00000400, 0x00000500, 0x00000600, 0x00080700, 0x00000000, // 91 [ + 0x00000000, 0x00000002, 0x00000400, 0x00000500, 0x00070000, 0x00080000, 0x00000000, // 92 '\' + 0x00000000, 0x00000302, 0x00000400, 0x00000500, 0x00000600, 0x00000706, 0x00000000, // 93 ] + 0x00000000, 0x00000300, 0x00050003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 94 ^ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00080706, 0x00000000, // 95 _ + 0x00000000, 0x00000002, 0x00000400, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 96 ` + 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 97 a + 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00000706, 0x00000000, // 98 b + 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00000005, 0x00080700, 0x00000000, // 99 c + 0x00000000, 0x00040000, 0x00050000, 0x00060500, 0x00070005, 0x00080700, 0x00000000, // 100 d + 0x00000000, 0x00000000, 0x00050400, 0x00060504, 0x00000005, 0x00080700, 0x00000000, // 101 e + 0x00000000, 0x00040300, 0x00000003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 102 f + 0x00000000, 0x00000000, 0x00050400, 0x00060004, 0x00070600, 0x00080000, 0x00000807, // 103 g + 0x00000000, 0x00000002, 0x00000003, 0x00000504, 0x00070005, 0x00080006, 0x00000000, // 104 h + 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000000, // 105 i + 0x00000000, 0x00000300, 0x00000000, 0x00000500, 0x00000600, 0x00000700, 0x00000007, // 106 j + 0x00000000, 0x00000002, 0x00000003, 0x00060004, 0x00000605, 0x00080006, 0x00000000, // 107 k + 0x00000000, 0x00000300, 0x00000400, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 108 l + 0x00000000, 0x00000000, 0x00050003, 0x00060504, 0x00070005, 0x00080006, 0x00000000, // 109 m + 0x00000000, 0x00000000, 0x00000403, 0x00060004, 0x00070005, 0x00080006, 0x00000000, // 110 n + 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 111 o + 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00000605, 0x00000006, 0x00000007, // 112 p + 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070600, 0x00080000, 0x00090000, // 113 q + 0x00000000, 0x00000000, 0x00050003, 0x00000504, 0x00000005, 0x00000006, 0x00000000, // 114 r + 0x00000000, 0x00000000, 0x00050400, 0x00000004, 0x00070600, 0x00000706, 0x00000000, // 115 s + 0x00000000, 0x00000300, 0x00050403, 0x00000500, 0x00000600, 0x00080000, 0x00000000, // 116 t + 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00080700, 0x00000000, // 117 u + 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070005, 0x00000700, 0x00000000, // 118 v + 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00070605, 0x00080006, 0x00000000, // 119 w + 0x00000000, 0x00000000, 0x00050003, 0x00000500, 0x00070005, 0x00080006, 0x00000000, // 120 x + 0x00000000, 0x00000000, 0x00050003, 0x00060004, 0x00000600, 0x00000700, 0x00000007, // 121 y + 0x00000000, 0x00000000, 0x00050403, 0x00000500, 0x00000005, 0x00080706, 0x00000000, // 122 z + 0x00000000, 0x00040300, 0x00000400, 0x00000504, 0x00000600, 0x00080700, 0x00000000, // 123 { + 0x00000000, 0x00000300, 0x00000400, 0x00000000, 0x00000600, 0x00000700, 0x00000000, // 124 | + 0x00000000, 0x00000302, 0x00000400, 0x00060500, 0x00000600, 0x00000706, 0x00000000, // 125 } + 0x00000000, 0x00000302, 0x00050000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // 126 ~ + 0x00000000, 0x00000000, 0x00000400, 0x00060004, 0x00070605, 0x00000000, 0x00000000, // 127  + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + }; + + static void PutTextInternal(const char *str, int len, short x, short y, int color, int backcolor) + { + int Opac = (color >> 24) & 0xFF; + int backOpac = (backcolor >> 24) & 0xFF; + int origX = x; + + if (!Opac && !backOpac) + return; + + while (*str && len && y < LUA_SCREEN_HEIGHT) + { + int c = *str++; + while (x > LUA_SCREEN_WIDTH && c != '\n') + { + c = *str; + if (c == '\0') + break; + str++; + } + + if (c == '\n') + { + x = origX; + y += 8; + continue; + } + else if (c == '\t') // just in case + { + const int tabSpace = 8; + x += (tabSpace - (((x - origX) / 4) % tabSpace)) * 4; + continue; + } + + if ((unsigned int)(c - 32) >= 96) + continue; + + const unsigned char *Cur_Glyph = (const unsigned char *) &Small_Font_Data + (c - 32) * 7 * 4; + + for (int y2 = 0; y2 < 8; y2++) + { + unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y2); + for (int x2 = -1; x2 < 4; x2++) + { + int shift = x2 << 3; + int mask = 0xFF << shift; + int intensity = (glyphLine & mask) >> shift; + + if (intensity && x2 >= 0 && y2 < 7) + { + //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); + //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); + //gui_drawpixel_fast(xdraw, ydraw, color); + gui_drawpixel_internal(x + x2, y + y2, color); + } + else if (backOpac) + { + for (int y3 = max(0, y2 - 1); y3 <= min(6, y2 + 1); y3++) + { + unsigned int glyphLine = *((unsigned int *)Cur_Glyph + y3); + for (int x3 = max(0, x2 - 1); x3 <= min(3, x2 + 1); x3++) + { + int shift = x3 << 3; + int mask = 0xFF << shift; + intensity |= (glyphLine & mask) >> shift; + if (intensity) + goto draw_outline; // speedup? + } + } + +draw_outline: + if (intensity) + { + //int xdraw = max(0,min(LUA_SCREEN_WIDTH - 1,x+x2)); + //int ydraw = max(0,min(LUA_SCREEN_HEIGHT - 1,y+y2)); + //gui_drawpixel_fast(xdraw, ydraw, backcolor); + gui_drawpixel_internal(x + x2, y + y2, backcolor); + } + } + } + } + + x += 4; + len--; + } + } + + static int strlinelen(const char *string) + { + const char *s = string; + while (*s && *s != '\n') + s++; + if (*s) + s++; + return s - string; + } + + static void LuaDisplayString(const char *string, int y, int x, uint32 color, uint32 outlineColor) + { + if (!string) + return; + + gui_prepare(); + + PutTextInternal(string, strlen(string), x, y, color, outlineColor); + + /* + const char* ptr = string; + while(*ptr && y < LUA_SCREEN_HEIGHT) + { + int len = strlinelen(ptr); + int skip = 0; + if(len < 1) len = 1; + + // break up the line if it's too long to display otherwise + if(len > 63) + { + len = 63; + const char* ptr2 = ptr + len-1; + for(int j = len-1; j; j--, ptr2--) + { + if(*ptr2 == ' ' || *ptr2 == '\t') + { + len = j; + skip = 1; + break; + } + } + } + + int xl = 0; + int yl = 0; + int xh = (LUA_SCREEN_WIDTH - 1 - 1) - 4*len; + int yh = LUA_SCREEN_HEIGHT - 1; + int x2 = min(max(x,xl),xh); + int y2 = min(max(y,yl),yh); + + PutTextInternal(ptr,len,x2,y2,color,outlineColor); + + ptr += len + skip; + y += 8; + } + */ + } + +// gui.text(int x, int y, string msg) +// +// Displays the given text on the screen, using the same font and techniques as the + +// main HUD. + static int gui_text(lua_State *L) + { + //extern int font_height; + const char *msg; + int x, y; + uint32 colour, borderColour; + + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + //msg = luaL_checkstring(L, 3); + msg = toCString(L, 3); + + // if (x < 0 || x >= LUA_SCREEN_WIDTH || y < 0 || y >= (LUA_SCREEN_HEIGHT - font_height)) + // luaL_error(L,"bad coordinates"); + colour = gui_optcolour(L, 4, LUA_BUILD_PIXEL(255, 255, 255, 255)); + borderColour = gui_optcolour(L, 5, LUA_BUILD_PIXEL(255, 0, 0, 0)); + + gui_prepare(); + + LuaDisplayString(msg, y, x, colour, borderColour); + + return 0; + } + +// gui.gdoverlay([int dx=0, int dy=0,] string str [, sx=0, sy=0, sw, sh] [, float alphamul=1.0]) +// +// Overlays the given image on the screen. + +// example: gui.gdoverlay(gd.createFromPng("myimage.png"):gdStr()) + static int gui_gdoverlay(lua_State *L) + { + int argCount = lua_gettop(L); + + int xStartDst = 0; + int yStartDst = 0; + int xStartSrc = 0; + int yStartSrc = 0; + + int index = 1; + if (lua_type(L, index) == LUA_TNUMBER) + { + xStartDst = lua_tointeger(L, index++); + if (lua_type(L, index) == LUA_TNUMBER) + yStartDst = lua_tointeger(L, index++); + } + + luaL_checktype(L, index, LUA_TSTRING); + + const unsigned char *ptr = (const unsigned char *)lua_tostring(L, index++); + + if (ptr[0] != 255 || (ptr[1] != 254 && ptr[1] != 255)) + luaL_error(L, "bad image data"); + + bool trueColor = (ptr[1] == 254); + ptr += 2; + + int imgwidth = *ptr++ << 8; + imgwidth |= *ptr++; + + int width = imgwidth; + int imgheight = *ptr++ << 8; + imgheight |= *ptr++; + + int height = imgheight; + if ((!trueColor && *ptr) || (trueColor && !*ptr)) + luaL_error(L, "bad image data"); + ptr++; + + int pitch = imgwidth * (trueColor ? 4 : 1); + + if ((argCount - index + 1) >= 4) + { + xStartSrc = luaL_checkinteger(L, index++); + yStartSrc = luaL_checkinteger(L, index++); + width = luaL_checkinteger(L, index++); + height = luaL_checkinteger(L, index++); + } + + int alphaMul = transparencyModifier; + if (lua_isnumber(L, index)) + alphaMul = (int)(alphaMul * lua_tonumber(L, index++)); + if (alphaMul <= 0) + return 0; + + // since there aren't that many possible opacity levels, + // do the opacity modification calculations beforehand instead of per pixel + int opacMap[256]; + for (int i = 0; i < 128; i++) + { + int opac = 255 - ((i << 1) | (i & 1)); // gdAlphaMax = 127, not 255 + opac = (opac * alphaMul) / 255; + if (opac < 0) + opac = 0; + if (opac > 255) + opac = 255; + opacMap[i] = opac; + } + + for (int i = 128; i < 256; i++) + opacMap[i] = 0; // what should we do for them, actually? + int colorsTotal = 0; + if (!trueColor) + { + colorsTotal = *ptr++ << 8; + colorsTotal |= *ptr++; + } + + int transparent = *ptr++ << 24; + transparent |= *ptr++ << 16; + transparent |= *ptr++ << 8; + transparent |= *ptr++; + struct + { + uint8 r, g, b, a; + } pal[256]; + if (!trueColor) + for (int i = 0; i < 256; i++) + { + pal[i].r = *ptr++; + pal[i].g = *ptr++; + pal[i].b = *ptr++; + pal[i].a = opacMap[*ptr++]; + } + + // some of clippings + if (xStartSrc < 0) + { + width += xStartSrc; + xStartDst -= xStartSrc; + xStartSrc = 0; + } + + if (yStartSrc < 0) + { + height += yStartSrc; + yStartDst -= yStartSrc; + yStartSrc = 0; + } + + if (xStartSrc + width >= imgwidth) + width = imgwidth - xStartSrc; + if (yStartSrc + height >= imgheight) + height = imgheight - yStartSrc; + if (xStartDst < 0) + { + width += xStartDst; + if (width <= 0) + return 0; + xStartSrc = -xStartDst; + xStartDst = 0; + } + + if (yStartDst < 0) + { + height += yStartDst; + if (height <= 0) + return 0; + yStartSrc = -yStartDst; + yStartDst = 0; + } + + if (xStartDst + width >= LUA_SCREEN_WIDTH) + width = LUA_SCREEN_WIDTH - xStartDst; + if (yStartDst + height >= LUA_SCREEN_HEIGHT) + height = LUA_SCREEN_HEIGHT - yStartDst; + if (width <= 0 || height <= 0) + return 0; // out of screen or invalid size + gui_prepare(); + + const uint8 *pix = (const uint8 *)(&ptr[yStartSrc * pitch + (xStartSrc * (trueColor ? 4 : 1))]); + int bytesToNextLine = pitch - (width * (trueColor ? 4 : 1)); + if (trueColor) + { + for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) + { + for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix += 4) + { + gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(opacMap[pix[0]], pix[1], pix[2], pix[3])); + } + } + } + else + { + for (int y = yStartDst; y < height + yStartDst && y < LUA_SCREEN_HEIGHT; y++, pix += bytesToNextLine) + { + for (int x = xStartDst; x < width + xStartDst && x < LUA_SCREEN_WIDTH; x++, pix++) + { + gui_drawpixel_fast(x, y, LUA_BUILD_PIXEL(pal[*pix].a, pal[*pix].r, pal[*pix].g, pal[*pix].b)); + } + } + } + + return 0; + } + +// function gui.register(function f) +// +// This function will be called just before a graphical update. +// More complicated, but doesn't suffer any frame delays. +// Nil will be accepted in place of a function to erase +// a previously registered function, and the previous function + +// (if any) is returned, or nil if none. + static int gui_register(lua_State *L) + { + // We'll do this straight up. + // First set up the stack. + lua_settop(L, 1); + + // Verify the validity of the entry + if (!lua_isnil(L, 1)) + luaL_checktype(L, 1, LUA_TFUNCTION); + + // Get the old value + lua_getfield(L, LUA_REGISTRYINDEX, guiCallbackTable); + + // Save the new value + lua_pushvalue(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, guiCallbackTable); + + // The old value is on top of the stack. Return it. + return 1; + } + +// string gui.popup(string message, [string type = "ok"]) +// + +// Popup dialog! + int gui_popup(lua_State *L) + { + const char *message = luaL_checkstring(L, 1); + const char *type = luaL_optstring(L, 2, "ok"); + +#if (defined(WIN32) && !defined(SDL)) + int t; + if (strcmp(type, "ok") == 0) + t = MB_OK; + else if (strcmp(type, "yesno") == 0) + t = MB_YESNO; + else if (strcmp(type, "yesnocancel") == 0) + t = MB_YESNOCANCEL; + else + return luaL_error(L, "invalid popup type \"%s\"", type); + + theApp.winCheckFullscreen(); + systemSoundClearBuffer(); + int result = AfxGetApp()->m_pMainWnd->MessageBox(message, "Lua Script Pop-up", t); + + lua_settop(L, 1); + + if (t != MB_OK) + { + if (result == IDYES) + lua_pushstring(L, "yes"); + else if (result == IDNO) + lua_pushstring(L, "no"); + else if (result == IDCANCEL) + lua_pushstring(L, "cancel"); + else + luaL_error(L, "win32 unrecognized return value %d", result); + return 1; + } + + // else, we don't care. + return 0; +#else + char *t; + #ifdef __linux + // The Linux backend has a "FromPause" variable. + // If set to 1, assume some known external event has screwed with the flow of time. + // Since this pauses the emulator waiting for a response, we set it to 1. +// FIXME: Well, actually it doesn't +// extern int FromPause; +// FromPause = 1; + + int pid; // appease compiler + + // Before doing any work, verify the correctness of the parameters. + if (strcmp(type, "ok") == 0) + t = "OK:100"; + else if (strcmp(type, "yesno") == 0) + t = "Yes:100,No:101"; + else if (strcmp(type, "yesnocancel") == 0) + t = "Yes:100,No:101,Cancel:102"; + else + return luaL_error(L, "invalid popup type \"%s\"", type); + + // Can we find a copy of xmessage? Search the path. + char *path = strdup(getenv("PATH")); + + char *current = path; + + char *colon; + + int found = 0; + + while (current) + { + colon = strchr(current, ':'); + + // Clip off the colon. + *colon++ = 0; + + int len = strlen(current); + char *filename = (char *)malloc(len + 12); // always give excess + snprintf(filename, len + 12, "%s/xmessage", current); + + if (access(filename, X_OK) == 0) + { + free(filename); + found = 1; + break; + } + + // Failed, move on. + current = colon; + free(filename); + } + + free(path); + + // We've found it? + if (!found) + goto use_console; + + pid = fork(); + if (pid == 0) + { // I'm the virgin sacrifice + // I'm gonna be dead in a matter of microseconds anyways, so wasted memory doesn't matter to me. + // Go ahead and abuse strdup. + char *parameters[] = { "xmessage", "-buttons", t, strdup(message), NULL }; + + execvp("xmessage", parameters); + + // Aw shitty + perror("exec xmessage"); + exit(1); + } + else if (pid < 0) // something went wrong!!! Oh hell... use the console + goto use_console; + else + { + // We're the parent. Watch for the child. + int r; + int res = waitpid(pid, &r, 0); + if (res < 0) // wtf? + goto use_console; + + // The return value gets copmlicated... + if (!WIFEXITED(r)) + { + luaL_error(L, "don't screw with my xmessage process!"); + } + + r = WEXITSTATUS(r); + + // We assume it's worked. + if (r == 0) + { + return 0; // no parameters for an OK + } + + if (r == 100) + { + lua_pushstring(L, "yes"); + return 1; + } + + if (r == 101) + { + lua_pushstring(L, "no"); + return 1; + } + + if (r == 102) + { + lua_pushstring(L, "cancel"); + return 1; + } + + // Wtf? + return luaL_error(L, "popup failed due to unknown results involving xmessage (%d)", r); + } + +use_console: + #endif + + // All else has failed + if (strcmp(type, "ok") == 0) + t = ""; + else if (strcmp(type, "yesno") == 0) + t = "yn"; + else if (strcmp(type, "yesnocancel") == 0) + t = "ync"; + else + return luaL_error(L, "invalid popup type \"%s\"", type); + + fprintf(stderr, "Lua Message: %s\n", message); + + while (true) + { + char buffer[64]; + + // We don't want parameters + if (!t[0]) + { + fprintf(stderr, "[Press Enter]"); + fgets(buffer, sizeof(buffer), stdin); + + // We're done + return 0; + } + + fprintf(stderr, "(%s): ", t); + fgets(buffer, sizeof(buffer), stdin); + + // Check if the option is in the list + if (strchr(t, tolower(buffer[0]))) + { + switch (tolower(buffer[0])) + { + case 'y': + lua_pushstring(L, "yes"); + return 1; + case 'n': + lua_pushstring(L, "no"); + return 1; + case 'c': + lua_pushstring(L, "cancel"); + return 1; + default: + luaL_error(L, "internal logic error in console based prompts for gui.popup"); + } + } + + // We fell through, so we assume the user answered wrong and prompt again. + } + + // Nothing here, since the only way out is in the loop. +#endif + } + +#if (defined(WIN32) && !defined(SDL)) + const char *s_keyToName[256] = + { + NULL, + "leftclick", + "rightclick", + NULL, + "middleclick", + NULL, + NULL, + NULL, + "backspace", + "tab", + NULL, + NULL, + NULL, + "enter", + NULL, + NULL, + "shift", // 0x10 + "control", + "alt", + "pause", + "capslock", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "escape", + NULL, + NULL, + NULL, + NULL, + "space", // 0x20 + "pageup", + "pagedown", + "end", + "home", + "left", + "up", + "right", + "down", + NULL, + NULL, + NULL, + NULL, + "insert", + "delete", + NULL, + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", + "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z", + NULL, + NULL, + NULL, + NULL, + NULL, + "numpad0", "numpad1", "numpad2", "numpad3", "numpad4", "numpad5", "numpad6", "numpad7", "numpad8", "numpad9", + "numpad*", "numpad+", + NULL, + "numpad-", "numpad.", "numpad/", + "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", + "F12", + "F13", "F14", "F15", "F16", "F17", "F18", "F19", "F20", "F21", "F22", "F23", + "F24", + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + "numlock", + "scrolllock", + NULL, // 0x92 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, // 0xB9 + "semicolon", + "plus", + "comma", + "minus", + "period", + "slash", + "tilde", + NULL, // 0xC1 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, // 0xDA + "leftbracket", + "backslash", + "rightbracket", + "quote", + }; +#endif + +// input.get() +// takes no input, returns a lua table of entries representing the current input state, +// independent of the joypad buttons the emulated game thinks are pressed +// for example: +// if the user is holding the W key and the left mouse button +// and has the mouse at the bottom-right corner of the game screen, + +// then this would return {W=true, leftclick=true, xmouse=255, ymouse=223} + static int input_getcurrentinputstatus(lua_State *L) + { + lua_newtable(L); + +#if (defined(WIN32) && !defined(SDL)) + // keyboard and mouse button status + { + unsigned char keys[256]; + if (true /*!GUI.BackgroundInput*/) // TODO: background input + { + if (GetKeyboardState(keys)) + { + for (int i = 1; i < 255; i++) + { + int mask = (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) ? 0x01 : 0x80; + if (keys[i] & mask) + { + const char *name = s_keyToName[i]; + if (name) + { + lua_pushboolean(L, true); + lua_setfield(L, -2, name); + } + } + } + } + } + else // use a slightly different method that will detect background input: + { + for (int i = 1; i < 255; i++) + { + const char *name = s_keyToName[i]; + if (name) + { + int active; + if (i == VK_CAPITAL || i == VK_NUMLOCK || i == VK_SCROLL) + active = GetKeyState(i) & 0x01; + else + active = GetAsyncKeyState(i) & 0x8000; + if (active) + { + lua_pushboolean(L, true); + lua_setfield(L, -2, name); + } + } + } + } + } + + // mouse position in game screen pixel coordinates + { + POINT mouse; + + int xofs = 0, yofs = 0, width = 240, height = 160; + if (!systemIsRunningGBA()) + { + if (gbBorderOn) + width = 256, height = 224, xofs = 48, yofs = 40; + else + width = 160, height = 144; + } + + GetCursorPos(&mouse); + AfxGetApp()->m_pMainWnd->ScreenToClient(&mouse); + + // game screen is always fully stretched to window size, + // with no aspect rate correction, or something like that. + RECT clientRect; + AfxGetApp()->m_pMainWnd->GetClientRect(&clientRect); + + int wndWidth = clientRect.right - clientRect.left; + int wndHeight = clientRect.bottom - clientRect.top; + mouse.x = (LONG) (mouse.x * ((float)width / wndWidth)) - xofs; + mouse.y = (LONG) (mouse.y * ((float)height / wndHeight)) - yofs; + + lua_pushinteger(L, mouse.x); + lua_setfield(L, -2, "xmouse"); + lua_pushinteger(L, mouse.y); + lua_setfield(L, -2, "ymouse"); + } + +#else + // NYI (well, return an empty table) +#endif + return 1; + } + + static int avi_framecount(lua_State *L) + { + #ifdef WIN32 + if (theApp.aviRecorder != NULL) + { + lua_pushinteger(L, theApp.aviRecorder->videoFrames()); + } + else + #endif + { + lua_pushinteger(L, 0); + } + return 1; + } + + static int avi_pause(lua_State *L) + { + #ifdef WIN32 + if (theApp.aviRecorder != NULL) + theApp.aviRecorder->Pause(true); + #endif + return 1; + } + + static int avi_resume(lua_State *L) + { + #ifdef WIN32 + if (theApp.aviRecorder != NULL) + theApp.aviRecorder->Pause(false); + #endif + return 1; + } + + static int sound_get(lua_State *L) + { + extern int32 soundLevel1; + extern int32 soundLevel2; + extern int32 soundBalance; + extern int32 soundMasterOn; + extern int32 soundVIN; + extern int32 sound1On; + extern int32 sound1EnvelopeVolume; + extern int32 sound2On; + extern int32 sound2EnvelopeVolume; + extern int32 sound3On; + extern int32 sound3OutputLevel; + extern int32 sound3Bank; + extern int32 sound3DataSize; + extern int32 sound3ForcedOutput; + extern int32 sound4On; + extern int32 sound4EnvelopeVolume; + extern u8 sound3WaveRam[0x20]; + + int freqReg; + double freq; + double leftvolscale; + double rightvolscale; + double panpot; + bool gba = systemIsRunningGBA(); + u8* gbMem = gba ? ioMem : gbMemory; + const int rNR10 = gba ? 0x60 : 0xff10; + const int rNR11 = gba ? 0x62 : 0xff11; + const int rNR12 = gba ? 0x63 : 0xff12; + const int rNR13 = gba ? 0x64 : 0xff13; + const int rNR14 = gba ? 0x65 : 0xff14; + const int rNR21 = gba ? 0x68 : 0xff16; + const int rNR22 = gba ? 0x69 : 0xff17; + const int rNR23 = gba ? 0x6c : 0xff18; + const int rNR24 = gba ? 0x6d : 0xff19; + const int rNR30 = gba ? 0x70 : 0xff1a; + const int rNR31 = gba ? 0x72 : 0xff1b; + const int rNR32 = gba ? 0x73 : 0xff1c; + const int rNR33 = gba ? 0x74 : 0xff1d; + const int rNR34 = gba ? 0x75 : 0xff1e; + const int rNR41 = gba ? 0x78 : 0xff20; + const int rNR42 = gba ? 0x79 : 0xff21; + const int rNR43 = gba ? 0x7c : 0xff22; + const int rNR44 = gba ? 0x7d : 0xff23; + const int rNR50 = gba ? 0x80 : 0xff24; + const int rNR51 = gba ? 0x81 : 0xff25; + const int rNR52 = gba ? 0x84 : 0xff26; + const int rWAVE_RAM = gba ? 0x90 : 0xff30; + + const int32 _soundVIN = 0x88; // gba ? 0x88 : soundVIN; + const bool soundVINLeft = ((_soundVIN & 0x80) != 0); + const bool soundVINRight = ((_soundVIN & 0x08) != 0); + + lua_newtable(L); + + // square1 + lua_newtable(L); + if(sound1On == 0 || soundMasterOn == 0) + { + lua_pushnumber(L, 0.0); + panpot = 0.5; + } + else + { + double envVolume = sound1EnvelopeVolume / 15.0; + if (soundVINLeft && (soundBalance & 0x10) != 0) + leftvolscale = ((soundLevel2 / 7.0) * envVolume); + else + leftvolscale = 0.0; + if (soundVINRight && (soundBalance & 0x01) != 0) + rightvolscale = ((soundLevel1 / 7.0) * envVolume); + else + rightvolscale = 0.0; + if ((leftvolscale + rightvolscale) != 0) + panpot = rightvolscale / (leftvolscale + rightvolscale); + else + panpot = 0.5; + lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); + } + lua_setfield(L, -2, "volume"); + lua_pushnumber(L, panpot); + lua_setfield(L, -2, "panpot"); + freqReg = (((int)(gbMem[rNR14] & 7) << 8) | gbMem[rNR13]); + freq = 131072.0 / (2048 - freqReg); + lua_pushnumber(L, freq); + lua_setfield(L, -2, "frequency"); + lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); + lua_setfield(L, -2, "midikey"); + lua_pushinteger(L, (gbMem[rNR11] & 0xC0) >> 6); + lua_setfield(L, -2, "duty"); + lua_newtable(L); + lua_pushinteger(L, freqReg); + lua_setfield(L, -2, "frequency"); + lua_setfield(L, -2, "regs"); + lua_setfield(L, -2, "square1"); + // square2 + lua_newtable(L); + if(sound2On == 0 || soundMasterOn == 0) + { + lua_pushnumber(L, 0.0); + panpot = 0.5; + } + else + { + double envVolume = sound2EnvelopeVolume / 15.0; + if (soundVINLeft && (soundBalance & 0x20) != 0) + leftvolscale = ((soundLevel2 / 7.0) * envVolume); + else + leftvolscale = 0.0; + if (soundVINRight && (soundBalance & 0x02) != 0) + rightvolscale = ((soundLevel1 / 7.0) * envVolume); + else + rightvolscale = 0.0; + if ((leftvolscale + rightvolscale) != 0) + panpot = rightvolscale / (leftvolscale + rightvolscale); + else + panpot = 0.5; + lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); + } + lua_setfield(L, -2, "volume"); + lua_pushnumber(L, panpot); + lua_setfield(L, -2, "panpot"); + freqReg = (((int)(gbMem[rNR24] & 7) << 8) | gbMem[rNR23]); + freq = 131072.0 / (2048 - freqReg); + lua_pushnumber(L, freq); + lua_setfield(L, -2, "frequency"); + lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); + lua_setfield(L, -2, "midikey"); + lua_pushinteger(L, (gbMem[rNR21] & 0xC0) >> 6); + lua_setfield(L, -2, "duty"); + lua_newtable(L); + lua_pushinteger(L, freqReg); + lua_setfield(L, -2, "frequency"); + lua_setfield(L, -2, "regs"); + lua_setfield(L, -2, "square2"); + // wavememory + lua_newtable(L); + if(sound3On == 0 || soundMasterOn == 0) + { + lua_pushnumber(L, 0.0); + panpot = 0.5; + } + else + { + double envVolume; + if (gba && sound3ForcedOutput != 0) + envVolume = 0.75; + else + { + double volTable[4] = { 0.0, 1.0, 0.5, 0.25 }; + envVolume = volTable[sound3OutputLevel & 3]; + } + + if (soundVINLeft && (soundBalance & 0x40) != 0) + leftvolscale = ((soundLevel2 / 7.0) * envVolume); + else + leftvolscale = 0.0; + if (soundVINRight && (soundBalance & 0x04) != 0) + rightvolscale = ((soundLevel1 / 7.0) * envVolume); + else + rightvolscale = 0.0; + if ((leftvolscale + rightvolscale) != 0) + panpot = rightvolscale / (leftvolscale + rightvolscale); + else + panpot = 0.5; + lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); + } + lua_setfield(L, -2, "volume"); + lua_pushnumber(L, panpot); + lua_setfield(L, -2, "panpot"); + int waveMemSamples = 32; + if (gba) + { + lua_pushlstring(L, (const char *) &sound3WaveRam[sound3Bank * 0x10], sound3DataSize ? 0x20 : 0x10); + waveMemSamples = sound3DataSize ? 64 : 32; + } + else + { + lua_pushlstring(L, (const char *) &gbMem[rWAVE_RAM], 0x10); + } + lua_setfield(L, -2, "waveform"); + freqReg = (((int)(gbMem[rNR34] & 7) << 8) | gbMem[rNR33]); + freq = 2097152.0 / (waveMemSamples * (2048 - freqReg)); + lua_pushnumber(L, freq); + lua_setfield(L, -2, "frequency"); + lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); + lua_setfield(L, -2, "midikey"); + lua_newtable(L); + lua_pushinteger(L, freqReg); + lua_setfield(L, -2, "frequency"); + lua_setfield(L, -2, "regs"); + lua_setfield(L, -2, "wavememory"); + // noise + lua_newtable(L); + if(sound4On == 0 || soundMasterOn == 0) + { + lua_pushnumber(L, 0.0); + panpot = 0.5; + } + else + { + double envVolume = sound4EnvelopeVolume / 15.0; + if (soundVINLeft && (soundBalance & 0x80) != 0) + leftvolscale = ((soundLevel2 / 7.0) * envVolume); + else + leftvolscale = 0.0; + if (soundVINRight && (soundBalance & 0x08) != 0) + rightvolscale = ((soundLevel1 / 7.0) * envVolume); + else + rightvolscale = 0.0; + if ((leftvolscale + rightvolscale) != 0) + panpot = rightvolscale / (leftvolscale + rightvolscale); + else + panpot = 0.5; + lua_pushnumber(L, (leftvolscale + rightvolscale) / 2.0); + } + lua_setfield(L, -2, "volume"); + lua_pushnumber(L, panpot); + lua_setfield(L, -2, "panpot"); + const int gbNoiseFreqTable[8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + freqReg = gbNoiseFreqTable[gbMem[rNR43] & 7] << (1 + (gbMem[rNR43] >> 4)); + lua_pushboolean(L, (gbMem[rNR43] & 8) != 0); + lua_setfield(L, -2, "short"); + freq = 1048576.0 / freqReg; + lua_pushnumber(L, freq); + lua_setfield(L, -2, "frequency"); + lua_pushnumber(L, (log(freq / 440.0) * 12 / log(2.0)) + 69); + lua_setfield(L, -2, "midikey"); + lua_newtable(L); + lua_pushinteger(L, freqReg); + lua_setfield(L, -2, "frequency"); + lua_setfield(L, -2, "regs"); + lua_setfield(L, -2, "noise"); + + return 1; + } + +// same as math.random, but uses SFMT instead of C rand() +// FIXME: this function doesn't care multi-instance, + +// original math.random either though (Lua 5.1) + static int sfmt_random(lua_State *L) + { + lua_Number r = (lua_Number) genrand_real2(); + switch (lua_gettop(L)) + { // check number of arguments + case 0: + { // no arguments + lua_pushnumber(L, r); // Number between 0 and 1 + break; + } + + case 1: + { // only upper limit + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1 <= u, 1, "interval is empty"); + lua_pushnumber(L, floor(r * u) + 1); // int between 1 and `u' + break; + } + + case 2: + { // lower and upper limits + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l <= u, 2, "interval is empty"); + lua_pushnumber(L, floor(r * (u - l + 1)) + l); // int between `l' and `u' + break; + } + + default: + return luaL_error(L, "wrong number of arguments"); + } + + return 1; + } + +// same as math.randomseed, but uses SFMT instead of C srand() +// FIXME: this function doesn't care multi-instance, + +// original math.randomseed either though (Lua 5.1) + static int sfmt_randomseed(lua_State *L) + { + init_gen_rand(luaL_checkint(L, 1)); + return 0; + } + +// the following bit operations are ported from LuaBitOp 1.0.1, +// because it can handle the sign bit (bit 31) correctly. + +/* +** Lua BitOp -- a bit operations library for Lua 5.1. +** http://bitop.luajit.org/ +** +** Copyright (C) 2008-2009 Mike Pall. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +** [ MIT license: http://www.opensource.org/licenses/mit-license.php ] +*/ + +#ifdef _MSC_VER +/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */ + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; +#else +#include +#endif + + typedef int32_t SBits; + typedef uint32_t UBits; + + typedef union + { + lua_Number n; +#ifdef LUA_NUMBER_DOUBLE + uint64_t b; +#else + UBits b; +#endif + } BitNum; + +/* Convert argument to bit type. */ + static UBits barg(lua_State *L, int idx) + { + BitNum bn; + UBits b; + bn.n = lua_tonumber(L, idx); +#if defined(LUA_NUMBER_DOUBLE) + bn.n += 6755399441055744.0; /* 2^52+2^51 */ +#ifdef SWAPPED_DOUBLE + b = (UBits)(bn.b >> 32); +#else + b = (UBits)(bn.b & 0xffffffff); +#endif +#elif defined(LUA_NUMBER_INT) || defined(LUA_NUMBER_LONG) || \ + defined(LUA_NUMBER_LONGLONG) || defined(LUA_NUMBER_LONG_LONG) || \ + defined(LUA_NUMBER_LLONG) + if (sizeof(UBits) == sizeof(lua_Number)) + b = bn.b; + else + b = (UBits)(SBits)bn.n; +#elif defined(LUA_NUMBER_FLOAT) +#error "A 'float' lua_Number type is incompatible with this library" +#else +#error "Unknown number type, check LUA_NUMBER_* in luaconf.h" +#endif + if (b == 0 && !lua_isnumber(L, idx)) + luaL_typerror(L, idx, "number"); + return b; + } + +/* Return bit type. */ +#define BRET(b) lua_pushnumber(L, (lua_Number)(SBits)(b)); return 1; + + static int bit_tobit(lua_State *L) { BRET(barg(L, 1)) } + static int bit_bnot(lua_State *L) { BRET(~barg(L, 1)) } + +#define BIT_OP(func, opr) \ + static int func(lua_State * L) { int i; UBits b = barg(L, 1); \ + for (i = lua_gettop(L); i > 1; i--) \ + b opr barg(L, i); BRET(b) } + BIT_OP(bit_band, &= ) + BIT_OP(bit_bor, |= ) + BIT_OP(bit_bxor, ^= ) + +#define bshl(b, n) (b << n) +#define bshr(b, n) (b >> n) +#define bsar(b, n) ((SBits)b >> n) +#define brol(b, n) ((b << n) | (b >> (32 - n))) +#define bror(b, n) ((b << (32 - n)) | (b >> n)) +#define BIT_SH(func, fn) \ + static int func(lua_State * L) { \ + UBits b = barg(L, 1); UBits n = barg(L, 2) & 31; BRET(fn(b, n)) } + BIT_SH(bit_lshift, bshl) + BIT_SH(bit_rshift, bshr) + BIT_SH(bit_arshift, bsar) + BIT_SH(bit_rol, brol) + BIT_SH(bit_ror, bror) + + static int bit_bswap(lua_State *L) + { + UBits b = barg(L, 1); + b = (b >> 24) | ((b >> 8) & 0xff00) | ((b & 0xff00) << 8) | (b << 24); + BRET(b) + } + + static int bit_tohex(lua_State *L) + { + UBits b = barg(L, 1); + SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2); + const char *hexdigits = "0123456789abcdef"; + char buf[8]; + int i; + if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; } + if (n > 8) n = 8; + for (i = (int)n; --i >= 0; ) + { + buf[i] = hexdigits[b & 15]; b >>= 4; + } + lua_pushlstring(L, buf, (size_t)n); + return 1; + } + + static const struct luaL_Reg bit_funcs[] = { + { "tobit", bit_tobit }, + { "bnot", bit_bnot }, + { "band", bit_band }, + { "bor", bit_bor }, + { "bxor", bit_bxor }, + { "lshift", bit_lshift }, + { "rshift", bit_rshift }, + { "arshift", bit_arshift }, + { "rol", bit_rol }, + { "ror", bit_ror }, + { "bswap", bit_bswap }, + { "tohex", bit_tohex }, + { NULL, NULL } + }; + +/* Signed right-shifts are implementation-defined per C89/C99. +** But the de facto standard are arithmetic right-shifts on two's +** complement CPUs. This behaviour is required here, so test for it. +*/ +#define BAD_SAR (bsar(-8, 2) != (SBits) - 2) + + bool luabitop_validate(lua_State *L) // originally named as luaopen_bit + { + UBits b; + lua_pushnumber(L, (lua_Number)1437217655L); + b = barg(L, -1); + if (b != (UBits)1437217655L || BAD_SAR) /* Perform a simple self-test. */ + { + const char *msg = "compiled with incompatible luaconf.h"; +#ifdef LUA_NUMBER_DOUBLE +#ifdef WIN32 + if (b == (UBits)1610612736L) + msg = "use D3DCREATE_FPU_PRESERVE with DirectX"; +#endif + if (b == (UBits)1127743488L) + msg = "not compiled with SWAPPED_DOUBLE"; +#endif + if (BAD_SAR) + msg = "arithmetic right-shift broken"; + luaL_error(L, "bit library self-test failed (%s)", msg); + return false; + } + return true; + } + +// LuaBitOp ends here + + static int bit_bshift_emulua(lua_State *L) + { + int shift = luaL_checkinteger(L, 2); + if (shift < 0) + { + lua_pushinteger(L, -shift); + lua_replace(L, 2); + return bit_lshift(L); + } + else + return bit_rshift(L); + } + + static int bitbit(lua_State *L) + { + int rv = 0; + int numArgs = lua_gettop(L); + for (int i = 1; i <= numArgs; i++) + { + int where = luaL_checkinteger(L, i); + if (where >= 0 && where < 32) + rv |= (1 << where); + } + lua_settop(L, 0); + BRET(rv); + } + +// The function called periodically to ensure Lua doesn't run amok. + static void VBALuaHookFunction(lua_State *L, lua_Debug *dbg) + { + if (numTries-- == 0) + { + int kill = 0; + +#if (defined(WIN32) && !defined(SDL)) + // Uh oh + theApp.winCheckFullscreen(); + systemSoundClearBuffer(); + int ret = AfxGetApp()->m_pMainWnd->MessageBox( + "The Lua script running has been running a long time. It may have gone crazy. Kill it?\n\n(No = don't check anymore either)", + "Lua Script Gone Nuts?", + MB_YESNO); + + if (ret == IDYES) + { + kill = 1; + } + +#else + fprintf( + stderr, + "The Lua script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n"); + + char buffer[64]; + while (true) + { + fprintf(stderr, "(y/n): "); + fgets(buffer, sizeof(buffer), stdin); + if (buffer[0] == 'y' || buffer[0] == 'Y') + { + kill = 1; + break; + } + + if (buffer[0] == 'n' || buffer[0] == 'N') + break; + } +#endif + if (kill) + { + luaL_error(L, "Killed by user request."); + VBALuaOnStop(); + } + + // else, kill the debug hook. + lua_sethook(L, NULL, 0, 0); + } + } + + static const struct luaL_reg vbalib[] = { + // {"speedmode", vba_speedmode}, // TODO: NYI + { "frameadvance", vba_frameadvance }, + { "pause", vba_pause }, + { "framecount", vba_framecount }, + { "lagcount", vba_getlagcount }, + { "lagged", vba_lagged }, + { "emulating", vba_emulating }, + { "registerbefore", vba_registerbefore }, + { "registerafter", vba_registerafter }, + { "registerexit", vba_registerexit }, + { "message", vba_message }, + { "print", print }, // sure, why not + { NULL, NULL } + }; + + static const struct luaL_reg memorylib[] = { + { "readbyte", memory_readbyte }, + { "readbytesigned", memory_readbytesigned }, + { "readword", memory_readword }, + { "readwordsigned", memory_readwordsigned }, + { "readdword", memory_readdword }, + { "readdwordsigned", memory_readdwordsigned }, + { "readbyterange", memory_readbyterange }, + { "writebyte", memory_writebyte }, + { "writeword", memory_writeword }, + { "writedword", memory_writedword }, + { "getregister", memory_getregister }, + { "setregister", memory_setregister }, + { "gbromreadbyte", memory_gbromreadbyte }, + { "gbromreadbytesigned", memory_gbromreadbytesigned }, + { "gbromreadword", memory_gbromreadword }, + { "gbromreadwordsigned", memory_gbromreadwordsigned }, + { "gbromreaddword", memory_gbromreaddword }, + { "gbromreaddwordsigned", memory_gbromreaddwordsigned }, + { "gbromreadbyterange", memory_gbromreadbyterange }, + + // alternate naming scheme for word and double-word and unsigned + { "readbyteunsigned", memory_readbyte }, + { "readwordunsigned", memory_readword }, + { "readdwordunsigned", memory_readdword }, + { "readshort", memory_readword }, + { "readshortunsigned", memory_readword }, + { "readshortsigned", memory_readwordsigned }, + { "readlong", memory_readdword }, + { "readlongunsigned", memory_readdword }, + { "readlongsigned", memory_readdwordsigned }, + { "writeshort", memory_writeword }, + { "writelong", memory_writedword }, + { "gbromreadbyteunsigned", memory_gbromreadbyte }, + { "gbromreadwordunsigned", memory_gbromreadword }, + { "gbromreaddwordunsigned", memory_gbromreaddword }, + { "gbromreadshort", memory_gbromreadword }, + { "gbromreadshortunsigned", memory_gbromreadword }, + { "gbromreadshortsigned", memory_gbromreadwordsigned }, + { "gbromreadlong", memory_gbromreaddword }, + { "gbromreadlongunsigned", memory_gbromreaddword }, + { "gbromreadlongsigned", memory_gbromreaddwordsigned }, + + // memory hooks + { "registerwrite", memory_registerwrite }, + //{"registerread", memory_registerread}, + { "registerexec", memory_registerexec }, + // alternate names + { "register", memory_registerwrite }, + { "registerrun", memory_registerexec }, + { "registerexecute", memory_registerexec }, + + { NULL, NULL } + }; + + static const struct luaL_reg joypadlib[] = { + { "get", joypad_get }, + { "getdown", joypad_getdown }, + { "getup", joypad_getup }, + { "set", joypad_set }, + + // alternative names + { "read", joypad_get }, + { "write", joypad_set }, + { "readdown", joypad_getdown }, + { "readup", joypad_getup }, + { NULL, NULL } + }; + + static const struct luaL_reg savestatelib[] = { + { "create", savestate_create }, + { "save", savestate_save }, + { "load", savestate_load }, + + { NULL, NULL } + }; + + static const struct luaL_reg movielib[] = { + { "active", movie_isactive }, + { "recording", movie_isrecording }, + { "playing", movie_isplaying }, + { "mode", movie_getmode }, + + { "length", movie_getlength }, + { "author", movie_getauthor }, + { "name", movie_getfilename }, + { "rerecordcount", movie_rerecordcount }, + { "setrerecordcount", movie_setrerecordcount }, + + { "rerecordcounting", movie_rerecordcounting }, + { "framecount", vba_framecount }, // for those familiar with + // other emulators that have + // movie.framecount() + // instead of + // emulatorname.framecount() + + { "stop", movie_stop }, + + // alternative names + { "close", movie_stop }, + { "getauthor", movie_getauthor }, + { "getname", movie_getfilename }, + { NULL, NULL } + }; + + static const struct luaL_reg guilib[] = { + { "register", gui_register }, + { "text", gui_text }, + { "box", gui_drawbox }, + { "line", gui_drawline }, + { "pixel", gui_drawpixel }, + { "opacity", gui_setopacity }, + { "transparency", gui_transparency }, + { "popup", gui_popup }, + { "parsecolor", gui_parsecolor }, + { "gdscreenshot", gui_gdscreenshot }, + { "gdoverlay", gui_gdoverlay }, + { "getpixel", gui_getpixel }, + + // alternative names + { "drawtext", gui_text }, + { "drawbox", gui_drawbox }, + { "drawline", gui_drawline }, + { "drawpixel", gui_drawpixel }, + { "setpixel", gui_drawpixel }, + { "writepixel", gui_drawpixel }, + { "rect", gui_drawbox }, + { "drawrect", gui_drawbox }, + { "drawimage", gui_gdoverlay }, + { "image", gui_gdoverlay }, + { "readpixel", gui_getpixel }, + { NULL, NULL } + }; + + static const struct luaL_reg inputlib[] = { + { "get", input_getcurrentinputstatus }, + + // alternative names + { "read", input_getcurrentinputstatus }, + { NULL, NULL } + }; + + static const struct luaL_reg soundlib[] = { + { "get", sound_get }, + + // alternative names + { NULL, NULL } + }; + +// gocha: since vba dumps avi so badly, +// I add avilib as a workaround for enhanced video encoding. + static const struct luaL_reg avilib[] = { + { "framecount", avi_framecount }, + { "pause", avi_pause }, + { "resume", avi_resume }, + { NULL, NULL } + }; + + void CallExitFunction(void) + { + if (!LUA) + return; + + lua_settop(LUA, 0); + lua_getfield(LUA, LUA_REGISTRYINDEX, luaCallIDStrings[LUACALL_BEFOREEXIT]); + + int errorcode = 0; + if (lua_isfunction(LUA, -1)) + { + errorcode = lua_pcall(LUA, 0, 0, 0); + } + + if (errorcode) + HandleCallbackError(LUA); + } + + void VBALuaFrameBoundary(void) + { + // printf("Lua Frame\n"); + + lua_joypads_used = 0; + + // HA! + if (!LUA || !luaRunning) + return; + + // Our function needs calling + lua_settop(LUA, 0); + lua_getfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); + + lua_State *thread = lua_tothread(LUA, 1); + + // Lua calling C must know that we're busy inside a frame boundary + frameBoundary = true; + frameAdvanceWaiting = false; + + numTries = 1000; + + int result = lua_resume(thread, 0); + + if (result == LUA_YIELD) + { + // Okay, we're fine with that. + } + else if (result != 0) + { + // Done execution by bad causes + VBALuaOnStop(); + lua_pushnil(LUA); + lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); + lua_pushnil(LUA); + lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); + + // Error? +//#if (defined(WIN32) && !defined(SDL)) +// info_print(info_uid, lua_tostring(thread, -1)); //Clear_Sound_Buffer(); +// AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(thread, -1), "Lua run error", MB_OK | MB_ICONSTOP); +//#else +// fprintf(stderr, "Lua thread bombed out: %s\n", lua_tostring(thread, -1)); +//#endif + printerror(thread, -1); + } + else + { + VBALuaOnStop(); + printf("Script died of natural causes.\n"); + } + + // Past here, VBA actually runs, so any Lua code is called mid-frame. We must + // not do anything too stupid, so let ourselves know. + frameBoundary = false; + + if (!frameAdvanceWaiting) + { + VBALuaOnStop(); + } + } + +/** + * Loads and runs the given Lua script. + * The emulator MUST be paused for this function to be + * called. Otherwise, all frame boundary assumptions go out the window. + * + * Returns true on success, false on failure. + */ + int VBALoadLuaCode(const char *filename) + { + static bool sfmtInitialized = false; + if (!sfmtInitialized) + { + init_gen_rand((unsigned)time(NULL)); + sfmtInitialized = true; + } + + if (filename != luaScriptName) + { + if (luaScriptName) + free(luaScriptName); + luaScriptName = strdup(filename); + } + + //stop any lua we might already have had running + VBALuaStop(); + + // Set current directory from filename (for dofile) + char dir[_MAX_PATH]; + char *slash, *backslash; + strcpy(dir, filename); + slash = strrchr(dir, '/'); + backslash = strrchr(dir, '\\'); + if (!slash || (backslash && backslash < slash)) + slash = backslash; + if (slash) + { + slash[1] = '\0'; // keep slash itself for some reasons + chdir(dir); + } + + if (!LUA) + { + LUA = lua_open(); + luaL_openlibs(LUA); + + luaL_register(LUA, "emu", vbalib); // added for better cross-emulator compatibility + luaL_register(LUA, "vba", vbalib); // kept for backward compatibility + luaL_register(LUA, "memory", memorylib); + luaL_register(LUA, "joypad", joypadlib); + luaL_register(LUA, "savestate", savestatelib); + luaL_register(LUA, "movie", movielib); + luaL_register(LUA, "gui", guilib); + luaL_register(LUA, "input", inputlib); + luaL_register(LUA, "sound", soundlib); + luaL_register(LUA, "bit", bit_funcs); // LuaBitOp library + luaL_register(LUA, "avi", avilib); // workaround for enhanced video encoding + lua_settop(LUA, 0); // clean the stack, because each call to luaL_register leaves a table on top + + // register a few utility functions outside of libraries (in the global namespace) + lua_register(LUA, "print", print); + lua_register(LUA, "tostring", tostring); + lua_register(LUA, "addressof", addressof); + lua_register(LUA, "copytable", copytable); + + // old bit operation functions + lua_register(LUA, "AND", bit_band); + lua_register(LUA, "OR", bit_bor); + lua_register(LUA, "XOR", bit_bxor); + lua_register(LUA, "SHIFT", bit_bshift_emulua); + lua_register(LUA, "BIT", bitbit); + + luabitop_validate(LUA); + + lua_pushstring(LUA, "math"); + lua_gettable(LUA, LUA_GLOBALSINDEX); + lua_pushcfunction(LUA, sfmt_random); + lua_setfield(LUA, -2, "random"); + lua_pushcfunction(LUA, sfmt_randomseed); + lua_setfield(LUA, -2, "randomseed"); + lua_settop(LUA, 0); + + // push arrays for storing hook functions in + for (int i = 0; i < LUAMEMHOOK_COUNT; i++) + { + lua_newtable(LUA); + lua_setfield(LUA, LUA_REGISTRYINDEX, luaMemHookTypeStrings[i]); + } + } + + // We make our thread NOW because we want it at the bottom of the stack. + // If all goes wrong, we let the garbage collector remove it. + lua_State *thread = lua_newthread(LUA); + + // Load the data + int result = luaL_loadfile(LUA, filename); + + if (result) + { +//#if (defined(WIN32) && !defined(SDL)) +// info_print(info_uid, lua_tostring(LUA, -1)); //Clear_Sound_Buffer(); +// AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua load error", MB_OK | MB_ICONSTOP); +//#else +// fprintf(stderr, "Failed to compile file: %s\n", lua_tostring(LUA, -1)); +//#endif + printerror(LUA, -1); + + // Wipe the stack. Our thread + lua_settop(LUA, 0); + return 0; // Oh shit. + } + + // Get our function into it + lua_xmove(LUA, thread, 1); + + // Save the thread to the registry. This is why I make the thread FIRST. + lua_setfield(LUA, LUA_REGISTRYINDEX, frameAdvanceThread); + + // Initialize settings + luaRunning = true; + skipRerecords = false; + numMemHooks = 0; + transparencyModifier = 255; // opaque + lua_joypads_used = 0; // not used + //wasPaused = systemIsPaused(); + //systemSetPause(false); + + // Set up our protection hook to be executed once every 10,000 bytecode instructions. + lua_sethook(thread, VBALuaHookFunction, LUA_MASKCOUNT, 10000); + +#ifdef WIN32 + info_print = PrintToWindowConsole; + info_onstart = WinLuaOnStart; + info_onstop = WinLuaOnStop; + if (!LuaConsoleHWnd) + LuaConsoleHWnd = CreateDialog(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDD_LUA), + AfxGetMainWnd()->GetSafeHwnd(), (DLGPROC) DlgLuaScriptDialog); + info_uid = (int)LuaConsoleHWnd; +#else + info_print = NULL; + info_onstart = NULL; + info_onstop = NULL; +#endif + if (info_onstart) + info_onstart(info_uid); + + // And run it right now. :) + VBALuaFrameBoundary(); + systemRenderFrame(); + + // We're done. + return 1; + } + +/** + * Equivalent to repeating the last VBALoadLuaCode() call. + */ + int VBAReloadLuaCode(void) + { + if (!luaScriptName) + { + systemScreenMessage("There's no script to reload."); + return 0; + } + else + return VBALoadLuaCode(luaScriptName); + } + +/** + * Terminates a running Lua script by killing the whole Lua engine. + * + * Always safe to call, except from within a lua call itself (duh). + * + */ + void VBALuaStop(void) + { + //already killed + if (!LUA) + return; + + //execute the user's shutdown callbacks + CallExitFunction(); + + /*info.*/ numMemHooks = 0; + for (int i = 0; i < LUAMEMHOOK_COUNT; i++) + CalculateMemHookRegions((LuaMemHookType)i); + + //sometimes iup uninitializes com + //MBG TODO - test whether this is really necessary. i dont think it is +#if (defined(WIN32) && !defined(SDL)) + CoInitialize(0); +#endif + + if (info_onstop) + info_onstop(info_uid); + + //lua_gc(LUA,LUA_GCCOLLECT,0); + lua_close(LUA); // this invokes our garbage collectors for us + LUA = NULL; + VBALuaOnStop(); + } + +/** + * Returns true if there is a Lua script running. + * + */ + int VBALuaRunning(void) + { + // FIXME: return false when no callback functions are registered. + return (int) (LUA != NULL); // should return true if callback functions are active. + } + +/** + * Returns true if Lua would like to steal the given joypad control. + * + * Range is 0 through 3 + */ + int VBALuaUsingJoypad(int which) + { + if (which < 0 || which > 3) + which = systemGetDefaultJoypad(); + return lua_joypads_used & (1 << which); + } + +/** + * Reads the buttons Lua is feeding for the given joypad, in the same + * format as the OS-specific code. + * + * This function must not be called more than once per frame. Ideally exactly once + * per frame (if VBALuaUsingJoypad says it's safe to do so) + */ + int VBALuaReadJoypad(int which) + { + if (which < 0 || which > 3) + which = systemGetDefaultJoypad(); + + //lua_joypads_used &= ~(1 << which); + return lua_joypads[which]; + } + +/** + * If this function returns true, the movie code should NOT increment + * the rerecord count for a load-state. + * + * This function will not return true if a script is not running. + */ + bool8 VBALuaRerecordCountSkip(void) + { + // FIXME: return true if (there are any active callback functions && skipRerecords) + return LUA && luaRunning && skipRerecords; + } + +/** + * Given a screen with the indicated resolution, + * draw the current GUI onto it. + */ + void VBALuaGui(uint8 *screen, int ppl, int width, int height) + { + if (!LUA /* || !luaRunning*/) + return; + + // First, check if we're being called by anybody + lua_getfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); + + if (lua_isfunction(LUA, -1)) + { + // We call it now + numTries = 1000; + + int ret = lua_pcall(LUA, 0, 0, 0); + if (ret != 0) + { + // This is grounds for trashing the function + // Note: This must be done before the messagebox pops up, + // otherwise the messagebox will cause a paint event which causes a weird + // infinite call sequence that makes Snes9x silently exit with error code 3, + // if a Lua GUI function crashes. (nitsuja) + lua_pushnil(LUA); + lua_setfield(LUA, LUA_REGISTRYINDEX, guiCallbackTable); + +//#if (defined(WIN32) && !defined(SDL)) +// info_print(info_uid, lua_tostring(LUA, -1)); //AfxGetApp()->m_pMainWnd->MessageBox(lua_tostring(LUA, -1), "Lua Error +// in GUI function", MB_OK); +//#else +// fprintf(stderr, "Lua error in gui.register function: %s\n", lua_tostring(LUA, -1)); +//#endif + printerror(LUA, -1); + } + } + + // And wreak the stack + lua_settop(LUA, 0); + + if (!gui_used) + return; + + gui_used = false; + + int x, y; + + //int pitch = (((ppl * systemColorDepth + 7)>>3)+3)&~3; + int pitch = ppl * (systemColorDepth / 8) + (systemColorDepth == 24 ? 0 : 4); + + if (width > LUA_SCREEN_WIDTH) + width = LUA_SCREEN_WIDTH; + if (height > LUA_SCREEN_HEIGHT) + height = LUA_SCREEN_HEIGHT; + + GetColorFunc getColor; + SetColorFunc setColor; + getColorIOFunc(systemColorDepth, &getColor, &setColor); + + for (y = 0; y < height; y++) + { + uint8 *scr = &screen[y * pitch]; + for (x = 0; x < width; x++, scr += systemColorDepth / 8) + { + const uint8 gui_alpha = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 3]; + if (gui_alpha == 0) + { + // do nothing + continue; + } + + const uint8 gui_red = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 2]; + const uint8 gui_green = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4 + 1]; + const uint8 gui_blue = gui_data[(y * LUA_SCREEN_WIDTH + x) * 4]; + int red, green, blue; + + if (gui_alpha == 255) + { + // direct copy + red = gui_red; + green = gui_green; + blue = gui_blue; + } + else + { + // alpha-blending + uint8 scr_red, scr_green, scr_blue; + getColor(scr, &scr_red, &scr_green, &scr_blue); + red = (((int)gui_red - scr_red) * gui_alpha / 255 + scr_red) & 255; + green = (((int)gui_green - scr_green) * gui_alpha / 255 + scr_green) & 255; + blue = (((int)gui_blue - scr_blue) * gui_alpha / 255 + scr_blue) & 255; + } + + setColor(scr, (uint8) red, (uint8) green, (uint8) blue); + } + } + + return; + } + + void VBALuaClearGui(void) + { + gui_used = false; + } + + lua_State *VBAGetLuaState() + { + return LUA; + } + + char *VBAGetLuaScriptName() + { + return luaScriptName; + } + diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/memgzio.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/memgzio.c Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,791 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ + +/* @(#) $Id: memgzio.c,v 1.5 2006/06/06 21:04:20 spacy51 Exp $ */ + +#include +#include +#include +#include +#include + +#include "memgzio.h" + +#ifndef local +#define local static +#endif + +#ifndef DEF_MEM_LEVEL +# define DEF_MEM_LEVEL 8 +#endif + +#ifndef OS_CODE +#define OS_CODE 3 +#endif + +#ifndef zmemcpy +#define zmemcpy memcpy +#endif + +/*struct internal_state {int dummy;};*/ /* for buggy compilers */ + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) \ + {if (p) \ + free(p);} + +static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct _MemFile +{ + char *memory; + char *next; + int available; + int error; + char mode; +} MEMFILE; + +typedef struct mem_stream +{ + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + MEMFILE *file; /* memoru file */ + Byte * inbuf; /* input buffer */ + Byte * outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char * msg; /* error message */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + long startpos; /* start of compressed data in file (header skipped) */ +} mem_stream; + +local gzFile gz_open OF((char *memory, const int available, const char *mode)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((mem_stream *s)); +local void check_header OF((mem_stream *s)); +local int destroy OF((mem_stream *s)); +local void putLong OF((MEMFILE *file, uLong x)); +local uLong getLong OF((mem_stream *s)); + +local MEMFILE *memOpen(char *memory, int available, char mode) +{ + MEMFILE *f; + + if (available <= 8) + return NULL; + + if (mode != 'w' && mode != 'r') + return NULL; + + f = (MEMFILE *)malloc(sizeof(MEMFILE)); + + f->memory = memory; + f->mode = mode; + f->error = 0; + + if (mode == 'w') + { + f->available = available - 8; + f->next = memory + 8; + memory[0] = 'V'; + memory[1] = 'B'; + memory[2] = 'A'; + memory[3] = ' '; + *((int *)(memory+4)) = 0; + } + else + { + if (memory[0] != 'V' || memory[1] != 'B' || memory[2] != 'A' || + memory[3] != ' ') + { + free(f); + return NULL; + } + f->available = *((int *)(memory+4)); + f->next = memory+8; + } + + return f; +} + +local size_t memWrite(const void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if (file->mode != 'w') + { + file->error = 1; + return 0; + } + + if (total > (size_t)file->available) + { + total = file->available; + } + memcpy(file->next, buffer, total); + file->available -= (int)total; + file->next += total; + return total; +} + +local size_t memRead(void *buffer, size_t size, size_t count, + MEMFILE *file) +{ + size_t total = size*count; + + if (file->mode != 'r') + { + file->error = 1; + return 0; + } + + if (file->available == 0) + return -1; + + if (total > (size_t)file->available) + { + total = file->available; + } + memcpy(buffer, file->next, total); + file->available -= (int)total; + file->next += total; + return total; +} + +local int memPutc(int c, MEMFILE *file) +{ + if (file->mode != 'w') + { + file->error = 1; + return -1; + } + + if (file->available >= 1) + { + *file->next++ = c; + file->available--; + } + else + return -1; + + return c; +} + +local long memTell(MEMFILE *f) +{ + return (long)(f->next - f->memory) - 8; +} + +local int memError(MEMFILE *f) +{ + return f->error; +} + +local int memClose(MEMFILE *f) +{ + if (f->mode == 'w') + { + *((int *)(f->memory+4)) = memTell(f); + } + free(f); + return 0; +} + +local int memPrintf(MEMFILE *f, const char *format, ...) +{ + char buffer[80]; + va_list list; + int len; + + va_start(list, format); + len = vsprintf(buffer, format, list); + va_end(list); + + return (int)memWrite(buffer, 1, len, f); +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open return NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). + */ +local gzFile gz_open(memory, available, mode) +char *memory; +const int available; +const char *mode; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char * p = (char *)mode; + mem_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char * m = fmode; + + s = (mem_stream *)ALLOC(sizeof(mem_stream)); + if (!s) + return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->z_err = Z_OK; + s->z_eof = 0; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + s->file = NULL; + + s->mode = '\0'; + do + { + if (*p == 'r') + s->mode = 'r'; + if (*p == 'w' || *p == 'a') + s->mode = 'w'; + if (*p >= '0' && *p <= '9') + { + level = *p - '0'; + } + else if (*p == 'f') + { + strategy = Z_FILTERED; + } + else if (*p == 'h') + { + strategy = Z_HUFFMAN_ONLY; + } + else + { + *m++ = *p; /* copy the mode */ + } + } + while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') + return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte *)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + } + else + { + s->stream.next_in = s->inbuf = (Byte *)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = memOpen(memory, available, s->mode); + + if (s->file == NULL) + { + return destroy(s), (gzFile)Z_NULL; + } + + if (s->mode == 'w') + { + /* Write a very simple .gz header: + */ + memPrintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0, 0, 0, 0 /*time*/, 0 /*xflags*/, OS_CODE); + s->startpos = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * startpos anyway in write mode, so this initialization is not + * necessary. + */ + } + else + { + check_header(s); /* skip the .gz header */ + s->startpos = (memTell(s->file) - s->stream.avail_in); + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. + */ +gzFile ZEXPORT memgzopen(memory, available, mode) +char *memory; +int available; +const char *mode; +{ + return gz_open(memory, available, mode); +} + +/* =========================================================================== + Read a byte from a mem_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. + */ +local int get_byte(s) +mem_stream *s; +{ + if (s->z_eof) + return EOF; + if (s->stream.avail_in == 0) + { + errno = 0; + s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) + { + s->z_eof = 1; + if (memError(s->file)) + s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a mem_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. + */ +local void check_header(s) +mem_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Check the gzip magic header */ + for (len = 0; len < 2; len++) + { + c = get_byte(s); + if (c != gz_magic[len]) + { + if (len != 0) + s->stream.avail_in++, s->stream.next_in--; + if (c != EOF) + { + s->stream.avail_in++, s->stream.next_in--; + s->transparent = 1; + } + s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; + return; + } + } + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) + { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) + (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) /* skip the extra field */ + { + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) + ; + } + if ((flags & ORIG_NAME) != 0) /* skip the original file name */ + { + while ((c = get_byte(s)) != 0 && c != EOF) + ; + } + if ((flags & COMMENT) != 0) /* skip the .gz file comment */ + { + while ((c = get_byte(s)) != 0 && c != EOF) + ; + } + if ((flags & HEAD_CRC) != 0) /* skip the header crc */ + { + for (len = 0; len < 2; len++) + (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + +/* =========================================================================== + * Cleanup then free the given mem_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy(s) +mem_stream *s; +{ + int err = Z_OK; + + if (!s) + return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) + { + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } + else if (s->mode == 'r') + { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && memClose(s->file)) + { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) + err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). + */ +int ZEXPORT memgzread(file, buf, len) +gzFile file; +voidp buf; +unsigned len; +{ + mem_stream *s = (mem_stream *)file; + Bytef * start = (Bytef *)buf; /* starting point for crc computation */ + Byte * next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') + return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) + return -1; + if (s->z_err == Z_STREAM_END) + return 0; /* EOF */ + + next_out = (Byte *)buf; + s->stream.next_out = (Bytef *)buf; + s->stream.avail_out = len; + + while (s->stream.avail_out != 0) + { + if (s->transparent) + { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) + n = s->stream.avail_out; + if (n > 0) + { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) + { + s->stream.avail_out -= (uInt)memRead(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->stream.total_in += (uLong)len; + s->stream.total_out += (uLong)len; + if (len == 0) + s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) + { + errno = 0; + s->stream.avail_in = (uInt)memRead(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) + { + s->z_eof = 1; + if (memError(s->file)) + { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + + if (s->z_err == Z_STREAM_END) + { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) + { + s->z_err = Z_DATA_ERROR; + } + else + { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may + * be different from s->stream.total_out) in case of + * concatenated .gz files. Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) + { + uLong total_in = s->stream.total_in; + uLong total_out = s->stream.total_out; + + inflateReset(&(s->stream)); + s->stream.total_in = total_in; + s->stream.total_out = total_out; + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) + break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + return (int)(len - s->stream.avail_out); +} + +#ifndef NO_DEFLATE +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). + */ +int ZEXPORT memgzwrite(file, buf, len) +gzFile file; +const voidp buf; +unsigned len; +{ + mem_stream *s = (mem_stream *)file; + + if (s == NULL || s->mode != 'w') + return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef *)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) + { + if (s->stream.avail_out == 0) + { + s->stream.next_out = s->outbuf; + if (memWrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) + { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + if (s->z_err != Z_OK) + break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} +#endif +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. + */ +local int do_flush(file, flush) +gzFile file; +int flush; +{ + uInt len; + int done = 0; + mem_stream *s = (mem_stream *)file; + + if (s == NULL || s->mode != 'w') + return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) + { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) + { + if ((uInt)memWrite(s->outbuf, 1, len, s->file) != len) + { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) + break; + s->z_err = deflate(&(s->stream), flush); + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) + s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) + break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file + */ +local void putLong(file, x) +MEMFILE *file; +uLong x; +{ + int n; + for (n = 0; n < 4; n++) + { + memPutc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given mem_stream. Sets z_err in case + of error. + */ +local uLong getLong(s) +mem_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) + s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. + */ +int ZEXPORT memgzclose(file) +gzFile file; +{ + int err; + mem_stream *s = (mem_stream *)file; + + if (s == NULL) + return Z_STREAM_ERROR; + + if (s->mode == 'w') + { +#ifdef NO_DEFLATE + return Z_STREAM_ERROR; +#else + err = do_flush(file, Z_FINISH); + if (err != Z_OK) + return destroy((mem_stream *)file); + + putLong(s->file, s->crc); + putLong(s->file, s->stream.total_in); +#endif + } + return destroy((mem_stream *)file); +} + +long ZEXPORT memtell(file) +gzFile file; +{ + mem_stream *s = (mem_stream *)file; + + if (s == NULL) + return Z_STREAM_ERROR; + + return memTell(s->file); +} diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/memgzio.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/memgzio.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,23 @@ +#ifndef MEMGZIO_H +#define MEMGZIO_H + +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2002 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_DEFLATE to avoid the compression code. + */ + +/* memgzio.c - IO on .gz files in memory + * Adapted from original gzio.c from zlib library by Forgotten + */ + +#include + +gzFile ZEXPORT memgzopen(char *memory, int available, const char *mode); +int ZEXPORT memgzread(gzFile file, voidp buf, unsigned len); +int ZEXPORT memgzwrite(gzFile file, const voidp buf, unsigned len); +int ZEXPORT memgzclose(gzFile file); +long ZEXPORT memtell(gzFile file); + +#endif // MEMGZIO_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/movie.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/movie.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1772 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +#ifdef HAVE_STRINGS_H +# include +#endif + +#if defined(__unix) || defined(__linux) || defined(__sun) || defined(__DJGPP) +# include +# include +# include +# include +# define stricmp strcasecmp +// FIXME: this is wrong, but we don't want buffer overflow +# if defined _MAX_PATH +# undef _MAX_PATH +//# define _MAX_PATH 128 +# define _MAX_PATH 260 +# endif +#endif + +#ifdef WIN32 +# include +# ifndef W_OK +# define W_OK 2 +# endif +# define ftruncate chsize +#endif + +#include "movie.h" +#include "System.h" +#include "../gba/GBA.h" +#include "../gba/GBAGlobals.h" +#include "../gba/RTC.h" +#include "../gb/GB.h" +#include "../gb/gbGlobals.h" +#include "inputGlobal.h" +#include "unzip.h" +#include "Util.h" + +#include "vbalua.h" + +#if (defined(WIN32) && !defined(SDL)) +# include "../win32/stdafx.h" +# include "../win32/MainWnd.h" +# include "../win32/VBA.h" +# include "../win32/WinMiscUtil.h" +#endif + +extern int emulating; // from system.cpp +extern u16 currentButtons[4]; // from System.cpp +extern u16 lastKeys; + +SMovie Movie; +bool loadingMovie = false; + +// probably bad idea to have so many global variables, but I hate to recompile almost everything after editing VBA.h +bool autoConvertMovieWhenPlaying = false; + +static u16 initialInputs[4] = { 0 }; + +static bool resetSignaled = false; +static bool resetSignaledLast = false; + +static int prevEmulatorType, prevBorder, prevWinBorder, prevBorderAuto; + +// little-endian integer pop/push functions: +static inline uint32 Pop32(const uint8 * &ptr) +{ + uint32 v = (ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24)); + ptr += 4; + return v; +} + +static inline uint16 Pop16(const uint8 * &ptr) /* const version */ +{ + uint16 v = (ptr[0] | (ptr[1] << 8)); + ptr += 2; + return v; +} + +static inline uint16 Pop16(uint8 * &ptr) /* non-const version */ +{ + uint16 v = (ptr[0] | (ptr[1] << 8)); + ptr += 2; + return v; +} + +static inline uint8 Pop8(const uint8 * &ptr) +{ + return *(ptr)++; +} + +static inline void Push32(uint32 v, uint8 * &ptr) +{ + ptr[0] = (uint8)(v & 0xff); + ptr[1] = (uint8)((v >> 8) & 0xff); + ptr[2] = (uint8)((v >> 16) & 0xff); + ptr[3] = (uint8)((v >> 24) & 0xff); + ptr += 4; +} + +static inline void Push16(uint16 v, uint8 * &ptr) +{ + ptr[0] = (uint8)(v & 0xff); + ptr[1] = (uint8)((v >> 8) & 0xff); + ptr += 2; +} + +static inline void Push8(uint8 v, uint8 * &ptr) +{ + *ptr++ = v; +} + +// little-endian integer read/write functions: +static inline uint16 Read16(const uint8 *ptr) +{ + return ptr[0] | (ptr[1] << 8); +} + +static inline void Write16(uint16 v, uint8 *ptr) +{ + ptr[0] = uint8(v & 0xff); + ptr[1] = uint8((v >> 8) & 0xff); +} + +static long file_length(FILE *fp) +{ + long cur_pos = ftell(fp); + fseek(fp, 0, SEEK_END); + long length = ftell(fp); + fseek(fp, cur_pos, SEEK_SET); + return length; +} + +static int bytes_per_frame(SMovie &mov) +{ + int num_controllers = 0; + + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) + if (mov.header.controllerFlags & MOVIE_CONTROLLER(i)) + ++num_controllers; + + return CONTROLLER_DATA_SIZE * num_controllers; +} + +static void reserve_buffer_space(uint32 space_needed) +{ + if (space_needed > Movie.inputBufferSize) + { + uint32 ptr_offset = Movie.inputBufferPtr - Movie.inputBuffer; + uint32 alloc_chunks = (space_needed - 1) / BUFFER_GROWTH_SIZE + 1; + uint32 old_size = Movie.inputBufferSize; + Movie.inputBufferSize = BUFFER_GROWTH_SIZE * alloc_chunks; + Movie.inputBuffer = (uint8 *)realloc(Movie.inputBuffer, Movie.inputBufferSize); + // FIXME: this only fixes the random input problem during dma-frame-skip, but not the skip + memset(Movie.inputBuffer + old_size, 0, Movie.inputBufferSize - old_size); + Movie.inputBufferPtr = Movie.inputBuffer + ptr_offset; + } +} + +static int read_movie_header(FILE *file, SMovie &movie) +{ + assert(file != NULL); + assert(VBM_HEADER_SIZE == sizeof(SMovieFileHeader)); // sanity check on the header type definition + + uint8 headerData [VBM_HEADER_SIZE]; + + if (fread(headerData, 1, VBM_HEADER_SIZE, file) != VBM_HEADER_SIZE) + return MOVIE_WRONG_FORMAT; // if we failed to read in all VBM_HEADER_SIZE bytes of the header + + const uint8 * ptr = headerData; + SMovieFileHeader &header = movie.header; + + header.magic = Pop32(ptr); + if (header.magic != VBM_MAGIC) + return MOVIE_WRONG_FORMAT; + + header.version = Pop32(ptr); + if (header.version != VBM_VERSION) + return MOVIE_WRONG_VERSION; + + header.uid = Pop32(ptr); + header.length_frames = Pop32(ptr) + 1; // HACK: add 1 to the length for compatibility + header.rerecord_count = Pop32(ptr); + + header.startFlags = Pop8(ptr); + header.controllerFlags = Pop8(ptr); + header.typeFlags = Pop8(ptr); + header.optionFlags = Pop8(ptr); + + header.saveType = Pop32(ptr); + header.flashSize = Pop32(ptr); + header.gbEmulatorType = Pop32(ptr); + + for (int i = 0; i < 12; i++) + header.romTitle[i] = Pop8(ptr); + + header.minorVersion = Pop8(ptr); + + header.romCRC = Pop8(ptr); + header.romOrBiosChecksum = Pop16(ptr); + header.romGameCode = Pop32(ptr); + + header.offset_to_savestate = Pop32(ptr); + header.offset_to_controller_data = Pop32(ptr); + + return MOVIE_SUCCESS; +} + +static void write_movie_header(FILE *file, const SMovie &movie) +{ + assert(ftell(file) == 0); // we assume file points to beginning of movie file + + uint8 headerData [VBM_HEADER_SIZE]; + uint8 *ptr = headerData; + const SMovieFileHeader &header = movie.header; + + Push32(header.magic, ptr); + Push32(header.version, ptr); + + Push32(header.uid, ptr); + Push32(header.length_frames - 1, ptr); // HACK: reduce the length by 1 for compatibility with certain faulty old tools + // like TME + Push32(header.rerecord_count, ptr); + + Push8(header.startFlags, ptr); + Push8(header.controllerFlags, ptr); + Push8(header.typeFlags, ptr); + Push8(header.optionFlags, ptr); + + Push32(header.saveType, ptr); + Push32(header.flashSize, ptr); + Push32(header.gbEmulatorType, ptr); + + for (int i = 0; i < 12; ++i) + Push8(header.romTitle[i], ptr); + + Push8(header.minorVersion, ptr); + + Push8(header.romCRC, ptr); + Push16(header.romOrBiosChecksum, ptr); + Push32(header.romGameCode, ptr); + + Push32(header.offset_to_savestate, ptr); + Push32(header.offset_to_controller_data, ptr); + + fwrite(headerData, 1, VBM_HEADER_SIZE, file); +} + +static void flush_movie_header() +{ + assert(Movie.file != 0 && "logical error!"); + if (!Movie.file) + return; + + long originalPos = ftell(Movie.file); + + // (over-)write the header + fseek(Movie.file, 0, SEEK_SET); + write_movie_header(Movie.file, Movie); + + fflush(Movie.file); + + fseek(Movie.file, originalPos, SEEK_SET); +} + +static void flush_movie_frames() +{ + assert(Movie.file && "logical error!"); + if (!Movie.file) + return; + + long originalPos = ftell(Movie.file); + + // overwrite the controller data + fseek(Movie.file, Movie.header.offset_to_controller_data, SEEK_SET); + fwrite(Movie.inputBuffer, 1, Movie.bytesPerFrame * Movie.header.length_frames, Movie.file); + + fflush(Movie.file); + + fseek(Movie.file, originalPos, SEEK_SET); +} + +static void truncate_movie(long length) +{ + // truncate movie to length + // NOTE: it's certain that the savestate block is never after the + // controller data block, because the VBM format decrees it. + + assert(Movie.file && length >= 0); + if (!Movie.file || length < 0) + return; + + assert(Movie.header.offset_to_savestate <= Movie.header.offset_to_controller_data); + if (Movie.header.offset_to_savestate > Movie.header.offset_to_controller_data) + return; + + Movie.header.length_frames = length; + flush_movie_header(); + const long truncLen = long(Movie.header.offset_to_controller_data + Movie.bytesPerFrame * length); + if (file_length(Movie.file) != truncLen) + { + ftruncate(fileno(Movie.file), truncLen); + } +} + +static void remember_input_state() +{ + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) + { + if (systemCartridgeType == 0) + { + initialInputs[i] = u16(~P1 & 0x03FF); + } + else + { + extern int32 gbJoymask[4]; + for (int i = 0; i < 4; ++i) + initialInputs[i] = u16(gbJoymask[i] & 0xFFFF); + } + } +} + +static void change_state(MovieState new_state) +{ +#if (defined(WIN32) && !defined(SDL)) + theApp.frameSearching = false; + theApp.frameSearchSkipping = false; +#endif + + if (new_state == MOVIE_STATE_NONE) + { + Movie.pauseFrame = -1; + + if (Movie.state == MOVIE_STATE_NONE) + return; + + truncate_movie(Movie.header.length_frames); + + fclose(Movie.file); + Movie.file = NULL; + Movie.currentFrame = 0; +#if (defined(WIN32) && !defined(SDL)) + // undo changes to border settings + { + gbBorderOn = prevBorder; + theApp.winGbBorderOn = prevWinBorder; + gbBorderAutomatic = prevBorderAuto; + systemGbBorderOn(); + } +#endif + gbEmulatorType = prevEmulatorType; + + extern int32 gbDMASpeedVersion; + gbDMASpeedVersion = 1; + + extern int32 gbEchoRAMFixOn; + gbEchoRAMFixOn = 1; + + gbNullInputHackTempEnabled = gbNullInputHackEnabled; + + if (Movie.inputBuffer) + { + free(Movie.inputBuffer); + Movie.inputBuffer = NULL; + } + } + else if (new_state == MOVIE_STATE_PLAY) + { + assert(Movie.file); + + // this would cause problems if not dealt with + if (Movie.currentFrame >= Movie.header.length_frames) + { + new_state = MOVIE_STATE_END; + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; + } + } + else if (new_state == MOVIE_STATE_RECORD) + { + assert(Movie.file); + + // this would cause problems if not dealt with + if (Movie.currentFrame > Movie.header.length_frames) + { + new_state = MOVIE_STATE_END; + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * Movie.header.length_frames; + } + + fseek(Movie.file, Movie.header.offset_to_controller_data + Movie.bytesPerFrame * Movie.currentFrame, SEEK_SET); + } + + if (new_state == MOVIE_STATE_END && Movie.state != MOVIE_STATE_END) + { +#if defined(SDL) + systemClearJoypads(); +#endif + systemScreenMessage("Movie end"); + } + + Movie.state = new_state; + + // checking for movie end + bool willPause = false; + + // if the movie's been set to pause at a certain frame + if (Movie.state != MOVIE_STATE_NONE && Movie.pauseFrame >= 0 && Movie.currentFrame == (uint32)Movie.pauseFrame) + { + Movie.pauseFrame = -1; + willPause = true; + } + + if (Movie.state == MOVIE_STATE_END) + { + if (Movie.currentFrame == Movie.header.length_frames) + { +#if (defined(WIN32) && !defined(SDL)) + if (theApp.movieOnEndPause) + { + willPause = true; + } +#else + // SDL FIXME +#endif + +#if (defined(WIN32) && !defined(SDL)) + switch (theApp.movieOnEndBehavior) + { + case 1: + // the old behavior + //VBAMovieRestart(); + break; + case 2: +#else + // SDL FIXME +#endif + if (Movie.RecordedThisSession) + { + // if user has been recording this movie since the last time it started playing, + // they probably don't want the movie to end now during playback, + // so switch back to recording when it reaches the end + VBAMovieSwitchToRecording(); + systemScreenMessage("Recording resumed"); + willPause = true; + } +#if (defined(WIN32) && !defined(SDL)) + break; + case 3: + // keep open + break; + case 0: + // fall through + default: + // close movie + //VBAMovieStop(false); + break; + } +#else + // SDL FIXME +#endif + } +#if 1 + else if (Movie.currentFrame > Movie.header.length_frames) + { +#if (defined(WIN32) && !defined(SDL)) + switch (theApp.movieOnEndBehavior) + { + case 1: + // FIXME: this should be delayed till the current frame ends + VBAMovieRestart(); + break; + case 2: + // nothing + break; + case 3: + // keep open + break; + case 0: + // fall through + default: + // close movie + VBAMovieStop(false); + break; + } +#else + // SDLFIXME +#endif + } +#endif + } // end if (Movie.state == MOVIE_STATE_END) + + if (willPause) + { + systemSetPause(true); + } +} + +void VBAMovieInit() +{ + memset(&Movie, 0, sizeof(Movie)); + Movie.state = MOVIE_STATE_NONE; + Movie.pauseFrame = -1; + + resetSignaled = false; + resetSignaledLast = false; +} + +void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle [12], uint32 &romGameCode, uint16 &checksum, uint8 &crc) +{ + if (systemCartridgeType == 0) // GBA + { + extern u8 *bios, *rom; + memcpy(romTitle, &rom[0xa0], 12); // GBA TITLE + memcpy(&romGameCode, &rom[0xac], 4); // GBA ROM GAME CODE + if ((movieInfo.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0) + checksum = utilCalcBIOSChecksum(bios, 4); // GBA BIOS CHECKSUM + else + checksum = 0; + crc = rom[0xbd]; // GBA ROM CRC + } + else // non-GBA + { + extern u8 *gbRom; + memcpy(romTitle, &gbRom[0x134], 12); // GB TITLE (note this can be 15 but is truncated to 12) + romGameCode = (uint32)gbRom[0x146]; // GB ROM UNIT CODE + + checksum = (gbRom[0x14e] << 8) | gbRom[0x14f]; // GB ROM CHECKSUM, read from big-endian + crc = gbRom[0x14d]; // GB ROM CRC + } +} + +#ifdef SDL +static void GetBatterySaveName(char *buffer) +{ + extern char batteryDir[2048], filename[2048]; // from SDL.cpp + extern char *sdlGetFilename(char *name); // from SDL.cpp + if (batteryDir[0]) + sprintf(buffer, "%s/%s.sav", batteryDir, sdlGetFilename(filename)); + else + sprintf(buffer, "%s.sav", filename); +} + +#endif + +static void SetPlayEmuSettings() +{ + prevEmulatorType = gbEmulatorType; + gbEmulatorType = Movie.header.gbEmulatorType; + +#if (defined(WIN32) && !defined(SDL)) +// theApp.removeIntros = false; + theApp.skipBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; + theApp.useBiosFile = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; +#else + extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp + extern bool8 useBios, skipBios, removeIntros; // from SDL.cpp + useBios = (Movie.header.optionFlags & MOVIE_SETTING_USEBIOSFILE) != 0; + skipBios = (Movie.header.optionFlags & MOVIE_SETTING_SKIPBIOSFILE) != 0; + removeIntros = false /*(Movie.header.optionFlags & MOVIE_SETTING_REMOVEINTROS) != 0*/; +#endif + + extern void SetPrefetchHack(bool); + if (systemCartridgeType == 0) // lag disablement applies only to GBA + SetPrefetchHack((Movie.header.optionFlags & MOVIE_SETTING_LAGHACK) != 0); + + gbNullInputHackTempEnabled = ((Movie.header.optionFlags & MOVIE_SETTING_GBINPUTHACK) != 0); + + // some GB/GBC games depend on the sound rate, so just use the highest one + systemSoundSetQuality(1); + useOldFrameTiming = false; + + extern int32 gbDMASpeedVersion; + if ((Movie.header.optionFlags & MOVIE_SETTING_GBCFF55FIX) != 0) + gbDMASpeedVersion = 1; + else + gbDMASpeedVersion = 0; // old CGB HDMA5 timing was used + + extern int32 gbEchoRAMFixOn; + if ((Movie.header.optionFlags & MOVIE_SETTING_GBECHORAMFIX) != 0) + gbEchoRAMFixOn = 1; + else + gbEchoRAMFixOn = 0; + +#if (defined(WIN32) && !defined(SDL)) + rtcEnable((Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0); + theApp.winSaveType = Movie.header.saveType; + theApp.winFlashSize = Movie.header.flashSize; + + prevBorder = gbBorderOn; + prevWinBorder = theApp.winGbBorderOn; + prevBorderAuto = gbBorderAutomatic; + if ((gbEmulatorType == 2 || gbEmulatorType == 5) + && !theApp.hideMovieBorder) // games played in SGB mode can have a border + { + gbBorderOn = true; + theApp.winGbBorderOn = true; + gbBorderAutomatic = false; + } + else + { + gbBorderOn = false; + theApp.winGbBorderOn = false; + gbBorderAutomatic = false; + if (theApp.hideMovieBorder) + { + theApp.hideMovieBorder = false; + prevBorder = false; // it might be expected behaviour that it stays hidden after the movie + } + } + systemGbBorderOn(); +#else + sdlRtcEnable = (Movie.header.optionFlags & MOVIE_SETTING_RTCENABLE) != 0; + saveType = Movie.header.saveType; + sdlFlashSize = Movie.header.flashSize; +#endif +} + +static void HardResetAndSRAMClear() +{ +#if (defined(WIN32) && !defined(SDL)) + winEraseBatteryFile(); // delete the damn SRAM file and keep it from being resurrected from RAM + MainWnd *temp = ((MainWnd *)theApp.m_pMainWnd); + if (!temp->winFileRun(true)) // restart running the game + { + temp->winFileClose(); + } +#else + char fname [1024]; + GetBatterySaveName(fname); + remove(fname); // delete the damn SRAM file + + // Henceforth, emuCleanUp means "clear out SRAM" + //theEmulator.emuCleanUp(); // keep it from being resurrected from RAM <--This is wrong, it'll deallocate all variables --Felipe + + /// FIXME the correct SDL code to call for a full restart isn't in a function yet + theEmulator.emuReset(false); +#endif +} + +int VBAMovieOpen(const char *filename, bool8 read_only) +{ + loadingMovie = true; + uint8 movieReadOnly = read_only ? 1 : 0; + + FILE * file; + STREAM stream; + int result; + int fn; + + char movie_filename[_MAX_PATH]; +#ifdef WIN32 + _fullpath(movie_filename, filename, _MAX_PATH); +#else + // SDL FIXME: convert to fullpath + strncpy(movie_filename, filename, _MAX_PATH); + movie_filename[_MAX_PATH - 1] = '\0'; +#endif + + if (movie_filename[0] == '\0') + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + + if (!emulating) + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } + +// bool alreadyOpen = (Movie.file != NULL && _stricmp(movie_filename, Movie.filename) == 0); + +// if (alreadyOpen) + change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it + + if (!(file = fopen(movie_filename, "rb+"))) + if (!(file = fopen(movie_filename, "rb"))) + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + //else + // movieReadOnly = 2; // we have to open the movie twice, no need to do this both times + +// if (!alreadyOpen) +// change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one +// +// if (!(file = fopen(movie_filename, "rb+"))) +// if(!(file = fopen(movie_filename, "rb"))) +// {loadingMovie = false; return MOVIE_FILE_NOT_FOUND;} +// else +// movieReadOnly = 2; + + // clear out the current movie + VBAMovieInit(); + + // read header + if ((result = read_movie_header(file, Movie)) != MOVIE_SUCCESS) + { + fclose(file); + { loadingMovie = false; return result; } + } + + // set emulator settings that make the movie more likely to stay synchronized + SetPlayEmuSettings(); + +// extern bool systemLoadBIOS(); +// if (!systemLoadBIOS()) +// { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } + + // read the metadata / author info from file + fread(Movie.authorInfo, 1, MOVIE_METADATA_SIZE, file); + fn = dup(fileno(file)); // XXX: why does this fail?? it returns -1 but errno == 0 + fclose(file); + + // apparently this lseek is necessary + lseek(fn, Movie.header.offset_to_savestate, SEEK_SET); + if (!(stream = utilGzReopen(fn, "rb"))) + if (!(stream = utilGzOpen(movie_filename, "rb"))) + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + else + fn = dup(fileno(file)); + // in case the above dup failed but opening the file normally doesn't fail + + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) + { + // load the snapshot + result = theEmulator.emuReadStateFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; + + // FIXME: Kludge for conversion + remember_input_state(); + } + else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) + { + // 'soft' reset: + theEmulator.emuReset(false); + + // load the SRAM + result = theEmulator.emuReadBatteryFromStream(stream) ? MOVIE_SUCCESS : MOVIE_WRONG_FORMAT; + } + else + { + HardResetAndSRAMClear(); + } + + utilGzClose(stream); + + if (result != MOVIE_SUCCESS) + { loadingMovie = false; return result; } + +// if (!(file = fopen(movie_filename, /*read_only ? "rb" :*/ "rb+"))) // want to be able to switch out of read-only later +// { +// if(!Movie.readOnly || !(file = fopen(movie_filename, "rb"))) // try read-only if failed +// return MOVIE_FILE_NOT_FOUND; +// } + if (!(file = fopen(movie_filename, "rb+"))) + if (!(file = fopen(movie_filename, "rb"))) + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + else + movieReadOnly = 2; + + // recalculate length of movie from the file size + Movie.bytesPerFrame = bytes_per_frame(Movie); + fseek(file, 0, SEEK_END); + long fileSize = ftell(file); + Movie.header.length_frames = (fileSize - Movie.header.offset_to_controller_data) / Movie.bytesPerFrame; + + if (fseek(file, Movie.header.offset_to_controller_data, SEEK_SET)) + { fclose(file); loadingMovie = false; return MOVIE_WRONG_FORMAT; } + + strcpy(Movie.filename, movie_filename); + Movie.file = file; + Movie.inputBufferPtr = Movie.inputBuffer; + Movie.currentFrame = 0; + Movie.readOnly = movieReadOnly; + Movie.RecordedThisSession = false; + + // read controller data + uint32 to_read = Movie.bytesPerFrame * Movie.header.length_frames; + reserve_buffer_space(to_read); + fread(Movie.inputBuffer, 1, to_read, file); + + change_state(MOVIE_STATE_PLAY); + + char messageString[64] = "Movie "; + bool converted = false; + if (autoConvertMovieWhenPlaying) + { + int result = VBAMovieConvertCurrent(); + if (result == MOVIE_SUCCESS) + strcat(messageString, "converted and "); + else if (result == MOVIE_WRONG_VERSION) + strcat(messageString, "higher revision "); + } + + if (Movie.state == MOVIE_STATE_PLAY) + strcat(messageString, "replaying "); + else + strcat(messageString, "finished "); + if (Movie.readOnly) + strcat(messageString, "(read)"); + else + strcat(messageString, "(edit)"); + systemScreenMessage(messageString); + + VBAUpdateButtonPressDisplay(); + VBAUpdateFrameCountDisplay(); + systemRefreshScreen(); + + { loadingMovie = false; return MOVIE_SUCCESS; } +} + +static void SetRecordEmuSettings() +{ + Movie.header.optionFlags = 0; +#if (defined(WIN32) && !defined(SDL)) + if (theApp.useBiosFile) + Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; + if (theApp.skipBiosFile) + Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; + if (rtcIsEnabled()) + Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; + Movie.header.saveType = theApp.winSaveType; + Movie.header.flashSize = theApp.winFlashSize; +#else + extern int saveType, sdlRtcEnable, sdlFlashSize; // from SDL.cpp + extern bool8 useBios, skipBios; // from SDL.cpp + if (useBios) + Movie.header.optionFlags |= MOVIE_SETTING_USEBIOSFILE; + if (skipBios) + Movie.header.optionFlags |= MOVIE_SETTING_SKIPBIOSFILE; + if (sdlRtcEnable) + Movie.header.optionFlags |= MOVIE_SETTING_RTCENABLE; + Movie.header.saveType = saveType; + Movie.header.flashSize = sdlFlashSize; +#endif + prevEmulatorType = Movie.header.gbEmulatorType = gbEmulatorType; + + if (!memLagTempEnabled) + Movie.header.optionFlags |= MOVIE_SETTING_LAGHACK; + + if (gbNullInputHackTempEnabled) + Movie.header.optionFlags |= MOVIE_SETTING_GBINPUTHACK; + + Movie.header.optionFlags |= MOVIE_SETTING_GBCFF55FIX; + extern int32 gbDMASpeedVersion; + gbDMASpeedVersion = 1; + + Movie.header.optionFlags |= MOVIE_SETTING_GBECHORAMFIX; + extern int32 gbEchoRAMFixOn; + gbEchoRAMFixOn = 1; + + // some GB/GBC games depend on the sound rate, so just use the highest one + systemSoundSetQuality(1); + + useOldFrameTiming = false; + +#if (defined(WIN32) && !defined(SDL)) +// theApp.removeIntros = false; + + prevBorder = gbBorderOn; + prevWinBorder = theApp.winGbBorderOn; + prevBorderAuto = gbBorderAutomatic; + if (gbEmulatorType == 2 || gbEmulatorType == 5) // only games played in SGB mode will have a border + { + gbBorderOn = true; + theApp.winGbBorderOn = true; + gbBorderAutomatic = false; + } + else + { + gbBorderOn = false; + theApp.winGbBorderOn = false; + gbBorderAutomatic = false; + } + systemGbBorderOn(); +#else + /// SDLFIXME +#endif +} + +uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly) +{ + if (controllerNum < 0 || controllerNum >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) + return 0; + + return normalOnly ? (currentButtons[controllerNum] & BUTTON_REGULAR_MASK) : currentButtons[controllerNum]; +} + +int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags) +{ + // make sure at least one controller is enabled + if ((controllerFlags & MOVIE_CONTROLLERS_ANY_MASK) == 0) + return MOVIE_WRONG_FORMAT; + + if (!emulating) + return MOVIE_UNKNOWN_ERROR; + + loadingMovie = true; + + FILE * file; + STREAM stream; + int fn; + + char movie_filename [_MAX_PATH]; +#ifdef WIN32 + _fullpath(movie_filename, filename, _MAX_PATH); +#else + // FIXME: convert to fullpath + strncpy(movie_filename, filename, _MAX_PATH); + movie_filename[_MAX_PATH - 1] = '\0'; +#endif + + bool alreadyOpen = (Movie.file != NULL && stricmp(movie_filename, Movie.filename) == 0); + + if (alreadyOpen) + change_state(MOVIE_STATE_NONE); // have to stop current movie before trying to re-open it + + if (movie_filename[0] == '\0') + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + + if (!(file = fopen(movie_filename, "wb"))) + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + + if (!alreadyOpen) + change_state(MOVIE_STATE_NONE); // stop current movie when we're able to open the other one + + // clear out the current movie + VBAMovieInit(); + + // fill in the movie's header + Movie.header.uid = (uint32)time(NULL); + Movie.header.magic = VBM_MAGIC; + Movie.header.version = VBM_VERSION; + Movie.header.rerecord_count = 0; + Movie.header.length_frames = 0; + Movie.header.startFlags = startFlags; + Movie.header.controllerFlags = controllerFlags; + Movie.header.typeFlags = typeFlags; + Movie.header.minorVersion = VBM_REVISION; + + // set emulator settings that make the movie more likely to stay synchronized when it's later played back + SetRecordEmuSettings(); + + // set ROM and BIOS checksums and stuff + VBAMovieGetRomInfo(Movie, Movie.header.romTitle, Movie.header.romGameCode, Movie.header.romOrBiosChecksum, Movie.header.romCRC); + + // write the header to file + write_movie_header(file, Movie); + + // copy over the metadata / author info + VBAMovieSetMetadata(authorInfo); + + // write the metadata / author info to file + fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); + + // write snapshot or SRAM if applicable + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT + || Movie.header.startFlags & MOVIE_START_FROM_SRAM) + { + Movie.header.offset_to_savestate = (uint32)ftell(file); + + // close the file and reopen it as a stream: + + fn = dup(fileno(file)); + fclose(file); + + if (!(stream = utilGzReopen(fn, "ab"))) // append mode to start at end, no seek necessary + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + + // write the save data: + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) + { + // save snapshot + if (!theEmulator.emuWriteStateToStream(stream)) + { + utilGzClose(stream); + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } + } + } + else if (Movie.header.startFlags & MOVIE_START_FROM_SRAM) + { + // save SRAM + if (!theEmulator.emuWriteBatteryToStream(stream)) + { + utilGzClose(stream); + { loadingMovie = false; return MOVIE_UNKNOWN_ERROR; } + } + + // 'soft' reset: + theEmulator.emuReset(false); + } + + utilGzClose(stream); + + // reopen the file and seek back to the end + + if (!(file = fopen(movie_filename, "rb+"))) + { loadingMovie = false; return MOVIE_FILE_NOT_FOUND; } + + fseek(file, 0, SEEK_END); + } + else // no snapshot or SRAM + { + HardResetAndSRAMClear(); + } + + Movie.header.offset_to_controller_data = (uint32)ftell(file); + + strcpy(Movie.filename, movie_filename); + Movie.file = file; + Movie.bytesPerFrame = bytes_per_frame(Movie); + Movie.inputBufferPtr = Movie.inputBuffer; + Movie.currentFrame = 0; + Movie.readOnly = false; + Movie.RecordedThisSession = true; + + change_state(MOVIE_STATE_RECORD); + + systemScreenMessage("Recording movie..."); + { loadingMovie = false; return MOVIE_SUCCESS; } +} + +void VBAUpdateButtonPressDisplay() +{ + uint32 keys = currentButtons[0] & BUTTON_REGULAR_RECORDING_MASK; + + const static char KeyMap[] = { 'A', 'B', 's', 'S', '>', '<', '^', 'v', 'R', 'L', '!', '?', '{', '}', 'v', '^' }; + const static int KeyOrder[] = { 5, 6, 4, 7, 0, 1, 9, 8, 3, 2, 12, 15, 13, 14, 11, 10 }; // < ^ > v A B L R S s { = } _ + // ? ! + char buffer[256]; + sprintf(buffer, " "); + +#ifndef WIN32 + // don't bother color-coding autofire and such + int i; + for (i = 0; i < 15; i++) + { + int j = KeyOrder[i]; + int mask = (1 << (j)); + buffer[strlen(" ") + i] = ((keys & mask) != 0) ? KeyMap[j] : ' '; + } + + systemScreenMessage(buffer, 2, -1); +#else + const bool eraseAll = !theApp.inputDisplay; + uint32 autoHeldKeys = eraseAll ? 0 : theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; + uint32 autoFireKeys = eraseAll ? 0 : (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; + uint32 pressedKeys = eraseAll ? 0 : keys; + + char colorList[64]; + memset(colorList, 1, strlen(buffer)); + + if (!eraseAll) + { + for (int i = 0; i < 15; i++) + { + const int j = KeyOrder[i]; + const int mask = (1 << (j)); + bool pressed = (pressedKeys & mask) != 0; + const bool autoHeld = (autoHeldKeys & mask) != 0; + const bool autoFired = (autoFireKeys & mask) != 0; + const bool erased = (lastKeys & mask) != 0 && (!pressed && !autoHeld && !autoFired); + extern int textMethod; + if (textMethod != 2 && (autoHeld || (autoFired && !pressed) || erased)) + { + int colorNum = 1; // default is white + if (autoHeld) + colorNum += (pressed ? 2 : 1); // yellow if pressed, red if not + else if (autoFired) + colorNum += 5; // blue if autofired and not currently pressed + else if (erased) + colorNum += 8; // black on black + + colorList[strlen(" ") + i] = colorNum; + pressed = true; + } + buffer[strlen(" ") + i] = pressed ? KeyMap[j] : ' '; + } + } + + lastKeys = currentButtons[0]; + lastKeys |= theApp.autoHold & BUTTON_REGULAR_RECORDING_MASK; + lastKeys |= (theApp.autoFire | theApp.autoFire2) & BUTTON_REGULAR_RECORDING_MASK; + + systemScreenMessage(buffer, 2, -1, colorList); +#endif +} + +void VBAUpdateFrameCountDisplay() +{ + const int MAGICAL_NUMBER = 64; // FIXME: this won't do any better, but only to remind you of sz issues + char frameDisplayString[MAGICAL_NUMBER]; + char lagFrameDisplayString[MAGICAL_NUMBER]; + char extraCountDisplayString[MAGICAL_NUMBER]; + +#if (defined(WIN32) && !defined(SDL)) + if (theApp.frameCounter) +#else + /// SDL FIXME +#endif + { + switch (Movie.state) + { + case MOVIE_STATE_PLAY: + case MOVIE_STATE_END: + { + sprintf(frameDisplayString, "%d / %d", Movie.currentFrame, Movie.header.length_frames); + if (!Movie.readOnly) + strcat(frameDisplayString, " (edit)"); + break; + } + case MOVIE_STATE_RECORD: + { + sprintf(frameDisplayString, "%d (record)", Movie.currentFrame); + break; + } + default: + { + sprintf(frameDisplayString, "%d (no movie)", systemCounters.frameCount); + break; + } + } + +#if (defined(WIN32) && !defined(SDL)) + if (theApp.lagCounter) +#else + /// SDL FIXME +#endif + { +// sprintf(lagFrameDisplayString, " %c %d", systemCounters.laggedLast ? '*' : '|', systemCounters.lagCount); + sprintf(lagFrameDisplayString, " | %d%s", systemCounters.lagCount, systemCounters.laggedLast ? " *" : ""); + strcat(frameDisplayString, lagFrameDisplayString); + } + +#if (defined(WIN32) && !defined(SDL)) + if (theApp.extraCounter) +#else + /// SDL FIXME +#endif + { + sprintf(extraCountDisplayString, " | %d", systemCounters.frameCount - systemCounters.extraCount); + strcat(frameDisplayString, extraCountDisplayString); + } + } +#if (defined(WIN32) && !defined(SDL)) + else + { + frameDisplayString[0] = '\0'; + } +#else + /// SDL FIXME +#endif + systemScreenMessage(frameDisplayString, 1, -1); +} + +// this function should only be called once every frame +void VBAMovieUpdateState() +{ + ++Movie.currentFrame; + + if (Movie.state == MOVIE_STATE_PLAY) + { + Movie.inputBufferPtr += Movie.bytesPerFrame; + if (Movie.currentFrame >= Movie.header.length_frames) + { + // the movie ends anyway; what to do next depends on the settings + change_state(MOVIE_STATE_END); + } + } + else if (Movie.state == MOVIE_STATE_RECORD) + { + // use first fseek? + fwrite(Movie.inputBufferPtr, 1, Movie.bytesPerFrame, Movie.file); + Movie.header.length_frames = Movie.currentFrame; + Movie.inputBufferPtr += Movie.bytesPerFrame; + Movie.RecordedThisSession = true; + flush_movie_header(); + } + else if (Movie.state == MOVIE_STATE_END) + { + change_state(MOVIE_STATE_END); + } +} + +void VBAMovieRead(int i, bool /*sensor*/) +{ + if (Movie.state != MOVIE_STATE_PLAY) + return; + + if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) + return; // not a controller we're recognizing + + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) + { + currentButtons[i] = Read16(Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); + } + else + { + currentButtons[i] = 0; // pretend the controller is disconnected + } + + if ((currentButtons[i] & BUTTON_MASK_NEW_RESET) != 0) + resetSignaled = true; +} + +void VBAMovieWrite(int i, bool /*sensor*/) +{ + if (Movie.state != MOVIE_STATE_RECORD) + return; + + if (i < 0 || i >= MOVIE_NUM_OF_POSSIBLE_CONTROLLERS) + return; // not a controller we're recognizing + + reserve_buffer_space((uint32)((Movie.inputBufferPtr - Movie.inputBuffer) + Movie.bytesPerFrame)); + + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) + { + // get the current controller data + uint16 buttonData = currentButtons[i]; + + // mask away the irrelevent bits + buttonData &= BUTTON_REGULAR_MASK | BUTTON_MOTION_MASK; + + // soft-reset "button" for 1 frame if the game is reset while recording + if (resetSignaled) + { + buttonData |= BUTTON_MASK_NEW_RESET; + } + + // backward compatibility kludge + if (resetSignaledLast) + { + buttonData |= BUTTON_MASK_OLD_RESET; + } + + Write16(buttonData, Movie.inputBufferPtr + CONTROLLER_DATA_SIZE * i); + + // and for display + currentButtons[i] = buttonData; + } + else + { + // pretend the controller is disconnected (otherwise input it gives could cause desync since we're not writing it to the + // movie) + currentButtons[i] = 0; + } +} + +void VBAMovieStop(bool8 suppress_message) +{ + if (Movie.state != MOVIE_STATE_NONE) + { + change_state(MOVIE_STATE_NONE); + if (!suppress_message) + systemScreenMessage("Movie stop"); + } +} + +int VBAMovieGetInfo(const char *filename, SMovie *info) +{ + assert(info != NULL); + if (info == NULL) + return -1; + + FILE * file; + int result; + SMovie &local_movie = *info; + + memset(info, 0, sizeof(*info)); + if (filename[0] == '\0') + return MOVIE_FILE_NOT_FOUND; + if (!(file = fopen(filename, "rb"))) + return MOVIE_FILE_NOT_FOUND; + + // read header + if ((result = (read_movie_header(file, local_movie))) != MOVIE_SUCCESS) + { + fclose(file); + return result; + } + + // read the metadata / author info from file + fread(local_movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, file); + + strncpy(local_movie.filename, filename, _MAX_PATH); + local_movie.filename[_MAX_PATH - 1] = '\0'; + + if (Movie.file != NULL && stricmp(local_movie.filename, Movie.filename) == 0) // alreadyOpen + { + local_movie.bytesPerFrame = Movie.bytesPerFrame; + local_movie.header.length_frames = Movie.header.length_frames; + } + else + { + // recalculate length of movie from the file size + local_movie.bytesPerFrame = bytes_per_frame(local_movie); + fseek(file, 0, SEEK_END); + int fileSize = ftell(file); + local_movie.header.length_frames = + (fileSize - local_movie.header.offset_to_controller_data) / local_movie.bytesPerFrame; + } + + fclose(file); + + if (access(filename, W_OK)) + info->readOnly = true; + + return MOVIE_SUCCESS; +} + +bool8 VBAMovieActive() +{ + return (Movie.state != MOVIE_STATE_NONE); +} + +bool8 VBAMovieLoading() +{ + return loadingMovie; +} + +bool8 VBAMoviePlaying() +{ + return (Movie.state == MOVIE_STATE_PLAY); +} + +bool8 VBAMovieRecording() +{ + return (Movie.state == MOVIE_STATE_RECORD); +} + +bool8 VBAMovieReadOnly() +{ + if (!VBAMovieActive()) + return false; + + return Movie.readOnly; +} + +void VBAMovieToggleReadOnly() +{ + if (!VBAMovieActive()) + return; + + if (Movie.readOnly != 2) + { + Movie.readOnly = !Movie.readOnly; + + systemScreenMessage(Movie.readOnly ? "Movie now read-only" : "Movie now editable"); + } + else + { + systemScreenMessage("Can't toggle read-only movie"); + } +} + +uint32 VBAMovieGetVersion() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.header.version; +} + +uint32 VBAMovieGetMinorVersion() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.header.minorVersion; +} + +uint32 VBAMovieGetId() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.header.uid; +} + +uint32 VBAMovieGetLength() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.header.length_frames; +} + +uint32 VBAMovieGetFrameCounter() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.currentFrame; +} + +uint32 VBAMovieGetRerecordCount() +{ + if (!VBAMovieActive()) + return 0; + + return Movie.header.rerecord_count; +} + +uint32 VBAMovieSetRerecordCount(uint32 newRerecordCount) +{ + uint32 oldRerecordCount = 0; + if (!VBAMovieActive()) + return 0; + + oldRerecordCount = Movie.header.rerecord_count; + Movie.header.rerecord_count = newRerecordCount; + return oldRerecordCount; +} + +std::string VBAMovieGetAuthorInfo() +{ + if (!VBAMovieActive()) + return ""; + + return Movie.authorInfo; +} + +std::string VBAMovieGetFilename() +{ + if (!VBAMovieActive()) + return ""; + + return Movie.filename; +} + +void VBAMovieFreeze(uint8 * *buf, uint32 *size) +{ + // sanity check + if (!VBAMovieActive()) + { + return; + } + + *buf = NULL; + *size = 0; + + // compute size needed for the buffer + // room for header.uid, currentFrame, and header.length_frames + uint32 size_needed = sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames); + size_needed += (uint32)(Movie.bytesPerFrame * Movie.header.length_frames); + *buf = new uint8[size_needed]; + *size = size_needed; + + uint8 *ptr = *buf; + if (!ptr) + { + return; + } + + Push32(Movie.header.uid, ptr); + Push32(Movie.currentFrame, ptr); + Push32(Movie.header.length_frames - 1, ptr); // HACK: shorten the length by 1 for backward compatibility + + memcpy(ptr, Movie.inputBuffer, Movie.bytesPerFrame * Movie.header.length_frames); +} + +int VBAMovieUnfreeze(const uint8 *buf, uint32 size) +{ + // sanity check + if (!VBAMovieActive()) + { + return MOVIE_NOT_FROM_A_MOVIE; + } + + const uint8 *ptr = buf; + if (size < sizeof(Movie.header.uid) + sizeof(Movie.currentFrame) + sizeof(Movie.header.length_frames)) + { + return MOVIE_WRONG_FORMAT; + } + + uint32 movie_id = Pop32(ptr); + uint32 current_frame = Pop32(ptr); + uint32 end_frame = Pop32(ptr) + 1; // HACK: restore the length for backward compatibility + uint32 space_needed = Movie.bytesPerFrame * end_frame; + + if (movie_id != Movie.header.uid) + return MOVIE_NOT_FROM_THIS_MOVIE; + + if (space_needed > size) + return MOVIE_WRONG_FORMAT; + + if (Movie.readOnly) + { + // here, we are going to keep the input data from the movie file + // and simply rewind to the currentFrame pointer + // this will cause a desync if the savestate is not in sync // <-- NOT ANYMORE + // with the on-disk recording data, but it's easily solved + // by loading another savestate or playing the movie from the beginning + + // don't allow loading a state inconsistent with the current movie + uint32 length_history = min(current_frame, Movie.header.length_frames); + if (end_frame < length_history) + return MOVIE_SNAPSHOT_INCONSISTENT; + + uint32 space_shared = Movie.bytesPerFrame * length_history; + if (memcmp(Movie.inputBuffer, ptr, space_shared)) + return MOVIE_SNAPSHOT_INCONSISTENT; + + Movie.currentFrame = current_frame; + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); + } + else + { + // here, we are going to take the input data from the savestate + // and make it the input data for the current movie, then continue + // writing new input data at the currentFrame pointer + Movie.currentFrame = current_frame; + Movie.header.length_frames = end_frame; + if (!VBALuaRerecordCountSkip()) + ++Movie.header.rerecord_count; + + Movie.RecordedThisSession = true; + + // do this before calling reserve_buffer_space() + Movie.inputBufferPtr = Movie.inputBuffer + Movie.bytesPerFrame * min(current_frame, Movie.header.length_frames); + reserve_buffer_space(space_needed); + memcpy(Movie.inputBuffer, ptr, space_needed); + + // for consistency, no auto movie conversion here since we don't auto convert the corresponding savestate + flush_movie_header(); + flush_movie_frames(); + } + + change_state(MOVIE_STATE_PLAY); // check for movie end + + // necessary! + resetSignaled = false; + resetSignaledLast = false; + + // necessary to check if there's a reset signal at the previous frame + if (current_frame > 0) + { + const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) + { + if ((Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) && (*(Movie.inputBufferPtr+1- Movie.bytesPerFrame) & NEW_RESET)) + { + resetSignaledLast = true; + break; + } + } + } + + return MOVIE_SUCCESS; +} + +bool VBAMovieEnded() +{ + return (Movie.state == MOVIE_STATE_END); +// return (Movie.state != MOVIE_STATE_NONE && Movie.currentFrame >= Movie.header.length_frames); +} + +bool VBAMovieAllowsRerecording() +{ + bool allows = (Movie.state != MOVIE_STATE_NONE) && (Movie.currentFrame <= Movie.header.length_frames); + return /*!VBAMovieReadOnly() &&*/ allows; +} + +bool VBAMovieSwitchToPlaying() +{ + if (!VBAMovieActive()) + return false; + + if (!Movie.readOnly) + { + VBAMovieToggleReadOnly(); + } + + change_state(MOVIE_STATE_PLAY); + if (Movie.state == MOVIE_STATE_PLAY) + systemScreenMessage("Movie replay (continue)"); + else + systemScreenMessage("Movie end"); + + return true; +} + +bool VBAMovieSwitchToRecording() +{ + if (!VBAMovieAllowsRerecording()) + return false; + + if (Movie.readOnly) + { + VBAMovieToggleReadOnly(); + } + + if (!VBALuaRerecordCountSkip()) + ++Movie.header.rerecord_count; + + change_state(MOVIE_STATE_RECORD); + systemScreenMessage("Movie re-record"); + + //truncate_movie(Movie.currentFrame); + + return true; +} + +uint32 VBAMovieGetState() +{ + // ? + if (!VBAMovieActive()) + return MOVIE_STATE_NONE; + + return Movie.state; +} + +void VBAMovieSignalReset() +{ + if (VBAMovieActive()) + resetSignaled = true; +} + +void VBAMovieResetIfRequested() +{ + if (resetSignaled) + { + theEmulator.emuReset(false); + resetSignaled = false; + resetSignaledLast = true; + } + else + { + resetSignaledLast = false; + } +} + +void VBAMovieSetMetadata(const char *info) +{ + if (!memcmp(Movie.authorInfo, info, MOVIE_METADATA_SIZE)) + return; + + memcpy(Movie.authorInfo, info, MOVIE_METADATA_SIZE); // strncpy would omit post-0 bytes + Movie.authorInfo[MOVIE_METADATA_SIZE - 1] = '\0'; + + if (Movie.file) + { + // (over-)write the header + fseek(Movie.file, 0, SEEK_SET); + write_movie_header(Movie.file, Movie); + + // write the metadata / author info to file + fwrite(Movie.authorInfo, 1, sizeof(char) * MOVIE_METADATA_SIZE, Movie.file); + + fflush(Movie.file); + } +} + +void VBAMovieRestart() +{ + if (VBAMovieActive()) + { + systemSoundClearBuffer(); + + bool8 modified = Movie.RecordedThisSession; + + VBAMovieStop(true); + + char movieName [_MAX_PATH]; + strncpy(movieName, Movie.filename, _MAX_PATH); + movieName[_MAX_PATH - 1] = '\0'; + VBAMovieOpen(movieName, Movie.readOnly); // can't just pass in Movie.filename, since VBAMovieOpen clears out Movie's + // variables + + Movie.RecordedThisSession = modified; + + systemScreenMessage("Movie replay (restart)"); + } +} + +int VBAMovieGetPauseAt() +{ + return Movie.pauseFrame; +} + +void VBAMovieSetPauseAt(int at) +{ + Movie.pauseFrame = at; +} + +/////////////////////// +// movie tools + +// FIXME: is it safe to convert/flush a movie while recording it (considering fseek() problem)? +int VBAMovieConvertCurrent() +{ + if (!VBAMovieActive()) + { + return MOVIE_NOTHING; + } + + if (Movie.header.minorVersion > VBM_REVISION) + { + return MOVIE_WRONG_VERSION; + } + + if (Movie.header.minorVersion == VBM_REVISION) + { + return MOVIE_NOTHING; + } + + Movie.header.minorVersion = VBM_REVISION; + + if (Movie.header.length_frames == 0) // this could happen + { + truncate_movie(0); + return MOVIE_SUCCESS; + } + + // fix movies recorded from snapshots + if (Movie.header.startFlags & MOVIE_START_FROM_SNAPSHOT) + { + uint8 *firstFramePtr = Movie.inputBuffer; + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) + { + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) + { + Push16(initialInputs[i], firstFramePtr); + // note: this is correct since Push16 advances the dest pointer by sizeof u16 + } + } + } + + // convert old resets to new ones + const u8 OLD_RESET = u8(BUTTON_MASK_OLD_RESET >> 8); + const u8 NEW_RESET = u8(BUTTON_MASK_NEW_RESET >> 8); + for (int i = 0; i < MOVIE_NUM_OF_POSSIBLE_CONTROLLERS; ++i) + { + if (Movie.header.controllerFlags & MOVIE_CONTROLLER(i)) + { + uint8 *startPtr = Movie.inputBuffer + sizeof(u16) * i + 1; + uint8 *endPtr = Movie.inputBuffer + Movie.bytesPerFrame * (Movie.header.length_frames - 1); + for (; startPtr < endPtr; startPtr += Movie.bytesPerFrame) + { + if (startPtr[Movie.bytesPerFrame] & OLD_RESET) + { + startPtr[0] |= NEW_RESET; + } + } + } + } + + flush_movie_header(); + flush_movie_frames(); + return MOVIE_SUCCESS; +} + +bool VBAMovieTuncateAtCurrentFrame() +{ + if (!VBAMovieActive()) + return false; + + truncate_movie(Movie.currentFrame); + change_state(MOVIE_STATE_END); + systemScreenMessage("Movie truncated"); + + return true; +} + +bool VBAMovieFixHeader() +{ + if (!VBAMovieActive()) + return false; + + flush_movie_header(); + systemScreenMessage("Movie header fixed"); + return true; +} diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/movie.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/movie.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,190 @@ +#ifndef VBA_MOVIE_H +#define VBA_MOVIE_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include +#include + +#include "../Port.h" + +#define ZLIB +///#ifdef ZLIB +#ifndef WIN32 +#include "zlib.h" +#endif + +#ifndef MOVIE_SUCCESS +# define MOVIE_SUCCESS 1 +# define MOVIE_NOTHING 0 +# define MOVIE_WRONG_FORMAT (-1) +# define MOVIE_WRONG_VERSION (-2) +# define MOVIE_FILE_NOT_FOUND (-3) +# define MOVIE_NOT_FROM_THIS_MOVIE (-4) +# define MOVIE_NOT_FROM_A_MOVIE (-5) +# define MOVIE_SNAPSHOT_INCONSISTENT (-6) +# define MOVIE_UNKNOWN_ERROR (-7) +#endif + +#define VBM_MAGIC (0x1a4D4256) // VBM0x1a +#define VBM_VERSION (1) +#define VBM_HEADER_SIZE (64) +#define CONTROLLER_DATA_SIZE (2) +#define BUFFER_GROWTH_SIZE (4096) +#define MOVIE_METADATA_SIZE (192) +#define MOVIE_METADATA_AUTHOR_SIZE (64) + +// revision 1 uses (?) insted of (!) as reset +#define VBM_REVISION (1) + +#define MOVIE_START_FROM_SNAPSHOT (1<<0) +#define MOVIE_START_FROM_SRAM (1<<1) + +#define MOVIE_CONTROLLER(i) (1<<(i)) +#define MOVIE_CONTROLLERS_ANY_MASK (MOVIE_CONTROLLER(0)|MOVIE_CONTROLLER(1)|MOVIE_CONTROLLER(2)|MOVIE_CONTROLLER(3)) +#define MOVIE_NUM_OF_POSSIBLE_CONTROLLERS (4) + +#define MOVIE_TYPE_GBA (1<<0) +#define MOVIE_TYPE_GBC (1<<1) +#define MOVIE_TYPE_SGB (1<<2) + +#define MOVIE_SETTING_USEBIOSFILE (1<<0) +#define MOVIE_SETTING_SKIPBIOSFILE (1<<1) +#define MOVIE_SETTING_RTCENABLE (1<<2) +#define MOVIE_SETTING_GBINPUTHACK (1<<3) +#define MOVIE_SETTING_LAGHACK (1<<4) +#define MOVIE_SETTING_GBCFF55FIX (1<<5) +#define MOVIE_SETTING_GBECHORAMFIX (1<<6) + +#define STREAM gzFile +/*#define READ_STREAM(p,l,s) gzread (s,p,l) + #define WRITE_STREAM(p,l,s) gzwrite (s,p,l) + #define OPEN_STREAM(f,m) gzopen (f,m) + #define REOPEN_STREAM(f,m) gzdopen (f,m) + #define FIND_STREAM(f) gztell(f) + #define REVERT_STREAM(f,o,s) gzseek(f,o,s) + #define CLOSE_STREAM(s) gzclose (s) + #else + #define STREAM FILE * + #define READ_STREAM(p,l,s) fread (p,1,l,s) + #define WRITE_STREAM(p,l,s) fwrite (p,1,l,s) + #define OPEN_STREAM(f,m) fopen (f,m) + #define REOPEN_STREAM(f,m) fdopen (f,m) + #define FIND_STREAM(f) ftell(f) + #define REVERT_STREAM(f,o,s) fseek(f,o,s) + #define CLOSE_STREAM(s) fclose (s) + #endif*/ + +enum MovieState +{ + MOVIE_STATE_NONE = 0, + MOVIE_STATE_PLAY, + MOVIE_STATE_RECORD, + MOVIE_STATE_END +}; + +struct SMovieFileHeader +{ + uint32 magic; // VBM0x1a + uint32 version; // 1 + int32 uid; // used to match savestates to a particular movie + uint32 length_frames; + uint32 rerecord_count; + uint8 startFlags; + uint8 controllerFlags; + uint8 typeFlags; + uint8 optionFlags; + uint32 saveType; // emulator setting value + uint32 flashSize; // emulator setting value + uint32 gbEmulatorType; // emulator setting value + char romTitle [12]; + uint8 minorVersion; // minor version/revision of the current movie version + uint8 romCRC; // the CRC of the ROM used while recording + uint16 romOrBiosChecksum; // the Checksum of the ROM used while recording, or a CRC of the BIOS if GBA + uint32 romGameCode; // the Game Code of the ROM used while recording, or "\0\0\0\0" if not GBA + uint32 offset_to_savestate; // offset to the savestate or SRAM inside file, set to 0 if unused + uint32 offset_to_controller_data; // offset to the controller data inside file +}; + +struct SMovie +{ + enum MovieState state; + char filename[/*_MAX_PATH*/ 260]; // FIXME: should use a string instead + FILE* file; + uint8 readOnly; + int32 pauseFrame; // FIXME: byte size + + SMovieFileHeader header; + char authorInfo[MOVIE_METADATA_SIZE]; + + uint32 currentFrame; // should == length_frame when recording, and be < length_frames when playing + uint32 bytesPerFrame; + uint8* inputBuffer; + uint32 inputBufferSize; + uint8* inputBufferPtr; + + // bool8 doesn't make much sense if it is meant to solve any portability problem, + // because there's no guarantee that true == 1 and false == 0 (or TRUE == 1 and FALSE == 0) on all platforms. + // while using user-defined boolean types might impact on performance. + // the more reliable (and faster!) way to maintain cross-platform I/O compatibility is + // to manually map from/to built-in boolean types to/from fixed-sized types value by value ONLY when doing I/O + // e.g. bool(true) <-> u8(1) and <-> bool(false) <-> u8(0), BOOL(TRUE) <-> s32(-1) and BOOL(FALSE) <-> s32(0) etc. + bool8 RecordedThisSession; +}; + +// methods used by the user-interface code +int VBAMovieOpen(const char *filename, bool8 read_only); +int VBAMovieCreate(const char *filename, const char *authorInfo, uint8 startFlags, uint8 controllerFlags, uint8 typeFlags); +int VBAMovieGetInfo(const char *filename, SMovie*info); +void VBAMovieGetRomInfo(const SMovie &movieInfo, char romTitle[12], uint32 &romGameCode, uint16 &checksum, uint8 &crc); +void VBAMovieStop(bool8 suppress_message); +const char *VBAChooseMovieFilename(bool8 read_only); + +// methods used by the emulation +void VBAMovieInit(); +void VBAMovieUpdateState(); +void VBAMovieRead(int controllerNum = 0, bool sensor = false); +void VBAMovieWrite(int controllerNum = 0, bool sensor = false); +void VBAUpdateButtonPressDisplay(); +void VBAUpdateFrameCountDisplay(); +//bool8 VBAMovieRewind (uint32 at_frame); +void VBAMovieFreeze(uint8 **buf, uint32 *size); +int VBAMovieUnfreeze(const uint8 *buf, uint32 size); +void VBAMovieRestart(); + +// accessor functions +bool8 VBAMovieActive(); +bool8 VBAMovieLoading(); +bool8 VBAMoviePlaying(); +bool8 VBAMovieRecording(); +// the following accessors return 0/false if !VBAMovieActive() +uint8 VBAMovieReadOnly(); +uint32 VBAMovieGetVersion(); +uint32 VBAMovieGetMinorVersion(); +uint32 VBAMovieGetId(); +uint32 VBAMovieGetLength(); +uint32 VBAMovieGetFrameCounter(); +uint32 VBAMovieGetState(); +uint32 VBAMovieGetRerecordCount (); +uint32 VBAMovieSetRerecordCount (uint32 newRerecordCount); +std::string VBAMovieGetAuthorInfo(); +std::string VBAMovieGetFilename(); + +uint16 VBAMovieGetCurrentInputOf(int controllerNum, bool normalOnly = true); +void VBAMovieSignalReset(); +void VBAMovieResetIfRequested(); +void VBAMovieSetMetadata(const char *info); +void VBAMovieToggleReadOnly(); +bool VBAMovieEnded(); +bool VBAMovieAllowsRerecording(); +bool VBAMovieSwitchToPlaying(); +bool VBAMovieSwitchToRecording(); +int VBAMovieGetPauseAt(); +void VBAMovieSetPauseAt(int at); +int VBAMovieConvertCurrent(); +bool VBAMovieTuncateAtCurrentFrame(); +bool VBAMovieFixHeader(); + +#endif // VBA_MOVIE_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/nesvideos-piece.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/nesvideos-piece.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,525 @@ +#include +#include +#include +#include + +/* Note: This module assumes everyone uses RGB15 as display depth */ + +static std::string VIDEO_CMD = + "mencoder - -o test0.avi" + " -noskip -mc 0" + " -ovc lavc" + " -oac mp3lame" + " -lameopts preset=256:aq=2:mode=3" + " -lavcopts vcodec=ffv1:context=0:format=BGR32:coder=0:vstrict=-1" + " >& mencoder.log"; + +static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length); + +#define BGR24 (0x42475218) // BGR24 fourcc +#define BGR16 (0x42475210) // BGR16 fourcc +#define BGR15 (0x4247520F) // BGR15 fourcc + +static FILE* (*openFunc) (const char*, const char*) = NULL; +static int (*closeFunc) (FILE*) = NULL; + +#if (defined(WIN32) || defined(win32)) // capital is standard, but check for either + #include + #define popen _popen; + #define pclose _pclose; +#endif + +#define u32(n) (n)&255,((n)>>8)&255,((n)>>16)&255,((n)>>24)&255 +#define u16(n) (n)&255,((n)>>8)&255 +#define s4(s) s[0],s[1],s[2],s[3] + +static const unsigned FPS_SCALE = (0x1000000); + +// general-purpose A/V sync debugging, ignored unless explicitly enabled with NESVideoEnableDebugging +static void (*debugVideoMessageFunc)(const char *msg) = NULL; +static void (*debugAudioMessageFunc)(const char *msg) = NULL; +// logo adds 1 "frame" to audio, so offset that (A/V frames shouldn't necessarily match up depending on the rates, but should at least make them start out matching in case they do) +static unsigned audioFramesWritten=0, videoFramesWritten=1; +static double audioSecondsWritten=0, videoSecondsWritten=0; + + +static class AVI +{ + FILE* avifp; + + bool KnowVideo; + unsigned width; + unsigned height; + unsigned fps_scaled; + std::vector VideoBuffer; + + bool KnowAudio; + unsigned rate; + unsigned chans; + unsigned bits; + std::vector AudioBuffer; + +public: + AVI() : + avifp(NULL), + KnowVideo(false), + KnowAudio(false) + { + } + ~AVI() + { + if(avifp) closeFunc(avifp); + } + + void Audio(unsigned r,unsigned b,unsigned c, + const unsigned char*d, unsigned nsamples) + { + if(!KnowAudio) + { + rate = r; + chans = c; + bits = b; + KnowAudio = true; + CheckFlushing(); + } + unsigned bytes = nsamples*chans*(bits/8); + + if(debugAudioMessageFunc) + { + audioFramesWritten++; + audioSecondsWritten += (double)nsamples / (double)rate; // += bytes times seconds per byte + char temp [64]; + sprintf(temp, "A: %.2lf s, %d f", audioSecondsWritten, audioFramesWritten); + debugAudioMessageFunc(temp); + } + + if(KnowVideo) + SendAudioFrame(d, bytes); + else + { + AudioBuffer.insert(AudioBuffer.end(), d, d+bytes); + fprintf(stderr, "Buffering %u bytes of audio\n", bytes); + } + } + void Video(unsigned w,unsigned h,unsigned f, const unsigned char*d) + { + if(!KnowVideo) + { + width=w; + height=h; + fps_scaled=f; + KnowVideo = true; + CheckFlushing(); + } + + unsigned bytes = width*height*2; + + //std::vector tmp(bytes, 'k'); + //d = &tmp[0]; + + if(debugVideoMessageFunc) + { + videoFramesWritten++; + videoSecondsWritten += (double)FPS_SCALE / (double)fps_scaled; // += seconds per frame + char temp [64]; + sprintf(temp, "V: %.2lf s, %d f", videoSecondsWritten, videoFramesWritten); + debugVideoMessageFunc(temp); + } + + if(KnowAudio) + SendVideoFrame(d, bytes); + else + { + VideoBuffer.insert(VideoBuffer.end(), d, d+bytes); + fprintf(stderr, "Buffering %u bytes of video\n", bytes); + } + } + +private: + void CheckFlushing() + { + //AudioBuffer.clear(); + //VideoBuffer.clear(); + + if(KnowAudio && KnowVideo) + { + unsigned last_offs; + + // Flush Audio + + last_offs = 0; + while(last_offs < AudioBuffer.size()) + { + unsigned bytes = rate / (fps_scaled / FPS_SCALE); + bytes *= chans*(bits/8); + + unsigned remain = AudioBuffer.size() - last_offs; + if(bytes > remain) bytes = remain; + if(!bytes) break; + + unsigned begin = last_offs; + last_offs += bytes; + SendAudioFrame(&AudioBuffer[begin], bytes); + } + AudioBuffer.erase(AudioBuffer.begin(), AudioBuffer.begin()+last_offs); + + // Flush Video + + last_offs = 0; + while(last_offs < VideoBuffer.size()) + { + unsigned bytes = width*height*2; + unsigned remain = VideoBuffer.size() - last_offs; + if(bytes > remain) bytes = remain; + if(!bytes)break; + + unsigned begin = last_offs; + last_offs += bytes; + SendVideoFrame(&VideoBuffer[begin], bytes); + } + VideoBuffer.erase(VideoBuffer.begin(), VideoBuffer.begin()+last_offs); + } + } + + void SendVideoFrame(const unsigned char* vidbuf, unsigned framesize) + { + CheckBegin(); + + //fprintf(stderr, "Writing 00dc of %u bytes\n", framesize); + + const unsigned char header[] = { s4("00dc"), u32(framesize) }; + FlushWrite(avifp, header, sizeof(header)); + FlushWrite(avifp, vidbuf, framesize); + } + + void SendAudioFrame(const unsigned char* audbuf, unsigned framesize) + { + CheckBegin(); + + //fprintf(stderr, "Writing 01wb of %u bytes\n", framesize); + + const unsigned char header[] = { s4("01wb"), u32(framesize) }; + FlushWrite(avifp, header, sizeof(header)); + FlushWrite(avifp, audbuf, framesize); + } + + void CheckBegin() + { + if(avifp) return; + + if(!openFunc) openFunc = popen; // default + if(!closeFunc) closeFunc = pclose; // default + + avifp = openFunc(VIDEO_CMD.c_str(), "wb"); + if(!avifp) return; + + const unsigned fourcc = BGR16; + const unsigned framesize = width*height*2; + + const unsigned aud_rate = rate; + const unsigned aud_chans = chans; + const unsigned aud_bits = bits; + + const unsigned nframes = 0; //unknown + const unsigned scale = FPS_SCALE; + const unsigned scaled_fps = fps_scaled; + + const unsigned SIZE_strh_vids = 4 + 4*2 + 2*2 + 8*4 + 2*4; + const unsigned SIZE_strf_vids = 4*3 + 2*2 + 4*6; + const unsigned SIZE_strl_vids = 4+ 4+(4+SIZE_strh_vids) + 4+(4+SIZE_strf_vids); + + const unsigned SIZE_strh_auds = 4 + 4*3 + 2*2 + 4*8 + 2*4; + const unsigned SIZE_strf_auds = 2*2 + 4*2 + 2*3; + const unsigned SIZE_strl_auds = 4+ 4+(4+SIZE_strh_auds) + 4+(4+SIZE_strf_auds); + + const unsigned SIZE_avih = 4*12; + const unsigned SIZE_hdrl = 4+4+ (4+SIZE_avih) + 4 + (4+SIZE_strl_vids) + 4 + (4+SIZE_strl_auds); + const unsigned SIZE_movi = 4 + nframes*(4+4+framesize); + const unsigned SIZE_avi = 4+4+ (4+SIZE_hdrl) + 4 + (4+SIZE_movi); + + const unsigned char AVIheader[] = + { + s4("RIFF"), + u32(SIZE_avi), + s4("AVI "), + + // HEADER + + s4("LIST"), + u32(SIZE_hdrl), + s4("hdrl"), + + s4("avih"), + u32(SIZE_avih), + u32(0), + u32(0), + u32(0), + u32(0), + u32(nframes), + u32(0), + u32(2), // two streams + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + + // VIDEO HEADER + + s4("LIST"), + u32(SIZE_strl_vids), + s4("strl"), + + s4("strh"), + u32(SIZE_strh_vids), + s4("vids"), + u32(0), + u32(0), + u16(0), + u16(0), + u32(0), + u32(scale), + u32(scaled_fps), + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + u16(0), + u16(0), + u16(0), + u16(0), + + s4("strf"), + u32(SIZE_strf_vids), + u32(0), + u32(width), + u32(height), + u16(0), + u16(0), + u32(fourcc), + u32(0), + u32(0), + u32(0), + u32(0), + u32(0), + + // AUDIO HEADER + + s4("LIST"), + u32(SIZE_strl_auds), + s4("strl"), + + s4("strh"), + u32(SIZE_strh_auds), + s4("auds"), + u32(0), //fourcc + u32(0), //handler + u32(0), //flags + u16(0), //prio + u16(0), //lang + u32(0), //init frames + u32(1), //scale + u32(aud_rate), + u32(0), //start + u32(0), //rate*length + u32(1048576), //suggested bufsize + u32(0), //quality + u32(aud_chans * (aud_bits / 8)), //sample size + u16(0), //frame size + u16(0), + u16(0), + u16(0), + + s4("strf"), + u32(SIZE_strf_auds), + u16(1), // pcm format + u16(aud_chans), + u32(aud_rate), + u32(aud_rate * aud_chans * (aud_bits/8)), // samples per second + u16(aud_chans * (aud_bits/8)), //block align + u16(aud_bits), //bits + u16(0), //cbSize + + // MOVIE + + s4("LIST"), + u32(SIZE_movi), + s4("movi") + }; + + FlushWrite(avifp, AVIheader, sizeof(AVIheader)); + } +} AVI; + +extern "C" +{ + int LoggingEnabled = 0; /* 0=no, 1=yes, 2=recording! */ + + const char* NESVideoGetVideoCmd() + { + return VIDEO_CMD.c_str(); + } + void NESVideoSetVideoCmd(const char *cmd) + { + VIDEO_CMD = cmd; + } + void NESVideoEnableDebugging( void videoMessageFunc(const char *msg), void audioMessageFunc(const char *msg) ) + { + debugVideoMessageFunc = videoMessageFunc; + debugAudioMessageFunc = audioMessageFunc; + } + void NESVideoSetFileFuncs( FILE* open(const char *,const char *), int close(FILE*) ) + { + openFunc = open; + closeFunc = close; + } + + void NESVideoLoggingVideo + (const void*data, unsigned width,unsigned height, + unsigned fps_scaled + ) + { + if(LoggingEnabled < 2) return; + + unsigned LogoFrames = fps_scaled >> 24; + + static bool First = true; + if(First) + { + First=false; + /* Bisqwit's logo addition routine. */ + /* If you don't have his files, this function does nothing + * and it does not matter at all. + */ + + const char *background = + width==320 ? "logo320_240" + : width==160 ? "logo160_144" + : width==240 ? "logo240_160" + : height>224 ? "logo256_240" + : "logo256_224"; + + /* Note: This should be 1 second long. */ + for(unsigned frame = 0; frame < LogoFrames; ++frame) + { + char Buf[4096]; + sprintf(Buf, "/shares/home/bisqwit/povray/nesvlogo/%s_f%u.tga", + background, frame); + + FILE*fp = fopen(Buf, "rb"); + if(!fp) // write blackness when missing frames to keep the intro 1 second long: + { + unsigned bytes = width*height*2; + unsigned char* buf = (unsigned char*)malloc(bytes); + if(buf) + { + memset(buf,0,bytes); + AVI.Video(width,height,fps_scaled, buf); + if(debugVideoMessageFunc) videoFramesWritten--; + free(buf); + } + } + else // write 1 frame of the logo: + { + int idlen = fgetc(fp); + /* Silently ignore all other header data. + * These files are assumed to be uncompressed BGR24 tga files with Y swapped. + * Even their geometry is assumed to match perfectly. + */ + fseek(fp, 1+1+2+2+1+ /*org*/2+2+ /*geo*/2+2+ 1+1+idlen, SEEK_CUR); + + bool yflip=true; + std::vector data(width*height*3); + for(unsigned y=height; y-->0; ) + fread(&data[y*width*3], 1, width*3, fp); + fclose(fp); + + std::vector result(width*height); + for(unsigned pos=0, max=result.size(); pos 0) + { + unsigned bytes = n*chans*(bits/8); + unsigned char* buf = (unsigned char*)malloc(bytes); + if(buf) + { + memset(buf,0,bytes); + AVI.Audio(rate,bits,chans, buf, n); + free(buf); + } + } + } + + AVI.Audio(rate,bits,chans, (const unsigned char*) data, nsamples); + } +} /* extern "C" */ + + + +static void FlushWrite(FILE* fp, const unsigned char*buf, unsigned length) +{ +/// unsigned failures = 0; +/// const static int FAILURE_THRESH = 8092; // don't want to loop infinitely if we keep failing to make progress - actually maybe you would want this, so the checking is disabled + while(length > 0 /*&& failures < FAILURE_THRESH*/) + { + unsigned written = fwrite(buf, 1, length, fp); +/// if(written == 0) +/// failures++; +/// else +/// { + length -= written; + buf += written; +/// failures = 0; +/// } + } +/// if(failures >= FAILURE_THRESH) +/// { +/// fprintf(stderr, "FlushWrite() failed to write %d bytes %d times - giving up.", length, failures); +/// LoggingEnabled = 0; +/// } +} + +// for the UB tech +#undef BGR24 +#undef BGR16 +#undef BGR15 + +#undef u32 +#undef u16 +#undef s4 diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/nesvideos-piece.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/nesvideos-piece.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,48 @@ +#ifndef NESVPIECEhh +#define NESVPIECEhh + +#define NESVIDEOS_LOGGING 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Is video logging enabled? 0=no, 1=yes, 2=active. Default value: 0 */ +extern int LoggingEnabled; + +/* Get and set the video recording command (shell command) */ +extern const char* NESVideoGetVideoCmd(); +extern void NESVideoSetVideoCmd(const char *cmd); + +/* Tells to use these functions for obtaining/releasing FILE pointers for writing - if not specified, popen/pclose are used. */ +extern void NESVideoSetFileFuncs( FILE* openFunc(const char *,const char *), int closeFunc(FILE*) ); + +/* Tells to call these functions per frame with amounts (seconds and frames) of video and audio progress */ +extern void NESVideoEnableDebugging( void videoMessageFunc(const char *msg), void audioMessageFunc(const char *msg) ); + +/* Save 1 frame of video. (Assumed to be 16-bit RGB) */ +/* FPS is scaled by 24 bits (*0x1000000) */ +/* Does not do anything if LoggingEnabled<2. */ +extern void NESVideoLoggingVideo + (const void*data, unsigned width, unsigned height, + unsigned fps_scaled); + +/* Save N bytes of audio. bytes_per_second is required on the first call. */ +/* Does not do anything if LoggingEnabled<2. */ +/* The interval of calling this function is not important, as long as all the audio + * data is eventually written without too big delay (5 seconds is too big) + * This function may be called multiple times per video frame, or once per a few video + * frames, or anything in between. Just that all audio data must be written exactly once, + * and in order. */ +extern void NESVideoLoggingAudio + (const void*data, + unsigned rate, unsigned bits, unsigned chans, + unsigned nsamples); +/* nsamples*chans*(bits/8) = bytes in *data. */ +/* rate*chans*(bits/8) = bytes per second. */ + +#ifdef __cplusplus +} +#endif + +#endif diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/unzip.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/unzip.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1208 @@ +/* unzip.c -- IO on .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Read unzip.h for more info + */ + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef NO_ERRNO_H +extern int errno; +#else +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) && \ + !defined(CASESENSITIVITYDEFAULT_NO) +#define CASESENSITIVITYDEFAULT_NO +#endif + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) \ + free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +const char unz_copyright[] = + " unzip 0.15 Copyright 1998 Gilles Vollant "; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile; /* relative offset of local header 4 bytes */ +} unz_file_info_internal; + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char * read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed; /*number of byte to be obtained after decomp*/ + FILE* file; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx)*/ +} file_in_zip_read_info_s; + +/* unz_s contain internal information about the zipfile + */ +typedef struct +{ + FILE*file; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s*pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ +} unz_s; + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. + */ + +local int unzlocal_getByte(FILE *fin, int *pi) +{ + unsigned char c; + int err = fread(&c, 1, 1, fin); + if (err == 1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ferror(fin)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets + */ +local int unzlocal_getShort(FILE *fin, uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin, &i); + x = (uLong)i; + + if (err == UNZ_OK) + err = unzlocal_getByte(fin, &i); + x += ((uLong)i)<<8; + + if (err == UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong(FILE *fin, uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin, &i); + x = (uLong)i; + + if (err == UNZ_OK) + err = unzlocal_getByte(fin, &i); + x += ((uLong)i)<<8; + + if (err == UNZ_OK) + err = unzlocal_getByte(fin, &i); + x += ((uLong)i)<<16; + + if (err == UNZ_OK) + err = unzlocal_getByte(fin, &i); + x += ((uLong)i)<<24; + + if (err == UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal(const char *fileName1, + const char *fileName2) +{ + for (;;) + { + char c1 = *(fileName1++); + char c2 = *(fileName2++); + if ((c1 >= 'a') && (c1 <= 'z')) + c1 -= 0x20; + if ((c2 >= 'a') && (c2 <= 'z')) + c2 -= 0x20; + if (c1 == '\0') + return ((c2 == '\0') ? 0 : -1); + if (c2 == '\0') + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } +} + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + + */ +extern int ZEXPORT unzStringFileNameCompare(const char *fileName1, + const char *fileName2, + int iCaseSensitivity) +{ + if (iCaseSensitivity == 0) + iCaseSensitivity = CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity == 1) + return strcmp(fileName1, fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1, fileName2); +} + +#define BUFREADCOMMENT (0x400) + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) + */ +local uLong unzlocal_SearchCentralDir(FILE *fin) +{ + unsigned char*buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack = 0xffff; /* maximum size of global comment */ + uLong uPosFound = 0; + + if (fseek(fin, 0, SEEK_END) != 0) + return 0; + + uSizeFile = ftell(fin); + + if (uMaxBack > uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char *)ALLOC(BUFREADCOMMENT+4); + if (buf == NULL) + return 0; + + uBackRead = 4; + while (uBackRead < uMaxBack) + { + uLong uReadSize, uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT > uMaxBack) + uBackRead = uMaxBack; + else + uBackRead += BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (fseek(fin, uReadPos, SEEK_SET) != 0) + break; + + if (fread(buf, (uInt)uReadSize, 1, fin) != 1) + break; + + for (i = (int)uReadSize-3; (i--) > 0;) + if (((*(buf+i)) == 0x50) && ((*(buf+i+1)) == 0x4b) && + ((*(buf+i+2)) == 0x05) && ((*(buf+i+3)) == 0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound != 0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib109.zip" or on an Unix computer + "zlib/zlib109.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + */ +extern unzFile ZEXPORT unzOpen(const char *path) +{ + unz_s us; + unz_s *s; + uLong central_pos, uL; + FILE * fin ; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err = UNZ_OK; + + if (unz_copyright[0] != ' ') + return NULL; + + fin = fopen(path, "rb"); + if (fin == NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos == 0) + err = UNZ_ERRNO; + + if (fseek(fin, central_pos, SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(fin, &uL) != UNZ_OK) + err = UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(fin, &number_disk) != UNZ_OK) + err = UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(fin, &number_disk_with_CD) != UNZ_OK) + err = UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(fin, &us.gi.number_entry) != UNZ_OK) + err = UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(fin, &number_entry_CD) != UNZ_OK) + err = UNZ_ERRNO; + + if ((number_entry_CD != us.gi.number_entry) || + (number_disk_with_CD != 0) || + (number_disk != 0)) + err = UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(fin, &us.size_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(fin, &us.offset_central_dir) != UNZ_OK) + err = UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(fin, &us.gi.size_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if ((central_pos < us.offset_central_dir+us.size_central_dir) && + (err == UNZ_OK)) + err = UNZ_BADZIPFILE; + + if (err != UNZ_OK) + { + fclose(fin); + return NULL; + } + + us.file = fin; + us.byte_before_the_zipfile = central_pos - + (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + + s = (unz_s *)ALLOC(sizeof(unz_s)); + *s = us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose(unzFile file) +{ + unz_s*s; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + fclose(s->file); + TRYFREE(s); + return UNZ_OK; +} + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo(unzFile file, + unz_global_info *pglobal_info) +{ + unz_s*s; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + *pglobal_info = s->gi; + return UNZ_OK; +} + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) + */ +local void unzlocal_DosDateToTmuDate(uLong ulDosDate, tm_unz *ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info + */ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal(unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err = UNZ_OK; + uLong uMagic; + long lSeek = 0; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + if (fseek(s->file, s->pos_in_central_dir+s->byte_before_the_zipfile, SEEK_SET) != 0) + err = UNZ_ERRNO; + + /* we check the magic */ + if (err == UNZ_OK) + if (unzlocal_getLong(s->file, &uMagic) != UNZ_OK) + err = UNZ_ERRNO; + else if (uMagic != 0x02014b50) + err = UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file, &file_info.version) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.version_needed) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.flag) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.compression_method) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &file_info.dosDate) != UNZ_OK) + err = UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate, &file_info.tmu_date); + + if (unzlocal_getLong(s->file, &file_info.crc) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &file_info.compressed_size) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &file_info.uncompressed_size) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.size_filename) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.size_file_extra) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.size_file_comment) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.disk_num_start) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &file_info.internal_fa) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &file_info.external_fa) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &file_info_internal.offset_curfile) != UNZ_OK) + err = UNZ_ERRNO; + + lSeek += file_info.size_filename; + if ((err == UNZ_OK) && (szFileName != NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename < fileNameBufferSize) + { + *(szFileName+file_info.size_filename) = '\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename > 0) && (fileNameBufferSize > 0)) + if (fread(szFileName, (uInt)uSizeRead, 1, s->file) != 1) + err = UNZ_ERRNO; + lSeek -= uSizeRead; + } + + if ((err == UNZ_OK) && (extraField != NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extra < extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek != 0) + if (fseek(s->file, lSeek, SEEK_CUR) == 0) + lSeek = 0; + else + err = UNZ_ERRNO; + if ((file_info.size_file_extra > 0) && (extraFieldBufferSize > 0)) + if (fread(extraField, (uInt)uSizeRead, 1, s->file) != 1) + err = UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + if ((err == UNZ_OK) && (szComment != NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment < commentBufferSize) + { + *(szComment+file_info.size_file_comment) = '\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (lSeek != 0) + if (fseek(s->file, lSeek, SEEK_CUR) == 0) + lSeek = 0; + else + err = UNZ_ERRNO; + if ((file_info.size_file_comment > 0) && (commentBufferSize > 0)) + if (fread(szComment, (uInt)uSizeRead, 1, s->file) != 1) + err = UNZ_ERRNO; + lSeek += file_info.size_file_comment - uSizeRead; + } + else + lSeek += file_info.size_file_comment; + + if ((err == UNZ_OK) && (pfile_info != NULL)) + *pfile_info = file_info; + + if ((err == UNZ_OK) && (pfile_info_internal != NULL)) + *pfile_info_internal = file_info_internal; + + return err; +} + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. + */ +extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + return unzlocal_GetCurrentFileInfoInternal(file, pfile_info, NULL, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem + */ +extern int ZEXPORT unzGoToFirstFile(unzFile file) +{ + int err = UNZ_OK; + unz_s*s; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + s->pos_in_central_dir = s->offset_central_dir; + s->num_file = 0; + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. + */ +extern int ZEXPORT unzGoToNextFile(unzFile file) +{ + unz_s*s; + int err; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1 == s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file, &s->cur_file_info, + &s->cur_file_info_internal, + NULL, 0, NULL, 0, NULL, 0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found + */ +extern int ZEXPORT unzLocateFile(unzFile file, + const char *szFileName, + int iCaseSensitivity) +{ + unz_s*s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file == NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName) >= UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s = (unz_s *)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file, NULL, + szCurrentFileName, sizeof(szCurrentFileName)-1, + NULL, 0, NULL, 0); + if (unzStringFileNameCompare(szCurrentFileName, + szFileName, iCaseSensitivity) == 0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) + */ +local int unzlocal_CheckCurrentFileCoherencyHeader(unz_s *s, + uInt *piSizeVar, + uLong *poffset_local_extrafield, + uInt *psize_local_extrafield) +{ + uLong uMagic, uData, uFlags; + uLong size_filename; + uLong size_extra_field; + int err = UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (fseek(s->file, s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile, SEEK_SET) != 0) + return UNZ_ERRNO; + + if (err == UNZ_OK) + if (unzlocal_getLong(s->file, &uMagic) != UNZ_OK) + err = UNZ_ERRNO; + else if (uMagic != 0x04034b50) + err = UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file, &uData) != UNZ_OK) + err = UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; + */ + if (unzlocal_getShort(s->file, &uFlags) != UNZ_OK) + err = UNZ_ERRNO; + + if (unzlocal_getShort(s->file, &uData) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.compression_method)) + err = UNZ_BADZIPFILE; + + if ((err == UNZ_OK) && (s->cur_file_info.compression_method != 0) && + (s->cur_file_info.compression_method != Z_DEFLATED)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file, &uData) != UNZ_OK) /* date/time */ + err = UNZ_ERRNO; + + if (unzlocal_getLong(s->file, &uData) != UNZ_OK) /* crc */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.crc) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file, &uData) != UNZ_OK) /* size compr */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.compressed_size) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file, &uData) != UNZ_OK) /* size uncompr */ + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (uData != s->cur_file_info.uncompressed_size) && + ((uFlags & 8) == 0)) + err = UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file, &size_filename) != UNZ_OK) + err = UNZ_ERRNO; + else if ((err == UNZ_OK) && (size_filename != s->cur_file_info.size_filename)) + err = UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file, &size_extra_field) != UNZ_OK) + err = UNZ_ERRNO; + *poffset_local_extrafield = s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. + */ +extern int ZEXPORT unzOpenCurrentFile(unzFile file) +{ + int err = UNZ_OK; + int Store; + uInt iSizeVar; + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s, &iSizeVar, + &offset_local_extrafield, &size_local_extrafield) != UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s *) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info == NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer = (char *)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield = 0; + + if (pfile_in_zip_read_info->read_buffer == NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised = 0; + + if ((s->cur_file_info.compression_method != 0) && + (s->cur_file_info.compression_method != Z_DEFLATED)) + err = UNZ_BADZIPFILE; + Store = s->cur_file_info.compression_method == 0; + + pfile_in_zip_read_info->crc32_wait = s->cur_file_info.crc; + pfile_in_zip_read_info->crc32 = 0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file = s->file; + pfile_in_zip_read_info->byte_before_the_zipfile = s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err = inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised = 1; + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) + */ +extern int ZEXPORT unzReadCurrentFile(unzFile file, voidp buf, unsigned len) +{ + int err = UNZ_OK; + uInt iRead = 0; + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len == 0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef *)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len > pfile_in_zip_read_info->rest_read_uncompressed) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + while (pfile_in_zip_read_info->stream.avail_out > 0) + { + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed > 0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed < uReadThis) + uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, SEEK_SET) != 0) + return UNZ_ERRNO; + if (fread(pfile_in_zip_read_info->read_buffer, uReadThis, 1, + pfile_in_zip_read_info->file) != 1) + return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed -= uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef *)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method == 0) + { + uInt uDoCopy, i ; + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i = 0; i < uDoCopy; i++) + *(pfile_in_zip_read_info->stream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed -= uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore, uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush = Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err = inflate(&pfile_in_zip_read_info->stream, flush); + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32, bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err == Z_STREAM_END) + return (iRead == 0) ? UNZ_EOF : iRead; + if (err != Z_OK) + break; + } + } + + if (err == Z_OK) + return iRead; + return err; +} + +/* + Give the current position in uncompressed data + */ +extern z_off_t ZEXPORT unztell(unzFile file) +{ + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +/* + return 1 if the end of file was reached, 0 elsewhere + */ +extern int ZEXPORT unzeof(unzFile file) +{ + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code + */ +extern int ZEXPORT unzGetLocalExtrafield(unzFile file, voidp buf, unsigned len) +{ + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf == NULL) + return (int)size_to_read; + + if (len > size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now == 0) + return 0; + + if (fseek(pfile_in_zip_read_info->file, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, SEEK_SET) != 0) + return UNZ_ERRNO; + + if (fread(buf, (uInt)size_to_read, 1, pfile_in_zip_read_info->file) != 1) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good + */ +extern int ZEXPORT unzCloseCurrentFile(unzFile file) +{ + int err = UNZ_OK; + + unz_s*s; + file_in_zip_read_info_s*pfile_in_zip_read_info; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + pfile_in_zip_read_info = s->pfile_in_zip_read; + + if (pfile_in_zip_read_info == NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err = UNZ_CRCERROR; + } + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read = NULL; + + return err; +} + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 + */ +extern int ZEXPORT unzGetGlobalComment(unzFile file, + char *szComment, + uLong uSizeBuf) +{ + //int err=UNZ_OK; + unz_s*s; + uLong uReadThis ; + if (file == NULL) + return UNZ_PARAMERROR; + s = (unz_s *)file; + + uReadThis = uSizeBuf; + if (uReadThis > s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (fseek(s->file, s->central_pos+22, SEEK_SET) != 0) + return UNZ_ERRNO; + + if (uReadThis > 0) + { + *szComment = '\0'; + if (fread(szComment, (uInt)uReadThis, 1, s->file) != 1) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment) = '\0'; + return (int)uReadThis; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/unzip.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/unzip.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,275 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 0.15 beta, Mar 19th, 1998, + + Copyright (C) 1998 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. + Old compressions used by old PKZip 1.x are not supported + + THIS IS AN ALPHA VERSION. AT THIS STAGE OF DEVELOPPEMENT, SOMES API OR STRUCTURE + CAN CHANGE IN FUTURE VERSION !! + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ +/* for more info about .ZIP format, see + ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip */ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\zlib\\zlib111.zip" or on an Unix computer + "zlib/zlib111.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff -r ac56489c2ca6 -r 5e8e5083da94 src/common/vbalua.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/common/vbalua.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,52 @@ +#ifndef VBA_LUA_H +#define VBA_LUA_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +enum LuaCallID +{ + LUACALL_BEFOREEMULATION, + LUACALL_AFTEREMULATION, + LUACALL_BEFOREEXIT, + + LUACALL_COUNT +}; +void CallRegisteredLuaFunctions(LuaCallID calltype); + +enum LuaMemHookType +{ + LUAMEMHOOK_WRITE, + LUAMEMHOOK_READ, + LUAMEMHOOK_EXEC, + LUAMEMHOOK_WRITE_SUB, + LUAMEMHOOK_READ_SUB, + LUAMEMHOOK_EXEC_SUB, + + LUAMEMHOOK_COUNT +}; +void CallRegisteredLuaMemHook(unsigned int address, int size, unsigned int value, LuaMemHookType hookType); + +// Just forward function declarations + +void VBALuaFrameBoundary(); +int VBALoadLuaCode(const char *filename); +int VBAReloadLuaCode(); +void VBALuaStop(); +int VBALuaRunning(); + +int VBALuaUsingJoypad(int); +int VBALuaReadJoypad(int); +int VBALuaSpeed(); +bool8 VBALuaRerecordCountSkip(); + +void VBALuaGui(uint8 *screen, int ppl, int width, int height); +void VBALuaClearGui(); + +char* VBAGetLuaScriptName(); + +// And some interesting REVERSE declarations! +char *VBAGetFreezeFilename(int slot); + +#endif // VBA_LUA_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gb/gbCheats.cpp --- a/src/gb/gbCheats.cpp Sat Mar 03 12:06:10 2012 -0600 +++ b/src/gb/gbCheats.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -21,457 +21,457 @@ void gbCheatUpdateMap() { - memset(gbCheatMap, 0, 0x10000); + memset(gbCheatMap, 0, 0x10000); - for (int i = 0; i < gbCheatNumber; i++) - { - if (gbCheatList[i].enabled) - gbCheatMap[gbCheatList[i].address] = true; - } + for (int i = 0; i < gbCheatNumber; i++) + { + if (gbCheatList[i].enabled) + gbCheatMap[gbCheatList[i].address] = true; + } } void gbCheatsSaveGame(gzFile gzFile) { - utilWriteInt(gzFile, gbCheatNumber); - if (gbCheatNumber) - utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); + utilWriteInt(gzFile, gbCheatNumber); + if (gbCheatNumber) + utilGzWrite(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); } void gbCheatsReadGame(gzFile gzFile, int version) { - if (version <= 8) + if (version <= 8) + { + int gbGgOn = utilReadInt(gzFile); + + if (gbGgOn) { - int gbGgOn = utilReadInt(gzFile); - - if (gbGgOn) - { - int n = utilReadInt(gzFile); - gbXxCheat tmpCheat; - for (int i = 0; i < n; i++) - { - utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); - gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); - } - } - - int gbGsOn = utilReadInt(gzFile); - - if (gbGsOn) - { - int n = utilReadInt(gzFile); - gbXxCheat tmpCheat; - for (int i = 0; i < n; i++) - { - utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); - gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); - } - } - } - else - { - gbCheatNumber = utilReadInt(gzFile); - - if (gbCheatNumber) - { - utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); - } + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for (int i = 0; i < n; i++) + { + utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); + gbAddGgCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } } - gbCheatUpdateMap(); + int gbGsOn = utilReadInt(gzFile); + + if (gbGsOn) + { + int n = utilReadInt(gzFile); + gbXxCheat tmpCheat; + for (int i = 0; i < n; i++) + { + utilGzRead(gzFile, &tmpCheat, sizeof(gbXxCheat)); + gbAddGsCheat(tmpCheat.cheatCode, tmpCheat.cheatDesc); + } + } + } + else + { + gbCheatNumber = utilReadInt(gzFile); + + if (gbCheatNumber) + { + utilGzRead(gzFile, &gbCheatList[0], sizeof(gbCheat)*gbCheatNumber); + } + } + + gbCheatUpdateMap(); } void gbCheatsSaveCheatList(const char *file) { - if (gbCheatNumber == 0) - return; - FILE *f = fopen(file, "wb"); - if (f == NULL) - return; - int version = 1; - fwrite(&version, 1, sizeof(version), f); - int type = 1; - fwrite(&type, 1, sizeof(type), f); - fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); - fwrite(gbCheatList, 1, sizeof(gbCheatList), f); - fclose(f); + if (gbCheatNumber == 0) + return; + FILE *f = fopen(file, "wb"); + if (f == NULL) + return; + int version = 1; + fwrite(&version, 1, sizeof(version), f); + int type = 1; + fwrite(&type, 1, sizeof(type), f); + fwrite(&gbCheatNumber, 1, sizeof(gbCheatNumber), f); + fwrite(gbCheatList, 1, sizeof(gbCheatList), f); + fclose(f); } bool gbCheatsLoadCheatList(const char *file) { - gbCheatNumber = 0; + gbCheatNumber = 0; - gbCheatUpdateMap(); + gbCheatUpdateMap(); - int count = 0; + int count = 0; - FILE *f = fopen(file, "rb"); + FILE *f = fopen(file, "rb"); - if (f == NULL) - return false; + if (f == NULL) + return false; - int version = 0; + int version = 0; - if (fread(&version, 1, sizeof(version), f) != sizeof(version)) - { - fclose(f); - return false; - } + if (fread(&version, 1, sizeof(version), f) != sizeof(version)) + { + fclose(f); + return false; + } - if (version != 1) - { - systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, - N_("Unsupported cheat list version %d"), version); - fclose(f); - return false; - } + if (version != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + fclose(f); + return false; + } - int type = 0; - if (fread(&type, 1, sizeof(type), f) != sizeof(type)) - { - fclose(f); - return false; - } + int type = 0; + if (fread(&type, 1, sizeof(type), f) != sizeof(type)) + { + fclose(f); + return false; + } - if (type != 1) - { - systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, - N_("Unsupported cheat list type %d"), type); - fclose(f); - return false; - } + if (type != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + fclose(f); + return false; + } - if (fread(&count, 1, sizeof(count), f) != sizeof(count)) - { - fclose(f); - return false; - } + if (fread(&count, 1, sizeof(count), f) != sizeof(count)) + { + fclose(f); + return false; + } - if (fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) - { - fclose(f); - return false; - } + if (fread(gbCheatList, 1, sizeof(gbCheatList), f) != sizeof(gbCheatList)) + { + fclose(f); + return false; + } - fclose(f); - gbCheatNumber = count; - gbCheatUpdateMap(); + fclose(f); + gbCheatNumber = count; + gbCheatUpdateMap(); - return true; + return true; } bool gbVerifyGsCode(const char *code) { - int len = strlen(code); + int len = strlen(code); - if (len == 0) - return true; + if (len == 0) + return true; - if (len != 8) - return false; + if (len != 8) + return false; - for (int i = 0; i < 8; i++) - if (!GBCHEAT_IS_HEX(code[i])) - return false; + for (int i = 0; i < 8; i++) + if (!GBCHEAT_IS_HEX(code[i])) + return false; - int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | - GBCHEAT_HEX_VALUE(code[7]) << 8 | - GBCHEAT_HEX_VALUE(code[4]) << 4 | - GBCHEAT_HEX_VALUE(code[5]); + int address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); - if (address < 0xa000 || - address > 0xdfff) - return false; + if (address < 0xa000 || + address > 0xdfff) + return false; - return true; + return true; } void gbAddGsCheat(const char *code, const char *desc) { - if (gbCheatNumber > 99) - { - systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, - N_("Maximum number of cheats reached.")); - return; - } + if (gbCheatNumber > 99) + { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return; + } - if (!gbVerifyGsCode(code)) - { - systemMessage(MSG_INVALID_GAMESHARK_CODE, - N_("Invalid GameShark code: %s"), code); - return; - } + if (!gbVerifyGsCode(code)) + { + systemMessage(MSG_INVALID_GAMESHARK_CODE, + N_("Invalid GameShark code: %s"), code); + return; + } - int i = gbCheatNumber; + int i = gbCheatNumber; - strcpy(gbCheatList[i].cheatCode, code); - strcpy(gbCheatList[i].cheatDesc, desc); + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); - gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | - GBCHEAT_HEX_VALUE(code[1]); + gbCheatList[i].code = GBCHEAT_HEX_VALUE(code[0]) << 4 | + GBCHEAT_HEX_VALUE(code[1]); - gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | - GBCHEAT_HEX_VALUE(code[3]); + gbCheatList[i].value = GBCHEAT_HEX_VALUE(code[2]) << 4 | + GBCHEAT_HEX_VALUE(code[3]); - gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | - GBCHEAT_HEX_VALUE(code[7]) << 8 | - GBCHEAT_HEX_VALUE(code[4]) << 4 | - GBCHEAT_HEX_VALUE(code[5]); + gbCheatList[i].address = GBCHEAT_HEX_VALUE(code[6]) << 12 | + GBCHEAT_HEX_VALUE(code[7]) << 8 | + GBCHEAT_HEX_VALUE(code[4]) << 4 | + GBCHEAT_HEX_VALUE(code[5]); - gbCheatList[i].compare = 0; + gbCheatList[i].compare = 0; - gbCheatList[i].enabled = true; + gbCheatList[i].enabled = true; - gbCheatMap[gbCheatList[i].address] = true; + gbCheatMap[gbCheatList[i].address] = true; - gbCheatNumber++; + gbCheatNumber++; } bool gbVerifyGgCode(const char *code) { - int len = strlen(code); + int len = strlen(code); - if (len != 11 && - len != 7 && - len != 6 && - len != 0) - return false; + if (len != 11 && + len != 7 && + len != 6 && + len != 0) + return false; - if (len == 0) - return true; + if (len == 0) + return true; - if (!GBCHEAT_IS_HEX(code[0])) - return false; - if (!GBCHEAT_IS_HEX(code[1])) - return false; - if (!GBCHEAT_IS_HEX(code[2])) - return false; - if (code[3] != '-') - return false; - if (!GBCHEAT_IS_HEX(code[4])) - return false; - if (!GBCHEAT_IS_HEX(code[5])) - return false; - if (!GBCHEAT_IS_HEX(code[6])) - return false; - if (code[7] != 0) + if (!GBCHEAT_IS_HEX(code[0])) + return false; + if (!GBCHEAT_IS_HEX(code[1])) + return false; + if (!GBCHEAT_IS_HEX(code[2])) + return false; + if (code[3] != '-') + return false; + if (!GBCHEAT_IS_HEX(code[4])) + return false; + if (!GBCHEAT_IS_HEX(code[5])) + return false; + if (!GBCHEAT_IS_HEX(code[6])) + return false; + if (code[7] != 0) + { + if (code[7] != '-') + return false; + if (code[8] != 0) { - if (code[7] != '-') - return false; - if (code[8] != 0) - { - if (!GBCHEAT_IS_HEX(code[8])) - return false; - if (!GBCHEAT_IS_HEX(code[9])) - return false; - if (!GBCHEAT_IS_HEX(code[10])) - return false; - } + if (!GBCHEAT_IS_HEX(code[8])) + return false; + if (!GBCHEAT_IS_HEX(code[9])) + return false; + if (!GBCHEAT_IS_HEX(code[10])) + return false; } + } - // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + - // GBCHEAT_HEX_VALUE(code[1]); + // int replace = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + // GBCHEAT_HEX_VALUE(code[1]); - int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + - (GBCHEAT_HEX_VALUE(code[4]) << 4) + - (GBCHEAT_HEX_VALUE(code[5])) + - ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + int address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); - if (address >= 0x8000 && address <= 0x9fff) - return false; + if (address >= 0x8000 && address <= 0x9fff) + return false; - if (address >= 0xc000) - return false; + if (address >= 0xc000) + return false; - if (code[7] == 0 || code[8] == '0') - return true; + if (code[7] == 0 || code[8] == '0') + return true; - int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + - (GBCHEAT_HEX_VALUE(code[10])); - compare = compare ^ 0xff; - compare = (compare >> 2) | ((compare << 6) & 0xc0); - compare ^= 0x45; + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ((compare << 6) & 0xc0); + compare ^= 0x45; - int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); + int cloak = (GBCHEAT_HEX_VALUE(code[8])) ^ (GBCHEAT_HEX_VALUE(code[9])); - if (cloak >= 1 && cloak <= 7) - return false; + if (cloak >= 1 && cloak <= 7) + return false; - return true; + return true; } void gbAddGgCheat(const char *code, const char *desc) { - if (gbCheatNumber > 99) - { - systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, - N_("Maximum number of cheats reached.")); - return; - } + if (gbCheatNumber > 99) + { + systemMessage(MSG_MAXIMUM_NUMBER_OF_CHEATS, + N_("Maximum number of cheats reached.")); + return; + } - if (!gbVerifyGgCode(code)) - { - systemMessage(MSG_INVALID_GAMEGENIE_CODE, - N_("Invalid GameGenie code: %s"), code); - return; - } + if (!gbVerifyGgCode(code)) + { + systemMessage(MSG_INVALID_GAMEGENIE_CODE, + N_("Invalid GameGenie code: %s"), code); + return; + } - int i = gbCheatNumber; + int i = gbCheatNumber; - int len = strlen(code); + int len = strlen(code); - strcpy(gbCheatList[i].cheatCode, code); - strcpy(gbCheatList[i].cheatDesc, desc); + strcpy(gbCheatList[i].cheatCode, code); + strcpy(gbCheatList[i].cheatDesc, desc); - gbCheatList[i].code = 1; - gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + - GBCHEAT_HEX_VALUE(code[1]); + gbCheatList[i].code = 1; + gbCheatList[i].value = (GBCHEAT_HEX_VALUE(code[0]) << 4) + + GBCHEAT_HEX_VALUE(code[1]); - gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + - (GBCHEAT_HEX_VALUE(code[4]) << 4) + - (GBCHEAT_HEX_VALUE(code[5])) + - ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); + gbCheatList[i].address = (GBCHEAT_HEX_VALUE(code[2]) << 8) + + (GBCHEAT_HEX_VALUE(code[4]) << 4) + + (GBCHEAT_HEX_VALUE(code[5])) + + ((GBCHEAT_HEX_VALUE(code[6]) ^ 0x0f) << 12); - gbCheatList[i].compare = 0; + gbCheatList[i].compare = 0; - if (len != 7 && len != 8) - { - int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + - (GBCHEAT_HEX_VALUE(code[10])); - compare = compare ^ 0xff; - compare = (compare >> 2) | ((compare << 6) & 0xc0); - compare ^= 0x45; + if (len != 7 && len != 8) + { + int compare = (GBCHEAT_HEX_VALUE(code[8]) << 4) + + (GBCHEAT_HEX_VALUE(code[10])); + compare = compare ^ 0xff; + compare = (compare >> 2) | ((compare << 6) & 0xc0); + compare ^= 0x45; - gbCheatList[i].compare = compare; - gbCheatList[i].code = 0; - } + gbCheatList[i].compare = compare; + gbCheatList[i].code = 0; + } - gbCheatList[i].enabled = true; + gbCheatList[i].enabled = true; - gbCheatMap[gbCheatList[i].address] = true; + gbCheatMap[gbCheatList[i].address] = true; - gbCheatNumber++; + gbCheatNumber++; } void gbCheatRemove(int i) { - if (i < 0 || i >= gbCheatNumber) - { - systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, - N_("Invalid cheat to remove %d"), i); - return; - } + if (i < 0 || i >= gbCheatNumber) + { + systemMessage(MSG_INVALID_CHEAT_TO_REMOVE, + N_("Invalid cheat to remove %d"), i); + return; + } - if ((i+1) < gbCheatNumber) - { - memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* - (gbCheatNumber-i-1)); - } + if ((i+1) < gbCheatNumber) + { + memcpy(&gbCheatList[i], &gbCheatList[i+1], sizeof(gbCheat)* + (gbCheatNumber-i-1)); + } - gbCheatNumber--; + gbCheatNumber--; - gbCheatUpdateMap(); + gbCheatUpdateMap(); } void gbCheatRemoveAll() { - gbCheatNumber = 0; - gbCheatUpdateMap(); + gbCheatNumber = 0; + gbCheatUpdateMap(); } void gbCheatEnable(int i) { - if (i >= 0 && i < gbCheatNumber) + if (i >= 0 && i < gbCheatNumber) + { + if (!gbCheatList[i].enabled) { - if (!gbCheatList[i].enabled) - { - gbCheatList[i].enabled = true; - gbCheatUpdateMap(); - } + gbCheatList[i].enabled = true; + gbCheatUpdateMap(); } + } } void gbCheatDisable(int i) { - if (i >= 0 && i < gbCheatNumber) + if (i >= 0 && i < gbCheatNumber) + { + if (gbCheatList[i].enabled) { - if (gbCheatList[i].enabled) - { - gbCheatList[i].enabled = false; - gbCheatUpdateMap(); - } + gbCheatList[i].enabled = false; + gbCheatUpdateMap(); } + } } bool gbCheatReadGSCodeFile(const char *fileName) { - FILE *file = fopen(fileName, "rb"); + FILE *file = fopen(fileName, "rb"); - if (!file) - return false; + if (!file) + return false; - fseek(file, 0x18, SEEK_SET); - int count = 0; - fread(&count, 1, 2, file); - int dummy = 0; - gbCheatRemoveAll(); - char desc[13]; - char code[9]; - int i; - for (i = 0; i < count; i++) - { - fread(&dummy, 1, 2, file); - fread(desc, 1, 12, file); - desc[12] = 0; - fread(code, 1, 8, file); - code[8] = 0; - gbAddGsCheat(code, desc); - } + fseek(file, 0x18, SEEK_SET); + int count = 0; + fread(&count, 1, 2, file); + int dummy = 0; + gbCheatRemoveAll(); + char desc[13]; + char code[9]; + int i; + for (i = 0; i < count; i++) + { + fread(&dummy, 1, 2, file); + fread(desc, 1, 12, file); + desc[12] = 0; + fread(code, 1, 8, file); + code[8] = 0; + gbAddGsCheat(code, desc); + } - for (i = 0; i < gbCheatNumber; i++) - gbCheatDisable(i); + for (i = 0; i < gbCheatNumber; i++) + gbCheatDisable(i); - fclose(file); - return true; + fclose(file); + return true; } u8 gbCheatRead(u16 address) { - if (!cheatsEnabled) - return gbReadMemoryQuick(address); + if (!cheatsEnabled) + return gbReadMemoryQuick(address); - for (int i = 0; i < gbCheatNumber; i++) + for (int i = 0; i < gbCheatNumber; i++) + { + if (gbCheatList[i].enabled && gbCheatList[i].address == address) { - if (gbCheatList[i].enabled && gbCheatList[i].address == address) + switch (gbCheatList[i].code) + { + case 0x100: // GameGenie support + if (gbReadMemoryQuick(address) == gbCheatList[i].compare) + return gbCheatList[i].value; + break; + case 0x00: + case 0x01: + case 0x80: + return gbCheatList[i].value; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + if (address >= 0xd000 && address < 0xe000) { - switch (gbCheatList[i].code) - { - case 0x100: // GameGenie support - if (gbReadMemoryQuick(address) == gbCheatList[i].compare) - return gbCheatList[i].value; - break; - case 0x00: - case 0x01: - case 0x80: - return gbCheatList[i].value; - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - if (address >= 0xd000 && address < 0xe000) - { - if (((gbMemoryMap[0x0d] - gbWram)/0x1000) == - (gbCheatList[i].code - 0x90)) - return gbCheatList[i].value; - } - else - return gbCheatList[i].value; - } + if (((gbMemoryMap[0x0d] - gbWram)/0x1000) == + (gbCheatList[i].code - 0x90)) + return gbCheatList[i].value; } + else + return gbCheatList[i].value; + } } - return gbReadMemoryQuick(address); + } + return gbReadMemoryQuick(address); } diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/EEprom.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/EEprom.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,210 @@ +#include + +#include "GBA.h" // for SAVE_GAME_VERSION_3 +#include "EEprom.h" +#include "../common/System.h" +#include "../common/Util.h" + +extern int32 cpuDmaCount; + +int32 eepromMode = EEPROM_IDLE; +int32 eepromByte = 0; +int32 eepromBits = 0; +int32 eepromAddress = 0; +u8 eepromData[0x2000]; +u8 eepromBuffer[16]; +bool8 eepromInUse = false; +int32 eepromSize = 512; + +variable_desc eepromSaveData[] = { + { &eepromMode, sizeof(int32) }, + { &eepromByte, sizeof(int32) }, + { &eepromBits, sizeof(int32) }, + { &eepromAddress, sizeof(int32) }, + { &eepromInUse, sizeof(bool8) }, + { &eepromData[0], 512 }, + { &eepromBuffer[0], 16 }, + { NULL, 0 } +}; + +void eepromReset() +{ + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + eepromAddress = 0; + eepromInUse = false; + eepromSize = 512; +} + +void eepromErase() +{ + memset(eepromData, 0, 0x2000*sizeof(u8)); + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + eepromAddress = 0; + memset(eepromBuffer, 0, 16*sizeof(u8)); + eepromInUse = false; + eepromSize = 512; +} + +void eepromSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, eepromSaveData); + utilWriteInt(gzFile, eepromSize); + utilGzWrite(gzFile, eepromData, 0x2000); +} + +void eepromReadGame(gzFile gzFile, int version) +{ + utilReadData(gzFile, eepromSaveData); + if (version >= SAVE_GAME_VERSION_3) + { + eepromSize = utilReadInt(gzFile); + utilGzRead(gzFile, eepromData, 0x2000); + } + else + { + // prior to 0.7.1, only 4K EEPROM was supported + eepromSize = 512; + } +} + +int eepromRead(u32 /* address */) +{ + switch (eepromMode) + { + case EEPROM_IDLE: + case EEPROM_READADDRESS: + case EEPROM_WRITEDATA: + return 1; + case EEPROM_READDATA: + { + eepromBits++; + if (eepromBits == 4) + { + eepromMode = EEPROM_READDATA2; + eepromBits = 0; + eepromByte = 0; + } + return 0; + } + case EEPROM_READDATA2: + { + int data = 0; + int address = eepromAddress << 3; + int mask = 1 << (7 - (eepromBits & 7)); + data = (eepromData[address+eepromByte] & mask) ? 1 : 0; + eepromBits++; + if ((eepromBits & 7) == 0) + eepromByte++; + if (eepromBits == 0x40) + eepromMode = EEPROM_IDLE; + return data; + } + default: + return 0; + } + return 1; +} + +void eepromWrite(u32 /* address */, u8 value) +{ + if (cpuDmaCount == 0) + return; + int bit = value & 1; + switch (eepromMode) + { + case EEPROM_IDLE: + eepromByte = 0; + eepromBits = 1; + eepromBuffer[eepromByte] = bit; + eepromMode = EEPROM_READADDRESS; + break; + case EEPROM_READADDRESS: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if ((eepromBits & 7) == 0) + { + eepromByte++; + } + if (cpuDmaCount == 0x11 || cpuDmaCount == 0x51) + { + if (eepromBits == 0x11) + { + eepromInUse = true; + eepromSize = 0x2000; + eepromAddress = ((eepromBuffer[0] & 0x3F) << 8) | + ((eepromBuffer[1] & 0xFF)); + if (!(eepromBuffer[0] & 0x40)) + { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } + else + { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + else + { + if (eepromBits == 9) + { + eepromInUse = true; + eepromAddress = (eepromBuffer[0] & 0x3F); + if (!(eepromBuffer[0] & 0x40)) + { + eepromBuffer[0] = bit; + eepromBits = 1; + eepromByte = 0; + eepromMode = EEPROM_WRITEDATA; + } + else + { + eepromMode = EEPROM_READDATA; + eepromByte = 0; + eepromBits = 0; + } + } + } + break; + case EEPROM_READDATA: + case EEPROM_READDATA2: + // should we reset here? + eepromMode = EEPROM_IDLE; + break; + case EEPROM_WRITEDATA: + eepromBuffer[eepromByte] <<= 1; + eepromBuffer[eepromByte] |= bit; + eepromBits++; + if ((eepromBits & 7) == 0) + { + eepromByte++; + } + if (eepromBits == 0x40) + { + eepromInUse = true; + // write data; + for (int i = 0; i < 8; i++) + { + eepromData[(eepromAddress << 3) + i] = eepromBuffer[i]; + } + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + } + else if (eepromBits == 0x41) + { + eepromMode = EEPROM_IDLE; + eepromByte = 0; + eepromBits = 0; + } + break; + } +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/EEprom.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/EEprom.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,27 @@ +#ifndef VBA_EEPROM_H +#define VBA_EEPROM_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +extern void eepromSaveGame(gzFile gzFile); +extern void eepromReadGame(gzFile gzFile, int version); +extern int eepromRead(u32 address); +extern void eepromWrite(u32 address, u8 value); +extern void eepromReset(); +extern void eepromErase(); +extern u8 eepromData[0x2000]; +extern bool8 eepromInUse; +extern int32 eepromSize; + +#define EEPROM_IDLE 0 +#define EEPROM_READADDRESS 1 +#define EEPROM_READDATA 2 +#define EEPROM_READDATA2 3 +#define EEPROM_WRITEDATA 4 + +#endif // VBA_EEPROM_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Flash.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Flash.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,285 @@ +#include +#include + +#include "Flash.h" +#include "GBA.h" +#include "GBAGlobals.h" +#include "Sram.h" +#include "../common/System.h" +#include "../common/Util.h" + +#define FLASH_READ_ARRAY 0 +#define FLASH_CMD_1 1 +#define FLASH_CMD_2 2 +#define FLASH_AUTOSELECT 3 +#define FLASH_CMD_3 4 +#define FLASH_CMD_4 5 +#define FLASH_CMD_5 6 +#define FLASH_ERASE_COMPLETE 7 +#define FLASH_PROGRAM 8 +#define FLASH_SETBANK 9 + +u8 flashSaveMemory[0x20000 + 4]; +int32 flashState = FLASH_READ_ARRAY; +int32 flashReadState = FLASH_READ_ARRAY; +int32 flashSize = 0x10000; +int32 flashDeviceID = 0x1b; +int32 flashManufacturerID = 0x32; +int32 flashBank = 0; + +static variable_desc flashSaveData[] = { + { &flashState, sizeof(int32) }, + { &flashReadState, sizeof(int32) }, + { &flashSaveMemory[0], 0x10000 }, + { NULL, 0 } +}; + +static variable_desc flashSaveData2[] = { + { &flashState, sizeof(int32) }, + { &flashReadState, sizeof(int32) }, + { &flashSize, sizeof(int32) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } +}; + +static variable_desc flashSaveData3[] = { + { &flashState, sizeof(int32) }, + { &flashReadState, sizeof(int32) }, + { &flashSize, sizeof(int32) }, + { &flashBank, sizeof(int32) }, + { &flashSaveMemory[0], 0x20000 }, + { NULL, 0 } +}; + +void flashReset() +{ + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashBank = 0; +} + +void flashErase() +{ + memset(flashSaveMemory, 0, 0x20000*sizeof(u8)); + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashSize = 0x10000; + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + flashBank = 0; +} + +void flashSaveGame(gzFile gzFile) +{ + utilWriteData(gzFile, flashSaveData3); +} + +void flashReadGame(gzFile gzFile, int version) +{ + if (version < SAVE_GAME_VERSION_5) + utilReadData(gzFile, flashSaveData); + else if (version < SAVE_GAME_VERSION_7) + { + utilReadData(gzFile, flashSaveData2); + flashBank = 0; + flashSetSize(flashSize); + } + else + { + utilReadData(gzFile, flashSaveData3); + } +} + +void flashSetSize(int size) +{ + // log("Setting flash size to %d\n", size); + flashSize = size; + if (size == 0x10000) + { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } + else + { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } +} + +u8 flashRead(u32 address) +{ + // log("Reading %08x from %08x\n", address, reg[15].I); + // log("Current read state is %d\n", flashReadState); + address &= 0xFFFF; + + switch (flashReadState) + { + case FLASH_READ_ARRAY: + return flashSaveMemory[(flashBank << 16) + address]; + case FLASH_AUTOSELECT: + switch (address & 0xFF) + { + case 0: + // manufacturer ID + return u8(flashManufacturerID); + case 1: + // device ID + return u8(flashDeviceID); + } + break; + case FLASH_ERASE_COMPLETE: + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + return 0xFF; + } + ; + return 0; +} + +void flashSaveDecide(u32 address, u8 byte) +{ + // log("Deciding save type %08x\n", address); + if (address == 0x0e005555) + { + saveType = 2; + cpuSaveGameFunc = flashWrite; + } + else + { + saveType = 1; + cpuSaveGameFunc = sramWrite; + } + + (*cpuSaveGameFunc)(address, byte); +} + +void flashWrite(u32 address, u8 byte) +{ + // log("Writing %02x at %08x\n", byte, address); + // log("Current state is %d\n", flashState); + address &= 0xFFFF; + switch (flashState) + { + case FLASH_READ_ARRAY: + if (address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + break; + case FLASH_CMD_1: + if (address == 0x2AAA && byte == 0x55) + flashState = FLASH_CMD_2; + else + flashState = FLASH_READ_ARRAY; + break; + case FLASH_CMD_2: + if (address == 0x5555) + { + if (byte == 0x90) + { + flashState = FLASH_AUTOSELECT; + flashReadState = FLASH_AUTOSELECT; + } + else if (byte == 0x80) + { + flashState = FLASH_CMD_3; + } + else if (byte == 0xF0) + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + else if (byte == 0xA0) + { + flashState = FLASH_PROGRAM; + } + else if (byte == 0xB0 && flashSize == 0x20000) + { + flashState = FLASH_SETBANK; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_3: + if (address == 0x5555 && byte == 0xAA) + { + flashState = FLASH_CMD_4; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_4: + if (address == 0x2AAA && byte == 0x55) + { + flashState = FLASH_CMD_5; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_CMD_5: + if (byte == 0x30) + { + // SECTOR ERASE + memset(&flashSaveMemory[(flashBank << 16) + (address & 0xF000)], + 0, + 0x1000); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } + else if (byte == 0x10) + { + // CHIP ERASE + memset(flashSaveMemory, 0, flashSize); + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashReadState = FLASH_ERASE_COMPLETE; + } + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_AUTOSELECT: + if (byte == 0xF0) + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + else if (address == 0x5555 && byte == 0xAA) + flashState = FLASH_CMD_1; + else + { + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + } + break; + case FLASH_PROGRAM: + flashSaveMemory[(flashBank<<16)+address] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + case FLASH_SETBANK: + if (address == 0) + { + flashBank = (byte & 1); + } + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + break; + } +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Flash.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Flash.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,23 @@ +#ifndef VBA_FLASH_H +#define VBA_FLASH_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +extern void flashSaveGame(gzFile gzFile); +extern void flashReadGame(gzFile gzFile, int version); +extern u8 flashRead(u32 address); +extern void flashWrite(u32 address, u8 byte); +extern u8 flashSaveMemory[0x20000 + 4]; +extern void flashSaveDecide(u32 address, u8 byte); +extern void flashReset(); +extern void flashErase(); +extern void flashSetSize(int size); + +extern int32 flashSize; + +#endif // VBA_FLASH_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBA.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBA.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,4567 @@ +#include +#include +#include +#include + +#include "../Port.h" +#include "../NLS.h" +#include "GBA.h" +//#include "GBAGlobals.h" +#include "GBACheats.h" // FIXME: SDL requires this included before "GBAinline.h" +#include "GBAinline.h" +#include "GBAGfx.h" +#include "GBASound.h" +#include "EEprom.h" +#include "Flash.h" +#include "Sram.h" +#include "bios.h" +#include "elf.h" +#include "agbprint.h" +#include "../common/unzip.h" +#include "../common/Util.h" +#include "../common/movie.h" +#include "../common/vbalua.h" + +#ifdef PROFILING +#include "../prof/prof.h" +#endif + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]), value) + +#ifdef __GNUC__ +#define _stricmp strcasecmp +#endif + +#define CPU_BREAK_LOOP \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks; \ + *extCpuLoopTicks = *extClockTicks; + +#define CPU_BREAK_LOOP_2 \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks; \ + *extCpuLoopTicks = *extClockTicks; \ + *extTicks = *extClockTicks; + +int32 cpuDmaTicksToUpdate = 0; +int32 cpuDmaCount = 0; +bool8 cpuDmaHack = 0; +u32 cpuDmaLast = 0; +int32 dummyAddress = 0; + +int32 *extCpuLoopTicks = NULL; +int32 *extClockTicks = NULL; +int32 *extTicks = NULL; + +#if (defined(WIN32) && !defined(SDL)) +HANDLE mapROM; // shared memory handles +HANDLE mapWORKRAM; +HANDLE mapBIOS; +HANDLE mapIRAM; +HANDLE mapPALETTERAM; +HANDLE mapVRAM; +HANDLE mapOAM; +HANDLE mapPIX; +HANDLE mapIOMEM; +#endif + +int32 gbaSaveType = 0; // used to remember the save type on reset +bool8 intState = false; +bool8 stopState = false; +bool8 holdState = false; +int32 holdType = 0; +bool8 cpuSramEnabled = true; +bool8 cpuFlashEnabled = true; +bool8 cpuEEPROMEnabled = true; +bool8 cpuEEPROMSensorEnabled = false; + +#ifdef PROFILING +int profilingTicks = 0; +int profilingTicksReload = 0; +static char *profilBuffer = NULL; +static int profilSize = 0; +static u32 profilLowPC = 0; +static int profilScale = 0; +#endif +bool8 freezeWorkRAM[0x40000]; +bool8 freezeInternalRAM[0x8000]; +int32 lcdTicks = 960; +bool8 timer0On = false; +int32 timer0Ticks = 0; +int32 timer0Reload = 0; +int32 timer0ClockReload = 0; +bool8 timer1On = false; +int32 timer1Ticks = 0; +int32 timer1Reload = 0; +int32 timer1ClockReload = 0; +bool8 timer2On = false; +int32 timer2Ticks = 0; +int32 timer2Reload = 0; +int32 timer2ClockReload = 0; +bool8 timer3On = false; +int32 timer3Ticks = 0; +int32 timer3Reload = 0; +int32 timer3ClockReload = 0; +u32 dma0Source = 0; +u32 dma0Dest = 0; +u32 dma1Source = 0; +u32 dma1Dest = 0; +u32 dma2Source = 0; +u32 dma2Dest = 0; +u32 dma3Source = 0; +u32 dma3Dest = 0; +void (*cpuSaveGameFunc)(u32, u8) = flashSaveDecide; +void (*renderLine)() = mode0RenderLine; +bool8 fxOn = false; +bool8 windowOn = false; +int32 frameSkipCount = 0; +u32 gbaLastTime = 0; +int32 gbaFrameCount = 0; +bool8 prefetchActive = false, prefetchPrevActive = false, prefetchApplies = false; +char buffer[1024]; +FILE *out = NULL; + +static bool newFrame = true; +static bool pauseAfterFrameAdvance = false; + +const int32 TIMER_TICKS[4] = { + 1, + 64, + 256, + 1024 +}; + +const int32 thumbCycles[] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 4 + 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 5 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 6 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 7 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 8 + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // 9 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // a + 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 4, 1, 1, // b + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, // d + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // e + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 // f +}; + +const int32 gamepakRamWaitState[4] = { 4, 3, 2, 8 }; +const int32 gamepakWaitState[8] = { 4, 3, 2, 8, 4, 3, 2, 8 }; +const int32 gamepakWaitState0[8] = { 2, 2, 2, 2, 1, 1, 1, 1 }; +const int32 gamepakWaitState1[8] = { 4, 4, 4, 4, 1, 1, 1, 1 }; +const int32 gamepakWaitState2[8] = { 8, 8, 8, 8, 1, 1, 1, 1 }; + +int32 memoryWait[16] = +{ 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +int32 memoryWait32[16] = +{ 0, 0, 9, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 0 }; +int32 memoryWaitSeq[16] = +{ 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4, 0 }; +int32 memoryWaitSeq32[16] = +{ 2, 0, 3, 0, 0, 2, 2, 0, 4, 4, 8, 8, 16, 16, 8, 0 }; +int32 memoryWaitFetch[16] = +{ 3, 0, 3, 0, 0, 1, 1, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; +int32 memoryWaitFetch32[16] = +{ 6, 0, 6, 0, 0, 2, 2, 0, 8, 8, 8, 8, 8, 8, 8, 0 }; + +const int32 cpuMemoryWait[16] = { + 0, 0, 2, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 0, 0 +}; +const int32 cpuMemoryWait32[16] = { + 0, 0, 3, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 0, 0 +}; + +const bool8 memory32[16] = { + true, false, false, true, true, false, false, true, false, false, false, false, false, false, true, false +}; + +u8 biosProtected[4]; + +u8 cpuBitsSet[256]; +u8 cpuLowestBitSet[256]; + +#ifdef WORDS_BIGENDIAN +bool8 cpuBiosSwapped = false; +#endif + +u32 myROM[] = { + 0xEA000006, + 0xEA000093, + 0xEA000006, + 0x00000000, + 0x00000000, + 0x00000000, + 0xEA000088, + 0x00000000, + 0xE3A00302, + 0xE1A0F000, + 0xE92D5800, + 0xE55EC002, + 0xE28FB03C, + 0xE79BC10C, + 0xE14FB000, + 0xE92D0800, + 0xE20BB080, + 0xE38BB01F, + 0xE129F00B, + 0xE92D4004, + 0xE1A0E00F, + 0xE12FFF1C, + 0xE8BD4004, + 0xE3A0C0D3, + 0xE129F00C, + 0xE8BD0800, + 0xE169F00B, + 0xE8BD5800, + 0xE1B0F00E, + 0x0000009C, + 0x0000009C, + 0x0000009C, + 0x0000009C, + 0x000001F8, + 0x000001F0, + 0x000000AC, + 0x000000A0, + 0x000000FC, + 0x00000168, + 0xE12FFF1E, + 0xE1A03000, + 0xE1A00001, + 0xE1A01003, + 0xE2113102, + 0x42611000, + 0xE033C040, + 0x22600000, + 0xE1B02001, + 0xE15200A0, + 0x91A02082, + 0x3AFFFFFC, + 0xE1500002, + 0xE0A33003, + 0x20400002, + 0xE1320001, + 0x11A020A2, + 0x1AFFFFF9, + 0xE1A01000, + 0xE1A00003, + 0xE1B0C08C, + 0x22600000, + 0x42611000, + 0xE12FFF1E, + 0xE92D0010, + 0xE1A0C000, + 0xE3A01001, + 0xE1500001, + 0x81A000A0, + 0x81A01081, + 0x8AFFFFFB, + 0xE1A0000C, + 0xE1A04001, + 0xE3A03000, + 0xE1A02001, + 0xE15200A0, + 0x91A02082, + 0x3AFFFFFC, + 0xE1500002, + 0xE0A33003, + 0x20400002, + 0xE1320001, + 0x11A020A2, + 0x1AFFFFF9, + 0xE0811003, + 0xE1B010A1, + 0xE1510004, + 0x3AFFFFEE, + 0xE1A00004, + 0xE8BD0010, + 0xE12FFF1E, + 0xE0010090, + 0xE1A01741, + 0xE2611000, + 0xE3A030A9, + 0xE0030391, + 0xE1A03743, + 0xE2833E39, + 0xE0030391, + 0xE1A03743, + 0xE2833C09, + 0xE283301C, + 0xE0030391, + 0xE1A03743, + 0xE2833C0F, + 0xE28330B6, + 0xE0030391, + 0xE1A03743, + 0xE2833C16, + 0xE28330AA, + 0xE0030391, + 0xE1A03743, + 0xE2833A02, + 0xE2833081, + 0xE0030391, + 0xE1A03743, + 0xE2833C36, + 0xE2833051, + 0xE0030391, + 0xE1A03743, + 0xE2833CA2, + 0xE28330F9, + 0xE0000093, + 0xE1A00840, + 0xE12FFF1E, + 0xE3A00001, + 0xE3A01001, + 0xE92D4010, + 0xE3A0C301, + 0xE3A03000, + 0xE3A04001, + 0xE3500000, + 0x1B000004, + 0xE5CC3301, + 0xEB000002, + 0x0AFFFFFC, + 0xE8BD4010, + 0xE12FFF1E, + 0xE5CC3208, + 0xE15C20B8, + 0xE0110002, + 0x10200002, + 0x114C00B8, + 0xE5CC4208, + 0xE12FFF1E, + 0xE92D500F, + 0xE3A00301, + 0xE1A0E00F, + 0xE510F004, + 0xE8BD500F, + 0xE25EF004, + 0xE59FD044, + 0xE92D5000, + 0xE14FC000, + 0xE10FE000, + 0xE92D5000, + 0xE3A0C302, + 0xE5DCE09C, + 0xE35E00A5, + 0x1A000004, + 0x05DCE0B4, + 0x021EE080, + 0xE28FE004, + 0x159FF018, + 0x059FF018, + 0xE59FD018, + 0xE8BD5000, + 0xE169F00C, + 0xE8BD5000, + 0xE25EF004, + 0x03007FF0, + 0x09FE2000, + 0x09FFC000, + 0x03007FE0 +}; + +variable_desc saveGameStruct[] = { + { &DISPCNT, sizeof(u16) }, + { &DISPSTAT, sizeof(u16) }, + { &VCOUNT, sizeof(u16) }, + { &BG0CNT, sizeof(u16) }, + { &BG1CNT, sizeof(u16) }, + { &BG2CNT, sizeof(u16) }, + { &BG3CNT, sizeof(u16) }, + { &BG0HOFS, sizeof(u16) }, + { &BG0VOFS, sizeof(u16) }, + { &BG1HOFS, sizeof(u16) }, + { &BG1VOFS, sizeof(u16) }, + { &BG2HOFS, sizeof(u16) }, + { &BG2VOFS, sizeof(u16) }, + { &BG3HOFS, sizeof(u16) }, + { &BG3VOFS, sizeof(u16) }, + { &BG2PA, sizeof(u16) }, + { &BG2PB, sizeof(u16) }, + { &BG2PC, sizeof(u16) }, + { &BG2PD, sizeof(u16) }, + { &BG2X_L, sizeof(u16) }, + { &BG2X_H, sizeof(u16) }, + { &BG2Y_L, sizeof(u16) }, + { &BG2Y_H, sizeof(u16) }, + { &BG3PA, sizeof(u16) }, + { &BG3PB, sizeof(u16) }, + { &BG3PC, sizeof(u16) }, + { &BG3PD, sizeof(u16) }, + { &BG3X_L, sizeof(u16) }, + { &BG3X_H, sizeof(u16) }, + { &BG3Y_L, sizeof(u16) }, + { &BG3Y_H, sizeof(u16) }, + { &WIN0H, sizeof(u16) }, + { &WIN1H, sizeof(u16) }, + { &WIN0V, sizeof(u16) }, + { &WIN1V, sizeof(u16) }, + { &WININ, sizeof(u16) }, + { &WINOUT, sizeof(u16) }, + { &MOSAIC, sizeof(u16) }, + { &BLDMOD, sizeof(u16) }, + { &COLEV, sizeof(u16) }, + { &COLY, sizeof(u16) }, + { &DM0SAD_L, sizeof(u16) }, + { &DM0SAD_H, sizeof(u16) }, + { &DM0DAD_L, sizeof(u16) }, + { &DM0DAD_H, sizeof(u16) }, + { &DM0CNT_L, sizeof(u16) }, + { &DM0CNT_H, sizeof(u16) }, + { &DM1SAD_L, sizeof(u16) }, + { &DM1SAD_H, sizeof(u16) }, + { &DM1DAD_L, sizeof(u16) }, + { &DM1DAD_H, sizeof(u16) }, + { &DM1CNT_L, sizeof(u16) }, + { &DM1CNT_H, sizeof(u16) }, + { &DM2SAD_L, sizeof(u16) }, + { &DM2SAD_H, sizeof(u16) }, + { &DM2DAD_L, sizeof(u16) }, + { &DM2DAD_H, sizeof(u16) }, + { &DM2CNT_L, sizeof(u16) }, + { &DM2CNT_H, sizeof(u16) }, + { &DM3SAD_L, sizeof(u16) }, + { &DM3SAD_H, sizeof(u16) }, + { &DM3DAD_L, sizeof(u16) }, + { &DM3DAD_H, sizeof(u16) }, + { &DM3CNT_L, sizeof(u16) }, + { &DM3CNT_H, sizeof(u16) }, + { &TM0D, sizeof(u16) }, + { &TM0CNT, sizeof(u16) }, + { &TM1D, sizeof(u16) }, + { &TM1CNT, sizeof(u16) }, + { &TM2D, sizeof(u16) }, + { &TM2CNT, sizeof(u16) }, + { &TM3D, sizeof(u16) }, + { &TM3CNT, sizeof(u16) }, + { &P1, sizeof(u16) }, + { &IE, sizeof(u16) }, + { &IF, sizeof(u16) }, + { &IME, sizeof(u16) }, + { &holdState, sizeof(bool8) }, + { &holdType, sizeof(int32) }, + { &lcdTicks, sizeof(int32) }, + { &timer0On, sizeof(bool8) }, + { &timer0Ticks, sizeof(int32) }, + { &timer0Reload, sizeof(int32) }, + { &timer0ClockReload, sizeof(int32) }, + { &timer1On, sizeof(bool8) }, + { &timer1Ticks, sizeof(int32) }, + { &timer1Reload, sizeof(int32) }, + { &timer1ClockReload, sizeof(int32) }, + { &timer2On, sizeof(bool8) }, + { &timer2Ticks, sizeof(int32) }, + { &timer2Reload, sizeof(int32) }, + { &timer2ClockReload, sizeof(int32) }, + { &timer3On, sizeof(bool8) }, + { &timer3Ticks, sizeof(int32) }, + { &timer3Reload, sizeof(int32) }, + { &timer3ClockReload, sizeof(int32) }, + { &dma0Source, sizeof(u32) }, + { &dma0Dest, sizeof(u32) }, + { &dma1Source, sizeof(u32) }, + { &dma1Dest, sizeof(u32) }, + { &dma2Source, sizeof(u32) }, + { &dma2Dest, sizeof(u32) }, + { &dma3Source, sizeof(u32) }, + { &dma3Dest, sizeof(u32) }, + { &fxOn, sizeof(bool8) }, + { &windowOn, sizeof(bool8) }, + { &N_FLAG, sizeof(bool8) }, + { &C_FLAG, sizeof(bool8) }, + { &Z_FLAG, sizeof(bool8) }, + { &V_FLAG, sizeof(bool8) }, + { &armState, sizeof(bool8) }, + { &armIrqEnable, sizeof(bool8) }, + { &armNextPC, sizeof(u32) }, + { &armMode, sizeof(int32) }, + { &saveType, sizeof(int32) }, + { NULL, 0 } +}; + +//int cpuLoopTicks = 0; +int cpuSavedTicks = 0; + +#ifdef PROFILING +void cpuProfil(char *buf, int size, u32 lowPC, int scale) +{ + profilBuffer = buf; + profilSize = size; + profilLowPC = lowPC; + profilScale = scale; +} + +void cpuEnableProfiling(int hz) +{ + if (hz == 0) + hz = 100; + profilingTicks = profilingTicksReload = 16777216 / hz; + profSetHertz(hz); +} + +#endif + +inline int CPUUpdateTicksAccess32(u32 address) +{ + return memoryWait32[(address >> 24) & 15]; +} + +inline int CPUUpdateTicksAccess16(u32 address) +{ + return memoryWait[(address >> 24) & 15]; +} + +inline int CPUUpdateTicksAccessSeq32(u32 address) +{ + return memoryWaitSeq32[(address >> 24) & 15]; +} + +inline int CPUUpdateTicksAccessSeq16(u32 address) +{ + return memoryWaitSeq[(address >> 24) & 15]; +} + +inline int CPUUpdateTicks() +{ + int cpuLoopTicks = lcdTicks; + + if (soundTicks < cpuLoopTicks) + cpuLoopTicks = soundTicks; + + if (timer0On && !(TM0CNT & 4) && (timer0Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer0Ticks; + } + if (timer1On && !(TM1CNT & 4) && (timer1Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer1Ticks; + } + if (timer2On && !(TM2CNT & 4) && (timer2Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer2Ticks; + } + if (timer3On && !(TM3CNT & 4) && (timer3Ticks < cpuLoopTicks)) + { + cpuLoopTicks = timer3Ticks; + } +#ifdef PROFILING + if (profilingTicksReload != 0) + { + if (profilingTicks < cpuLoopTicks) + { + cpuLoopTicks = profilingTicks; + } + } +#endif + cpuSavedTicks = cpuLoopTicks; + return cpuLoopTicks; +} + +void CPUUpdateWindow0() +{ + int x00 = WIN0H >> 8; + int x01 = WIN0H & 255; + + if (x00 <= x01) + { + for (int i = 0; i < 240; i++) + { + gfxInWin0[i] = (i >= x00 && i < x01); + } + } + else + { + for (int i = 0; i < 240; i++) + { + gfxInWin0[i] = (i >= x00 || i < x01); + } + } +} + +void CPUUpdateWindow1() +{ + int x00 = WIN1H >> 8; + int x01 = WIN1H & 255; + + if (x00 <= x01) + { + for (int i = 0; i < 240; i++) + { + gfxInWin1[i] = (i >= x00 && i < x01); + } + } + else + { + for (int i = 0; i < 240; i++) + { + gfxInWin1[i] = (i >= x00 || i < x01); + } + } +} + +#define CLEAR_ARRAY(a) \ + { \ + u32 *array = (a); \ + for (int i = 0; i < 240; i++) { \ + *array++ = 0x80000000; \ + } \ + } \ + +void CPUUpdateRenderBuffers(bool force) +{ + if (!(layerEnable & 0x0100) || force) + { + CLEAR_ARRAY(line0); + } + if (!(layerEnable & 0x0200) || force) + { + CLEAR_ARRAY(line1); + } + if (!(layerEnable & 0x0400) || force) + { + CLEAR_ARRAY(line2); + } + if (!(layerEnable & 0x0800) || force) + { + CLEAR_ARRAY(line3); + } +} + +bool CPUWriteStateToStream(gzFile gzFile) +{ + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + utilGzWrite(gzFile, &rom[0xa0], 16); + + utilWriteInt(gzFile, useBios); + + utilGzWrite(gzFile, ®[0], sizeof(reg)); + + utilWriteData(gzFile, saveGameStruct); + + // new to version 0.7.1 + utilWriteInt(gzFile, stopState); + // new to version 0.8 + utilWriteInt(gzFile, intState); + + utilGzWrite(gzFile, internalRAM, 0x8000); + utilGzWrite(gzFile, paletteRAM, 0x400); + utilGzWrite(gzFile, workRAM, 0x40000); + utilGzWrite(gzFile, vram, 0x20000); + utilGzWrite(gzFile, oam, 0x400); + utilGzWrite(gzFile, pix, 4 * 241 * 162); + utilGzWrite(gzFile, ioMem, 0x400); + + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + soundSaveGame(gzFile); + + cheatsSaveGame(gzFile); + + // version 1.5 + rtcSaveGame(gzFile); + + // SAVE_GAME_VERSION_9 (new to re-recording version which is based on 1.72) + { + extern int32 sensorX, sensorY; // from SDL.cpp + utilGzWrite(gzFile, &sensorX, sizeof(sensorX)); + utilGzWrite(gzFile, &sensorY, sizeof(sensorY)); + bool8 movieActive = VBAMovieActive(); + utilGzWrite(gzFile, &movieActive, sizeof(movieActive)); + if (movieActive) + { + uint8 *movie_freeze_buf = NULL; + uint32 movie_freeze_size = 0; + + VBAMovieFreeze(&movie_freeze_buf, &movie_freeze_size); + if (movie_freeze_buf) + { + utilGzWrite(gzFile, &movie_freeze_size, sizeof(movie_freeze_size)); + utilGzWrite(gzFile, movie_freeze_buf, movie_freeze_size); + delete [] movie_freeze_buf; + } + else + { + systemMessage(0, N_("Failed to save movie snapshot.")); + return false; + } + } + utilGzWrite(gzFile, &GBASystemCounters.frameCount, sizeof(GBASystemCounters.frameCount)); + } + + // SAVE_GAME_VERSION_10 + { + utilGzWrite(gzFile, memoryWait, 16 * sizeof(int32)); + utilGzWrite(gzFile, memoryWait32, 16 * sizeof(int32)); + utilGzWrite(gzFile, memoryWaitSeq, 16 * sizeof(int32)); + utilGzWrite(gzFile, memoryWaitSeq32, 16 * sizeof(int32)); + utilGzWrite(gzFile, memoryWaitFetch, 16 * sizeof(int32)); + utilGzWrite(gzFile, memoryWaitFetch32, 16 * sizeof(int32)); + } + + // SAVE_GAME_VERSION_11 + { + utilGzWrite(gzFile, &prefetchActive, sizeof(bool8)); + utilGzWrite(gzFile, &prefetchPrevActive, sizeof(bool8)); + utilGzWrite(gzFile, &prefetchApplies, sizeof(bool8)); + } + + // SAVE_GAME_VERSION_12 + { + utilGzWrite(gzFile, &memLagTempEnabled, sizeof(bool8)); // necessary + utilGzWrite(gzFile, &speedHack, sizeof(bool8)); // just in case it's ever used... + } + + // SAVE_GAME_VERSION_13 + { + utilGzWrite(gzFile, &GBASystemCounters.lagCount, sizeof(GBASystemCounters.lagCount)); + utilGzWrite(gzFile, &GBASystemCounters.lagged, sizeof(GBASystemCounters.lagged)); + utilGzWrite(gzFile, &GBASystemCounters.laggedLast, sizeof(GBASystemCounters.laggedLast)); + } + + return true; +} + +bool CPUWriteState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "wb"); + + if (gzFile == NULL) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), file); + return false; + } + + bool res = CPUWriteStateToStream(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUWriteMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "w"); + + if (gzFile == NULL) + { + return false; + } + + bool res = CPUWriteStateToStream(gzFile); + + long pos = utilGzTell(gzFile) + 8; + + if (pos >= (available)) + res = false; + + utilGzClose(gzFile); + + return res; +} + +static int tempStateID = 0; +static int tempFailCount = 0; +static bool backupSafe = true; + +bool CPUReadStateFromStream(gzFile gzFile) +{ + bool8 ub; + char tempBackupName [128]; + if (backupSafe) + { + sprintf(tempBackupName, "gbatempsave%d.sav", tempStateID++); + CPUWriteState(tempBackupName); + } + + int version = utilReadInt(gzFile); + + if (version > SAVE_GAME_VERSION || version < SAVE_GAME_VERSION_1) + { + systemMessage(MSG_UNSUPPORTED_VBA_SGM, + N_("Unsupported VisualBoyAdvance save game version %d"), + version); + goto failedLoad; + } + + u8 romname[17]; + + utilGzRead(gzFile, romname, 16); + + if (memcmp(&rom[0xa0], romname, 16) != 0) + { + romname[16] = 0; + for (int i = 0; i < 16; i++) + if (romname[i] < 32) + romname[i] = 32; + systemMessage(MSG_CANNOT_LOAD_SGM, N_("Cannot load save game for %s"), romname); + goto failedLoad; + } + + ub = utilReadInt(gzFile) ? true : false; + + if (ub != useBios) + { + if (useBios) + systemMessage(MSG_SAVE_GAME_NOT_USING_BIOS, + N_("Save game is not using the BIOS files")); + else + systemMessage(MSG_SAVE_GAME_USING_BIOS, + N_("Save game is using the BIOS file")); + goto failedLoad; + } + + utilGzRead(gzFile, ®[0], sizeof(reg)); + + utilReadData(gzFile, saveGameStruct); + + if (version < SAVE_GAME_VERSION_3) + stopState = false; + else + stopState = utilReadInt(gzFile) ? true : false; + + if (version < SAVE_GAME_VERSION_4) + intState = false; + else + intState = utilReadInt(gzFile) ? true : false; + + utilGzRead(gzFile, internalRAM, 0x8000); + utilGzRead(gzFile, paletteRAM, 0x400); + utilGzRead(gzFile, workRAM, 0x40000); + utilGzRead(gzFile, vram, 0x20000); + utilGzRead(gzFile, oam, 0x400); + if (version < SAVE_GAME_VERSION_6) + utilGzRead(gzFile, pix, 4 * 240 * 160); + else + utilGzRead(gzFile, pix, 4 * 241 * 162); + utilGzRead(gzFile, ioMem, 0x400); + + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + soundReadGame(gzFile, version); + + if (version > SAVE_GAME_VERSION_1) + { + cheatsReadGame(gzFile); + } + if (version > SAVE_GAME_VERSION_6) + { + rtcReadGame(gzFile); + } + + if (version <= SAVE_GAME_VERSION_7) + { + u32 temp; +#define SWAP(a, b, c) \ + temp = (a); \ + (a) = (b) << 16 | (c); \ + (b) = (temp) >> 16; \ + (c) = (temp) & 0xFFFF; + + SWAP(dma0Source, DM0SAD_H, DM0SAD_L); + SWAP(dma0Dest, DM0DAD_H, DM0DAD_L); + SWAP(dma1Source, DM1SAD_H, DM1SAD_L); + SWAP(dma1Dest, DM1DAD_H, DM1DAD_L); + SWAP(dma2Source, DM2SAD_H, DM2SAD_L); + SWAP(dma2Dest, DM2DAD_H, DM2DAD_L); + SWAP(dma3Source, DM3SAD_H, DM3SAD_L); + SWAP(dma3Dest, DM3DAD_H, DM3DAD_L); + } + + // set pointers! + layerEnable = layerSettings & DISPCNT; + + CPUUpdateRender(); + CPUUpdateRenderBuffers(true); + CPUUpdateWindow0(); + CPUUpdateWindow1(); + gbaSaveType = 0; + switch (saveType) + { + case 0: + cpuSaveGameFunc = flashSaveDecide; + break; + case 1: + cpuSaveGameFunc = sramWrite; + gbaSaveType = 1; + break; + case 2: + cpuSaveGameFunc = flashWrite; + gbaSaveType = 2; + break; + default: + systemMessage(MSG_UNSUPPORTED_SAVE_TYPE, + N_("Unsupported save type %d"), saveType); + break; + } + if (eepromInUse) + gbaSaveType = 3; + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if (version >= SAVE_GAME_VERSION_9) // new to re-recording version: + { + extern int32 sensorX, sensorY; // from SDL.cpp + utilGzRead(gzFile, &sensorX, sizeof(sensorX)); + utilGzRead(gzFile, &sensorY, sizeof(sensorY)); + + bool8 movieSnapshot; + utilGzRead(gzFile, &movieSnapshot, sizeof(movieSnapshot)); + if (VBAMovieActive() && !movieSnapshot) + { + systemMessage(0, N_("Can't load a non-movie snapshot while a movie is active.")); + goto failedLoad; + } + + if (movieSnapshot) // even if a movie isn't active we still want to parse through this in case other stuff is added + // later on in the save format + { + uint32 movieInputDataSize = 0; + utilGzRead(gzFile, &movieInputDataSize, sizeof(movieInputDataSize)); + uint8 *local_movie_data = new uint8[movieInputDataSize]; + int readBytes = utilGzRead(gzFile, local_movie_data, movieInputDataSize); + if (readBytes != movieInputDataSize) + { + systemMessage(0, N_("Corrupt movie snapshot.")); + if (local_movie_data) + delete [] local_movie_data; + goto failedLoad; + } + int code = VBAMovieUnfreeze(local_movie_data, movieInputDataSize); + if (local_movie_data) + delete [] local_movie_data; + if (code != MOVIE_SUCCESS && VBAMovieActive()) + { + char errStr [1024]; + strcpy(errStr, "Failed to load movie snapshot"); + switch (code) + { + case MOVIE_NOT_FROM_THIS_MOVIE: + strcat(errStr, ";\nSnapshot not from this movie"); break; + case MOVIE_NOT_FROM_A_MOVIE: + strcat(errStr, ";\nNot a movie snapshot"); break; // shouldn't get here... + case MOVIE_SNAPSHOT_INCONSISTENT: + strcat(errStr, ";\nSnapshot inconsistent with movie"); break; + case MOVIE_WRONG_FORMAT: + strcat(errStr, ";\nWrong format"); break; + } + strcat(errStr, "."); + systemMessage(0, N_(errStr)); + goto failedLoad; + } + } + utilGzRead(gzFile, &GBASystemCounters.frameCount, sizeof(GBASystemCounters.frameCount)); + } + if (version >= SAVE_GAME_VERSION_10) + { + utilGzRead(gzFile, memoryWait, 16 * sizeof(int32)); + utilGzRead(gzFile, memoryWait32, 16 * sizeof(int32)); + utilGzRead(gzFile, memoryWaitSeq, 16 * sizeof(int32)); + utilGzRead(gzFile, memoryWaitSeq32, 16 * sizeof(int32)); + utilGzRead(gzFile, memoryWaitFetch, 16 * sizeof(int32)); + utilGzRead(gzFile, memoryWaitFetch32, 16 * sizeof(int32)); + } + if (version >= SAVE_GAME_VERSION_11) + { + utilGzRead(gzFile, &prefetchActive, sizeof(bool8)); + //if(prefetchActive && !prefetchPrevActive) systemScreenMessage("pre-fetch enabled",3,600); + //if(!prefetchActive && prefetchPrevActive) systemScreenMessage("pre-fetch disabled",3,600); + utilGzRead(gzFile, &prefetchPrevActive, sizeof(bool8)); + utilGzRead(gzFile, &prefetchApplies, sizeof(bool8)); + } + if (version >= SAVE_GAME_VERSION_12) + { + utilGzRead(gzFile, &memLagTempEnabled, sizeof(bool8)); // necessary + utilGzRead(gzFile, &speedHack, sizeof(bool8)); // just in case it's ever used... + } + if (version >= SAVE_GAME_VERSION_13) + { + utilGzRead(gzFile, &GBASystemCounters.lagCount, sizeof(GBASystemCounters.lagCount)); + utilGzRead(gzFile, &GBASystemCounters.lagged, sizeof(GBASystemCounters.lagged)); + utilGzRead(gzFile, &GBASystemCounters.laggedLast, sizeof(GBASystemCounters.laggedLast)); + } + + if (backupSafe) + { + remove(tempBackupName); + tempFailCount = 0; + } + systemSetJoypad(0, ~P1 & 0x3FF); + VBAUpdateButtonPressDisplay(); + VBAUpdateFrameCountDisplay(); + systemRefreshScreen(); + return true; + +failedLoad: + if (backupSafe) + { + tempFailCount++; + if (tempFailCount < 3) // fail no more than 2 times in a row + CPUReadState(tempBackupName); + remove(tempBackupName); + } + return false; +} + +bool CPUReadMemState(char *memory, int available) +{ + gzFile gzFile = utilMemGzOpen(memory, available, "r"); + + backupSafe = false; + bool res = CPUReadStateFromStream(gzFile); + backupSafe = true; + + utilGzClose(gzFile); + + return res; +} + +bool CPUReadState(const char *file) +{ + gzFile gzFile = utilGzOpen(file, "rb"); + + if (gzFile == NULL) + return false; + + bool res = CPUReadStateFromStream(gzFile); + + utilGzClose(gzFile); + + return res; +} + +bool CPUExportEepromFile(const char *fileName) +{ + if (eepromInUse) + { + FILE *file = fopen(fileName, "wb"); + + if (!file) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + for (int i = 0; i < eepromSize; ) + { + for (int j = 0; j < 8; j++) + { + if (fwrite(&eepromData[i + 7 - j], 1, 1, file) != 1) + { + fclose(file); + return false; + } + } + i += 8; + } + fclose(file); + } + return true; +} + +bool CPUWriteBatteryToStream(gzFile gzFile) +{ + if (!gzFile) + return false; + + utilWriteInt(gzFile, SAVE_GAME_VERSION); + + // for simplicity, we put both types of battery files should be in the stream, even if one's empty + eepromSaveGame(gzFile); + flashSaveGame(gzFile); + + return true; +} + +bool CPUWriteBatteryFile(const char *fileName) +{ + if (gbaSaveType == 0) + { + if (eepromInUse) + gbaSaveType = 3; + else + switch (saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + if (gbaSaveType) + { + FILE *file = fopen(fileName, "wb"); + + if (!file) + { + systemMessage(MSG_ERROR_CREATING_FILE, N_("Error creating file %s"), + fileName); + return false; + } + + // only save if Flash/Sram in use or EEprom in use + if (gbaSaveType != 3) + { + if (gbaSaveType == 2) + { + if (fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) + { + fclose(file); + return false; + } + } + else + { + if (fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) + { + fclose(file); + return false; + } + } + } + else + { + if (fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) + { + fclose(file); + return false; + } + } + fclose(file); + } + + return true; +} + +bool CPUReadGSASnapshot(const char *fileName) +{ + int i; + FILE *file = fopen(fileName, "rb"); + + if (!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + // long size = ftell(file); + fseek(file, 0x0, SEEK_SET); + fread(&i, 1, 4, file); + fseek(file, i, SEEK_CUR); // Skip SharkPortSave + fseek(file, 4, SEEK_CUR); // skip some sort of flag + fread(&i, 1, 4, file); // name length + fseek(file, i, SEEK_CUR); // skip name + fread(&i, 1, 4, file); // desc length + fseek(file, i, SEEK_CUR); // skip desc + fread(&i, 1, 4, file); // notes length + fseek(file, i, SEEK_CUR); // skip notes + int saveSize; + fread(&saveSize, 1, 4, file); // read length + saveSize -= 0x1c; // remove header size + char buffer[17]; + char buffer2[17]; + fread(buffer, 1, 16, file); + buffer[16] = 0; + for (i = 0; i < 16; i++) + if (buffer[i] < 32) + buffer[i] = 32; + memcpy(buffer2, &rom[0xa0], 16); + buffer2[16] = 0; + for (i = 0; i < 16; i++) + if (buffer2[i] < 32) + buffer2[i] = 32; + if (memcmp(buffer, buffer2, 16)) + { + systemMessage(MSG_CANNOT_IMPORT_SNAPSHOT_FOR, + N_("Cannot import snapshot for %s. Current game is %s"), + buffer, + buffer2); + fclose(file); + return false; + } + fseek(file, 12, SEEK_CUR); // skip some flags + if (saveSize >= 65536) + { + if (fread(flashSaveMemory, 1, saveSize, file) != (size_t)saveSize) + { + fclose(file); + return false; + } + } + else + { + systemMessage(MSG_UNSUPPORTED_SNAPSHOT_FILE, + N_("Unsupported snapshot file %s"), + fileName); + fclose(file); + return false; + } + fclose(file); + CPUReset(); + return true; +} + +bool CPUWriteGSASnapshot(const char *fileName, + const char *title, + const char *desc, + const char *notes) +{ + FILE *file = fopen(fileName, "wb"); + + if (!file) + { + systemMessage(MSG_CANNOT_OPEN_FILE, N_("Cannot open file %s"), fileName); + return false; + } + + u8 buffer[17]; + + utilPutDword(buffer, 0x0d); // SharkPortSave length + fwrite(buffer, 1, 4, file); + fwrite("SharkPortSave", 1, 0x0d, file); + utilPutDword(buffer, 0x000f0000); + fwrite(buffer, 1, 4, file); // save type 0x000f0000 = GBA save + utilPutDword(buffer, strlen(title)); + fwrite(buffer, 1, 4, file); // title length + fwrite(title, 1, strlen(title), file); + utilPutDword(buffer, strlen(desc)); + fwrite(buffer, 1, 4, file); // desc length + fwrite(desc, 1, strlen(desc), file); + utilPutDword(buffer, strlen(notes)); + fwrite(buffer, 1, 4, file); // notes length + fwrite(notes, 1, strlen(notes), file); + int saveSize = 0x10000; + if (gbaSaveType == 2) + saveSize = flashSize; + int totalSize = saveSize + 0x1c; + + utilPutDword(buffer, totalSize); // length of remainder of save - CRC + fwrite(buffer, 1, 4, file); + + char temp[0x2001c]; + memset(temp, 0, 28); + memcpy(temp, &rom[0xa0], 16); // copy internal name + temp[0x10] = rom[0xbe]; // reserved area (old checksum) + temp[0x11] = rom[0xbf]; // reserved area (old checksum) + temp[0x12] = rom[0xbd]; // complement check + temp[0x13] = rom[0xb0]; // maker + temp[0x14] = 1; // 1 save ? + memcpy(&temp[0x1c], flashSaveMemory, saveSize); // copy save + fwrite(temp, 1, totalSize, file); // write save + header + u32 crc = 0; + + for (int i = 0; i < totalSize; i++) + { + crc += ((u32)temp[i] << (crc % 0x18)); + } + + utilPutDword(buffer, crc); + fwrite(buffer, 1, 4, file); // CRC? + + fclose(file); + return true; +} + +bool CPUImportEepromFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if (!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + if (size == 512 || size == 0x2000) + { + if (fread(eepromData, 1, size, file) != (size_t)size) + { + fclose(file); + return false; + } + for (int i = 0; i < size; ) + { + u8 tmp = eepromData[i]; + eepromData[i] = eepromData[7 - i]; + eepromData[7 - i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7 - i]; + eepromData[7 - i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7 - i]; + eepromData[7 - i] = tmp; + i++; + tmp = eepromData[i]; + eepromData[i] = eepromData[7 - i]; + eepromData[7 - i] = tmp; + i++; + i += 4; + } + } + else + { + fclose(file); + return false; + } + fclose(file); + return true; +} + +bool CPUReadBatteryFromStream(gzFile gzFile) +{ + if (!gzFile) + return false; + + int version = utilReadInt(gzFile); + + // for simplicity, we put both types of battery files should be in the stream, even if one's empty + eepromReadGame(gzFile, version); + flashReadGame(gzFile, version); + + return true; +} + +bool CPUReadBatteryFile(const char *fileName) +{ + FILE *file = fopen(fileName, "rb"); + + if (!file) + return false; + + // check file size to know what we should read + fseek(file, 0, SEEK_END); + + long size = ftell(file); + fseek(file, 0, SEEK_SET); + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + if (size == 512 || size == 0x2000) + { + if (fread(eepromData, 1, size, file) != (size_t)size) + { + fclose(file); + return false; + } + } + else + { + if (size == 0x20000) + { + if (fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) + { + fclose(file); + return false; + } + flashSetSize(0x20000); + } + else + { + if (fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) + { + fclose(file); + return false; + } + flashSetSize(0x10000); + } + } + fclose(file); + return true; +} + +bool CPUWritePNGFile(const char *fileName) +{ + return utilWritePNGFile(fileName, 240, 160, pix); +} + +bool CPUWriteBMPFile(const char *fileName) +{ + return utilWriteBMPFile(fileName, 240, 160, pix); +} + +void CPUCleanUp() +{ + newFrame = true; + + GBASystemCounters.frameCount = 0; + GBASystemCounters.lagCount = 0; + GBASystemCounters.extraCount = 0; + GBASystemCounters.lagged = true; + GBASystemCounters.laggedLast = true; + +#ifdef PROFILING + if (profilingTicksReload) + { + profCleanup(); + } +#endif + +#if (defined(WIN32) && !defined(SDL)) + #define FreeMappedMem(name, mapName, offset) \ + if (name != NULL) { \ + UnmapViewOfFile((name) - (offset)); \ + name = NULL; \ + CloseHandle(mapName); \ + } +#else + #define FreeMappedMem(name, mapName, offset) \ + if (name != NULL) { \ + free(name); \ + name = NULL; \ + } +#endif + + FreeMappedMem(rom, mapROM, 0); + FreeMappedMem(vram, mapVRAM, 0); + FreeMappedMem(paletteRAM, mapPALETTERAM, 0); + FreeMappedMem(internalRAM, mapIRAM, 0); + FreeMappedMem(workRAM, mapWORKRAM, 0); + FreeMappedMem(bios, mapBIOS, 0); + FreeMappedMem(pix, mapPIX, 4); + FreeMappedMem(oam, mapOAM, 0); + FreeMappedMem(ioMem, mapIOMEM, 0); + + eepromErase(); + flashErase(); + + elfCleanUp(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + systemClearJoypads(); + systemResetSensor(); + +// gbaLastTime = gbaFrameCount = 0; + systemRefreshScreen(); +} + +int CPULoadRom(const char *szFile) +{ + int size = 0x2000000; + + if (rom != NULL) + { + CPUCleanUp(); + } + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + // size+4 is so RAM search and watch are safe to read any byte in the allocated region as a 4-byte int +#if (defined(WIN32) && !defined(SDL)) + #define AllocMappedMem(name, mapName, nameStr, size, useCalloc, offset) \ + mapName = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (size) + (offset) + (4), nameStr); \ + if ((mapName) && GetLastError() == ERROR_ALREADY_EXISTS) { \ + CloseHandle(mapName); \ + mapName = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (size) + (offset) + (4), NULL); \ + } \ + name = (u8 *)MapViewOfFile(mapName, FILE_MAP_WRITE, 0, 0, 0) + (offset); \ + if ((name) == NULL) { \ + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), nameStr); \ + CPUCleanUp(); \ + return 0; \ + } \ + memset(name, 0, size + 4); +#else + #define AllocMappedMem(name, mapName, nameStr, size, useCalloc, offset) \ + name = (u8 *)(useCalloc ? calloc(1, size + 4) : malloc(size + 4)); \ + if ((name) == NULL) { \ + systemMessage(MSG_OUT_OF_MEMORY, N_("Failed to allocate memory for %s"), nameStr); \ + CPUCleanUp(); \ + return 0; \ + } \ + memset(name, 0, size + 4); +#endif + + AllocMappedMem(rom, mapROM, "vbaROM", 0x2000000, false, 0); + AllocMappedMem(workRAM, mapWORKRAM, "vbaWORKRAM", 0x40000, true, 0); + + u8 *whereToLoad = rom; + if (cpuIsMultiBoot) + whereToLoad = workRAM; + + if (utilIsELF(szFile)) + { + FILE *f = fopen(szFile, "rb"); + if (!f) + { + systemMessage(MSG_ERROR_OPENING_IMAGE, N_("Error opening image %s"), + szFile); + FreeMappedMem(rom, mapROM, 0); + FreeMappedMem(workRAM, mapWORKRAM, 0); + return 0; + } + bool res = elfRead(szFile, size, f); + if (!res || size == 0) + { + FreeMappedMem(rom, mapROM, 0); + FreeMappedMem(workRAM, mapWORKRAM, 0); + elfCleanUp(); + return 0; + } + } + else if (!utilLoad(szFile, + utilIsGBAImage, + whereToLoad, + size)) + { + FreeMappedMem(rom, mapROM, 0); + FreeMappedMem(workRAM, mapWORKRAM, 0); + return 0; + } + + u16 *temp = (u16 *)(rom + ((size + 1) & ~1)); + int i; + for (i = (size + 1) & ~1; i < 0x2000000; i += 2) + { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + AllocMappedMem(bios, mapBIOS, "vbaBIOS", 0x4000, true, 0); + AllocMappedMem(internalRAM, mapIRAM, "vbaIRAM", 0x8000, true, 0); + AllocMappedMem(paletteRAM, mapPALETTERAM, "vbaPALETTERAM", 0x400, true, 0); + AllocMappedMem(vram, mapVRAM, "vbaVRAM", 0x20000, true, 0); + AllocMappedMem(oam, mapOAM, "vbaOAM", 0x400, true, 0); + + // HACK: +4 at start to accomodate the 2xSaI filter reading out of bounds of the leftmost pixel + AllocMappedMem(pix, mapPIX, "vbaPIX", 4 * 241 * 162, true, 4); + AllocMappedMem(ioMem, mapIOMEM, "vbaIOMEM", 0x400, true, 0); + + CPUUpdateRenderBuffers(true); + + return size; +} + +void CPUUpdateRender() +{ + switch (DISPCNT & 7) + { + case 0: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode0RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode0RenderLineNoWindow; + else + renderLine = mode0RenderLineAll; + break; + case 1: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode1RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode1RenderLineNoWindow; + else + renderLine = mode1RenderLineAll; + break; + case 2: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode2RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode2RenderLineNoWindow; + else + renderLine = mode2RenderLineAll; + break; + case 3: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode3RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode3RenderLineNoWindow; + else + renderLine = mode3RenderLineAll; + break; + case 4: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode4RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode4RenderLineNoWindow; + else + renderLine = mode4RenderLineAll; + break; + case 5: + if ((!fxOn && !windowOn && !(layerEnable & 0x8000)) || + cpuDisableSfx) + renderLine = mode5RenderLine; + else if (fxOn && !windowOn && !(layerEnable & 0x8000)) + renderLine = mode5RenderLineNoWindow; + else + renderLine = mode5RenderLineAll; + default: + break; + } +} + +void CPUUpdateCPSR() +{ + u32 CPSR = reg[16].I & 0x40; + if (N_FLAG) + CPSR |= 0x80000000; + if (Z_FLAG) + CPSR |= 0x40000000; + if (C_FLAG) + CPSR |= 0x20000000; + if (V_FLAG) + CPSR |= 0x10000000; + if (!armState) + CPSR |= 0x00000020; + if (!armIrqEnable) + CPSR |= 0x80; + CPSR |= (armMode & 0x1F); + reg[16].I = CPSR; +} + +void CPUUpdateFlags(bool breakLoop) +{ + u32 CPSR = reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true : false; + Z_FLAG = (CPSR & 0x40000000) ? true : false; + C_FLAG = (CPSR & 0x20000000) ? true : false; + V_FLAG = (CPSR & 0x10000000) ? true : false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if (breakLoop) + { + if (armIrqEnable && (IF & IE) && (IME & 1)) + { + CPU_BREAK_LOOP_2; + } + } +} + +void CPUUpdateFlags() +{ + CPUUpdateFlags(true); +} + +#ifdef WORDS_BIGENDIAN +static void CPUSwap(volatile u32 *a, volatile u32 *b) +{ + volatile u32 c = *b; + *b = *a; + *a = c; +} + +#else +static void CPUSwap(u32 *a, u32 *b) +{ + u32 c = *b; + *b = *a; + *a = c; +} + +#endif + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + // if(armMode == mode) + // return; + + CPUUpdateCPSR(); + + switch (armMode) + { + case 0x10: + case 0x1F: + reg[R13_USR].I = reg[13].I; + reg[R14_USR].I = reg[14].I; + reg[17].I = reg[16].I; + break; + case 0x11: + CPUSwap(®[R8_FIQ].I, ®[8].I); + CPUSwap(®[R9_FIQ].I, ®[9].I); + CPUSwap(®[R10_FIQ].I, ®[10].I); + CPUSwap(®[R11_FIQ].I, ®[11].I); + CPUSwap(®[R12_FIQ].I, ®[12].I); + reg[R13_FIQ].I = reg[13].I; + reg[R14_FIQ].I = reg[14].I; + reg[SPSR_FIQ].I = reg[17].I; + break; + case 0x12: + reg[R13_IRQ].I = reg[13].I; + reg[R14_IRQ].I = reg[14].I; + reg[SPSR_IRQ].I = reg[17].I; + break; + case 0x13: + reg[R13_SVC].I = reg[13].I; + reg[R14_SVC].I = reg[14].I; + reg[SPSR_SVC].I = reg[17].I; + break; + case 0x17: + reg[R13_ABT].I = reg[13].I; + reg[R14_ABT].I = reg[14].I; + reg[SPSR_ABT].I = reg[17].I; + break; + case 0x1b: + reg[R13_UND].I = reg[13].I; + reg[R14_UND].I = reg[14].I; + reg[SPSR_UND].I = reg[17].I; + break; + } + + u32 CPSR = reg[16].I; + u32 SPSR = reg[17].I; + + switch (mode) + { + case 0x10: + case 0x1F: + reg[13].I = reg[R13_USR].I; + reg[14].I = reg[R14_USR].I; + reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(®[8].I, ®[R8_FIQ].I); + CPUSwap(®[9].I, ®[R9_FIQ].I); + CPUSwap(®[10].I, ®[R10_FIQ].I); + CPUSwap(®[11].I, ®[R11_FIQ].I); + CPUSwap(®[12].I, ®[R12_FIQ].I); + reg[13].I = reg[R13_FIQ].I; + reg[14].I = reg[R14_FIQ].I; + if (saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_FIQ].I; + break; + case 0x12: + reg[13].I = reg[R13_IRQ].I; + reg[14].I = reg[R14_IRQ].I; + reg[16].I = SPSR; + if (saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_IRQ].I; + break; + case 0x13: + reg[13].I = reg[R13_SVC].I; + reg[14].I = reg[R14_SVC].I; + reg[16].I = SPSR; + if (saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_SVC].I; + break; + case 0x17: + reg[13].I = reg[R13_ABT].I; + reg[14].I = reg[R14_ABT].I; + reg[16].I = SPSR; + if (saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_ABT].I; + break; + case 0x1b: + reg[13].I = reg[R13_UND].I; + reg[14].I = reg[R14_UND].I; + reg[16].I = SPSR; + if (saveState) + reg[17].I = CPSR; + else + reg[17].I = reg[SPSR_UND].I; + break; + default: + systemMessage(MSG_UNSUPPORTED_ARM_MODE, N_("Unsupported ARM mode %02x"), mode); + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPUUpdateCPSR(); +} + +void CPUSwitchMode(int mode, bool saveState) +{ + CPUSwitchMode(mode, saveState, true); +} + +void CPUUndefinedException() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x1b, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + armNextPC = 0x04; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt() +{ + u32 PC = reg[15].I; + bool savedArmState = armState; + CPUSwitchMode(0x13, true, false); + reg[14].I = PC - (savedArmState ? 4 : 2); + reg[15].I = 0x08; + armState = true; + armIrqEnable = false; + armNextPC = 0x08; + reg[15].I += 4; +} + +void CPUSoftwareInterrupt(int comment) +{ + static bool disableMessage = false; + if (armState) + comment >>= 16; +#ifdef BKPT_SUPPORT + if (comment == 0xff) + { + extern void (*dbgOutput)(char *, u32); + dbgOutput(NULL, reg[0].I); + return; + } +#endif +#ifdef PROFILING + if (comment == 0xfe) + { + profStartup(reg[0].I, reg[1].I); + return; + } + if (comment == 0xfd) + { + profControl(reg[0].I); + return; + } + if (comment == 0xfc) + { + profCleanup(); + return; + } + if (comment == 0xfb) + { + profCount(); + return; + } +#endif + if (comment == 0xfa) + { + agbPrintFlush(); + return; + } +#ifdef SDL + if (comment == 0xf9) + { + emulating = 0; + CPU_BREAK_LOOP_2; + return; + } +#endif + if (useBios) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4 : armNextPC - 2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + return; + } + // This would be correct, but it causes problems if uncommented + // else { + // biosProtected = 0xe3a02004; + // } + + switch (comment) + { + case 0x00: + BIOS_SoftReset(); + break; + case 0x01: + BIOS_RegisterRamReset(); + break; + case 0x02: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Halt: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + break; + case 0x03: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Stop: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + holdState = true; + holdType = -1; + stopState = true; + break; + case 0x04: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("IntrWait: 0x%08x,0x%08x (VCOUNT = %2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x05: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("VBlankIntrWait: (VCOUNT = %2d)\n", + VCOUNT); + } +#endif + CPUSoftwareInterrupt(); + break; + case 0x06: + CPUSoftwareInterrupt(); + break; + case 0x07: + CPUSoftwareInterrupt(); + break; + case 0x08: + BIOS_Sqrt(); + break; + case 0x09: + BIOS_ArcTan(); + break; + case 0x0A: + BIOS_ArcTan2(); + break; + case 0x0B: + BIOS_CpuSet(); + break; + case 0x0C: + BIOS_CpuFastSet(); + break; + case 0x0E: + BIOS_BgAffineSet(); + break; + case 0x0F: + BIOS_ObjAffineSet(); + break; + case 0x10: + BIOS_BitUnPack(); + break; + case 0x11: + BIOS_LZ77UnCompWram(); + break; + case 0x12: + BIOS_LZ77UnCompVram(); + break; + case 0x13: + BIOS_HuffUnComp(); + break; + case 0x14: + BIOS_RLUnCompWram(); + break; + case 0x15: + BIOS_RLUnCompVram(); + break; + case 0x16: + BIOS_Diff8bitUnFilterWram(); + break; + case 0x17: + BIOS_Diff8bitUnFilterVram(); + break; + case 0x18: + BIOS_Diff16bitUnFilter(); + break; + case 0x19: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("SoundBiasSet: 0x%08x (VCOUNT = %2d)\n", + reg[0].I, + VCOUNT); + } +#endif + if (reg[0].I) + soundPause(); + else + soundResume(); + break; + case 0x1F: + BIOS_MidiKey2Freq(); + break; + case 0x2A: + BIOS_SndDriverJmpTableCopy(); + // let it go, because we don't really emulate this function // FIXME (?) + default: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("SWI: %08x at %08x (0x%08x,0x%08x,0x%08x,VCOUNT = %2d)\n", comment, + armState ? armNextPC - 4 : armNextPC - 2, + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + if (!disableMessage) + { + systemMessage(MSG_UNSUPPORTED_BIOS_FUNCTION, + N_( + "Unsupported BIOS function %02x called from %08x. A BIOS file is needed in order to get correct behaviour."), + comment, + armMode ? armNextPC - 4 : armNextPC - 2); + disableMessage = true; + } + break; + } +} + +void CPUCompareVCOUNT() +{ + if (VCOUNT == (DISPSTAT >> 8)) + { + DISPSTAT |= 4; + UPDATE_REG(0x04, DISPSTAT); + + if (DISPSTAT & 0x20) + { + IF |= 4; + UPDATE_REG(0x202, IF); + } + } + else + { + DISPSTAT &= 0xFFFB; + UPDATE_REG(0x4, DISPSTAT); + } +} + +void doDMA(u32 &s, u32 &d, u32 si, u32 di, u32 c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + + int sc = c; + + cpuDmaCount = c; + + if (transfer32) + { + s &= 0xFFFFFFFC; + if (s < 0x02000000 && (reg[15].I >> 24)) + { + while (c != 0) + { + CPUWriteMemory(d, 0); + d += di; + c--; + } + } + else + { + while (c != 0) + { + CPUWriteMemory(d, CPUReadMemory(s)); + d += di; + s += si; + c--; + } + } + } + else + { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if (s < 0x02000000 && (reg[15].I >> 24)) + { + while (c != 0) + { + CPUWriteHalfWord(d, 0); + d += di; + c--; + } + } + else + { + while (c != 0) + { + cpuDmaLast = CPUReadHalfWord(s); + CPUWriteHalfWord(d, cpuDmaLast); + d += di; + s += si; + c--; + } + } + } + + cpuDmaCount = 0; + + int sw = 1 + memoryWaitSeq[sm & 15]; + int dw = 1 + memoryWaitSeq[dm & 15]; + + int totalTicks = 0; + + if (transfer32) + { + if (!memory32[sm & 15]) + sw <<= 1; + if (!memory32[dm & 15]) + dw <<= 1; + } + + totalTicks = (sw + dw) * sc; + + cpuDmaTicksToUpdate += totalTicks; + + if (*extCpuLoopTicks >= 0) + { + CPU_BREAK_LOOP; + } +} + +void CPUCheckDMA(int reason, int dmamask) +{ + cpuDmaHack = 0; + // DMA 0 + if ((DM0CNT_H & 0x8000) && (dmamask & 1)) + { + if (((DM0CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch ((DM0CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32) - 4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch ((DM0CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32) - 4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA0) + { + int count = (DM0CNT_L ? DM0CNT_L : 0x4000) << 1; + if (DM0CNT_H & 0x0400) + count <<= 1; + log("DMA0: s=%08x d=%08x c=%04x count=%08x\n", dma0Source, dma0Dest, + DM0CNT_H, + count); + } +#endif + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + cpuDmaHack = 1; + if (DM0CNT_H & 0x4000) + { + IF |= 0x0100; + UPDATE_REG(0x202, IF); + } + + if (((DM0CNT_H >> 5) & 3) == 3) + { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if (!(DM0CNT_H & 0x0200) || (reason == 0)) + { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if ((DM1CNT_H & 0x8000) && (dmamask & 2)) + { + if (((DM1CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch ((DM1CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32) - 4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch ((DM1CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32) - 4; + break; + case 2: + destIncrement = 0; + break; + } + if (reason == 3) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA1) + { + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + 16); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, 0, 4, + 0x0400); + } + else + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA1) + { + int count = (DM1CNT_L ? DM1CNT_L : 0x4000) << 1; + if (DM1CNT_H & 0x0400) + count <<= 1; + log("DMA1: s=%08x d=%08x c=%04x count=%08x\n", dma1Source, dma1Dest, + DM1CNT_H, + count); + } +#endif + doDMA(dma1Source, dma1Dest, sourceIncrement, destIncrement, + DM1CNT_L ? DM1CNT_L : 0x4000, + DM1CNT_H & 0x0400); + } + cpuDmaHack = 1; + + if (DM1CNT_H & 0x4000) + { + IF |= 0x0200; + UPDATE_REG(0x202, IF); + } + + if (((DM1CNT_H >> 5) & 3) == 3) + { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if (!(DM1CNT_H & 0x0200) || (reason == 0)) + { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if ((DM2CNT_H & 0x8000) && (dmamask & 4)) + { + if (((DM2CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch ((DM2CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32) - 4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch ((DM2CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32) - 4; + break; + case 2: + destIncrement = 0; + break; + } + if (reason == 3) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA2) + { + int count = (4) << 2; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, 0, 4, + 0x0400); + } + else + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA2) + { + int count = (DM2CNT_L ? DM2CNT_L : 0x4000) << 1; + if (DM2CNT_H & 0x0400) + count <<= 1; + log("DMA2: s=%08x d=%08x c=%04x count=%08x\n", dma2Source, dma2Dest, + DM2CNT_H, + count); + } +#endif + doDMA(dma2Source, dma2Dest, sourceIncrement, destIncrement, + DM2CNT_L ? DM2CNT_L : 0x4000, + DM2CNT_H & 0x0400); + } + cpuDmaHack = 1; + if (DM2CNT_H & 0x4000) + { + IF |= 0x0400; + UPDATE_REG(0x202, IF); + } + + if (((DM2CNT_H >> 5) & 3) == 3) + { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if (!(DM2CNT_H & 0x0200) || (reason == 0)) + { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if ((DM3CNT_H & 0x8000) && (dmamask & 8)) + { + if (((DM3CNT_H >> 12) & 3) == reason) + { + u32 sourceIncrement = 4; + u32 destIncrement = 4; + switch ((DM3CNT_H >> 7) & 3) + { + case 0: + break; + case 1: + sourceIncrement = (u32) - 4; + break; + case 2: + sourceIncrement = 0; + break; + } + switch ((DM3CNT_H >> 5) & 3) + { + case 0: + break; + case 1: + destIncrement = (u32) - 4; + break; + case 2: + destIncrement = 0; + break; + } +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_DMA3) + { + int count = (DM3CNT_L ? DM3CNT_L : 0x10000) << 1; + if (DM3CNT_H & 0x0400) + count <<= 1; + log("DMA3: s=%08x d=%08x c=%04x count=%08x\n", dma3Source, dma3Dest, + DM3CNT_H, + count); + } +#endif + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if (DM3CNT_H & 0x4000) + { + IF |= 0x0800; + UPDATE_REG(0x202, IF); + } + + if (((DM3CNT_H >> 5) & 3) == 3) + { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if (!(DM3CNT_H & 0x0200) || (reason == 0)) + { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } + cpuDmaHack = 0; +} + +void CPUUpdateRegister(u32 address, u16 value) +{ + switch (address) + { + case 0x00: + { + bool change = ((DISPCNT ^ value) & 0x80) ? true : false; + bool changeBG = ((DISPCNT ^ value) & 0x0F00) ? true : false; + DISPCNT = (value & 0xFFF7); + UPDATE_REG(0x00, DISPCNT); + layerEnable = layerSettings & value; + windowOn = (layerEnable & 0x6000) ? true : false; + if (change && !((value & 0x80))) + { + if (!(DISPSTAT & 1)) + { + lcdTicks = 960; + // VCOUNT = 0; + // UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + // (*renderLine)(); + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if (changeBG) + CPUUpdateRenderBuffers(false); + // CPUUpdateTicks(); + break; + } + case 0x04: + DISPSTAT = (value & 0xFF38) | (DISPSTAT & 7); + UPDATE_REG(0x04, DISPSTAT); + break; + case 0x06: + // not writable + break; + case 0x08: + BG0CNT = (value & 0xDFCF); + UPDATE_REG(0x08, BG0CNT); + break; + case 0x0A: + BG1CNT = (value & 0xDFCF); + UPDATE_REG(0x0A, BG1CNT); + break; + case 0x0C: + BG2CNT = (value & 0xFFCF); + UPDATE_REG(0x0C, BG2CNT); + break; + case 0x0E: + BG3CNT = (value & 0xFFCF); + UPDATE_REG(0x0E, BG3CNT); + break; + case 0x10: + BG0HOFS = value & 511; + UPDATE_REG(0x10, BG0HOFS); + break; + case 0x12: + BG0VOFS = value & 511; + UPDATE_REG(0x12, BG0VOFS); + break; + case 0x14: + BG1HOFS = value & 511; + UPDATE_REG(0x14, BG1HOFS); + break; + case 0x16: + BG1VOFS = value & 511; + UPDATE_REG(0x16, BG1VOFS); + break; + case 0x18: + BG2HOFS = value & 511; + UPDATE_REG(0x18, BG2HOFS); + break; + case 0x1A: + BG2VOFS = value & 511; + UPDATE_REG(0x1A, BG2VOFS); + break; + case 0x1C: + BG3HOFS = value & 511; + UPDATE_REG(0x1C, BG3HOFS); + break; + case 0x1E: + BG3VOFS = value & 511; + UPDATE_REG(0x1E, BG3VOFS); + break; + case 0x20: + BG2PA = value; + UPDATE_REG(0x20, BG2PA); + break; + case 0x22: + BG2PB = value; + UPDATE_REG(0x22, BG2PB); + break; + case 0x24: + BG2PC = value; + UPDATE_REG(0x24, BG2PC); + break; + case 0x26: + BG2PD = value; + UPDATE_REG(0x26, BG2PD); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: + BG3PA = value; + UPDATE_REG(0x30, BG3PA); + break; + case 0x32: + BG3PB = value; + UPDATE_REG(0x32, BG3PB); + break; + case 0x34: + BG3PC = value; + UPDATE_REG(0x34, BG3PC); + break; + case 0x36: + BG3PD = value; + UPDATE_REG(0x36, BG3PD); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + WIN0H = value; + UPDATE_REG(0x40, WIN0H); + CPUUpdateWindow0(); + break; + case 0x42: + WIN1H = value; + UPDATE_REG(0x42, WIN1H); + CPUUpdateWindow1(); + break; + case 0x44: + WIN0V = value; + UPDATE_REG(0x44, WIN0V); + break; + case 0x46: + WIN1V = value; + UPDATE_REG(0x46, WIN1V); + break; + case 0x48: + WININ = value & 0x3F3F; + UPDATE_REG(0x48, WININ); + break; + case 0x4A: + WINOUT = value & 0x3F3F; + UPDATE_REG(0x4A, WINOUT); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD >> 6) & 3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + soundEvent(address & 0xFF, (u8)(value & 0xFF)); + soundEvent((address & 0xFF) + 1, (u8)(value >> 8)); + break; + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent(address & 0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if (start && (value & 0x8000)) + { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + break; + } + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if (start && (value & 0x8000)) + { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + break; + } + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if (start && (value & 0x8000)) + { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + break; + } + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if (start && (value & 0x8000)) + { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0, 8); + } + break; + } + case 0x100: + timer0Reload = value; + break; + case 0x102: + timer0Ticks = timer0ClockReload = TIMER_TICKS[value & 3]; + if (!timer0On && (value & 0x80)) + { + // reload the counter + TM0D = timer0Reload; + if (timer0ClockReload == 1) + timer0Ticks = 0x10000 - TM0D; + UPDATE_REG(0x100, TM0D); + } + timer0On = value & 0x80 ? true : false; + TM0CNT = value & 0xC7; + UPDATE_REG(0x102, TM0CNT); + // CPUUpdateTicks(); + break; + case 0x104: + timer1Reload = value; + break; + case 0x106: + timer1Ticks = timer1ClockReload = TIMER_TICKS[value & 3]; + if (!timer1On && (value & 0x80)) + { + // reload the counter + TM1D = timer1Reload; + if (timer1ClockReload == 1) + timer1Ticks = 0x10000 - TM1D; + UPDATE_REG(0x104, TM1D); + } + timer1On = value & 0x80 ? true : false; + TM1CNT = value & 0xC7; + UPDATE_REG(0x106, TM1CNT); + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Ticks = timer2ClockReload = TIMER_TICKS[value & 3]; + if (!timer2On && (value & 0x80)) + { + // reload the counter + TM2D = timer2Reload; + if (timer2ClockReload == 1) + timer2Ticks = 0x10000 - TM2D; + UPDATE_REG(0x108, TM2D); + } + timer2On = value & 0x80 ? true : false; + TM2CNT = value & 0xC7; + UPDATE_REG(0x10A, TM2CNT); + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Ticks = timer3ClockReload = TIMER_TICKS[value & 3]; + if (!timer3On && (value & 0x80)) + { + // reload the counter + TM3D = timer3Reload; + if (timer3ClockReload == 1) + timer3Ticks = 0x10000 - TM3D; + UPDATE_REG(0x10C, TM3D); + } + timer3On = value & 0x80 ? true : false; + TM3CNT = value & 0xC7; + UPDATE_REG(0x10E, TM3CNT); + break; + case 0x128: + if (value & 0x80) + { + value &= 0xff7f; + if (value & 1 && (value & 0x4000)) + { + UPDATE_REG(0x12a, 0xFF); + IF |= 0x80; + UPDATE_REG(0x202, IF); + value &= 0x7f7f; + } + } + UPDATE_REG(0x128, value); + break; + case 0x130: + P1 |= (value & 0x3FF); + UPDATE_REG(0x130, P1); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + case 0x200: + IE = value & 0x3FFF; + UPDATE_REG(0x200, IE); + if ((IME & 1) && (IF & IE) && armIrqEnable) + { + CPU_BREAK_LOOP_2; + } + break; + case 0x202: + IF ^= (value & IF); + UPDATE_REG(0x202, IF); + break; + case 0x204: + { + int i; + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + if (!speedHack) + { + memoryWait[0x08] = memoryWait[0x09] = gamepakWaitState[(value >> 2) & 7]; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = + gamepakWaitState0[(value >> 2) & 7]; + + memoryWait[0x0a] = memoryWait[0x0b] = gamepakWaitState[(value >> 5) & 7]; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = + gamepakWaitState1[(value >> 5) & 7]; + + memoryWait[0x0c] = memoryWait[0x0d] = gamepakWaitState[(value >> 8) & 7]; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = + gamepakWaitState2[(value >> 8) & 7]; + } + else + { + memoryWait[0x08] = memoryWait[0x09] = 4; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 2; + + memoryWait[0x0a] = memoryWait[0x0b] = 4; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 4; + + memoryWait[0x0c] = memoryWait[0x0d] = 4; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 8; + } + for (i = 0; i < 16; i++) + { + memoryWaitFetch32[i] = memoryWait32[i] = memoryWait[i] * + (memory32[i] ? 1 : 2); + memoryWaitFetch[i] = memoryWait[i]; + } + memoryWaitFetch32[3] += 1; + memoryWaitFetch32[2] += 3; + + prefetchActive = false; + prefetchApplies = false; + if (value & 0x4000) + { + for (i = 8; i < 16; i++) + { + memoryWaitFetch32[i] = 2 * cpuMemoryWait[i]; + memoryWaitFetch[i] = cpuMemoryWait[i]; + } + if (((value & 3) == 3)) + { + if (!memLagTempEnabled) + { + memoryWaitFetch[8]--; // hack to prevent inaccurately extreme lag at some points of many games (possibly + // from no pre-fetch emulation) + /// FIXME: how correct is this? Should it set the fetch to 0 or change fetch32 or + // anything else? + + prefetchActive = true; + } + prefetchApplies = true; + } + } + //if(prefetchActive && !prefetchPrevActive) systemScreenMessage("pre-fetch enabled",3,600); + //if(!prefetchActive && prefetchPrevActive) systemScreenMessage("pre-fetch disabled",3,600); + prefetchPrevActive = prefetchActive; + + UPDATE_REG(0x204, value); + break; + } + case 0x208: + IME = value & 1; + UPDATE_REG(0x208, IME); + if ((IME & 1) && (IF & IE) && armIrqEnable) + { + CPU_BREAK_LOOP_2; + } + break; + case 0x300: + if (value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address & 0x3FE, value); + break; + } +} + +void CPUWriteHalfWordWrapped(u32 address, u16 value) +{ +#ifdef GBA_LOGGING + if (address & 1) + { + if (systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch (address >> 24) + { + case 2: +#ifdef SDL + if (*((u16 *)&freezeWorkRAM[address & 0x3FFFE])) + cheatsWriteHalfWord((u16 *)&workRAM[address & 0x3FFFE], + value, + *((u16 *)&freezeWorkRAM[address & 0x3FFFE])); + else +#endif + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]), value); + break; + case 3: +#ifdef SDL + if (*((u16 *)&freezeInternalRAM[address & 0x7ffe])) + cheatsWriteHalfWord((u16 *)&internalRAM[address & 0x7ffe], + value, + *((u16 *)&freezeInternalRAM[address & 0x7ffe])); + else +#endif + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + CPUUpdateRegister(address & 0x3fe, value); + break; + case 5: + WRITE16LE(((u16 *)&paletteRAM[address & 0x3fe]), value); + break; + case 6: + if (address & 0x10000) + WRITE16LE(((u16 *)&vram[address & 0x17ffe]), value); + else + WRITE16LE(((u16 *)&vram[address & 0x1fffe]), value); + break; + case 7: + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + { + if (!rtcWrite(address, value)) + goto unwritable; + } + else if (!agbPrintWrite(address, value)) + goto unwritable; + break; + case 13: + if (cpuEEPROMEnabled) + { + eepromWrite(address, (u8)(value & 0xFF)); + break; + } + goto unwritable; + case 14: + if (!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, (u8)(value & 0xFF)); + break; + } + goto unwritable; + default: +unwritable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal halfword write: %04x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +void CPUWriteHalfWord(u32 address, u16 value) +{ + CPUWriteHalfWordWrapped(address, value); + CallRegisteredLuaMemHook(address, 2, value, LUAMEMHOOK_WRITE); +} + +void CPUWriteByteWrapped(u32 address, u8 b) +{ + switch (address >> 24) + { + case 2: +#ifdef SDL + if (freezeWorkRAM[address & 0x3FFFF]) + cheatsWriteByte(&workRAM[address & 0x3FFFF], b); + else +#endif + workRAM[address & 0x3FFFF] = b; + break; + case 3: +#ifdef SDL + if (freezeInternalRAM[address & 0x7fff]) + cheatsWriteByte(&internalRAM[address & 0x7fff], b); + else +#endif + internalRAM[address & 0x7fff] = b; + break; + case 4: + switch (address & 0x3FF) + { + case 0x301: + if (b == 0x80) + stopState = true; + holdState = 1; + holdType = -1; + break; + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + soundEvent(address & 0xFF, b); + break; + default: + // if(address & 1) { + // CPUWriteHalfWord(address-1, (CPUReadHalfWord(address-1)&0x00FF)|((int)b<<8)); + // } else + if (address & 1) + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe]))) + & 0x00FF) | + b << 8); + else + CPUUpdateRegister(address & 0x3fe, + ((READ16LE(((u16 *)&ioMem[address & 0x3fe])) & 0xFF00) | b)); + } + break; + case 5: + // no need to switch + *((u16 *)&paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + // no need to switch + if (address & 0x10000) + *((u16 *)&vram[address & 0x17FFE]) = (b << 8) | b; + else + *((u16 *)&vram[address & 0x1FFFE]) = (b << 8) | b; + break; + case 7: + // no need to switch + *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if (cpuEEPROMEnabled) + { + eepromWrite(address, b); + break; + } + goto unwritable; + case 14: + if (!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, b); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal byte write: %02x to %08x from %08x\n", + b, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +void CPUWriteByte(u32 address, u8 b) +{ + CPUWriteByteWrapped(address, b); + CallRegisteredLuaMemHook(address, 1, b, LUAMEMHOOK_WRITE); +} + +bool CPULoadBios(const char *biosFileName, bool useBiosFile) +{ + useBios = false; + if (useBiosFile) + { + useBios = utilLoadBIOS(bios, biosFileName, 4); + if (!useBios) + { + systemMessage(MSG_INVALID_BIOS_FILE_SIZE, N_("Invalid GBA BIOS file")); + } + } + + if (!useBios) + { + // load internal BIOS + memcpy(bios, myROM, sizeof(myROM)); + } + + return useBios; +} + +void CPUInit() +{ +#ifdef WORDS_BIGENDIAN + if (!cpuBiosSwapped) + { + for (unsigned int i = 0; i < sizeof(myROM) / 4; i++) + { + WRITE32LE(&myROM[i], myROM[i]); + } + cpuBiosSwapped = true; + } +#endif + gbaSaveType = 0; + eepromInUse = 0; + saveType = 0; + + if (!useBios) + { + // load internal BIOS + memcpy(bios, myROM, sizeof(myROM)); + } + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + int i = 0; + for (i = 0; i < 256; i++) + { + int cpuBitSetCount = 0; + int j; + for (j = 0; j < 8; j++) + if (i & (1 << j)) + cpuBitSetCount++; + cpuBitsSet[i] = cpuBitSetCount; + + for (j = 0; j < 8; j++) + if (i & (1 << j)) + break; + cpuLowestBitSet[i] = j; + } + + for (i = 0; i < 0x400; i++) + ioReadable[i] = true; + for (i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for (i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for (i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for (i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for (i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for (i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for (i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for (i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for (i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for (i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for (i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for (i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for (i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for (i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for (i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for (i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + *((u16 *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((u16 *)&rom[0x1fe209e]) = 0x4770; // BX LR + + { + int32 origMemoryWaitFetch[16] = { 3, 0, 3, 0, 0, 1, 1, 0, 4, 4, 4, 4, 4, 4, 4, 0 }; + int32 origMemoryWaitFetch32[16] = { 6, 0, 6, 0, 0, 2, 2, 0, 8, 8, 8, 8, 8, 8, 8, 0 }; + memcpy(memoryWaitFetch, origMemoryWaitFetch, 16 * sizeof(int32)); + memcpy(memoryWaitFetch32, origMemoryWaitFetch32, 16 * sizeof(int32)); + } +} + +void CPUReset(bool userReset) +{ + // movie must be closed while opening/creating a movie + if (userReset && VBAMovieRecording()) + { + VBAMovieSignalReset(); + return; + } + + if (!VBAMovieActive()) + { + GBASystemCounters.frameCount = 0; + GBASystemCounters.lagCount = 0; + GBASystemCounters.extraCount = 0; + GBASystemCounters.lagged = true; + GBASystemCounters.laggedLast = true; + } + + if (gbaSaveType == 0) + { + if (eepromInUse) + gbaSaveType = 3; + else + switch (saveType) + { + case 1: + gbaSaveType = 1; + break; + case 2: + gbaSaveType = 2; + break; + } + } + + rtcReset(); + // clean registers + memset(®[0], 0, sizeof(reg)); + // clean OAM + memset(oam, 0, 0x400); + // clean palette + memset(paletteRAM, 0, 0x400); + // clean picture + memset(pix, 0, 4 * 241 * 162); + // clean vram + memset(vram, 0, 0x20000); + // clean io memory + memset(ioMem, 0, 0x400); + // clean RAM + memset(internalRAM, 0, 0x8000); /// FIXME: is it unsafe to erase ALL of this? Even the init code doesn't. + memset(workRAM, 0, 0x40000); /// ditto + + DISPCNT = 0x0080; + DISPSTAT = 0x0000; + VCOUNT = 0x0000; + BG0CNT = 0x0000; + BG1CNT = 0x0000; + BG2CNT = 0x0000; + BG3CNT = 0x0000; + BG0HOFS = 0x0000; + BG0VOFS = 0x0000; + BG1HOFS = 0x0000; + BG1VOFS = 0x0000; + BG2HOFS = 0x0000; + BG2VOFS = 0x0000; + BG3HOFS = 0x0000; + BG3VOFS = 0x0000; + BG2PA = 0x0100; + BG2PB = 0x0000; + BG2PC = 0x0000; + BG2PD = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + BG3PA = 0x0100; + BG3PB = 0x0000; + BG3PC = 0x0000; + BG3PD = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + WIN0H = 0x0000; + WIN1H = 0x0000; + WIN0V = 0x0000; + WIN1V = 0x0000; + WININ = 0x0000; + WINOUT = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + TM0D = 0x0000; + TM0CNT = 0x0000; + TM1D = 0x0000; + TM1CNT = 0x0000; + TM2D = 0x0000; + TM2CNT = 0x0000; + TM3D = 0x0000; + TM3CNT = 0x0000; + P1 = 0x03FF; + IE = 0x0000; + IF = 0x0000; + IME = 0x0000; + + armMode = 0x1F; + + if (cpuIsMultiBoot) + { + reg[13].I = 0x03007F00; + reg[15].I = 0x02000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + else + { + if (useBios && !skipBios) + { + reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } + else + { + reg[13].I = 0x03007F00; + reg[15].I = 0x08000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, DISPCNT); + UPDATE_REG(0x20, BG2PA); + UPDATE_REG(0x26, BG2PD); + UPDATE_REG(0x30, BG3PA); + UPDATE_REG(0x36, BG3PD); + UPDATE_REG(0x130, P1); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + reg[16].I |= 0x40; + CPUUpdateCPSR(); + + armNextPC = reg[15].I; + reg[15].I += 4; + + // reset internal state + holdState = false; + holdType = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + BIOS_RegisterRamReset(); + + lcdTicks = 960; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + cpuSaveGameFunc = flashSaveDecide; + renderLine = mode0RenderLine; + fxOn = false; + windowOn = false; + frameSkipCount = 0; + saveType = 0; + layerEnable = DISPCNT & layerSettings; + + CPUUpdateRenderBuffers(true); + + for (int i = 0; i < 256; i++) + { + map[i].address = (u8 *)&dummyAddress; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if (!useBios) + { + if (cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else + BIOS_RegisterRamReset(0xff); + } + else + { + if (cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + } + + switch (cpuSaveType) + { + case 0: // automatic + cpuSramEnabled = true; + cpuFlashEnabled = true; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + break; + case 1: // EEPROM + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + break; + case 2: // SRAM + cpuSramEnabled = true; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = sramWrite; + break; + case 3: // FLASH + cpuSramEnabled = false; + cpuFlashEnabled = true; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = flashWrite; + break; + case 4: // EEPROM+Sensor + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + break; + case 5: // NONE + cpuSramEnabled = false; + cpuFlashEnabled = false; + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + break; + } + + systemResetSensor(); + + systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED; + + gbaLastTime = systemGetClock(); + gbaFrameCount = 0; + + systemRefreshScreen(); +} + +void CPUInterrupt() +{ + u32 PC = reg[15].I; + bool savedState = armState; + CPUSwitchMode(0x12, true, false); + reg[14].I = PC; + if (!savedState) + reg[14].I += 2; + reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + armNextPC = reg[15].I; + reg[15].I += 4; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +void TogglePrefetchHack() +{ + memLagTempEnabled = !memLagTempEnabled; + + if (emulating) + { + extern bool8 prefetchActive, prefetchPrevActive, prefetchApplies; + if (prefetchApplies && prefetchActive == memLagTempEnabled) + { + prefetchActive = !prefetchActive; + //if(prefetchActive && !prefetchPrevActive) systemScreenMessage("pre-fetch enabled",3,600); + //if(!prefetchActive && prefetchPrevActive) systemScreenMessage("pre-fetch disabled",3,600); + extern int32 memoryWaitFetch [16]; + if (prefetchActive) + memoryWaitFetch[8]--; + else + memoryWaitFetch[8]++; + prefetchPrevActive = prefetchActive; + } + } +} + +void SetPrefetchHack(bool set) +{ + if ((bool)memLagTempEnabled == set) + TogglePrefetchHack(); +} + +#ifdef SDL +void log(const char *defaultMsg, ...) +{ + char buffer[2048]; + va_list valist; + + va_start(valist, defaultMsg); + vsprintf(buffer, defaultMsg, valist); + + if (out == NULL) + { + out = fopen("trace.log", "w"); + } + + fputs(buffer, out); + + va_end(valist); +} + +#else +extern void winlog(const char *, ...); +#endif + +void CPULoop(int _ticks) +{ + int32 ticks = _ticks; + int32 clockTicks; + int32 cpuLoopTicks = 0; + int32 timerOverflow = 0; + // variables used by the CPU core + + extCpuLoopTicks = &cpuLoopTicks; + extClockTicks = &clockTicks; + extTicks = &ticks; + + cpuLoopTicks = CPUUpdateTicks(); + if (cpuLoopTicks > ticks) + { + cpuLoopTicks = ticks; + cpuSavedTicks = ticks; + } + + if (intState) + { + cpuLoopTicks = 5; + cpuSavedTicks = 5; + } + + if (newFrame) + { + extern void VBAOnExitingFrameBoundary(); + VBAOnExitingFrameBoundary(); + + // update joystick information + systemReadJoypads(); + + u32 joy = systemGetJoypad(0, cpuEEPROMSensorEnabled); + +// if (cpuEEPROMSensorEnabled) +// systemUpdateMotionSensor(0); + + P1 = 0x03FF ^ (joy & 0x3FF); + UPDATE_REG(0x130, P1); + u16 P1CNT = READ16LE(((u16 *)&ioMem[0x132])); + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if ((P1CNT & 0x4000) || stopState) + { + u16 p1 = (0x3FF ^ P1) & 0x3FF; + if (P1CNT & 0x8000) + { + if (p1 == (P1CNT & 0x3FF)) + { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + else + { + if (p1 & P1CNT) + { + IF |= 0x1000; + UPDATE_REG(0x202, IF); + } + } + } + + // HACK: some special "buttons" + extButtons = (joy >> 18); + speedup = (extButtons & 1) != 0; + + VBAMovieResetIfRequested(); + + CallRegisteredLuaFunctions(LUACALL_BEFOREEMULATION); + + newFrame = false; + } + + for (;; ) + { +#ifndef FINAL_VERSION + if (systemDebug) + { + if (systemDebug >= 10 && !holdState) + { + CPUUpdateCPSR(); + sprintf( + buffer, + "R00=%08x R01=%08x R02=%08x R03=%08x R04=%08x R05=%08x R06=%08x R07=%08x R08=%08x" + "R09=%08x R10=%08x R11=%08x R12=%08x R13=%08x R14=%08x R15=%08x R16=%08x R17=%08x\n", + reg[0].I, + reg[1].I, + reg[2].I, + reg[3].I, + reg[4].I, + reg[5].I, + reg[6].I, + reg[7].I, + reg[8].I, + reg[9].I, + reg[10].I, + reg[11].I, + reg[12].I, + reg[13].I, + reg[14].I, + reg[15].I, + reg[16].I, + reg[17].I); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + else if (!holdState) + { + sprintf(buffer, "PC=%08x\n", armNextPC); +#ifdef SDL + log(buffer); +#else + winlog(buffer); +#endif + } + } +#endif + + if (!holdState) + { + if (armState) + { + CallRegisteredLuaMemHook(armNextPC, 4, CPUReadMemoryQuick(armNextPC), LUAMEMHOOK_EXEC); +#include "arm-new.h" + } + else + { + CallRegisteredLuaMemHook(armNextPC, 2, CPUReadHalfWordQuick(armNextPC), LUAMEMHOOK_EXEC); +#include "thumb.h" + } + } + else + { + clockTicks = lcdTicks; + + if (soundTicks < clockTicks) + clockTicks = soundTicks; + + if (timer0On && (timer0Ticks < clockTicks)) + { + clockTicks = timer0Ticks; + } + if (timer1On && (timer1Ticks < clockTicks)) + { + clockTicks = timer1Ticks; + } + if (timer2On && (timer2Ticks < clockTicks)) + { + clockTicks = timer2Ticks; + } + if (timer3On && (timer3Ticks < clockTicks)) + { + clockTicks = timer3Ticks; + } +#ifdef PROFILING + if (profilingTicksReload != 0) + { + if (profilingTicks < clockTicks) + { + clockTicks = profilingTicks; + } + } +#endif + } + + cpuLoopTicks -= clockTicks; + if ((cpuLoopTicks <= 0)) + { + if (cpuSavedTicks) + { + clockTicks = cpuSavedTicks; // + cpuLoopTicks; + } + cpuDmaTicksToUpdate = -cpuLoopTicks; + +updateLoop: + lcdTicks -= clockTicks; + + if (lcdTicks <= 0) + { + if (DISPSTAT & 1) // V-BLANK + { // if in V-Blank mode, keep computing... + if (DISPSTAT & 2) + { + lcdTicks += 960; + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + else + { + lcdTicks += 272; + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + if (DISPSTAT & 16) + { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + + if (VCOUNT >= 228) + { + DISPSTAT &= 0xFFFC; + UPDATE_REG(0x04, DISPSTAT); + VCOUNT = 0; + UPDATE_REG(0x06, VCOUNT); + CPUCompareVCOUNT(); + } + } + else + { + int framesToSkip = systemFramesToSkip(); + + if (DISPSTAT & 2) + { + // if in H-Blank, leave it and move to drawing mode + VCOUNT++; + UPDATE_REG(0x06, VCOUNT); + + lcdTicks += 960; + DISPSTAT &= 0xFFFD; + if (VCOUNT == 160) + { + DISPSTAT |= 1; + DISPSTAT &= 0xFFFD; + UPDATE_REG(0x04, DISPSTAT); + if (DISPSTAT & 0x0008) + { + IF |= 1; + UPDATE_REG(0x202, IF); + } + CPUCheckDMA(1, 0x0f); + + systemFrame(); + + ++gbaFrameCount; + u32 gbaCurrentTime = systemGetClock(); + if (gbaCurrentTime - gbaLastTime >= 1000) + { + systemShowSpeed(int(float(gbaFrameCount) * 100000 / (float(gbaCurrentTime - gbaLastTime) * 60) + .5f)); + gbaLastTime = gbaCurrentTime; + gbaFrameCount = 0; + } + + ++GBASystemCounters.frameCount; + if (GBASystemCounters.lagged) + { + ++GBASystemCounters.lagCount; + } + GBASystemCounters.laggedLast = GBASystemCounters.lagged; + GBASystemCounters.lagged = true; + + if (cheatsEnabled) + cheatsCheckKeys(P1 ^ 0x3FF, extButtons); + + extern void VBAOnEnteringFrameBoundary(); + VBAOnEnteringFrameBoundary(); + + newFrame = true; + + pauseAfterFrameAdvance = systemPauseOnFrame(); + + if (frameSkipCount >= framesToSkip || pauseAfterFrameAdvance) + { + systemRenderFrame(); + frameSkipCount = 0; + + bool capturePressed = (extButtons & 2) != 0; + if (capturePressed && !capturePrevious) + { + captureNumber = systemScreenCapture(captureNumber); + } + capturePrevious = capturePressed && !pauseAfterFrameAdvance; + } + else + { + ++frameSkipCount; + } + + if (pauseAfterFrameAdvance) + { + systemSetPause(true); + } + } + + UPDATE_REG(0x04, DISPSTAT); + CPUCompareVCOUNT(); + } + else + { + if (frameSkipCount >= framesToSkip || pauseAfterFrameAdvance) + { + (*renderLine)(); + + switch (systemColorDepth) + { + case 16: + { + u16 *dest = (u16 *)pix + 242 * (VCOUNT + 1); + for (int x = 0; x < 240; ) + { + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap16[lineMix[x++] & 0xFFFF]; + } + // for filters that read past the screen + *dest++ = 0; + break; + } + case 24: + { + u8 *dest = (u8 *)pix + 240 * VCOUNT * 3; + for (int x = 0; x < 240; ) + { + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + *((u32 *)dest) = systemColorMap32[lineMix[x++] & 0xFFFF]; + dest += 3; + } + break; + } + case 32: + { + u32 *dest = (u32 *)pix + 241 * (VCOUNT + 1); + for (int x = 0; x < 240; ) + { + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + *dest++ = systemColorMap32[lineMix[x++] & 0xFFFF]; + } + break; + } + } + } + // entering H-Blank + DISPSTAT |= 2; + UPDATE_REG(0x04, DISPSTAT); + lcdTicks += 272; + CPUCheckDMA(2, 0x0f); + if (DISPSTAT & 16) + { + IF |= 2; + UPDATE_REG(0x202, IF); + } + } + } + } + + if (!stopState) + { + if (timer0On) + { + if (timer0ClockReload == 1) + { + u32 tm0d = TM0D + clockTicks; + if (tm0d > 0xffff) + { + tm0d += timer0Reload; + timerOverflow |= 1; + soundTimerOverflow(0); + if (TM0CNT & 0x40) + { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + TM0D = tm0d & 0xFFFF; + timer0Ticks = 0x10000 - TM0D; + UPDATE_REG(0x100, TM0D); + } + else + { + timer0Ticks -= clockTicks; + if (timer0Ticks <= 0) + { + timer0Ticks += timer0ClockReload; + TM0D++; + if (TM0D == 0) + { + TM0D = timer0Reload; + timerOverflow |= 1; + soundTimerOverflow(0); + if (TM0CNT & 0x40) + { + IF |= 0x08; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x100, TM0D); + } + } + } + + if (timer1On) + { + if (TM1CNT & 4) + { + if (timerOverflow & 1) + { + TM1D++; + if (TM1D == 0) + { + TM1D += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if (TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } + else + { + if (timer1ClockReload == 1) + { + u32 tm1d = TM1D + clockTicks; + if (tm1d > 0xffff) + { + tm1d += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if (TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + TM1D = tm1d & 0xFFFF; + timer1Ticks = 0x10000 - TM1D; + UPDATE_REG(0x104, TM1D); + } + else + { + timer1Ticks -= clockTicks; + if (timer1Ticks <= 0) + { + timer1Ticks += timer1ClockReload; + TM1D++; + + if (TM1D == 0) + { + TM1D = timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if (TM1CNT & 0x40) + { + IF |= 0x10; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x104, TM1D); + } + } + } + } + + if (timer2On) + { + if (TM2CNT & 4) + { + if (timerOverflow & 2) + { + TM2D++; + if (TM2D == 0) + { + TM2D += timer2Reload; + timerOverflow |= 4; + if (TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } + else + { + if (timer2ClockReload == 1) + { + u32 tm2d = TM2D + clockTicks; + if (tm2d > 0xffff) + { + tm2d += timer2Reload; + timerOverflow |= 4; + if (TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + TM2D = tm2d & 0xFFFF; + timer2Ticks = 0x10000 - TM2D; + UPDATE_REG(0x108, TM2D); + } + else + { + timer2Ticks -= clockTicks; + if (timer2Ticks <= 0) + { + timer2Ticks += timer2ClockReload; + TM2D++; + + if (TM2D == 0) + { + TM2D = timer2Reload; + timerOverflow |= 4; + if (TM2CNT & 0x40) + { + IF |= 0x20; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x108, TM2D); + } + } + } + } + + if (timer3On) + { + if (TM3CNT & 4) + { + if (timerOverflow & 4) + { + TM3D++; + if (TM3D == 0) + { + TM3D += timer3Reload; + if (TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10c, TM3D); + } + } + else + { + if (timer3ClockReload == 1) + { + u32 tm3d = TM3D + clockTicks; + if (tm3d > 0xffff) + { + tm3d += timer3Reload; + if (TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + TM3D = tm3d & 0xFFFF; + timer3Ticks = 0x10000 - TM3D; + UPDATE_REG(0x10C, TM3D); + } + else + { + timer3Ticks -= clockTicks; + if (timer3Ticks <= 0) + { + timer3Ticks += timer3ClockReload; + TM3D++; + + if (TM3D == 0) + { + TM3D = timer3Reload; + if (TM3CNT & 0x40) + { + IF |= 0x40; + UPDATE_REG(0x202, IF); + } + } + UPDATE_REG(0x10C, TM3D); + } + } + } + } + } + // we shouldn't be doing sound in stop state, but we lose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + soundTicks -= clockTicks; + if (soundTicks < 1) + { + soundTick(); + soundTicks += SOUND_CLOCK_TICKS; + } + timerOverflow = 0; + +#ifdef PROFILING + profilingTicks -= clockTicks; + if (profilingTicks <= 0) + { + profilingTicks += profilingTicksReload; + if (profilBuffer && profilSize) + { + u16 *b = (u16 *)profilBuffer; + int pc = ((reg[15].I - profilLowPC) * profilScale) / 0x10000; + if (pc >= 0 && pc < profilSize) + { + b[pc]++; + } + } + } +#endif + + ticks -= clockTicks; + cpuLoopTicks = CPUUpdateTicks(); + + // FIXME: it is too bad that it is still not determined whether the loop can be exited at this point + if (cpuDmaTicksToUpdate > 0) + { + clockTicks = cpuSavedTicks; + if (clockTicks > cpuDmaTicksToUpdate) + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if (cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + goto updateLoop; // this is evil + } + + if (IF && (IME & 1) && armIrqEnable) + { + int res = IF & IE; + if (stopState) + res &= 0x3080; + if (res) + { + if (intState) + { + CPUInterrupt(); + intState = false; + if (holdState) + { + holdState = false; + stopState = false; + } + } + else + { + if (!holdState) + { + intState = true; + cpuLoopTicks = 5; + cpuSavedTicks = 5; + } + else + { + CPUInterrupt(); + if (holdState) + { + holdState = false; + stopState = false; + } + } + } + } + } + + if (useOldFrameTiming) + { + if (ticks <= 0) + { + newFrame = true; + break; + } + } + else if (newFrame) + { + // FIXME: it should be enough to use frameBoundary only if there were no need for supporting the old timing + // but is there still any GBA .vbm that uses the old timing? +/// extern void VBAOnEnteringFrameBoundary(); +/// VBAOnEnteringFrameBoundary(); + + break; + } + } + } +} + +struct EmulatedSystem GBASystem = +{ + // emuMain + CPULoop, + // emuReset + CPUReset, + // emuCleanUp + CPUCleanUp, + // emuReadBattery + CPUReadBatteryFile, + // emuWriteBattery + CPUWriteBatteryFile, + // emuReadBatteryFromStream + CPUReadBatteryFromStream, + // emuWriteBatteryToStream + CPUWriteBatteryToStream, + // emuReadState + CPUReadState, + // emuWriteState + CPUWriteState, + // emuReadStateFromStream + CPUReadStateFromStream, + // emuWriteStateToStream + CPUWriteStateToStream, + // emuReadMemState + CPUReadMemState, + // emuWriteMemState + CPUWriteMemState, + // emuWritePNG + CPUWritePNGFile, + // emuWriteBMP + CPUWriteBMPFile, + // emuUpdateCPSR + CPUUpdateCPSR, + // emuHasDebugger + true, + // emuCount +#ifdef FINAL_VERSION + 250000, +#else + 5000, +#endif +}; + +// is there a reason to use more than one set of counters? +EmulatedSystemCounters &GBASystemCounters = systemCounters; + +/* + EmulatedSystemCounters GBASystemCounters = + { + // frameCount + 0, + // lagCount + 0, + // lagged + true, + // laggedLast + true, + }; + */ + + +#undef CPU_BREAK_LOOP +#undef CPU_BREAK_LOOP2 diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBA.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBA.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,122 @@ +#ifndef VBA_GBA_H +#define VBA_GBA_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +#if (defined(WIN32) && !defined(SDL)) +#include "../win32/stdafx.h" // for HANDLE +//#include // for HANDLE +// NOTE: if you get this error: +// #error WINDOWS.H already included. MFC apps must not #include +// it is probably because stdafx.h is getting included at the wrong place +// (i.e. after anything else) in a file, or your precompiled headers are otherwise wrong +#endif + +#define SAVE_GAME_VERSION_1 1 +#define SAVE_GAME_VERSION_2 2 +#define SAVE_GAME_VERSION_3 3 +#define SAVE_GAME_VERSION_4 4 +#define SAVE_GAME_VERSION_5 5 +#define SAVE_GAME_VERSION_6 6 +#define SAVE_GAME_VERSION_7 7 +#define SAVE_GAME_VERSION_8 8 +#define SAVE_GAME_VERSION_9 9 +#define SAVE_GAME_VERSION_10 10 +#define SAVE_GAME_VERSION_11 11 +#define SAVE_GAME_VERSION_12 12 +#define SAVE_GAME_VERSION_13 13 +#define SAVE_GAME_VERSION SAVE_GAME_VERSION_13 + +#if (defined(WIN32) && !defined(SDL)) +extern HANDLE mapROM; // shared memory handles +extern HANDLE mapWORKRAM; +extern HANDLE mapBIOS; +extern HANDLE mapIRAM; +extern HANDLE mapPALETTERAM; +extern HANDLE mapVRAM; +extern HANDLE mapOAM; +extern HANDLE mapPIX; +extern HANDLE mapIOMEM; +#endif + +/* +extern reg_pair reg[45]; +extern u8 biosProtected[4]; + +extern bool8 N_FLAG; +extern bool8 Z_FLAG; +extern bool8 C_FLAG; +extern bool8 V_FLAG; +extern bool8 armIrqEnable; +extern bool8 armState; +extern int32 armMode; +*/ +extern void (*cpuSaveGameFunc)(u32, u8); + +extern bool8 freezeWorkRAM[0x40000]; +extern bool8 freezeInternalRAM[0x8000]; +extern bool CPUReadGSASnapshot(const char *); +extern bool CPUWriteGSASnapshot(const char *, const char *, const char *, const char *); +extern bool CPUWriteBatteryFile(const char *); +extern bool CPUReadBatteryFile(const char *); +extern bool CPUWriteBatteryToStream(gzFile); +extern bool CPUReadBatteryFromStream(gzFile); +extern bool CPUExportEepromFile(const char *); +extern bool CPUImportEepromFile(const char *); +extern bool CPUWritePNGFile(const char *); +extern bool CPUWriteBMPFile(const char *); +extern void CPUCleanUp(); +extern void CPUUpdateRender(); +extern void CPUUpdateRenderBuffers(bool force); +extern bool CPUReadMemState(char *, int); +extern bool CPUReadState(const char *); +extern bool CPUWriteMemState(char *, int); +extern bool CPUWriteState(const char *); +extern bool CPUReadStateFromStream(gzFile); +extern bool CPUWriteStateToStream(gzFile); +extern int CPULoadRom(const char *); +extern void CPUUpdateRegister(u32, u16); +extern void CPUWriteHalfWord(u32, u16); +extern void CPUWriteByte(u32, u8); +extern bool CPULoadBios(const char *, bool); +extern void CPUInit(); +extern void CPUReset(bool userReset = false); +extern void CPULoop(int); +extern void CPUCheckDMA(int, int); +#ifdef PROFILING +extern void cpuProfil(char *buffer, int, u32, int); +extern void cpuEnableProfiling(int hz); +#endif + +extern struct EmulatedSystem GBASystem; +extern struct EmulatedSystemCounters &GBASystemCounters; + +#define R13_IRQ 18 +#define R14_IRQ 19 +#define SPSR_IRQ 20 +#define R13_USR 26 +#define R14_USR 27 +#define R13_SVC 28 +#define R14_SVC 29 +#define SPSR_SVC 30 +#define R13_ABT 31 +#define R14_ABT 32 +#define SPSR_ABT 33 +#define R13_UND 34 +#define R14_UND 35 +#define SPSR_UND 36 +#define R8_FIQ 37 +#define R9_FIQ 38 +#define R10_FIQ 39 +#define R11_FIQ 40 +#define R12_FIQ 41 +#define R13_FIQ 42 +#define R14_FIQ 43 +#define SPSR_FIQ 44 + +#endif // VBA_GBA_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBACheats.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBACheats.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1822 @@ +#include +#include +#include + +#include "../common/System.h" +#include "../common/Util.h" +#include "../NLS.h" +#include "GBACheats.h" +#include "GBA.h" +#include "GBAinline.h" +#include "GBAGlobals.h" + +/** + * Gameshark code types: + * + * NNNNNNNN 001DC0DE - ID code for the game (game 4 character name) from ROM + * DEADFACE XXXXXXXX - changes decryption seeds + * 0AAAAAAA 000000YY - 8-bit constant write + * 1AAAAAAA 0000YYYY - 16-bit constant write + * 2AAAAAAA YYYYYYYY - 32-bit constant write + * 3AAAAAAA YYYYYYYY - ?? + * 6AAAAAAA 0000YYYY - 16-bit ROM Patch (address >> 1) + * 6AAAAAAA 1000YYYY - 16-bit ROM Patch ? (address >> 1) + * 6AAAAAAA 2000YYYY - 16-bit ROM Patch ? (address >> 1) + * 8A1AAAAA 000000YY - 8-bit button write + * 8A2AAAAA 0000YYYY - 16-bit button write + * 8A3AAAAA YYYYYYYY - 32-bit button write + * 80F00000 0000YYYY - button slow motion + * DAAAAAAA 0000YYYY - if address contains 16-bit value enable next code + * FAAAAAAA 0000YYYY - Master code function + * + * CodeBreaker codes types: + * + * 0000AAAA 000Y - Game CRC (Y are flags: 8 - CRC, 2 - DI) + * 1AAAAAAA YYYY - Master Code function (store address at ((YYYY << 0x16) + * + 0x08000100)) + * 2AAAAAAA YYYY - 16-bit or + * 3AAAAAAA YYYY - 8-bit constant write + * 4AAAAAAA YYYY - Slide code + * XXXXCCCC IIII (C is count and I is address increment, X is value incr.) + * 5AAAAAAA CCCC - Super code (Write bytes to address, CCCC is count) + * BBBBBBBB BBBB + * 6AAAAAAA YYYY - 16-bit and + * 7AAAAAAA YYYY - if address contains 16-bit value enable next code + * 8AAAAAAA YYYY - 16-bit constant write + * 9AAAAAAA YYYY - change decryption (when first code only?) + * AAAAAAAA YYYY - if address does not contain 16-bit value enable next code + * BAAAAAAA YYYY - if 16-bit < YYYY + * CAAAAAAA YYYY - if 16-bit > YYYY + * D0000020 YYYY - if button keys equal value enable next code + * EAAAAAAA YYYY - increase value stored in address + */ +#define UNKNOWN_CODE -1 +#define INT_8_BIT_WRITE 0 +#define INT_16_BIT_WRITE 1 +#define INT_32_BIT_WRITE 2 +#define GSA_16_BIT_ROM_PATCH 3 +#define GSA_8_BIT_GS_WRITE 4 +#define GSA_16_BIT_GS_WRITE 5 +#define GSA_32_BIT_GS_WRITE 6 +#define CBA_IF_KEYS_PRESSED 7 +#define CBA_IF_TRUE 8 +#define CBA_SLIDE_CODE 9 +#define CBA_IF_FALSE 10 +#define CBA_AND 11 +#define GSA_8_BIT_GS_WRITE2 12 +#define GSA_16_BIT_GS_WRITE2 13 +#define GSA_32_BIT_GS_WRITE2 14 +#define GSA_16_BIT_ROM_PATCH2 15 +#define GSA_8_BIT_SLIDE 16 +#define GSA_16_BIT_SLIDE 17 +#define GSA_32_BIT_SLIDE 18 +#define GSA_8_BIT_IF_TRUE 19 +#define GSA_32_BIT_IF_TRUE 20 +#define GSA_8_BIT_IF_FALSE 21 +#define GSA_32_BIT_IF_FALSE 22 +#define GSA_8_BIT_FILL 23 +#define GSA_16_BIT_FILL 24 +#define GSA_8_BIT_IF_TRUE2 25 +#define GSA_16_BIT_IF_TRUE2 26 +#define GSA_32_BIT_IF_TRUE2 27 +#define GSA_8_BIT_IF_FALSE2 28 +#define GSA_16_BIT_IF_FALSE2 29 +#define GSA_32_BIT_IF_FALSE2 30 +#define GSA_SLOWDOWN 31 +#define CBA_ADD 32 +#define CBA_OR 33 +#define CBA_LT 34 +#define CBA_GT 35 +#define CBA_SUPER 36 + +CheatsData cheatsList[100]; +int cheatsNumber = 0; + +u8 cheatsCBASeedBuffer[0x30]; +u32 cheatsCBASeed[4]; +u32 cheatsCBATemporaryValue = 0; +u16 cheatsCBATable[256]; +bool cheatsCBATableGenerated = false; + +u8 cheatsCBACurrentSeed[12] = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#define CHEAT_IS_HEX(a) (((a) >= 'A' && (a) <= 'F') || ((a) >= '0' && (a) <= '9')) + +#define CHEAT_PATCH_ROM_16BIT(a, v) \ + WRITE16LE(((u16 *)&rom[(a) & 0x1ffffff]), v); + +static bool isMultilineWithData(int i) +{ + // we consider it a multiline code if it has more than one line of data + // otherwise, it can still be considered a single code + if (i < cheatsNumber && i >= 0) + switch (cheatsList[i].size) + { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case CBA_IF_KEYS_PRESSED: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + return false; + // the codes below have two lines of data + case CBA_SLIDE_CODE: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case CBA_LT: + case CBA_GT: + case CBA_SUPER: + return true; + } + return false; +} + +static int getCodeLength(int num) +{ + if (num >= cheatsNumber || num < 0) + return 1; + + // this is for all the codes that are true multiline + switch (cheatsList[num].size) + { + case INT_8_BIT_WRITE: + case INT_16_BIT_WRITE: + case INT_32_BIT_WRITE: + case GSA_16_BIT_ROM_PATCH: + case GSA_8_BIT_GS_WRITE: + case GSA_16_BIT_GS_WRITE: + case GSA_32_BIT_GS_WRITE: + case CBA_AND: + case GSA_8_BIT_FILL: + case GSA_16_BIT_FILL: + case GSA_SLOWDOWN: + case CBA_ADD: + case CBA_OR: + return 1; + case CBA_IF_KEYS_PRESSED: + case CBA_IF_TRUE: + case CBA_IF_FALSE: + case CBA_SLIDE_CODE: + case GSA_8_BIT_GS_WRITE2: + case GSA_16_BIT_GS_WRITE2: + case GSA_32_BIT_GS_WRITE2: + case GSA_16_BIT_ROM_PATCH2: + case GSA_8_BIT_SLIDE: + case GSA_16_BIT_SLIDE: + case GSA_32_BIT_SLIDE: + case GSA_8_BIT_IF_TRUE: + case GSA_32_BIT_IF_TRUE: + case GSA_8_BIT_IF_FALSE: + case GSA_32_BIT_IF_FALSE: + case CBA_LT: + case CBA_GT: + return 2; + case GSA_8_BIT_IF_TRUE2: + case GSA_16_BIT_IF_TRUE2: + case GSA_32_BIT_IF_TRUE2: + case GSA_8_BIT_IF_FALSE2: + case GSA_16_BIT_IF_FALSE2: + case GSA_32_BIT_IF_FALSE2: + return 3; + case CBA_SUPER: + return (cheatsList[num].value+5)/6; + } + return 1; +} + +int cheatsCheckKeys(u32 keys, u32 extended) +{ + int ticks = 0; + for (int i = 0; i < cheatsNumber; i++) + { + if (!cheatsList[i].enabled) + { + // make sure we skip other lines in this code + i += getCodeLength(i)-1; + continue; + } + switch (cheatsList[i].size) + { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + break; + case GSA_16_BIT_ROM_PATCH: + if ((cheatsList[i].status & 1) == 0) + { + if (CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + cheatsList[i].oldValue = CPUReadHalfWord(cheatsList[i].address); + cheatsList[i].status |= 1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, cheatsList[i].value); + } + } + break; + case GSA_8_BIT_GS_WRITE: + if (extended & 4) + { + CPUWriteByte(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_16_BIT_GS_WRITE: + if (extended & 4) + { + CPUWriteHalfWord(cheatsList[i].address, cheatsList[i].value); + } + break; + case GSA_32_BIT_GS_WRITE: + if (extended & 4) + { + CPUWriteMemory(cheatsList[i].address, cheatsList[i].value); + } + break; + case CBA_IF_KEYS_PRESSED: + { + u16 value = cheatsList[i].value; + u32 addr = cheatsList[i].address; + if ((addr & 0x30) == 0x20) + { + if ((keys & value) != value) + { + i++; + } + } + else if ((addr & 0x30) == 0x10) + { + if ((keys & value) == value) + { + i++; + } + } + break; + } + case CBA_IF_TRUE: + if (CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case CBA_SLIDE_CODE: + { + u32 address = cheatsList[i].address; + u16 value = cheatsList[i].value; + i++; + if (i < cheatsNumber) + { + int count = (cheatsList[i].address & 0xFFFF); + u16 vinc = (cheatsList[i].address >> 16) & 0xFFFF; + int inc = cheatsList[i].value; + + for (int x = 0; x < count; x++) + { + CPUWriteHalfWord(address, value); + address += inc; + value += vinc; + } + } + break; + } + case CBA_IF_FALSE: + if (CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case CBA_AND: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) & + cheatsList[i].value); + break; + case GSA_8_BIT_GS_WRITE2: + i++; + if (i < cheatsNumber) + { + if (extended & 4) + { + CPUWriteByte(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_GS_WRITE2: + i++; + if (i < cheatsNumber) + { + if (extended & 4) + { + CPUWriteHalfWord(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_32_BIT_GS_WRITE2: + i++; + if (i < cheatsNumber) + { + if (extended & 4) + { + CPUWriteMemory(cheatsList[i-1].value, cheatsList[i].address); + } + } + break; + case GSA_16_BIT_ROM_PATCH2: + i++; + if (i < cheatsNumber) + { + if ((cheatsList[i-1].status & 1) == 0) + { + u32 addr = ((cheatsList[i-1].value & 0x00FFFFFF) << 1) + 0x8000000; + if (CPUReadHalfWord(addr) != (cheatsList[i].address & 0xFFFF)) + { + cheatsList[i-1].oldValue = CPUReadHalfWord(addr); + cheatsList[i-1].status |= 1; + CHEAT_PATCH_ROM_16BIT(addr, cheatsList[i].address & 0xFFFF); + } + } + } + break; + case GSA_8_BIT_SLIDE: + i++; + if (i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u8 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff); + while (count > 0) + { + CPUWriteByte(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_16_BIT_SLIDE: + i++; + if (i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u16 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*2; + while (count > 0) + { + CPUWriteHalfWord(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_32_BIT_SLIDE: + i++; + if (i < cheatsNumber) + { + u32 addr = cheatsList[i-1].value; + u32 value = cheatsList[i].address; + int vinc = (cheatsList[i].value >> 24) & 255; + int count = (cheatsList[i].value >> 16) & 255; + int ainc = (cheatsList[i].value & 0xffff)*4; + while (count > 0) + { + CPUWriteMemory(addr, value); + value += vinc; + addr += ainc; + count--; + } + } + break; + case GSA_8_BIT_IF_TRUE: + if (CPUReadByte(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case GSA_32_BIT_IF_TRUE: + if (CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) + { + i++; + } + break; + case GSA_8_BIT_IF_FALSE: + if (CPUReadByte(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case GSA_32_BIT_IF_FALSE: + if (CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) + { + i++; + } + break; + case GSA_8_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u8 v = cheatsList[i].value & 0xff; + u32 end = addr + (cheatsList[i].value >> 8); + do + { + CPUWriteByte(addr, v); + addr++; + } + while (addr <= end); + break; + } + case GSA_16_BIT_FILL: + { + u32 addr = cheatsList[i].address; + u16 v = cheatsList[i].value & 0xffff; + u32 end = addr + ((cheatsList[i].value >> 16) << 1); + do + { + CPUWriteHalfWord(addr, v); + addr += 2; + } + while (addr <= end); + break; + } + case GSA_8_BIT_IF_TRUE2: + if (CPUReadByte(cheatsList[i].address) != cheatsList[i].value) + { + i += 2; + } + break; + case GSA_16_BIT_IF_TRUE2: + if (CPUReadHalfWord(cheatsList[i].address) != cheatsList[i].value) + { + i += 2; + } + break; + case GSA_32_BIT_IF_TRUE2: + if (CPUReadMemory(cheatsList[i].address) != cheatsList[i].value) + { + i += 2; + } + break; + case GSA_8_BIT_IF_FALSE2: + if (CPUReadByte(cheatsList[i].address) == cheatsList[i].value) + { + i += 2; + } + break; + case GSA_16_BIT_IF_FALSE2: + if (CPUReadHalfWord(cheatsList[i].address) == cheatsList[i].value) + { + i += 2; + } + break; + case GSA_32_BIT_IF_FALSE2: + if (CPUReadMemory(cheatsList[i].address) == cheatsList[i].value) + { + i += 2; + } + break; + case GSA_SLOWDOWN: + // check if button was pressed and released, if so toggle our state + if ((cheatsList[i].status & 4) && !(extended & 4)) + cheatsList[i].status ^= 1; + if (extended & 4) + cheatsList[i].status |= 4; + else + cheatsList[i].status &= ~4; + + if (cheatsList[i].status & 1) + ticks += 2*256*((cheatsList[i].value >> 8) & 255); + break; + case CBA_ADD: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) + + (u16)cheatsList[i].value); + break; + case CBA_OR: + CPUWriteHalfWord(cheatsList[i].address, + CPUReadHalfWord(cheatsList[i].address) | + cheatsList[i].value); + break; + case CBA_LT: + if (CPUReadHalfWord(cheatsList[i].address) >= cheatsList[i].value) + i++; + break; + case CBA_GT: + if (CPUReadHalfWord(cheatsList[i].address) <= cheatsList[i].value) + i++; + break; + case CBA_SUPER: + { + int count = 2*cheatsList[i].value; + u32 address = cheatsList[i].address; + for (int x = 0; x < count; x++) + { + u8 b; + int res = x % 6; + if (res < 4) + b = (cheatsList[i].address >> (24-8*res)) & 0xFF; + else + b = (cheatsList[i].value >> (8 - 8*(res-4))) & 0x0FF; + CPUWriteByte(address, b); + address++; + if (x && !res) + i++; + } + if (count % 6) + i++; + break; + } + } + } + return ticks; +} + +void cheatsAdd(const char *codeStr, + const char *desc, + u32 address, + u32 value, + int code, + int size) +{ + if (cheatsNumber < 100) + { + int x = cheatsNumber; + cheatsList[x].code = code; + cheatsList[x].size = size; + cheatsList[x].address = address; + cheatsList[x].value = value; + strcpy(cheatsList[x].codestring, codeStr); + strcpy(cheatsList[x].desc, desc); + cheatsList[x].enabled = true; + cheatsList[x].status = 0; + + // we only store the old value for this simple codes. ROM patching + // is taken care when it actually patches the ROM + switch (cheatsList[x].size) + { + case INT_8_BIT_WRITE: + cheatsList[x].oldValue = CPUReadByte(address); + break; + case INT_16_BIT_WRITE: + cheatsList[x].oldValue = CPUReadHalfWord(address); + break; + case INT_32_BIT_WRITE: + cheatsList[x].oldValue = CPUReadMemory(address); + break; + } + cheatsNumber++; + } +} + +void cheatsDelete(int number, bool restore) +{ + if (number < cheatsNumber && number >= 0) + { + int x = number; + + if (restore) + { + switch (cheatsList[x].size) + { + case INT_8_BIT_WRITE: + CPUWriteByte(cheatsList[x].address, (u8)cheatsList[x].oldValue); + break; + case INT_16_BIT_WRITE: + CPUWriteHalfWord(cheatsList[x].address, (u16)cheatsList[x].oldValue); + break; + case INT_32_BIT_WRITE: + CPUWriteMemory(cheatsList[x].address, cheatsList[x].oldValue); + break; + case GSA_16_BIT_ROM_PATCH: + if (cheatsList[x].status & 1) + { + cheatsList[x].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[x].address, + cheatsList[x].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2: + if (cheatsList[x].status & 1) + { + cheatsList[x].status &= ~1; + CHEAT_PATCH_ROM_16BIT(((cheatsList[x].value & 0x00FFFFFF) << 1)+ + 0x8000000, + cheatsList[x].oldValue); + } + break; + } + } + if ((x+1) < cheatsNumber) + { + memcpy(&cheatsList[x], &cheatsList[x+1], sizeof(CheatsData)* + (cheatsNumber-x-1)); + } + cheatsNumber--; + } +} + +void cheatsDeleteAll(bool restore) +{ + for (int i = cheatsNumber-1; i >= 0; i--) + { + cheatsDelete(i, restore); + } +} + +void cheatsEnable(int i) +{ + if (i >= 0 && i < cheatsNumber) + { + cheatsList[i].enabled = true; + } +} + +void cheatsDisable(int i) +{ + if (i >= 0 && i < cheatsNumber) + { + switch (cheatsList[i].size) + { + case GSA_16_BIT_ROM_PATCH: + if (cheatsList[i].status & 1) + { + cheatsList[i].status &= ~1; + CHEAT_PATCH_ROM_16BIT(cheatsList[i].address, + cheatsList[i].oldValue); + } + break; + case GSA_16_BIT_ROM_PATCH2: + if (cheatsList[i].status & 1) + { + cheatsList[i].status &= ~1; + CHEAT_PATCH_ROM_16BIT(((cheatsList[i].value & 0x00FFFFFF) << 1)+ + 0x8000000, + cheatsList[i].oldValue); + } + break; + } + cheatsList[i].enabled = false; + } +} + +bool cheatsVerifyCheatCode(const char *code, const char *desc) +{ + int len = strlen(code); + if (len != 11 && len != 13 && len != 17) + { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code); + return false; + } + + if (code[8] != ':') + { + systemMessage(MSG_INVALID_CHEAT_CODE, N_("Invalid cheat code '%s'"), code); + return false; + } + + int i; + for (i = 0; i < 8; i++) + { + if (!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s'"), code); + return false; + } + } + for (i = 9; i < len; i++) + { + if (!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CHEAT_CODE, + N_("Invalid cheat code '%s'"), code); + return false; + } + } + + u32 address = 0; + u32 value = 0; + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + sscanf(buffer, "%x", &address); + + switch (address >> 24) + { + case 2: + case 3: + break; + default: + systemMessage(MSG_INVALID_CHEAT_CODE_ADDRESS, + N_("Invalid cheat code address: %08x"), + address); + return false; + } + + strncpy(buffer, &code[9], 8); + sscanf(buffer, "%x", &value); + int type = 0; + if (len == 13) + type = 1; + if (len == 17) + type = 2; + cheatsAdd(code, desc, address, value, type, type); + return true; +} + +void cheatsAddCheatCode(const char *code, const char *desc) +{ + cheatsVerifyCheatCode(code, desc); +} + +void cheatsDecryptGSACode(u32& address, u32& value, bool v3) +{ + u32 rollingseed = 0xC6EF3720; + u32 seeds_v1[] = { 0x09F4FBBD, 0x9681884A, 0x352027E9, 0xF3DEE5A7 }; + u32 seeds_v3[] = { 0x7AA9648F, 0x7FAE6994, 0xC0EFAAD5, 0x42712C57 }; + u32 *seeds = v3 ? seeds_v3 : seeds_v1; + + int bitsleft = 32; + while (bitsleft > 0) + { + value -= ((((address << 4) + seeds[2]) ^ (address + rollingseed)) ^ + ((address >> 5) + seeds[3])); + address -= ((((value << 4) + seeds[0]) ^ (value + rollingseed)) ^ + ((value >> 5) + seeds[1])); + rollingseed -= 0x9E3779B9; + bitsleft--; + } +} + +void cheatsAddGSACode(const char *code, const char *desc, bool v3) +{ + if (strlen(code) != 16) + { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + + int i; + for (i = 0; i < 16; i++) + { + if (!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_GSA_CODE, + N_("Invalid GSA code. Format is XXXXXXXXYYYYYYYY")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[8], 8); + buffer[8] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + cheatsDecryptGSACode(address, value, v3); + + if (value == 0x1DC0DE) + { + u32 gamecode = READ32LE(((u32 *)&rom[0xac])); + if (gamecode != address) + { + char buffer[5]; + *((u32 *)buffer) = address; + buffer[4] = 0; + char buffer2[5]; + *((u32 *)buffer2) = READ32LE(((u32 *)&rom[0xac])); + buffer2[4] = 0; + systemMessage(MSG_GBA_CODE_WARNING, + N_("Warning: cheats are for game %s. Current game is %s.\nCodes may not work correctly."), + buffer, buffer2); + } + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, v3 ? 257 : 256, + UNKNOWN_CODE); + return; + } + if (isMultilineWithData(cheatsNumber-1)) + { + cheatsAdd(code, desc, address, value, v3 ? 257 : 256, UNKNOWN_CODE); + return; + } + if (v3) + { + int type = (address >> 25) & 127; + u32 addr = (address & 0x00F00000) << 4 | (address & 0x0003FFFF); + switch (type) + { + case 0x00: + if (address == 0) + { + type = (value >> 25) & 127; + addr = (value & 0x00F00000) << 4 | (value & 0x0003FFFF); + switch (type) + { + case 0x04: + cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_SLOWDOWN); + break; + case 0x08: + cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_GS_WRITE2); + break; + case 0x09: + cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_GS_WRITE2); + break; + case 0x0a: + cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_GS_WRITE2); + break; + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + cheatsAdd(code, desc, 0, value & 0x00FFFFFF, 257, GSA_16_BIT_ROM_PATCH2); + break; + case 0x40: + cheatsAdd(code, desc, 0, addr, 257, GSA_8_BIT_SLIDE); + break; + case 0x41: + cheatsAdd(code, desc, 0, addr, 257, GSA_16_BIT_SLIDE); + break; + case 0x42: + cheatsAdd(code, desc, 0, addr, 257, GSA_32_BIT_SLIDE); + break; + default: + cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE); + break; + } + } + else + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_FILL); + break; + case 0x01: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_FILL); + break; + case 0x02: + cheatsAdd(code, desc, addr, value, 257, INT_32_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE); + break; + case 0x05: + cheatsAdd(code, desc, addr, value, 257, CBA_IF_TRUE); + break; + case 0x06: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE); + break; + case 0x08: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE); + break; + case 0x09: + cheatsAdd(code, desc, addr, value, 257, CBA_IF_FALSE); + break; + case 0x0a: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE); + break; + case 0x24: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_TRUE2); + break; + case 0x25: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_TRUE2); + break; + case 0x26: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_TRUE2); + break; + case 0x28: + cheatsAdd(code, desc, addr, value, 257, GSA_8_BIT_IF_FALSE2); + break; + case 0x29: + cheatsAdd(code, desc, addr, value, 257, GSA_16_BIT_IF_FALSE2); + break; + case 0x2a: + cheatsAdd(code, desc, addr, value, 257, GSA_32_BIT_IF_FALSE2); + break; + default: + cheatsAdd(code, desc, address, value, 257, UNKNOWN_CODE); + break; + } + } + else + { + int type = (address >> 28) & 15; + switch (type) + { + case 0: + case 1: + case 2: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, type); + break; + case 6: + address <<= 1; + type = (address >> 28) & 15; + if (type == 0x0c) + { + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, + GSA_16_BIT_ROM_PATCH); + break; + } + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + case 8: + switch ((address >> 20) & 15) + { + case 1: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_8_BIT_GS_WRITE); + break; + case 2: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_16_BIT_GS_WRITE); + break; + case 3: + cheatsAdd(code, desc, address & 0x0F0FFFFF, value, 256, + GSA_32_BIT_GS_WRITE); + case 15: + cheatsAdd(code, desc, 0, value & 0xFF00, 256, GSA_SLOWDOWN); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + } + break; + case 0x0d: + if (address != 0xDEADFACE) + { + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 256, + CBA_IF_TRUE); + } + else + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + default: + // unsupported code + cheatsAdd(code, desc, address, value, 256, + UNKNOWN_CODE); + break; + } + } +} + +bool cheatsImportGSACodeFile(const char *name, int game, bool v3) +{ + FILE *f = fopen(name, "rb"); + if (!f) + return false; + + int games = 0; + int len = 0; + fseek(f, 0x1e, SEEK_CUR); + fread(&games, 1, 4, f); + bool found = false; + int g = 0; + while (games > 0) + { + if (g == game) + { + found = true; + break; + } + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + int codes = 0; + fread(&codes, 1, 4, f); + while (codes > 0) + { + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fseek(f, 8, SEEK_CUR); + fread(&len, 1, 4, f); + fseek(f, len*12, SEEK_CUR); + codes--; + } + games--; + g++; + } + if (found) + { + char desc[256]; + char code[17]; + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + int codes = 0; + fread(&codes, 1, 4, f); + while (codes > 0) + { + fread(&len, 1, 4, f); + fread(desc, 1, len, f); + desc[len] = 0; + desc[31] = 0; + fread(&len, 1, 4, f); + fseek(f, len, SEEK_CUR); + fseek(f, 4, SEEK_CUR); + fread(&len, 1, 4, f); + while (len) + { + fseek(f, 4, SEEK_CUR); + fread(code, 1, 8, f); + fseek(f, 4, SEEK_CUR); + fread(&code[8], 1, 8, f); + code[16] = 0; + cheatsAddGSACode(code, desc, v3); + len -= 2; + } + codes--; + } + } + fclose(f); + return false; +} + +void cheatsCBAReverseArray(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void chatsCBAScramble(u8 *array, int count, u8 b) +{ + u8 *x = array + (count >> 3); + u8 *y = array + (b >> 3); + u32 z = *x & (1 << (count & 7)); + u32 x0 = (*x & (~(1 << (count & 7)))); + if (z != 0) + z = 1; + if ((*y & (1 << (b & 7))) != 0) + x0 |= (1 << (count & 7)); + *x = x0; + u32 temp = *y & (~(1 << (b & 7))); + if (z != 0) + temp |= (1 << (b & 7)); + *y = temp; +} + +u32 cheatsCBAGetValue(u8 *array) +{ + return array[0] | array[1]<<8 | array[2] << 16 | array[3]<<24; +} + +u16 cheatsCBAGetData(u8 *array) +{ + return array[4] | array[5]<<8; +} + +void cheatsCBAArrayToValue(u8 *array, u8 *dest) +{ + dest[0] = array[3]; + dest[1] = array[2]; + dest[2] = array[1]; + dest[3] = array[0]; + dest[4] = array[5]; + dest[5] = array[4]; +} + +void cheatsCBAParseSeedCode(u32 address, u32 value, u32 *array) +{ + array[0] = 1; + array[1] = value & 0xFF; + array[2] = (address >> 0x10) & 0xFF; + array[3] = (value >> 8) & 0xFF; + array[4] = (address >> 0x18) & 0x0F; + array[5] = address & 0xFFFF; + array[6] = address; + array[7] = value; +} + +u32 cheatsCBAEncWorker() +{ + u32 x = (cheatsCBATemporaryValue * 0x41c64e6d) + 0x3039; + u32 y = (x * 0x41c64e6d) + 0x3039; + u32 z = x >> 0x10; + x = ((y >> 0x10) & 0x7fff) << 0x0f; + z = (z << 0x1e) | x; + x = (y * 0x41c64e6d) + 0x3039; + cheatsCBATemporaryValue = x; + return z | ((x >> 0x10) & 0x7fff); +} + +#define ROR(v, s) \ + (((v) >> (s)) | (((v) & ((1 << (s))-1)) << (32 - (s)))) + +u32 cheatsCBACalcIndex(u32 x, u32 y) +{ + if (y != 0) + { + if (y == 1) + x = 0; + else if (x == y) + x = 0; + if (y < 1) + return x; + else if (x < y) + return x; + u32 x0 = 1; + + while (y < 0x10000000) + { + if (y < x) + { + y = y << 4; + x0 = x0 << 4; + } + else + break; + } + + while (y < 0x80000000) + { + if (y < x) + { + y = y << 1; + x0 = x0 << 1; + } + else + break; + } + +loop: + u32 z = 0; + if (x >= y) + x -= y; + if (x >= (y >> 1)) + { + x -= (y >> 1); + z |= ROR(x0, 1); + } + if (x >= (y >> 2)) + { + x -= (y >> 2); + z |= ROR(x0, 2); + } + if (x >= (y >> 3)) + { + x -= (y >> 3); + z |= ROR(x0, 3); + } + + u32 temp = x0; + + if (x != 0) + { + x0 = x0 >> 4; + if (x0 != 0) + { + y = y >> 4; + goto loop; + } + } + + z = z & 0xe0000000; + + if (z != 0) + { + if ((temp & 7) == 0) + return x; + } + else + return x; + + if ((z & ROR(temp, 3)) != 0) + x += y >> 3; + if ((z & ROR(temp, 2)) != 0) + x += y >> 2; + if ((z & ROR(temp, 1)) != 0) + x += y >> 1; + return x; + } + else + {} + // should not happen in the current code + return 0; +} + +void cheatsCBAUpdateSeedBuffer(u32 a, u8 *buffer, int count) +{ + int i; + for (i = 0; i < count; i++) + buffer[i] = i; + for (i = 0; (u32)i < a; i++) + { + u32 a = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 b = cheatsCBACalcIndex(cheatsCBAEncWorker(), count); + u32 t = buffer[a]; + buffer[a] = buffer[b]; + buffer[b] = t; + } +} + +void cheatsCBAChangeEncryption(u32 *seed) +{ + int i; + + cheatsCBATemporaryValue = (seed[1] ^ 0x1111); + cheatsCBAUpdateSeedBuffer(0x50, cheatsCBASeedBuffer, 0x30); + cheatsCBATemporaryValue = 0x4efad1c3; + + for (i = 0; (u32)i < seed[4]; i++) + { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + cheatsCBASeed[2] = cheatsCBAEncWorker(); + cheatsCBASeed[3] = cheatsCBAEncWorker(); + + cheatsCBATemporaryValue = seed[3] ^ 0xf254; + + for (i = 0; (u32)i < seed[3]; i++) + { + cheatsCBATemporaryValue = cheatsCBAEncWorker(); + } + + cheatsCBASeed[0] = cheatsCBAEncWorker(); + cheatsCBASeed[1] = cheatsCBAEncWorker(); + + *((u32 *)&cheatsCBACurrentSeed[0]) = seed[6]; + *((u32 *)&cheatsCBACurrentSeed[4]) = seed[7]; + *((u32 *)&cheatsCBACurrentSeed[8]) = 0; +} + +u16 cheatsCBAGenValue(u32 x, u32 y, u32 z) +{ + y <<= 0x10; + z <<= 0x10; + x <<= 0x18; + u32 x0 = (int)y >> 0x10; + z = (int)z >> 0x10; + x = (int)x >> 0x10; + for (int i = 0; i < 8; i++) + { + u32 temp = z ^ x; + if ((int)temp >= 0) + { + temp = z << 0x11; + } + else + { + temp = z << 0x01; + temp ^= x0; + temp = temp << 0x10; + } + z = (int)temp >> 0x10; + temp = x << 0x11; + x = (int)temp >> 0x10; + } + return z & 0xffff; +} + +void cheatsCBAGenTable() +{ + for (int i = 0; i < 0x100; i++) + { + cheatsCBATable[i] = cheatsCBAGenValue(i, 0x1021, 0); + } + cheatsCBATableGenerated = true; +} + +u16 cheatsCBACalcCRC(u8 *rom, int count) +{ + u32 crc = 0xffffffff; + + if (count & 3) + { + // 0x08000EAE + } + else + { + count = (count >> 2) - 1; + if (count != -1) + { + while (count != -1) + { + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + crc = (((crc << 0x08) ^ cheatsCBATable[(((u32)crc << 0x10) >> 0x18) + ^ *rom++]) << 0x10) >> 0x10; + count--; + } + } + } + return crc & 0xffff; +} + +void cheatsCBADecrypt(u8 *decrypt) +{ + u8 buffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + u8 *array = &buffer[1]; + + cheatsCBAReverseArray(decrypt, array); + + for (int count = 0x2f; count >= 0; count--) + { + chatsCBAScramble(array, count, cheatsCBASeedBuffer[count]); + } + cheatsCBAArrayToValue(array, decrypt); + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) ^ + cheatsCBASeed[0]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) ^ + cheatsCBASeed[1]) & 0xffff; + + cheatsCBAReverseArray(decrypt, array); + + u32 cs = cheatsCBAGetValue(cheatsCBACurrentSeed); + for (int i = 0; i <= 4; i++) + { + array[i] = ((cs >> 8) ^ array[i+1]) ^ array[i] ; + } + + array[5] = (cs >> 8) ^ array[5]; + + for (int j = 5; j >= 0; j--) + { + array[j] = (cs ^ array[j-1]) ^ array[j]; + } + + cheatsCBAArrayToValue(array, decrypt); + + *((u32 *)decrypt) = cheatsCBAGetValue(decrypt) + ^ cheatsCBASeed[2]; + *((u16 *)(decrypt+4)) = (cheatsCBAGetData(decrypt) + ^ cheatsCBASeed[3]) & 0xffff; +} + +int cheatsCBAGetCount() +{ + int count = 0; + for (int i = 0; i < cheatsNumber; i++) + { + if (cheatsList[i].code == 512) + count++; + } + return count; +} + +bool cheatsCBAShouldDecrypt() +{ + for (int i = 0; i < cheatsNumber; i++) + { + if (cheatsList[i].code == 512) + { + return (cheatsList[i].codestring[0] == '9'); + } + } + return false; +} + +void cheatsAddCBACode(const char *code, const char *desc) +{ + if (strlen(code) != 13) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + int i; + for (i = 0; i < 8; i++) + { + if (!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + if (code[8] != ' ') + { + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + + for (i = 9; i < 13; i++) + { + if (!CHEAT_IS_HEX(code[i])) + { + // wrong cheat + systemMessage(MSG_INVALID_CBA_CODE, + N_("Invalid CBA code. Format is XXXXXXXX YYYY.")); + return; + } + } + + char buffer[10]; + strncpy(buffer, code, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + strncpy(buffer, &code[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u8 array[8] = { + address &255, + (address >> 8) & 255, + (address >> 16) & 255, + (address >> 24) & 255, + (value & 255), + (value >> 8) & 255, + 0, + 0 + }; + + if (cheatsCBAGetCount() == 0 && + (address >> 28) == 9) + { + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, UNKNOWN_CODE); + } + else + { + if (cheatsCBAShouldDecrypt()) + cheatsCBADecrypt(array); + + address = READ32LE(((u32 *)array)); + value = READ16LE(((u16 *)&array[4])); + + int type = (address >> 28) & 15; + + if (isMultilineWithData(cheatsNumber-1)) + { + cheatsAdd(code, desc, address, value, 512, UNKNOWN_CODE); + return; + } + + switch (type) + { + case 0x00: + { + if (!cheatsCBATableGenerated) + cheatsCBAGenTable(); + u32 crc = cheatsCBACalcCRC(rom, 0x10000); + if (crc != address) + { + systemMessage(MSG_CBA_CODE_WARNING, + N_("Warning: Codes seem to be for a different game.\nCodes may not work correctly.")); + } + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + UNKNOWN_CODE); + break; + } + case 0x02: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_OR); + break; + case 0x03: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + INT_8_BIT_WRITE); + break; + case 0x04: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_SLIDE_CODE); + break; + case 0x05: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_SUPER); + break; + case 0x06: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_AND); + break; + case 0x07: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_TRUE); + break; + case 0x08: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + INT_16_BIT_WRITE); + break; + case 0x0a: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_FALSE); + break; + case 0x0b: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_LT); + break; + case 0x0c: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_GT); + break; + case 0x0d: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_IF_KEYS_PRESSED); + break; + case 0x0e: + cheatsAdd(code, desc, address & 0x0FFFFFFF, value, 512, + CBA_ADD); + break; + default: + // unsupported code + cheatsAdd(code, desc, address & 0xFFFFFFFF, value, 512, + UNKNOWN_CODE); + break; + } + } +} + +void cheatsSaveGame(gzFile file) +{ + utilWriteInt(file, cheatsNumber); + + utilGzWrite(file, cheatsList, sizeof(cheatsList)); +} + +void cheatsReadGame(gzFile file) +{ + cheatsNumber = 0; + + cheatsNumber = utilReadInt(file); + + utilGzRead(file, cheatsList, sizeof(cheatsList)); + + bool firstCodeBreaker = true; + + for (int i = 0; i < cheatsNumber; i++) + { + cheatsList[i].status = 0; + if (!cheatsList[i].codestring[0]) + { + switch (cheatsList[i].size) + { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if (cheatsList[i].enabled) + { + cheatsEnable(i); + } + + if (cheatsList[i].code == 512 && firstCodeBreaker) + { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if ((address >> 28) == 9) + { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } +} + +void cheatsSaveCheatList(const char *file) +{ + if (cheatsNumber == 0) + return; + FILE *f = fopen(file, "wb"); + if (f == NULL) + return; + int version = 1; + fwrite(&version, 1, sizeof(version), f); + int type = 0; + fwrite(&type, 1, sizeof(type), f); + fwrite(&cheatsNumber, 1, sizeof(cheatsNumber), f); + fwrite(cheatsList, 1, sizeof(cheatsList), f); + fclose(f); +} + +bool cheatsLoadCheatList(const char *file) +{ + cheatsNumber = 0; + + int count = 0; + + FILE *f = fopen(file, "rb"); + + if (f == NULL) + return false; + + int version = 0; + + if (fread(&version, 1, sizeof(version), f) != sizeof(version)) + { + fclose(f); + return false; + } + + if (version != 1) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_VERSION, + N_("Unsupported cheat list version %d"), version); + fclose(f); + return false; + } + + int type = 0; + if (fread(&type, 1, sizeof(type), f) != sizeof(type)) + { + fclose(f); + return false; + } + + if (type != 0) + { + systemMessage(MSG_UNSUPPORTED_CHEAT_LIST_TYPE, + N_("Unsupported cheat list type %d"), type); + fclose(f); + return false; + } + + if (fread(&count, 1, sizeof(count), f) != sizeof(count)) + { + fclose(f); + return false; + } + + if (fread(cheatsList, 1, sizeof(cheatsList), f) != sizeof(cheatsList)) + { + fclose(f); + return false; + } + + bool firstCodeBreaker = true; + + for (int i = 0; i < count; i++) + { + cheatsList[i].status = 0; // remove old status as it is not used + if (!cheatsList[i].codestring[0]) + { + switch (cheatsList[i].size) + { + case 0: + sprintf(cheatsList[i].codestring, "%08x:%02x", cheatsList[i].address, + cheatsList[i].value); + break; + case 1: + sprintf(cheatsList[i].codestring, "%08x:%04x", cheatsList[i].address, + cheatsList[i].value); + break; + case 2: + sprintf(cheatsList[i].codestring, "%08x:%08x", cheatsList[i].address, + cheatsList[i].value); + break; + } + } + + if (cheatsList[i].code == 512 && firstCodeBreaker) + { + firstCodeBreaker = false; + char buffer[10]; + strncpy(buffer, cheatsList[i].codestring, 8); + buffer[8] = 0; + u32 address; + sscanf(buffer, "%x", &address); + if ((address >> 28) == 9) + { + strncpy(buffer, &cheatsList[i].codestring[9], 4); + buffer[4] = 0; + u32 value; + sscanf(buffer, "%x", &value); + + u32 seed[8]; + cheatsCBAParseSeedCode(address, value, seed); + cheatsCBAChangeEncryption(seed); + } + } + } + cheatsNumber = count; + fclose(f); + return true; +} + +extern int *extCpuLoopTicks; +extern int *extClockTicks; +extern int *extTicks; +extern int cpuSavedTicks; + +extern void debuggerBreakOnWrite(u32 *, u32, u32, int); + +#define CPU_BREAK_LOOP2 \ + cpuSavedTicks = cpuSavedTicks - *extCpuLoopTicks; \ + *extCpuLoopTicks = *extClockTicks; \ + *extTicks = *extClockTicks; + +void cheatsWriteMemory(u32 *address, u32 value, u32 mask) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if (cheatsNumber == 0) + { + debuggerBreakOnWrite(address, *address, value, 2); + CPU_BREAK_LOOP2; + *address = value; + return; + } +#endif +#endif +} + +void cheatsWriteHalfWord(u16 *address, u16 value, u16 mask) +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if (cheatsNumber == 0) + { + debuggerBreakOnWrite((u32 *)address, *address, value, 1); + CPU_BREAK_LOOP2; + *address = value; + return; + } +#endif +#endif +} + +#if defined BKPT_SUPPORT && defined SDL +void cheatsWriteByte(u8 *address, u8 value) +#else +void cheatsWriteByte(u8 *, u8) +#endif +{ +#ifdef BKPT_SUPPORT +#ifdef SDL + if (cheatsNumber == 0) + { + debuggerBreakOnWrite((u32 *)address, *address, value, 0); + CPU_BREAK_LOOP2; + *address = value; + return; + } +#endif +#endif +} + +#undef CPU_BREAK_LOOP2 diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBACheats.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBACheats.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,44 @@ +#ifndef VBA_GBA_CHEATS_H +#define VBA_GBA_CHEATS_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +struct CheatsData +{ + int code; + int size; + int status; + bool enabled; + u32 address; + u32 value; + u32 oldValue; + char codestring[20]; + char desc[32]; +}; + +extern void cheatsAdd(const char *, const char *, u32, u32, int, int); +extern void cheatsAddCheatCode(const char *code, const char *desc); +extern void cheatsAddGSACode(const char *code, const char *desc, bool v3); +extern void cheatsAddCBACode(const char *code, const char *desc); +extern bool cheatsImportGSACodeFile(const char *name, int game, bool v3); +extern void cheatsDelete(int number, bool restore); +extern void cheatsDeleteAll(bool restore); +extern void cheatsEnable(int number); +extern void cheatsDisable(int number); +extern void cheatsSaveGame(gzFile file); +extern void cheatsReadGame(gzFile file); +extern void cheatsSaveCheatList(const char *file); +extern bool cheatsLoadCheatList(const char *file); +extern void cheatsWriteMemory(u32 *, u32, u32); +extern void cheatsWriteHalfWord(u16 *, u16, u16); +extern void cheatsWriteByte(u8 *, u8); +extern int cheatsCheckKeys(u32, u32); +extern int cheatsNumber; +extern CheatsData cheatsList[100]; + +#endif // GBA_CHEATS_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBAGfx.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBAGfx.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,35 @@ +#include "../Port.h" +#include "GBAGfx.h" + +int coeff[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 +}; + +// some of the rendering code in gfx.h (such as mode 0 line 1298) +// renders outside the given buffer (past 239) which corrupts other memory, +// so rather than find all places in that code that need to be fixed, +// just give it enough extra scratch space to use + +u32 line0[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 line1[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 line2[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 line3[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 lineOBJ[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 lineOBJWin[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +u32 lineMix[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +bool gfxInWin0[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +bool gfxInWin1[240+LINE_BUFFER_OVERFLOW_LEEWAY]; + +int gfxBG2Changed = 0; +int gfxBG3Changed = 0; + +int gfxBG2X = 0; +int gfxBG2Y = 0; +int gfxBG2LastX = 0; +int gfxBG2LastY = 0; +int gfxBG3X = 0; +int gfxBG3Y = 0; +int gfxBG3LastX = 0; +int gfxBG3LastY = 0; +int gfxLastVCOUNT = 0; diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBAGfx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBAGfx.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1791 @@ +#ifndef VBA_GBA_GFX_H +#define VBA_GBA_GFX_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../Port.h" +#include "GBA.h" +#include "GBAGlobals.h" + +//#define SPRITE_DEBUG + +void gfxDrawTextScreen(u16, u16, u16, u32 *); +void gfxDrawRotScreen(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32 *); +void gfxDrawRotScreen16Bit(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32 *); +void gfxDrawRotScreen256(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32 *); +void gfxDrawRotScreen16Bit160(u16, + u16, u16, + u16, u16, + u16, u16, + u16, u16, + int&, int&, + int, + u32 *); +void gfxDrawSprites(u32 *); +void gfxIncreaseBrightness(u32 *line, int coeff); +void gfxDecreaseBrightness(u32 *line, int coeff); +void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb); + +void mode0RenderLine(); +void mode0RenderLineNoWindow(); +void mode0RenderLineAll(); + +void mode1RenderLine(); +void mode1RenderLineNoWindow(); +void mode1RenderLineAll(); + +void mode2RenderLine(); +void mode2RenderLineNoWindow(); +void mode2RenderLineAll(); + +void mode3RenderLine(); +void mode3RenderLineNoWindow(); +void mode3RenderLineAll(); + +void mode4RenderLine(); +void mode4RenderLineNoWindow(); +void mode4RenderLineAll(); + +void mode5RenderLine(); +void mode5RenderLineNoWindow(); +void mode5RenderLineAll(); + +extern int coeff[32]; + +#define LINE_BUFFER_OVERFLOW_LEEWAY (512-240) + +extern u32 line0[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 line1[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 line2[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 line3[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 lineOBJ[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 lineOBJWin[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern u32 lineMix[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern bool gfxInWin0[240+LINE_BUFFER_OVERFLOW_LEEWAY]; +extern bool gfxInWin1[240+LINE_BUFFER_OVERFLOW_LEEWAY]; + +extern int gfxBG2Changed; +extern int gfxBG3Changed; + +extern int gfxBG2X; +extern int gfxBG2Y; +extern int gfxBG2LastX; +extern int gfxBG2LastY; +extern int gfxBG3X; +extern int gfxBG3Y; +extern int gfxBG3LastX; +extern int gfxBG3LastY; +extern int gfxLastVCOUNT; + +inline void gfxClearArray(u32 *array) +{ + for (int i = 0; i < 240; i++) + { + *array++ = 0x80000000; + } +} + +inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 * charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch ((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + VCOUNT) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if (mosaicOn) + { + if ((VCOUNT % mosaicY) != 0) + { + mosaicY = (VCOUNT / mosaicY) * mosaicY; + yyy = (vofs + mosaicY) & maskY; + } + } + + if (yyy > 255 && sizeY > 256) + { + yyy &= 255; + screenBase += 0x400; + if (sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if ((control) & 0x80) + { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for (int x = 0; x < 240; x++) + { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if (data & 0x0400) + tileX = 7 - tileX; + if (data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio) : 0x80000000; + + if (data & 0x0400) + { + if (tileX == 0) + screenSource++; + } + else if (tileX == 7) + screenSource++; + xxx++; + if (xxx == 256) + { + if (sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else + { + screenSource = screenBase + yshift; + xxx = 0; + } + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + else + { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for (int x = 0; x < 240; x++) + { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if (data & 0x0400) + tileX = 7 - tileX; + if (data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if (tileX & 1) + { + color = (color >> 4); + } + else + { + color &= 0x0F; + } + + int pal = (READ16LE(screenSource)>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio) : 0x80000000; + + if (data & 0x0400) + { + if (tileX == 0) + screenSource++; + } + else if (tileX == 7) + screenSource++; + xxx++; + if (xxx == 256) + { + if (sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else + { + screenSource = screenBase + yshift; + xxx = 0; + } + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if (mosaicOn) + { + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 * charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u8 * screenBase = (u8 *)&vram[((control >> 8) & 0x1f) * 0x800]; + int prio = ((control & 3) << 25) + 0x1000000; + + int sizeX = 128; + int sizeY = 128; + switch ((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = sizeY = 256; + break; + case 2: + sizeX = sizeY = 512; + break; + case 3: + sizeX = sizeY = 1024; + break; + } + + int dx = pa & 0x7FFF; + if (pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if (pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if (pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if (pd & 0x8000) + dmy |= 0xFFFF8000; + + if (VCOUNT == 0) + changed = 3; + + if (changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if (changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if (control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + if (control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if (xxx < 0) + xxx += sizeX; + if (yyy < 0) + yyy += sizeY; + } + + if (control & 0x80) + { + for (int x = 0; x < 240; x++) + { + if (xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + int tile = screenBase[(xxx>>3) + (yyy>>3)*(sizeX>>3)]; + + int tileX = (xxx & 7); + int tileY = yyy & 7; + + u8 color = charBase[(tile<<6) + (tileY<<3) + tileX]; + + line[x] = color ? (READ16LE(&palette[color])|prio) : 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + + if (control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if (xxx < 0) + xxx += sizeX; + if (yyy < 0) + yyy += sizeY; + } + } + } + else + { + for (int x = 0; x < 240; x++) + { + if (xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + int tile = screenBase[(xxx>>3) + (yyy>>3)*(sizeX>>3)]; + + int tileX = (xxx & 7); + int tileY = yyy & 7; + + u8 color = charBase[(tile<<6) + (tileY<<3) + tileX]; + + line[x] = color ? (READ16LE(&palette[color])|prio) : 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + + if (control & 0x2000) + { + xxx %= sizeX; + yyy %= sizeY; + if (xxx < 0) + xxx += sizeX; + if (yyy < 0) + yyy += sizeY; + } + } + } + + if (control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen16Bit(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if (pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if (pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if (pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if (pd & 0x8000) + dmy |= 0xFFFF8000; + + if (VCOUNT == 0) + changed = 3; + + if (changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + currentX |= 0xF8000000; + } + else + currentX += dmx; + + if (changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if (control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for (int x = 0; x < 240; x++) + { + if (xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if (control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen256(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int ¤tX, int& currentY, + int changed, + u32 *line) +{ + u16 *palette = (u16 *)paletteRAM; + u8 * screenBase = (DISPCNT & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 240; + int sizeY = 160; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if (pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if (pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if (pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if (pd & 0x8000) + dmy |= 0xFFFF8000; + + if (VCOUNT == 0) + changed = 3; + + if (changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if (changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if (control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT / mosaicY) * mosaicY; + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for (int x = 0; x < 240; x++) + { + if (xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + u8 color = screenBase[yyy * 240 + xxx]; + + line[x] = color ? (READ16LE(&palette[color])|prio) : 0x80000000; + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if (control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawRotScreen16Bit160(u16 control, + u16 x_l, u16 x_h, + u16 y_l, u16 y_h, + u16 pa, u16 pb, + u16 pc, u16 pd, + int& currentX, int& currentY, + int changed, + u32 *line) +{ + u16 *screenBase = (DISPCNT & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((control & 3) << 25) + 0x1000000; + int sizeX = 160; + int sizeY = 128; + + int startX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + startX |= 0xF8000000; + int startY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + startY |= 0xF8000000; + + int dx = pa & 0x7FFF; + if (pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if (pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if (pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFFF; + if (pd & 0x8000) + dmy |= 0xFFFF8000; + + if (VCOUNT == 0) + changed = 3; + + if (changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if (x_h & 0x0800) + currentX |= 0xF8000000; + } + else + { + currentX += dmx; + } + + if (changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if (y_h & 0x0800) + currentY |= 0xF8000000; + } + else + { + currentY += dmy; + } + + int realX = currentX; + int realY = currentY; + + if (control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (VCOUNT / mosaicY) * mosaicY; + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + for (int x = 0; x < 240; x++) + { + if (xxx < 0 || + yyy < 0 || + xxx >= sizeX || + yyy >= sizeY) + { + line[x] = 0x80000000; + } + else + { + line[x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + } + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if (control & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +inline void gfxDrawSprites(u32 *lineOBJ) +{ + int m = 0; + gfxClearArray(lineOBJ); + if (layerEnable & 0x1000) + { + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for (int x = 0; x < 128; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + // ignore OBJ-WIN + if ((a0 & 0x0c00) == 0x0800) + continue; + + int sizeY = 8; + int sizeX = 8; + + switch (((a0 >>12) & 0x0c)|(a1>>14)) + { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + continue; + } + +#ifdef SPRITE_DEBUG + int maskX = sizeX-1; + int maskY = sizeY-1; +#endif + + int sy = (a0 & 255); + + if (sy > 160) + sy -= 256; + + if (a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if (a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + + int t = VCOUNT - sy; + if ((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + if ((sx < 240) || (((sx + fieldX) & 511) < 240)) + { + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if (dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if (dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if (dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if (dmy & 0x8000) + dmy |= 0xFFFF8000; + + if (a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if (a0 & 0x2000) + { + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if (DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for (int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + if (xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) + ; + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7FFF)]; + if ((color == 0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if ((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) + { + m++; + if (m == mosaicX) + m = 0; + } +#ifdef SPRITE_DEBUG + if (t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + sx = (sx+1)&511;; + realX += dx; + realY += dy; + } + } + else + { + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for (int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + if (xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY || + sx >= 240) + ; + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if (xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color == 0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if ((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette+color]) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if ((a0 & 0x1000) && m) + { + m++; + if (m == mosaicX) + m = 0; + } + +#ifdef SPRITE_DEBUG + if (t == 0 || t == maskY || x == 0 || x == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1)&511;; + realX += dx; + realY += dy; + } + } + } + } + } + else + { + int t = VCOUNT - sy; + if ((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + if (((sx < 240) || (((sx+sizeX)&511) < 240)) && !(a0 & 0x0200)) + { + if (a0 & 0x2000) + { + if (a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + { + inc = sizeX >> 2; + } + else + { + c &= 0x3FE; + } + int xxx = 0; + if (a1 & 0x1000) + xxx = sizeX-1; + + if (a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if (a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for (int xx = 0; xx < sizeX; xx++) + { + if (sx < 240) + { + u8 color = vram[address]; + if ((color == 0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if ((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[color]) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + + if (a0 & 0x1000) + { + m++; + if (m == mosaicX) + m = 0; + } + +#ifdef SPRITE_DEBUG + if (t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + } + + sx = (sx+1) & 511; + if (a1 & 0x1000) + { + xxx--; + address--; + if (xxx == -1) + { + address -= 56; + xxx = 7; + } + if (address < 0x10000) + address += 0x8000; + } + else + { + xxx++; + address++; + if (xxx == 8) + { + address += 56; + xxx = 0; + } + if (address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if (a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + { + inc = sizeX >> 3; + } + int xxx = 0; + if (a1 & 0x1000) + xxx = sizeX - 1; + + if (a0 & 0x1000) + { + t -= (t % mosaicY); + } + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if (a1 & 0x1000) + { + xxx = 7; + for (int xx = sizeX - 1; xx >= 0; xx--) + { + if (sx < 240) + { + u8 color = vram[address]; + if (xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if ((color == 0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if ((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if (a0 & 0x1000) + { + m++; + if (m == mosaicX) + m = 0; + } +#ifdef SPRITE_DEBUG + if (t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx--; + if (!(xx & 1)) + address--; + if (xxx == -1) + { + xxx = 7; + address -= 28; + } + if (address < 0x10000) + address += 0x8000; + } + } + else + { + for (int xx = 0; xx < sizeX; xx++) + { + if (sx < 240) + { + u8 color = vram[address]; + if (xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if ((color == 0) && (((prio >> 25)&3) < + ((lineOBJ[sx]>>25)&3))) + { + lineOBJ[sx] = (lineOBJ[sx] & 0xF9FFFFFF) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + else if ((color) && (prio < (lineOBJ[sx]&0xFF000000))) + { + lineOBJ[sx] = READ16LE(&spritePalette[palette + color]) | prio; + if ((a0 & 0x1000) && m) + lineOBJ[sx] = (lineOBJ[sx-1] & 0xF9FFFFFF) | prio; + } + } + if (a0 & 0x1000) + { + m++; + if (m == mosaicX) + m = 0; + } +#ifdef SPRITE_DEBUG + if (t == 0 || t == maskY || xx == 0 || xx == maskX) + lineOBJ[sx] = 0x001F; +#endif + sx = (sx+1) & 511; + xxx++; + if (xx & 1) + address++; + if (xxx == 8) + { + address += 28; + xxx = 0; + } + if (address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +inline void gfxDrawOBJWin(u32 *lineOBJWin) +{ + gfxClearArray(lineOBJWin); + if (layerEnable & 0x8000) + { + u16 *sprites = (u16 *)oam; + // u16 *spritePalette = &((u16 *)paletteRAM)[256]; + for (int x = 0; x < 128; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + // ignore non OBJ-WIN + if ((a0 & 0x0c00) != 0x0800) + continue; + + int sizeY = 8; + int sizeX = 8; + + switch (((a0 >>12) & 0x0c)|(a1>>14)) + { + case 0: + break; + case 1: + sizeX = sizeY = 16; + break; + case 2: + sizeX = sizeY = 32; + break; + case 3: + sizeX = sizeY = 64; + break; + case 4: + sizeX = 16; + break; + case 5: + sizeX = 32; + break; + case 6: + sizeX = 32; + sizeY = 16; + break; + case 7: + sizeX = 64; + sizeY = 32; + break; + case 8: + sizeY = 16; + break; + case 9: + sizeY = 32; + break; + case 10: + sizeX = 16; + sizeY = 32; + break; + case 11: + sizeX = 32; + sizeY = 64; + break; + default: + continue; + } + + int sy = (a0 & 255); + + if (sy > 160) + sy -= 256; + + if (a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if (a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + + int t = VCOUNT - sy; + if ((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + if ((sx < 240) || (((sx + fieldX) & 511) < 240)) + { + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if (dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if (dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if (dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if (dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + if (a0 & 0x2000) + { + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + int inc = 32; + if (DISPCNT & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for (int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + if (xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY) + {} + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + if (color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1)&511;; + realX += dx; + realY += dy; + } + } + else + { + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + inc = sizeX >> 3; + // int palette = (a2 >> 8) & 0xF0; + for (int x = 0; x < fieldX; x++) + { + int xxx = realX >> 8; + int yyy = realY >> 8; + + // if(x == 0 || x == (sizeX-1) || + // t == 0 || t == (sizeY-1)) { + // lineOBJ[sx] = 0x001F | prio; + // } else { + if (xxx < 0 || xxx >= sizeX || + yyy < 0 || yyy >= sizeY) + {} + else + { + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if (xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if (color) + { + lineOBJWin[sx] = 1; + } + } + // } + sx = (sx+1)&511;; + realX += dx; + realY += dy; + } + } + } + } + } + else + { + int t = VCOUNT - sy; + if ((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + if (((sx < 240) || (((sx+sizeX)&511) < 240)) && !(a0 & 0x0200)) + { + if (a0 & 0x2000) + { + if (a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + { + inc = sizeX >> 2; + } + else + { + c &= 0x3FE; + } + int xxx = 0; + if (a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if (a1 & 0x1000) + xxx = 7; + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + for (int xx = 0; xx < sizeX; xx++) + { + if (sx < 240) + { + u8 color = vram[address]; + if (color) + { + lineOBJWin[sx] = 1; + } + } + + sx = (sx+1) & 511; + if (a1 & 0x1000) + { + xxx--; + address--; + if (xxx == -1) + { + address -= 56; + xxx = 7; + } + if (address < 0x10000) + address += 0x8000; + } + else + { + xxx++; + address++; + if (xxx == 8) + { + address += 56; + xxx = 0; + } + if (address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if (a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if ((DISPCNT & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + if (DISPCNT & 0x40) + { + inc = sizeX >> 3; + } + int xxx = 0; + if (a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if (a1 & 0x1000) + { + xxx = 7; + for (int xx = sizeX - 1; xx >= 0; xx--) + { + if (sx < 240) + { + u8 color = vram[address]; + if (xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if (color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx--; + if (!(xx & 1)) + address--; + if (xxx == -1) + { + xxx = 7; + address -= 28; + } + if (address < 0x10000) + address += 0x8000; + } + } + else + { + for (int xx = 0; xx < sizeX; xx++) + { + if (sx < 240) + { + u8 color = vram[address]; + if (xx & 1) + { + color = (color >> 4); + } + else + color &= 0x0F; + + if (color) + { + lineOBJWin[sx] = 1; + } + } + sx = (sx+1) & 511; + xxx++; + if (xx & 1) + address++; + if (xxx == 8) + { + address += 28; + xxx = 0; + } + if (address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } + } +} + +inline u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + return color; +} + +inline void gfxIncreaseBrightness(u32 *line, int coeff) +{ + for (int x = 0; x < 240; x++) + { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r + (((31 - r) * coeff) >> 4); + g = g + (((31 - g) * coeff) >> 4); + b = b + (((31 - b) * coeff) >> 4); + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +inline u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if (r < 0) + r = 0; + if (g < 0) + g = 0; + if (b < 0) + b = 0; + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + + return color; +} + +inline void gfxDecreaseBrightness(u32 *line, int coeff) +{ + for (int x = 0; x < 240; x++) + { + u32 color = *line; + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + + r = r - ((r * coeff) >> 4); + g = g - ((g * coeff) >> 4); + b = b - ((b * coeff) >> 4); + if (r < 0) + r = 0; + if (g < 0) + g = 0; + if (b < 0) + b = 0; + *line++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } +} + +inline u32 gfxAlphaBlend(u32 color, u32 color2, int ca, int cb) +{ + if (color < 0x80000000) + { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) >> 4) + ((r0 * cb) >> 4); + g = ((g * ca) >> 4) + ((g0 * cb) >> 4); + b = ((b * ca) >> 4) + ((b0 * cb) >> 4); + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + return (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } + return color; +} + +inline void gfxAlphaBlend(u32 *ta, u32 *tb, int ca, int cb) +{ + for (int x = 0; x < 240; x++) + { + u32 color = *ta; + if (color < 0x80000000) + { + int r = (color & 0x1F); + int g = ((color >> 5) & 0x1F); + int b = ((color >> 10) & 0x1F); + u32 color2 = (*tb++); + int r0 = (color2 & 0x1F); + int g0 = ((color2 >> 5) & 0x1F); + int b0 = ((color2 >> 10) & 0x1F); + + r = ((r * ca) >> 4) + ((r0 * cb) >> 4); + g = ((g * ca) >> 4) + ((g0 * cb) >> 4); + b = ((b * ca) >> 4) + ((b0 * cb) >> 4); + + if (r > 31) + r = 31; + if (g > 31) + g = 31; + if (b > 31) + b = 31; + + *ta++ = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + } + else + { + ta++; + tb++; + } + } +} + +#endif // VBA_GBA_GFX_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBAGlobals.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBAGlobals.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,123 @@ +#include "GBAGlobals.h" + +reg_pair reg[45]; +memoryMap map[256]; +bool8 ioReadable[0x400]; +bool8 N_FLAG = 0; +bool8 C_FLAG = 0; +bool8 Z_FLAG = 0; +bool8 V_FLAG = 0; +bool8 armState = true; +bool8 armIrqEnable = true; +u32 armNextPC = 0x00000000; +int32 armMode = 0x1f; +u32 stop = 0x08000568; +int32 saveType = 0; +bool8 useBios = false; +bool8 skipBios = false; +int32 frameSkip = 1; +u32 extButtons = 0; +bool8 capturePrevious = false; +int32 captureNumber = 0; +bool8 speedup = false; +bool8 synchronize = true; +bool8 cpuDisableSfx = false; +bool8 cpuIsMultiBoot = false; +bool8 parseDebug = true; +int32 layerSettings = 0xff00; +int32 layerEnable = 0xff00; +bool8 speedHack = false; +bool8 memLagEnabled = false; +bool8 memLagTempEnabled = false; +bool8 useOldFrameTiming = false; +int32 cpuSaveType = 0; +bool8 cpuEnhancedDetection = true; +bool8 cheatsEnabled = true; + +u8 *bios = NULL; +u8 *rom = NULL; +u8 *internalRAM = NULL; +u8 *workRAM = NULL; +u8 *paletteRAM = NULL; +u8 *vram = NULL; +u8 *pix = NULL; +u8 *oam = NULL; +u8 *ioMem = NULL; + +u16 DISPCNT = 0x0080; +u16 DISPSTAT = 0x0000; +u16 VCOUNT = 0x0000; +u16 BG0CNT = 0x0000; +u16 BG1CNT = 0x0000; +u16 BG2CNT = 0x0000; +u16 BG3CNT = 0x0000; +u16 BG0HOFS = 0x0000; +u16 BG0VOFS = 0x0000; +u16 BG1HOFS = 0x0000; +u16 BG1VOFS = 0x0000; +u16 BG2HOFS = 0x0000; +u16 BG2VOFS = 0x0000; +u16 BG3HOFS = 0x0000; +u16 BG3VOFS = 0x0000; +u16 BG2PA = 0x0100; +u16 BG2PB = 0x0000; +u16 BG2PC = 0x0000; +u16 BG2PD = 0x0100; +u16 BG2X_L = 0x0000; +u16 BG2X_H = 0x0000; +u16 BG2Y_L = 0x0000; +u16 BG2Y_H = 0x0000; +u16 BG3PA = 0x0100; +u16 BG3PB = 0x0000; +u16 BG3PC = 0x0000; +u16 BG3PD = 0x0100; +u16 BG3X_L = 0x0000; +u16 BG3X_H = 0x0000; +u16 BG3Y_L = 0x0000; +u16 BG3Y_H = 0x0000; +u16 WIN0H = 0x0000; +u16 WIN1H = 0x0000; +u16 WIN0V = 0x0000; +u16 WIN1V = 0x0000; +u16 WININ = 0x0000; +u16 WINOUT = 0x0000; +u16 MOSAIC = 0x0000; +u16 BLDMOD = 0x0000; +u16 COLEV = 0x0000; +u16 COLY = 0x0000; +u16 DM0SAD_L = 0x0000; +u16 DM0SAD_H = 0x0000; +u16 DM0DAD_L = 0x0000; +u16 DM0DAD_H = 0x0000; +u16 DM0CNT_L = 0x0000; +u16 DM0CNT_H = 0x0000; +u16 DM1SAD_L = 0x0000; +u16 DM1SAD_H = 0x0000; +u16 DM1DAD_L = 0x0000; +u16 DM1DAD_H = 0x0000; +u16 DM1CNT_L = 0x0000; +u16 DM1CNT_H = 0x0000; +u16 DM2SAD_L = 0x0000; +u16 DM2SAD_H = 0x0000; +u16 DM2DAD_L = 0x0000; +u16 DM2DAD_H = 0x0000; +u16 DM2CNT_L = 0x0000; +u16 DM2CNT_H = 0x0000; +u16 DM3SAD_L = 0x0000; +u16 DM3SAD_H = 0x0000; +u16 DM3DAD_L = 0x0000; +u16 DM3DAD_H = 0x0000; +u16 DM3CNT_L = 0x0000; +u16 DM3CNT_H = 0x0000; +u16 TM0D = 0x0000; +u16 TM0CNT = 0x0000; +u16 TM1D = 0x0000; +u16 TM1CNT = 0x0000; +u16 TM2D = 0x0000; +u16 TM2CNT = 0x0000; +u16 TM3D = 0x0000; +u16 TM3CNT = 0x0000; +u16 P1 = 0xFFFF; +u16 IE = 0x0000; +u16 IF = 0x0000; +u16 IME = 0x0000; diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBAGlobals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBAGlobals.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,208 @@ +#ifndef VBA_GBA_GLOBALS_H +#define VBA_GBA_GLOBALS_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../Port.h" + +#define VERBOSE_SWI 1 +#define VERBOSE_UNALIGNED_MEMORY 2 +#define VERBOSE_ILLEGAL_WRITE 4 +#define VERBOSE_ILLEGAL_READ 8 +#define VERBOSE_DMA0 16 +#define VERBOSE_DMA1 32 +#define VERBOSE_DMA2 64 +#define VERBOSE_DMA3 128 +#define VERBOSE_UNDEFINED 256 +#define VERBOSE_AGBPRINT 512 + +// moved from armdis.cpp +#define debuggerReadMemory(addr) \ + READ32LE(&map[(addr) >> 24].address[(addr) & map[(addr) >> 24].mask]) + +#define debuggerReadHalfWord(addr) \ + READ16LE(&map[(addr) >> 24].address[(addr) & map[(addr) >> 24].mask]) + +#define debuggerReadByte(addr) \ + READ8LE(&map[(addr) >> 24].address[(addr) & map[(addr) >> 24].mask]) + +#define debuggerWriteMemory(addr, value) \ + WRITE32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]), (value)) + +#define debuggerWriteHalfWord(addr, value) \ + WRITE16LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]), (value)) + +#define debuggerWriteByte(addr, value) \ + WRITE8LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask]), (value)) + +// moved from GBA.h +typedef struct +{ + u8 *address; + u32 mask; +} memoryMap; + +#ifndef NO_GBA_MAP +extern memoryMap map[256]; +#endif + +// moved from GBA.h +typedef union +{ + struct + { +#ifdef WORDS_BIGENDIAN + u8 B3; + u8 B2; + u8 B1; + u8 B0; +#else + u8 B0; + u8 B1; + u8 B2; + u8 B3; +#endif + } B; + struct + { +#ifdef WORDS_BIGENDIAN + u16 W1; + u16 W0; +#else + u16 W0; + u16 W1; +#endif + } W; +#ifdef WORDS_BIGENDIAN + volatile u32 I; +#else + u32 I; +#endif +} reg_pair; + +extern reg_pair reg[45]; +extern u8 biosProtected[4]; +extern bool8 ioReadable[0x400]; +extern bool8 N_FLAG; +extern bool8 C_FLAG; +extern bool8 Z_FLAG; +extern bool8 V_FLAG; +extern bool8 armState; +extern bool8 armIrqEnable; +extern u32 armNextPC; +extern int32 armMode; +extern u32 stop; +extern int32 saveType; +extern bool8 useBios; +extern bool8 skipBios; +extern int32 frameSkip; +extern u32 extButtons; +extern bool8 capturePrevious; +extern int32 captureNumber; +extern bool8 speedup; +extern bool8 synchronize; +extern bool8 cpuDisableSfx; +extern bool8 cpuIsMultiBoot; +extern bool8 parseDebug; +extern int32 layerSettings; +extern int32 layerEnable; +extern bool8 speedHack; +extern bool8 memLagEnabled, memLagTempEnabled; +extern bool8 useOldFrameTiming; +extern int32 cpuSaveType; +extern bool8 cpuEnhancedDetection; +extern bool8 cheatsEnabled; + +extern int emulating; + +extern u8 *bios; +extern u8 *rom; +extern u8 *internalRAM; +extern u8 *workRAM; +extern u8 *paletteRAM; +extern u8 *vram; +extern u8 *pix; +extern u8 *oam; +extern u8 *ioMem; + +extern u16 DISPCNT; +extern u16 DISPSTAT; +extern u16 VCOUNT; +extern u16 BG0CNT; +extern u16 BG1CNT; +extern u16 BG2CNT; +extern u16 BG3CNT; +extern u16 BG0HOFS; +extern u16 BG0VOFS; +extern u16 BG1HOFS; +extern u16 BG1VOFS; +extern u16 BG2HOFS; +extern u16 BG2VOFS; +extern u16 BG3HOFS; +extern u16 BG3VOFS; +extern u16 BG2PA; +extern u16 BG2PB; +extern u16 BG2PC; +extern u16 BG2PD; +extern u16 BG2X_L; +extern u16 BG2X_H; +extern u16 BG2Y_L; +extern u16 BG2Y_H; +extern u16 BG3PA; +extern u16 BG3PB; +extern u16 BG3PC; +extern u16 BG3PD; +extern u16 BG3X_L; +extern u16 BG3X_H; +extern u16 BG3Y_L; +extern u16 BG3Y_H; +extern u16 WIN0H; +extern u16 WIN1H; +extern u16 WIN0V; +extern u16 WIN1V; +extern u16 WININ; +extern u16 WINOUT; +extern u16 MOSAIC; +extern u16 BLDMOD; +extern u16 COLEV; +extern u16 COLY; +extern u16 DM0SAD_L; +extern u16 DM0SAD_H; +extern u16 DM0DAD_L; +extern u16 DM0DAD_H; +extern u16 DM0CNT_L; +extern u16 DM0CNT_H; +extern u16 DM1SAD_L; +extern u16 DM1SAD_H; +extern u16 DM1DAD_L; +extern u16 DM1DAD_H; +extern u16 DM1CNT_L; +extern u16 DM1CNT_H; +extern u16 DM2SAD_L; +extern u16 DM2SAD_H; +extern u16 DM2DAD_L; +extern u16 DM2DAD_H; +extern u16 DM2CNT_L; +extern u16 DM2CNT_H; +extern u16 DM3SAD_L; +extern u16 DM3SAD_H; +extern u16 DM3DAD_L; +extern u16 DM3DAD_H; +extern u16 DM3CNT_L; +extern u16 DM3CNT_H; +extern u16 TM0D; +extern u16 TM0CNT; +extern u16 TM1D; +extern u16 TM1CNT; +extern u16 TM2D; +extern u16 TM2CNT; +extern u16 TM3D; +extern u16 TM3CNT; +extern u16 P1; +extern u16 IE; +extern u16 IF; +extern u16 IME; + +#endif // VBA_GBA_GLOBALS_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBASound.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBASound.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1542 @@ +#if (defined(WIN32) && !defined(SDL)) +# include "../win32/stdafx.h" +# include "../win32/VBA.h" +#endif + +#include +#include + +#include "GBASound.h" +#include "../common/System.h" // SDL build needs this +#include "../common/Util.h" +#include "GBA.h" +#include "GBAGlobals.h" + +#ifndef countof +#define countof(a) (sizeof(a) / sizeof(a[0])) +#endif + +soundtick_t USE_TICKS_AS = 380; // (16777216.0/44100.0); // FIXME: (16777216.0/280896.0)(fps) vs 60.0fps? + +#define SOUND_MAGIC 0x60000000 +#define SOUND_MAGIC_2 0x30000000 +#define NOISE_MAGIC (2097152.0 / 44100.0) + +extern bool8 stopState; + +u8 soundWavePattern[4][32] = { + { 0x01, 0x01, 0x01, 0x01, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff }, + { 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff }, + { 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff }, + { 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff } +}; + +int32 soundFreqRatio[8] = { + 1048576, // 0 + 524288, // 1 + 262144, // 2 + 174763, // 3 + 131072, // 4 + 104858, // 5 + 87381, // 6 + 74898 // 7 +}; + +int32 soundShiftClock[16] = { + 2, // 0 + 4, // 1 + 8, // 2 + 16, // 3 + 32, // 4 + 64, // 5 + 128, // 6 + 256, // 7 + 512, // 8 + 1024, // 9 + 2048, // 10 + 4096, // 11 + 8192, // 12 + 16384, // 13 + 1, // 14 + 1 // 15 +}; + +int32 soundVolume = 0; + +u8 soundBuffer[6][735]; +u16 soundFinalWave[1470]; +u16 soundFrameSound[735 * 30 * 2]; // for avi logging + +u32 soundBufferLen = 1470; +u32 soundBufferTotalLen = 14700; +int32 soundQuality = 2; +int32 soundPaused = 1; +int32 soundPlay = 0; +soundtick_t soundTicks = soundQuality * USE_TICKS_AS; +soundtick_t SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; +u32 soundNextPosition = 0; + +int32 soundLevel1 = 0; +int32 soundLevel2 = 0; +int32 soundBalance = 0; +int32 soundMasterOn = 0; +u32 soundIndex = 0; +u32 soundBufferIndex = 0; +int32 soundFrameSoundWritten = 0; +int32 soundDebug = 0; +bool8 soundOffFlag = false; + +int32 sound1On = 0; +int32 sound1ATL = 0; +int32 sound1Skip = 0; +int32 sound1Index = 0; +int32 sound1Continue = 0; +int32 sound1EnvelopeVolume = 0; +int32 sound1EnvelopeATL = 0; +int32 sound1EnvelopeUpDown = 0; +int32 sound1EnvelopeATLReload = 0; +int32 sound1SweepATL = 0; +int32 sound1SweepATLReload = 0; +int32 sound1SweepSteps = 0; +int32 sound1SweepUpDown = 0; +int32 sound1SweepStep = 0; +u8 * sound1Wave = soundWavePattern[2]; + +int32 sound2On = 0; +int32 sound2ATL = 0; +int32 sound2Skip = 0; +int32 sound2Index = 0; +int32 sound2Continue = 0; +int32 sound2EnvelopeVolume = 0; +int32 sound2EnvelopeATL = 0; +int32 sound2EnvelopeUpDown = 0; +int32 sound2EnvelopeATLReload = 0; +u8 * sound2Wave = soundWavePattern[2]; + +int32 sound3On = 0; +int32 sound3ATL = 0; +int32 sound3Skip = 0; +int32 sound3Index = 0; +int32 sound3Continue = 0; +int32 sound3OutputLevel = 0; +int32 sound3Last = 0; +u8 sound3WaveRam[0x20]; +int32 sound3Bank = 0; +int32 sound3DataSize = 0; +int32 sound3ForcedOutput = 0; + +int32 sound4On = 0; +int32 sound4Clock = 0; +int32 sound4ATL = 0; +int32 sound4Skip = 0; +int32 sound4Index = 0; +int32 sound4ShiftRight = 0x7f; +int32 sound4ShiftSkip = 0; +int32 sound4ShiftIndex = 0; +int32 sound4NSteps = 0; +int32 sound4CountDown = 0; +int32 sound4Continue = 0; +int32 sound4EnvelopeVolume = 0; +int32 sound4EnvelopeATL = 0; +int32 sound4EnvelopeUpDown = 0; +int32 sound4EnvelopeATLReload = 0; + +int32 soundControl = 0; + +int32 soundDSFifoAIndex = 0; +int32 soundDSFifoACount = 0; +int32 soundDSFifoAWriteIndex = 0; +bool8 soundDSAEnabled = false; +int32 soundDSATimer = 0; +u8 soundDSFifoA[32]; +u8 soundDSAValue = 0; + +int32 soundDSFifoBIndex = 0; +int32 soundDSFifoBCount = 0; +int32 soundDSFifoBWriteIndex = 0; +bool8 soundDSBEnabled = false; +int32 soundDSBTimer = 0; +u8 soundDSFifoB[32]; +u8 soundDSBValue = 0; + +int32 soundEnableFlag = 0x3ff; +int32 soundMutedFlag = 0; + +s16 soundFilter[4000]; +s16 soundRight[5] = { 0, 0, 0, 0, 0 }; +s16 soundLeft[5] = { 0, 0, 0, 0, 0 }; +int32 soundEchoIndex = 0; +bool8 soundEcho = false; +bool8 soundLowPass = false; +bool8 soundReverse = false; + +static int32 soundTicks_int32; +static int32 SOUND_CLOCK_TICKS_int32; +static int32 soundDSBValue_int32; +variable_desc soundSaveStruct[] = { + { &soundPaused, sizeof(int32) }, + { &soundPlay, sizeof(int32) }, + { &soundTicks_int32, sizeof(int32) }, + { &SOUND_CLOCK_TICKS_int32, sizeof(int32) }, + { &soundLevel1, sizeof(int32) }, + { &soundLevel2, sizeof(int32) }, + { &soundBalance, sizeof(int32) }, + { &soundMasterOn, sizeof(int32) }, + { &soundIndex, sizeof(int32) }, + { &sound1On, sizeof(int32) }, + { &sound1ATL, sizeof(int32) }, + { &sound1Skip, sizeof(int32) }, + { &sound1Index, sizeof(int32) }, + { &sound1Continue, sizeof(int32) }, + { &sound1EnvelopeVolume, sizeof(int32) }, + { &sound1EnvelopeATL, sizeof(int32) }, + { &sound1EnvelopeATLReload, sizeof(int32) }, + { &sound1EnvelopeUpDown, sizeof(int32) }, + { &sound1SweepATL, sizeof(int32) }, + { &sound1SweepATLReload, sizeof(int32) }, + { &sound1SweepSteps, sizeof(int32) }, + { &sound1SweepUpDown, sizeof(int32) }, + { &sound1SweepStep, sizeof(int32) }, + { &sound2On, sizeof(int32) }, + { &sound2ATL, sizeof(int32) }, + { &sound2Skip, sizeof(int32) }, + { &sound2Index, sizeof(int32) }, + { &sound2Continue, sizeof(int32) }, + { &sound2EnvelopeVolume, sizeof(int32) }, + { &sound2EnvelopeATL, sizeof(int32) }, + { &sound2EnvelopeATLReload, sizeof(int32) }, + { &sound2EnvelopeUpDown, sizeof(int32) }, + { &sound3On, sizeof(int32) }, + { &sound3ATL, sizeof(int32) }, + { &sound3Skip, sizeof(int32) }, + { &sound3Index, sizeof(int32) }, + { &sound3Continue, sizeof(int32) }, + { &sound3OutputLevel, sizeof(int32) }, + { &sound4On, sizeof(int32) }, + { &sound4ATL, sizeof(int32) }, + { &sound4Skip, sizeof(int32) }, + { &sound4Index, sizeof(int32) }, + { &sound4Clock, sizeof(int32) }, + { &sound4ShiftRight, sizeof(int32) }, + { &sound4ShiftSkip, sizeof(int32) }, + { &sound4ShiftIndex, sizeof(int32) }, + { &sound4NSteps, sizeof(int32) }, + { &sound4CountDown, sizeof(int32) }, + { &sound4Continue, sizeof(int32) }, + { &sound4EnvelopeVolume, sizeof(int32) }, + { &sound4EnvelopeATL, sizeof(int32) }, + { &sound4EnvelopeATLReload, sizeof(int32) }, + { &sound4EnvelopeUpDown, sizeof(int32) }, + { &soundEnableFlag, sizeof(int32) }, + { &soundControl, sizeof(int32) }, + { &soundDSFifoAIndex, sizeof(int32) }, + { &soundDSFifoACount, sizeof(int32) }, + { &soundDSFifoAWriteIndex, sizeof(int32) }, + { &soundDSAEnabled, sizeof(bool8) }, + { &soundDSATimer, sizeof(int32) }, + { &soundDSFifoA[0], 32 }, + { &soundDSAValue, sizeof(u8) }, + { &soundDSFifoBIndex, sizeof(int32) }, + { &soundDSFifoBCount, sizeof(int32) }, + { &soundDSFifoBWriteIndex, sizeof(int32) }, + { &soundDSBEnabled, sizeof(int32) }, + { &soundDSBTimer, sizeof(int32) }, + { &soundDSFifoB[0], 32 }, + { &soundDSBValue_int32, sizeof(int32) }, // save as int32 because of a mistake of the past. + { &soundBuffer[0][0], 6 * 735 }, + { &soundFinalWave[0], 2 * 735 }, + { NULL, 0 } +}; + +variable_desc soundSaveStructV2[] = { + { &sound3WaveRam[0], 0x20 }, + { &sound3Bank, sizeof(int32) }, + { &sound3DataSize, sizeof(int32) }, + { &sound3ForcedOutput, sizeof(int32) }, + { NULL, 0 } +}; + +//variable_desc soundSaveStructV3[] = { +// { &soundTicks, sizeof(soundtick_t) }, +// { &SOUND_CLOCK_TICKS, sizeof(soundtick_t) }, +// { &USE_TICKS_AS, sizeof(soundtick_t) }, +// { NULL, 0 } +//}; + +void soundEvent(u32 address, u8 data) +{ + int freq = 0; + + switch (address) + { + case NR10: + data &= 0x7f; + sound1SweepATL = sound1SweepATLReload = 344 * ((data >> 4) & 7); + sound1SweepSteps = data & 7; + sound1SweepUpDown = data & 0x08; + sound1SweepStep = 0; + ioMem[address] = data; + break; + case NR11: + sound1Wave = soundWavePattern[data >> 6]; + sound1ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR12: + sound1EnvelopeUpDown = data & 0x08; + sound1EnvelopeATLReload = 689 * (data & 7); + if ((data & 0xF8) == 0) + sound1EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR13: + freq = (((int)(ioMem[NR14] & 7)) << 8) | data; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + freq = 2048 - freq; + if (freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + ioMem[address] = data; + break; + case NR14: + data &= 0xC7; + freq = (((int)(data & 7) << 8) | ioMem[NR13]); + freq = 2048 - freq; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + sound1Continue = data & 0x40; + if (freq) + { + sound1Skip = SOUND_MAGIC / freq; + } + else + sound1Skip = 0; + if (data & 0x80) + { + ioMem[NR52] |= 1; + sound1EnvelopeVolume = ioMem[NR12] >> 4; + sound1EnvelopeUpDown = ioMem[NR12] & 0x08; + sound1ATL = 172 * (64 - (ioMem[NR11] & 0x3f)); + sound1EnvelopeATLReload = sound1EnvelopeATL = 689 * (ioMem[NR12] & 7); + sound1SweepATL = sound1SweepATLReload = 344 * ((ioMem[NR10] >> 4) & 7); + sound1SweepSteps = ioMem[NR10] & 7; + sound1SweepUpDown = ioMem[NR10] & 0x08; + sound1SweepStep = 0; + + sound1Index = 0; + sound1On = 1; + } + ioMem[address] = data; + break; + case NR21: + sound2Wave = soundWavePattern[data >> 6]; + sound2ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR22: + sound2EnvelopeUpDown = data & 0x08; + sound2EnvelopeATLReload = 689 * (data & 7); + if ((data & 0xF8) == 0) + sound2EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR23: + freq = (((int)(ioMem[NR24] & 7)) << 8) | data; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + freq = 2048 - freq; + if (freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + ioMem[address] = data; + break; + case NR24: + data &= 0xC7; + freq = (((int)(data & 7) << 8) | ioMem[NR23]); + freq = 2048 - freq; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + sound2Continue = data & 0x40; + if (freq) + { + sound2Skip = SOUND_MAGIC / freq; + } + else + sound2Skip = 0; + if (data & 0x80) + { + ioMem[NR52] |= 2; + sound2EnvelopeVolume = ioMem[NR22] >> 4; + sound2EnvelopeUpDown = ioMem[NR22] & 0x08; + sound2ATL = 172 * (64 - (ioMem[NR21] & 0x3f)); + sound2EnvelopeATLReload = sound2EnvelopeATL = 689 * (ioMem[NR22] & 7); + + sound2Index = 0; + sound2On = 1; + } + ioMem[address] = data; + break; + case NR30: + data &= 0xe0; + if (!(data & 0x80)) + { + ioMem[NR52] &= 0xfb; + sound3On = 0; + } + if (((data >> 6) & 1) != sound3Bank) + memcpy(&ioMem[0x90], &sound3WaveRam[(((data >> 6) & 1) * 0x10) ^ 0x10], + 0x10); + sound3Bank = (data >> 6) & 1; + sound3DataSize = (data >> 5) & 1; + ioMem[address] = data; + break; + case NR31: + sound3ATL = 172 * (256 - data); + ioMem[address] = data; + break; + case NR32: + data &= 0xe0; + sound3OutputLevel = (data >> 5) & 3; + sound3ForcedOutput = (data >> 7) & 1; + ioMem[address] = data; + break; + case NR33: + freq = 2048 - (((int)(ioMem[NR34] & 7) << 8) | data); + if (freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + sound3Skip = 0; + ioMem[address] = data; + break; + case NR34: + data &= 0xc7; + freq = 2048 - (((data & 7) << 8) | (int)ioMem[NR33]); + if (freq) + { + sound3Skip = SOUND_MAGIC_2 / freq; + } + else + { + sound3Skip = 0; + } + sound3Continue = data & 0x40; + if ((data & 0x80) && (ioMem[NR30] & 0x80)) + { + ioMem[NR52] |= 4; + sound3ATL = 172 * (256 - ioMem[NR31]); + sound3Index = 0; + sound3On = 1; + } + ioMem[address] = data; + break; + case NR41: + data &= 0x3f; + sound4ATL = 172 * (64 - (data & 0x3f)); + ioMem[address] = data; + break; + case NR42: + sound4EnvelopeUpDown = data & 0x08; + sound4EnvelopeATLReload = 689 * (data & 7); + if ((data & 0xF8) == 0) + sound4EnvelopeVolume = 0; + ioMem[address] = data; + break; + case NR43: + freq = soundFreqRatio[data & 7]; + sound4NSteps = data & 0x08; + + sound4Skip = freq * NOISE_MAGIC; + + sound4Clock = data >> 4; + + freq = freq / soundShiftClock[sound4Clock]; + + sound4ShiftSkip = freq * NOISE_MAGIC; + ioMem[address] = data; + break; + case NR44: + data &= 0xc0; + sound4Continue = data & 0x40; + if (data & 0x80) + { + ioMem[NR52] |= 8; + sound4EnvelopeVolume = ioMem[NR42] >> 4; + sound4EnvelopeUpDown = ioMem[NR42] & 0x08; + sound4ATL = 172 * (64 - (ioMem[NR41] & 0x3f)); + sound4EnvelopeATLReload = sound4EnvelopeATL = 689 * (ioMem[NR42] & 7); + + sound4On = 1; + + sound4Index = 0; + sound4ShiftIndex = 0; + + freq = soundFreqRatio[ioMem[NR43] & 7]; + + sound4Skip = freq * NOISE_MAGIC; + + sound4NSteps = ioMem[NR43] & 0x08; + + freq = freq / soundShiftClock[ioMem[NR43] >> 4]; + + sound4ShiftSkip = freq * NOISE_MAGIC; + if (sound4NSteps) + sound4ShiftRight = 0x7f; + else + sound4ShiftRight = 0x7fff; + } + ioMem[address] = data; + break; + case NR50: + data &= 0x77; + soundLevel1 = data & 7; + soundLevel2 = (data >> 4) & 7; + ioMem[address] = data; + break; + case NR51: + soundBalance = (data & soundEnableFlag); + ioMem[address] = data; + break; + case NR52: + data &= 0x80; + data |= ioMem[NR52] & 15; + soundMasterOn = data & 0x80; + if (!(data & 0x80)) + { + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + } + ioMem[address] = data; + break; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + sound3WaveRam[(sound3Bank * 0x10) ^ 0x10 + (address & 15)] = data; + break; + } +} + +void soundEvent(u32 address, u16 data) +{ + switch (address) + { + case SGCNT0_H: + data &= 0xFF0F; + soundControl = data & 0x770F;; + if (data & 0x0800) + { + soundDSFifoAWriteIndex = 0; + soundDSFifoAIndex = 0; + soundDSFifoACount = 0; + soundDSAValue = 0; + memset(soundDSFifoA, 0, 32); + } + soundDSAEnabled = (data & 0x0300) ? true : false; + soundDSATimer = (data & 0x0400) ? 1 : 0; + if (data & 0x8000) + { + soundDSFifoBWriteIndex = 0; + soundDSFifoBIndex = 0; + soundDSFifoBCount = 0; + soundDSBValue = 0; + memset(soundDSFifoB, 0, 32); + } + soundDSBEnabled = (data & 0x3000) ? true : false; + soundDSBTimer = (data & 0x4000) ? 1 : 0; + *((u16 *)&ioMem[address]) = data; + break; + case FIFOA_L: + case FIFOA_H: + soundDSFifoA[soundDSFifoAWriteIndex++] = data & 0xFF; + soundDSFifoA[soundDSFifoAWriteIndex++] = data >> 8; + soundDSFifoACount += 2; + soundDSFifoAWriteIndex &= 31; + *((u16 *)&ioMem[address]) = data; + break; + case FIFOB_L: + case FIFOB_H: + soundDSFifoB[soundDSFifoBWriteIndex++] = data & 0xFF; + soundDSFifoB[soundDSFifoBWriteIndex++] = data >> 8; + soundDSFifoBCount += 2; + soundDSFifoBWriteIndex &= 31; + *((u16 *)&ioMem[address]) = data; + break; + case 0x88: + data &= 0xC3FF; + *((u16 *)&ioMem[address]) = data; + break; + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + *((u16 *)&sound3WaveRam[(sound3Bank * 0x10) ^ 0x10 + (address & 14)]) = data; + *((u16 *)&ioMem[address]) = data; + break; + } +} + +void soundChannel1() +{ + int vol = sound1EnvelopeVolume; + + int freq = 0; + int value = 0; + + if (sound1On && (sound1ATL || !sound1Continue)) + { + sound1Index += soundQuality * sound1Skip; + sound1Index &= 0x1fffffff; + + value = ((s8)sound1Wave[sound1Index >> 24]) * vol; + } + + soundBuffer[0][soundIndex] = value; + + if (sound1On) + { + if (sound1ATL) + { + sound1ATL -= soundQuality; + + if (sound1ATL <= 0 && sound1Continue) + { + ioMem[NR52] &= 0xfe; + sound1On = 0; + } + } + + if (sound1EnvelopeATL) + { + sound1EnvelopeATL -= soundQuality; + + if (sound1EnvelopeATL <= 0) + { + if (sound1EnvelopeUpDown) + { + if (sound1EnvelopeVolume < 15) + sound1EnvelopeVolume++; + } + else + { + if (sound1EnvelopeVolume) + sound1EnvelopeVolume--; + } + + sound1EnvelopeATL += sound1EnvelopeATLReload; + } + } + + if (sound1SweepATL) + { + sound1SweepATL -= soundQuality; + + if (sound1SweepATL <= 0) + { + freq = (((int)(ioMem[NR14] & 7) << 8) | ioMem[NR13]); + + int updown = 1; + + if (sound1SweepUpDown) + updown = -1; + + int newfreq = 0; + if (sound1SweepSteps) + { + newfreq = freq + updown * freq / (1 << sound1SweepSteps); + if (newfreq == freq) + newfreq = 0; + } + else + newfreq = freq; + + if (newfreq < 0) + { + sound1SweepATL += sound1SweepATLReload; + } + else if (newfreq > 2047) + { + sound1SweepATL = 0; + sound1On = 0; + ioMem[NR52] &= 0xfe; + } + else + { + sound1SweepATL += sound1SweepATLReload; + sound1Skip = SOUND_MAGIC / (2048 - newfreq); + + ioMem[NR13] = newfreq & 0xff; + ioMem[NR14] = (ioMem[NR14] & 0xf8) | ((newfreq >> 8) & 7); + } + } + } + } +} + +void soundChannel2() +{ + // int freq = 0; + int vol = sound2EnvelopeVolume; + + int value = 0; + + if (sound2On && (sound2ATL || !sound2Continue)) + { + sound2Index += soundQuality * sound2Skip; + sound2Index &= 0x1fffffff; + + value = ((s8)sound2Wave[sound2Index >> 24]) * vol; + } + + soundBuffer[1][soundIndex] = value; + + if (sound2On) + { + if (sound2ATL) + { + sound2ATL -= soundQuality; + + if (sound2ATL <= 0 && sound2Continue) + { + ioMem[NR52] &= 0xfd; + sound2On = 0; + } + } + + if (sound2EnvelopeATL) + { + sound2EnvelopeATL -= soundQuality; + + if (sound2EnvelopeATL <= 0) + { + if (sound2EnvelopeUpDown) + { + if (sound2EnvelopeVolume < 15) + sound2EnvelopeVolume++; + } + else + { + if (sound2EnvelopeVolume) + sound2EnvelopeVolume--; + } + sound2EnvelopeATL += sound2EnvelopeATLReload; + } + } + } +} + +void soundChannel3() +{ + int value = sound3Last; + + if (sound3On && (sound3ATL || !sound3Continue)) + { + sound3Index += soundQuality * sound3Skip; + if (sound3DataSize) + { + sound3Index &= 0x3fffffff; + value = sound3WaveRam[sound3Index >> 25]; + } + else + { + sound3Index &= 0x1fffffff; + value = sound3WaveRam[sound3Bank * 0x10 + (sound3Index >> 25)]; + } + + if ((sound3Index & 0x01000000)) + { + value &= 0x0f; + } + else + { + value >>= 4; + } + + value -= 8; + value *= 2; + + if (sound3ForcedOutput) + { + value = ((value >> 1) + value) >> 1; + } + else + { + switch (sound3OutputLevel) + { + case 0: + value = 0; + break; + case 1: + break; + case 2: + value = (value >> 1); + break; + case 3: + value = (value >> 2); + break; + } + } + //value += 1; + sound3Last = value; + } + + soundBuffer[2][soundIndex] = value; + + if (sound3On) + { + if (sound3ATL) + { + sound3ATL -= soundQuality; + + if (sound3ATL <= 0 && sound3Continue) + { + ioMem[NR52] &= 0xfb; + sound3On = 0; + } + } + } +} + +void soundChannel4() +{ + int vol = sound4EnvelopeVolume; + + int value = 0; + + if (sound4Clock <= 0x0c) + { + if (sound4On && (sound4ATL || !sound4Continue)) + { + #define NOISE_ONE_SAMP_SCALE 0x200000 + + sound4Index += soundQuality * sound4Skip; + sound4ShiftIndex += soundQuality * sound4ShiftSkip; + + if (sound4NSteps) + { + while (sound4ShiftIndex >= NOISE_ONE_SAMP_SCALE) + { + sound4ShiftRight = (((sound4ShiftRight << 6) ^ + (sound4ShiftRight << 5)) & 0x40) | + (sound4ShiftRight >> 1); + sound4ShiftIndex -= NOISE_ONE_SAMP_SCALE; + } + } + else + { + while (sound4ShiftIndex >= NOISE_ONE_SAMP_SCALE) + { + sound4ShiftRight = (((sound4ShiftRight << 14) ^ + (sound4ShiftRight << 13)) & 0x4000) | + (sound4ShiftRight >> 1); + + sound4ShiftIndex -= NOISE_ONE_SAMP_SCALE; + } + } + + sound4Index %= NOISE_ONE_SAMP_SCALE; + sound4ShiftIndex %= NOISE_ONE_SAMP_SCALE; + + value = ((sound4ShiftRight & 1) * 2 - 1) * vol; + } + else + { + value = 0; + } + } + + soundBuffer[3][soundIndex] = value; + + if (sound4On) + { + if (sound4ATL) + { + sound4ATL -= soundQuality; + + if (sound4ATL <= 0 && sound4Continue) + { + ioMem[NR52] &= 0xfd; + sound4On = 0; + } + } + + if (sound4EnvelopeATL) + { + sound4EnvelopeATL -= soundQuality; + + if (sound4EnvelopeATL <= 0) + { + if (sound4EnvelopeUpDown) + { + if (sound4EnvelopeVolume < 15) + sound4EnvelopeVolume++; + } + else + { + if (sound4EnvelopeVolume) + sound4EnvelopeVolume--; + } + sound4EnvelopeATL += sound4EnvelopeATLReload; + } + } + } +} + +void soundDirectSoundA() +{ + soundBuffer[4][soundIndex] = soundDSAValue; +} + +void soundDirectSoundATimer() +{ + if (soundDSAEnabled) + { + if (soundDSFifoACount <= 16) + { + CPUCheckDMA(3, 2); + if (soundDSFifoACount <= 16) + { + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + soundEvent(FIFOA_L, (u16)0); + soundEvent(FIFOA_H, (u16)0); + } + } + + soundDSAValue = (soundDSFifoA[soundDSFifoAIndex]); + soundDSFifoAIndex = (++soundDSFifoAIndex) & 31; + soundDSFifoACount--; + } + else + soundDSAValue = 0; +} + +void soundDirectSoundB() +{ + soundBuffer[5][soundIndex] = soundDSBValue; +} + +void soundDirectSoundBTimer() +{ + if (soundDSBEnabled) + { + if (soundDSFifoBCount <= 16) + { + CPUCheckDMA(3, 4); + if (soundDSFifoBCount <= 16) + { + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + soundEvent(FIFOB_L, (u16)0); + soundEvent(FIFOB_H, (u16)0); + } + } + + soundDSBValue = (soundDSFifoB[soundDSFifoBIndex]); + soundDSFifoBIndex = (++soundDSFifoBIndex) & 31; + soundDSFifoBCount--; + } + else + { + soundDSBValue = 0; + } +} + +void soundTimerOverflow(int timer) +{ + if (soundDSAEnabled && (soundDSATimer == timer)) + { + soundDirectSoundATimer(); + } + if (soundDSBEnabled && (soundDSBTimer == timer)) + { + soundDirectSoundBTimer(); + } +} + +void soundMix() +{ + int res = 0; + int cgbRes = 0; + int ratio = ioMem[0x82] & 3; + int dsaRatio = ioMem[0x82] & 4; + int dsbRatio = ioMem[0x82] & 8; + + if (ioMem) + soundBalance = (ioMem[NR51] & soundEnableFlag & ~soundMutedFlag); + + if (soundBalance & 16) + { + cgbRes = ((s8)soundBuffer[0][soundIndex]); + } + if (soundBalance & 32) + { + cgbRes += ((s8)soundBuffer[1][soundIndex]); + } + if (soundBalance & 64) + { + cgbRes += ((s8)soundBuffer[2][soundIndex]); + } + if (soundBalance & 128) + { + cgbRes += ((s8)soundBuffer[3][soundIndex]); + } + + if ((soundControl & 0x0200) && (soundEnableFlag & 0x100)) + { + if (!dsaRatio) + res = ((s8)soundBuffer[4][soundIndex]) >> 1; + else + res = ((s8)soundBuffer[4][soundIndex]); + } + + if ((soundControl & 0x2000) && (soundEnableFlag & 0x200)) + { + if (!dsbRatio) + res += ((s8)soundBuffer[5][soundIndex]) >> 1; + else + res += ((s8)soundBuffer[5][soundIndex]); + } + + res = (res * 170); + cgbRes = (cgbRes * 52 * soundLevel1); + + switch (ratio) + { + case 0: + case 3: // prohibited, but 25% + cgbRes >>= 2; + break; + case 1: + cgbRes >>= 1; + break; + case 2: + break; + } + + res += cgbRes; + + if (soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + } + + if (soundLowPass) + { + soundLeft[4] = soundLeft[3]; + soundLeft[3] = soundLeft[2]; + soundLeft[2] = soundLeft[1]; + soundLeft[1] = soundLeft[0]; + soundLeft[0] = res; + res = (soundLeft[4] + 2 * soundLeft[3] + 8 * soundLeft[2] + 2 * soundLeft[1] + + soundLeft[0]) / 14; + } + + bool noSpecialEffects = false; +#if (defined(WIN32) && !defined(SDL)) + if (theApp.soundRecording || theApp.aviRecording || theApp.nvAudioLog) + noSpecialEffects = true; +#endif + + if (!noSpecialEffects) + { + switch (soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume + 1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + } + + if (res > 32767) + res = 32767; + if (res < -32768) + res = -32768; + + if (soundReverse && !noSpecialEffects) + { + soundFinalWave[++soundBufferIndex] = res; + if ((soundFrameSoundWritten + 1) >= countof(soundFrameSound)) + /*assert(false)*/; + else + soundFrameSound[++soundFrameSoundWritten] = res; + } + else + { + soundFinalWave[soundBufferIndex++] = res; + if (soundFrameSoundWritten >= countof(soundFrameSound)) + /*assert(false)*/; + else + soundFrameSound[soundFrameSoundWritten++] = res; + } + + res = 0; + cgbRes = 0; + + if (soundBalance & 1) + { + cgbRes = ((s8)soundBuffer[0][soundIndex]); + } + if (soundBalance & 2) + { + cgbRes += ((s8)soundBuffer[1][soundIndex]); + } + if (soundBalance & 4) + { + cgbRes += ((s8)soundBuffer[2][soundIndex]); + } + if (soundBalance & 8) + { + cgbRes += ((s8)soundBuffer[3][soundIndex]); + } + + if ((soundControl & 0x0100) && (soundEnableFlag & 0x100)) + { + if (!dsaRatio) + res = ((s8)soundBuffer[4][soundIndex]) >> 1; + else + res = ((s8)soundBuffer[4][soundIndex]); + } + + if ((soundControl & 0x1000) && (soundEnableFlag & 0x200)) + { + if (!dsbRatio) + res += ((s8)soundBuffer[5][soundIndex]) >> 1; + else + res += ((s8)soundBuffer[5][soundIndex]); + } + + res = (res * 170); + cgbRes = (cgbRes * 52 * soundLevel1); + + switch (ratio) + { + case 0: + case 3: // prohibited, but 25% + cgbRes >>= 2; + break; + case 1: + cgbRes >>= 1; + break; + case 2: + break; + } + + res += cgbRes; + + if (soundEcho) + { + res *= 2; + res += soundFilter[soundEchoIndex]; + res /= 2; + soundFilter[soundEchoIndex++] = res; + + if (soundEchoIndex >= 4000) + soundEchoIndex = 0; + } + + if (soundLowPass) + { + soundRight[4] = soundRight[3]; + soundRight[3] = soundRight[2]; + soundRight[2] = soundRight[1]; + soundRight[1] = soundRight[0]; + soundRight[0] = res; + res = (soundRight[4] + 2 * soundRight[3] + 8 * soundRight[2] + 2 * soundRight[1] + + soundRight[0]) / 14; + } + + if (!noSpecialEffects) + { + switch (soundVolume) + { + case 0: + case 1: + case 2: + case 3: + res *= (soundVolume + 1); + break; + case 4: + res >>= 2; + break; + case 5: + res >>= 1; + break; + } + } + + if (res > 32767) + res = 32767; + if (res < -32768) + res = -32768; + + if (soundReverse && !noSpecialEffects) + { + soundFinalWave[-1 + soundBufferIndex++] = res; + if ((soundFrameSoundWritten) >= countof(soundFrameSound)) + /*assert(false)*/; + else + soundFrameSound[-1 + soundFrameSoundWritten++] = res; + } + else + { + soundFinalWave[soundBufferIndex++] = res; + if ((soundFrameSoundWritten + 1) >= countof(soundFrameSound)) + /*assert(false)*/; + else + soundFrameSound[soundFrameSoundWritten++] = res; + } +} + +void soundTick() +{ + if (systemSoundOn) + { + if (soundMasterOn && !stopState) + { + soundChannel1(); + soundChannel2(); + soundChannel3(); + soundChannel4(); + soundDirectSoundA(); + soundDirectSoundB(); + soundMix(); + } + else + { + soundFinalWave[soundBufferIndex++] = 0; + soundFinalWave[soundBufferIndex++] = 0; + if ((soundFrameSoundWritten + 1) >= countof(soundFrameSound)) + /*assert(false)*/; + else + { + soundFrameSound[soundFrameSoundWritten++] = 0; + soundFrameSound[soundFrameSoundWritten++] = 0; + } + } + + soundIndex++; + + if (2 * soundBufferIndex >= soundBufferLen) + { + if (systemSoundOn) + { + if (soundPaused) + { + soundResume(); + } + + systemSoundWriteToBuffer(); + } + soundIndex = 0; + soundBufferIndex = 0; + } + } +} + +void soundShutdown() +{ + systemSoundShutdown(); +} + +void soundPause() +{ + systemSoundPause(); +} + +void soundResume() +{ + systemSoundResume(); +} + +void soundEnableChannels(int channels) +{ + int c = (channels & 0x0f) << 4; + soundEnableFlag |= ((channels & 0x30f) | c); +} + +void soundDisableChannels(int channels) +{ + int c = (channels & 0x0f) << 4; + soundEnableFlag &= ~((channels & 0x30f) | c); +} + +int soundGetEnabledChannels() +{ + return (soundEnableFlag & 0x30f); +} + +#if 0 +// unused +void soundMuteChannels(int channels) +{ + soundMutedFlag |= channels & 0x30f; +} + +void soundUnmuteChannels(int channels) +{ + soundMutedFlag &= ~(channels & 0x30f); +} + +int soundGetMutedChannels() +{ + return (soundMutedFlag & 0x30f); +} +#endif + +void soundReset() +{ + systemSoundReset(); + + soundPaused = 1; + soundPlay = 0; + SOUND_CLOCK_TICKS = soundQuality * USE_TICKS_AS; + soundTicks = SOUND_CLOCK_TICKS; + soundNextPosition = 0; + soundMasterOn = 1; + soundIndex = 0; + soundBufferIndex = 0; + soundLevel1 = 7; + soundLevel2 = 7; + + sound1On = 0; + sound1ATL = 0; + sound1Skip = 0; + sound1Index = 0; + sound1Continue = 0; + sound1EnvelopeVolume = 0; + sound1EnvelopeATL = 0; + sound1EnvelopeUpDown = 0; + sound1EnvelopeATLReload = 0; + sound1SweepATL = 0; + sound1SweepATLReload = 0; + sound1SweepSteps = 0; + sound1SweepUpDown = 0; + sound1SweepStep = 0; + sound1Wave = soundWavePattern[2]; + + sound2On = 0; + sound2ATL = 0; + sound2Skip = 0; + sound2Index = 0; + sound2Continue = 0; + sound2EnvelopeVolume = 0; + sound2EnvelopeATL = 0; + sound2EnvelopeUpDown = 0; + sound2EnvelopeATLReload = 0; + sound2Wave = soundWavePattern[2]; + + sound3On = 0; + sound3ATL = 0; + sound3Skip = 0; + sound3Index = 0; + sound3Continue = 0; + sound3OutputLevel = 0; + sound3Last = 0; + sound3Bank = 0; + sound3DataSize = 0; + sound3ForcedOutput = 0; + + sound4On = 0; + sound4Clock = 0; + sound4ATL = 0; + sound4Skip = 0; + sound4Index = 0; + sound4ShiftRight = 0x7f; + sound4NSteps = 0; + sound4CountDown = 0; + sound4Continue = 0; + sound4EnvelopeVolume = 0; + sound4EnvelopeATL = 0; + sound4EnvelopeUpDown = 0; + sound4EnvelopeATLReload = 0; + + sound1On = 0; + sound2On = 0; + sound3On = 0; + sound4On = 0; + + int addr = 0x90; + + while (addr < 0xA0) + { + ioMem[addr++] = 0x00; + ioMem[addr++] = 0xff; + } + + addr = 0; + while (addr < 0x20) + { + sound3WaveRam[addr++] = 0x00; + sound3WaveRam[addr++] = 0xff; + } + + memset(soundFinalWave, 0, soundBufferLen); + + memset(soundFilter, 0, sizeof(soundFilter)); + soundEchoIndex = 0; +} + +bool soundInit() +{ + if (systemSoundInit()) + { + memset(soundBuffer[0], 0, 735 * 2); + memset(soundBuffer[1], 0, 735 * 2); + memset(soundBuffer[2], 0, 735 * 2); + memset(soundBuffer[3], 0, 735 * 2); + + memset(soundFinalWave, 0, soundBufferLen); + + soundPaused = 1; + return true; + } + return false; +} + +void soundSetQuality(int quality) +{ + if (soundQuality != quality && systemSoundCanChangeQuality()) + { + if (!soundOffFlag) + soundShutdown(); + soundQuality = quality; + soundNextPosition = 0; + if (!soundOffFlag) + soundInit(); + SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } + else if (soundQuality != quality) + { + soundNextPosition = 0; + SOUND_CLOCK_TICKS = USE_TICKS_AS * soundQuality; + soundIndex = 0; + soundBufferIndex = 0; + } +} + +void soundSaveGame(gzFile gzFile) +{ + soundTicks_int32 = (int32) soundTicks; + SOUND_CLOCK_TICKS_int32 = (int32) SOUND_CLOCK_TICKS; + soundDSBValue_int32 = (int32) soundDSBValue; + + utilWriteData(gzFile, soundSaveStruct); + utilWriteData(gzFile, soundSaveStructV2); + + utilGzWrite(gzFile, &soundQuality, sizeof(int32)); + //utilWriteData(gzFile, soundSaveStructV3); +} + +void soundReadGame(gzFile gzFile, int version) +{ + int32 oldSoundPaused = soundPaused; + int32 oldSoundEnableFlag = soundEnableFlag; + utilReadData(gzFile, soundSaveStruct); + soundPaused = oldSoundPaused; + soundEnableFlag = oldSoundEnableFlag; + + if (version >= SAVE_GAME_VERSION_3) + { + utilReadData(gzFile, soundSaveStructV2); + } + else + { + sound3Bank = (ioMem[NR30] >> 6) & 1; + sound3DataSize = (ioMem[NR30] >> 5) & 1; + sound3ForcedOutput = (ioMem[NR32] >> 7) & 1; + // nothing better to do here... + memcpy(&sound3WaveRam[0x00], &ioMem[0x90], 0x10); + memcpy(&sound3WaveRam[0x10], &ioMem[0x90], 0x10); + } + soundBufferIndex = soundIndex * 2; + + int quality = 1; + utilGzRead(gzFile, &quality, sizeof(int32)); + soundSetQuality(quality); + + sound1Wave = soundWavePattern[ioMem[NR11] >> 6]; + sound2Wave = soundWavePattern[ioMem[NR21] >> 6]; + + //if(version >= SAVE_GAME_VERSION_14) { + // utilReadData(gzFile, soundSaveStructV3); + //} + //else { + soundTicks = (soundtick_t) soundTicks_int32; + SOUND_CLOCK_TICKS = (soundtick_t) SOUND_CLOCK_TICKS_int32; + //} + soundDSBValue = (u8) (soundDSBValue_int32 & 0xff); +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBASound.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBASound.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,73 @@ +#ifndef VBA_GBA_SOUND_H +#define VBA_GBA_SOUND_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "zlib.h" +#include "../Port.h" + +#define NR10 0x60 +#define NR11 0x62 +#define NR12 0x63 +#define NR13 0x64 +#define NR14 0x65 +#define NR21 0x68 +#define NR22 0x69 +#define NR23 0x6c +#define NR24 0x6d +#define NR30 0x70 +#define NR31 0x72 +#define NR32 0x73 +#define NR33 0x74 +#define NR34 0x75 +#define NR41 0x78 +#define NR42 0x79 +#define NR43 0x7c +#define NR44 0x7d +#define NR50 0x80 +#define NR51 0x81 +#define NR52 0x84 +#define SGCNT0_H 0x82 +#define FIFOA_L 0xa0 +#define FIFOA_H 0xa2 +#define FIFOB_L 0xa4 +#define FIFOB_H 0xa6 + +extern void soundTick(); +extern void soundShutdown(); +extern bool soundInit(); +extern void soundPause(); +extern void soundResume(); +extern void soundEnableChannels(int); +extern void soundDisableChannels(int); +extern int soundGetEnabledChannels(); +extern void soundReset(); +extern void soundSaveGame(gzFile); +extern void soundReadGame(gzFile, int); +extern void soundEvent(u32, u8); +extern void soundEvent(u32, u16); +extern void soundTimerOverflow(int); +extern void soundSetQuality(int); + +typedef int32 soundtick_t; + +extern soundtick_t SOUND_CLOCK_TICKS; +extern soundtick_t soundTicks; +extern int32 soundPaused; +extern bool8 soundOffFlag; +extern int32 soundQuality; +extern u32 soundBufferLen; +extern u32 soundBufferTotalLen; +extern u32 soundNextPosition; +extern u16 soundFinalWave[1470]; +extern u16 soundFrameSound[735*30*2]; +extern int32 soundFrameSoundWritten; +extern int32 soundVolume; + +extern bool8 soundEcho; +extern bool8 soundLowPass; +extern bool8 soundReverse; + +#endif // VBA_GBA_SOUND_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/GBAinline.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/GBAinline.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,490 @@ +#ifndef VBA_GBAINLINE_H +#define VBA_GBAINLINE_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../Port.h" +#include "../common/System.h" +#include "../common/vbalua.h" +#include "GBAGlobals.h" +#include "EEprom.h" +#include "Flash.h" +#include "RTC.h" + +extern bool8 cpuSramEnabled; +extern bool8 cpuFlashEnabled; +extern bool8 cpuEEPROMEnabled; +extern bool8 cpuEEPROMSensorEnabled; + +#define CPUReadByteQuick(addr) \ + map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] + +#define CPUReadHalfWordQuick(addr) \ + READ16LE(((u16 *)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define CPUReadMemoryQuick(addr) \ + READ32LE(((u32 *)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +//inline u32 CPUReadMemoryQuick(u32 addr) +//{ +// u32 addrShift = (addr)>>24; +// u32 rt = (addr) & map[addrShift].mask; +// return READ32LE(((u32*)&map[addrShift].address[rt])); +//} + +inline u32 CPUReadMemory(u32 address) +{ +#ifdef GBA_LOGGING + if (address & 3) + { + if (systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + switch (address >> 24) + { + case 0: + if (reg[15].I >> 24) + { + if (address < 0x4000) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + value = READ32LE(((u32 *)&biosProtected)); + } + else + goto unreadable; + } + else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 2: + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 3: + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 4: + if ((address < 0x4000400) && ioReadable[address & 0x3fc]) + { + if (ioReadable[(address & 0x3fc) + 2]) + { + if (address >= 0x400012d && address <= 0x4000131) + GBASystemCounters.lagged = false; + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + } + else + { + if (address >= 0x400012f && address <= 0x4000131) + GBASystemCounters.lagged = false; + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + } + else + goto unreadable; + break; + case 5: + value = READ32LE(((u32 *)&paletteRAM[address & 0x3fC])); + break; + case 6: + value = READ32LE(((u32 *)&vram[address & 0x1fffc])); + break; + case 7: + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 13: + if (cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if (cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal word read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + // if(ioMem[0x205] & 0x40) { + if (armState) + { + value = CPUReadMemoryQuick(reg[15].I); + } + else + { + value = CPUReadHalfWordQuick(reg[15].I) | + CPUReadHalfWordQuick(reg[15].I) << 16; + } + // } else { + // value = *((u32 *)&bios[address & 0x3ffc]); + // } + // return 0xFFFFFFFF; + } + + if (address & 3) + { +#ifdef C_CORE + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); +#else +#ifdef __GNUC__ + asm ("and $3, %%ecx;" + "shl $3 ,%%ecx;" + "ror %%cl, %0" + : "=r" (value) + : "r" (value), "c" (address)); +#else + __asm { + mov ecx, address; + and ecx, 3; + shl ecx, 3; + ror [dword ptr value], cl; + } +#endif +#endif + } + return value; +} + +extern u32 myROM[]; + +inline u32 CPUReadHalfWord(u32 address) +{ +#ifdef GBA_LOGGING + if (address & 1) + { + if (systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaligned halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } + } +#endif + + u32 value; + + switch (address >> 24) + { + case 0: + if (reg[15].I >> 24) + { + if (address < 0x4000) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + value = READ16LE(((u16 *)&biosProtected[address&2])); + } + else + goto unreadable; + } + else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if ((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + if (address >= 0x400012f && address <= 0x4000131) + GBASystemCounters.lagged = false; + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + } + else + goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&paletteRAM[address & 0x3fe])); + break; + case 6: + value = READ16LE(((u16 *)&vram[address & 0x1fffe])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + if (cpuEEPROMEnabled) + // no need to swap this + return eepromRead(address); + goto unreadable; + case 14: + if (cpuFlashEnabled | cpuSramEnabled) + // no need to swap this + return flashRead(address); + // default + default: +unreadable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal halfword read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + extern bool8 cpuDmaHack; + extern u32 cpuDmaLast; + extern int32 cpuDmaCount; + if (cpuDmaHack && cpuDmaCount) + { + value = (u16)cpuDmaLast; + } + else + { + if (armState) + { + value = CPUReadHalfWordQuick(reg[15].I + (address & 2)); + } + else + { + value = CPUReadHalfWordQuick(reg[15].I); + } + } + // return value; + // if(address & 1) + // value = (value >> 8) | ((value & 0xFF) << 24); + // return 0xFFFF; + break; + } + + if (address & 1) + { + value = (value >> 8) | (value << 24); + } + + return value; +} + +inline u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if ((address & 1)) + value = (s8)value; + return value; +} + +inline u8 CPUReadByte(u32 address) +{ + switch (address >> 24) + { + case 0: + if (reg[15].I >> 24) + { + if (address < 0x4000) + { +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + return biosProtected[address & 3]; + } + else + goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if ((address < 0x4000400) && ioReadable[address & 0x3ff]) + { + if (address == 0x4000130 || address == 0x4000131) + GBASystemCounters.lagged = false; + return ioMem[address & 0x3ff]; + } + else + goto unreadable; + case 5: + return paletteRAM[address & 0x3ff]; + case 6: + return vram[address & 0x1ffff]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + if (cpuEEPROMEnabled) + return eepromRead(address); + goto unreadable; + case 14: + if (cpuSramEnabled | cpuFlashEnabled) + return flashRead(address); + if (cpuEEPROMSensorEnabled) + { + switch (address & 0x00008f00) + { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } + // default + default: +unreadable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_READ) + { + log("Illegal byte read: %08x at %08x\n", address, armMode ? + armNextPC - 4 : armNextPC - 2); + } +#endif + + if (armState) + { + return CPUReadByteQuick(reg[15].I+(address & 3)); + } + else + { + return CPUReadByteQuick(reg[15].I+(address & 1)); + } + // return 0xFF; + break; + } +} + +inline void CPUWriteMemoryWrapped(u32 address, u32 value) +{ +#ifdef GBA_LOGGING + if (address & 3) + { + if (systemVerbose & VERBOSE_UNALIGNED_MEMORY) + { + log("Unaliagned word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } + } +#endif + + switch (address >> 24) + { + case 0x02: +#ifdef SDL + if (*((u32 *)&freezeWorkRAM[address & 0x3FFFC])) + cheatsWriteMemory((u32 *)&workRAM[address & 0x3FFFC], + value, + *((u32 *)&freezeWorkRAM[address & 0x3FFFC])); + else +#endif + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: +#ifdef SDL + if (*((u32 *)&freezeInternalRAM[address & 0x7ffc])) + cheatsWriteMemory((u32 *)&internalRAM[address & 0x7FFC], + value, + *((u32 *)&freezeInternalRAM[address & 0x7ffc])); + else +#endif + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + break; + case 0x05: + WRITE32LE(((u32 *)&paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + if (address & 0x10000) + WRITE32LE(((u32 *)&vram[address & 0x17ffc]), value); + else + WRITE32LE(((u32 *)&vram[address & 0x1fffc]), value); + break; + case 0x07: + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if (cpuEEPROMEnabled) + { + eepromWrite(address, value); + break; + } + goto unwritable; + case 0x0E: + if (!eepromInUse | cpuSramEnabled | cpuFlashEnabled) + { + (*cpuSaveGameFunc)(address, (u8)value); + break; + } + // default + default: +unwritable: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_ILLEGAL_WRITE) + { + log("Illegal word write: %08x to %08x from %08x\n", + value, + address, + armMode ? armNextPC - 4 : armNextPC - 2); + } +#endif + break; + } +} + +inline void CPUWriteMemory(u32 address, u32 value) +{ + CPUWriteMemoryWrapped(address, value); + CallRegisteredLuaMemHook(address, 4, value, LUAMEMHOOK_WRITE); +} + +#endif // VBA_GBAINLINE_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Makefile.am Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,48 @@ +noinst_LIBRARIES = libgba.a + +libgba_a_SOURCES = \ + agbprint.h \ + armdis.h \ + arm-new.h \ + bios.h \ + EEprom.h \ + elf.h \ + Flash.h \ + GBACheats.h \ + GBAGfx.h \ + GBAGlobals.h \ + GBA.h \ + GBAinline.h \ + GBASound.h \ + RTC.h \ + Sram.h \ + thumb.h \ + \ + agbprint.cpp \ + armdis.cpp \ + bios.cpp \ + EEprom.cpp \ + elf.cpp \ + Flash.cpp \ + GBACheats.cpp \ + GBA.cpp \ + GBAGfx.cpp \ + GBAGlobals.cpp \ + GBASound.cpp \ + Mode0.cpp \ + Mode1.cpp \ + Mode2.cpp \ + Mode3.cpp \ + Mode4.cpp \ + Mode5.cpp \ + remote.cpp \ + RTC.cpp \ + Sram.cpp + + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -DSDL \ + -DSYSCONFDIR=\"$(sysconfdir)\" + +AM_CXXFLAGS = -fno-exceptions @SDL_CFLAGS@ diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode0.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode0.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,633 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode0RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + if (layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if (layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if (layerEnable & 0x0400) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if (layerEnable & 0x0800) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + +void mode0RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + if (layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if (layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if (layerEnable & 0x0400) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if (layerEnable & 0x0800) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + int effect = (BLDMOD >> 6) & 3; + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if (line1[x] < (color & 0xFF000000)) + { + color = line1[x]; + top = 0x02; + } + + if (line2[x] < (color & 0xFF000000)) + { + color = line2[x]; + top = 0x04; + } + + if (line3[x] < (color & 0xFF000000)) + { + color = line3[x]; + top = 0x08; + } + + if (lineOBJ[x] < (color & 0xFF000000)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch (effect) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if (line0[x] < back) + { + if (top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if (line1[x] < (back & 0xFF000000)) + { + if (top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if (line2[x] < (back & 0xFF000000)) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if (line3[x] < (back & 0xFF000000)) + { + if (top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if (lineOBJ[x] < (back & 0xFF000000)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if (line0[x] < back) + { + back = line0[x]; + top2 = 0x01; + } + + if (line1[x] < (back & 0xFF000000)) + { + back = line1[x]; + top2 = 0x02; + } + + if (line2[x] < (back & 0xFF000000)) + { + back = line2[x]; + top2 = 0x04; + } + + if (line3[x] < (back & 0xFF000000)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + +void mode0RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if ((layerEnable & 0x0100)) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if ((layerEnable & 0x0200)) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if ((layerEnable & 0x0400)) + { + gfxDrawTextScreen(BG2CNT, BG2HOFS, BG2VOFS, line2); + } + + if ((layerEnable & 0x0800)) + { + gfxDrawTextScreen(BG3CNT, BG3HOFS, BG3VOFS, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if ((mask & 1) && (line0[x] < color)) + { + color = line0[x]; + top = 0x01; + } + + if ((mask & 2) && ((u8)(line1[x]>>24) < (u8)(color >> 24))) + { + color = line1[x]; + top = 0x02; + } + + if ((mask & 4) && ((u8)(line2[x]>>24) < (u8)(color >> 24))) + { + color = line2[x]; + top = 0x04; + } + + if ((mask & 8) && ((u8)(line3[x]>>24) < (u8)(color >> 24))) + { + color = line3[x]; + top = 0x08; + } + + if ((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + // special FX on in the window + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if ((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if ((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if ((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 1) && ((u8)(line0[x]>>24) < (u8)(back >> 24))) + { + back = line0[x]; + top2 = 0x01; + } + + if ((mask & 2) && ((u8)(line1[x]>>24) < (u8)(back >> 24))) + { + back = line1[x]; + top2 = 0x02; + } + + if ((mask & 4) && ((u8)(line2[x]>>24) < (u8)(back >> 24))) + { + back = line2[x]; + top2 = 0x04; + } + + if ((mask & 8) && ((u8)(line3[x]>>24) < (u8)(back >> 24))) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 1) && ((u8)(line0[x]>>24) < (u8)(back >> 24))) + { + back = line0[x]; + top2 = 0x01; + } + + if ((mask & 2) && ((u8)(line1[x]>>24) < (u8)(back >> 24))) + { + back = line1[x]; + top2 = 0x02; + } + + if ((mask & 4) && ((u8)(line2[x]>>24) < (u8)(back >> 24))) + { + back = line2[x]; + top2 = 0x04; + } + + if ((mask & 8) && ((u8)(line3[x]>>24) < (u8)(back >> 24))) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode1.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode1.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,580 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode1RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if (layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if (layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line0[x] < color) + { + color = line0[x]; + top = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(color >> 24)) + { + color = line1[x]; + top = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if ((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if ((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode1RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if (layerEnable & 0x0100) + { + gfxDrawTextScreen(BG0CNT, BG0HOFS, BG0VOFS, line0); + } + + if (layerEnable & 0x0200) + { + gfxDrawTextScreen(BG1CNT, BG1HOFS, BG1VOFS, line1); + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if (line0[x] < color && (mask & 1)) + { + color = line0[x]; + top = 0x01; + } + + if ((u8)(line1[x]>>24) < (u8)(color >> 24) && (mask & 2)) + { + color = line1[x]; + top = 0x02; + } + + if ((u8)(line2[x]>>24) < (u8)(color >> 24) && (mask & 4)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) + { + color = lineOBJ[x]; + top = 0x10; + } + + // special FX on the window + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + if ((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x01) + { + back = line0[x]; + top2 = 0x01; + } + } + + if ((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x02) + { + back = line1[x]; + top2 = 0x02; + } + } + + if ((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if ((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if ((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 1) && (u8)(line0[x]>>24) < (u8)(back >> 24)) + { + back = line0[x]; + top2 = 0x01; + } + + if ((mask & 2) && (u8)(line1[x]>>24) < (u8)(back >> 24)) + { + back = line1[x]; + top2 = 0x02; + } + + if ((mask & 4) && (u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode2.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,530 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode2RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if (layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if ((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if (layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if ((u8)(line2[x]>>24) < (u8)(color >> 24)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(color >> 24)) + { + color = line3[x]; + top = 0x08; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((u8)(line2[x]>>24) < (u8)(back >> 24)) + { + back = line2[x]; + top2 = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode2RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, gfxBG2X, gfxBG2Y, + changed, line2); + } + + if (layerEnable & 0x0800) + { + int changed = gfxBG3Changed; + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen(BG3CNT, BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + BG3PA, BG3PB, BG3PC, BG3PD, gfxBG3X, gfxBG3Y, + changed, line3); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if (line2[x] < color && (mask & 4)) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(line3[x]>>24) < (u8)(color >> 24) && (mask & 8)) + { + color = line3[x]; + top = 0x08; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24) && (mask & 16)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x08) + { + back = line3[x]; + top2 = 0x08; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if ((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if ((mask & 8) && (u8)(line3[x]>>24) < (u8)(back >> 24)) + { + back = line3[x]; + top2 = 0x08; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode3.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode3.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,443 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode3RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode3RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x80) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if ((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if ((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode4.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode4.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,440 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode4RenderLine() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineNoWindow() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + if (layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >> 24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if (line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode4RenderLineAll() +{ + u16 *palette = (u16 *)paletteRAM; + + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + if (layerEnable & 0x400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen256(BG2CNT, BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + BG2PA, BG2PB, BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + u32 backdrop = (READ16LE(&palette[0]) | 0x30000000); + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + for (int x = 0; x < 240; x++) + { + u32 color = backdrop; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if ((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if ((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = backdrop; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Mode5.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Mode5.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,443 @@ +#include "GBAGfx.h" +#include "GBAGlobals.h" + +void mode5RenderLine() +{ + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if ((top & 0x10) && (color & 0x00010000)) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineNoWindow() +{ + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + + if (line2[x] < color) + { + color = line2[x]; + top = 0x04; + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(color >>24)) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if (line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + +void mode5RenderLineAll() +{ + if (DISPCNT & 0x0080) + { + for (int x = 0; x < 240; x++) + { + lineMix[x] = 0x7fff; + } + gfxLastVCOUNT = VCOUNT; + return; + } + + u16 *palette = (u16 *)paletteRAM; + + if (layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + + if (gfxLastVCOUNT > VCOUNT) + changed = 3; + + gfxDrawRotScreen16Bit160(BG2CNT, BG2X_L, BG2X_H, + BG2Y_L, BG2Y_H, BG2PA, BG2PB, + BG2PC, BG2PD, + gfxBG2X, gfxBG2Y, changed, + line2); + } + + gfxDrawSprites(lineOBJ); + gfxDrawOBJWin(lineOBJWin); + + bool inWindow0 = false; + bool inWindow1 = false; + + if (layerEnable & 0x2000) + { + u8 v0 = WIN0V >> 8; + u8 v1 = WIN0V & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow0 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow0 |= (VCOUNT >= v0 || VCOUNT < v1); + } + if (layerEnable & 0x4000) + { + u8 v0 = WIN1V >> 8; + u8 v1 = WIN1V & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if (v1 >= v0) + inWindow1 |= (VCOUNT >= v0 && VCOUNT < v1); + else + inWindow1 |= (VCOUNT >= v0 || VCOUNT < v1); + } + + u8 inWin0Mask = WININ & 0xFF; + u8 inWin1Mask = WININ >> 8; + u8 outMask = WINOUT & 0xFF; + + u32 background = (READ16LE(&palette[0]) | 0x30000000); + + for (int x = 0; x < 240; x++) + { + u32 color = background; + u8 top = 0x20; + u8 mask = outMask; + + if (!(lineOBJWin[x] & 0x80000000)) + { + mask = WINOUT >> 8; + } + + if (inWindow1) + { + if (gfxInWin1[x]) + mask = inWin1Mask; + } + + if (inWindow0) + { + if (gfxInWin0[x]) + { + mask = inWin0Mask; + } + } + + if ((mask & 4) && (line2[x] < color)) + { + color = line2[x]; + top = 0x04; + } + + if ((mask & 16) && ((u8)(lineOBJ[x]>>24) < (u8)(color >>24))) + { + color = lineOBJ[x]; + top = 0x10; + } + + if (mask & 32) + { + if (!(color & 0x00010000)) + { + switch ((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + if (top & BLDMOD) + { + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + if (top != 0x04) + { + back = line2[x]; + top2 = 0x04; + } + } + + if ((mask & 16) && (u8)(lineOBJ[x]>>24) < (u8)(back >> 24)) + { + if (top != 0x10) + { + back = lineOBJ[x]; + top2 = 0x10; + } + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + } + break; + } + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + else + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + } + else if (color & 0x00010000) + { + // semi-transparent OBJ + u32 back = background; + u8 top2 = 0x20; + + if ((mask & 4) && line2[x] < back) + { + back = line2[x]; + top2 = 0x04; + } + + if (top2 & (BLDMOD>>8)) + color = gfxAlphaBlend(color, back, + coeff[COLEV & 0x1F], + coeff[(COLEV >> 8) & 0x1F]); + else + { + switch ((BLDMOD >> 6) & 3) + { + case 2: + if (BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if (BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + } + + lineMix[x] = color; + } + gfxBG2Changed = 0; + gfxLastVCOUNT = VCOUNT; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/RTC.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/RTC.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,240 @@ +#include + +#include "../Port.h" +#include "../NLS.h" +#include "../common/System.h" // systemMessage +#include "../common/Util.h" +#include "../common/movie.h" +#include "GBAGlobals.h" + +enum RTCSTATE { IDLE, COMMAND, DATA, READDATA }; + +typedef struct +{ + u8 byte0; + u8 byte1; + u8 byte2; + u8 command; + int dataLen; + int bits; + RTCSTATE state; + u8 data[12]; + // reserved variables for future + u8 reserved[12]; + bool reserved2; + u32 reserved3; +} RTCCLOCKDATA; + +static RTCCLOCKDATA rtcClockData; +static bool rtcEnabled = false; + +void rtcEnable(bool enable) +{ + rtcEnabled = enable; +} + +bool rtcIsEnabled() +{ + return rtcEnabled; +} + +u16 rtcRead(u32 address) +{ + if (rtcEnabled) + { + if (address == 0x80000c8) + return rtcClockData.byte2; + else if (address == 0x80000c6) + return rtcClockData.byte1; + else if (address == 0x80000c4) + { + return rtcClockData.byte0; + } + } + + return READ16LE((&rom[address & 0x1FFFFFE])); +} + +static u8 toBCD(u8 value) +{ + value = value % 100; + int l = value % 10; + int h = value / 10; + return h * 16 + l; +} + +bool rtcWrite(u32 address, u16 value) +{ + if (!rtcEnabled) + return false; + + if (address == 0x80000c8) + { + rtcClockData.byte2 = (u8)value; // enable ? + } + else if (address == 0x80000c6) + { + rtcClockData.byte1 = (u8)value; // read/write + } + else if (address == 0x80000c4) + { + if (rtcClockData.byte2 & 1) + { + if (rtcClockData.state == IDLE && rtcClockData.byte0 == 1 && value == 5) + { + rtcClockData.state = COMMAND; + rtcClockData.bits = 0; + rtcClockData.command = 0; + } + else if (!(rtcClockData.byte0 & 1) && (value & 1)) // bit transfer + { + rtcClockData.byte0 = (u8)value; + switch (rtcClockData.state) + { + case COMMAND: + rtcClockData.command |= ((value & 2) >> 1) << (7-rtcClockData.bits); + rtcClockData.bits++; + if (rtcClockData.bits == 8) + { + rtcClockData.bits = 0; + switch (rtcClockData.command) + { + case 0x60: + // not sure what this command does but it doesn't take parameters + // maybe it is a reset or stop + rtcClockData.state = IDLE; + rtcClockData.bits = 0; + break; + case 0x62: + // this sets the control state but not sure what those values are + rtcClockData.state = READDATA; + rtcClockData.dataLen = 1; + break; + case 0x63: + rtcClockData.dataLen = 1; + rtcClockData.data[0] = 0x40; + rtcClockData.state = DATA; + break; + case 0x65: + { + struct tm *newtime; + time_t long_time; + + if (VBAMovieActive() || VBAMovieLoading()) + { + long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60; + newtime = gmtime(&long_time); + } + else + { + time(&long_time); /* Get time as long integer. */ + newtime = localtime(&long_time); /* Convert to local time. */ + } + + rtcClockData.dataLen = 7; + rtcClockData.data[0] = toBCD(newtime->tm_year); + rtcClockData.data[1] = toBCD(newtime->tm_mon+1); + rtcClockData.data[2] = toBCD(newtime->tm_mday); + rtcClockData.data[3] = 0; + rtcClockData.data[4] = toBCD(newtime->tm_hour); + rtcClockData.data[5] = toBCD(newtime->tm_min); + rtcClockData.data[6] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + break; + } + case 0x67: + { + struct tm *newtime; + time_t long_time; + + if (VBAMovieActive() || VBAMovieLoading()) + { + long_time = VBAMovieGetId() + VBAMovieGetFrameCounter()/60; + newtime = gmtime(&long_time); + } + else + { + time(&long_time); /* Get time as long integer. */ + newtime = localtime(&long_time); /* Convert to local time. */ + } + + rtcClockData.dataLen = 3; + rtcClockData.data[0] = toBCD(newtime->tm_hour); + rtcClockData.data[1] = toBCD(newtime->tm_min); + rtcClockData.data[2] = toBCD(newtime->tm_sec); + rtcClockData.state = DATA; + break; + } + default: + systemMessage(0, N_("Unknown RTC command %02x"), rtcClockData.command); + rtcClockData.state = IDLE; + break; + } + } + break; + case DATA: + if (rtcClockData.byte1 & 2) + {} + else + { + rtcClockData.byte0 = (rtcClockData.byte0 & ~2) | + ((rtcClockData.data[rtcClockData.bits >> 3] >> + (rtcClockData.bits & 7)) & 1)*2; + rtcClockData.bits++; + if (rtcClockData.bits == 8*rtcClockData.dataLen) + { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + case READDATA: + if (!(rtcClockData.byte1 & 2)) + {} + else + { + rtcClockData.data[rtcClockData.bits >> 3] = + (rtcClockData.data[rtcClockData.bits >> 3] >> 1) | + ((value << 6) & 128); + rtcClockData.bits++; + if (rtcClockData.bits == 8*rtcClockData.dataLen) + { + rtcClockData.bits = 0; + rtcClockData.state = IDLE; + } + } + break; + default: + break; + } + } + else + rtcClockData.byte0 = (u8)value; + } + } + return true; +} + +void rtcReset() +{ + memset(&rtcClockData, 0, sizeof(rtcClockData)); + + rtcClockData.byte0 = 0; + rtcClockData.byte1 = 0; + rtcClockData.byte2 = 0; + rtcClockData.command = 0; + rtcClockData.dataLen = 0; + rtcClockData.bits = 0; + rtcClockData.state = IDLE; +} + +void rtcSaveGame(gzFile gzFile) +{ + utilGzWrite(gzFile, &rtcClockData, sizeof(rtcClockData)); +} + +void rtcReadGame(gzFile gzFile) +{ + utilGzRead(gzFile, &rtcClockData, sizeof(rtcClockData)); +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/RTC.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/RTC.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,17 @@ +#ifndef VBA_RTC_H +#define VBA_RTC_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +extern u16 rtcRead(u32 address); +extern bool rtcWrite(u32 address, u16 value); +extern void rtcEnable(bool); +extern bool rtcIsEnabled(); +extern void rtcReset(); + +extern void rtcReadGame(gzFile gzFile); +extern void rtcSaveGame(gzFile gzFile); + +#endif // VBA_RTC_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Sram.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Sram.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,15 @@ +#include "../common/System.h" +#include "Flash.h" +#include "Sram.h" + +u8 sramRead(u32 address) +{ + return flashSaveMemory[address & 0xFFFF]; +} + +void sramWrite(u32 address, u8 byte) +{ + flashSaveMemory[address & 0xFFFF] = byte; + systemSaveUpdateCounter = SYSTEM_SAVE_UPDATED; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/Sram.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/Sram.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,11 @@ +#ifndef VBA_SRAM_H +#define VBA_SRAM_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +extern u8 sramRead(u32 address); +extern void sramWrite(u32 address, u8 byte); + +#endif // VBA_SRAM_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/agbprint.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/agbprint.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,82 @@ +#include +#include + +#include "GBAGlobals.h" + +extern void (*dbgOutput)(char *, u32); +extern int systemVerbose; + +static bool agbPrintEnabled = false; +static bool agbPrintProtect = false; + +bool agbPrintWrite(u32 address, u16 value) +{ + if (agbPrintEnabled) + { + if (address == 0x9fe2ffe) // protect + { + agbPrintProtect = (value != 0); + debuggerWriteHalfWord(address, value); + return true; + } + else + { + if (agbPrintProtect && + ((address >= 0x9fe20f8 && address <= 0x9fe20ff) // control structure + || (address >= 0x8fd0000 && address <= 0x8fdffff) + || (address >= 0x9fd0000 && address <= 0x9fdffff))) + { + debuggerWriteHalfWord(address, value); + return true; + } + } + } + return false; +} + +void agbPrintReset() +{ + agbPrintProtect = false; +} + +void agbPrintEnable(bool enable) +{ + agbPrintEnabled = enable; +} + +bool agbPrintIsEnabled() +{ + return agbPrintEnabled; +} + +void agbPrintFlush() +{ + u16 get = debuggerReadHalfWord(0x9fe20fc); + u16 put = debuggerReadHalfWord(0x9fe20fe); + + u32 address = (debuggerReadHalfWord(0x9fe20fa) << 16); + if (address != 0xfd0000 && address != 0x1fd0000) + { + dbgOutput("Did you forget to call AGBPrintInit?\n", 0); + // get rid of the text otherwise we will continue to be called + debuggerWriteHalfWord(0x9fe20fc, put); + return; + } + + u8 *data = &rom[address]; + + while (get != put) + { + char c = data[get++]; + char s[2]; + s[0] = c; + s[1] = 0; + + if (systemVerbose & VERBOSE_AGBPRINT) + dbgOutput(s, 0); + if (c == '\n') + break; + } + debuggerWriteHalfWord(0x9fe20fc, get); +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/agbprint.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/agbprint.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,14 @@ +#ifndef VBA_AGBPRINT_H +#define VBA_AGBPRINT_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +extern void agbPrintEnable(bool); +extern bool agbPrintIsEnabled(); +extern void agbPrintReset(); +extern bool agbPrintWrite(u32, u16); +extern void agbPrintFlush(); + +#endif // VBA_AGBPRINT_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/arm-new.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/arm-new.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,7453 @@ +#ifdef BKPT_SUPPORT +#define CONSOLE_OUTPUT(a, b) \ + extern void (*dbgOutput)(char *, u32); \ + if ((opcode == 0xe0000000) && (reg[0].I == 0xC0DED00D)) { \ + dbgOutput((a), (b)); \ + } +#else +#define CONSOLE_OUTPUT(a, b) +#endif + +#define OP_AND \ + reg[dest].I = reg[(opcode >> 16) & 15].I & value; \ + CONSOLE_OUTPUT(NULL, reg[2].I); + +#define OP_ANDS \ + reg[dest].I = reg[(opcode >> 16) & 15].I & value; \ + \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_EOR \ + reg[dest].I = reg[(opcode >> 16) & 15].I ^ value; + +#define OP_EORS \ + reg[dest].I = reg[(opcode >> 16) & 15].I ^ value; \ + \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; +#ifdef C_CORE +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) +#define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) | \ + (NEG(a) & POS(c)) | \ + (NEG(b) & POS(c))) ? true : false; +#define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) | \ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) | \ + (NEG(a) & POS(c)) | \ + (POS(b) & POS(c))) ? true : false; +#define SUBOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) | \ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#define OP_SUB \ + { \ + reg[dest].I = reg[base].I - value; \ + } +#define OP_SUBS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define OP_RSB \ + { \ + reg[dest].I = value - reg[base].I; \ + } +#define OP_RSBS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(rhs, lhs, res); \ + SUBOVERFLOW(rhs, lhs, res); \ + } +#define OP_ADD \ + { \ + reg[dest].I = reg[base].I + value; \ + } +#define OP_ADDS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define OP_ADC \ + { \ + reg[dest].I = reg[base].I + value + (u32)C_FLAG; \ + } +#define OP_ADCS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define OP_SBC \ + { \ + reg[dest].I = reg[base].I - value - !((u32)C_FLAG); \ + } +#define OP_SBCS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define OP_RSC \ + { \ + reg[dest].I = value - reg[base].I - !((u32)C_FLAG); \ + } +#define OP_RSCS \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs - !((u32)C_FLAG); \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(rhs, lhs, res); \ + SUBOVERFLOW(rhs, lhs, res); \ + } +#define OP_CMP \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define OP_CMN \ + { \ + u32 lhs = reg[base].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } + +#define LOGICAL_LSL_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } +#define LOGICAL_LSR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } +#define LOGICAL_ASR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false; \ + value = (s32)v >> (int)shift; \ + } +#define LOGICAL_ROR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } +#define LOGICAL_RRX_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + shift = (int)C_FLAG; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (shift << 31)); \ + } +#define LOGICAL_ROR_IMM \ + { \ + u32 v = opcode & 0xff; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } +#define ARITHMETIC_LSL_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = v << shift; \ + } +#define ARITHMETIC_LSR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = v >> shift; \ + } +#define ARITHMETIC_ASR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = (s32)v >> (int)shift; \ + } +#define ARITHMETIC_ROR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } +#define ARITHMETIC_RRX_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + shift = (int)C_FLAG; \ + value = ((v >> 1) | \ + (shift << 31)); \ + } +#define ARITHMETIC_ROR_IMM \ + { \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } +#define ROR_IMM_MSR \ + { \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } +#define ROR_VALUE \ + { \ + value = ((value << (32 - shift)) | \ + (value >> shift)); \ + } +#define RCR_VALUE \ + { \ + shift = (int)C_FLAG; \ + value = ((value >> 1) | \ + (shift << 31)); \ + } +#else +#ifdef __GNUC__ + #ifdef __POWERPC__ + #define OP_SUB \ + { \ + reg[dest].I = reg[base].I - value; \ + } + #define OP_SUBS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSB \ + { \ + reg[dest].I = value - reg[base].I; \ + } + #define OP_RSBS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADD \ + { \ + reg[dest].I = reg[base].I + value; \ + } + + #define OP_ADDS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_ADC \ + { \ + reg[dest].I = reg[base].I + value + (u32)C_FLAG; \ + } + #define OP_ADCS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_SBC \ + { \ + reg[dest].I = reg[base].I - value - (C_FLAG ^ 1); \ + } + #define OP_SBCS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_RSC \ + { \ + reg[dest].I = value - reg[base].I - (C_FLAG ^ 1); \ + } + #define OP_RSCS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("mtspr xer, %4\n" \ + "subfeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMP \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define OP_CMN \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[base].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + + #define LOGICAL_LSL_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } + #define LOGICAL_LSR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } + #define LOGICAL_ASR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = ((s32)v >> (int)(shift - 1)) & 1 ? true : false; \ + value = (s32)v >> (int)shift; \ + } + #define LOGICAL_ROR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } + #define LOGICAL_RRX_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + shift = (int)C_FLAG; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (shift << 31)); \ + } + #define LOGICAL_ROR_IMM \ + { \ + u32 v = opcode & 0xff; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } + #define ARITHMETIC_LSL_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = v << shift; \ + } + #define ARITHMETIC_LSR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = v >> shift; \ + } + #define ARITHMETIC_ASR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = (s32)v >> (int)shift; \ + } + #define ARITHMETIC_ROR_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } + #define ARITHMETIC_RRX_REG \ + { \ + u32 v = reg[opcode & 0x0f].I; \ + shift = (int)C_FLAG; \ + value = ((v >> 1) | \ + (shift << 31)); \ + } + #define ARITHMETIC_ROR_IMM \ + { \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } + #define ROR_IMM_MSR \ + { \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } + #define ROR_VALUE \ + { \ + value = ((value << (32 - shift)) | \ + (value >> shift)); \ + } + #define RCR_VALUE \ + { \ + shift = (int)C_FLAG; \ + value = ((value >> 1) | \ + (shift << 31)); \ + } +#else +#define OP_SUB \ + asm ("sub %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_SUBS \ + asm ("sub %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_RSB \ + asm ("sub %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (reg[base].I), "b" (value)); + +#define OP_RSBS \ + asm ("sub %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (reg[base].I), "b" (value)); + +#define OP_ADD \ + asm ("add %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADDS \ + asm ("add %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADC \ + asm ("bt $0, C_FLAG;" \ + "adc %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_ADCS \ + asm ("bt $0, C_FLAG;" \ + "adc %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_SBC \ + asm ("bt $0, C_FLAG;" \ + "cmc;" \ + "sbb %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); + +#define OP_SBCS \ + asm ("bt $0, C_FLAG;" \ + "cmc;" \ + "sbb %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[base].I)); +#define OP_RSC \ + asm ("bt $0, C_FLAG;" \ + "cmc;" \ + "sbb %1, %%ebx;" \ + : "=b" (reg[dest].I) \ + : "r" (reg[base].I), "b" (value)); + +#define OP_RSCS \ + asm ("bt $0, C_FLAG;" \ + "cmc;" \ + "sbb %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (reg[base].I), "b" (value)); +#define OP_CMP \ + asm ("sub %0, %1;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : \ + : "r" (value), "r" (reg[base].I)); + +#define OP_CMN \ + asm ("add %0, %1;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : \ + : "r" (value), "r" (reg[base].I)); +#define LOGICAL_LSL_REG \ + asm ("shl %%cl, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_LSR_REG \ + asm ("shr %%cl, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_ASR_REG \ + asm ("sar %%cl, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_ROR_REG \ + asm ("ror %%cl, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define LOGICAL_RRX_REG \ + asm ("bt $0, C_FLAG;" \ + "rcr $1, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (reg[opcode & 0x0f].I)); + +#define LOGICAL_ROR_IMM \ + asm ("ror %%cl, %%eax;" \ + "setcb %%cl;" \ + : "=a" (value), "=c" (C_OUT) \ + : "a" (opcode & 0xff), "c" (shift)); +#define ARITHMETIC_LSL_REG \ + asm ("\ + shl %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_LSR_REG \ + asm ("\ + shr %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_ASR_REG \ + asm ("\ + sar %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_ROR_REG \ + asm ("\ + ror %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (reg[opcode & 0x0f].I), "c" (shift)); + +#define ARITHMETIC_RRX_REG \ + asm ("\ + bt $0, C_FLAG;\ + rcr $1, %%eax;" \ + : "=a" (value) \ + : "a" (reg[opcode & 0x0f].I)); + +#define ARITHMETIC_ROR_IMM \ + asm ("\ + ror %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (opcode & 0xff), "c" (shift)); +#define ROR_IMM_MSR \ + asm ("ror %%cl, %%eax;" \ + : "=a" (value) \ + : "a" (opcode & 0xFF), "c" (shift)); +#define ROR_VALUE \ + asm ("ror %%cl, %0" \ + : "=r" (value) \ + : "r" (value), "c" (shift)); +#define RCR_VALUE \ + asm ("bt $0, C_FLAG;" \ + "rcr $1, %0" \ + : "=r" (value) \ + : "r" (value)); +#endif +#else +#define OP_SUB \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm sub ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + } + +#define OP_SUBS \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm sub ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } + +#define OP_RSB \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm mov eax, value \ + __asm sub eax, ebx \ + __asm mov ebx, dest \ + __asm mov dword ptr [OFFSET reg + 4 * ebx], eax \ + } + +#define OP_RSBS \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm mov eax, value \ + __asm sub eax, ebx \ + __asm mov ebx, dest \ + __asm mov dword ptr [OFFSET reg + 4 * ebx], eax \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } + +#define OP_ADD \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm add ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + } + +#define OP_ADDS \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm add ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } + +#define OP_ADC \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm bt word ptr C_FLAG, 0 \ + __asm adc ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + } + +#define OP_ADCS \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm bt word ptr C_FLAG, 0 \ + __asm adc ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } + +#define OP_SBC \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm mov eax, value \ + __asm bt word ptr C_FLAG, 0 \ + __asm cmc \ + __asm sbb ebx, eax \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + } + +#define OP_SBCS \ + { \ + __asm mov ebx, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm mov eax, value \ + __asm bt word ptr C_FLAG, 0 \ + __asm cmc \ + __asm sbb ebx, eax \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define OP_RSC \ + { \ + __asm mov ebx, value \ + __asm mov eax, base \ + __asm mov eax, dword ptr[OFFSET reg + 4 * eax] \ + __asm bt word ptr C_FLAG, 0 \ + __asm cmc \ + __asm sbb ebx, eax \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + } + +#define OP_RSCS \ + { \ + __asm mov ebx, value \ + __asm mov eax, base \ + __asm mov eax, dword ptr[OFFSET reg + 4 * eax] \ + __asm bt word ptr C_FLAG, 0 \ + __asm cmc \ + __asm sbb ebx, eax \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define OP_CMP \ + { \ + __asm mov eax, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm sub ebx, value \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } + +#define OP_CMN \ + { \ + __asm mov eax, base \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm add ebx, value \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define LOGICAL_LSL_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shl eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT + +#define LOGICAL_LSR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shr eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT + +#define LOGICAL_ASR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm sar eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT + +#define LOGICAL_ROR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0F \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm ror eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT + +#define LOGICAL_RRX_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0F \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm bt word ptr C_OUT, 0 \ + __asm rcr eax, 1 \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT + +#define LOGICAL_ROR_IMM \ + __asm mov eax, opcode \ + __asm and eax, 0xff \ + __asm mov cl, byte ptr shift \ + __asm ror eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_OUT +#define ARITHMETIC_LSL_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shl eax, cl \ + __asm mov value, eax + +#define ARITHMETIC_LSR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shr eax, cl \ + __asm mov value, eax + +#define ARITHMETIC_ASR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0f \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm sar eax, cl \ + __asm mov value, eax + +#define ARITHMETIC_ROR_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0F \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm ror eax, cl \ + __asm mov value, eax + +#define ARITHMETIC_RRX_REG \ + __asm mov eax, opcode \ + __asm and eax, 0x0F \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm bt word ptr C_FLAG, 0 \ + __asm rcr eax, 1 \ + __asm mov value, eax + +#define ARITHMETIC_ROR_IMM \ + __asm mov eax, opcode \ + __asm and eax, 0xff \ + __asm mov cl, byte ptr shift \ + __asm ror eax, cl \ + __asm mov value, eax +#define ROR_IMM_MSR \ + { \ + __asm mov eax, opcode \ + __asm and eax, 0xff \ + __asm mov cl, byte ptr shift \ + __asm ror eax, CL \ + __asm mov value, eax \ + } +#define ROR_VALUE \ + { \ + __asm mov cl, byte ptr shift \ + __asm ror dword ptr value, cl \ + } +#define RCR_VALUE \ + { \ + __asm mov cl, byte ptr shift \ + __asm bt word ptr C_FLAG, 0 \ + __asm rcr dword ptr value, 1 \ + } +#endif +#endif + +#define OP_TST \ + u32 res = reg[base].I & value; \ + N_FLAG = (res & 0x80000000) ? true : false; \ + Z_FLAG = (res) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_TEQ \ + u32 res = reg[base].I ^ value; \ + N_FLAG = (res & 0x80000000) ? true : false; \ + Z_FLAG = (res) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_ORR \ + reg[dest].I = reg[base].I | value; + +#define OP_ORRS \ + reg[dest].I = reg[base].I | value; \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_MOV \ + reg[dest].I = value; + +#define OP_MOVS \ + reg[dest].I = value; \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_BIC \ + reg[dest].I = reg[base].I & (~value); + +#define OP_BICS \ + reg[dest].I = reg[base].I & (~value); \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; + +#define OP_MVN \ + reg[dest].I = ~value; + +#define OP_MVNS \ + reg[dest].I = ~value; \ + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; \ + Z_FLAG = (reg[dest].I) ? false : true; \ + C_FLAG = C_OUT; + +#define CASE_16(BASE) \ +case BASE: \ +case BASE + 1: \ +case BASE + 2: \ +case BASE + 3: \ +case BASE + 4: \ +case BASE + 5: \ +case BASE + 6: \ +case BASE + 7: \ +case BASE + 8: \ +case BASE + 9: \ +case BASE + 10: \ +case BASE + 11: \ +case BASE + 12: \ +case BASE + 13: \ +case BASE + 14: \ +case BASE + 15: + +#define CASE_256(BASE) \ + CASE_16(BASE) \ + CASE_16(BASE + 0x10) \ + CASE_16(BASE + 0x20) \ + CASE_16(BASE + 0x30) \ + CASE_16(BASE + 0x40) \ + CASE_16(BASE + 0x50) \ + CASE_16(BASE + 0x60) \ + CASE_16(BASE + 0x70) \ + CASE_16(BASE + 0x80) \ + CASE_16(BASE + 0x90) \ + CASE_16(BASE + 0xa0) \ + CASE_16(BASE + 0xb0) \ + CASE_16(BASE + 0xc0) \ + CASE_16(BASE + 0xd0) \ + CASE_16(BASE + 0xe0) \ + CASE_16(BASE + 0xf0) + +#define LOGICAL_DATA_OPCODE(OPCODE, OPCODE2, BASE) \ +case BASE: \ +case BASE + 8: \ +{ \ + /* OP Rd,Rb,Rm LSL # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + \ + if (shift) { \ + LOGICAL_LSL_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 2: \ +case BASE + 10: \ +{ \ + /* OP Rd,Rb,Rm LSR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_LSR_REG \ + } else { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false; \ + } \ + \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 4: \ +case BASE + 12: \ +{ \ + /* OP Rd,Rb,Rm ASR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ASR_REG \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } \ + \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 6: \ +case BASE + 14: \ +{ \ + /* OP Rd,Rb,Rm ROR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ROR_REG \ + } else { \ + LOGICAL_RRX_REG \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 1: \ +{ \ + /* OP Rd,Rb,Rm LSL Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false); \ + } else if (shift < 32) { \ + LOGICAL_LSL_REG \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 3: \ +{ \ + /* OP Rd,Rb,Rm LSR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false); \ + } else if (shift < 32) { \ + LOGICAL_LSR_REG \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 5: \ +{ \ + /* OP Rd,Rb,Rm ASR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift < 32) { \ + if (shift) { \ + LOGICAL_ASR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 7: \ +{ \ + /* OP Rd,Rb,Rm ROR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + shift &= 0x1f; \ + if (shift) { \ + LOGICAL_ROR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + C_OUT = (value & 0x80000000 ? true : false); \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + C_OUT = (value & 0x80000000 ? true : false); \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 0x200: \ +case BASE + 0x201: \ +case BASE + 0x202: \ +case BASE + 0x203: \ +case BASE + 0x204: \ +case BASE + 0x205: \ +case BASE + 0x206: \ +case BASE + 0x207: \ +case BASE + 0x208: \ +case BASE + 0x209: \ +case BASE + 0x20a: \ +case BASE + 0x20b: \ +case BASE + 0x20c: \ +case BASE + 0x20d: \ +case BASE + 0x20e: \ +case BASE + 0x20f: \ +{ \ + int shift = (opcode & 0xF00) >> 7; \ + int base = (opcode >> 16) & 0x0F; \ + int dest = (opcode >> 12) & 0x0F; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ROR_IMM \ + } else { \ + value = opcode & 0xff; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; + +#define LOGICAL_DATA_OPCODE_WITHOUT_base(OPCODE, OPCODE2, BASE) \ +case BASE: \ +case BASE + 8: \ +{ \ + /* OP Rd,Rb,Rm LSL # */ \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + \ + if (shift) { \ + LOGICAL_LSL_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 2: \ +case BASE + 10: \ +{ \ + /* OP Rd,Rb,Rm LSR # */ \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_LSR_REG \ + } else { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000) ? true : false; \ + } \ + \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 4: \ +case BASE + 12: \ +{ \ + /* OP Rd,Rb,Rm ASR # */ \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ASR_REG \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } \ + \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 6: \ +case BASE + 14: \ +{ \ + /* OP Rd,Rb,Rm ROR # */ \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ROR_REG \ + } else { \ + LOGICAL_RRX_REG \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 1: \ +{ \ + /* OP Rd,Rb,Rm LSL Rs */ \ + clockTicks++; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 1 ? true : false); \ + } else if (shift < 32) { \ + LOGICAL_LSL_REG \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 3: \ +{ \ + /* OP Rd,Rb,Rm LSR Rs */ \ + clockTicks++; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (reg[opcode & 0x0F].I & 0x80000000 ? true : false); \ + } else if (shift < 32) { \ + LOGICAL_LSR_REG \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 5: \ +{ \ + /* OP Rd,Rb,Rm ASR Rs */ \ + clockTicks++; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift < 32) { \ + if (shift) { \ + LOGICAL_ASR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 7: \ +{ \ + /* OP Rd,Rb,Rm ROR Rs */ \ + clockTicks++; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + shift &= 0x1f; \ + if (shift) { \ + LOGICAL_ROR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + C_OUT = (value & 0x80000000 ? true : false); \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + C_OUT = (value & 0x80000000 ? true : false); \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 0x200: \ +case BASE + 0x201: \ +case BASE + 0x202: \ +case BASE + 0x203: \ +case BASE + 0x204: \ +case BASE + 0x205: \ +case BASE + 0x206: \ +case BASE + 0x207: \ +case BASE + 0x208: \ +case BASE + 0x209: \ +case BASE + 0x20a: \ +case BASE + 0x20b: \ +case BASE + 0x20c: \ +case BASE + 0x20d: \ +case BASE + 0x20e: \ +case BASE + 0x20f: \ +{ \ + int shift = (opcode & 0xF00) >> 7; \ + int dest = (opcode >> 12) & 0x0F; \ + bool C_OUT = C_FLAG; \ + u32 value; \ + if (shift) { \ + LOGICAL_ROR_IMM \ + } else { \ + value = opcode & 0xff; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; + +#define ARITHMETIC_DATA_OPCODE(OPCODE, OPCODE2, BASE) \ +case BASE: \ +case BASE + 8: \ +{ \ + /* OP Rd,Rb,Rm LSL # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + ARITHMETIC_LSL_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 2: \ +case BASE + 10: \ +{ \ + /* OP Rd,Rb,Rm LSR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + ARITHMETIC_LSR_REG \ + } else { \ + value = 0; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 4: \ +case BASE + 12: \ +{ \ + /* OP Rd,Rb,Rm ASR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + ARITHMETIC_ASR_REG \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + } else value = 0; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 6: \ +case BASE + 14: \ +{ \ + /* OP Rd,Rb,Rm ROR # */ \ + int base = (opcode >> 16) & 0x0F; \ + int shift = (opcode >> 7) & 0x1F; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + ARITHMETIC_ROR_REG \ + } else { \ + ARITHMETIC_RRX_REG \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 1: \ +{ \ + /* OP Rd,Rb,Rm LSL Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + } else if (shift < 32) { \ + ARITHMETIC_LSL_REG \ + } else value = 0; \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 3: \ +{ \ + /* OP Rd,Rb,Rm LSR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + } else if (shift < 32) { \ + ARITHMETIC_LSR_REG \ + } else value = 0; \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 5: \ +{ \ + /* OP Rd,Rb,Rm ASR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift < 32) { \ + if (shift) { \ + ARITHMETIC_ASR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + } else value = 0; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 7: \ +{ \ + /* OP Rd,Rb,Rm ROR Rs */ \ + clockTicks++; \ + int base = (opcode >> 16) & 0x0F; \ + int shift = reg[(opcode >> 8) & 15].B.B0; \ + int dest = (opcode >> 12) & 15; \ + u32 value; \ + if (shift) { \ + shift &= 0x1f; \ + if (shift) { \ + ARITHMETIC_ROR_REG \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + } else { \ + value = reg[opcode & 0x0F].I; \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; \ +case BASE + 0x200: \ +case BASE + 0x201: \ +case BASE + 0x202: \ +case BASE + 0x203: \ +case BASE + 0x204: \ +case BASE + 0x205: \ +case BASE + 0x206: \ +case BASE + 0x207: \ +case BASE + 0x208: \ +case BASE + 0x209: \ +case BASE + 0x20a: \ +case BASE + 0x20b: \ +case BASE + 0x20c: \ +case BASE + 0x20d: \ +case BASE + 0x20e: \ +case BASE + 0x20f: \ +{ \ + int shift = (opcode & 0xF00) >> 7; \ + int base = (opcode >> 16) & 0x0F; \ + int dest = (opcode >> 12) & 0x0F; \ + u32 value; \ + { \ + ARITHMETIC_ROR_IMM \ + } \ + if (dest == 15) { \ + OPCODE2 \ + /* todo */ \ + if (opcode & 0x00100000) { \ + clockTicks++; \ + CPUSwitchMode(reg[17].I & 0x1f, false); \ + } \ + if (armState) { \ + reg[15].I &= 0xFFFFFFFC; \ + armNextPC = reg[15].I; \ + reg[15].I += 4; \ + } else { \ + reg[15].I &= 0xFFFFFFFE; \ + armNextPC = reg[15].I; \ + reg[15].I += 2; \ + } \ + } else { \ + OPCODE \ + } \ +} \ +break; + +u32 opcode = CPUReadMemoryQuick(armNextPC); + +clockTicks = memoryWaitFetch32[(armNextPC >> 24) & 15]; + +#ifndef FINAL_VERSION +if (armNextPC == stop) +{ + armNextPC++; +} +#endif + +armNextPC = reg[15].I; +reg[15].I += 4; +int cond = opcode >> 28; +// suggested optimization for frequent cases +bool cond_res; +if (cond == 0x0e) +{ + cond_res = true; +} +else +{ + switch (cond) + { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG && (N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } +} + +if (cond_res) +{ + switch (((opcode >> 16) & 0xFF0) | ((opcode >> 4) & 0x0F)) + { + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_AND, OP_AND, 0x000); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_ANDS, OP_AND, 0x010); + case 0x009: + { + // MUL Rd, Rm, Rs + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x019: + { + // MULS Rd, Rm, Rs + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs; + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; + Z_FLAG = (reg[dest].I) ? false : true; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x00b: + case 0x02b: + { + // STRH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address -= offset; + reg[base].I = address; + } + break; + case 0x04b: + case 0x06b: + { + // STRH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address -= offset; + reg[base].I = address; + } + break; + case 0x08b: + case 0x0ab: + { + // STRH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address += offset; + reg[base].I = address; + } + break; + case 0x0cb: + case 0x0eb: + { + // STRH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + address += offset; + reg[base].I = address; + } + break; + case 0x10b: + { + // STRH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x12b: + { + // STRH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x14b: + { + // STRH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x16b: + { + // STRH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x18b: + { + // STRH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x1ab: + { + // STRH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x1cb: + { + // STRH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + } + break; + case 0x1eb: + { + // STRH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 4 + CPUUpdateTicksAccess16(address); + CPUWriteHalfWord(address, reg[dest].W.W0); + reg[base].I = address; + } + break; + case 0x01b: + case 0x03b: + { + // LDRH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05b: + case 0x07b: + { + // LDRH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09b: + case 0x0bb: + { + // LDRH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0db: + case 0x0fb: + { + // LDRH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11b: + { + // LDRH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x13b: + { + // LDRH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x15b: + { + // LDRH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x17b: + { + // LDRH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x19b: + { + // LDRH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x1bb: + { + // LDRH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x1db: + { + // LDRH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + } + break; + case 0x1fb: + { + // LDRH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = CPUReadHalfWord(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x01d: + case 0x03d: + { + // LDRSB Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05d: + case 0x07d: + { + // LDRSB Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09d: + case 0x0bd: + { + // LDRSB Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0dd: + case 0x0fd: + { + // LDRSB Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11d: + { + // LDRSB Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x13d: + { + // LDRSB Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x15d: + { + // LDRSB Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x17d: + { + // LDRSB Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x19d: + { + // LDRSB Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x1bd: + { + // LDRSB Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x1dd: + { + // LDRSB Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + } + break; + case 0x1fd: + { + // LDRSB Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s8)CPUReadByte(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x01f: + case 0x03f: + { + // LDRSH Rd, [Rn], -Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x05f: + case 0x07f: + { + // LDRSH Rd, [Rn], #-offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + { + address -= offset; + reg[base].I = address; + } + } + break; + case 0x09f: + case 0x0bf: + { + // LDRSH Rd, [Rn], Rm + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x0df: + case 0x0ff: + { + // LDRSH Rd, [Rn], #offset + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I; + int offset = (opcode & 0x0F) | ((opcode >> 4) & 0xF0); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + { + address += offset; + reg[base].I = address; + } + } + break; + case 0x11f: + { + // LDRSH Rd, [Rn, -Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x13f: + { + // LDRSH Rd, [Rn, -Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x15f: + { + // LDRSH Rd, [Rn, -#offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x17f: + { + // LDRSH Rd, [Rn, -#offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I - ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x19f: + { + // LDRSH Rd, [Rn, Rm] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x1bf: + { + // LDRSH Rd, [Rn, Rm]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + reg[opcode & 0x0F].I; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + reg[base].I = address; + } + break; + case 0x1df: + { + // LDRSH Rd, [Rn, #offset] + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + } + break; + case 0x1ff: + { + // LDRSH Rd, [Rn, #offset]! + int base = (opcode >> 16) & 0x0F; + int dest = (opcode >> 12) & 0x0F; + u32 address = reg[base].I + ((opcode & 0x0F) | ((opcode >> 4) & 0xF0)); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + reg[dest].I = (s16)CPUReadHalfWordSigned(address); + if (dest != base) + reg[base].I = address; + } + break; + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EOR, OP_EOR, 0x020); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_EORS, OP_EOR, 0x030); + case 0x029: + { + // MLA Rd, Rm, Rs, Rn + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs + reg[(opcode >> 12) & 0x0f].I; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x039: + { + // MLAS Rd, Rm, Rs, Rn + int dest = (opcode >> 16) & 0x0F; + int mult = (opcode & 0x0F); + u32 rs = reg[(opcode >> 8) & 0x0F].I; + reg[dest].I = reg[mult].I * rs + reg[(opcode >> 12) & 0x0f].I; + N_FLAG = (reg[dest].I & 0x80000000) ? true : false; + Z_FLAG = (reg[dest].I) ? false : true; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + ARITHMETIC_DATA_OPCODE(OP_SUB, OP_SUB, 0x040); + ARITHMETIC_DATA_OPCODE(OP_SUBS, OP_SUB, 0x050); + ARITHMETIC_DATA_OPCODE(OP_RSB, OP_RSB, 0x060); + ARITHMETIC_DATA_OPCODE(OP_RSBS, OP_RSB, 0x070); + ARITHMETIC_DATA_OPCODE(OP_ADD, OP_ADD, 0x080); + ARITHMETIC_DATA_OPCODE(OP_ADDS, OP_ADD, 0x090); + case 0x089: + { + // UMULL RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = ((u64)umult) * ((u64)usource); + reg[destLo].I = (u32)(uTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(uTemp >> 32); + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((usource & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x099: + { + // UMULLS RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = ((u64)umult) * ((u64)usource); + reg[destLo].I = (u32)(uTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(uTemp >> 32); + Z_FLAG = (uTemp) ? false : true; + N_FLAG = (reg[destHi].I & 0x80000000) ? true : false; + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((usource & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + ARITHMETIC_DATA_OPCODE(OP_ADC, OP_ADC, 0x0a0); + ARITHMETIC_DATA_OPCODE(OP_ADCS, OP_ADC, 0x0b0); + case 0x0a9: + { + // UMLAL RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = (u64)reg[destHi].I; + uTemp <<= 32; + uTemp |= (u64)reg[destLo].I; + uTemp += ((u64)umult) * ((u64)usource); + reg[destLo].I = (u32)(uTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(uTemp >> 32); + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((usource & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x0b9: + { + // UMLALS RdLo, RdHi, Rn, Rs + u32 umult = reg[(opcode & 0x0F)].I; + u32 usource = reg[(opcode >> 8) & 0x0F].I; + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u64 uTemp = (u64)reg[destHi].I; + uTemp <<= 32; + uTemp |= (u64)reg[destLo].I; + uTemp += ((u64)umult) * ((u64)usource); + reg[destLo].I = (u32)(uTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(uTemp >> 32); + Z_FLAG = (uTemp) ? false : true; + N_FLAG = (reg[destHi].I & 0x80000000) ? true : false; + if ((usource & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((usource & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((usource & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + ARITHMETIC_DATA_OPCODE(OP_SBC, OP_SBC, 0x0c0); + ARITHMETIC_DATA_OPCODE(OP_SBCS, OP_SBC, 0x0d0); + case 0x0c9: + { + // SMULL RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = m * s; + reg[destLo].I = (u32)(sTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(sTemp >> 32); + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + case 0x0d9: + { + // SMULLS RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = m * s; + reg[destLo].I = (u32)(sTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(sTemp >> 32); + Z_FLAG = (sTemp) ? false : true; + N_FLAG = (sTemp < 0) ? true : false; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 2; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 3; + else if ((rs & 0xFF000000) == 0) + clockTicks += 4; + else + clockTicks += 5; + } + break; + ARITHMETIC_DATA_OPCODE(OP_RSC, OP_RSC, 0x0e0); + ARITHMETIC_DATA_OPCODE(OP_RSCS, OP_RSC, 0x0f0); + case 0x0e9: + { + // SMLAL RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = (u64)reg[destHi].I; + sTemp <<= 32; + sTemp |= (u64)reg[destLo].I; + sTemp += m * s; + reg[destLo].I = (u32)(sTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(sTemp >> 32); + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + case 0x0f9: + { + // SMLALS RdLo, RdHi, Rm, Rs + int destLo = (opcode >> 12) & 0x0F; + int destHi = (opcode >> 16) & 0x0F; + u32 rs = reg[(opcode >> 8) & 0x0F].I; + s64 m = (s32)reg[(opcode & 0x0F)].I; + s64 s = (s32)rs; + s64 sTemp = (u64)reg[destHi].I; + sTemp <<= 32; + sTemp |= (u64)reg[destLo].I; + sTemp += m * s; + reg[destLo].I = (u32)(sTemp & 0xFFFFFFFF); + reg[destHi].I = (u32)(sTemp >> 32); + Z_FLAG = (sTemp) ? false : true; + N_FLAG = (sTemp < 0) ? true : false; + if (((s32)rs) < 0) + rs = ~rs; + if ((rs & 0xFFFFFF00) == 0) + clockTicks += 3; + else if ((rs & 0xFFFF0000) == 0) + clockTicks += 4; + else if ((rs & 0xFF000000) == 0) + clockTicks += 5; + else + clockTicks += 6; + } + break; + LOGICAL_DATA_OPCODE(OP_TST, OP_TST, 0x110); + case 0x100: + // MRS Rd, CPSR + // TODO: check if right instruction.... + CPUUpdateCPSR(); + reg[(opcode >> 12) & 0x0F].I = reg[16].I; + break; + case 0x109: + { + // SWP Rd, Rm, [Rn] + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, reg[opcode & 15].I); + reg[(opcode >> 12) & 15].I = temp; + } + break; + LOGICAL_DATA_OPCODE(OP_TEQ, OP_TEQ, 0x130); + case 0x120: + { + // MSR CPSR_fields, Rm + CPUUpdateCPSR(); + u32 value = reg[opcode & 15].I; + u32 newValue = reg[16].I; + if (armMode > 0x10) + { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + CPUSwitchMode(newValue & 0x1f, false); + reg[16].I = newValue; + CPUUpdateFlags(); + } + break; + case 0x121: + { + // BX Rm + // TODO: check if right instruction... + clockTicks += 3; + int base = opcode & 0x0F; + armState = reg[base].I & 1 ? false : true; + if (armState) + { + reg[15].I = reg[base].I & 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + else + { + reg[15].I = reg[base].I & 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + } + } + break; + ARITHMETIC_DATA_OPCODE(OP_CMP, OP_CMP, 0x150); + case 0x140: + // MRS Rd, SPSR + // TODO: check if right instruction... + reg[(opcode >> 12) & 0x0F].I = reg[17].I; + break; + case 0x149: + { + // SWPB Rd, Rm, [Rn] + u32 address = reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, reg[opcode & 15].B.B0); + reg[(opcode >> 12) & 15].I = temp; + } + break; + ARITHMETIC_DATA_OPCODE(OP_CMN, OP_CMN, 0x170); + case 0x160: + { + // MSR SPSR_fields, Rm + u32 value = reg[opcode & 15].I; + if (armMode > 0x10 && armMode < 0x1f) + { + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + break; + LOGICAL_DATA_OPCODE(OP_ORR, OP_ORR, 0x180); + LOGICAL_DATA_OPCODE(OP_ORRS, OP_ORR, 0x190); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOV, OP_MOV, 0x1a0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MOVS, OP_MOV, 0x1b0); + LOGICAL_DATA_OPCODE(OP_BIC, OP_BIC, 0x1c0); + LOGICAL_DATA_OPCODE(OP_BICS, OP_BIC, 0x1d0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVN, OP_MVN, 0x1e0); + LOGICAL_DATA_OPCODE_WITHOUT_base(OP_MVNS, OP_MVN, 0x1f0); +#ifdef BKPT_SUPPORT + case 0x127: + case 0x7ff: // for GDB support + extern void (*dbgSignal)(int, int); + reg[15].I -= 4; + armNextPC -= 4; + dbgSignal(5, (opcode & 0x0f) | ((opcode >> 4) & 0xfff0)); + return; +#endif + case 0x320: + case 0x321: + case 0x322: + case 0x323: + case 0x324: + case 0x325: + case 0x326: + case 0x327: + case 0x328: + case 0x329: + case 0x32a: + case 0x32b: + case 0x32c: + case 0x32d: + case 0x32e: + case 0x32f: + { + // MSR CPSR_fields, # + CPUUpdateCPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) + { + ROR_IMM_MSR; + } + u32 newValue = reg[16].I; + if (armMode > 0x10) + { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + CPUSwitchMode(newValue & 0x1f, false); + reg[16].I = newValue; + CPUUpdateFlags(); + } + break; + case 0x360: + case 0x361: + case 0x362: + case 0x363: + case 0x364: + case 0x365: + case 0x366: + case 0x367: + case 0x368: + case 0x369: + case 0x36a: + case 0x36b: + case 0x36c: + case 0x36d: + case 0x36e: + case 0x36f: + { + // MSR SPSR_fields, # + if (armMode > 0x10 && armMode < 0x1f) + { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) + { + ROR_IMM_MSR; + } + if (opcode & 0x00010000) + reg[17].I = (reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + reg[17].I = (reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + reg[17].I = (reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + reg[17].I = (reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + break; + CASE_16(0x400) + // T versions shouldn't be different on GBA + CASE_16(0x420) + { + // STR Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x480) + // T versions shouldn't be different on GBA + CASE_16(0x4a0) + { + // STR Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x500) + { + // STR Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x520) + { + // STR Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x580) + { + // STR Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x5a0) + { + // STR Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x410) + { + // LDR Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x430) + { + // LDRT Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x490) + { + // LDR Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x4b0) + { + // LDRT Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + } + break; + CASE_16(0x510) + { + // LDR Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x530) + { + // LDR Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x590) + { + // LDR Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x5b0) + { + // LDR Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x440) + // T versions shouldn't be different on GBA + CASE_16(0x460) + { + // STRB Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x4c0) + // T versions shouldn't be different on GBA + CASE_16(0x4e0) + // STRB Rd, [Rn], # + { + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x540) + { + // STRB Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x560) + { + // STRB Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5c0) + { + // STRB Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5e0) + { + // STRB Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x450) + // T versions shouldn't be different + CASE_16(0x470) + { + // LDRB Rd, [Rn], -# + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I -= offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x4d0) + CASE_16(0x4f0) // T versions should not be different + { + // LDRB Rd, [Rn], # + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I += offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x550) + { + // LDRB Rd, [Rn, -#] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x570) + { + // LDRB Rd, [Rn, -#]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5d0) + { + // LDRB Rd, [Rn, #] + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + CASE_16(0x5f0) + { + // LDRB Rd, [Rn, #]! + int offset = opcode & 0xFFF; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x600: + case 0x608: + // T versions are the same + case 0x620: + case 0x628: + { + // STR Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x602: + case 0x60a: + // T versions are the same + case 0x622: + case 0x62a: + { + // STR Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x604: + case 0x60c: + // T versions are the same + case 0x624: + case 0x62c: + { + // STR Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x606: + case 0x60e: + // T versions are the same + case 0x626: + case 0x62e: + { + // STR Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address - value; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x680: + case 0x688: + // T versions are the same + case 0x6a0: + case 0x6a8: + { + // STR Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x682: + case 0x68a: + // T versions are the same + case 0x6a2: + case 0x6aa: + { + // STR Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x684: + case 0x68c: + // T versions are the same + case 0x6a4: + case 0x6ac: + { + // STR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x686: + case 0x68e: + // T versions are the same + case 0x6a6: + case 0x6ae: + { + // STR Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteMemory(address, reg[dest].I); + reg[base].I = address + value; + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x700: + case 0x708: + { + // STR Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x702: + case 0x70a: + { + // STR Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x704: + case 0x70c: + { + // STR Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x706: + case 0x70e: + { + // STR Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x720: + case 0x728: + { + // STR Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x722: + case 0x72a: + { + // STR Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x724: + case 0x72c: + { + // STR Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x726: + case 0x72e: + { + // STR Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x780: + case 0x788: + { + // STR Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x782: + case 0x78a: + { + // STR Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x784: + case 0x78c: + { + // STR Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x786: + case 0x78e: + { + // STR Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a0: + case 0x7a8: + { + // STR Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a2: + case 0x7aa: + { + // STR Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a4: + case 0x7ac: + { + // STR Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x7a6: + case 0x7ae: + { + // STR Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[base].I = address; + CPUWriteMemory(address, reg[dest].I); + clockTicks += 2 + CPUUpdateTicksAccess32(address); + } + break; + case 0x610: + case 0x618: + // T versions are the same + case 0x630: + case 0x638: + { + // LDR Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x612: + case 0x61a: + // T versions are the same + case 0x632: + case 0x63a: + { + // LDR Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x614: + case 0x61c: + // T versions are the same + case 0x634: + case 0x63c: + { + // LDR Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x616: + case 0x61e: + // T versions are the same + case 0x636: + case 0x63e: + { + // LDR Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address - value; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x690: + case 0x698: + // T versions are the same + case 0x6b0: + case 0x6b8: + { + // LDR Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x692: + case 0x69a: + // T versions are the same + case 0x6b2: + case 0x6ba: + { + // LDR Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x694: + case 0x69c: + // T versions are the same + case 0x6b4: + case 0x6bc: + { + // LDR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x696: + case 0x69e: + // T versions are the same + case 0x6b6: + case 0x6be: + { + // LDR Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address + value; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x710: + case 0x718: + { + // LDR Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x712: + case 0x71a: + { + // LDR Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x714: + case 0x71c: + { + // LDR Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x716: + case 0x71e: + { + // LDR Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x730: + case 0x738: + { + // LDR Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x732: + case 0x73a: + { + // LDR Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x734: + case 0x73c: + { + // LDR Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x736: + case 0x73e: + { + // LDR Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x790: + case 0x798: + { + // LDR Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x792: + case 0x79a: + { + // LDR Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x794: + case 0x79c: + { + // LDR Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x796: + case 0x79e: + { + // LDR Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadMemory(address); + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b0: + case 0x7b8: + { + // LDR Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b2: + case 0x7ba: + { + // LDR Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b4: + case 0x7bc: + { + // LDR Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x7b6: + case 0x7be: + { + // LDR Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadMemory(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess32(address); + if (dest == 15) + { + clockTicks += 2; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + case 0x640: + case 0x648: + // T versions are the same + case 0x660: + case 0x668: + { + // STRB Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x642: + case 0x64a: + // T versions are the same + case 0x662: + case 0x66a: + { + // STRB Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x644: + case 0x64c: + // T versions are the same + case 0x664: + case 0x66c: + { + // STRB Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x646: + case 0x64e: + // T versions are the same + case 0x666: + case 0x66e: + { + // STRB Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address - value; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c0: + case 0x6c8: + // T versions are the same + case 0x6e0: + case 0x6e8: + { + // STRB Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c2: + case 0x6ca: + // T versions are the same + case 0x6e2: + case 0x6ea: + { + // STRB Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c4: + case 0x6cc: + // T versions are the same + case 0x6e4: + case 0x6ec: + { + // STR Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + offset; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6c6: + case 0x6ce: + // T versions are the same + case 0x6e6: + case 0x6ee: + { + // STRB Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + CPUWriteByte(address, reg[dest].B.B0); + reg[base].I = address + value; + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x740: + case 0x748: + { + // STRB Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x742: + case 0x74a: + { + // STRB Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x744: + case 0x74c: + { + // STRB Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x746: + case 0x74e: + { + // STRB Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x760: + case 0x768: + { + // STRB Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x762: + case 0x76a: + { + // STRB Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x764: + case 0x76c: + { + // STRB Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x766: + case 0x76e: + { + // STRB Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c0: + case 0x7c8: + { + // STRB Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c2: + case 0x7ca: + { + // STRB Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c4: + case 0x7cc: + { + // STRB Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7c6: + case 0x7ce: + { + // STRB Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e0: + case 0x7e8: + { + // STRB Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e2: + case 0x7ea: + { + // STRB Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e4: + case 0x7ec: + { + // STRB Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7e6: + case 0x7ee: + { + // STRB Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[base].I = address; + CPUWriteByte(address, reg[dest].B.B0); + clockTicks += 2 + CPUUpdateTicksAccess16(address); + } + break; + case 0x650: + case 0x658: + // T versions are the same + case 0x670: + case 0x678: + { + // LDRB Rd, [Rn], -Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x652: + case 0x65a: + // T versions are the same + case 0x672: + case 0x67a: + { + // LDRB Rd, [Rn], -Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x654: + case 0x65c: + // T versions are the same + case 0x674: + case 0x67c: + { + // LDRB Rd, [Rn], -Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address - offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x656: + case 0x65e: + // T versions are the same + case 0x676: + case 0x67e: + { + // LDRB Rd, [Rn], -Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address - value; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d0: + case 0x6d8: + // T versions are the same + case 0x6f0: + case 0x6f8: + { + // LDRB Rd, [Rn], Rm, LSL # + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d2: + case 0x6da: + // T versions are the same + case 0x6f2: + case 0x6fa: + { + // LDRB Rd, [Rn], Rm, LSR # + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d4: + case 0x6dc: + // T versions are the same + case 0x6f4: + case 0x6fc: + { + // LDRB Rd, [Rn], Rm, ASR # + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address + offset; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x6d6: + case 0x6de: + // T versions are the same + case 0x6f6: + case 0x6fe: + { + // LDRB Rd, [Rn], Rm, ROR # + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address + value; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x750: + case 0x758: + { + // LDRB Rd, [Rn, -Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x752: + case 0x75a: + { + // LDRB Rd, [Rn, -Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x754: + case 0x75c: + { + // LDRB Rd, [Rn, -Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x756: + case 0x75e: + { + // LDRB Rd, [Rn, -Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x770: + case 0x778: + { + // LDRB Rd, [Rn, -Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x772: + case 0x77a: + { + // LDRB Rd, [Rn, -Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x774: + case 0x77c: + { + // LDRB Rd, [Rn, -Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x776: + case 0x77e: + { + // LDRB Rd, [Rn, -Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I - value; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d0: + case 0x7d8: + { + // LDRB Rd, [Rn, Rm, LSL #] + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d2: + case 0x7da: + { + // LDRB Rd, [Rn, Rm, LSR #] + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d4: + case 0x7dc: + { + // LDRB Rd, [Rn, Rm, ASR #] + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7d6: + case 0x7de: + { + // LDRB Rd, [Rn, Rm, ROR #] + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadByte(address); + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f0: + case 0x7f8: + { + // LDRB Rd, [Rn, Rm, LSL #]! + int offset = reg[opcode & 15].I << ((opcode >> 7) & 31); + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f2: + case 0x7fa: + { + // LDRB Rd, [Rn, Rm, LSR #]! + int shift = (opcode >> 7) & 31; + int offset = shift ? reg[opcode & 15].I >> shift : 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f4: + case 0x7fc: + { + // LDRB Rd, [Rn, Rm, ASR #]! + int shift = (opcode >> 7) & 31; + int offset; + if (shift) + offset = (int)((s32)reg[opcode & 15].I >> shift); + else if (reg[opcode & 15].I & 0x80000000) + offset = 0xFFFFFFFF; + else + offset = 0; + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + offset; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; + case 0x7f6: + case 0x7fe: + { + // LDRB Rd, [Rn, Rm, ROR #]! + int shift = (opcode >> 7) & 31; + u32 value = reg[opcode & 15].I; + if (shift) + { + ROR_VALUE; + } + else + { + RCR_VALUE; + } + int dest = (opcode >> 12) & 15; + int base = (opcode >> 16) & 15; + u32 address = reg[base].I + value; + reg[dest].I = CPUReadByte(address); + if (dest != base) + reg[base].I = address; + clockTicks += 3 + CPUUpdateTicksAccess16(address); + } + break; +#define STMW_REG(val, num) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!offset) { \ + reg[base].I = temp; \ + clockTicks += 1 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + } else { \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); \ + } \ + address += 4; \ + } +#define STM_REG(val, num) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(num)].I); \ + if (!offset) { \ + clockTicks += 1 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + } else { \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); \ + } \ + address += 4; \ + } + + CASE_16(0x800) + // STMDA Rn, {Rlist} + { + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x820) + { + // STMDA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + CASE_16(0x840) + { + // STMDA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + + if (armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x860) + { + // STMDA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + + if (armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + + CASE_16(0x880) + { + // STMIA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8a0) + { + // STMIA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8c0) + { + // STMIA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + if (armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + if (armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x8e0) + { + // STMIA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + if (armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + if (armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + + CASE_16(0x900) + { + // STMDB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x920) + { + // STMDB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + CASE_16(0x940) + { + // STMDB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + + if (armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x960) + { + // STMDB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + + if (armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + reg[base].I = temp; + } + } + break; + + CASE_16(0x980) + // STMIB Rn, {Rlist} + { + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + STM_REG(8192, 13); + STM_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9a0) + { + // STMIB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + STMW_REG(8192, 13); + STMW_REG(16384, 14); + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9c0) + { + // STMIB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + STM_REG(1, 0); + STM_REG(2, 1); + STM_REG(4, 2); + STM_REG(8, 3); + STM_REG(16, 4); + STM_REG(32, 5); + STM_REG(64, 6); + STM_REG(128, 7); + if (armMode == 0x11) + { + STM_REG(256, R8_FIQ); + STM_REG(512, R9_FIQ); + STM_REG(1024, R10_FIQ); + STM_REG(2048, R11_FIQ); + STM_REG(4096, R12_FIQ); + } + else + { + STM_REG(256, 8); + STM_REG(512, 9); + STM_REG(1024, 10); + STM_REG(2048, 11); + STM_REG(4096, 12); + } + if (armMode != 0x10 && armMode != 0x1f) + { + STM_REG(8192, R13_USR); + STM_REG(16384, R14_USR); + } + else + { + STM_REG(8192, 13); + STM_REG(16384, 14); + } + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + clockTicks += 1 + CPUUpdateTicksAccess32(address); + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + CASE_16(0x9e0) + { + // STMIB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + u32 temp = reg[base].I + 4 * (cpuBitsSet[opcode & 0xFF] + + cpuBitsSet[(opcode >> 8) & 255]); + STMW_REG(1, 0); + STMW_REG(2, 1); + STMW_REG(4, 2); + STMW_REG(8, 3); + STMW_REG(16, 4); + STMW_REG(32, 5); + STMW_REG(64, 6); + STMW_REG(128, 7); + if (armMode == 0x11) + { + STMW_REG(256, R8_FIQ); + STMW_REG(512, R9_FIQ); + STMW_REG(1024, R10_FIQ); + STMW_REG(2048, R11_FIQ); + STMW_REG(4096, R12_FIQ); + } + else + { + STMW_REG(256, 8); + STMW_REG(512, 9); + STMW_REG(1024, 10); + STMW_REG(2048, 11); + STMW_REG(4096, 12); + } + if (armMode != 0x10 && armMode != 0x1f) + { + STMW_REG(8192, R13_USR); + STMW_REG(16384, R14_USR); + } + else + { + STMW_REG(8192, 13); + STMW_REG(16384, 14); + } + if (opcode & 32768) + { + CPUWriteMemory(address, reg[15].I + 4); + if (!offset) + { + reg[base].I = temp; + clockTicks += 1 + CPUUpdateTicksAccess32(address); + } + else + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); + } + } + break; + +#define LDM_REG(val, num) \ + if (opcode & (val)) { \ + reg[(num)].I = CPUReadMemory(address); \ + if (offset) \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); \ + else { \ + clockTicks += 1 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + } \ + address += 4; \ + } + + CASE_16(0x810) + { + // LDMDA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x830) + { + // LDMDA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x850) + { + // LDMDA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x870) + { + // LDMDA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if (!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x890) + { + // LDMIA Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x8b0) + { + // LDMIA Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x8d0) + { + // LDMIA Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x8f0) + { + // LDMIA Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = reg[base].I & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if (!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x910) + { + // LDMDB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x930) + { + // LDMDB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x950) + { + // LDMDB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x970) + { + // LDMDB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if (!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + + CASE_16(0x990) + { + // LDMIB Rn, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + } + break; + CASE_16(0x9b0) + { + // LDMIB Rn!, {Rlist} + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + if (opcode & 32768) + { + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + armNextPC = reg[15].I; + reg[15].I += 4; + } + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + break; + CASE_16(0x9d0) + { + // LDMIB Rn, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + } + } + break; + CASE_16(0x9f0) + { + // LDMIB Rn!, {Rlist}^ + int base = (opcode & 0x000F0000) >> 16; + u32 temp = reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (reg[base].I + 4) & 0xFFFFFFFC; + clockTicks += 2; + int offset = 0; + if (opcode & 0x8000) + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + LDM_REG(8192, 13); + LDM_REG(16384, 14); + + reg[15].I = CPUReadMemory(address); + if (!offset) + clockTicks += 2 + CPUUpdateTicksAccess32(address); + else + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); + + if (!(opcode & (1 << base))) + reg[base].I = temp; + + CPUSwitchMode(reg[17].I & 0x1f, false); + if (armState) + { + armNextPC = reg[15].I & 0xFFFFFFFC; + reg[15].I = armNextPC + 4; + } + else + { + armNextPC = reg[15].I & 0xFFFFFFFE; + reg[15].I = armNextPC + 2; + } + } + else + { + LDM_REG(1, 0); + LDM_REG(2, 1); + LDM_REG(4, 2); + LDM_REG(8, 3); + LDM_REG(16, 4); + LDM_REG(32, 5); + LDM_REG(64, 6); + LDM_REG(128, 7); + + if (armMode == 0x11) + { + LDM_REG(256, R8_FIQ); + LDM_REG(512, R9_FIQ); + LDM_REG(1024, R10_FIQ); + LDM_REG(2048, R11_FIQ); + LDM_REG(4096, R12_FIQ); + } + else + { + LDM_REG(256, 8); + LDM_REG(512, 9); + LDM_REG(1024, 10); + LDM_REG(2048, 11); + LDM_REG(4096, 12); + } + + if (armMode != 0x10 && armMode != 0x1f) + { + LDM_REG(8192, R13_USR); + LDM_REG(16384, R14_USR); + } + else + { + LDM_REG(8192, 13); + LDM_REG(16384, 14); + } + + if (!(opcode & (1 << base))) + reg[base].I = temp; + } + } + break; + CASE_256(0xa00) + { + // B + clockTicks += 3; + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + { + offset |= 0xFF000000; + } + offset <<= 2; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + CASE_256(0xb00) + { + // BL + clockTicks += 3; + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + { + offset |= 0xFF000000; + } + offset <<= 2; + reg[14].I = reg[15].I - 4; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + CASE_256(0xf00) + // SWI + clockTicks += 3; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); + break; +#ifdef GP_SUPPORT + case 0xe11: + case 0xe13: + case 0xe15: + case 0xe17: + case 0xe19: + case 0xe1b: + case 0xe1d: + case 0xe1f: + // MRC + break; + case 0xe01: + case 0xe03: + case 0xe05: + case 0xe07: + case 0xe09: + case 0xe0b: + case 0xe0d: + case 0xe0f: + // MRC + break; +#endif + default: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_UNDEFINED) + log("Undefined ARM instruction %08x at %08x\n", opcode, + armNextPC - 4); +#endif + CPUUndefinedException(); + break; + // END + } +} diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/armdis.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/armdis.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,774 @@ +/************************************************************************/ +/* Arm/Thumb command set disassembler */ +/************************************************************************/ +#include + +#include "GBAGlobals.h" +#include "armdis.h" +#include "elf.h" + +struct Opcodes +{ + u32 mask; + u32 cval; + const char *mnemonic; +}; + +const char hdig[] = "0123456789abcdef"; + +const char *decVals[16] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15" +}; + +const char *regs[16] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" +}; + +const char *conditions[16] = { + "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", "gt", "le", "", "nv" +}; + +const char *shifts[5] = { + "lsl", "lsr", "asr", "ror", "rrx" +}; + +const char *armMultLoadStore[12] = { + // non-stack + "da", "ia", "db", "ib", + // stack store + "ed", "ea", "fd", "fa", + // stack load + "fa", "fd", "ea", "ed" +}; + +const Opcodes thumbOpcodes[] = { + // Format 1 + { 0xf800, 0x0000, "lsl %r0, %r3, %o" }, + { 0xf800, 0x0800, "lsr %r0, %r3, %o" }, + { 0xf800, 0x1000, "asr %r0, %r3, %o" }, + // Format 2 + { 0xfe00, 0x1800, "add %r0, %r3, %r6" }, + { 0xfe00, 0x1a00, "sub %r0, %r3, %r6" }, + { 0xfe00, 0x1c00, "add %r0, %r3, %i" }, + { 0xfe00, 0x1e00, "sub %r0, %r3, %i" }, + // Format 3 + { 0xf800, 0x2000, "mov %r8, %O" }, + { 0xf800, 0x2800, "cmp %r8, %O" }, + { 0xf800, 0x3000, "add %r8, %O" }, + { 0xf800, 0x3800, "sub %r8, %O" }, + // Format 4 + { 0xffc0, 0x4000, "and %r0, %r3" }, + { 0xffc0, 0x4040, "eor %r0, %r3" }, + { 0xffc0, 0x4080, "lsl %r0, %r3" }, + { 0xffc0, 0x40c0, "lsr %r0, %r3" }, + { 0xffc0, 0x4100, "asr %r0, %r3" }, + { 0xffc0, 0x4140, "adc %r0, %r3" }, + { 0xffc0, 0x4180, "sbc %r0, %r3" }, + { 0xffc0, 0x41c0, "ror %r0, %r3" }, + { 0xffc0, 0x4200, "tst %r0, %r3" }, + { 0xffc0, 0x4240, "neg %r0, %r3" }, + { 0xffc0, 0x4280, "cmp %r0, %r3" }, + { 0xffc0, 0x42c0, "cmn %r0, %r3" }, + { 0xffc0, 0x4300, "orr %r0, %r3" }, + { 0xffc0, 0x4340, "mul %r0, %r3" }, + { 0xffc0, 0x4380, "bic %r0, %r3" }, + { 0xffc0, 0x43c0, "mvn %r0, %r3" }, + // Format 5 + { 0xff80, 0x4700, "bx %h36" }, + { 0xfcc0, 0x4400, "[ ??? ]" }, + { 0xff00, 0x4400, "add %h07, %h36" }, + { 0xff00, 0x4500, "cmp %h07, %h36" }, + { 0xff00, 0x4600, "mov %h07, %h36" }, + // Format 6 + { 0xf800, 0x4800, "ldr %r8, [%I] (=%J)" }, + // Format 7 + { 0xfa00, 0x5000, "str%b %r0, [%r3, %r6]" }, + { 0xfa00, 0x5800, "ldr%b %r0, [%r3, %r6]" }, + // Format 8 + { 0xfe00, 0x5200, "strh %r0, [%r3, %r6]" }, + { 0xfe00, 0x5600, "ldsb %r0, [%r3, %r6]" }, + { 0xfe00, 0x5a00, "ldrh %r0, [%r3, %r6]" }, + { 0xfe00, 0x5e00, "ldsh %r0, [%r3, %r6]" }, + // Format 9 + { 0xe800, 0x6000, "str%B %r0, [%r3, %p]" }, + { 0xe800, 0x6800, "ldr%B %r0, [%r3, %p]" }, + // Format 10 + { 0xf800, 0x8000, "strh %r0, [%r3, %e]" }, + { 0xf800, 0x8800, "ldrh %r0, [%r3, %e]" }, + // Format 11 + { 0xf800, 0x9000, "str %r8, [sp, %w]" }, + { 0xf800, 0x9800, "ldr %r8, [sp, %w]" }, + // Format 12 + { 0xf800, 0xa000, "add %r8, pc, %w (=%K)" }, + { 0xf800, 0xa800, "add %r8, sp, %w" }, + // Format 13 + { 0xff00, 0xb000, "add sp, %s" }, + // Format 14 + { 0xffff, 0xb500, "push {lr}" }, + { 0xff00, 0xb400, "push {%l}" }, + { 0xff00, 0xb500, "push {%l,lr}" }, + { 0xffff, 0xbd00, "pop {pc}" }, + { 0xff00, 0xbd00, "pop {%l,pc}" }, + { 0xff00, 0xbc00, "pop {%l}" }, + // Format 15 + { 0xf800, 0xc000, "stmia %r8!, {%l}" }, + { 0xf800, 0xc800, "ldmia %r8!, {%l}" }, + // Format 17 + { 0xff00, 0xdf00, "swi %m" }, + // Format 16 + { 0xf000, 0xd000, "b%c %W" }, + // Format 18 + { 0xf800, 0xe000, "b %a" }, + // Format 19 + { 0xf800, 0xf000, "bl %A" }, + { 0xf800, 0xf800, "blh %Z" }, + { 0xff00, 0xbe00, "bkpt %O" }, + // Unknown + { 0x0000, 0x0000, "[ ??? ]" } +}; + +const Opcodes armOpcodes[] = { + // Undefined + { 0x0e000010, 0x06000010, "[ undefined ]" }, + // Branch instructions + { 0x0ff000f0, 0x01200010, "bx%c %r0" }, + { 0x0f000000, 0x0a000000, "b%c %o" }, + { 0x0f000000, 0x0b000000, "bl%c %o" }, + { 0x0f000000, 0x0f000000, "swi%c %q" }, + // PSR transfer + { 0x0fbf0fff, 0x010f0000, "mrs%c %r3, %p" }, + { 0x0db0f000, 0x0120f000, "msr%c %p, %i" }, + // Multiply instructions + { 0x0fe000f0, 0x00000090, "mul%c%s %r4, %r0, %r2" }, + { 0x0fe000f0, 0x00200090, "mla%c%s %r4, %r0, %r2, %r3" }, + { 0x0fa000f0, 0x00800090, "%umull%c%s %r3, %r4, %r0, %r2" }, + { 0x0fa000f0, 0x00a00090, "%umlal%c%s %r3, %r4, %r0, %r2" }, + // Load/Store instructions + { 0x0fb00ff0, 0x01000090, "swp%c%b %r3, %r0, [%r4]" }, + { 0x0fb000f0, 0x01000090, "[ ??? ]" }, + { 0x0c100000, 0x04000000, "str%c%b%t %r3, %a" }, + { 0x0c100000, 0x04100000, "ldr%c%b%t %r3, %a" }, + { 0x0e100090, 0x00000090, "str%c%h %r3, %a" }, + { 0x0e100090, 0x00100090, "ldr%c%h %r3, %a" }, + { 0x0e100000, 0x08000000, "stm%c%m %r4%l" }, + { 0x0e100000, 0x08100000, "ldm%c%m %r4%l" }, + // Data processing + { 0x0de00000, 0x00000000, "and%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00200000, "eor%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00400000, "sub%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00600000, "rsb%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00800000, "add%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00a00000, "adc%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00c00000, "sbc%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x00e00000, "rsc%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x01000000, "tst%c%s %r4, %i" }, + { 0x0de00000, 0x01200000, "teq%c%s %r4, %i" }, + { 0x0de00000, 0x01400000, "cmp%c%s %r4, %i" }, + { 0x0de00000, 0x01600000, "cmn%c%s %r4, %i" }, + { 0x0de00000, 0x01800000, "orr%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x01a00000, "mov%c%s %r3, %i" }, + { 0x0de00000, 0x01c00000, "bic%c%s %r3, %r4, %i" }, + { 0x0de00000, 0x01e00000, "mvn%c%s %r3, %i" }, + // Coprocessor operations + { 0x0f000010, 0x0e000000, "cdp%c %P, %N, %r3, %R4, %R0%V" }, + { 0x0e100000, 0x0c000000, "stc%c%L %P, %r3, %A" }, + { 0x0f100010, 0x0e000010, "mcr%c %P, %N, %r3, %R4, %R0%V" }, + { 0x0f100010, 0x0e100010, "mrc%c %P, %N, %r3, %R4, %R0%V" }, + // Unknown + { 0x00000000, 0x00000000, "[ ??? ]" } +}; + +char *addStr(char *dest, const char *src) +{ + while (*src) + { + *dest++ = *src++; + } + return dest; +} + +char *addHex(char *dest, int siz, u32 val) +{ + if (siz == 0) + { + siz = 28; + while ((((val >> siz) & 15) == 0) && (siz >= 4)) + siz -= 4; + siz += 4; + } + while (siz > 0) + { + siz -= 4; + *dest++ = hdig[(val >> siz) & 15]; + } + return dest; +} + +int disArm(u32 offset, char *dest, int flags) +{ + u32 opcode = debuggerReadMemory(offset); + + const Opcodes *sp = armOpcodes; + while (sp->cval != (opcode & sp->mask)) + sp++; + + if (flags & DIS_VIEW_ADDRESS) + { + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags & DIS_VIEW_CODE) + { + dest = addHex(dest, 32, opcode); + *dest++ = ' '; + } + + const char *src = sp->mnemonic; + while (*src) + { + if (*src != '%') + *dest++ = *src++; + else + { + src++; + switch (*src) + { + case 'c': + dest = addStr(dest, conditions[opcode >> 28]); + break; + case 'r': + dest = addStr(dest, regs[(opcode >> ((*(++src) - '0') * 4)) & 15]); + break; + case 'o': + { + *dest++ = '$'; + int off = opcode & 0xffffff; + if (off & 0x800000) + off |= 0xff000000; + off <<= 2; + dest = addHex(dest, 32, offset + 8 + off); + } + break; + case 'i': + if (opcode & (1 << 25)) + { + dest = addStr(dest, "#0x"); + int imm = opcode & 0xff; + int rot = (opcode & 0xf00) >> 7; + int val = (imm << (32 - rot)) | (imm >> rot); + dest = addHex(dest, 0, val); + } + else + { + dest = addStr(dest, regs[opcode & 0x0f]); + int shi = (opcode >> 5) & 3; + int sdw = (opcode >> 7) & 0x1f; + if ((sdw == 0) && (shi == 3)) + shi = 4; + if ((sdw) || (opcode & 0x10) || (shi)) + { + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode & 0x10) + { + *dest++ = ' '; + dest = addStr(dest, regs[(opcode >> 8) & 15]); + } + else + { + if (sdw == 0 && ((shi == 1) || (shi == 2))) + sdw = 32; + if (shi != 4) + { + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } + break; + case 'p': + if (opcode & (1 << 22)) + dest = addStr(dest, "spsr"); + else + dest = addStr(dest, "cpsr"); + if (opcode & 0x00F00000) + { + *dest++ = '_'; + if (opcode & 0x00080000) + *dest++ = 'f'; + if (opcode & 0x00040000) + *dest++ = 's'; + if (opcode & 0x00020000) + *dest++ = 'x'; + if (opcode & 0x00010000) + *dest++ = 'c'; + } + break; + case 's': + if (opcode & (1 << 20)) + *dest++ = 's'; + break; + case 'S': + if (opcode & (1 << 22)) + *dest++ = 's'; + break; + case 'u': + if (opcode & (1 << 22)) + *dest++ = 's'; + else + *dest++ = 'u'; + break; + case 'b': + if (opcode & (1 << 22)) + *dest++ = 'b'; + break; + case 'a': + if ((opcode & 0x076f0000) == 0x004f0000) + { + *dest++ = '['; + *dest++ = '$'; + int adr = offset + 8; + int add = (opcode & 15) | ((opcode >> 8) & 0xf0); + if (opcode & (1 << 23)) + adr += add; + else + adr -= add; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest, 32, debuggerReadMemory(adr)); + *dest++ = ')'; + } + if ((opcode & 0x072f0000) == 0x050f0000) + { + *dest++ = '['; + *dest++ = '$'; + int adr = offset + 8; + if (opcode & (1 << 23)) + adr += opcode & 0xfff; + else + adr -= opcode & 0xfff; + dest = addHex(dest, 32, adr); + *dest++ = ']'; + dest = addStr(dest, " (="); + *dest++ = '$'; + dest = addHex(dest, 32, debuggerReadMemory(adr)); + *dest++ = ')'; + } + else + { + int reg = (opcode >> 16) & 15; + *dest++ = '['; + dest = addStr(dest, regs[reg]); + if (!(opcode & (1 << 24))) + *dest++ = ']'; + if (((opcode & (1 << 25)) && (opcode & (1 << 26))) || (!(opcode & (1 << 22)) && !(opcode & (1 << 26)))) + { + dest = addStr(dest, ", "); + if (!(opcode & (1 << 23))) + *dest++ = '-'; + dest = addStr(dest, regs[opcode & 0x0f]); + int shi = (opcode >> 5) & 3; + if (opcode & (1 << 26)) + { + if (((opcode >> 7) & 0x1f) || (opcode & 0x10) || (shi == 1) || (shi == 2)) + { + dest = addStr(dest, ", "); + dest = addStr(dest, shifts[shi]); + if (opcode & 0x10) + { + *dest++ = ' '; + dest = addStr(dest, regs[(opcode >> 8) & 15]); + } + else + { + int sdw = (opcode >> 7) & 0x1f; + if (sdw == 0 && ((shi == 1) || (shi == 2))) + sdw = 32; + dest = addStr(dest, " #0x"); + dest = addHex(dest, 8, sdw); + } + } + } + } + else + { + int off; + if (opcode & (1 << 26)) + off = opcode & 0xfff; + else + off = (opcode & 15) | ((opcode >> 4) & 0xf0); + if (off) + { + dest = addStr(dest, ", "); + if (!(opcode & (1 << 23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + } + if (opcode & (1 << 24)) + { + *dest++ = ']'; + if (opcode & (1 << 21)) + *dest++ = '!'; + } + } + break; + case 't': + if ((opcode & 0x01200000) == 0x01200000) + *dest++ = 't'; + break; + case 'h': + if (opcode & (1 << 6)) + *dest++ = 's'; + if (opcode & (1 << 5)) + *dest++ = 'h'; + else + *dest++ = 'b'; + break; + case 'm': + if (((opcode >> 16) & 15) == 13) + { + if (opcode & 0x00100000) + dest = addStr(dest, armMultLoadStore[8 + ((opcode >> 23) & 3)]); + else + dest = addStr(dest, armMultLoadStore[4 + ((opcode >> 23) & 3)]); + } + else + dest = addStr(dest, armMultLoadStore[(opcode >> 23) & 3]); + break; + case 'l': + if (opcode & (1 << 21)) + *dest++ = '!'; + dest = addStr(dest, ", {"); + { + int rlst = opcode & 0xffff; + int msk = 0; + int not_first = 0; + while (msk < 16) + { + if (rlst & (1 << msk)) + { + int fr = msk; + while (rlst & (1 << msk)) + msk++; + int to = msk - 1; + if (not_first) + //dest = addStr(dest, ", "); + *dest++ = ','; + dest = addStr(dest, regs[fr]); + if (fr != to) + { + if (fr == to - 1) + //dest = addStr(", "); + *dest++ = ','; + else + *dest++ = '-'; + dest = addStr(dest, regs[to]); + } + not_first = 1; + } + else + msk++; + } + *dest++ = '}'; + if (opcode & (1 << 22)) + *dest++ = '^'; + } + break; + case 'q': + *dest++ = '$'; + dest = addHex(dest, 24, opcode & 0xffffff); + break; + case 'P': + *dest++ = 'p'; + dest = addStr(dest, decVals[(opcode >> 8) & 15]); + break; + case 'N': + if (opcode & 0x10) + dest = addStr(dest, decVals[(opcode >> 21) & 7]); + else + dest = addStr(dest, decVals[(opcode >> 20) & 15]); + break; + case 'R': + { + src++; + int reg = 4 * (*src - '0'); + *dest++ = 'c'; + dest = addStr(dest, decVals[(opcode >> reg) & 15]); + } + break; + case 'V': + { + int val = (opcode >> 5) & 7; + if (val) + { + dest = addStr(dest, ", "); + dest = addStr(dest, decVals[val]); + } + } + break; + case 'L': + if (opcode & (1 << 22)) + *dest++ = 'l'; + break; + case 'A': + if ((opcode & 0x012f0000) == 0x010f0000) + { + int adr = offset + 8; + int add = (opcode & 0xff) << 2; + if (opcode & (1 << 23)) + adr += add; + else + adr -= add; + *dest++ = '$'; + addHex(dest, 32, adr); + } + else + { + *dest++ = '['; + dest = addStr(dest, regs[(opcode >> 16) & 15]); + if (!(opcode & (1 << 24))) + *dest++ = ']'; + int off = (opcode & 0xff) << 2; + if (off) + { + dest = addStr(dest, ", "); + if (!(opcode & (1 << 23))) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, off); + } + if (opcode & (1 << 24)) + { + *dest++ = ']'; + if (opcode & (1 << 21)) + *dest++ = '!'; + } + } + break; + } + src++; + } + } + *dest++ = 0; + + return 4; +} + +int disThumb(u32 offset, char *dest, int flags) +{ + u32 opcode = debuggerReadHalfWord(offset); + + const Opcodes *sp = thumbOpcodes; + int ret = 2; + while (sp->cval != (opcode & sp->mask)) + sp++; + + if (flags & DIS_VIEW_ADDRESS) + { + dest = addHex(dest, 32, offset); + *dest++ = ' '; + } + if (flags & DIS_VIEW_CODE) + { + dest = addHex(dest, 16, opcode); + *dest++ = ' '; + } + + const char *src = sp->mnemonic; + while (*src) + { + if (*src != '%') + *dest++ = *src++; + else + { + src++; + switch (*src) + { + case 'r': + src++; + dest = addStr(dest, regs[(opcode >> (*src - '0')) & 7]); + break; + case 'o': + dest = addStr(dest, "#0x"); + { + int val = (opcode >> 6) & 0x1f; + dest = addHex(dest, 8, val); + } + break; + case 'p': + dest = addStr(dest, "#0x"); + { + int val = (opcode >> 6) & 0x1f; + if (!(opcode & (1 << 12))) + val <<= 2; + dest = addHex(dest, 0, val); + } + break; + case 'e': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, ((opcode >> 6) & 0x1f) << 1); + break; + case 'i': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode >> 6) & 7); + break; + case 'h': + { + src++; + int reg = (opcode >> (*src - '0')) & 7; + src++; + if (opcode & (1 << (*src - '0'))) + reg += 8; + dest = addStr(dest, regs[reg]); + } + break; + case 'O': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode & 0xff)); + break; + case 'I': + *dest++ = '$'; + dest = addHex(dest, 32, (offset & 0xfffffffc) + 4 + ((opcode & 0xff) << 2)); + break; + case 'J': + { + u32 value = debuggerReadMemory((offset & 0xfffffffc) + 4 + + ((opcode & 0xff) << 2)); + *dest++ = '$'; + dest = addHex(dest, 32, value); + const char *s = elfGetAddressSymbol(value); + if (*s) + { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'K': + { + u32 value = (offset & 0xfffffffc) + 4 + ((opcode & 0xff) << 2); + *dest++ = '$'; + dest = addHex(dest, 32, value); + const char *s = elfGetAddressSymbol(value); + if (*s) + { + *dest++ = ' '; + dest = addStr(dest, s); + } + } + break; + case 'b': + if (opcode & (1 << 10)) + *dest++ = 'b'; + break; + case 'B': + if (opcode & (1 << 12)) + *dest++ = 'b'; + break; + case 'w': + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode & 0xff) << 2); + break; + case 'W': + *dest++ = '$'; + { + int add = opcode & 0xff; + if (add & 0x80) + add |= 0xffffff00; + dest = addHex(dest, 32, (offset & 0xfffffffe) + 4 + (add << 1)); + } + break; + case 'c': + dest = addStr(dest, conditions[(opcode >> 8) & 15]); + break; + case 's': + if (opcode & (1 << 7)) + *dest++ = '-'; + dest = addStr(dest, "#0x"); + dest = addHex(dest, 0, (opcode & 0x7f) << 2); + break; + case 'l': + { + int rlst = opcode & 0xff; + int msk = 0; + int not_first = 0; + while (msk < 8) + { + if (rlst & (1 << msk)) + { + int fr = msk; + while (rlst & (1 << msk)) + msk++; + int to = msk - 1; + if (not_first) + *dest++ = ','; + dest = addStr(dest, regs[fr]); + if (fr != to) + { + if (fr == to - 1) + *dest++ = ','; + else + *dest++ = '-'; + dest = addStr(dest, regs[to]); + } + not_first = 1; + } + else + msk++; + } + } + break; + case 'm': + *dest++ = '$'; + dest = addHex(dest, 8, opcode & 0xff); + break; + case 'Z': + *dest++ = '$'; + dest = addHex(dest, 16, (opcode & 0x7ff) << 1); + break; + case 'a': + *dest++ = '$'; + { + int add = opcode & 0x07ff; + if (add & 0x400) + add |= 0xfffff800; + add <<= 1; + dest = addHex(dest, 32, offset + 4 + add); + } + break; + case 'A': + { + int nopcode = debuggerReadHalfWord(offset + 2); + int add = opcode & 0x7ff; + if (add & 0x400) + add |= 0xfff800; + add = (add << 12) | ((nopcode & 0x7ff) << 1); + *dest++ = '$'; + dest = addHex(dest, 32, offset + 4 + add); + const char *s = elfGetAddressSymbol(offset + 4 + add); + if (*s) + { + *dest++ = ' '; + *dest++ = '('; + dest = addStr(dest, s); + *dest++ = ')'; + } + ret = 4; + } + break; + } + src++; + } + } + *dest++ = 0; + return ret; +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/armdis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/armdis.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,14 @@ +#ifndef VBA_GBA_ARMDIS_H +#define VBA_GBA_ARMDIS_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define DIS_VIEW_ADDRESS 1 +#define DIS_VIEW_CODE 2 + +int disThumb(u32 offset, char *dest, int flags); +int disArm(u32 offset, char *dest, int flags); + +#endif // VBA_GBA_ARMDIS_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/bios.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/bios.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,1278 @@ +#include +#include +#include + +#include "bios.h" +#include "../common/System.h" +#include "GBA.h" +#include "GBACheats.h" // FIXME: SDL build requires this +#include "GBAinline.h" +#include "GBAGlobals.h" + +s16 sineTable[256] = { + (s16)0x0000, (s16)0x0192, (s16)0x0323, (s16)0x04B5, (s16)0x0645, (s16)0x07D5, (s16)0x0964, (s16)0x0AF1, + (s16)0x0C7C, (s16)0x0E05, (s16)0x0F8C, (s16)0x1111, (s16)0x1294, (s16)0x1413, (s16)0x158F, (s16)0x1708, + (s16)0x187D, (s16)0x19EF, (s16)0x1B5D, (s16)0x1CC6, (s16)0x1E2B, (s16)0x1F8B, (s16)0x20E7, (s16)0x223D, + (s16)0x238E, (s16)0x24DA, (s16)0x261F, (s16)0x275F, (s16)0x2899, (s16)0x29CD, (s16)0x2AFA, (s16)0x2C21, + (s16)0x2D41, (s16)0x2E5A, (s16)0x2F6B, (s16)0x3076, (s16)0x3179, (s16)0x3274, (s16)0x3367, (s16)0x3453, + (s16)0x3536, (s16)0x3612, (s16)0x36E5, (s16)0x37AF, (s16)0x3871, (s16)0x392A, (s16)0x39DA, (s16)0x3A82, + (s16)0x3B20, (s16)0x3BB6, (s16)0x3C42, (s16)0x3CC5, (s16)0x3D3E, (s16)0x3DAE, (s16)0x3E14, (s16)0x3E71, + (s16)0x3EC5, (s16)0x3F0E, (s16)0x3F4E, (s16)0x3F84, (s16)0x3FB1, (s16)0x3FD3, (s16)0x3FEC, (s16)0x3FFB, + (s16)0x4000, (s16)0x3FFB, (s16)0x3FEC, (s16)0x3FD3, (s16)0x3FB1, (s16)0x3F84, (s16)0x3F4E, (s16)0x3F0E, + (s16)0x3EC5, (s16)0x3E71, (s16)0x3E14, (s16)0x3DAE, (s16)0x3D3E, (s16)0x3CC5, (s16)0x3C42, (s16)0x3BB6, + (s16)0x3B20, (s16)0x3A82, (s16)0x39DA, (s16)0x392A, (s16)0x3871, (s16)0x37AF, (s16)0x36E5, (s16)0x3612, + (s16)0x3536, (s16)0x3453, (s16)0x3367, (s16)0x3274, (s16)0x3179, (s16)0x3076, (s16)0x2F6B, (s16)0x2E5A, + (s16)0x2D41, (s16)0x2C21, (s16)0x2AFA, (s16)0x29CD, (s16)0x2899, (s16)0x275F, (s16)0x261F, (s16)0x24DA, + (s16)0x238E, (s16)0x223D, (s16)0x20E7, (s16)0x1F8B, (s16)0x1E2B, (s16)0x1CC6, (s16)0x1B5D, (s16)0x19EF, + (s16)0x187D, (s16)0x1708, (s16)0x158F, (s16)0x1413, (s16)0x1294, (s16)0x1111, (s16)0x0F8C, (s16)0x0E05, + (s16)0x0C7C, (s16)0x0AF1, (s16)0x0964, (s16)0x07D5, (s16)0x0645, (s16)0x04B5, (s16)0x0323, (s16)0x0192, + (s16)0x0000, (s16)0xFE6E, (s16)0xFCDD, (s16)0xFB4B, (s16)0xF9BB, (s16)0xF82B, (s16)0xF69C, (s16)0xF50F, + (s16)0xF384, (s16)0xF1FB, (s16)0xF074, (s16)0xEEEF, (s16)0xED6C, (s16)0xEBED, (s16)0xEA71, (s16)0xE8F8, + (s16)0xE783, (s16)0xE611, (s16)0xE4A3, (s16)0xE33A, (s16)0xE1D5, (s16)0xE075, (s16)0xDF19, (s16)0xDDC3, + (s16)0xDC72, (s16)0xDB26, (s16)0xD9E1, (s16)0xD8A1, (s16)0xD767, (s16)0xD633, (s16)0xD506, (s16)0xD3DF, + (s16)0xD2BF, (s16)0xD1A6, (s16)0xD095, (s16)0xCF8A, (s16)0xCE87, (s16)0xCD8C, (s16)0xCC99, (s16)0xCBAD, + (s16)0xCACA, (s16)0xC9EE, (s16)0xC91B, (s16)0xC851, (s16)0xC78F, (s16)0xC6D6, (s16)0xC626, (s16)0xC57E, + (s16)0xC4E0, (s16)0xC44A, (s16)0xC3BE, (s16)0xC33B, (s16)0xC2C2, (s16)0xC252, (s16)0xC1EC, (s16)0xC18F, + (s16)0xC13B, (s16)0xC0F2, (s16)0xC0B2, (s16)0xC07C, (s16)0xC04F, (s16)0xC02D, (s16)0xC014, (s16)0xC005, + (s16)0xC000, (s16)0xC005, (s16)0xC014, (s16)0xC02D, (s16)0xC04F, (s16)0xC07C, (s16)0xC0B2, (s16)0xC0F2, + (s16)0xC13B, (s16)0xC18F, (s16)0xC1EC, (s16)0xC252, (s16)0xC2C2, (s16)0xC33B, (s16)0xC3BE, (s16)0xC44A, + (s16)0xC4E0, (s16)0xC57E, (s16)0xC626, (s16)0xC6D6, (s16)0xC78F, (s16)0xC851, (s16)0xC91B, (s16)0xC9EE, + (s16)0xCACA, (s16)0xCBAD, (s16)0xCC99, (s16)0xCD8C, (s16)0xCE87, (s16)0xCF8A, (s16)0xD095, (s16)0xD1A6, + (s16)0xD2BF, (s16)0xD3DF, (s16)0xD506, (s16)0xD633, (s16)0xD767, (s16)0xD8A1, (s16)0xD9E1, (s16)0xDB26, + (s16)0xDC72, (s16)0xDDC3, (s16)0xDF19, (s16)0xE075, (s16)0xE1D5, (s16)0xE33A, (s16)0xE4A3, (s16)0xE611, + (s16)0xE783, (s16)0xE8F8, (s16)0xEA71, (s16)0xEBED, (s16)0xED6C, (s16)0xEEEF, (s16)0xF074, (s16)0xF1FB, + (s16)0xF384, (s16)0xF50F, (s16)0xF69C, (s16)0xF82B, (s16)0xF9BB, (s16)0xFB4B, (s16)0xFCDD, (s16)0xFE6E +}; + +void BIOS_ArcTan() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("ArcTan: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + + s32 a = -((s32)(reg[0].I * reg[0].I)) >> 14; + s32 b = ((0xA9 * a) >> 14) + 0x390; + b = ((b * a) >> 14) + 0x91C; + b = ((b * a) >> 14) + 0xFB6; + b = ((b * a) >> 14) + 0x16AA; + b = ((b * a) >> 14) + 0x2081; + b = ((b * a) >> 14) + 0x3651; + b = ((b * a) >> 14) + 0xA2F9; + reg[0].I = (reg[0].I * b) >> 16; + +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("ArcTan: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_ArcTan2() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("ArcTan2: %08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + s16 x = reg[0].I; + s16 y = reg[1].I; + + if (y == 0) + { + reg[0].I = 0x8000 & x; + reg[3].I = 0x170; + } + else + { + if (x == 0) + { + reg[0].I = (0x8000 & y) + 0x4000; + reg[3].I = 0x170; + } + else + { + if (abs(x) > abs(y)) + { + reg[1].I = x; + reg[0].I = y << 14; + BIOS_Div(); + BIOS_ArcTan(); + if (x < 0) + reg[0].I = 0x8000 + reg[0].I; + else + reg[0].I = ((y & 0x8000) << 1) + reg[0].I; + reg[3].I = 0x170; + } + else + { + reg[0].I = x << 14; + BIOS_Div(); + BIOS_ArcTan(); + reg[0].I = (0x4000 + (y & 0x8000)) - reg[0].I; + reg[3].I = 0x170; + } + } + } + +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("ArcTan2: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_BitUnPack() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("BitUnPack: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 header = reg[2].I; + + int len = CPUReadHalfWord(header); + // check address + int bits = CPUReadByte(header+2); + int revbits = 8 - bits; + // u32 value = 0; + u32 base = CPUReadMemory(header+4); + bool addBase = (base & 0x80000000) ? true : false; + base &= 0x7fffffff; + int dataSize = CPUReadByte(header+3); + + int data = 0; + int bitwritecount = 0; + while (1) + { + len -= 1; + if (len < 0) + break; + int mask = 0xff >> revbits; + u8 b = CPUReadByte(source); + source++; + int bitcount = 0; + while (1) + { + if (bitcount >= 8) + break; + u32 d = b & mask; + u32 temp = d >> bitcount; + if (!temp && addBase) + { + temp += base; + } + data |= temp << bitwritecount; + bitwritecount += dataSize; + if (bitwritecount >= 32) + { + CPUWriteMemory(dest, data); + dest += 4; + data = 0; + bitwritecount = 0; + } + mask <<= bits; + bitcount += bits; + } + } +} + +void BIOS_BgAffineSet() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("BgAffineSet: %08x,%08x,%08x (VCOUNT=%2d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + + for (int i = 0; i < num; i++) + { + s32 cx = CPUReadMemory(src); + src += 4; + s32 cy = CPUReadMemory(src); + src += 4; + s16 dispx = CPUReadHalfWord(src); + src += 2; + s16 dispy = CPUReadHalfWord(src); + src += 2; + s16 rx = CPUReadHalfWord(src); + src += 2; + s16 ry = CPUReadHalfWord(src); + src += 2; + u16 theta = CPUReadHalfWord(src)>>8; + src += 4; // keep structure alignment + s32 a = (s32)sineTable[(theta+0x40)&255]; + s32 b = (s32)sineTable[theta]; + + s16 dx = (s16)((rx * a)>>14); + s16 dmx = (s16)((rx * b)>>14); + s16 dy = (s16)((ry * b)>>14); + s16 dmy = (s16)((ry * a)>>14); + + CPUWriteHalfWord(dest, dx); + dest += 2; + CPUWriteHalfWord(dest, -dmx); + dest += 2; + CPUWriteHalfWord(dest, dy); + dest += 2; + CPUWriteHalfWord(dest, dmy); + dest += 2; + + s32 startx = cx - dx * dispx + dmx * dispy; + s32 starty = cy - dy * dispx - dmy * dispy; + + CPUWriteMemory(dest, startx); + dest += 4; + CPUWriteMemory(dest, starty); + dest += 4; + } +} + +void BIOS_CpuSet() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("CpuSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if (((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + int count = cnt & 0x1FFFFF; + + // 32-bit ? + if ((cnt >> 26) & 1) + { + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + // fill ? + if ((cnt >> 24) & 1) + { + u32 value = CPUReadMemory(source); + while (count) + { + CPUWriteMemory(dest, value); + dest += 4; + count--; + } + } + else + { + // copy + while (count) + { + CPUWriteMemory(dest, CPUReadMemory(source)); + source += 4; + dest += 4; + count--; + } + } + } + else + { + // 16-bit fill? + if ((cnt >> 24) & 1) + { + u16 value = CPUReadHalfWord(source); + while (count) + { + CPUWriteHalfWord(dest, value); + dest += 2; + count--; + } + } + else + { + // copy + while (count) + { + CPUWriteHalfWord(dest, CPUReadHalfWord(source)); + source += 2; + dest += 2; + count--; + } + } + } +} + +void BIOS_CpuFastSet() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("CpuFastSet: 0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + reg[2].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + u32 cnt = reg[2].I; + + if (((source & 0xe000000) == 0) || + ((source + (((cnt << 11)>>9) & 0x1fffff)) & 0xe000000) == 0) + return; + + // needed for 32-bit mode! + source &= 0xFFFFFFFC; + dest &= 0xFFFFFFFC; + + int count = cnt & 0x1FFFFF; + + // fill? + if ((cnt >> 24) & 1) + { + while (count > 0) + { + // BIOS always transfers 32 bytes at a time + u32 value = CPUReadMemory(source); + for (int i = 0; i < 8; i++) + { + CPUWriteMemory(dest, value); + dest += 4; + } + count -= 8; + } + } + else + { + // copy + while (count > 0) + { + // BIOS always transfers 32 bytes at a time + for (int i = 0; i < 8; i++) + { + CPUWriteMemory(dest, CPUReadMemory(source)); + source += 4; + dest += 4; + } + count -= 8; + } + } +} + +void BIOS_Diff8bitUnFilterWram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Diff8bitUnFilterWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff) & 0xe000000) == 0)) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + CPUWriteByte(dest++, data); + len--; + + while (len > 0) + { + u8 diff = CPUReadByte(source++); + data += diff; + CPUWriteByte(dest++, data); + len--; + } +} + +void BIOS_Diff8bitUnFilterVram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Diff8bitUnFilterVram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u8 data = CPUReadByte(source++); + u16 writeData = data; + int shift = 8; + int bytes = 1; + + while (len >= 2) + { + u8 diff = CPUReadByte(source++); + data += diff; + writeData |= (data << shift); + bytes++; + shift += 8; + if (bytes == 2) + { + CPUWriteHalfWord(dest, writeData); + dest += 2; + len -= 2; + bytes = 0; + writeData = 0; + shift = 0; + } + } +} + +void BIOS_Diff16bitUnFilter() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Diff16bitUnFilter: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, + reg[1].I, VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + u16 data = CPUReadHalfWord(source); + source += 2; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + + while (len >= 2) + { + u16 diff = CPUReadHalfWord(source); + source += 2; + data += diff; + CPUWriteHalfWord(dest, data); + dest += 2; + len -= 2; + } +} + +void BIOS_Div() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Div: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + int number = reg[0].I; + int denom = reg[1].I; + + if (denom != 0) + { + reg[0].I = number / denom; + reg[1].I = number % denom; + s32 temp = (s32)reg[0].I; + reg[3].I = temp < 0 ? (u32)-temp : (u32)temp; + } +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Div: return=0x%08x,0x%08x,0x%08x\n", + reg[0].I, + reg[1].I, + reg[3].I); + } +#endif +} + +void BIOS_DivARM() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("DivARM: 0x%08x, (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + u32 temp = reg[0].I; + reg[0].I = reg[1].I; + reg[1].I = temp; + BIOS_Div(); +} + +void BIOS_HuffUnComp() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("HuffUnComp: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + u8 treeSize = CPUReadByte(source++); + + u32 treeStart = source; + + source += (treeSize<<1) + 1; + + int len = header >> 8; + + u32 mask = 0x80000000; + u32 data = CPUReadMemory(source); + source += 4; + + int pos = 0; + u8 rootNode = CPUReadByte(treeStart); + u8 currentNode = rootNode; + bool writeData = false; + int byteShift = 0; + int byteCount = 0; + u32 writeValue = 0; + + if ((header & 0x0F) == 8) + { + while (len > 0) + { + // take left + if (pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if (data & mask) + { + // right + if (currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } + else + { + // left + if (currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if (writeData) + { + writeValue |= (currentNode << byteShift); + byteCount++; + byteShift += 8; + + pos = 0; + currentNode = rootNode; + writeData = false; + + if (byteCount == 4) + { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + writeValue = 0; + dest += 4; + len -= 4; + } + } + mask >>= 1; + if (mask == 0) + { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } + else + { + int halfLen = 0; + int value = 0; + while (len > 0) + { + // take left + if (pos == 0) + pos++; + else + pos += (((currentNode & 0x3F)+1)<<1); + + if ((data & mask)) + { + // right + if (currentNode & 0x40) + writeData = true; + currentNode = CPUReadByte(treeStart+pos+1); + } + else + { + // left + if (currentNode & 0x80) + writeData = true; + currentNode = CPUReadByte(treeStart+pos); + } + + if (writeData) + { + if (halfLen == 0) + value |= currentNode; + else + value |= (currentNode<<4); + + halfLen += 4; + if (halfLen == 8) + { + writeValue |= (value << byteShift); + byteCount++; + byteShift += 8; + + halfLen = 0; + value = 0; + + if (byteCount == 4) + { + byteCount = 0; + byteShift = 0; + CPUWriteMemory(dest, writeValue); + dest += 4; + writeValue = 0; + len -= 4; + } + } + pos = 0; + currentNode = rootNode; + writeData = false; + } + mask >>= 1; + if (mask == 0) + { + mask = 0x80000000; + data = CPUReadMemory(source); + source += 4; + } + } + } +} + +void BIOS_LZ77UnCompVram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("LZ77UnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + int len = header >> 8; + + while (len > 0) + { + u8 d = CPUReadByte(source++); + + if (d) + { + for (int i = 0; i < 8; i++) + { + if (d & 0x80) + { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest + byteCount - offset - 1; + for (int i = 0; i < length; i++) + { + writeValue |= (CPUReadByte(windowOffset++) << byteShift); + byteShift += 8; + byteCount++; + + if (byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if (len == 0) + return; + } + } + else + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if (byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if (len == 0) + return; + } + d <<= 1; + } + } + else + { + for (int i = 0; i < 8; i++) + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if (byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteShift = 0; + byteCount = 0; + writeValue = 0; + } + len--; + if (len == 0) + return; + } + } + } +} + +void BIOS_LZ77UnCompWram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("LZ77UnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", reg[0].I, reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while (len > 0) + { + u8 d = CPUReadByte(source++); + + if (d) + { + for (int i = 0; i < 8; i++) + { + if (d & 0x80) + { + u16 data = CPUReadByte(source++) << 8; + data |= CPUReadByte(source++); + int length = (data >> 12) + 3; + int offset = (data & 0x0FFF); + u32 windowOffset = dest - offset - 1; + for (int i = 0; i < length; i++) + { + CPUWriteByte(dest++, CPUReadByte(windowOffset++)); + len--; + if (len == 0) + return; + } + } + else + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if (len == 0) + return; + } + d <<= 1; + } + } + else + { + for (int i = 0; i < 8; i++) + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if (len == 0) + return; + } + } + } +} + +void BIOS_ObjAffineSet() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("ObjAffineSet: 0x%08x,0x%08x,0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + reg[2].I, + reg[3].I, + VCOUNT); + } +#endif + + u32 src = reg[0].I; + u32 dest = reg[1].I; + int num = reg[2].I; + int offset = reg[3].I; + + for (int i = 0; i < num; i++) + { + s16 rx = CPUReadHalfWord(src); + src += 2; + s16 ry = CPUReadHalfWord(src); + src += 2; + u16 theta = CPUReadHalfWord(src)>>8; + src += 4; // keep structure alignment + + s32 a = (s32)sineTable[(theta+0x40)&255]; + s32 b = (s32)sineTable[theta]; + + s16 dx = (s16)((rx * a)>>14); + s16 dmx = (s16)((rx * b)>>14); + s16 dy = (s16)((ry * b)>>14); + s16 dmy = (s16)((ry * a)>>14); + + CPUWriteHalfWord(dest, dx); + dest += offset; + CPUWriteHalfWord(dest, -dmx); + dest += offset; + CPUWriteHalfWord(dest, dy); + dest += offset; + CPUWriteHalfWord(dest, dmy); + dest += offset; + } +} + +void BIOS_RegisterRamReset(u32 flags) +{ + // no need to trace here. this is only called directly from GBA.cpp + // to emulate bios initialization + + if (flags) + { + if (flags & 0x01) + { + // clear work RAM + memset(workRAM, 0, 0x40000); + } + if (flags & 0x02) + { + // clear internal RAM + memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff + } + if (flags & 0x04) + { + // clear palette RAM + memset(paletteRAM, 0, 0x400); + } + if (flags & 0x08) + { + // clear VRAM + memset(vram, 0, 0x18000); + } + if (flags & 0x10) + { + // clean OAM + memset(oam, 0, 0x400); + } + + if (flags & 0x80) + { + int i; + for (i = 0; i < 8; i++) + CPUUpdateRegister(0x200+i*2, 0); + + CPUUpdateRegister(0x202, 0xFFFF); + + for (i = 0; i < 8; i++) + CPUUpdateRegister(0x4+i*2, 0); + + for (i = 0; i < 16; i++) + CPUUpdateRegister(0x20+i*2, 0); + + for (i = 0; i < 24; i++) + CPUUpdateRegister(0xb0+i*2, 0); + + CPUUpdateRegister(0x130, 0); + CPUUpdateRegister(0x20, 0x100); + CPUUpdateRegister(0x30, 0x100); + CPUUpdateRegister(0x26, 0x100); + CPUUpdateRegister(0x36, 0x100); + } + + if (flags & 0x20) + { + int i; + for (i = 0; i < 8; i++) + CPUUpdateRegister(0x110+i*2, 0); + CPUUpdateRegister(0x134, 0x8000); + for (i = 0; i < 7; i++) + CPUUpdateRegister(0x140+i*2, 0); + } + + if (flags & 0x40) + { + int i; + CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0x80); + CPUWriteMemory(0x4000080, 0x880e0000); + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); + CPUWriteByte(0x4000070, 0x70); + for (i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000070, 0); + for (i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000084, 0); + } + } +} + +void BIOS_RegisterRamReset() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("RegisterRamReset: 0x%08x (VCOUNT=%d)\n", + reg[0].I, + VCOUNT); + } +#endif + + BIOS_RegisterRamReset(reg[0].I); +} + +void BIOS_RLUnCompVram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("RLUnCompVram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + int byteCount = 0; + int byteShift = 0; + u32 writeValue = 0; + + while (len > 0) + { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if (d & 0x80) + { + u8 data = CPUReadByte(source++); + l += 3; + for (int i = 0; i < l; i++) + { + writeValue |= (data << byteShift); + byteShift += 8; + byteCount++; + + if (byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if (len == 0) + return; + } + } + else + { + l++; + for (int i = 0; i < l; i++) + { + writeValue |= (CPUReadByte(source++) << byteShift); + byteShift += 8; + byteCount++; + if (byteCount == 2) + { + CPUWriteHalfWord(dest, writeValue); + dest += 2; + byteCount = 0; + byteShift = 0; + writeValue = 0; + } + len--; + if (len == 0) + return; + } + } + } +} + +void BIOS_RLUnCompWram() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("RLUnCompWram: 0x%08x,0x%08x (VCOUNT=%d)\n", + reg[0].I, + reg[1].I, + VCOUNT); + } +#endif + + u32 source = reg[0].I; + u32 dest = reg[1].I; + + u32 header = CPUReadMemory(source); + source += 4; + + if (((source & 0xe000000) == 0) || + ((source + ((header >> 8) & 0x1fffff)) & 0xe000000) == 0) + return; + + int len = header >> 8; + + while (len > 0) + { + u8 d = CPUReadByte(source++); + int l = d & 0x7F; + if (d & 0x80) + { + u8 data = CPUReadByte(source++); + l += 3; + for (int i = 0; i < l; i++) + { + CPUWriteByte(dest++, data); + len--; + if (len == 0) + return; + } + } + else + { + l++; + for (int i = 0; i < l; i++) + { + CPUWriteByte(dest++, CPUReadByte(source++)); + len--; + if (len == 0) + return; + } + } + } +} + +void BIOS_SoftReset() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("SoftReset: (VCOUNT=%d)\n", VCOUNT); + } +#endif + + armState = true; + armMode = 0x1F; + armIrqEnable = false; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + reg[13].I = 0x03007F00; + reg[14].I = 0x00000000; + reg[16].I = 0x00000000; + reg[R13_IRQ].I = 0x03007FA0; + reg[R14_IRQ].I = 0x00000000; + reg[SPSR_IRQ].I = 0x00000000; + reg[R13_SVC].I = 0x03007FE0; + reg[R14_SVC].I = 0x00000000; + reg[SPSR_SVC].I = 0x00000000; + u8 b = internalRAM[0x7ffa]; + + memset(&internalRAM[0x7e00], 0, 0x200); + + if (b) + { + armNextPC = 0x02000000; + reg[15].I = 0x02000004; + } + else + { + armNextPC = 0x08000000; + reg[15].I = 0x08000004; + } +} + +void BIOS_Sqrt() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Sqrt: %08x (VCOUNT=%2d)\n", + reg[0].I, + VCOUNT); + } +#endif + reg[0].I = (u32)sqrt((double)reg[0].I); +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("Sqrt: return=%08x\n", + reg[0].I); + } +#endif +} + +void BIOS_MidiKey2Freq() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("MidiKey2Freq: WaveData=%08x mk=%08x fp=%08x\n", + reg[0].I, + reg[1].I, + reg[2].I); + } +#endif + int freq = CPUReadMemory(reg[0].I+4); + double tmp; + tmp = ((double)(180 - reg[1].I)) - ((double)reg[2].I / 256.f); + tmp = pow((double)2.f, tmp / 12.f); + reg[0].I = (int)((double)freq / tmp); + +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("MidiKey2Freq: return %08x\n", + reg[0].I); + } +#endif +} + +void BIOS_SndDriverJmpTableCopy() +{ +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_SWI) + { + log("SndDriverJmpTableCopy: dest=%08x\n", + reg[0].I); + } +#endif + for (int i = 0; i < 0x24; i++) + { + CPUWriteMemory(reg[0].I, 0x9c); + reg[0].I += 4; + } +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/bios.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/bios.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,34 @@ +#ifndef VBA_BIOS_H +#define VBA_BIOS_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../Port.h" + +extern void BIOS_ArcTan(); +extern void BIOS_ArcTan2(); +extern void BIOS_BitUnPack(); +extern void BIOS_BgAffineSet(); +extern void BIOS_CpuSet(); +extern void BIOS_CpuFastSet(); +extern void BIOS_Diff8bitUnFilterWram(); +extern void BIOS_Diff8bitUnFilterVram(); +extern void BIOS_Diff16bitUnFilter(); +extern void BIOS_Div(); +extern void BIOS_DivARM(); +extern void BIOS_HuffUnComp(); +extern void BIOS_LZ77UnCompVram(); +extern void BIOS_LZ77UnCompWram(); +extern void BIOS_ObjAffineSet(); +extern void BIOS_RegisterRamReset(); +extern void BIOS_RegisterRamReset(u32); +extern void BIOS_RLUnCompVram(); +extern void BIOS_RLUnCompWram(); +extern void BIOS_SoftReset(); +extern void BIOS_Sqrt(); +extern void BIOS_MidiKey2Freq(); +extern void BIOS_SndDriverJmpTableCopy(); + +#endif // VBA_BIOS_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/elf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/elf.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,3229 @@ +#include +#include +#include + +#include "../Port.h" +#include "../NLS.h" +#include "../common/System.h" // systemMessage +#include "GBAGlobals.h" +#include "elf.h" + +#define elfReadMemory(addr) \ + READ32LE((&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +#define DW_TAG_array_type 0x01 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 0x1d +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_base_type 0x24 +#define DW_TAG_const_type 0x26 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_subprogram 0x2e +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_compdir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_inline 0x20 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_artificial 0x34 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_macro_info 0x43 +#define DW_AT_specification 0x47 +#define DW_AT_type 0x49 +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +// DWARF 2.1/3.0 extensions +#define DW_AT_entry_pc 0x52 +#define DW_AT_ranges 0x55 +// ARM Compiler extensions +#define DW_AT_proc_body 0x2000 +#define DW_AT_save_offset 0x2001 +#define DW_AT_user_2002 0x2002 +// MIPS extensions +#define DW_AT_MIPS_linkage_name 0x2007 + +#define DW_FORM_addr 0x01 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_reg0 0x50 +#define DW_OP_reg1 0x51 +#define DW_OP_reg2 0x52 +#define DW_OP_reg3 0x53 +#define DW_OP_reg4 0x54 +#define DW_OP_reg5 0x55 +#define DW_OP_reg6 0x56 +#define DW_OP_reg7 0x57 +#define DW_OP_reg8 0x58 +#define DW_OP_reg9 0x59 +#define DW_OP_reg10 0x5a +#define DW_OP_reg11 0x5b +#define DW_OP_reg12 0x5c +#define DW_OP_reg13 0x5d +#define DW_OP_reg14 0x5e +#define DW_OP_reg15 0x5f +#define DW_OP_fbreg 0x91 + +#define DW_LNS_extended_op 0x00 +#define DW_LNS_copy 0x01 +#define DW_LNS_advance_pc 0x02 +#define DW_LNS_advance_line 0x03 +#define DW_LNS_set_file 0x04 +#define DW_LNS_set_column 0x05 +#define DW_LNS_negate_stmt 0x06 +#define DW_LNS_set_basic_block 0x07 +#define DW_LNS_const_add_pc 0x08 +#define DW_LNS_fixed_advance_pc 0x09 + +#define DW_LNE_end_sequence 0x01 +#define DW_LNE_set_address 0x02 +#define DW_LNE_define_file 0x03 + +#define DW_CFA_advance_loc 0x01 +#define DW_CFA_offset 0x02 +#define DW_CFA_restore 0x03 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_nop 0x00 + +#define CASE_TYPE_TAG \ +case DW_TAG_const_type: \ +case DW_TAG_volatile_type: \ +case DW_TAG_pointer_type: \ +case DW_TAG_base_type: \ +case DW_TAG_array_type: \ +case DW_TAG_structure_type: \ +case DW_TAG_union_type: \ +case DW_TAG_typedef: \ +case DW_TAG_subroutine_type: \ +case DW_TAG_enumeration_type: \ +case DW_TAG_enumerator: \ +case DW_TAG_reference_type + +struct ELFcie +{ + ELFcie *next; + u32 offset; + u8 * augmentation; + u32 codeAlign; + s32 dataAlign; + int returnAddress; + u8 * data; + u32 dataLen; +}; + +struct ELFfde +{ + ELFcie *cie; + u32 address; + u32 end; + u8 * data; + u32 dataLen; +}; + +enum ELFRegMode +{ + REG_NOT_SET, + REG_OFFSET, + REG_REGISTER +}; + +struct ELFFrameStateRegister +{ + ELFRegMode mode; + int reg; + s32 offset; +}; + +struct ELFFrameStateRegisters +{ + ELFFrameStateRegister regs[16]; + ELFFrameStateRegisters *previous; +}; + +enum ELFCfaMode +{ + CFA_NOT_SET, + CFA_REG_OFFSET +}; + +struct ELFFrameState +{ + ELFFrameStateRegisters registers; + + ELFCfaMode cfaMode; + int cfaRegister; + s32 cfaOffset; + + u32 pc; + + int dataAlign; + int codeAlign; + int returnAddress; +}; + +extern bool8 cpuIsMultiBoot; + +Symbol *elfSymbols = NULL; +char * elfSymbolsStrTab = NULL; +int elfSymbolsCount = 0; + +ELFSectionHeader **elfSectionHeaders = NULL; +char *elfSectionHeadersStringTable = NULL; +int elfSectionHeadersCount = 0; +u8 * elfFileData = NULL; + +CompileUnit *elfCompileUnits = NULL; +DebugInfo * elfDebugInfo = NULL; +char * elfDebugStrings = NULL; + +ELFcie * elfCies = NULL; +ELFfde **elfFdes = NULL; +int elfFdeCount = 0; + +CompileUnit *elfCurrentUnit = NULL; + +u32 elfRead4Bytes(u8 *); +u16 elfRead2Bytes(u8 *); + +CompileUnit *elfGetCompileUnit(u32 addr) +{ + if (elfCompileUnits) + { + CompileUnit *unit = elfCompileUnits; + while (unit) + { + if (unit->lowPC) + { + if (addr >= unit->lowPC && addr < unit->highPC) + return unit; + } + else + { + ARanges *r = unit->ranges; + if (r) + { + int count = r->count; + for (int j = 0; j < count; j++) + { + if (addr >= r->ranges[j].lowPC && addr < r->ranges[j].highPC) + return unit; + } + } + } + unit = unit->next; + } + } + return NULL; +} + +char *elfGetAddressSymbol(u32 addr) +{ + static char buffer[256]; + + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if (unit) + { + Function *func = unit->functions; + while (func) + { + if (addr >= func->lowPC && addr < func->highPC) + { + int offset = addr - func->lowPC; + char *name = func->name; + if (!name) + name = ""; + if (offset) + sprintf(buffer, "%s+%d", name, offset); + else + strcpy(buffer, name); + return buffer; + } + func = func->next; + } + } + + if (elfSymbolsCount) + { + for (int i = 0; i < elfSymbolsCount; i++) + { + Symbol *s = &elfSymbols[i]; + if ((addr >= s->value) && addr < (s->value+s->size)) + { + int offset = addr-s->value; + char *name = s->name; + if (name == NULL) + name = ""; + if (offset) + sprintf(buffer, "%s+%d", name, addr-s->value); + else + strcpy(buffer, name); + return buffer; + } + else if (addr == s->value) + { + if (s->name) + strcpy(buffer, s->name); + else + strcpy(buffer, ""); + return buffer; + } + } + } + + return ""; +} + +bool elfFindLineInModule(u32 *addr, char *name, int line) +{ + CompileUnit *unit = elfCompileUnits; + + while (unit) + { + if (unit->lineInfoTable) + { + int i; + int count = unit->lineInfoTable->fileCount; + char *found = NULL; + for (i = 0; i < count; i++) + { + if (strcmp(name, unit->lineInfoTable->files[i]) == 0) + { + found = unit->lineInfoTable->files[i]; + break; + } + } + // found a matching filename... try to find line now + if (found) + { + LineInfoItem *table = unit->lineInfoTable->lines; + count = unit->lineInfoTable->number; + for (i = 0; i < count; i++) + { + if (table[i].file == found && table[i].line == line) + { + *addr = table[i].address; + return true; + } + } + // we can only find a single match + return false; + } + } + unit = unit->next; + } + return false; +} + +int elfFindLine(CompileUnit *unit, Function * /* func */, u32 addr, char **f) +{ + int currentLine = -1; + if (unit->hasLineInfo) + { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for (i = 0; i < count; i++) + { + if (addr <= table[i].address) + break; + } + if (i == count) + i--; + *f = table[i].file; + currentLine = table[i].line; + } + return currentLine; +} + +bool elfFindLineInUnit(u32 *addr, CompileUnit *unit, int line) +{ + if (unit->hasLineInfo) + { + int count = unit->lineInfoTable->number; + LineInfoItem *table = unit->lineInfoTable->lines; + int i; + for (i = 0; i < count; i++) + { + if (line == table[i].line) + { + *addr = table[i].address; + return true; + } + } + } + return false; +} + +bool elfGetCurrentFunction(u32 addr, Function **f, CompileUnit **u) +{ + CompileUnit *unit = elfGetCompileUnit(addr); + // found unit, need to find function + if (unit) + { + Function *func = unit->functions; + while (func) + { + if (addr >= func->lowPC && addr < func->highPC) + { + *f = func; + *u = unit; + return true; + } + func = func->next; + } + } + return false; +} + +bool elfGetObject(char *name, Function *f, CompileUnit *u, Object **o) +{ + if (f && u) + { + Object *v = f->variables; + + while (v) + { + if (strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + v = f->parameters; + while (v) + { + if (strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + v = u->variables; + while (v) + { + if (strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + } + + CompileUnit *c = elfCompileUnits; + + while (c) + { + if (c != u) + { + Object *v = c->variables; + while (v) + { + if (strcmp(name, v->name) == 0) + { + *o = v; + return true; + } + v = v->next; + } + } + c = c->next; + } + + return false; +} + +char *elfGetSymbol(int i, u32 *value, u32 *size, int *type) +{ + if (i < elfSymbolsCount) + { + Symbol *s = &elfSymbols[i]; + *value = s->value; + *size = s->size; + *type = s->type; + return s->name; + } + return NULL; +} + +bool elfGetSymbolAddress(char *sym, u32 *addr, u32 *size, int *type) +{ + if (elfSymbolsCount) + { + for (int i = 0; i < elfSymbolsCount; i++) + { + Symbol *s = &elfSymbols[i]; + if (strcmp(sym, s->name) == 0) + { + *addr = s->value; + *size = s->size; + *type = s->type; + return true; + } + } + } + return false; +} + +ELFfde *elfGetFde(u32 address) +{ + if (elfFdes) + { + int i; + for (i = 0; i < elfFdeCount; i++) + { + if (address >= elfFdes[i]->address && + address < elfFdes[i]->end) + { + return elfFdes[i]; + } + } + } + + return NULL; +} + +void elfExecuteCFAInstructions(ELFFrameState *state, u8 *data, u32 len, + u32 pc) +{ + u8 *end = data + len; + int bytes; + int reg; + ELFFrameStateRegisters *fs; + + while (data < end && state->pc < pc) + { + u8 op = *data++; + + switch (op >> 6) + { + case DW_CFA_advance_loc: + state->pc += (op & 0x3f) * state->codeAlign; + break; + case DW_CFA_offset: + reg = op & 0x3f; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore: + // we don't care much about the other possible settings, + // so just setting to unset is enough for now + state->registers.regs[op & 0x3f].mode = REG_NOT_SET; + break; + case 0: + switch (op & 0x3f) + { + case DW_CFA_nop: + break; + case DW_CFA_advance_loc1: + state->pc += state->codeAlign * (*data++); + break; + case DW_CFA_advance_loc2: + state->pc += state->codeAlign * elfRead2Bytes(data); + data += 2; + break; + case DW_CFA_advance_loc4: + state->pc += state->codeAlign * elfRead4Bytes(data); + data += 4; + break; + case DW_CFA_offset_extended: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_OFFSET; + state->registers.regs[reg].offset = state->dataAlign * + (s32)elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_NOT_SET; + break; + case DW_CFA_register: + reg = elfReadLEB128(data, &bytes); + data += bytes; + state->registers.regs[reg].mode = REG_REGISTER; + state->registers.regs[reg].reg = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_CFA_remember_state: + fs = (ELFFrameStateRegisters *)calloc(1, + sizeof(ELFFrameStateRegisters)); + memcpy(fs, &state->registers, sizeof(ELFFrameStateRegisters)); + state->registers.previous = fs; + break; + case DW_CFA_restore_state: + if (state->registers.previous == NULL) + { + printf("Error: previous frame state is NULL.\n"); + return; + } + fs = state->registers.previous; + memcpy(&state->registers, fs, sizeof(ELFFrameStateRegisters)); + free(fs); + break; + case DW_CFA_def_cfa: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_register: + state->cfaRegister = elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + case DW_CFA_def_cfa_offset: + state->cfaOffset = (s32)elfReadLEB128(data, &bytes); + data += bytes; + state->cfaMode = CFA_REG_OFFSET; + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + break; + default: + printf("Unknown CFA opcode %08x\n", op); + return; + } + } +} + +ELFFrameState *elfGetFrameState(ELFfde *fde, u32 address) +{ + ELFFrameState *state = (ELFFrameState *)calloc(1, sizeof(ELFFrameState)); + state->pc = fde->address; + state->dataAlign = fde->cie->dataAlign; + state->codeAlign = fde->cie->codeAlign; + state->returnAddress = fde->cie->returnAddress; + + elfExecuteCFAInstructions(state, + fde->cie->data, + fde->cie->dataLen, + 0xffffffff); + elfExecuteCFAInstructions(state, + fde->data, + fde->dataLen, + address); + + return state; +} + +void elfPrintCallChain(u32 address) +{ + int count = 1; + + reg_pair regs[15]; + reg_pair newRegs[15]; + + memcpy(®s[0], ®[0], sizeof(reg_pair) * 15); + + while (count < 20) + { + char *addr = elfGetAddressSymbol(address); + if (*addr == 0) + addr = "???"; + + printf("%08x %s\n", address, addr); + + ELFfde *fde = elfGetFde(address); + + if (fde == NULL) + { + break; + } + + ELFFrameState *state = elfGetFrameState(fde, address); + + if (!state) + { + break; + } + + if (state->cfaMode == CFA_REG_OFFSET) + { + memcpy(&newRegs[0], ®s[0], sizeof(reg_pair) * 15); + u32 addr = 0; + for (int i = 0; i < 15; i++) + { + ELFFrameStateRegister *r = &state->registers. + regs[i]; + + switch (r->mode) + { + case REG_NOT_SET: + newRegs[i].I = regs[i].I; + break; + case REG_OFFSET: + newRegs[i].I = elfReadMemory(regs[state->cfaRegister].I + + state->cfaOffset + + r->offset); + break; + case REG_REGISTER: + newRegs[i].I = regs[r->reg].I; + break; + default: + printf("Unknown register mode: %d\n", r->mode); + break; + } + } + memcpy(regs, newRegs, sizeof(reg_pair)*15); + addr = newRegs[14].I; + addr &= 0xfffffffe; + address = addr; + count++; + } + else + { + printf("CFA not set\n"); + break; + } + if (state->registers.previous) + { + ELFFrameStateRegisters *prev = state->registers.previous; + + while (prev) + { + ELFFrameStateRegisters *p = prev->previous; + free(prev); + prev = p; + } + } + free(state); + } +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type, u32 base) +{ + u32 framebase = 0; + if (f && f->frameBase) + { + ELFBlock *b = f->frameBase; + switch (*b->data) + { + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + framebase = reg[*b->data-0x50].I; + break; + default: + fprintf(stderr, "Unknown frameBase %02x\n", *b->data); + break; + } + } + + ELFBlock *loc = o; + u32 location = 0; + int bytes = 0; + if (loc) + { + switch (*loc->data) + { + case DW_OP_addr: + location = elfRead4Bytes(loc->data+1); + *type = LOCATION_memory; + break; + case DW_OP_plus_uconst: + location = base + elfReadLEB128(loc->data+1, &bytes); + *type = LOCATION_memory; + break; + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + location = *loc->data - 0x50; + *type = LOCATION_register; + break; + case DW_OP_fbreg: + { + int bytes; + s32 off = elfReadSignedLEB128(loc->data+1, &bytes); + location = framebase + off; + *type = LOCATION_memory; + break; + } + default: + fprintf(stderr, "Unknown location %02x\n", *loc->data); + break; + } + } + return location; +} + +u32 elfDecodeLocation(Function *f, ELFBlock *o, LocationType *type) +{ + return elfDecodeLocation(f, o, type, 0); +} + +// reading function + +u32 elfRead4Bytes(u8 *data) +{ + u32 value = *data++; + value |= (*data++ << 8); + value |= (*data++ << 16); + value |= (*data << 24); + return value; +} + +u16 elfRead2Bytes(u8 *data) +{ + u16 value = *data++; + value |= (*data << 8); + return value; +} + +char *elfReadString(u8 *data, int *bytesRead) +{ + if (*data == 0) + { + *bytesRead = 1; + return NULL; + } + *bytesRead = strlen((char *)data) + 1; + return (char *)data; +} + +s32 elfReadSignedLEB128(u8 *data, int *bytesRead) +{ + s32 result = 0; + int shift = 0; + int count = 0; + + u8 byte; + do + { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + if ((shift < 32) && (byte & 0x40)) + result |= -(1 << shift); + *bytesRead = count; + return result; +} + +u32 elfReadLEB128(u8 *data, int *bytesRead) +{ + u32 result = 0; + int shift = 0; + int count = 0; + u8 byte; + do + { + byte = *data++; + count++; + result |= (byte & 0x7f) << shift; + shift += 7; + } + while (byte & 0x80); + *bytesRead = count; + return result; +} + +u8 *elfReadSection(u8 *data, ELFSectionHeader *sh) +{ + return data + READ32LE(&sh->offset); +} + +ELFSectionHeader *elfGetSectionByName(char *name) +{ + for (int i = 0; i < elfSectionHeadersCount; i++) + { + if (strcmp(name, + &elfSectionHeadersStringTable[READ32LE(&elfSectionHeaders[i]-> + name)]) == 0) + { + return elfSectionHeaders[i]; + } + } + return NULL; +} + +ELFSectionHeader *elfGetSectionByNumber(int number) +{ + if (number < elfSectionHeadersCount) + { + return elfSectionHeaders[number]; + } + return NULL; +} + +CompileUnit *elfGetCompileUnitForData(u8 *data) +{ + u8 *end = elfCurrentUnit->top + 4 + elfCurrentUnit->length; + + if (data >= elfCurrentUnit->top && data < end) + return elfCurrentUnit; + + CompileUnit *unit = elfCompileUnits; + + while (unit) + { + end = unit->top + 4 + unit->length; + + if (data >= unit->top && data < end) + return unit; + + unit = unit->next; + } + + printf("Error: cannot find reference to compile unit at offset %08x\n", + (int)(data - elfDebugInfo->infodata)); + exit(-1); +} + +u8 *elfReadAttribute(u8 *data, ELFAttr *attr) +{ + int bytes; + int form = attr->form; +start: + switch (form) + { + case DW_FORM_addr: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_data2: + attr->value = elfRead2Bytes(data); + data += 2; + break; + case DW_FORM_data4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_string: + attr->string = (char *)data; + data += strlen(attr->string)+1; + break; + case DW_FORM_strp: + attr->string = elfDebugStrings + elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_block: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = elfReadLEB128(data, &bytes); + data += bytes; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_block1: + attr->block = (ELFBlock *)malloc(sizeof(ELFBlock)); + attr->block->length = *data++; + attr->block->data = data; + data += attr->block->length; + break; + case DW_FORM_data1: + attr->value = *data++; + break; + case DW_FORM_flag: + attr->flag = (*data++) ? true : false; + break; + case DW_FORM_sdata: + attr->value = elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_udata: + attr->value = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_FORM_ref_addr: + attr->value = (elfDebugInfo->infodata + elfRead4Bytes(data)) - + elfGetCompileUnitForData(data)->top; + data += 4; + break; + case DW_FORM_ref4: + attr->value = elfRead4Bytes(data); + data += 4; + break; + case DW_FORM_ref_udata: + attr->value = (elfDebugInfo->infodata + + (elfGetCompileUnitForData(data)->top - + elfDebugInfo->infodata) + + elfReadLEB128(data, &bytes)) - + elfCurrentUnit->top; + data += bytes; + break; + case DW_FORM_indirect: + form = elfReadLEB128(data, &bytes); + data += bytes; + goto start; + default: + fprintf(stderr, "Unsupported FORM %02x\n", form); + exit(-1); + } + return data; +} + +ELFAbbrev *elfGetAbbrev(ELFAbbrev **table, u32 number) +{ + int hash = number % 121; + + ELFAbbrev *abbrev = table[hash]; + + while (abbrev) + { + if (abbrev->number == number) + return abbrev; + abbrev = abbrev->next; + } + return NULL; +} + +ELFAbbrev * *elfReadAbbrevs(u8 *data, u32 offset) +{ + data += offset; + ELFAbbrev **abbrevs = (ELFAbbrev * *)calloc(sizeof(ELFAbbrev *)*121, 1); + int bytes = 0; + u32 number = elfReadLEB128(data, &bytes); + data += bytes; + while (number) + { + ELFAbbrev *abbrev = (ELFAbbrev *)calloc(sizeof(ELFAbbrev), 1); + + // read tag information + abbrev->number = number; + abbrev->tag = elfReadLEB128(data, &bytes); + data += bytes; + abbrev->hasChildren = *data++ ? true : false; + + // read attributes + int name = elfReadLEB128(data, &bytes); + data += bytes; + int form = elfReadLEB128(data, &bytes); + data += bytes; + + while (name) + { + if ((abbrev->numAttrs % 4) == 0) + { + abbrev->attrs = (ELFAttr *)realloc(abbrev->attrs, + (abbrev->numAttrs + 4) * + sizeof(ELFAttr)); + } + abbrev->attrs[abbrev->numAttrs].name = name; + abbrev->attrs[abbrev->numAttrs++].form = form; + + name = elfReadLEB128(data, &bytes); + data += bytes; + form = elfReadLEB128(data, &bytes); + data += bytes; + } + + int hash = number % 121; + abbrev->next = abbrevs[hash]; + abbrevs[hash] = abbrev; + + number = elfReadLEB128(data, &bytes); + data += bytes; + + if (elfGetAbbrev(abbrevs, number) != NULL) + break; + } + + return abbrevs; +} + +void elfParseCFA(u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_frame"); + + if (h == NULL) + { + return; + } + + u8 *data = elfReadSection(top, h); + + u8 *topOffset = data; + + u8 *end = data + READ32LE(&h->size); + + ELFcie *cies = NULL; + + while (data < end) + { + u32 offset = data - topOffset; + u32 len = elfRead4Bytes(data); + data += 4; + + u8 *dataEnd = data + len; + + u32 id = elfRead4Bytes(data); + data += 4; + + if (id == 0xffffffff) + { + // skip version + *data++; + + ELFcie *cie = (ELFcie *)calloc(1, sizeof(ELFcie)); + + cie->next = cies; + cies = cie; + + cie->offset = offset; + + cie->augmentation = data; + while (*data) + data++; + data++; + + if (*cie->augmentation) + { + fprintf(stderr, "Error: augmentation not supported\n"); + exit(-1); + } + + int bytes; + cie->codeAlign = elfReadLEB128(data, &bytes); + data += bytes; + + cie->dataAlign = elfReadSignedLEB128(data, &bytes); + data += bytes; + + cie->returnAddress = *data++; + + cie->data = data; + cie->dataLen = dataEnd - data; + } + else + { + ELFfde *fde = (ELFfde *)calloc(1, sizeof(ELFfde)); + + ELFcie *cie = cies; + + while (cie != NULL) + { + if (cie->offset == id) + break; + cie = cie->next; + } + + if (!cie) + { + fprintf(stderr, "Cannot find CIE %08x\n", id); + exit(-1); + } + + fde->cie = cie; + + fde->address = elfRead4Bytes(data); + data += 4; + + fde->end = fde->address + elfRead4Bytes(data); + data += 4; + + fde->data = data; + fde->dataLen = dataEnd - data; + + if ((elfFdeCount %10) == 0) + { + elfFdes = (ELFfde * *)realloc(elfFdes, (elfFdeCount+10) * + sizeof(ELFfde *)); + } + elfFdes[elfFdeCount++] = fde; + } + data = dataEnd; + } + + elfCies = cies; +} + +void elfAddLine(LineInfo *l, u32 a, int file, int line, int *max) +{ + if (l->number == *max) + { + *max += 1000; + l->lines = (LineInfoItem *)realloc(l->lines, *max*sizeof(LineInfoItem)); + } + LineInfoItem *li = &l->lines[l->number]; + li->file = l->files[file-1]; + li->address = a; + li->line = line; + l->number++; +} + +void elfParseLineInfo(CompileUnit *unit, u8 *top) +{ + ELFSectionHeader *h = elfGetSectionByName(".debug_line"); + if (h == NULL) + { + fprintf(stderr, "No line information found\n"); + return; + } + LineInfo *l = unit->lineInfoTable = (LineInfo *)calloc(1, sizeof(LineInfo)); + l->number = 0; + int max = 1000; + l->lines = (LineInfoItem *)malloc(1000*sizeof(LineInfoItem)); + + u8 *data = elfReadSection(top, h); + data += unit->lineInfo; + u32 totalLen = elfRead4Bytes(data); + data += 4; + u8 *end = data + totalLen; + // u16 version = elfRead2Bytes(data); + data += 2; + // u32 offset = elfRead4Bytes(data); + data += 4; + int minInstrSize = *data++; + int defaultIsStmt = *data++; + int lineBase = (s8)*data++; + int lineRange = *data++; + int opcodeBase = *data++; + u8 *stdOpLen = (u8 *)malloc(opcodeBase * sizeof(u8)); + stdOpLen[0] = 1; + int i; + for (i = 1; i < opcodeBase; i++) + stdOpLen[i] = *data++; + + free(stdOpLen); // todo + int bytes = 0; + + char *s; + while ((s = elfReadString(data, &bytes)) != NULL) + { + data += bytes; + // fprintf(stderr, "Directory is %s\n", s); + } + data += bytes; + int count = 4; + int index = 0; + l->files = (char * *)malloc(sizeof(char *)*count); + + while ((s = elfReadString(data, &bytes)) != NULL) + { + l->files[index++] = s; + + data += bytes; + // directory + elfReadLEB128(data, &bytes); + data += bytes; + // time + elfReadLEB128(data, &bytes); + data += bytes; + // size + elfReadLEB128(data, &bytes); + data += bytes; + // fprintf(stderr, "File is %s\n", s); + if (index == count) + { + count += 4; + l->files = (char * *)realloc(l->files, sizeof(char *)*count); + } + } + l->fileCount = index; + data += bytes; + + while (data < end) + { + u32 address = 0; + int file = 1; + int line = 1; + int col = 0; + int isStmt = defaultIsStmt; + int basicBlock = 0; + int endSeq = 0; + + while (!endSeq) + { + int op = *data++; + switch (op) + { + case DW_LNS_extended_op: + { + data++; + op = *data++; + switch (op) + { + case DW_LNE_end_sequence: + endSeq = 1; + break; + case DW_LNE_set_address: + address = elfRead4Bytes(data); + data += 4; + break; + default: + fprintf(stderr, "Unknown extended LINE opcode %02x\n", op); + exit(-1); + } + break; + } + case DW_LNS_copy: + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line, file); + elfAddLine(l, address, file, line, &max); + basicBlock = 0; + break; + case DW_LNS_advance_pc: + address += minInstrSize * elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_advance_line: + line += elfReadSignedLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_file: + file = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_set_column: + col = elfReadLEB128(data, &bytes); + data += bytes; + break; + case DW_LNS_negate_stmt: + isStmt = !isStmt; + break; + case DW_LNS_set_basic_block: + basicBlock = 1; + break; + case DW_LNS_const_add_pc: + address += (minInstrSize *((255 - opcodeBase)/lineRange)); + break; + case DW_LNS_fixed_advance_pc: + address += elfRead2Bytes(data); + data += 2; + break; + default: + op = op - opcodeBase; + address += (op / lineRange) * minInstrSize; + line += lineBase + (op % lineRange); + elfAddLine(l, address, file, line, &max); + // fprintf(stderr, "Address %08x line %d (%d)\n", address, line,file); + basicBlock = 1; + break; + } + } + } + l->lines = (LineInfoItem *)realloc(l->lines, l->number*sizeof(LineInfoItem)); +} + +u8 *elfSkipData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + + for (i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if (abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if (abbrev->hasChildren) + { + int nesting = 1; + while (nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + for (i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if (abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if (abbrev->hasChildren) + { + nesting++; + } + } + } + return data; +} + +Type *elfParseType(CompileUnit *unit, u32); +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object); +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **function); +void elfCleanUp(Function *); + +void elfAddType(Type *type, CompileUnit *unit, u32 offset) +{ + if (type->next == NULL) + { + if (unit->types != type && type->offset == 0) + { + type->offset = offset; + type->next = unit->types; + unit->types = type; + } + } +} + +void elfParseType(u8 *data, u32 offset, ELFAbbrev *abbrev, CompileUnit *unit, + Type **type) +{ + switch (abbrev->tag) + { + case DW_TAG_typedef: + { + u32 typeref = 0; + char *name = NULL; + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + name = attr->string; + break; + case DW_AT_type: + typeref = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown attribute for typedef %02x\n", attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for typedef\n"); + *type = elfParseType(unit, typeref); + if (name) + (*type)->name = name; + return; + break; + } + case DW_TAG_union_type: + case DW_TAG_structure_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + if (abbrev->tag == DW_TAG_structure_type) + t->type = TYPE_struct; + else + t->type = TYPE_union; + + Struct *s = (Struct *)calloc(sizeof(Struct), 1); + t->structure = s; + elfAddType(t, unit, offset); + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_sibling: + case DW_AT_containing_type: // todo? + case DW_AT_declaration: + case DW_AT_specification: // TODO: + break; + default: + fprintf(stderr, "Unknown attribute for struct %02x\n", attr->name); + break; + } + } + if (abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + while (num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch (abbr->tag) + { + case DW_TAG_member: + { + if ((index % 4) == 0) + s->members = (Member *)realloc(s->members, + sizeof(Member)*(index+4)); + Member *m = &s->members[index]; + m->location = NULL; + m->bitOffset = 0; + m->bitSize = 0; + m->byteSize = 0; + for (int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_type: + m->type = elfParseType(unit, attr->value); + break; + case DW_AT_data_member_location: + m->location = attr->block; + break; + case DW_AT_byte_size: + m->byteSize = attr->value; + break; + case DW_AT_bit_offset: + m->bitOffset = attr->value; + break; + case DW_AT_bit_size: + m->bitSize = attr->value; + break; + case DW_AT_decl_file: + case DW_AT_decl_line: + case DW_AT_accessibility: + case DW_AT_artificial: // todo? + break; + default: + fprintf(stderr, "Unknown member attribute %02x\n", + attr->name); + } + } + index++; + break; + } + case DW_TAG_subprogram: + { + Function *fnc = NULL; + data = elfParseFunction(data, abbr, unit, &fnc); + if (fnc != NULL) + { + if (unit->lastFunction) + unit->lastFunction->next = fnc; + else + unit->functions = fnc; + unit->lastFunction = fnc; + } + break; + } + case DW_TAG_inheritance: + // TODO: add support + data = elfSkipData(data, abbr, unit->abbrevs); + break; +CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + case DW_TAG_variable: + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown struct tag %02x %s\n", abbr->tag, t->name); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + s->memberCount = index; + } + *type = t; + return; + break; + } + case DW_TAG_base_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_base; + elfAddType(t, unit, offset); + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_encoding: + t->encoding = attr->value; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_bit_size: + t->bitSize = attr->value; + break; + default: + fprintf(stderr, "Unknown attribute for base type %02x\n", + attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for base type\n"); + *type = t; + return; + break; + } + case DW_TAG_pointer_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_pointer; + + elfAddType(t, unit, offset); + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown pointer type attribute %02x\n", attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for pointer type\n"); + *type = t; + return; + break; + } + case DW_TAG_reference_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + + t->type = TYPE_reference; + + elfAddType(t, unit, offset); + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_type: + t->pointer = elfParseType(unit, attr->value); + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + default: + fprintf(stderr, "Unknown ref type attribute %02x\n", attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for ref type\n"); + *type = t; + return; + break; + } + case DW_TAG_volatile_type: + { + u32 typeref = 0; + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown volatile attribute for type %02x\n", + attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for volatile type\n"); + *type = elfParseType(unit, typeref); + return; + break; + } + case DW_TAG_const_type: + { + u32 typeref = 0; + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_type: + typeref = attr->value; + break; + default: + fprintf(stderr, "Unknown const attribute for type %02x\n", + attr->name); + break; + } + } + if (abbrev->hasChildren) + fprintf(stderr, "Unexpected children for const type\n"); + *type = elfParseType(unit, typeref); + return; + break; + } + case DW_TAG_enumeration_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_enum; + Enum *e = (Enum *)calloc(sizeof(Enum), 1); + t->enumeration = e; + elfAddType(t, unit, offset); + int count = 0; + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + t->name = attr->string; + break; + case DW_AT_byte_size: + t->size = attr->value; + break; + case DW_AT_sibling: + case DW_AT_decl_file: + case DW_AT_decl_line: + break; + default: + fprintf(stderr, "Unknown enum attribute %02x\n", attr->name); + } + } + if (abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + while (num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch (abbr->tag) + { + case DW_TAG_enumerator: + { + count++; + e->members = (EnumMember *)realloc(e->members, + count*sizeof(EnumMember)); + EnumMember *m = &e->members[count-1]; + for (int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_name: + m->name = attr->string; + break; + case DW_AT_const_value: + m->value = attr->value; + break; + default: + fprintf(stderr, "Unknown sub param attribute %02x\n", + attr->name); + } + } + break; + } + default: + fprintf(stderr, "Unknown enum tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + e->count = count; + *type = t; + return; + break; + } + case DW_TAG_subroutine_type: + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_function; + FunctionType *f = (FunctionType *)calloc(sizeof(FunctionType), 1); + t->function = f; + elfAddType(t, unit, offset); + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_prototyped: + case DW_AT_sibling: + break; + case DW_AT_type: + f->returnType = elfParseType(unit, attr->value); + break; + default: + fprintf(stderr, "Unknown subroutine attribute %02x\n", attr->name); + } + } + if (abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastVar = NULL; + while (num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch (abbr->tag) + { + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbr, unit, &o); + if (f->args) + lastVar->next = o; + else + f->args = o; + lastVar = o; + break; + } + case DW_TAG_unspecified_parameters: + // no use in the debugger yet + data = elfSkipData(data, abbr, unit->abbrevs); + break; +CASE_TYPE_TAG: + // skip types... parsed only when used + data = elfSkipData(data, abbr, unit->abbrevs); + break; + default: + fprintf(stderr, "Unknown subroutine tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + } + *type = t; + return; + break; + } + case DW_TAG_array_type: + { + u32 typeref = 0; + int i; + Array *array = (Array *)calloc(sizeof(Array), 1); + Type * t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_array; + elfAddType(t, unit, offset); + + for (i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_type: + typeref = attr->value; + array->type = elfParseType(unit, typeref); + break; + default: + fprintf(stderr, "Unknown array attribute %02x\n", attr->name); + } + } + if (abbrev->hasChildren) + { + int bytes; + u32 num = elfReadLEB128(data, &bytes); + data += bytes; + int index = 0; + int maxBounds = 0; + while (num) + { + ELFAbbrev *abbr = elfGetAbbrev(unit->abbrevs, num); + + switch (abbr->tag) + { + case DW_TAG_subrange_type: + { + if (maxBounds == index) + { + maxBounds += 4; + array->bounds = (int *)realloc(array->bounds, + sizeof(int)*maxBounds); + } + for (int i = 0; i < abbr->numAttrs; i++) + { + ELFAttr *attr = &abbr->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_upper_bound: + array->bounds[index] = attr->value+1; + break; + case DW_AT_type: // ignore + break; + default: + fprintf(stderr, "Unknown subrange attribute %02x\n", + attr->name); + } + } + index++; + break; + } + default: + fprintf(stderr, "Unknown array tag %02x\n", abbr->tag); + data = elfSkipData(data, abbr, unit->abbrevs); + break; + } + num = elfReadLEB128(data, &bytes); + data += bytes; + } + array->maxBounds = index; + } + t->size = array->type->size; + for (i = 0; i < array->maxBounds; i++) + t->size *= array->bounds[i]; + t->array = array; + *type = t; + return; + break; + } + default: + fprintf(stderr, "Unknown type TAG %02x\n", abbrev->tag); + exit(-1); + } +} + +Type *elfParseType(CompileUnit *unit, u32 offset) +{ + Type *t = unit->types; + + while (t) + { + if (t->offset == offset) + return t; + t = t->next; + } + if (offset == 0) + { + Type *t = (Type *)calloc(sizeof(Type), 1); + t->type = TYPE_void; + t->offset = 0; + elfAddType(t, unit, 0); + return t; + } + u8 *data = unit->top + offset; + int bytes; + int abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Type *type = NULL; + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + elfParseType(data, offset, abbrev, unit, &type); + return type; +} + +void elfGetObjectAttributes(CompileUnit *unit, u32 offset, Object *o) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if (o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_const_value: + case DW_AT_abstract_origin: + case DW_AT_declaration: + case DW_AT_artificial: + // todo + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } +} + +u8 *elfParseObject(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Object **object) +{ + Object *o = (Object *)calloc(sizeof(Object), 1); + + o->next = NULL; + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_location: + o->location = attr->block; + break; + case DW_AT_name: + if (o->name == NULL) + o->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + o->name = attr->string; + break; + case DW_AT_decl_file: + o->file = attr->value; + break; + case DW_AT_decl_line: + o->line = attr->value; + break; + case DW_AT_type: + o->type = elfParseType(unit, attr->value); + break; + case DW_AT_external: + o->external = attr->flag; + break; + case DW_AT_abstract_origin: + elfGetObjectAttributes(unit, attr->value, o); + break; + case DW_AT_const_value: + case DW_AT_declaration: + case DW_AT_artificial: + break; + case DW_AT_specification: + // TODO: + break; + default: + fprintf(stderr, "Unknown object attribute %02x\n", attr->name); + break; + } + } + *object = o; + return data; +} + +u8 *elfParseBlock(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function *func, Object **lastVar) +{ + int bytes; + u32 start = func->lowPC; + u32 end = func->highPC; + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_low_pc: + start = attr->value; + break; + case DW_AT_high_pc: + end = attr->value; + break; + case DW_AT_ranges: // ignore for now + break; + default: + fprintf(stderr, "Unknown block attribute %02x\n", attr->name); + break; + } + } + + if (abbrev->hasChildren) + { + int nesting = 1; + + while (nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch (abbrev->tag) + { +CASE_TYPE_TAG: // types only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_lexical_block: + data = elfParseBlock(data, abbrev, unit, func, lastVar); + break; + case DW_TAG_subprogram: + { + Function *f = NULL; + data = elfParseFunction(data, abbrev, unit, &f); + if (f != NULL) + { + if (unit->lastFunction) + unit->lastFunction->next = f; + else + unit->functions = f; + unit->lastFunction = f; + } + break; + } + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if (o->startScope == 0) + o->startScope = start; + if (o->endScope == 0) + o->endScope = 0; + if (func->variables) + (*lastVar)->next = o; + else + func->variables = o; + *lastVar = o; + break; + } + case DW_TAG_inlined_subroutine: + // TODO: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + default: + { + fprintf(stderr, "Unknown block TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + } + } + } + } + return data; +} + +void elfGetFunctionAttributes(CompileUnit *unit, u32 offset, Function *func) +{ + u8 *data = unit->top + offset; + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + return; + } + + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch (attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_name: + if (func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_declaration: + case DW_AT_artificial: + case DW_AT_prototyped: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + return; +} + +u8 *elfParseFunction(u8 *data, ELFAbbrev *abbrev, CompileUnit *unit, + Function **f) +{ + Function *func = (Function *)calloc(sizeof(Function), 1); + *f = func; + + int bytes; + bool mangled = false; + bool declaration = false; + for (int i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + switch (attr->name) + { + case DW_AT_sibling: + break; + case DW_AT_name: + if (func->name == NULL) + func->name = attr->string; + break; + case DW_AT_MIPS_linkage_name: + func->name = attr->string; + mangled = true; + break; + case DW_AT_low_pc: + func->lowPC = attr->value; + break; + case DW_AT_high_pc: + func->highPC = attr->value; + break; + case DW_AT_prototyped: + break; + case DW_AT_decl_file: + func->file = attr->value; + break; + case DW_AT_decl_line: + func->line = attr->value; + break; + case DW_AT_external: + func->external = attr->flag; + break; + case DW_AT_frame_base: + func->frameBase = attr->block; + break; + case DW_AT_type: + func->returnType = elfParseType(unit, attr->value); + break; + case DW_AT_abstract_origin: + elfGetFunctionAttributes(unit, attr->value, func); + break; + case DW_AT_declaration: + declaration = attr->flag; + break; + case DW_AT_inline: + case DW_AT_specification: + case DW_AT_artificial: + case DW_AT_proc_body: + case DW_AT_save_offset: + case DW_AT_user_2002: + case DW_AT_virtuality: + case DW_AT_containing_type: + case DW_AT_accessibility: + // todo; + break; + case DW_AT_vtable_elem_location: + free(attr->block); + break; + default: + fprintf(stderr, "Unknown function attribute %02x\n", attr->name); + break; + } + } + + if (declaration) + { + elfCleanUp(func); + free(func); + *f = NULL; + + while (1) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + return data; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + data = elfSkipData(data, abbrev, unit->abbrevs); + } + } + + if (abbrev->hasChildren) + { + int nesting = 1; + Object *lastParam = NULL; + Object *lastVar = NULL; + + while (nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + + switch (abbrev->tag) + { +CASE_TYPE_TAG: // no need to parse types. only parsed when used + case DW_TAG_label: // not needed + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_subprogram: + { + Function *fnc = NULL; + data = elfParseFunction(data, abbrev, unit, &fnc); + if (fnc != NULL) + { + if (unit->lastFunction == NULL) + unit->functions = fnc; + else + unit->lastFunction->next = fnc; + unit->lastFunction = fnc; + } + break; + } + case DW_TAG_lexical_block: + { + data = elfParseBlock(data, abbrev, unit, func, &lastVar); + break; + } + case DW_TAG_formal_parameter: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if (func->parameters) + lastParam->next = o; + else + func->parameters = o; + lastParam = o; + break; + } + case DW_TAG_variable: + { + Object *o; + data = elfParseObject(data, abbrev, unit, &o); + if (func->variables) + lastVar->next = o; + else + func->variables = o; + lastVar = o; + break; + } + case DW_TAG_unspecified_parameters: + case DW_TAG_inlined_subroutine: + { + // todo + for (int i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if (abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if (abbrev->hasChildren) + nesting++; + break; + } + default: + { + fprintf(stderr, "Unknown function TAG %02x\n", abbrev->tag); + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + } + } + } + } + return data; +} + +u8 *elfParseUnknownData(u8 *data, ELFAbbrev *abbrev, ELFAbbrev **abbrevs) +{ + int i; + int bytes; + // switch(abbrev->tag) { + // default: + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for (i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if (abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if (abbrev->hasChildren) + { + int nesting = 1; + while (nesting) + { + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + if (!abbrevNum) + { + nesting--; + continue; + } + + abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + fprintf(stderr, "Unknown TAG %02x\n", abbrev->tag); + + for (i = 0; i < abbrev->numAttrs; i++) + { + data = elfReadAttribute(data, &abbrev->attrs[i]); + if (abbrev->attrs[i].form == DW_FORM_block1) + free(abbrev->attrs[i].block); + } + + if (abbrev->hasChildren) + { + nesting++; + } + } + } + // } + return data; +} + +u8 *elfParseCompileUnitChildren(u8 *data, CompileUnit *unit) +{ + int bytes; + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + Object *lastObj = NULL; + while (abbrevNum) + { + ELFAbbrev *abbrev = elfGetAbbrev(unit->abbrevs, abbrevNum); + switch (abbrev->tag) + { + case DW_TAG_subprogram: + { + Function *func = NULL; + data = elfParseFunction(data, abbrev, unit, &func); + if (func != NULL) + { + if (unit->lastFunction) + unit->lastFunction->next = func; + else + unit->functions = func; + unit->lastFunction = func; + } + break; + } +CASE_TYPE_TAG: + data = elfSkipData(data, abbrev, unit->abbrevs); + break; + case DW_TAG_variable: + { + Object *var = NULL; + data = elfParseObject(data, abbrev, unit, &var); + if (lastObj) + lastObj->next = var; + else + unit->variables = var; + lastObj = var; + break; + } + default: + data = elfParseUnknownData(data, abbrev, unit->abbrevs); + break; + } + + abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + } + return data; +} + +CompileUnit *elfParseCompUnit(u8 *data, u8 *abbrevData) +{ + int bytes; + u8 *top = data; + + u32 length = elfRead4Bytes(data); + data += 4; + + u16 version = elfRead2Bytes(data); + data += 2; + + u32 offset = elfRead4Bytes(data); + data += 4; + + u8 addrSize = *data++; + + if (version != 2) + { + fprintf(stderr, "Unsupported debugging information version %d\n", version); + return NULL; + } + + if (addrSize != 4) + { + fprintf(stderr, "Unsupported address size %d\n", addrSize); + return NULL; + } + + ELFAbbrev **abbrevs = elfReadAbbrevs(abbrevData, offset); + + u32 abbrevNum = elfReadLEB128(data, &bytes); + data += bytes; + + ELFAbbrev *abbrev = elfGetAbbrev(abbrevs, abbrevNum); + + CompileUnit *unit = (CompileUnit *)calloc(sizeof(CompileUnit), 1); + unit->top = top; + unit->length = length; + unit->abbrevs = abbrevs; + unit->next = NULL; + + elfCurrentUnit = unit; + + int i; + + for (i = 0; i < abbrev->numAttrs; i++) + { + ELFAttr *attr = &abbrev->attrs[i]; + data = elfReadAttribute(data, attr); + + switch (attr->name) + { + case DW_AT_name: + unit->name = attr->string; + break; + case DW_AT_stmt_list: + unit->hasLineInfo = true; + unit->lineInfo = attr->value; + break; + case DW_AT_low_pc: + unit->lowPC = attr->value; + break; + case DW_AT_high_pc: + unit->highPC = attr->value; + break; + case DW_AT_compdir: + unit->compdir = attr->string; + break; + // ignore + case DW_AT_language: + case DW_AT_producer: + case DW_AT_macro_info: + case DW_AT_entry_pc: + break; + default: + fprintf(stderr, "Unknown attribute %02x\n", attr->name); + break; + } + } + + if (abbrev->hasChildren) + elfParseCompileUnitChildren(data, unit); + + return unit; +} + +void elfParseAranges(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".debug_aranges"); + if (sh == NULL) + { + fprintf(stderr, "No aranges found\n"); + return; + } + + data = elfReadSection(data, sh); + u8 *end = data + READ32LE(&sh->size); + + int max = 4; + ARanges *ranges = (ARanges *)calloc(sizeof(ARanges), 4); + + int index = 0; + + while (data < end) + { + u32 len = elfRead4Bytes(data); + data += 4; + // u16 version = elfRead2Bytes(data); + data += 2; + u32 offset = elfRead4Bytes(data); + data += 4; + // u8 addrSize = *data++; + // u8 segSize = *data++; + data += 2; // remove if uncommenting above + data += 4; + ranges[index].count = (len-20)/8; + ranges[index].offset = offset; + ranges[index].ranges = (ARange *)calloc(sizeof(ARange), (len-20)/8); + int i = 0; + while (true) + { + u32 addr = elfRead4Bytes(data); + data += 4; + u32 len = elfRead4Bytes(data); + data += 4; + if (addr == 0 && len == 0) + break; + ranges[index].ranges[i].lowPC = addr; + ranges[index].ranges[i].highPC = addr+len; + i++; + } + index++; + if (index == max) + { + max += 4; + ranges = (ARanges *)realloc(ranges, max*sizeof(ARanges)); + } + } + elfDebugInfo->numRanges = index; + elfDebugInfo->ranges = ranges; +} + +void elfReadSymtab(u8 *data) +{ + ELFSectionHeader *sh = elfGetSectionByName(".symtab"); + int table = READ32LE(&sh->link); + + char *strtable = (char *)elfReadSection(data, elfGetSectionByNumber(table)); + + ELFSymbol *symtab = (ELFSymbol *)elfReadSection(data, sh); + + int count = READ32LE(&sh->size) / sizeof(ELFSymbol); + elfSymbolsCount = 0; + + elfSymbols = (Symbol *)malloc(sizeof(Symbol)*count); + + int i; + + for (i = 0; i < count; i++) + { + ELFSymbol *s = &symtab[i]; + int type = s->info & 15; + int binding = s->info >> 4; + + if (binding) + { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = binding; + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + for (i = 0; i < count; i++) + { + ELFSymbol *s = &symtab[i]; + int bind = s->info>>4; + int type = s->info & 15; + + if (!bind) + { + Symbol *sym = &elfSymbols[elfSymbolsCount]; + sym->name = &strtable[READ32LE(&s->name)]; + sym->binding = (s->info >> 4); + sym->type = type; + sym->value = READ32LE(&s->value); + sym->size = READ32LE(&s->size); + elfSymbolsCount++; + } + } + elfSymbolsStrTab = strtable; + // free(symtab); +} + +bool elfReadProgram(ELFHeader *eh, u8 *data, int& size, bool parseDebug) +{ + int count = READ16LE(&eh->e_phnum); + int i; + + if (READ32LE(&eh->e_entry) == 0x2000000) + cpuIsMultiBoot = true; + + // read program headers... should probably move this code down + u8 *p = data + READ32LE(&eh->e_phoff); + size = 0; + for (i = 0; i < count; i++) + { + ELFProgramHeader *ph = (ELFProgramHeader *)p; + p += sizeof(ELFProgramHeader); + if (READ16LE(&eh->e_phentsize) != sizeof(ELFProgramHeader)) + { + p += READ16LE(&eh->e_phentsize) - sizeof(ELFProgramHeader); + } + + // printf("PH %d %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, ph->type, ph->offset, ph->vaddr, ph->paddr, + // ph->filesz, ph->memsz, ph->flags, ph->align); + if (cpuIsMultiBoot) + { + if (READ32LE(&ph->paddr) >= 0x2000000 && + READ32LE(&ph->paddr) <= 0x203ffff) + { + memcpy(&workRAM[READ32LE(&ph->paddr) & 0x3ffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + } + } + else + { + if (READ32LE(&ph->paddr) >= 0x8000000 && + READ32LE(&ph->paddr) <= 0x9ffffff) + { + memcpy(&rom[READ32LE(&ph->paddr) & 0x1ffffff], + data + READ32LE(&ph->offset), + READ32LE(&ph->filesz)); + size += READ32LE(&ph->filesz); + } + } + } + + char *stringTable = NULL; + + // read section headers + p = data + READ32LE(&eh->e_shoff); + count = READ16LE(&eh->e_shnum); + + ELFSectionHeader **sh = (ELFSectionHeader * *) + malloc(sizeof(ELFSectionHeader *) * count); + + for (i = 0; i < count; i++) + { + sh[i] = (ELFSectionHeader *)p; + p += sizeof(ELFSectionHeader); + if (READ16LE(&eh->e_shentsize) != sizeof(ELFSectionHeader)) + p += READ16LE(&eh->e_shentsize) - sizeof(ELFSectionHeader); + } + + if (READ16LE(&eh->e_shstrndx) != 0) + { + stringTable = (char *)elfReadSection(data, + sh[READ16LE(&eh->e_shstrndx)]); + } + + elfSectionHeaders = sh; + elfSectionHeadersStringTable = stringTable; + elfSectionHeadersCount = count; + + for (i = 0; i < count; i++) + { + // printf("SH %d %-20s %08x %08x %08x %08x %08x %08x %08x %08x\n", + // i, &stringTable[sh[i]->name], sh[i]->name, sh[i]->type, + // sh[i]->flags, sh[i]->addr, sh[i]->offset, sh[i]->size, + // sh[i]->link, sh[i]->info); + if (READ32LE(&sh[i]->flags) & 2) // load section + { + if (cpuIsMultiBoot) + { + if (READ32LE(&sh[i]->addr) >= 0x2000000 && + READ32LE(&sh[i]->addr) <= 0x203ffff) + { + memcpy(&workRAM[READ32LE(&sh[i]->addr) & 0x3ffff], data + + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + } + } + else + { + if (READ32LE(&sh[i]->addr) >= 0x8000000 && + READ32LE(&sh[i]->addr) <= 0x9ffffff) + { + memcpy(&rom[READ32LE(&sh[i]->addr) & 0x1ffffff], + data + READ32LE(&sh[i]->offset), + READ32LE(&sh[i]->size)); + size += READ32LE(&sh[i]->size); + } + } + } + } + + if (parseDebug) + { + fprintf(stderr, "Parsing debug info\n"); + + ELFSectionHeader *dbgHeader = elfGetSectionByName(".debug_info"); + if (dbgHeader == NULL) + { + fprintf(stderr, "Cannot find debug information\n"); + goto end; + } + + ELFSectionHeader *h = elfGetSectionByName(".debug_abbrev"); + if (h == NULL) + { + fprintf(stderr, "Cannot find abbreviation table\n"); + goto end; + } + + elfDebugInfo = (DebugInfo *)calloc(sizeof(DebugInfo), 1); + u8 *abbrevdata = elfReadSection(data, h); + + h = elfGetSectionByName(".debug_str"); + + if (h == NULL) + elfDebugStrings = NULL; + else + elfDebugStrings = (char *)elfReadSection(data, h); + + u8 *debugdata = elfReadSection(data, dbgHeader); + + elfDebugInfo->debugdata = data; + elfDebugInfo->infodata = debugdata; + + u32 total = READ32LE(&dbgHeader->size); + u8 *end = debugdata + total; + u8 *ddata = debugdata; + + CompileUnit *last = NULL; + CompileUnit *unit = NULL; + + while (ddata < end) + { + unit = elfParseCompUnit(ddata, abbrevdata); + unit->offset = ddata-debugdata; + elfParseLineInfo(unit, data); + if (last == NULL) + elfCompileUnits = unit; + else + last->next = unit; + last = unit; + ddata += 4 + unit->length; + } + elfParseAranges(data); + CompileUnit *comp = elfCompileUnits; + while (comp) + { + ARanges *r = elfDebugInfo->ranges; + for (int i = 0; i < elfDebugInfo->numRanges; i++) + if (r[i].offset == comp->offset) + { + comp->ranges = &r[i]; + break; + } + comp = comp->next; + } + elfParseCFA(data); + elfReadSymtab(data); + } +end: + if (sh) + { + free(sh); + } + + elfSectionHeaders = NULL; + elfSectionHeadersStringTable = NULL; + elfSectionHeadersCount = 0; + + return true; +} + +extern bool8 parseDebug; + +bool elfRead(const char *name, int& siz, FILE *f) +{ + fseek(f, 0, SEEK_END); + long size = ftell(f); + elfFileData = (u8 *)malloc(size); + fseek(f, 0, SEEK_SET); + fread(elfFileData, 1, size, f); + fclose(f); + + ELFHeader *header = (ELFHeader *)elfFileData; + + if (READ32LE(&header->magic) != 0x464C457F || + READ16LE(&header->e_machine) != 40 || + header->clazz != 1) + { + systemMessage(0, N_("Not a valid ELF file %s"), name); + free(elfFileData); + elfFileData = NULL; + return false; + } + + if (!elfReadProgram(header, elfFileData, siz, parseDebug)) + { + free(elfFileData); + elfFileData = NULL; + return false; + } + + return true; +} + +void elfCleanUp(Object *o) +{ + free(o->location); +} + +void elfCleanUp(Function *func) +{ + Object *o = func->parameters; + while (o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + + o = func->variables; + while (o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(func->frameBase); +} + +void elfCleanUp(ELFAbbrev **abbrevs) +{ + for (int i = 0; i < 121; i++) + { + ELFAbbrev *abbrev = abbrevs[i]; + + while (abbrev) + { + free(abbrev->attrs); + ELFAbbrev *next = abbrev->next; + free(abbrev); + + abbrev = next; + } + } +} + +void elfCleanUp(Type *t) +{ + switch (t->type) + { + case TYPE_function: + if (t->function) + { + Object *o = t->function->args; + while (o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + free(t->function); + } + break; + case TYPE_array: + if (t->array) + { + free(t->array->bounds); + free(t->array); + } + break; + case TYPE_struct: + case TYPE_union: + if (t->structure) + { + for (int i = 0; i < t->structure->memberCount; i++) + { + free(t->structure->members[i].location); + } + free(t->structure->members); + free(t->structure); + } + break; + case TYPE_enum: + if (t->enumeration) + { + free(t->enumeration->members); + free(t->enumeration); + } + break; + case TYPE_base: + case TYPE_pointer: + case TYPE_void: + case TYPE_reference: + break; // nothing to do + } +} + +void elfCleanUp(CompileUnit *comp) +{ + elfCleanUp(comp->abbrevs); + free(comp->abbrevs); + Function *func = comp->functions; + while (func) + { + elfCleanUp(func); + Function *next = func->next; + free(func); + func = next; + } + Type *t = comp->types; + while (t) + { + elfCleanUp(t); + Type *next = t->next; + free(t); + t = next; + } + Object *o = comp->variables; + while (o) + { + elfCleanUp(o); + Object *next = o->next; + free(o); + o = next; + } + if (comp->lineInfoTable) + { + free(comp->lineInfoTable->lines); + free(comp->lineInfoTable->files); + free(comp->lineInfoTable); + } +} + +void elfCleanUp() +{ + CompileUnit *comp = elfCompileUnits; + + while (comp) + { + elfCleanUp(comp); + CompileUnit *next = comp->next; + free(comp); + comp = next; + } + elfCompileUnits = NULL; + free(elfSymbols); + elfSymbols = NULL; + // free(elfSymbolsStrTab); + elfSymbolsStrTab = NULL; + + elfDebugStrings = NULL; + if (elfDebugInfo) + { + int num = elfDebugInfo->numRanges; + int i; + for (i = 0; i < num; i++) + { + free(elfDebugInfo->ranges[i].ranges); + } + free(elfDebugInfo->ranges); + free(elfDebugInfo); + elfDebugInfo = NULL; + } + + if (elfFdes) + { + if (elfFdeCount) + { + for (int i = 0; i < elfFdeCount; i++) + free(elfFdes[i]); + } + free(elfFdes); + + elfFdes = NULL; + elfFdeCount = 0; + } + + ELFcie *cie = elfCies; + while (cie) + { + ELFcie *next = cie->next; + free(cie); + cie = next; + } + elfCies = NULL; + + if (elfFileData) + { + free(elfFileData); + elfFileData = NULL; + } +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/elf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/elf.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,296 @@ +#ifndef VBA_ELF_H +#define VBA_ELF_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +enum LocationType +{ + LOCATION_register, + LOCATION_memory, + LOCATION_value +}; + +#define DW_ATE_boolean 0x02 +#define DW_ATE_signed 0x05 +#define DW_ATE_unsigned 0x07 +#define DW_ATE_unsigned_char 0x08 + +struct ELFHeader +{ + u32 magic; + u8 clazz; + u8 data; + u8 version; + u8 pad[9]; + u16 e_type; + u16 e_machine; + u32 e_version; + u32 e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shstrndx; +}; + +struct ELFProgramHeader +{ + u32 type; + u32 offset; + u32 vaddr; + u32 paddr; + u32 filesz; + u32 memsz; + u32 flags; + u32 align; +}; + +struct ELFSectionHeader +{ + u32 name; + u32 type; + u32 flags; + u32 addr; + u32 offset; + u32 size; + u32 link; + u32 info; + u32 addralign; + u32 entsize; +}; + +struct ELFSymbol +{ + u32 name; + u32 value; + u32 size; + u8 info; + u8 other; + u16 shndx; +}; + +struct ELFBlock +{ + int length; + u8 *data; +}; + +struct ELFAttr +{ + u32 name; + u32 form; + union + { + u32 value; + char * string; + u8 * data; + bool flag; + ELFBlock *block; + }; +}; + +struct ELFAbbrev +{ + u32 number; + u32 tag; + bool hasChildren; + int numAttrs; + ELFAttr * attrs; + ELFAbbrev *next; +}; + +enum TypeEnum +{ + TYPE_base, + TYPE_pointer, + TYPE_function, + TYPE_void, + TYPE_array, + TYPE_struct, + TYPE_reference, + TYPE_enum, + TYPE_union +}; + +struct Type; +struct Object; + +struct FunctionType +{ + Type * returnType; + Object *args; +}; + +struct Member +{ + char * name; + Type * type; + int bitSize; + int bitOffset; + int byteSize; + ELFBlock *location; +}; + +struct Struct +{ + int memberCount; + Member *members; +}; + +struct Array +{ + Type *type; + int maxBounds; + int * bounds; +}; + +struct EnumMember +{ + char *name; + u32 value; +}; + +struct Enum +{ + int count; + EnumMember *members; +}; + +struct Type +{ + u32 offset; + TypeEnum type; + char * name; + int encoding; + int size; + int bitSize; + union + { + Type * pointer; + FunctionType *function; + Array * array; + Struct * structure; + Enum * enumeration; + }; + Type *next; +}; + +struct Object +{ + char * name; + int file; + int line; + bool external; + Type * type; + ELFBlock *location; + u32 startScope; + u32 endScope; + Object * next; +}; + +struct Function +{ + char * name; + u32 lowPC; + u32 highPC; + int file; + int line; + bool external; + Type * returnType; + Object * parameters; + Object * variables; + ELFBlock *frameBase; + Function *next; +}; + +struct LineInfoItem +{ + u32 address; + char *file; + int line; +}; + +struct LineInfo +{ + int fileCount; + char ** files; + int number; + LineInfoItem *lines; +}; + +struct ARange +{ + u32 lowPC; + u32 highPC; +}; + +struct ARanges +{ + u32 offset; + int count; + ARange *ranges; +}; + +struct CompileUnit +{ + u32 length; + u8 * top; + u32 offset; + ELFAbbrev ** abbrevs; + ARanges * ranges; + char * name; + char * compdir; + u32 lowPC; + u32 highPC; + bool hasLineInfo; + u32 lineInfo; + LineInfo * lineInfoTable; + Function * functions; + Function * lastFunction; + Object * variables; + Type * types; + CompileUnit *next; +}; + +struct DebugInfo +{ + u8 * debugfile; + u8 * abbrevdata; + u8 * debugdata; + u8 * infodata; + int numRanges; + ARanges *ranges; +}; + +struct Symbol +{ + char *name; + int type; + int binding; + u32 address; + u32 value; + u32 size; +}; + +extern u32 elfReadLEB128(u8 *, int *); +extern s32 elfReadSignedLEB128(u8 *, int *); +extern bool elfRead(const char *, int &, FILE *f); +extern bool elfGetSymbolAddress(char *, u32 *, u32 *, int *); +extern char *elfGetAddressSymbol(u32); +extern char *elfGetSymbol(int, u32 *, u32 *, int *); +extern void elfCleanUp(); +extern bool elfGetCurrentFunction(u32, Function **, CompileUnit **c); +extern bool elfGetObject(char *, Function *, CompileUnit *, Object * *); +extern bool elfFindLineInUnit(u32 *, CompileUnit *, int); +extern bool elfFindLineInModule(u32 *, char *, int); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *); +u32 elfDecodeLocation(Function *, ELFBlock *, LocationType *, u32); +int elfFindLine(CompileUnit *unit, Function *func, u32 addr, char * *); + +#endif // VBA_ELF_H diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/remote.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/remote.cpp Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,712 @@ +#include +#include +#include + +#ifndef WIN32 +# include +# include +# include +# ifdef HAVE_NETINET_IN_H +# include +# endif // HAVE_NETINET_IN_H +# ifdef HAVE_ARPA_INET_H +# include +# else // ! HAVE_ARPA_INET_H +# define socklen_t int +# endif // ! HAVE_ARPA_INET_H +#else // WIN32 +# include "../win32/stdafx.h" +# include +# include +# define socklen_t int +# define close closesocket +# define read _read +# define write _write +#endif // WIN32 + +#include "GBA.h" +#include "GBAGlobals.h" + +extern bool debugger; +extern void CPUUpdateCPSR(); +#ifdef SDL +extern void (*dbgMain)(); +extern void (*dbgSignal)(int, int); +extern void debuggerMain(); +extern void debuggerSignal(int, int); +#endif + +int remotePort = 55555; +int remoteSignal = 5; +int remoteSocket = -1; +int remoteListenSocket = -1; +bool remoteConnected = false; +bool remoteResumed = false; + +int (*remoteSendFnc)(char *, int) = NULL; +int (*remoteRecvFnc)(char *, int) = NULL; +bool (*remoteInitFnc)() = NULL; +void (*remoteCleanUpFnc)() = NULL; + +#if (defined WIN32 && !defined SDL) +void remoteSetSockets(SOCKET l, SOCKET r) +{ + remoteSocket = r; + remoteListenSocket = l; +} + +#endif + +int remoteTcpSend(char *data, int len) +{ + return send(remoteSocket, data, len, 0); +} + +int remoteTcpRecv(char *data, int len) +{ + return recv(remoteSocket, data, len, 0); +} + +bool remoteTcpInit() +{ + if (remoteSocket == -1) + { +#ifdef WIN32 + WSADATA wsaData; + int error = WSAStartup(MAKEWORD(1, 1), &wsaData); +#endif // WIN32 + int s = socket(PF_INET, SOCK_STREAM, 0); + + remoteListenSocket = s; + + if (s < 0) + { + fprintf(stderr, "Error opening socket\n"); + exit(-1); + } + int tmp = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof(tmp)); + + // char hostname[256]; + // gethostname(hostname, 256); + + // hostent *ent = gethostbyname(hostname); + // unsigned long a = *((unsigned long *)ent->h_addr); + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(remotePort); + addr.sin_addr.s_addr = htonl(0); + int count = 0; + while (count < 3) + { + if (bind(s, (sockaddr *)&addr, sizeof(addr))) + { + addr.sin_port = htons(ntohs(addr.sin_port)+1); + } + else + break; + } + if (count == 3) + { + fprintf(stderr, "Error binding \n"); + exit(-1); + } + + fprintf(stderr, "Listening for a connection at port %d\n", + ntohs(addr.sin_port)); + + if (listen(s, 1)) + { + fprintf(stderr, "Error listening\n"); + exit(-1); + } + socklen_t len = sizeof(addr); + +#ifdef WIN32 + int flag = 0; + ioctlsocket(s, FIONBIO, (unsigned long *)&flag); +#endif // WIN32 + int s2 = accept(s, (sockaddr *)&addr, &len); + if (s2 > 0) + { + fprintf(stderr, "Got a connection from %s %d\n", + inet_ntoa((in_addr)addr.sin_addr), + ntohs(addr.sin_port)); + } + else + { +#ifdef WIN32 + int error = WSAGetLastError(); +#endif // WIN32 + } + char dummy; + recv(s2, &dummy, 1, 0); + if (dummy != '+') + { + fprintf(stderr, "ACK not received\n"); + exit(-1); + } + remoteSocket = s2; + // close(s); + } + return true; +} + +void remoteTcpCleanUp() +{ + if (remoteSocket > 0) + { + fprintf(stderr, "Closing remote socket\n"); + close(remoteSocket); + remoteSocket = -1; + } + if (remoteListenSocket > 0) + { + fprintf(stderr, "Closing listen socket\n"); + close(remoteListenSocket); + remoteListenSocket = -1; + } +} + +int remotePipeSend(char *data, int len) +{ + int res = write(1, data, len); + return res; +} + +int remotePipeRecv(char *data, int len) +{ + int res = read(0, data, len); + return res; +} + +bool remotePipeInit() +{ + char dummy; + read(0, &dummy, 1); + if (dummy != '+') + { + fprintf(stderr, "ACK not received\n"); + exit(-1); + } + + return true; +} + +void remotePipeCleanUp() +{} + +void remoteSetPort(int port) +{ + remotePort = port; +} + +void remoteSetProtocol(int p) +{ + if (p == 0) + { + remoteSendFnc = remoteTcpSend; + remoteRecvFnc = remoteTcpRecv; + remoteInitFnc = remoteTcpInit; + remoteCleanUpFnc = remoteTcpCleanUp; + } + else + { + remoteSendFnc = remotePipeSend; + remoteRecvFnc = remotePipeRecv; + remoteInitFnc = remotePipeInit; + remoteCleanUpFnc = remotePipeCleanUp; + } +} + +void remoteInit() +{ + if (remoteInitFnc) + remoteInitFnc(); +} + +void remotePutPacket(char *packet) +{ + char *hex = "0123456789abcdef"; + char buffer[1024]; + + int count = strlen(packet); + + unsigned char csum = 0; + + char *p = buffer; + *p++ = '$'; + + for (int i = 0; i < count; i++) + { + csum += packet[i]; + *p++ = packet[i]; + } + *p++ = '#'; + *p++ = hex[csum>>4]; + *p++ = hex[csum & 15]; + *p++ = 0; + // printf("Sending %s\n", buffer); + remoteSendFnc(buffer, count + 4); + + char c = 0; + remoteRecvFnc(&c, 1); + /* + if(c == '+') + printf("ACK\n"); + else if(c=='-') + printf("NACK\n"); + */ +} + +void remoteOutput(char *s, u32 addr) +{ + char buffer[16384]; + + char *d = buffer; + *d++ = 'O'; + + if (s) + { + char c = *s++; + while (c) + { + sprintf(d, "%02x", c); + d += 2; + c = *s++; + } + } + else + { + char c = debuggerReadByte(addr); + addr++; + while (c) + { + sprintf(d, "%02x", c); + d += 2; + c = debuggerReadByte(addr); + addr++; + } + } + remotePutPacket(buffer); + // fprintf(stderr, "Output sent %s\n", buffer); +} + +void remoteSendSignal() +{ + char buffer[1024]; + sprintf(buffer, "S%02x", remoteSignal); + remotePutPacket(buffer); +} + +void remoteSendStatus() +{ + char buffer[1024]; + sprintf(buffer, "T%02x", remoteSignal); + char *s = buffer; + s += 3; + for (int i = 0; i < 15; i++) + { + u32 v = reg[i].I; + sprintf(s, "%02x:%02x%02x%02x%02x;", i, + (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + } + u32 v = armNextPC; + sprintf(s, "0f:%02x%02x%02x%02x;", (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + CPUUpdateCPSR(); + v = reg[16].I; + sprintf(s, "19:%02x%02x%02x%02x;", (v & 255), + (v >> 8) & 255, + (v >> 16) & 255, + (v >> 24) & 255); + s += 12; + *s = 0; + // printf("Sending %s\n", buffer); + remotePutPacket(buffer); +} + +void remoteBinaryWrite(char *p) +{ + u32 address; + int count; + sscanf(p, "%x,%x:", &address, &count); + // printf("Binary write for %08x %d\n", address, count); + + p = strchr(p, ':'); + p++; + for (int i = 0; i < count; i++) + { + u8 b = *p++; + switch (b) + { + case 0x7d: + b = *p++; + debuggerWriteByte(address, (b^0x20)); + address++; + break; + default: + debuggerWriteByte(address, b); + address++; + break; + } + } + // printf("ROM is %08x\n", debuggerReadMemory(0x8000254)); + remotePutPacket("OK"); +} + +void remoteMemoryWrite(char *p) +{ + u32 address; + int count; + sscanf(p, "%x,%x:", &address, &count); + // printf("Memory write for %08x %d\n", address, count); + + p = strchr(p, ':'); + p++; + for (int i = 0; i < count; i++) + { + u8 v = 0; + char c = *p++; + if (c <= '9') + v = (c - '0') << 4; + else + v = (c + 10 - 'a') << 4; + c = *p++; + if (c <= '9') + v += (c - '0'); + else + v += (c + 10 - 'a'); + debuggerWriteByte(address, v); + address++; + } + // printf("ROM is %08x\n", debuggerReadMemory(0x8000254)); + remotePutPacket("OK"); +} + +void remoteMemoryRead(char *p) +{ + u32 address; + int count; + sscanf(p, "%x,%x:", &address, &count); + // printf("Memory read for %08x %d\n", address, count); + + char buffer[1024]; + + char *s = buffer; + for (int i = 0; i < count; i++) + { + u8 b = debuggerReadByte(address); + sprintf(s, "%02x", b); + address++; + s += 2; + } + *s = 0; + remotePutPacket(buffer); +} + +void remoteStepOverRange(char *p) +{ + u32 address; + u32 final; + sscanf(p, "%x,%x", &address, &final); + + remotePutPacket("OK"); + + remoteResumed = true; + do + { + CPULoop(1); + if (debugger) + break; + } + while (armNextPC >= address && armNextPC < final); + + remoteResumed = false; + + remoteSendStatus(); +} + +void remoteWriteWatch(char *p, bool active) +{ + u32 address; + int count; + sscanf(p, ",%x,%x#", &address, &count); + + fprintf(stderr, "Write watch for %08x %d\n", address, count); + + if (address < 0x2000000 || address > 0x3007fff) + { + remotePutPacket("E01"); + return; + } + + if (address > 0x203ffff && address < 0x3000000) + { + remotePutPacket("E01"); + return; + } + + u32 final = address + count; + + if (address < 0x2040000 && final > 0x2040000) + { + remotePutPacket("E01"); + return; + } + else if (address < 0x3008000 && final > 0x3008000) + { + remotePutPacket("E01"); + return; + } + + for (int i = 0; i < count; i++) + { + if ((address >> 24) == 2) + freezeWorkRAM[address & 0x3ffff] = active; + else + freezeInternalRAM[address & 0x7fff] = active; + address++; + } + + remotePutPacket("OK"); +} + +void remoteReadRegisters(char *p) +{ + char buffer[1024]; + + char *s = buffer; + int i; + // regular registers + for (i = 0; i < 15; i++) + { + u32 v = reg[i].I; + sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + (v >> 16) & 255, (v >> 24) & 255); + s += 8; + } + // PC + u32 pc = armNextPC; + sprintf(s, "%02x%02x%02x%02x", pc & 255, (pc >> 8) & 255, + (pc >> 16) & 255, (pc >> 24) & 255); + s += 8; + + // floating point registers (24-bit) + for (i = 0; i < 8; i++) + { + sprintf(s, "000000000000000000000000"); + s += 24; + } + + // FP status register + sprintf(s, "00000000"); + s += 8; + // CPSR + CPUUpdateCPSR(); + u32 v = reg[16].I; + sprintf(s, "%02x%02x%02x%02x", v & 255, (v >> 8) & 255, + (v >> 16) & 255, (v >> 24) & 255); + s += 8; + *s = 0; + remotePutPacket(buffer); +} + +void remoteWriteRegister(char *p) +{ + int r; + + sscanf(p, "%x=", &r); + + p = strchr(p, '='); + p++; + + char c = *p++; + + u32 v = 0; + + u8 data[4] = {0, 0, 0, 0}; + + int i = 0; + + while (c != '#') + { + u8 b = 0; + if (c <= '9') + b = (c - '0') << 4; + else + b = (c + 10 - 'a') << 4; + c = *p++; + if (c <= '9') + b += (c - '0'); + else + b += (c + 10 - 'a'); + data[i++] = b; + c = *p++; + } + + v = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + + // printf("Write register %d=%08x\n", r, v); + reg[r].I = v; + if (r == 15) + { + armNextPC = v; + if (armState) + reg[15].I = v + 4; + else + reg[15].I = v + 2; + } + remotePutPacket("OK"); +} + +void remoteStubMain() +{ + if (!debugger) + return; + + if (remoteResumed) + { + remoteSendStatus(); + remoteResumed = false; + } + + while (true) + { + char buffer[1024]; + int res = remoteRecvFnc(buffer, 1024); + + if (res == -1) + { + fprintf(stderr, "GDB connection lost\n"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + debugger = false; + break; + } + + // fprintf(stderr, "Received %s\n", buffer); + char *p = buffer; + char c = *p++; + char pp = '+'; + remoteSendFnc(&pp, 1); + + if (c != '$') + continue; + c = *p++; + switch (c) + { + case '?': + remoteSendSignal(); + break; + case 'D': + remotePutPacket("OK"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + remoteResumed = true; + debugger = false; + return; + case 'e': + remoteStepOverRange(p); + break; + case 'k': + remotePutPacket("OK"); +#ifdef SDL + dbgMain = debuggerMain; + dbgSignal = debuggerSignal; +#endif + debugger = false; + emulating = false; + return; + case 'C': + remoteResumed = true; + debugger = false; + return; + case 'c': + remoteResumed = true; + debugger = false; + return; + case 's': + remoteResumed = true; + remoteSignal = 5; + CPULoop(1); + if (remoteResumed) + { + remoteResumed = false; + remoteSendStatus(); + } + break; + case 'g': + remoteReadRegisters(p); + break; + case 'P': + remoteWriteRegister(p); + break; + case 'M': + remoteMemoryWrite(p); + break; + case 'm': + remoteMemoryRead(p); + break; + case 'X': + remoteBinaryWrite(p); + break; + case 'H': + remotePutPacket("OK"); + break; + case 'q': + remotePutPacket(""); + break; + case 'Z': + if (*p++ == '2') + { + remoteWriteWatch(p, true); + } + else + remotePutPacket(""); + break; + case 'z': + if (*p++ == '2') + { + remoteWriteWatch(p, false); + } + else + remotePutPacket(""); + break; + default: + { + *(strchr(p, '#') + 3) = 0; + fprintf(stderr, "Unknown packet %s\n", --p); + remotePutPacket(""); + break; + } + } + } +} + +void remoteStubSignal(int sig, int number) +{ + remoteSignal = sig; + remoteResumed = false; + remoteSendStatus(); + debugger = true; +} + +void remoteCleanUp() +{ + if (remoteCleanUpFnc) + remoteCleanUpFnc(); +} + diff -r ac56489c2ca6 -r 5e8e5083da94 src/gba/thumb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gba/thumb.h Sun Mar 04 14:33:52 2012 -0600 @@ -0,0 +1,2524 @@ +#ifdef C_CORE +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) +#define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) | \ + (NEG(a) & POS(c)) | \ + (NEG(b) & POS(c))) ? true : false; +#define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) | \ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) | \ + (NEG(a) & POS(c)) | \ + (POS(b) & POS(c))) ? true : false; +#define SUBOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) | \ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#define ADD_RD_RS_RN \ + { \ + u32 lhs = reg[source].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define ADD_RD_RS_O3 \ + { \ + u32 lhs = reg[source].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define ADD_RN_O8(d) \ + { \ + u32 lhs = reg[(d)].I; \ + u32 rhs = (opcode & 255); \ + u32 res = lhs + rhs; \ + reg[(d)].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define CMN_RD_RS \ + { \ + u32 lhs = reg[dest].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define ADC_RD_RS \ + { \ + u32 lhs = reg[dest].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + ADDCARRY(lhs, rhs, res); \ + ADDOVERFLOW(lhs, rhs, res); \ + } +#define SUB_RD_RS_RN \ + { \ + u32 lhs = reg[source].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define SUB_RD_RS_O3 \ + { \ + u32 lhs = reg[source].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define SUB_RN_O8(d) \ + { \ + u32 lhs = reg[(d)].I; \ + u32 rhs = (opcode & 255); \ + u32 res = lhs - rhs; \ + reg[(d)].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define CMP_RN_O8(d) \ + { \ + u32 lhs = reg[(d)].I; \ + u32 rhs = (opcode & 255); \ + u32 res = lhs - rhs; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define SBC_RD_RS \ + { \ + u32 lhs = reg[dest].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#define LSL_RD_RM_I5 \ + { \ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false; \ + value = reg[source].I << shift; \ + } +#define LSL_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false; \ + value = reg[dest].I << value; \ + } +#define LSR_RD_RM_I5 \ + { \ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false; \ + value = reg[source].I >> shift; \ + } +#define LSR_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false; \ + value = reg[dest].I >> value; \ + } +#define ASR_RD_RM_I5 \ + { \ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false; \ + value = (s32)reg[source].I >> (int)shift; \ + } +#define ASR_RD_RS \ + { \ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false; \ + value = (s32)reg[dest].I >> (int)value; \ + } +#define ROR_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false; \ + value = ((reg[dest].I << (32 - value)) | \ + (reg[dest].I >> value)); \ + } +#define NEG_RD_RS \ + { \ + u32 lhs = reg[source].I; \ + u32 rhs = 0; \ + u32 res = rhs - lhs; \ + reg[dest].I = res; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(rhs, lhs, res); \ + SUBOVERFLOW(rhs, lhs, res); \ + } +#define CMP_RD_RS \ + { \ + u32 lhs = reg[dest].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + Z_FLAG = (res == 0) ? true : false; \ + N_FLAG = NEG(res) ? true : false; \ + SUBCARRY(lhs, rhs, res); \ + SUBOVERFLOW(lhs, rhs, res); \ + } +#else +#ifdef __GNUC__ +#ifdef __POWERPC__ + #define ADD_RD_RS_RN \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADD_RD_RS_O3 ADD_RD_RS_RN + #define ADD_RN_O8(d) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMN_RD_RS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("addco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define ADC_RD_RS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("mtspr xer, %4\n" \ + "addeo. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_RN \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (value) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SUB_RD_RS_O3 SUB_RD_RS_RN + #define SUB_RN_O8(d) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + reg[(d)].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RN_O8(d) \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[(d)].I), \ + "r" (opcode & 255) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define SBC_RD_RS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("mtspr xer, %4\n" \ + "subfeo. %0, %3, %2\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value), \ + "r" (C_FLAG << 29) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define LSL_RD_RM_I5 \ + { \ + C_FLAG = (reg[source].I >> (32 - shift)) & 1 ? true : false; \ + value = reg[source].I << shift; \ + } + #define LSL_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (32 - value)) & 1 ? true : false; \ + value = reg[dest].I << value; \ + } + #define LSR_RD_RM_I5 \ + { \ + C_FLAG = (reg[source].I >> (shift - 1)) & 1 ? true : false; \ + value = reg[source].I >> shift; \ + } + #define LSR_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false; \ + value = reg[dest].I >> value; \ + } + #define ASR_RD_RM_I5 \ + { \ + C_FLAG = ((s32)reg[source].I >> (int)(shift - 1)) & 1 ? true : false; \ + value = (s32)reg[source].I >> (int)shift; \ + } + #define ASR_RD_RS \ + { \ + C_FLAG = ((s32)reg[dest].I >> (int)(value - 1)) & 1 ? true : false; \ + value = (s32)reg[dest].I >> (int)value; \ + } + #define ROR_RD_RS \ + { \ + C_FLAG = (reg[dest].I >> (value - 1)) & 1 ? true : false; \ + value = ((reg[dest].I << (32 - value)) | \ + (reg[dest].I >> value)); \ + } + #define NEG_RD_RS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subfco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[source].I), \ + "r" (0) \ + ); \ + reg[dest].I = Result; \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } + #define CMP_RD_RS \ + { \ + register int Flags; \ + register int Result; \ + asm volatile ("subco. %0, %2, %3\n" \ + "mcrxr cr1\n" \ + "mfcr %1\n" \ + : "=r" (Result), \ + "=r" (Flags) \ + : "r" (reg[dest].I), \ + "r" (value) \ + ); \ + Z_FLAG = (Flags >> 29) & 1; \ + N_FLAG = (Flags >> 31) & 1; \ + C_FLAG = (Flags >> 25) & 1; \ + V_FLAG = (Flags >> 26) & 1; \ + } +#else +#define ADD_RD_RS_RN \ + asm ("add %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[source].I)); +#define ADD_RD_RS_O3 \ + asm ("add %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[source].I)); +#define ADD_RN_O8(d) \ + asm ("add %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[(d)].I) \ + : "r" (opcode & 255), "b" (reg[(d)].I)); +#define CMN_RD_RS \ + asm ("add %0, %1;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : \ + : "r" (value), "r" (reg[dest].I) : "1"); +#define ADC_RD_RS \ + asm ("bt $0, C_FLAG;" \ + "adc %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setcb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[dest].I)); +#define SUB_RD_RS_RN \ + asm ("sub %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[source].I)); +#define SUB_RD_RS_O3 \ + asm ("sub %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[source].I)); +#define SUB_RN_O8(d) \ + asm ("sub %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[(d)].I) \ + : "r" (opcode & 255), "b" (reg[(d)].I)); +#define CMP_RN_O8(d) \ + asm ("sub %0, %1;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : \ + : "r" (opcode & 255), "r" (reg[(d)].I) : "1"); +#define SBC_RD_RS \ + asm volatile ("bt $0, C_FLAG;" \ + "cmc;" \ + "sbb %1, %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "r" (value), "b" (reg[dest].I) : "cc", "memory"); +#define LSL_RD_RM_I5 \ + asm ("shl %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[source].I), "c" (shift)); +#define LSL_RD_RS \ + asm ("shl %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[dest].I), "c" (value)); +#define LSR_RD_RM_I5 \ + asm ("shr %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[source].I), "c" (shift)); +#define LSR_RD_RS \ + asm ("shr %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[dest].I), "c" (value)); +#define ASR_RD_RM_I5 \ + asm ("sar %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[source].I), "c" (shift)); +#define ASR_RD_RS \ + asm ("sar %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[dest].I), "c" (value)); +#define ROR_RD_RS \ + asm ("ror %%cl, %%eax;" \ + "setcb C_FLAG;" \ + : "=a" (value) \ + : "a" (reg[dest].I), "c" (value)); +#define NEG_RD_RS \ + asm ("neg %%ebx;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : "=b" (reg[dest].I) \ + : "b" (reg[source].I)); +#define CMP_RD_RS \ + asm ("sub %0, %1;" \ + "setsb N_FLAG;" \ + "setzb Z_FLAG;" \ + "setncb C_FLAG;" \ + "setob V_FLAG;" \ + : \ + : "r" (value), "r" (reg[dest].I) : "1"); +#endif +#else +#define ADD_RD_RS_RN \ + { \ + __asm mov eax, source \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm add ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define ADD_RD_RS_O3 \ + { \ + __asm mov eax, source \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm add ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define ADD_RN_O8(d) \ + { \ + __asm mov ebx, opcode \ + __asm and ebx, 255 \ + __asm add dword ptr [OFFSET reg + 4 * (d)], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define CMN_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm add ebx, value \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define ADC_RD_RS \ + { \ + __asm mov ebx, dest \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm bt word ptr C_FLAG, 0 \ + __asm adc ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define SUB_RD_RS_RN \ + { \ + __asm mov eax, source \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm sub ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define SUB_RD_RS_O3 \ + { \ + __asm mov eax, source \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm sub ebx, value \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define SUB_RN_O8(d) \ + { \ + __asm mov ebx, opcode \ + __asm and ebx, 255 \ + __asm sub dword ptr [OFFSET reg + 4 * (d)], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define CMP_RN_O8(d) \ + { \ + __asm mov eax, dword ptr [OFFSET reg + 4 * (d)] \ + __asm mov ebx, opcode \ + __asm and ebx, 255 \ + __asm sub eax, ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define SBC_RD_RS \ + { \ + __asm mov ebx, dest \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm mov eax, value \ + __asm bt word ptr C_FLAG, 0 \ + __asm cmc \ + __asm sbb ebx, eax \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define LSL_RD_RM_I5 \ + { \ + __asm mov eax, source \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shl eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define LSL_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr value \ + __asm shl eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define LSR_RD_RM_I5 \ + { \ + __asm mov eax, source \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm shr eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define LSR_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr value \ + __asm shr eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define ASR_RD_RM_I5 \ + { \ + __asm mov eax, source \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr shift \ + __asm sar eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define ASR_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr value \ + __asm sar eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define ROR_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov eax, dword ptr [OFFSET reg + 4 * eax] \ + __asm mov cl, byte ptr value \ + __asm ror eax, cl \ + __asm mov value, eax \ + __asm setc byte ptr C_FLAG \ + } +#define NEG_RD_RS \ + { \ + __asm mov ebx, source \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * ebx] \ + __asm neg ebx \ + __asm mov eax, dest \ + __asm mov dword ptr [OFFSET reg + 4 * eax], ebx \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#define CMP_RD_RS \ + { \ + __asm mov eax, dest \ + __asm mov ebx, dword ptr [OFFSET reg + 4 * eax] \ + __asm sub ebx, value \ + __asm sets byte ptr N_FLAG \ + __asm setz byte ptr Z_FLAG \ + __asm setnc byte ptr C_FLAG \ + __asm seto byte ptr V_FLAG \ + } +#endif +#endif + +u32 opcode = CPUReadHalfWordQuick(armNextPC); +clockTicks = thumbCycles[opcode >> 8] + memoryWaitFetch[(armNextPC >> 24) & 15]; +#ifndef FINAL_VERSION +if (armNextPC == stop) +{ + armNextPC = armNextPC++; +} +#endif + +armNextPC = reg[15].I; +reg[15].I += 2; + +switch (opcode >> 8) +{ +case 0x00: +case 0x01: +case 0x02: +case 0x03: +case 0x04: +case 0x05: +case 0x06: +case 0x07: +{ + // LSL Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if (shift) + { + LSL_RD_RM_I5; + } + else + { + value = reg[source].I; + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false : true); +} +break; +case 0x08: +case 0x09: +case 0x0a: +case 0x0b: +case 0x0c: +case 0x0d: +case 0x0e: +case 0x0f: +{ + // LSR Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if (shift) + { + LSR_RD_RM_I5; + } + else + { + C_FLAG = reg[source].I & 0x80000000 ? true : false; + value = 0; + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false : true); +} +break; +case 0x10: +case 0x11: +case 0x12: +case 0x13: +case 0x14: +case 0x15: +case 0x16: +case 0x17: +{ + // ASR Rd, Rm, #Imm 5 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + int shift = (opcode >> 6) & 0x1f; + u32 value; + + if (shift) + { + ASR_RD_RM_I5; + } + else + { + if (reg[source].I & 0x80000000) + { + value = 0xFFFFFFFF; + C_FLAG = true; + } + else + { + value = 0; + C_FLAG = false; + } + } + reg[dest].I = value; + // C_FLAG set above + N_FLAG = (value & 0x80000000 ? true : false); + Z_FLAG = (value ? false : true); +} +break; +case 0x18: +case 0x19: +{ + // ADD Rd, Rs, Rn + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = reg[(opcode >> 6) & 0x07].I; + ADD_RD_RS_RN; +} +break; +case 0x1a: +case 0x1b: +{ + // SUB Rd, Rs, Rn + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = reg[(opcode >> 6) & 0x07].I; + SUB_RD_RS_RN; +} +break; +case 0x1c: +case 0x1d: +{ + // ADD Rd, Rs, #Offset3 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = (opcode >> 6) & 7; + ADD_RD_RS_O3; +} +break; +case 0x1e: +case 0x1f: +{ + // SUB Rd, Rs, #Offset3 + int dest = opcode & 0x07; + int source = (opcode >> 3) & 0x07; + u32 value = (opcode >> 6) & 7; + SUB_RD_RS_O3; +} +break; +case 0x20: + // MOV R0, #Offset8 + reg[0].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[0].I ? false : true); + break; +case 0x21: + // MOV R1, #Offset8 + reg[1].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[1].I ? false : true); + break; +case 0x22: + // MOV R2, #Offset8 + reg[2].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[2].I ? false : true); + break; +case 0x23: + // MOV R3, #Offset8 + reg[3].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[3].I ? false : true); + break; +case 0x24: + // MOV R4, #Offset8 + reg[4].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[4].I ? false : true); + break; +case 0x25: + // MOV R5, #Offset8 + reg[5].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[5].I ? false : true); + break; +case 0x26: + // MOV R6, #Offset8 + reg[6].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[6].I ? false : true); + break; +case 0x27: + // MOV R7, #Offset8 + reg[7].I = opcode & 255; + N_FLAG = false; + Z_FLAG = (reg[7].I ? false : true); + break; +case 0x28: + // CMP R0, #Offset8 + CMP_RN_O8(0); + break; +case 0x29: + // CMP R1, #Offset8 + CMP_RN_O8(1); + break; +case 0x2a: + // CMP R2, #Offset8 + CMP_RN_O8(2); + break; +case 0x2b: + // CMP R3, #Offset8 + CMP_RN_O8(3); + break; +case 0x2c: + // CMP R4, #Offset8 + CMP_RN_O8(4); + break; +case 0x2d: + // CMP R5, #Offset8 + CMP_RN_O8(5); + break; +case 0x2e: + // CMP R6, #Offset8 + CMP_RN_O8(6); + break; +case 0x2f: + // CMP R7, #Offset8 + CMP_RN_O8(7); + break; +case 0x30: + // ADD R0,#Offset8 + ADD_RN_O8(0); + break; +case 0x31: + // ADD R1,#Offset8 + ADD_RN_O8(1); + break; +case 0x32: + // ADD R2,#Offset8 + ADD_RN_O8(2); + break; +case 0x33: + // ADD R3,#Offset8 + ADD_RN_O8(3); + break; +case 0x34: + // ADD R4,#Offset8 + ADD_RN_O8(4); + break; +case 0x35: + // ADD R5,#Offset8 + ADD_RN_O8(5); + break; +case 0x36: + // ADD R6,#Offset8 + ADD_RN_O8(6); + break; +case 0x37: + // ADD R7,#Offset8 + ADD_RN_O8(7); + break; +case 0x38: + // SUB R0,#Offset8 + SUB_RN_O8(0); + break; +case 0x39: + // SUB R1,#Offset8 + SUB_RN_O8(1); + break; +case 0x3a: + // SUB R2,#Offset8 + SUB_RN_O8(2); + break; +case 0x3b: + // SUB R3,#Offset8 + SUB_RN_O8(3); + break; +case 0x3c: + // SUB R4,#Offset8 + SUB_RN_O8(4); + break; +case 0x3d: + // SUB R5,#Offset8 + SUB_RN_O8(5); + break; +case 0x3e: + // SUB R6,#Offset8 + SUB_RN_O8(6); + break; +case 0x3f: + // SUB R7,#Offset8 + SUB_RN_O8(7); + break; +case 0x40: + switch ((opcode >> 6) & 3) + { + case 0x00: + { + // AND Rd, Rs + int dest = opcode & 7; + reg[dest].I &= reg[(opcode >> 3) & 7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; +#ifdef BKPT_SUPPORT +#define THUMB_CONSOLE_OUTPUT(a, b) \ + if ((opcode == 0x4000) && (reg[0].I == 0xC0DED00D)) { \ + extern void (*dbgOutput)(char *, u32); \ + dbgOutput((a), (b)); \ + } +#else +#define THUMB_CONSOLE_OUTPUT(a, b) +#endif + THUMB_CONSOLE_OUTPUT(NULL, reg[2].I); + } + break; + case 0x01: + // EOR Rd, Rs + { + int dest = opcode & 7; + reg[dest].I ^= reg[(opcode >> 3) & 7].I; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + } + break; + case 0x02: + // LSL Rd, Rs + { + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].B.B0; + if (value) + { + if (value == 32) + { + value = 0; + C_FLAG = (reg[dest].I & 1 ? true : false); + } + else if (value < 32) + { + LSL_RD_RS; + } + else + { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + case 0x03: + { + // LSR Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].B.B0; + if (value) + { + if (value == 32) + { + value = 0; + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } + else if (value < 32) + { + LSR_RD_RS; + } + else + { + value = 0; + C_FLAG = false; + } + reg[dest].I = value; + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + } + break; +case 0x41: + switch ((opcode >> 6) & 3) + { + case 0x00: + { + // ASR Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].B.B0; + // ASR + if (value) + { + if (value < 32) + { + ASR_RD_RS; + reg[dest].I = value; + } + else + { + if (reg[dest].I & 0x80000000) + { + reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } + else + { + reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + clockTicks++; + } + break; + case 0x01: + { + // ADC Rd, Rs + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3) & 7].I; + // ADC + ADC_RD_RS; + } + break; + case 0x02: + { + // SBC Rd, Rs + int dest = opcode & 0x07; + u32 value = reg[(opcode >> 3) & 7].I; + + // SBC + SBC_RD_RS; + } + break; + case 0x03: + // ROR Rd, Rs + { + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].B.B0; + + if (value) + { + value = value & 0x1f; + if (value == 0) + { + C_FLAG = (reg[dest].I & 0x80000000 ? true : false); + } + else + { + ROR_RD_RS; + reg[dest].I = value; + } + } + clockTicks++; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = reg[dest].I ? false : true; + } + break; + } + break; +case 0x42: + switch ((opcode >> 6) & 3) + { + case 0x00: + { + // TST Rd, Rs + u32 value = reg[opcode & 7].I & reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; + } + break; + case 0x01: + { + // NEG Rd, Rs + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; + } + break; + case 0x02: + { + // CMP Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].I; + CMP_RD_RS; + } + break; + case 0x03: + { + // CMN Rd, Rs + int dest = opcode & 7; + u32 value = reg[(opcode >> 3) & 7].I; + // CMN + CMN_RD_RS; + } + break; + } + break; +case 0x43: + switch ((opcode >> 6) & 3) + { + case 0x00: + { + // ORR Rd, Rs + int dest = opcode & 7; + reg[dest].I |= reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x01: + { + // MUL Rd, Rs + int dest = opcode & 7; + u32 rm = reg[(opcode >> 3) & 7].I; + reg[dest].I = reg[dest].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFFFF00) == 0) + clockTicks += 1; + else if ((rm & 0xFFFF0000) == 0) + clockTicks += 2; + else if ((rm & 0xFF000000) == 0) + clockTicks += 3; + else + clockTicks += 4; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x02: + { + // BIC Rd, Rs + int dest = opcode & 7; + reg[dest].I &= (~reg[(opcode >> 3) & 7].I); + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + case 0x03: + { + // MVN Rd, Rs + int dest = opcode & 7; + reg[dest].I = ~reg[(opcode >> 3) & 7].I; + Z_FLAG = reg[dest].I ? false : true; + N_FLAG = reg[dest].I & 0x80000000 ? true : false; + } + break; + } + break; +case 0x44: +{ + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + switch ((opcode >> 6) & 3) + { + default: + goto unknown_thumb; + case 1: + // ADD Rd, Hs + reg[dest].I += reg[base + 8].I; + break; + case 2: + // ADD Hd, Rs + reg[dest + 8].I += reg[base].I; + if (dest == 7) + { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + case 3: + // ADD Hd, Hs + reg[dest + 8].I += reg[base + 8].I; + if (dest == 7) + { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + } +} +break; +case 0x45: +{ + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + u32 value; + switch ((opcode >> 6) & 3) + { + case 0: + // CMP Rd, Hs + value = reg[base].I; + CMP_RD_RS; + break; + case 1: + // CMP Rd, Hs + value = reg[base + 8].I; + CMP_RD_RS; + break; + case 2: + // CMP Hd, Rs + value = reg[base].I; + dest += 8; + CMP_RD_RS; + break; + case 3: + // CMP Hd, Hs + value = reg[base + 8].I; + dest += 8; + CMP_RD_RS; + break; + } +} +break; +case 0x46: +{ + int dest = opcode & 7; + int base = (opcode >> 3) & 7; + switch ((opcode >> 6) & 3) + { + case 0: + // this form should not be used... + // MOV Rd, Rs + reg[dest].I = reg[base].I; + break; + case 1: + // MOV Rd, Hs + reg[dest].I = reg[base + 8].I; + break; + case 2: + // MOV Hd, Rs + reg[dest + 8].I = reg[base].I; + if (dest == 7) + { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + case 3: + // MOV Hd, Hs + reg[dest + 8].I = reg[base + 8].I; + if (dest == 7) + { + reg[15].I &= 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks++; + } + break; + } +} +break; +case 0x47: +{ + int base = (opcode >> 3) & 7; + switch ((opcode >> 6) & 3) + { + case 0: + // BX Rs + reg[15].I = (reg[base].I) & 0xFFFFFFFE; + if (reg[base].I & 1) + { + armState = false; + armNextPC = reg[15].I; + reg[15].I += 2; + } + else + { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + case 1: + // BX Hs + reg[15].I = (reg[8 + base].I) & 0xFFFFFFFE; + if (reg[8 + base].I & 1) + { + armState = false; + armNextPC = reg[15].I; + reg[15].I += 2; + } + else + { + armState = true; + reg[15].I &= 0xFFFFFFFC; + armNextPC = reg[15].I; + reg[15].I += 4; + } + break; + default: + goto unknown_thumb; + } +} +break; +case 0x48: + // LDR R0,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[0].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x49: + // LDR R1,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[1].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4a: + // LDR R2,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[2].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4b: + // LDR R3,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[3].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4c: + // LDR R4,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[4].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4d: + // LDR R5,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[5].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4e: + // LDR R6,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[6].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x4f: + // LDR R7,[PC, #Imm] +{ + u32 address = (reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + reg[7].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x50: +case 0x51: + // STR Rd, [Rs, Rn] +{ + u32 + address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + CPUWriteMemory(address, + reg[opcode & 7].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x52: +case 0x53: + // STRH Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + CPUWriteHalfWord(address, + reg[opcode & 7].W.W0); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x54: +case 0x55: + // STRB Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + CPUWriteByte(address, + reg[opcode & 7].B.B0); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x56: +case 0x57: + // LDSB Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + reg[opcode & 7].I = (s8)CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x58: +case 0x59: + // LDR Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + reg[opcode & 7].I = CPUReadMemory(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x5a: +case 0x5b: + // LDRH Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + reg[opcode & 7].I = CPUReadHalfWord(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x5c: +case 0x5d: + // LDRB Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + reg[opcode & 7].I = CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x5e: +case 0x5f: + // LDSH Rd, [Rs, Rn] +{ + u32 address = reg[(opcode >> 3) & 7].I + reg[(opcode >> 6) & 7].I; + reg[opcode & 7].I = (s16)CPUReadHalfWordSigned(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x60: +case 0x61: +case 0x62: +case 0x63: +case 0x64: +case 0x65: +case 0x66: +case 0x67: + // STR Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 2); + CPUWriteMemory(address, + reg[opcode & 7].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x68: +case 0x69: +case 0x6a: +case 0x6b: +case 0x6c: +case 0x6d: +case 0x6e: +case 0x6f: + // LDR Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 2); + reg[opcode & 7].I = CPUReadMemory(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x70: +case 0x71: +case 0x72: +case 0x73: +case 0x74: +case 0x75: +case 0x76: +case 0x77: + // STRB Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31)); + CPUWriteByte(address, + reg[opcode & 7].B.B0); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x78: +case 0x79: +case 0x7a: +case 0x7b: +case 0x7c: +case 0x7d: +case 0x7e: +case 0x7f: + // LDRB Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31)); + reg[opcode & 7].I = CPUReadByte(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x80: +case 0x81: +case 0x82: +case 0x83: +case 0x84: +case 0x85: +case 0x86: +case 0x87: + // STRH Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 1); + CPUWriteHalfWord(address, + reg[opcode & 7].W.W0); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x88: +case 0x89: +case 0x8a: +case 0x8b: +case 0x8c: +case 0x8d: +case 0x8e: +case 0x8f: + // LDRH Rd, [Rs, #Imm] +{ + u32 address = reg[(opcode >> 3) & 7].I + (((opcode >> 6) & 31) << 1); + reg[opcode & 7].I = CPUReadHalfWord(address); + clockTicks += CPUUpdateTicksAccess16(address); +} +break; +case 0x90: + // STR R0, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[0].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x91: + // STR R1, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[1].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x92: + // STR R2, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[2].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x93: + // STR R3, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[3].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x94: + // STR R4, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[4].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x95: + // STR R5, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[5].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x96: + // STR R6, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[6].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x97: + // STR R7, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + CPUWriteMemory(address, reg[7].I); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x98: + // LDR R0, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[0].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x99: + // LDR R1, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[1].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9a: + // LDR R2, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[2].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9b: + // LDR R3, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[3].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9c: + // LDR R4, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[4].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9d: + // LDR R5, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[5].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9e: + // LDR R6, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[6].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0x9f: + // LDR R7, [SP, #Imm] +{ + u32 address = reg[13].I + ((opcode & 255) << 2); + reg[7].I = CPUReadMemoryQuick(address); + clockTicks += CPUUpdateTicksAccess32(address); +} +break; +case 0xa0: + // ADD R0, PC, Imm + reg[0].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa1: + // ADD R1, PC, Imm + reg[1].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa2: + // ADD R2, PC, Imm + reg[2].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa3: + // ADD R3, PC, Imm + reg[3].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa4: + // ADD R4, PC, Imm + reg[4].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa5: + // ADD R5, PC, Imm + reg[5].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa6: + // ADD R6, PC, Imm + reg[6].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa7: + // ADD R7, PC, Imm + reg[7].I = (reg[15].I & 0xFFFFFFFC) + ((opcode & 255) << 2); + break; +case 0xa8: + // ADD R0, SP, Imm + reg[0].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xa9: + // ADD R1, SP, Imm + reg[1].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xaa: + // ADD R2, SP, Imm + reg[2].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xab: + // ADD R3, SP, Imm + reg[3].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xac: + // ADD R4, SP, Imm + reg[4].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xad: + // ADD R5, SP, Imm + reg[5].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xae: + // ADD R6, SP, Imm + reg[6].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xaf: + // ADD R7, SP, Imm + reg[7].I = reg[13].I + ((opcode & 255) << 2); + break; +case 0xb0: +{ + // ADD SP, Imm + int offset = (opcode & 127) << 2; + if (opcode & 0x80) + offset = -offset; + reg[13].I += offset; +} +break; +#define PUSH_REG(val, r) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + if (offset) \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); \ + else \ + clockTicks += 1 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + address += 4; \ + } +case 0xb4: + // PUSH {Rlist} +{ + int offset = 0; + u32 temp = reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + reg[13].I = temp; +} +break; +case 0xb5: + // PUSH {Rlist, LR} +{ + int offset = 0; + u32 temp = reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + reg[13].I = temp; +} +break; +#define POP_REG(val, r) \ + if (opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (offset) \ + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); \ + else \ + clockTicks += 2 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + address += 4; \ + } +case 0xbc: + // POP {Rlist} +{ + int offset = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4 * cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[13].I = temp; +} +break; +case 0xbd: + // POP {Rlist, PC} +{ + int offset = 0; + u32 address = reg[13].I & 0xFFFFFFFC; + u32 temp = reg[13].I + 4 + 4 * cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + if (offset) + clockTicks += CPUUpdateTicksAccessSeq32(address); + else + clockTicks += CPUUpdateTicksAccess32(address); + armNextPC = reg[15].I; + reg[15].I += 2; + reg[13].I = temp; +} +break; +#define THUMB_STM_REG(val, r, b) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, reg[(r)].I); \ + if (!offset) { \ + reg[(b)].I = temp; \ + clockTicks += 1 + CPUUpdateTicksAccess32(address); \ + } else \ + clockTicks += 1 + CPUUpdateTicksAccessSeq32(address); \ + offset = 1; \ + address += 4; \ + } +case 0xc0: +{ + // STM R0!, {Rlist} + u32 address = reg[0].I & 0xFFFFFFFC; + u32 temp = reg[0].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 0); + THUMB_STM_REG(2, 1, 0); + THUMB_STM_REG(4, 2, 0); + THUMB_STM_REG(8, 3, 0); + THUMB_STM_REG(16, 4, 0); + THUMB_STM_REG(32, 5, 0); + THUMB_STM_REG(64, 6, 0); + THUMB_STM_REG(128, 7, 0); +} +break; +case 0xc1: +{ + // STM R1!, {Rlist} + u32 address = reg[1].I & 0xFFFFFFFC; + u32 temp = reg[1].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 1); + THUMB_STM_REG(2, 1, 1); + THUMB_STM_REG(4, 2, 1); + THUMB_STM_REG(8, 3, 1); + THUMB_STM_REG(16, 4, 1); + THUMB_STM_REG(32, 5, 1); + THUMB_STM_REG(64, 6, 1); + THUMB_STM_REG(128, 7, 1); +} +break; +case 0xc2: +{ + // STM R2!, {Rlist} + u32 address = reg[2].I & 0xFFFFFFFC; + u32 temp = reg[2].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 2); + THUMB_STM_REG(2, 1, 2); + THUMB_STM_REG(4, 2, 2); + THUMB_STM_REG(8, 3, 2); + THUMB_STM_REG(16, 4, 2); + THUMB_STM_REG(32, 5, 2); + THUMB_STM_REG(64, 6, 2); + THUMB_STM_REG(128, 7, 2); +} +break; +case 0xc3: +{ + // STM R3!, {Rlist} + u32 address = reg[3].I & 0xFFFFFFFC; + u32 temp = reg[3].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 3); + THUMB_STM_REG(2, 1, 3); + THUMB_STM_REG(4, 2, 3); + THUMB_STM_REG(8, 3, 3); + THUMB_STM_REG(16, 4, 3); + THUMB_STM_REG(32, 5, 3); + THUMB_STM_REG(64, 6, 3); + THUMB_STM_REG(128, 7, 3); +} +break; +case 0xc4: +{ + // STM R4!, {Rlist} + u32 address = reg[4].I & 0xFFFFFFFC; + u32 temp = reg[4].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 4); + THUMB_STM_REG(2, 1, 4); + THUMB_STM_REG(4, 2, 4); + THUMB_STM_REG(8, 3, 4); + THUMB_STM_REG(16, 4, 4); + THUMB_STM_REG(32, 5, 4); + THUMB_STM_REG(64, 6, 4); + THUMB_STM_REG(128, 7, 4); +} +break; +case 0xc5: +{ + // STM R5!, {Rlist} + u32 address = reg[5].I & 0xFFFFFFFC; + u32 temp = reg[5].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 5); + THUMB_STM_REG(2, 1, 5); + THUMB_STM_REG(4, 2, 5); + THUMB_STM_REG(8, 3, 5); + THUMB_STM_REG(16, 4, 5); + THUMB_STM_REG(32, 5, 5); + THUMB_STM_REG(64, 6, 5); + THUMB_STM_REG(128, 7, 5); +} +break; +case 0xc6: +{ + // STM R6!, {Rlist} + u32 address = reg[6].I & 0xFFFFFFFC; + u32 temp = reg[6].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 6); + THUMB_STM_REG(2, 1, 6); + THUMB_STM_REG(4, 2, 6); + THUMB_STM_REG(8, 3, 6); + THUMB_STM_REG(16, 4, 6); + THUMB_STM_REG(32, 5, 6); + THUMB_STM_REG(64, 6, 6); + THUMB_STM_REG(128, 7, 6); +} +break; +case 0xc7: +{ + // STM R7!, {Rlist} + u32 address = reg[7].I & 0xFFFFFFFC; + u32 temp = reg[7].I + 4 * cpuBitsSet[opcode & 0xff]; + int offset = 0; + // store + THUMB_STM_REG(1, 0, 7); + THUMB_STM_REG(2, 1, 7); + THUMB_STM_REG(4, 2, 7); + THUMB_STM_REG(8, 3, 7); + THUMB_STM_REG(16, 4, 7); + THUMB_STM_REG(32, 5, 7); + THUMB_STM_REG(64, 6, 7); + THUMB_STM_REG(128, 7, 7); +} +break; +#define THUMB_LDM_REG(val, r) \ + if (opcode & (val)) { \ + reg[(r)].I = CPUReadMemory(address); \ + if (offset) \ + clockTicks += 2 + CPUUpdateTicksAccessSeq32(address); \ + else \ + clockTicks += 2 + CPUUpdateTicksAccess32(address); \ + offset = 1; \ + address += 4; \ + } +case 0xc8: +{ + // LDM R0!, {Rlist} + u32 address = reg[0].I & 0xFFFFFFFC; + u32 temp = reg[0].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 1)) + reg[0].I = temp; +} +break; +case 0xc9: +{ + // LDM R1!, {Rlist} + u32 address = reg[1].I & 0xFFFFFFFC; + u32 temp = reg[1].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 2)) + reg[1].I = temp; +} +break; +case 0xca: +{ + // LDM R2!, {Rlist} + u32 address = reg[2].I & 0xFFFFFFFC; + u32 temp = reg[2].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 4)) + reg[2].I = temp; +} +break; +case 0xcb: +{ + // LDM R3!, {Rlist} + u32 address = reg[3].I & 0xFFFFFFFC; + u32 temp = reg[3].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 8)) + reg[3].I = temp; +} +break; +case 0xcc: +{ + // LDM R4!, {Rlist} + u32 address = reg[4].I & 0xFFFFFFFC; + u32 temp = reg[4].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 16)) + reg[4].I = temp; +} +break; +case 0xcd: +{ + // LDM R5!, {Rlist} + u32 address = reg[5].I & 0xFFFFFFFC; + u32 temp = reg[5].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 32)) + reg[5].I = temp; +} +break; +case 0xce: +{ + // LDM R6!, {Rlist} + u32 address = reg[6].I & 0xFFFFFFFC; + u32 temp = reg[6].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 64)) + reg[6].I = temp; +} +break; +case 0xcf: +{ + // LDM R7!, {Rlist} + u32 address = reg[7].I & 0xFFFFFFFC; + u32 temp = reg[7].I + 4 * cpuBitsSet[opcode & 0xFF]; + int offset = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + if (!(opcode & 128)) + reg[7].I = temp; +} +break; +case 0xd0: + // BEQ offset + if (Z_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd1: + // BNE offset + if (!Z_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd2: + // BCS offset + if (C_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd3: + // BCC offset + if (!C_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd4: + // BMI offset + if (N_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd5: + // BPL offset + if (!N_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd6: + // BVS offset + if (V_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd7: + // BVC offset + if (!V_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd8: + // BHI offset + if (C_FLAG && !Z_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xd9: + // BLS offset + if (!C_FLAG || Z_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xda: + // BGE offset + if (N_FLAG == V_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xdb: + // BLT offset + if (N_FLAG != V_FLAG) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xdc: + // BGT offset + if (!Z_FLAG && (N_FLAG == V_FLAG)) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xdd: + // BLE offset + if (Z_FLAG || (N_FLAG != V_FLAG)) + { + reg[15].I += ((s8)(opcode & 0xFF)) << 1; + armNextPC = reg[15].I; + reg[15].I += 2; + clockTicks = 3; + } + break; +case 0xdf: + // SWI #comment + CPUSoftwareInterrupt(opcode & 0xFF); + break; +case 0xe0: +case 0xe1: +case 0xe2: +case 0xe3: +case 0xe4: +case 0xe5: +case 0xe6: +case 0xe7: +{ + // B offset + int offset = (opcode & 0x3FF) << 1; + if (opcode & 0x0400) + offset |= 0xFFFFF800; + reg[15].I += offset; + armNextPC = reg[15].I; + reg[15].I += 2; +} +break; +case 0xf0: +case 0xf1: +case 0xf2: +case 0xf3: +{ + // BLL #offset + int offset = (opcode & 0x7FF); + reg[14].I = reg[15].I + (offset << 12); +} +break; +case 0xf4: +case 0xf5: +case 0xf6: +case 0xf7: +{ + // BLL #offset + int offset = (opcode & 0x7FF); + reg[14].I = reg[15].I + ((offset << 12) | 0xFF800000); +} +break; +case 0xf8: +case 0xf9: +case 0xfa: +case 0xfb: +case 0xfc: +case 0xfd: +case 0xfe: +case 0xff: +{ + // BLH #offset + int offset = (opcode & 0x7FF); + u32 temp = reg[15].I - 2; + reg[15].I = (reg[14].I + (offset << 1)) & 0xFFFFFFFE; + armNextPC = reg[15].I; + reg[15].I += 2; + reg[14].I = temp | 1; +} +break; +#ifdef BKPT_SUPPORT +case 0xbe: + // BKPT #comment + extern void (*dbgSignal)(int, int); + reg[15].I -= 2; + armNextPC -= 2; + dbgSignal(5, opcode & 255); + return; +#endif +case 0xb1: +case 0xb2: +case 0xb3: +case 0xb6: +case 0xb7: +case 0xb8: +case 0xb9: +case 0xba: +case 0xbb: +#ifndef BKPT_SUPPORT +case 0xbe: +#endif +case 0xbf: +case 0xde: +default: +unknown_thumb: +#ifdef GBA_LOGGING + if (systemVerbose & VERBOSE_UNDEFINED) + log("Undefined THUMB instruction %04x at %08x\n", opcode, armNextPC - 2); +#endif + CPUUndefinedException(); + break; +}