# HG changeset patch # User Mike Pavone # Date 1382852327 25200 # Node ID c08a4efeee7fca02eec68334789a7ccdfbac2923 # Parent 7696d824489d42ded9aa8ce9c32d6a91fb45bb3d# Parent db5880d8ea034595ffcfbb16b0746a92c9644f1c Update opengl branch from default. Fix build breakage unrelated to merge diff -r 7696d824489d -r c08a4efeee7f 68kinst.c --- a/68kinst.c Tue Jul 23 23:01:03 2013 -0700 +++ b/68kinst.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include #include diff -r 7696d824489d -r c08a4efeee7f 68kinst.h --- a/68kinst.h Tue Jul 23 23:01:03 2013 -0700 +++ b/68kinst.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef M68KINST_H_ #define M68KINST_H_ diff -r 7696d824489d -r c08a4efeee7f COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,674 @@ + 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 7696d824489d -r c08a4efeee7f Makefile --- a/Makefile Tue Jul 23 23:01:03 2013 -0700 +++ b/Makefile Sat Oct 26 22:38:47 2013 -0700 @@ -1,4 +1,4 @@ -LIBS=sdl +LIBS=sdl gl LDFLAGS=-lm `pkg-config --libs $(LIBS)` ifdef DEBUG CFLAGS=-ggdb -std=gnu99 `pkg-config --cflags-only-I $(LIBS)` -Wreturn-type -Werror=return-type @@ -16,20 +16,20 @@ Z80OBJS=z80inst.o z80_to_x86.o zruntime.o AUDIOOBJS=ym2612.o psg.o wave.o -all : dis trans stateview blastem +all : dis zdis stateview vgmplay blastem -blastem : blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) - $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) +blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) + $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) dis : dis.o 68kinst.o $(CC) -o dis dis.o 68kinst.o zdis : zdis.o z80inst.o $(CC) -o zdis zdis.o z80inst.o - + libemu68k.a : $(M68KOBJS) $(TRANSOBJS) ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS) - + trans : trans.o $(M68KOBJS) $(TRANSOBJS) $(CC) -o trans trans.o $(M68KOBJS) $(TRANSOBJS) @@ -42,11 +42,14 @@ ztestgen : ztestgen.o z80inst.o $(CC) -o ztestgen ztestgen.o z80inst.o -stateview : stateview.o vdp.o render_sdl.o - $(CC) -o stateview stateview.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)` +stateview : stateview.o vdp.o render_sdl.o config.o tern.o gst.o + $(CC) -o stateview stateview.o vdp.o render_sdl.o config.o tern.o gst.o `pkg-config --libs $(LIBS)` -vgmplay : vgmplay.o render_sdl.o $(AUDIOOBJS) - $(CC) -o vgmplay vgmplay.o render_sdl.o $(AUDIOOBJS) `pkg-config --libs $(LIBS)` +vgmplay : vgmplay.o render_sdl.o config.o tern.o $(AUDIOOBJS) + $(CC) -o vgmplay vgmplay.o render_sdl.o config.o tern.o $(AUDIOOBJS) $(LDFLAGS) + +testgst : testgst.o gst.o + $(CC) -o testgst testgst.o gst.o test_x86 : test_x86.o gen_x86.o $(CC) -o test_x86 test_x86.o gen_x86.o @@ -56,7 +59,7 @@ offsets : offsets.c z80_to_x86.h m68k_to_x86.h $(CC) -o offsets offsets.c - + %.o : %.S $(CC) -c -o $@ $< diff -r 7696d824489d -r c08a4efeee7f README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,87 @@ +BlastEm 0.1.0 +------------- + +Installation +------------ + +Extract this tarball to a directory of your choosing. If you wish to change the +configuration settings, copy default.cfg to ~/.config/blastem/blastem.cfg and +modify the copy. You may also whish to add the blastem directory to your PATH +environment variable. + +Configuration +------------- + +Configuration is read from the file at ~/.config/blastem/blastem.cfg if it +exists othwerise it is read from default.cfg from the same directory as the +blastem executable. Sections are denoted by a section name followed by an open +curly bracket, the section's contents and a closing curly bracket. Individual +configuration values are set by entering the value's name followed by a space +or tab and followed by the desired value. + +Bindings +-------- + +The keys subsection of bindings maps keyboard keys to gamepad buttons or UI +actions. The key name goes on the left and the action is on the right. +Most keys are named for the character they produce when pressed. Additionally, +the arrow, enter and escape keys have the symbolic names up, down, left, right, +enter and esc respectively. Other keys that do not produce characters are not +yet supported. + +The pads subsection is used to map gamepads and joysticks. Analog axes are not +currently supported. An example configuration is provided in default.cfg to map +SDL joystick 0 to the second controller. + +Video +----- + +Currently the only setting in the video section is "width", which is the width +of the window in pixels. Height is calculated from this value. Both width and +height can be overridden from the command line. + +Audio +----- + +The audio section has two config values: rate and buffer. rate selects the +sample rate and buffer sets the size of the output buffer in samples. 512 is +generally a good value, but if you're experiencing audio dropouts you might +want to increase it to 1024. + +Debugger +-------- + +BlastEm has an integrated command-line debugger loosely based on GDB's +interface. The interface is very rough at the moment. Available commands in the +68K debugger are: + b ADDRESS - Set a breakpoint at ADDRESS + a ADDRESS - Advance to address + n - Advance to next instruction + c - Continue + p[/(x|X|d|c)] VALUE - Print a register or memory location + vs - Print VDP sprite list + vr - Print VDP register info + zb - Set a Z80 breakpoint + q - Quit BlastEm +Available commands in the Z80 debugger are: + b ADDRESS - Set a breakpoint at ADDRESS + a ADDRESS - Advance to address + n - Advance to next instruction + c - Continue + p[/(x|X|d|c)] VALUE - Print a register or memory location + di[/(x|X|d|c)] VALUE - Print VALUE before every debugger prompt + de BREAKPOINT - Delete a Z80 breakpoint + q - Quit BlastEm + +The -d flag can be used to cause BlastEm to start in the debugger. +Alternatively, you can use the ui.enter_debugger action (mapped to the 'u' key +by default)to enter the debugger while a game is running. + +License +------- + +BlastEm is free software distributed under the terms of the GNU General Public +License version 3 or higher. This gives you the right to redistribute and/or +modify the program as long as you follow the terms of the license. See the file +COPYING for full license details. + diff -r 7696d824489d -r c08a4efeee7f analyze_olp.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/analyze_olp.py Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +from zipfile import ZipFile +from sys import exit, argv + +def detect_rise(last, sample, bit): + mask = 1 << bit + return (not last & mask) and (sample & mask) + +def detect_fall(last, sample, bit): + mask = 1 << bit + return (last & mask) and (not sample & mask) + +def detect_high(sample, bit): + mask = 1 << bit + return sample & mask + +def detect_low(sample, bit): + mask = 1 << bit + return not sample & mask + +def analyze_delays(chanmap, datafile): + m68k_clk = chanmap['M68K CLK'] + m_as = chanmap['!AS'] + last = False + clks = 0 + as_start = 0 + for line in datafile.readlines(): + line = line.strip() + if line and not line.startswith(';'): + sample,_,num = line.partition('@') + sample = int(sample, 16) + if not (last is False): + if detect_rise(last, sample, m68k_clk): + clks = clks + 1 + if detect_rise(last, sample, m_as): + as_clks = clks - as_start + if as_clks > 2: + print '!AS held for', as_clks, 'cycles starting (delay of ' + str(as_clks - 2) + ') at', as_start, 'and ending at', clks + elif detect_fall(last, sample, m_as): + as_start = clks + last = sample + +def main(args): + if len(args) < 2: + print 'Usage: analyze_olp.py filename' + exit(1) + olpfile = ZipFile(args[1], "r") + channelfile = olpfile.open('channel.labels') + channels = [line.strip() for line in channelfile.readlines()] + channelfile.close() + chanmap = {} + for i in xrange(0, len(channels)): + chanmap[channels[i]] = i + datafile = olpfile.open('data.ols') + analyze_delays(chanmap, datafile) + + +if __name__ == '__main__': + main(argv) diff -r 7696d824489d -r c08a4efeee7f blastem.c --- a/blastem.c Tue Jul 23 23:01:03 2013 -0700 +++ b/blastem.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include "m68k_to_x86.h" #include "z80_to_x86.h" @@ -5,10 +10,13 @@ #include "vdp.h" #include "render.h" #include "blastem.h" +#include "gst.h" #include #include #include +#define BLASTEM_VERSION "0.1.0" + #define CARTRIDGE_WORDS 0x200000 #define RAM_WORDS 32 * 1024 #define Z80_RAM_BYTES 8 * 1024 @@ -25,6 +33,8 @@ #define LINES_NTSC 262 #define LINES_PAL 312 +#define MAX_SOUND_CYCLES 100000 + uint32_t mclks_per_frame = MCLKS_LINE*LINES_NTSC; uint16_t cart[CARTRIDGE_WORDS]; @@ -35,6 +45,8 @@ int z80_enabled = 1; int frame_limit = 0; +tern_node * config; + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -50,7 +62,7 @@ uint8_t block[SMD_BLOCK_SIZE]; filesize -= SMD_HEADER_SIZE; fseek(f, SMD_HEADER_SIZE, SEEK_SET); - + uint16_t * dst = cart; while (filesize > 0) { fread(block, 1, SMD_BLOCK_SIZE, f); @@ -137,19 +149,20 @@ if (next_hint < context->int_cycle) { context->int_cycle = next_hint; context->int_num = 4; - + } } } } context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; - /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", - context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), + /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", + context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/ } int break_on_sync = 0; +int save_state = 0; uint8_t reset = 1; uint8_t need_reset = 0; @@ -195,9 +208,16 @@ void sync_sound(genesis_context * gen, uint32_t target) { //printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2); + while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { + uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; + //printf("Running PSG to cycle %d\n", cur_target); + psg_run(gen->psg, cur_target); + //printf("Running YM-2612 to cycle %d\n", cur_target); + ym_run(gen->ym, cur_target); + } psg_run(gen->psg, target); ym_run(gen->ym, target); - + //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); } @@ -219,7 +239,7 @@ } //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); - + if (!headless) { break_on_sync |= wait_render_frame(v_context, frame_limit); } @@ -256,10 +276,20 @@ context->int_ack = 0; } adjust_int_cycle(context, v_context); - if (break_on_sync && address) { + if (address) { + if (break_on_sync) { break_on_sync = 0; debugger(context, address); } + if (save_state) { + save_state = 0; + while (!z_context->pc) + { + sync_z80(z_context, z_context->current_cycle * MCLKS_PER_Z80 + MCLKS_PER_Z80); + } + save_gst(gen, "savestate.gst", address); + } + } return context; } @@ -282,6 +312,7 @@ vdp_run_dma_done(v_context, mclks_per_frame); if (v_context->cycles >= mclks_per_frame) { if (!headless) { + //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles); wait_render_frame(v_context, frame_limit); } vdp_adjust_cycles(v_context, mclks_per_frame); @@ -299,7 +330,7 @@ } } } - context->current_cycle = v_context->cycles / MCLKS_PER_68K; + //context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if(vdp_port < 8) { blocked = vdp_control_port_write(v_context, value); @@ -332,7 +363,6 @@ blocked = 0; } } - context->current_cycle = v_context->cycles / MCLKS_PER_68K; } else { adjust_int_cycle(context, v_context); } @@ -341,6 +371,7 @@ exit(1); } if (v_context->cycles != before_cycle) { + //printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { @@ -380,7 +411,7 @@ sync_sound(gen, context->current_cycle * MCLKS_PER_Z80); psg_write(gen->psg, value); } else { - //TODO: Implement undocumented test register(s) + vdp_test_port_write(gen->vdp, value); } return context; } @@ -395,6 +426,7 @@ uint16_t value; sync_components(context, 0); vdp_context * v_context = context->video_context; + uint32_t before_cycle = v_context->cycles; if (vdp_port < 0x10) { if (vdp_port < 4) { value = vdp_data_port_read(v_context); @@ -404,10 +436,15 @@ value = vdp_hv_counter_read(v_context); //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); } - context->current_cycle = v_context->cycles/MCLKS_PER_68K; + } else if (vdp_port < 0x18){ + printf("Illegal read from PSG port %X\n", vdp_port); + exit(1); } else { - printf("Illegal read from PSG or test register port %X\n", vdp_port); - exit(1); + value = vdp_test_port_read(v_context); + } + if (v_context->cycles != before_cycle) { + //printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); + context->current_cycle = v_context->cycles / MCLKS_PER_68K; } return value; } @@ -489,20 +526,21 @@ } } else { if (location == 0x1100) { - sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); if (busack_cycle <= context->current_cycle) { busack = new_busack; busack_cycle = CYCLE_NEVER; } if (value & 1) { dputs("bus requesting Z80"); - + if(!reset && !busreq) { - busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; + sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K + Z80_ACK_DELAY*MCLKS_PER_Z80); + busack_cycle = (gen->z80->current_cycle * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; new_busack = Z80_REQ_ACK; } busreq = 1; } else { + sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); if (busreq) { dputs("releasing z80 bus"); #ifdef DO_DEBUG_PRINT @@ -518,7 +556,7 @@ } //busack_cycle = CYCLE_NEVER; //busack = Z80_REQ_BUSY; - + } } else if (location == 0x1200) { sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); @@ -1433,7 +1471,7 @@ //Z80 debug commands switch(input_buf[1]) { - case 'b': + case 'b': param = find_param(input_buf); if (!param) { fputs("zb command requires a parameter\n", stderr); @@ -1463,188 +1501,15 @@ return context; } -#define GST_68K_REGS 0x80 -#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) -#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS) -#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS) -#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS) -#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS) -#define GST_68K_RAM 0x2478 -#define GST_Z80_REGS 0x404 -#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS) -#define GST_Z80_RAM 0x474 - -uint32_t read_le_32(uint8_t * data) -{ - return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; -} - -uint16_t read_le_16(uint8_t * data) -{ - return data[1] << 8 | data[0]; -} - -uint16_t read_be_16(uint8_t * data) -{ - return data[0] << 8 | data[1]; -} - -uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile) -{ - uint8_t buffer[4096]; - fseek(gstfile, GST_68K_REGS, SEEK_SET); - if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { - fputs("Failed to read 68K registers from savestate\n", stderr); - return 0; - } - uint8_t * curpos = buffer; - for (int i = 0; i < 8; i++) { - context->dregs[i] = read_le_32(curpos); - curpos += sizeof(uint32_t); - } - for (int i = 0; i < 8; i++) { - context->aregs[i] = read_le_32(curpos); - curpos += sizeof(uint32_t); - } - uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET); - uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET); - context->status = sr >> 8; - for (int flag = 4; flag >= 0; flag--) { - context->flags[flag] = sr & 1; - sr >>= 1; - } - if (context->status & (1 << 5)) { - context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET); - } else { - context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET); - } - fseek(gstfile, GST_68K_RAM, SEEK_SET); - for (int i = 0; i < (32*1024);) { - if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { - fputs("Failed to read 68K RAM from savestate\n", stderr); - return 0; - } - for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { - context->mem_pointers[1][i++] = read_be_16(curpos); - } - } - return pc; -} - -uint8_t z80_load_gst(z80_context * context, FILE * gstfile) +void set_speed_percent(genesis_context * context, uint32_t percent) { - uint8_t regdata[GST_Z80_REG_SIZE]; - fseek(gstfile, GST_Z80_REGS, SEEK_SET); - if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { - fputs("Failed to read Z80 registers from savestate\n", stderr); - return 0; - } - uint8_t * curpos = regdata; - uint8_t f = *(curpos++); - context->flags[ZF_C] = f & 1; - f >>= 1; - context->flags[ZF_N] = f & 1; - f >>= 1; - context->flags[ZF_PV] = f & 1; - f >>= 2; - context->flags[ZF_H] = f & 1; - f >>= 2; - context->flags[ZF_Z] = f & 1; - f >>= 1; - context->flags[ZF_S] = f; - - context->regs[Z80_A] = *curpos; - curpos += 3; - for (int reg = Z80_C; reg <= Z80_IYH; reg++) { - context->regs[reg++] = *(curpos++); - context->regs[reg] = *curpos; - curpos += 3; - } - uint16_t pc = read_le_16(curpos); - curpos += 4; - context->sp = read_le_16(curpos); - curpos += 4; - f = *(curpos++); - context->alt_flags[ZF_C] = f & 1; - f >>= 1; - context->alt_flags[ZF_N] = f & 1; - f >>= 1; - context->alt_flags[ZF_PV] = f & 1; - f >>= 2; - context->alt_flags[ZF_H] = f & 1; - f >>= 2; - context->alt_flags[ZF_Z] = f & 1; - f >>= 1; - context->alt_flags[ZF_S] = f; - context->alt_regs[Z80_A] = *curpos; - curpos += 3; - for (int reg = Z80_C; reg <= Z80_H; reg++) { - context->alt_regs[reg++] = *(curpos++); - context->alt_regs[reg] = *curpos; - curpos += 3; - } - context->regs[Z80_I] = *curpos; - curpos += 2; - context->iff1 = context->iff2 = *curpos; - curpos += 2; - reset = !*(curpos++); - busreq = *curpos; - curpos += 3; - uint32_t bank = read_le_32(curpos); - if (bank < 0x400000) { - context->mem_pointers[1] = context->mem_pointers[2] + bank; - } else { - context->mem_pointers[1] = NULL; - } - context->bank_reg = bank >> 15; - fseek(gstfile, GST_Z80_RAM, SEEK_SET); - if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { - fputs("Failed to read Z80 RAM from savestate\n", stderr); - return 0; - } - context->native_pc = z80_get_native_address_trans(context, pc); - return 1; + uint32_t old_clock = context->master_clock; + context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; + while (context->ym->current_cycle != context->psg->cycles) { + sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); } - -uint32_t load_gst(genesis_context * gen, char * fname) -{ - FILE * gstfile = fopen(fname, "rb"); - if (!gstfile) { - fprintf(stderr, "Could not open file %s for reading\n", fname); - goto error; - } - char ident[5]; - if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) { - fprintf(stderr, "Could not read ident code from %s\n", fname); - goto error_close; - } - if (memcmp(ident, "GST\xE0\x40", 3) != 0) { - fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\xE0\\x40.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]); - goto error_close; - } - uint32_t pc = m68k_load_gst(gen->m68k, gstfile); - if (!pc) { - goto error_close; - } - if (!vdp_load_gst(gen->vdp, gstfile)) { - goto error_close; - } - if (!ym_load_gst(gen->ym, gstfile)) { - goto error_close; - } - if (!z80_load_gst(gen->z80, gstfile)) { - goto error_close; - } - gen->ports[0].control = 0x40; - gen->ports[1].control = 0x40; - adjust_int_cycle(gen->m68k, gen->vdp); - fclose(gstfile); - return pc; - -error_close: - fclose(gstfile); -error: - return 0; + ym_adjust_master_clock(context->ym, context->master_clock); + psg_adjust_master_clock(context->psg, context->master_clock); } #define ROM_END 0x1A4 @@ -1658,7 +1523,7 @@ const memmap_chunk static_map[] = { {0, 0x400000, 0xFFFFFF, 0, MMAP_READ, cart, NULL, NULL, NULL, NULL}, - {0xE00000, 0x1000000, 0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, + {0xE00000, 0x1000000, 0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, NULL, NULL, NULL, NULL}, {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, NULL, (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, @@ -1711,7 +1576,7 @@ memmap[0].mask = 0xFFFFFF; memmap[0].flags = MMAP_READ; memmap[0].buffer = cart; - + ram_start &= 0xFFFFFE; ram_end |= 1; memmap[1].start = ram_start; @@ -1728,7 +1593,7 @@ size /= 2; } memmap[1].buffer = gen->save_ram = malloc(size); - + memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0])); num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1; } else { @@ -1737,7 +1602,7 @@ memmap[0].mask = 0xFFFFFF; memmap[0].flags = MMAP_READ; memmap[0].buffer = cart; - + memmap[1].start = 0x200000; memmap[1].end = 0x400000; memmap[1].mask = 0x1FFFFF; @@ -1757,7 +1622,7 @@ memmap[num_chunks].end = 0xA13100; memmap[num_chunks].mask = 0xFF; memmap[num_chunks].write_16 = (write_16_fun)write_bank_reg_w; - memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; + memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; num_chunks++; ram_end++; size = ram_end-ram_start; @@ -1786,7 +1651,7 @@ init_x86_68k_opts(&opts, memmap, num_chunks); opts.address_log = address_log; init_68k_context(&context, opts.native_code_map, &opts); - + context.video_context = gen->vdp; context.system = gen; //cartridge ROM @@ -1803,11 +1668,15 @@ if (statefile) { uint32_t pc = load_gst(gen, statefile); if (!pc) { + fprintf(stderr, "Failed to load save state %s\n", statefile); exit(1); } + printf("Loaded %s\n", statefile); if (debug) { insert_breakpoint(&context, pc, (uint8_t *)debugger); } + adjust_int_cycle(gen->m68k, gen->vdp); + gen->z80->native_pc = z80_get_native_address_trans(gen->z80, gen->z80->pc); start_68k_context(&context, pc); } else { if (debug) { @@ -1865,40 +1734,61 @@ version_reg = NO_DISK | JAP; } else if (detect_specific_region('E') || detect_specific_region('A')) { version_reg = NO_DISK | EUR; + } else { + char * def_region = tern_find_ptr(config, "default_region"); + if (def_region) { + switch(*def_region) + { + case 'j': + case 'J': + version_reg = NO_DISK | JAP; + break; + case 'u': + case 'U': + version_reg = NO_DISK | USA; + break; + case 'e': + case 'E': + version_reg = NO_DISK | EUR; + break; + } + } } } int main(int argc, char ** argv) { if (argc < 2) { - fputs("Usage: blastem FILENAME\n", stderr); + fputs("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n", stderr); return 1; } - if(!load_rom(argv[1])) { - fprintf(stderr, "Failed to open %s for reading\n", argv[1]); - return 1; - } + config = load_config(argv[0]); detect_region(); int width = -1; int height = -1; int debug = 0; int ym_log = 0; + int loaded = 0; + uint8_t force_version = 0; + char * romfname = NULL; FILE *address_log = NULL; char * statefile = NULL; - for (int i = 2; i < argc; i++) { + uint8_t fullscreen = 0; + for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { case 'd': debug = 1; break; case 'f': - frame_limit = 1; + fullscreen = 1; break; case 'l': address_log = fopen("address.log", "w"); break; case 'v': - headless = 1; + printf("blastem %s\n", BLASTEM_VERSION); + return 0; break; case 'n': z80_enabled = 0; @@ -1913,15 +1803,15 @@ { case 'j': case 'J': - version_reg = NO_DISK | JAP; + force_version = NO_DISK | JAP; break; case 'u': case 'U': - version_reg = NO_DISK | USA; + force_version = NO_DISK | USA; break; case 'e': case 'E': - version_reg = NO_DISK | EUR; + force_version = NO_DISK | EUR; break; default: fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]); @@ -1939,18 +1829,55 @@ case 'y': ym_log = 1; break; + case 'h': + puts( + "Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n" + "Options:\n" + " -h Print this help text\n" + " -r (J|U|E) Force region to Japan, US or Europe respectively\n" + " -f Start in fullscreen mode\n" + " -s FILE Load a GST format savestate from FILE\n" + " -d Enter debugger on startup\n" + " -n Disable Z80\n" + " -v Display version number and exit\n" + " -l Log 68K code addresses (useful for assemblers)\n" + " -y Log individual YM-2612 channels to WAVE files\n" + ); + return 0; default: fprintf(stderr, "Unrecognized switch %s\n", argv[i]); return 1; } + } else if (!loaded) { + if(!load_rom(argv[i])) { + fprintf(stderr, "Failed to open %s for reading\n", argv[i]); + return 1; + } + romfname = argv[i]; + loaded = 1; } else if (width < 0) { width = atoi(argv[i]); } else if (height < 0) { height = atoi(argv[i]); } } + if (!loaded) { + fputs("You must specify a ROM filename!\n", stderr); + return 1; + } + if (force_version) { + version_reg = force_version; + } update_title(); - width = width < 320 ? 640 : width; + int def_width = 0; + char *config_width = tern_find_ptr(config, "videowidth"); + if (config_width) { + def_width = atoi(config_width); + } + if (!def_width) { + def_width = 640; + } + width = width < 320 ? def_width : width; height = height < 240 ? (width/320) * 240 : height; uint32_t fps = 60; if (version_reg & 0x40) { @@ -1958,41 +1885,41 @@ fps = 50; } if (!headless) { - render_init(width, height, title, fps, 0); + render_init(width, height, title, fps, fullscreen, 0); } vdp_context v_context; - + genesis_context gen; + memset(&gen, 0, sizeof(gen)); + gen.master_clock = gen.normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL; + init_vdp_context(&v_context); - + ym2612_context y_context; - ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0); - + ym_init(&y_context, render_sample_rate(), gen.master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0); + psg_context p_context; - psg_init(&p_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_PSG, render_audio_buffer()); - + psg_init(&p_context, render_sample_rate(), gen.master_clock, MCLKS_PER_PSG, render_audio_buffer()); + z80_context z_context; x86_z80_options z_opts; init_x86_z80_opts(&z_opts); init_z80_context(&z_context, &z_opts); - genesis_context gen; - memset(&gen, 0, sizeof(gen)); - z_context.system = &gen; z_context.mem_pointers[0] = z80_ram; z_context.sync_cycle = z_context.target_cycle = mclks_per_frame/MCLKS_PER_Z80; z_context.int_cycle = CYCLE_NEVER; z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart; - + gen.z80 = &z_context; gen.vdp = &v_context; gen.ym = &y_context; gen.psg = &p_context; genesis = &gen; - - int fname_size = strlen(argv[1]); + + int fname_size = strlen(romfname); sram_filename = malloc(fname_size+6); - memcpy(sram_filename, argv[1], fname_size); + memcpy(sram_filename, romfname, fname_size); int i; for (i = fname_size-1; fname_size >= 0; --i) { if (sram_filename[i] == '.') { @@ -2004,7 +1931,7 @@ strcpy(sram_filename + fname_size, ".sram"); } set_keybindings(); - + init_run_cpu(&gen, debug, address_log, statefile); return 0; } diff -r 7696d824489d -r c08a4efeee7f blastem.h --- a/blastem.h Tue Jul 23 23:01:03 2013 -0700 +++ b/blastem.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef BLASTEM_H_ #define BLASTEM_H_ @@ -8,6 +13,7 @@ #include "vdp.h" #include "psg.h" #include "io.h" +#include "config.h" #define RAM_FLAG_ODD 0x1800 #define RAM_FLAG_EVEN 0x1000 @@ -24,15 +30,22 @@ uint8_t *save_ram; uint32_t save_ram_mask; uint32_t save_flags; + uint32_t master_clock; //Current master clock value + uint32_t normal_clock; //Normal master clock (used to restore master clock after turbo mode) uint8_t bank_regs[8]; io_port ports[3]; } genesis_context; extern genesis_context * genesis; extern int break_on_sync; +extern int save_state; +extern tern_node * config; +extern uint8_t busreq; +extern uint8_t reset; uint16_t read_dma_value(uint32_t address); m68k_context * debugger(m68k_context * context, uint32_t address); +void set_speed_percent(genesis_context * context, uint32_t percent); #endif //BLASTEM_H_ diff -r 7696d824489d -r c08a4efeee7f comparetests.py --- a/comparetests.py Tue Jul 23 23:01:03 2013 -0700 +++ b/comparetests.py Sat Oct 26 22:38:47 2013 -0700 @@ -31,7 +31,7 @@ if not good: continue try: - b = subprocess.check_output(['./blastem', path, '-v']) + b = subprocess.check_output(['./trans', path]) try: m = subprocess.check_output(['musashi/mustrans', path]) #_,_,b = b.partition('\n') diff -r 7696d824489d -r c08a4efeee7f config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,234 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "tern.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +char * alloc_concat(char * first, char * second) +{ + int flen = strlen(first); + int slen = strlen(second); + char * ret = malloc(flen + slen + 1); + memcpy(ret, first, flen); + memcpy(ret+flen, second, slen+1); + return ret; +} + +char * alloc_concat_m(int num_parts, char ** parts) +{ + int total = 0; + for (int i = 0; i < num_parts; i++) { + total += strlen(parts[i]); + } + char * ret = malloc(total + 1); + *ret = 0; + for (int i = 0; i < num_parts; i++) { + strcat(ret, parts[i]); + } + return ret; +} + +long file_size(FILE * f) +{ + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + return fsize; +} + +char * strip_ws(char * text) +{ + while (*text && (!isprint(*text) || isblank(*text))) + { + text++; + } + char * ret = text; + text = ret + strlen(ret) - 1; + while (text > ret && (!isprint(*text) || isblank(*text))) + { + *text = 0; + text--; + } + return ret; +} + +char * split_keyval(char * text) +{ + while (*text && !isblank(*text)) + { + text++; + } + if (!*text) { + return text; + } + *text = 0; + return text+1; +} + +#define MAX_NEST 30 //way more than I'll ever need + +tern_node * parse_config(char * config_data) +{ + char *state, *curline; + char *prefix = NULL; + int nest_level = 0; + char * prefix_parts[MAX_NEST]; + int line = 1; + tern_node * head = NULL; + while ((curline = strtok_r(config_data, "\n", &state))) + { + config_data = NULL; + curline = strip_ws(curline); + int len = strlen(curline); + if (!len) { + continue; + } + if (curline[0] == '#') { + continue; + } + if (curline[0] == '}') { + if (!nest_level) { + fprintf(stderr, "unexpected } on line %d\n", line); + exit(1); + } + if (prefix) { + free(prefix); + prefix = NULL; + } + nest_level--; + curline = strip_ws(curline+1); + } + char * end = curline + len - 1; + if (*end == '{') { + *end = 0; + curline = strip_ws(curline); + prefix_parts[nest_level++] = curline; + if (prefix) { + free(prefix); + prefix = NULL; + } + } else { + if (nest_level && !prefix) { + prefix = alloc_concat_m(nest_level, prefix_parts); + } + char * val = strip_ws(split_keyval(curline)); + char * key = curline; + if (*key) { + if (prefix) { + key = alloc_concat(prefix, key); + } + head = tern_insert_ptr(head, key, strdup(val)); + if (prefix) { + free(key); + } + } + } + } + if (prefix) { + free(prefix); + } + return head; +} + +tern_node * parse_config_file(char * config_path) +{ + tern_node * ret = NULL; + FILE * config_file = fopen(config_path, "r"); + if (!config_file) { + goto open_fail; + } + long config_size = file_size(config_file); + if (!config_size) { + goto config_empty; + } + char * config_data = malloc(config_size); + if (fread(config_data, 1, config_size, config_file) != config_size) { + goto config_read_fail; + } + ret = parse_config(config_data); +config_read_fail: + free(config_data); +config_empty: + fclose(config_file); +open_fail: + return ret; +} + +char * readlink_alloc(char * path) +{ + char * linktext = NULL; + ssize_t linksize = 512; + ssize_t cursize = 0; + do { + if (linksize > cursize) { + cursize = linksize; + if (linktext) { + free(linktext); + } + } + linktext = malloc(cursize); + linksize = readlink(path, linktext, cursize-1); + if (linksize == -1) { + perror("readlink"); + free(linktext); + linktext = NULL; + } + } while (linksize > cursize); + return linktext; +} + +tern_node * load_config(char * expath) +{ + char * linktext; + char * home = getenv("HOME"); + if (!home) { + goto load_in_app_dir; + } + char * path = alloc_concat(home, "/.config/blastem/blastem.cfg"); + tern_node * ret = parse_config_file(path); + if (ret) { + goto success; + } + free(path); +load_in_app_dir: + + linktext = readlink_alloc("/proc/self/exe"); + if (!linktext) { + goto link_prob; + } + char * cur; + int linksize = strlen(linktext); + for(cur = linktext + linksize - 1; cur != linktext; cur--) + { + if (*cur == '/') { + *cur = 0; + break; + } + } + if (cur == linktext) { + goto link_prob; + } + path = alloc_concat(linktext, "/default.cfg"); + ret = parse_config_file(path); +success: + return ret; +link_prob: + if (linktext) { + free(linktext); + } +no_proc: + //TODO: Fall back to using expath if /proc is not available + fputs("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n", stderr); + exit(1); +} + diff -r 7696d824489d -r c08a4efeee7f config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config.h Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,13 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#ifndef CONFIG_H_ +#define CONFIG_H_ +#include "tern.h" + +tern_node * load_config(char * expath); + +#endif //CONFIG_H_ + diff -r 7696d824489d -r c08a4efeee7f default.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/default.cfg Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,79 @@ + +bindings { + keys { + up gamepads.1.up + down gamepads.1.down + left gamepads.1.left + right gamepads.1.right + a gamepads.1.a + s gamepads.1.b + d gamepads.1.c + q gamepads.1.x + w gamepads.1.y + e gamepads.1.z + f gamepads.1.mode + enter gamepads.1.start + + [ ui.vdp_debug_mode + ] ui.vdp_debug_pal + u ui.enter_debugger + esc ui.exit + ` ui.save_state + 0 ui.set_speed.0 + 1 ui.set_speed.1 + 2 ui.set_speed.2 + 3 ui.set_speed.3 + 4 ui.set_speed.4 + 5 ui.set_speed.5 + 6 ui.set_speed.6 + 7 ui.set_speed.7 + = ui.next_speed + - ui.prev_speed + } + pads { + 0 { + dpads { + 0 { + up gamepads.2.up + down gamepads.2.down + left gamepads.2.left + right gamepads.2.right + } + } + buttons { + 0 gamepads.2.a + 1 gamepads.2.b + 2 gamepads.2.c + 3 gamepads.2.x + 4 gamepads.2.y + 5 gamepads.2.z + 6 gamepads.2.mode + 7 gamepads.2.start + } + } + } +} + +video { + width 640 +} + +audio { + rate 48000 + buffer 512 +} + +clocks { + speeds { + 1 150 + 2 200 + 3 300 + 4 400 + 5 25 + 6 50 + 7 75 + } +} + +default_region U + diff -r 7696d824489d -r c08a4efeee7f dis.c --- a/dis.c Tue Jul 23 23:01:03 2013 -0700 +++ b/dis.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include #include diff -r 7696d824489d -r c08a4efeee7f gdb_remote.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gdb_remote.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,128 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "blastem.h" +#include +#include +#include +#include +#include + +#define INITIAL_BUFFER_SIZE 4096 + +char * buf = NULL; +char * curbuf = NULL; +size_t bufsize; +int cont = 0; +int expect_break_response=0; +uint32_t resume_pc; + +void gdb_debug_enter(genesis_context * gen, uint32_t pc) +{ + resume_pc = pc; + while(!cont) + { + } + cont = 0; +} + +void gdb_run_command(genesis_context * gen, char * command) +{ + switch(*command) + { + case 'c': + if (*(command+1) != 0) { + resume_pc = + } + cont = 1; + expect_break_response = 1; + break; + case 's': + + } +} + +void gdb_run_commands(genesis_context * gen) +{ + int enter_debugger = 0; + char * cur = buf; + while(cur < curbuf); + { + if(*cur == '$') { + cur++ + char * start = cur; + while (cur < curbuf && *cur != '#') { + cur++; + } + if (*cur == '#') { + //check to make sure we've received the checksum bytes + if (curbuf-cur >= 2) { + //TODO: verify checksum + //Null terminate payload + //send acknowledgement + write(FILENO_STDOUT, "+", 1); + gdb_run_command(genesis_context * gen, start); + cur += 2; + } else { + cur = start - 1; + break; + } + } else { + cur = start - 1; + break; + } + } else { + if (*cur == 0x03) { + enter_debugger = 1; + } + cur++; + } + } + + //FIXME + if (consumed == curbuf-buf) { + curbuf = buf; + } else if (consumed > 0) { + memmove(buf, buf + consumed, curbuf - buf - consumed); + curbuf -= consumed; + } +} + +void gdb_command_poll(genesis_context * gen) +{ + for(;;) + { + if (curbuf == buf + bufsize) { + //buffer is full, expand it + bufsize *= 2; + buf = realloc(buf, bufsize); + if (!buf) { + fprintf(stderr, "Failed to grow GDB command buffer to %d bytes\n", (int)bufsize); + exit(1); + } + curbuf = buf + bufsize/2; + } + int numread = read(STDIN_FILENO, buf, bufsize - (curbuf-buf)); + if (numread < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } else { + fprintf(stderr, "Error %d while reading GDB commands from stdin", errno); + exit(1); + } + } else if (numread == 0) { + exit(0); + } + gdb_run_commands(genesis_context * gen); + } +} + +void gdb_remote_init() +{ + fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK); + buf = malloc(INITIAL_BUFFER_SIZE); + curbuf = buf; + bufzie = INITIAL_BUFFER_SIZE; +} diff -r 7696d824489d -r c08a4efeee7f gen_x86.c --- a/gen_x86.c Tue Jul 23 23:01:03 2013 -0700 +++ b/gen_x86.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "68kinst.h" #include @@ -25,6 +30,7 @@ #define PRE_SIZE 0x66 #define OP_JCC 0x70 #define OP_IMMED_ARITH 0x80 +#define OP_TEST 0x84 #define OP_XCHG 0x86 #define OP_MOV 0x88 #define OP_XCHG_AX 0x90 @@ -425,7 +431,7 @@ uint8_t * x86_ir(uint8_t * out, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, int32_t val, uint8_t dst, uint8_t size) { uint8_t sign_extend = 0; - if ((size == SZ_D || size == SZ_Q) && val <= 0x7F && val >= -0x80) { + if (opcode != OP_NOT_NEG && (size == SZ_D || size == SZ_Q) && val <= 0x7F && val >= -0x80) { sign_extend = 1; opcode |= BIT_DIR; } @@ -1018,6 +1024,31 @@ return x86_rrdisp8_sizedir(out, OP_CMP, dst, src_base, disp, size, BIT_DIR); } +uint8_t * test_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) +{ + return x86_rr_sizedir(out, OP_TEST, src, dst, size); +} + +uint8_t * test_ir(uint8_t * out, int32_t val, uint8_t dst, uint8_t size) +{ + return x86_ir(out, OP_NOT_NEG, OP_EX_TEST_I, OP_TEST, val, dst, size); +} + +uint8_t * test_irdisp8(uint8_t * out, int32_t val, uint8_t dst_base, int8_t disp, uint8_t size) +{ + return x86_irdisp8(out, OP_NOT_NEG, OP_EX_TEST_I, val, dst_base, disp, size); +} + +uint8_t * test_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size) +{ + return x86_rrdisp8_sizedir(out, OP_TEST, src, dst_base, disp, size, 0); +} + +uint8_t * test_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size) +{ + return x86_rrdisp8_sizedir(out, OP_TEST, dst, src_base, disp, size, BIT_DIR); +} + uint8_t * imul_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) { return x86_rr_sizedir(out, OP2_IMUL | (PRE_2BYTE << 8), dst, src, size); @@ -1134,7 +1165,7 @@ } uint8_t * mov_ir(uint8_t * out, int64_t val, uint8_t dst, uint8_t size) -{ +{ uint8_t sign_extend = 0; if (size == SZ_Q && val <= 0x7FFFFFFF && val >= -2147483648) { sign_extend = 1; diff -r 7696d824489d -r c08a4efeee7f gen_x86.h --- a/gen_x86.h Tue Jul 23 23:01:03 2013 -0700 +++ b/gen_x86.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef GEN_X86_H_ #define GEN_X86_H_ @@ -149,6 +154,11 @@ uint8_t * imul_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * div_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * idiv_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size); +uint8_t * test_ir(uint8_t * out, int32_t val, uint8_t dst, uint8_t size); +uint8_t * test_irdisp8(uint8_t * out, int32_t val, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size); uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size); uint8_t * mov_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * mov_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size); diff -r 7696d824489d -r c08a4efeee7f gentests.py --- a/gentests.py Tue Jul 23 23:01:03 2013 -0700 +++ b/gentests.py Sat Oct 26 22:38:47 2013 -0700 @@ -72,6 +72,16 @@ def get_dreg(self): return Register('d', self.avail_dregs.pop()) +class Dummy(object): + def __str__(self): + return '' + def write_init(self, outfile, size, already): + pass + def consume_regs(self, program): + pass + +dummy_op = Dummy() + class Register(object): def __init__(self, kind, num): self.kind = kind @@ -441,30 +451,47 @@ self.src.consume_regs(program) self.dst.consume_regs(program) +class Inst1Op(Inst2Op): + def __init__(self, name, size, dst): + super(Inst1Op, self).__init__(name, size, dummy_op, dst) + + def __str__(self): + return self.name + '.' + self.size + ' ' + str(self.dst) + class Entry(object): def __init__(self, line): fields = split_fields(line) self.name = fields[0] sizes = fields[1] sources = fields[2].split(';') - dests = fields[3].split(';') + if len(fields) > 3: + dests = fields[3].split(';') + else: + dests = None combos = [] for size in sizes: for source in sources: if size != 'b' or source != 'a': - for dest in dests: - if size != 'b' or dest != 'a': - combos.append((size, source, dest)) + if dests: + for dest in dests: + if size != 'b' or dest != 'a': + combos.append((size, source, dest)) + else: + combos.append((size, None, source)) self.cases = combos def programs(self): res = [] for (size, src, dst) in self.cases: - sources = get_variations(src, size) dests = get_variations(dst, size) - for source in sources: + if src: + sources = get_variations(src, size) + for source in sources: + for dest in dests: + res.append(Program(Inst2Op(self.name, size, source, dest))) + else: for dest in dests: - res.append(Program(Inst2Op(self.name, size, source, dest))) + res.append(Program(Inst1Op(self.name, size, dest))) return res def process_entries(f): diff -r 7696d824489d -r c08a4efeee7f gst.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gst.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,483 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "gst.h" +#include + +#define GST_68K_REGS 0x80 +#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) +#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS) +#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS) +#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS) +#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS) +#define GST_68K_RAM 0x2478 +#define GST_Z80_REGS 0x404 +#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS) +#define GST_Z80_RAM 0x474 +#define GST_VDP_REGS 0xFA +#define GST_VDP_MEM 0x12478 +#define GST_YM_OFFSET 0x1E4 +#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET) + +uint32_t read_le_32(uint8_t * data) +{ + return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; +} + +uint16_t read_le_16(uint8_t * data) +{ + return data[1] << 8 | data[0]; +} + +uint16_t read_be_16(uint8_t * data) +{ + return data[0] << 8 | data[1]; +} + +void write_le_32(uint8_t * dst, uint32_t val) +{ + dst[0] = val; + dst[1] = val >> 8; + dst[2] = val >> 16; + dst[3] = val >> 24; +} + +void write_le_16(uint8_t * dst, uint16_t val) +{ + dst[0] = val; + dst[1] = val >> 8; +} + +void write_be_32(uint8_t * dst, uint32_t val) +{ + dst[0] = val >> 24; + dst[1] = val >> 16; + dst[2] = val >> 8; + dst[3] = val; +} + +void write_be_16(uint8_t * dst, uint16_t val) +{ + dst[0] = val >> 8; + dst[1] = val; +} + +uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile) +{ + uint8_t buffer[4096]; + fseek(gstfile, GST_68K_REGS, SEEK_SET); + if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { + fputs("Failed to read 68K registers from savestate\n", stderr); + return 0; + } + uint8_t * curpos = buffer; + for (int i = 0; i < 8; i++) { + context->dregs[i] = read_le_32(curpos); + curpos += sizeof(uint32_t); + } + for (int i = 0; i < 8; i++) { + context->aregs[i] = read_le_32(curpos); + curpos += sizeof(uint32_t); + } + uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET); + uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET); + context->status = sr >> 8; + for (int flag = 4; flag >= 0; flag--) { + context->flags[flag] = sr & 1; + sr >>= 1; + } + if (context->status & (1 << 5)) { + context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET); + } else { + context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET); + } + fseek(gstfile, GST_68K_RAM, SEEK_SET); + for (int i = 0; i < (32*1024);) { + if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { + fputs("Failed to read 68K RAM from savestate\n", stderr); + return 0; + } + for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { + context->mem_pointers[1][i++] = read_be_16(curpos); + } + } + return pc; +} + +uint8_t m68k_save_gst(m68k_context * context, uint32_t pc, FILE * gstfile) +{ + uint8_t buffer[4096]; + uint8_t * curpos = buffer; + for (int i = 0; i < 8; i++) { + write_le_32(curpos, context->dregs[i]); + curpos += sizeof(uint32_t); + } + for (int i = 0; i < 8; i++) { + write_le_32(curpos, context->aregs[i]); + curpos += sizeof(uint32_t); + } + write_le_32(buffer + GST_68K_PC_OFFSET, pc); + uint16_t sr = context->status << 3; + for (int flag = 4; flag >= 0; flag--) { + sr <<= 1; + sr |= context->flags[flag]; + } + write_le_16(buffer + GST_68K_SR_OFFSET, sr); + if (context->status & (1 << 5)) { + write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[8]); + write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[7]); + } else { + write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[7]); + write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[8]); + } + fseek(gstfile, GST_68K_REGS, SEEK_SET); + if (fwrite(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { + fputs("Failed to write 68K registers to savestate\n", stderr); + return 0; + } + + fseek(gstfile, GST_68K_RAM, SEEK_SET); + for (int i = 0; i < (32*1024);) { + for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { + write_be_16(curpos, context->mem_pointers[1][i++]); + } + if (fwrite(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { + fputs("Failed to write 68K RAM to savestate\n", stderr); + return 0; + } + } + return 1; +} + +uint8_t z80_load_gst(z80_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_Z80_REG_SIZE]; + fseek(gstfile, GST_Z80_REGS, SEEK_SET); + if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + fputs("Failed to read Z80 registers from savestate\n", stderr); + return 0; + } + uint8_t * curpos = regdata; + uint8_t f = *(curpos++); + context->flags[ZF_C] = f & 1; + f >>= 1; + context->flags[ZF_N] = f & 1; + f >>= 1; + context->flags[ZF_PV] = f & 1; + f >>= 2; + context->flags[ZF_H] = f & 1; + f >>= 2; + context->flags[ZF_Z] = f & 1; + f >>= 1; + context->flags[ZF_S] = f; + + context->regs[Z80_A] = *curpos; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_IYH; reg++) { + context->regs[reg++] = *(curpos++); + context->regs[reg] = *curpos; + curpos += 3; + } + context->pc = read_le_16(curpos); + curpos += 4; + context->sp = read_le_16(curpos); + curpos += 4; + f = *(curpos++); + context->alt_flags[ZF_C] = f & 1; + f >>= 1; + context->alt_flags[ZF_N] = f & 1; + f >>= 1; + context->alt_flags[ZF_PV] = f & 1; + f >>= 2; + context->alt_flags[ZF_H] = f & 1; + f >>= 2; + context->alt_flags[ZF_Z] = f & 1; + f >>= 1; + context->alt_flags[ZF_S] = f; + context->alt_regs[Z80_A] = *curpos; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_H; reg++) { + context->alt_regs[reg++] = *(curpos++); + context->alt_regs[reg] = *curpos; + curpos += 3; + } + context->regs[Z80_I] = *curpos; + curpos += 2; + context->iff1 = context->iff2 = *curpos; + curpos += 2; + reset = !*(curpos++); + busreq = *curpos; + curpos += 3; + uint32_t bank = read_le_32(curpos); + if (bank < 0x400000) { + context->mem_pointers[1] = context->mem_pointers[2] + bank; + } else { + context->mem_pointers[1] = NULL; + } + context->bank_reg = bank >> 15; + fseek(gstfile, GST_Z80_RAM, SEEK_SET); + if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { + fputs("Failed to read Z80 RAM from savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) +{ + uint8_t tmp_buf[CRAM_SIZE*2]; + fseek(state_file, GST_VDP_REGS, SEEK_SET); + if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { + fputs("Failed to read VDP registers from savestate\n", stderr); + return 0; + } + context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); + if (!context->double_res) { + context->framebuf = context->oddbuf; + } + latch_mode(context); + if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { + fputs("Failed to read CRAM from savestate\n", stderr); + return 0; + } + for (int i = 0; i < CRAM_SIZE; i++) { + uint16_t value; + context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; + context->colors[i] = color_map[value & 0xEEE]; + context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; + context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; + } + if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { + fputs("Failed to read VSRAM from savestate\n", stderr); + return 0; + } + for (int i = 0; i < VSRAM_SIZE; i++) { + context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; + } + fseek(state_file, GST_VDP_MEM, SEEK_SET); + if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { + fputs("Failed to read VRAM from savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t vdp_save_gst(vdp_context * context, FILE * outfile) +{ + uint8_t tmp_buf[CRAM_SIZE*2]; + fseek(outfile, GST_VDP_REGS, SEEK_SET); + if(fwrite(context->regs, 1, VDP_REGS, outfile) != VDP_REGS) { + fputs("Error writing VDP regs to savestate\n", stderr); + return 0; + } + for (int i = 0; i < CRAM_SIZE; i++) + { + tmp_buf[i*2] = context->cram[i]; + tmp_buf[i*2+1] = context->cram[i] >> 8; + } + if (fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile) != sizeof(tmp_buf)) { + fputs("Error writing CRAM to savestate\n", stderr); + return 0; + } + for (int i = 0; i < VSRAM_SIZE; i++) + { + tmp_buf[i*2] = context->vsram[i]; + tmp_buf[i*2+1] = context->vsram[i] >> 8; + } + if (fwrite(tmp_buf, 2, VSRAM_SIZE, outfile) != VSRAM_SIZE) { + fputs("Error writing VSRAM to savestate\n", stderr); + return 0; + } + fseek(outfile, GST_VDP_MEM, SEEK_SET); + if (fwrite(context->vdpmem, 1, VRAM_SIZE, outfile) != VRAM_SIZE) { + fputs("Error writing VRAM to savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t z80_save_gst(z80_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_Z80_REG_SIZE]; + uint8_t * curpos = regdata; + memset(regdata, 0, sizeof(regdata)); + uint8_t f = context->flags[ZF_S]; + f <<= 1; + f |= context->flags[ZF_Z] ; + f <<= 2; + f |= context->flags[ZF_H]; + f <<= 2; + f |= context->flags[ZF_PV]; + f <<= 1; + f |= context->flags[ZF_N]; + f <<= 1; + f |= context->flags[ZF_C]; + *(curpos++) = f; + *curpos = context->regs[Z80_A]; + + curpos += 3; + for (int reg = Z80_C; reg <= Z80_IYH; reg++) { + *(curpos++) = context->regs[reg++]; + *curpos = context->regs[reg]; + curpos += 3; + } + write_le_16(curpos, context->pc); + curpos += 4; + write_le_16(curpos, context->sp); + curpos += 4; + f = context->alt_flags[ZF_S]; + f <<= 1; + f |= context->alt_flags[ZF_Z] ; + f <<= 2; + f |= context->alt_flags[ZF_H]; + f <<= 2; + f |= context->alt_flags[ZF_PV]; + f <<= 1; + f |= context->alt_flags[ZF_N]; + f <<= 1; + f |= context->alt_flags[ZF_C]; + *(curpos++) = f; + *curpos = context->alt_regs[Z80_A]; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_H; reg++) { + *(curpos++) = context->alt_regs[reg++]; + *curpos = context->alt_regs[reg]; + curpos += 3; + } + *curpos = context->regs[Z80_I]; + curpos += 2; + *curpos = context->iff1; + curpos += 2; + *(curpos++) = !reset; + *curpos = busreq; + curpos += 3; + uint32_t bank = context->bank_reg << 15; + write_le_32(curpos, bank); + fseek(gstfile, GST_Z80_REGS, SEEK_SET); + if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + fseek(gstfile, GST_Z80_RAM, SEEK_SET); + if(fwrite(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { + fputs("Failed to write Z80 RAM to savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_YM_SIZE]; + fseek(gstfile, GST_YM_OFFSET, SEEK_SET); + if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + for (int i = 0; i < sizeof(regdata); i++) { + if (i & 0x100) { + ym_address_write_part2(context, i & 0xFF); + } else { + ym_address_write_part1(context, i); + } + ym_data_write(context, regdata[i]); + } + return 1; +} + +uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_YM_SIZE]; + for (int i = 0; i < sizeof(regdata); i++) { + if (i & 0x100) { + int reg = (i & 0xFF); + if (reg >= YM_PART2_START && reg < YM_REG_END) { + regdata[i] = context->part2_regs[reg-YM_PART2_START]; + } else { + regdata[i] = 0xFF; + } + } else { + if (i >= YM_PART1_START && i < YM_REG_END) { + regdata[i] = context->part1_regs[i-YM_PART1_START]; + } else { + regdata[i] = 0xFF; + } + } + } + fseek(gstfile, GST_YM_OFFSET, SEEK_SET); + if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + return 1; +} + +uint32_t load_gst(genesis_context * gen, char * fname) +{ + FILE * gstfile = fopen(fname, "rb"); + if (!gstfile) { + fprintf(stderr, "Could not open file %s for reading\n", fname); + goto error; + } + char ident[5]; + if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) { + fprintf(stderr, "Could not read ident code from %s\n", fname); + goto error_close; + } + if (memcmp(ident, "GST\x40\xE0", 5) != 0) { + fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\x40\\xE0.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]); + goto error_close; + } + uint32_t pc = m68k_load_gst(gen->m68k, gstfile); + if (!pc) { + goto error_close; + } + if (!vdp_load_gst(gen->vdp, gstfile)) { + goto error_close; + } + if (!ym_load_gst(gen->ym, gstfile)) { + goto error_close; + } + if (!z80_load_gst(gen->z80, gstfile)) { + goto error_close; + } + gen->ports[0].control = 0x40; + gen->ports[1].control = 0x40; + fclose(gstfile); + return pc; + +error_close: + fclose(gstfile); +error: + return 0; +} + +uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc) +{ + FILE * gstfile = fopen(fname, "wb"); + if (!gstfile) { + fprintf(stderr, "Could not open %s for writing\n", fname); + goto error; + } + if (fwrite("GST\x40\xE0", 1, 5, gstfile) != 5) { + fputs("Error writing signature to savestate\n", stderr); + goto error_close; + } + if (!m68k_save_gst(gen->m68k, m68k_pc, gstfile)) { + goto error_close; + } + if (!z80_save_gst(gen->z80, gstfile)) { + goto error_close; + } + if (!vdp_save_gst(gen->vdp, gstfile)) { + goto error_close; + } + if (!ym_save_gst(gen->ym, gstfile)) { + goto error_close; + } + return 1; + +error_close: + fclose(gstfile); +error: + return 0; +} diff -r 7696d824489d -r c08a4efeee7f gst.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gst.h Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,13 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#ifndef GST_H_ +#define GST_H_ +#include "blastem.h" + +uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc); +uint32_t load_gst(genesis_context * gen, char * fname); + +#endif //GST_H_ diff -r 7696d824489d -r c08a4efeee7f io.c --- a/io.c Tue Jul 23 23:01:03 2013 -0700 +++ b/io.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "io.h" #include "blastem.h" #include "render.h" @@ -12,7 +17,12 @@ typedef enum { UI_DEBUG_MODE_INC, UI_DEBUG_PAL_INC, - UI_ENTER_DEBUGGER + UI_ENTER_DEBUGGER, + UI_SAVE_STATE, + UI_SET_SPEED, + UI_NEXT_SPEED, + UI_PREV_SPEED, + UI_EXIT } ui_action; typedef struct { @@ -106,7 +116,7 @@ void bind_gamepad(int keycode, int gamepadnum, int button) { - + if (gamepadnum < 1 || gamepadnum > 2) { return; } @@ -132,9 +142,9 @@ bind_dpad(joystick, dpad, direction, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); } -void bind_ui(int keycode, ui_action action) +void bind_ui(int keycode, ui_action action, uint8_t param) { - bind_key(keycode, BIND_UI, action, 0, 0); + bind_key(keycode, BIND_UI, action, 0, param); } void handle_binding_down(keybinding * binding) @@ -176,6 +186,10 @@ uint8_t ui_debug_mode = 0; uint8_t ui_debug_pal = 0; +int current_speed = 0; +int num_speeds = 1; +uint32_t * speeds = NULL; + void handle_binding_up(keybinding * binding) { switch(binding->bind_type) @@ -197,7 +211,7 @@ if (ui_debug_mode == 4) { ui_debug_mode = 0; } - render_debug_mode(ui_debug_mode); + genesis->vdp->debug = ui_debug_mode; break; case UI_DEBUG_PAL_INC: ui_debug_pal++; @@ -209,6 +223,37 @@ case UI_ENTER_DEBUGGER: break_on_sync = 1; break; + case UI_SAVE_STATE: + save_state = 1; + break; + case UI_NEXT_SPEED: + current_speed++; + if (current_speed >= num_speeds) { + current_speed = 0; + } + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + break; + case UI_PREV_SPEED: + current_speed--; + if (current_speed < 0) { + current_speed = num_speeds - 1; + } + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + break; + case UI_SET_SPEED: + if (binding->value < num_speeds) { + current_speed = binding->value; + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + } else { + printf("Setting speed to %d\n", speeds[current_speed]); + set_speed_percent(genesis, binding->value); + } + break; + case UI_EXIT: + exit(0); } break; } @@ -252,36 +297,233 @@ } } +int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, int * padnum_out, int * padbutton_out) +{ + int gpadslen = strlen("gamepads."); + if (!memcmp(target, "gamepads.", gpadslen)) { + if (target[gpadslen] >= '1' && target[gpadslen] <= '8') { + int padnum = target[gpadslen] - '0'; + int button = tern_find_int(padbuttons, target + gpadslen + 1, 0); + if (button) { + *padnum_out = padnum; + *padbutton_out = button; + return 1; + } else { + if (target[gpadslen+1]) { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1); + } else { + fprintf(stderr, "Gamepad mapping string '%s' has no button component\n", target); + } + } + } else { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); + } + } else if(!memcmp(target, "ui.", strlen("ui."))) { + *padbutton_out = 0; + if (!strcmp(target + 3, "vdp_debug_mode")) { + *ui_out = UI_DEBUG_MODE_INC; + } else if(!strcmp(target + 3, "vdp_debug_pal")) { + *ui_out = UI_DEBUG_PAL_INC; + } else if(!strcmp(target + 3, "enter_debugger")) { + *ui_out = UI_ENTER_DEBUGGER; + } else if(!strcmp(target + 3, "save_state")) { + *ui_out = UI_SAVE_STATE; + } else if(!memcmp(target + 3, "set_speed.", strlen("set_speed."))) { + *ui_out = UI_SET_SPEED; + *padbutton_out = atoi(target + 3 + strlen("set_speed.")); + } else if(!strcmp(target + 3, "next_speed")) { + *ui_out = UI_NEXT_SPEED; + } else if(!strcmp(target + 3, "prev_speed")) { + *ui_out = UI_PREV_SPEED; + } else if(!strcmp(target + 3, "exit")) { + *ui_out = UI_EXIT; + } else { + fprintf(stderr, "Unreconized UI binding type %s\n", target); + return 0; + } + return 2; + } else { + fprintf(stderr, "Unrecognized binding type %s\n", target); + } + return 0; +} + +void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, char * prefix) +{ + char * curstr = NULL; + int len; + if (!cur) { + return; + } + char onec[2]; + if (prefix) { + len = strlen(prefix); + curstr = malloc(len + 2); + memcpy(curstr, prefix, len); + } else { + curstr = onec; + len = 0; + } + curstr[len] = cur->el; + curstr[len+1] = 0; + if (cur->el) { + process_keys(cur->straight.next, special, padbuttons, curstr); + } else { + int keycode = tern_find_int(special, curstr, 0); + if (!keycode) { + keycode = curstr[0]; + if (curstr[1] != 0) { + fprintf(stderr, "%s is not recognized as a key identifier, truncating to %c\n", curstr, curstr[0]); + } + } + char * target = cur->straight.value.ptrval; + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_gamepad(keycode, padnum, button); + } else if(bindtype == 2) { + bind_ui(keycode, ui_func, button); + } + } + process_keys(cur->left, special, padbuttons, prefix); + process_keys(cur->right, special, padbuttons, prefix); + if (curstr && len) { + free(curstr); + } +} + +void process_speeds(tern_node * cur, char * prefix) +{ + char * curstr = NULL; + int len; + if (!cur) { + return; + } + char onec[2]; + if (prefix) { + len = strlen(prefix); + curstr = malloc(len + 2); + memcpy(curstr, prefix, len); + } else { + curstr = onec; + len = 0; + } + curstr[len] = cur->el; + curstr[len+1] = 0; + if (cur->el) { + process_speeds(cur->straight.next, curstr); + } else { + int speed_index = atoi(curstr); + if (speed_index < 1) { + if (!strcmp(curstr, "0")) { + fputs("Speed index 0 cannot be set to a custom value\n", stderr); + } else { + fprintf(stderr, "%s is not a valid speed index", curstr); + } + } else { + if (speed_index >= num_speeds) { + speeds = realloc(speeds, sizeof(uint32_t) * (speed_index+1)); + for(; num_speeds < speed_index + 1; num_speeds++) { + speeds[num_speeds] = 0; + } + } + speeds[speed_index] = atoi(cur->straight.value.ptrval); + } + } + process_speeds(cur->left, prefix); + process_speeds(cur->right, prefix); + if (curstr && len) { + free(curstr); + } +} + void set_keybindings() { - bind_gamepad(RENDERKEY_UP, 1, DPAD_UP); - bind_gamepad(RENDERKEY_DOWN, 1, DPAD_DOWN); - bind_gamepad(RENDERKEY_LEFT, 1, DPAD_LEFT); - bind_gamepad(RENDERKEY_RIGHT, 1, DPAD_RIGHT); - bind_gamepad('a', 1, BUTTON_A); - bind_gamepad('s', 1, BUTTON_B); - bind_gamepad('d', 1, BUTTON_C); - bind_gamepad('q', 1, BUTTON_X); - bind_gamepad('w', 1, BUTTON_Y); - bind_gamepad('e', 1, BUTTON_Z); - bind_gamepad('\r', 1, BUTTON_START); - bind_gamepad('f', 1, BUTTON_MODE); - bind_ui('[', UI_DEBUG_MODE_INC); - bind_ui(']', UI_DEBUG_PAL_INC); - bind_ui('u', UI_ENTER_DEBUGGER); - - bind_dpad_gamepad(0, 0, RENDER_DPAD_UP, 2, DPAD_UP); - bind_dpad_gamepad(0, 0, RENDER_DPAD_DOWN, 2, DPAD_DOWN); - bind_dpad_gamepad(0, 0, RENDER_DPAD_LEFT, 2, DPAD_LEFT); - bind_dpad_gamepad(0, 0, RENDER_DPAD_RIGHT, 2, DPAD_RIGHT); - bind_button_gamepad(0, 0, 2, BUTTON_A); - bind_button_gamepad(0, 1, 2, BUTTON_B); - bind_button_gamepad(0, 2, 2, BUTTON_C); - bind_button_gamepad(0, 3, 2, BUTTON_X); - bind_button_gamepad(0, 4, 2, BUTTON_Y); - bind_button_gamepad(0, 5, 2, BUTTON_Z); - bind_button_gamepad(0, 6, 2, BUTTON_START); - bind_button_gamepad(0, 7, 2, BUTTON_MODE); + tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP); + special = tern_insert_int(special, "down", RENDERKEY_DOWN); + special = tern_insert_int(special, "left", RENDERKEY_LEFT); + special = tern_insert_int(special, "right", RENDERKEY_RIGHT); + special = tern_insert_int(special, "enter", '\r'); + special = tern_insert_int(special, "esc", RENDERKEY_ESC); + + tern_node * padbuttons = tern_insert_int(NULL, ".up", DPAD_UP); + padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN); + padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT); + padbuttons = tern_insert_int(padbuttons, ".right", DPAD_RIGHT); + padbuttons = tern_insert_int(padbuttons, ".a", BUTTON_A); + padbuttons = tern_insert_int(padbuttons, ".b", BUTTON_B); + padbuttons = tern_insert_int(padbuttons, ".c", BUTTON_C); + padbuttons = tern_insert_int(padbuttons, ".x", BUTTON_X); + padbuttons = tern_insert_int(padbuttons, ".y", BUTTON_Y); + padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z); + padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START); + padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE); + + tern_node * keys = tern_find_prefix(config, "bindingskeys"); + process_keys(keys, special, padbuttons, NULL); + char prefix[] = "bindingspads00"; + for (int i = 0; i < 100 && i < render_num_joysticks(); i++) + { + if (i < 10) { + prefix[strlen("bindingspads")] = i + '0'; + prefix[strlen("bindingspads")+1] = 0; + } else { + prefix[strlen("bindingspads")] = i/10 + '0'; + prefix[strlen("bindingspads")+1] = i%10 + '0'; + } + tern_node * pad = tern_find_prefix(config, prefix); + if (pad) { + char dprefix[] = "dpads0"; + for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++) + { + dprefix[strlen("dpads")] = dpad + '0'; + tern_node * pad_dpad = tern_find_prefix(pad, dprefix); + char * dirs[] = {"up", "down", "left", "right"}; + int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT}; + for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) { + char * target = tern_find_ptr(pad_dpad, dirs[dir]); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button); + } + //TODO: Handle UI bindings + } + } + } + char bprefix[] = "buttons00"; + for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++) + { + if (but < 10) { + bprefix[strlen("buttons")] = but + '0'; + bprefix[strlen("buttons")+1] = 0; + } else { + bprefix[strlen("buttons")] = but/10 + '0'; + bprefix[strlen("buttons")+1] = but%10 + '0'; + } + char * target = tern_find_ptr(pad, bprefix); + if (target) { + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_button_gamepad(i, but, padnum, button); + } + //TODO: Handle UI bindings + } + } + } + } + tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); + speeds = malloc(sizeof(uint32_t)); + speeds[0] = 100; + process_speeds(speed_nodes, NULL); + for (int i = 0; i < num_speeds; i++) { + if (!speeds[i]) { + fprintf(stderr, "Speed index %d was not set to a valid percentage!", i); + speeds[i] = 100; + } + } } #define TH 0x40 diff -r 7696d824489d -r c08a4efeee7f io.h --- a/io.h Tue Jul 23 23:01:03 2013 -0700 +++ b/io.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef IO_H_ #define IO_H_ #include diff -r 7696d824489d -r c08a4efeee7f m68k_to_x86.c --- a/m68k_to_x86.c Tue Jul 23 23:01:03 2013 -0700 +++ b/m68k_to_x86.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "m68k_to_x86.h" #include "68kinst.h" @@ -123,9 +128,9 @@ //We only get one memory parameter, so if the dst operand is a register in memory, //we need to copy this to a temp register first reg = native_reg(&(inst->dst), opts); - if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) + if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) || inst->op == M68K_EXG) { - + ea->mode = MODE_REG_DISPLACE8; ea->base = CONTEXT; ea->disp = reg_offset(&(inst->src)); @@ -150,7 +155,7 @@ out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D); } case MODE_AREG_INDIRECT: - case MODE_AREG_POSTINC: + case MODE_AREG_POSTINC: if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { @@ -168,7 +173,7 @@ out = call(out, opts->read_32); break; } - + if (inst->src.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->src.params.regs.pri] >= 0) { @@ -441,7 +446,7 @@ out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } } - + if (inst->dst.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { @@ -781,7 +786,7 @@ dst = mov_ir(dst, 0, FLAG_V, SZ_B); dst = mov_ir(dst, 0, FLAG_C, SZ_B); } - + if (inst->dst.addr_mode != MODE_AREG) { if (src.mode == MODE_REG_DIRECT) { flags_reg = src.base; @@ -2459,7 +2464,7 @@ dst = pop_r(dst, SCRATCH2); dst = mov_rr(dst, reg, SCRATCH1, SZ_D); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); - + } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+3, SCRATCH1, SZ_B); dst = push_r(dst, SCRATCH2); @@ -2527,7 +2532,7 @@ dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); if (reg >= 0) { - + dst = shl_ir(dst, 8, SCRATCH1, SZ_W); dst = mov_rr(dst, SCRATCH1, reg, SZ_W); dst = pop_r(dst, SCRATCH1); @@ -2628,7 +2633,7 @@ } else { dst = mov_rdisp8r(dst, src_op->base, src_op->disp, RCX, SZ_B); } - + } dst = and_ir(dst, 63, RCX, SZ_D); nz_off = dst+1; @@ -2676,7 +2681,7 @@ if (inst->extra.size == OPSIZE_LONG) { uint8_t * neq_32_off = dst + 1; dst = jcc(dst, CC_NZ, dst+2); - + //set the carry bit to the lsb if (dst_op->mode == MODE_REG_DIRECT) { dst = special(dst, 1, dst_op->base, SZ_D); @@ -2703,7 +2708,7 @@ dst = shift_irdisp8(dst, 31, dst_op->base, dst_op->disp, inst->extra.size); dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size); } - + } end_off = dst+1; dst = jmp(dst, dst+2); @@ -2715,7 +2720,7 @@ } } } - + } if (!special && end_off) { *end_off = dst - (end_off + 1); @@ -2952,7 +2957,9 @@ case M68K_BCLR: case M68K_BSET: case M68K_BTST: - dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : 6); + dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : ( + inst->op == M68K_BTST ? 6 : (inst->op == M68K_BCLR ? 10 : 8)) + ); if (src_op.mode == MODE_IMMED) { if (inst->extra.size == OPSIZE_BYTE) { src_op.disp &= 0x7; @@ -3084,7 +3091,7 @@ default: isize = 2; } - uint8_t * passed = dst+1; + uint8_t * passed = dst+1; dst = jcc(dst, CC_GE, dst+2); dst = mov_ir(dst, 1, FLAG_N, SZ_B); dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D); @@ -3322,7 +3329,7 @@ } dst = call(dst, (uint8_t *)(inst->op == M68K_MOVE_SR ? set_sr : set_ccr)); dst = cycles(dst, 12); - + } break; case M68K_MOVE_USP: @@ -3446,7 +3453,7 @@ dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } - + dst = mov_ir(dst, 0, FLAG_C, SZ_B); dst = setcc_r(dst, CC_Z, FLAG_Z); dst = setcc_r(dst, CC_S, FLAG_N); @@ -3557,6 +3564,7 @@ dst = cmp_ir(dst, 32, SCRATCH1, SZ_B); norm_off = dst+1; dst = jcc(dst, CC_L, dst+2); + dst = sub_ir(dst, 32, SCRATCH1, SZ_B); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { dst = rol_ir(dst, 31, dst_op.base, inst->extra.size); @@ -3574,7 +3582,6 @@ dst = ror_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } } - dst = sub_ir(dst, 32, SCRATCH1, SZ_B); *norm_off = dst - (norm_off+1); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { @@ -3781,8 +3788,38 @@ } dst = m68k_save_result(inst, dst, opts); break; - /*case M68K_STOP: - break;*/ + case M68K_STOP: { + //TODO: Trap if not in system mode + //manual says 4 cycles, but it has to be at least 8 since it's a 2-word instruction + //possibly even 12 since that's how long MOVE to SR takes + dst = cycles(dst, BUS*2); + dst = mov_ir(dst, src_op.disp & 0x1, FLAG_C, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 1) & 0x1, FLAG_V, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 3) & 0x1, FLAG_N, SZ_B); + dst = mov_irind(dst, (src_op.disp >> 4) & 0x1, CONTEXT, SZ_B); + dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B); + if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) { + //leave supervisor mode + dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); + dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D); + dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); + } + uint8_t * loop_top = dst; + dst = call(dst, (uint8_t *)do_sync); + dst = cmp_rr(dst, LIMIT, CYCLES, SZ_D); + uint8_t * normal_cycle_up = dst + 1; + dst = jcc(dst, CC_A, dst+2); + dst = cycles(dst, BUS); + uint8_t * after_cycle_up = dst + 1; + dst = jmp(dst, dst+2); + *normal_cycle_up = dst - (normal_cycle_up + 1); + dst = mov_rr(dst, LIMIT, CYCLES, SZ_D); + *after_cycle_up = dst - (after_cycle_up+1); + dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); + dst = jcc(dst, CC_C, loop_top); + break; + } case M68K_SUB: size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size; dst = cycles(dst, BUS); @@ -3840,9 +3877,12 @@ dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { dst = rol_ir(dst, 16, src_op.base, SZ_D); + dst = cmp_ir(dst, 0, src_op.base, SZ_D); } else{ dst = rol_irdisp8(dst, 16, src_op.base, src_op.disp, SZ_D); - } + dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, SZ_D); + } + dst = mov_ir(dst, 0, FLAG_C, SZ_B); dst = setcc_r(dst, CC_Z, FLAG_Z); dst = setcc_r(dst, CC_S, FLAG_N); @@ -3912,7 +3952,7 @@ m68kinst instbuf; x86_68k_options * opts = context->options; uint8_t * dst = opts->cur_code; - uint8_t * dst_end = opts->code_end; + uint8_t * dst_end = opts->code_end; address &= 0xFFFFFF; if(get_native_address(opts->native_code_map, address)) { return dst; @@ -4040,7 +4080,7 @@ return orig_start; } } - + map_native_address(context, instbuf.address, dst, (after-inst)*2, MAX_NATIVE_SIZE); opts->cur_code = dst+MAX_NATIVE_SIZE; jmp(orig_start, dst); @@ -4087,12 +4127,12 @@ } bp_stub = dst; native = call(native, bp_stub); - + //Calculate length of prologue dst = check_cycles_int(dst, address, opts); int check_int_size = dst-bp_stub; dst = bp_stub; - + //Save context and call breakpoint handler dst = call(dst, (uint8_t *)m68k_save_context); dst = push_r(dst, SCRATCH1); @@ -4170,7 +4210,7 @@ ub_jcc = dst + 1; dst = jcc(dst, CC_NC, dst+2); } - + if (memmap[chunk].mask != 0xFFFFFF) { dst = and_ir(dst, memmap[chunk].mask, adr_reg, SZ_D); } @@ -4206,7 +4246,17 @@ dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, cfun); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); + dst = call(dst, cfun); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust + 1); if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); } else { @@ -4214,7 +4264,7 @@ dst = mov_rr(dst, RAX, SCRATCH1, size); } dst = jmp(dst, (uint8_t *)m68k_load_context); - + *not_null = dst - (not_null + 1); } if (size == SZ_B) { @@ -4223,7 +4273,7 @@ dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, adr_reg, SZ_Q); if (is_write) { dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size); - + } else { dst = mov_rindr(dst, SCRATCH1, SCRATCH1, size); } @@ -4299,7 +4349,17 @@ dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, cfun); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); + dst = call(dst, cfun); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust+1); if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); } else { @@ -4333,7 +4393,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks) { - opts->flags = 0; + memset(opts, 0, sizeof(*opts)); for (int i = 0; i < 8; i++) opts->dregs[i] = opts->aregs[i] = -1; opts->dregs[0] = R10; @@ -4352,14 +4412,14 @@ opts->code_end = opts->cur_code + size; opts->ram_inst_sizes = malloc(sizeof(uint8_t *) * 64); memset(opts->ram_inst_sizes, 0, sizeof(uint8_t *) * 64); - + opts->read_16 = gen_mem_fun(opts, memmap, num_chunks, READ_16); opts->read_8 = gen_mem_fun(opts, memmap, num_chunks, READ_8); opts->write_16 = gen_mem_fun(opts, memmap, num_chunks, WRITE_16); opts->write_8 = gen_mem_fun(opts, memmap, num_chunks, WRITE_8); - + uint8_t * dst = opts->cur_code; - + opts->read_32 = dst; dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_16); @@ -4373,7 +4433,7 @@ dst = shl_ir(dst, 16, SCRATCH2, SZ_D); dst = or_rr(dst, SCRATCH2, SCRATCH1, SZ_D); dst = retn(dst); - + opts->write_32_lowfirst = dst; dst = push_r(dst, SCRATCH2); dst = push_r(dst, SCRATCH1); @@ -4383,7 +4443,7 @@ dst = pop_r(dst, SCRATCH2); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); dst = jmp(dst, opts->write_16); - + opts->write_32_highfirst = dst; dst = push_r(dst, SCRATCH1); dst = push_r(dst, SCRATCH2); @@ -4393,7 +4453,7 @@ dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = jmp(dst, opts->write_16); - + opts->handle_cycle_limit_int = dst; dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); uint8_t * do_int = dst+1; @@ -4404,7 +4464,17 @@ dst = call(dst, (uint8_t *)m68k_save_context); dst = mov_rr(dst, CONTEXT, RDI, SZ_Q); dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, (uint8_t *)sync_components); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); + dst = call(dst, (uint8_t *)sync_components); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust+1); dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); dst = jmp(dst, (uint8_t *)m68k_load_context); *skip_sync = dst - (skip_sync+1); @@ -4445,7 +4515,7 @@ //discard function return address dst = pop_r(dst, SCRATCH2); dst = jmp_r(dst, SCRATCH1); - + opts->trap = dst; dst = push_r(dst, SCRATCH2); //swap USP and SSP if not already in supervisor mode @@ -4474,7 +4544,7 @@ dst = call(dst, (uint8_t *)m68k_native_addr_and_sync); dst = cycles(dst, 18); dst = jmp_r(dst, SCRATCH1); - + opts->cur_code = dst; } diff -r 7696d824489d -r c08a4efeee7f m68k_to_x86.h --- a/m68k_to_x86.h Tue Jul 23 23:01:03 2013 -0700 +++ b/m68k_to_x86.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef M68K_TO_X86_H_ #define M68K_TO_X86_H_ #include diff -r 7696d824489d -r c08a4efeee7f mem.c --- a/mem.c Tue Jul 23 23:01:03 2013 -0700 +++ b/mem.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include diff -r 7696d824489d -r c08a4efeee7f mem.h --- a/mem.h Tue Jul 23 23:01:03 2013 -0700 +++ b/mem.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef MEM_H_ #define MEM_H_ diff -r 7696d824489d -r c08a4efeee7f psg.c --- a/psg.c Tue Jul 23 23:01:03 2013 -0700 +++ b/psg.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "psg.h" #include "render.h" #include @@ -8,15 +13,23 @@ memset(context, 0, sizeof(*context)); context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); - double clock_rate = (double)master_clock / (double)clock_div; - context->buffer_inc = ((double)sample_rate / (double)master_clock) * clock_div; context->clock_inc = clock_div; + context->sample_rate = sample_rate; context->samples_frame = samples_frame; + psg_adjust_master_clock(context, master_clock); for (int i = 0; i < 4; i++) { context->volume[i] = 0xF; } } +#define BUFFER_INC_RES 1000000000UL + +void psg_adjust_master_clock(psg_context * context, uint32_t master_clock) +{ + uint64_t old_inc = context->buffer_inc; + context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc; +} + void psg_write(psg_context * context, uint8_t value) { if (value & 0x80) { @@ -65,7 +78,7 @@ //table shamelessly swiped from PSG doc from smspower.org int16_t volume_table[16] = { 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV, - 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, + 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0 }; @@ -92,8 +105,8 @@ } } context->buffer_fraction += context->buffer_inc; - if (context->buffer_fraction >= 1.0) { - context->buffer_fraction -= 1.0; + if (context->buffer_fraction >= BUFFER_INC_RES) { + context->buffer_fraction -= BUFFER_INC_RES; int16_t acc = 0; for (int i = 0; i < 3; i++) { if (context->output_state[i]) { diff -r 7696d824489d -r c08a4efeee7f psg.h --- a/psg.h Tue Jul 23 23:01:03 2013 -0700 +++ b/psg.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef PSG_CONTEXT_H_ #define PSG_CONTEXT_H_ @@ -6,11 +11,12 @@ typedef struct { int16_t *audio_buffer; int16_t *back_buffer; - double buffer_fraction; - double buffer_inc; + uint64_t buffer_fraction; + uint64_t buffer_inc; uint32_t buffer_pos; uint32_t clock_inc; uint32_t cycles; + uint32_t sample_rate; uint32_t samples_frame; uint16_t lsfr; uint16_t counter_load[4]; @@ -25,6 +31,7 @@ void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame); +void psg_adjust_master_clock(psg_context * context, uint32_t master_clock); void psg_write(psg_context * context, uint8_t value); void psg_run(psg_context * context, uint32_t cycles); diff -r 7696d824489d -r c08a4efeee7f render.h --- a/render.h Tue Jul 23 23:01:03 2013 -0700 +++ b/render.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef RENDER_H_ #define RENDER_H_ @@ -12,9 +17,9 @@ } surface_info; uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); -surface_info render_alloc_surfaces(); +void render_alloc_surfaces(vdp_context * context); uint8_t render_depth(); -void render_init(int width, int height, char * title, uint32_t fps, uint8_t use_gl); +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen, uint8_t use_gl); void render_context(vdp_context * context); void render_wait_quit(vdp_context * context); void render_wait_psg(psg_context * context); @@ -28,6 +33,7 @@ void process_events(); int render_joystick_num_buttons(int joystick); int render_joystick_num_hats(int joystick); +int render_num_joysticks(); //TODO: Throw an ifdef in here once there's more than one renderer #include @@ -35,6 +41,7 @@ #define RENDERKEY_DOWN SDLK_DOWN #define RENDERKEY_LEFT SDLK_LEFT #define RENDERKEY_RIGHT SDLK_RIGHT +#define RENDERKEY_ESC SDLK_ESCAPE #define RENDER_DPAD_UP SDL_HAT_UP #define RENDER_DPAD_DOWN SDL_HAT_DOWN #define RENDER_DPAD_LEFT SDL_HAT_LEFT diff -r 7696d824489d -r c08a4efeee7f render_sdl.c --- a/render_sdl.c Tue Jul 23 23:01:03 2013 -0700 +++ b/render_sdl.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include "render.h" @@ -5,7 +10,9 @@ #include "io.h" #ifndef DISABLE_OPENGL +#define GL_GLEXT_PROTOTYPES #include +#include #endif SDL_Surface *screen; @@ -78,6 +85,11 @@ SDL_Joystick * joysticks[MAX_JOYSTICKS]; int num_joysticks; +int render_num_joysticks() +{ + return num_joysticks; +} + uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) { if (render_gl) { @@ -113,7 +125,7 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); if (i < 2) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8​, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf); } else { uint32_t blank = 255; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); @@ -135,7 +147,9 @@ return screen->format->BytesPerPixel * 8; } -void render_init(int width, int height, char * title, uint32_t fps, uint8_t use_gl) +char * caption = NULL; + +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen, uint8_t use_gl) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); @@ -144,7 +158,7 @@ atexit(SDL_Quit); atexit(render_close_audio); printf("width: %d, height: %d\n", width, height); - uint32_t flags + uint32_t flags = SDL_ANYFORMAT; #ifndef DISABLE_OPENGL if (use_gl) { @@ -158,7 +172,11 @@ #else { #endif - flags = SDL_SWSURFACE | SDL_ANYFORMAT; + if (fullscreen) { + flags |= SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; + } else { + flags |= SDL_SWSURFACE; + } } screen = SDL_SetVideoMode(width, height, 32, flags); if (!screen) { @@ -174,6 +192,7 @@ render_gl = use_gl; #endif SDL_WM_SetCaption(title, title); + caption = title; min_delay = 0; for (int i = 0; i < 100; i++) { uint32_t start = SDL_GetTicks(); @@ -196,10 +215,21 @@ audio_ready = SDL_CreateCond(); SDL_AudioSpec desired, actual; - desired.freq = 48000; + char * rate_str = tern_find_ptr(config, "audiorate"); + int rate = rate_str ? atoi(rate_str) : 0; + if (!rate) { + rate = 48000; + } + desired.freq = rate; desired.format = AUDIO_S16SYS; desired.channels = 2; - desired.samples = 2048;//1024; + char * samples_str = tern_find_ptr(config, "audiobuffer"); + int samples = samples_str ? atoi(samples_str) : 0; + if (!samples) { + samples = 512; + } + printf("config says: %d\n", samples); + desired.samples = samples*2; desired.callback = audio_callback; desired.userdata = NULL; @@ -298,7 +328,8 @@ if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } - SDL_UpdateRect(screen, 0, 0, screen->clip_rect.w, screen->clip_rect.h); + //SDL_UpdateRect(screen, 0, 0, screen->clip_rect.w, screen->clip_rect.h); + SDL_Flip(screen); if (context->regs[REG_MODE_4] & BIT_INTERLACE) { context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; @@ -385,6 +416,8 @@ return 0; } +char * fps_caption = NULL; + uint32_t frame_counter = 0; uint32_t start = 0; int wait_render_frame(vdp_context * context, int frame_limit) @@ -411,15 +444,19 @@ //TODO: Figure out why this causes segfaults - /*frame_counter++; + frame_counter++; if ((last_frame - start) > 1000) { if (start && (last_frame-start)) { - printf("\r%f fps", ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); + if (!fps_caption) { + fps_caption = malloc(strlen(caption) + strlen(" - 1000.1 fps") + 1); + } + sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); + SDL_WM_SetCaption(fps_caption, caption); fflush(stdout); } start = last_frame; frame_counter = 0; - }*/ + } return ret; } diff -r 7696d824489d -r c08a4efeee7f runtime.S --- a/runtime.S Tue Jul 23 23:01:03 2013 -0700 +++ b/runtime.S Sat Oct 26 22:38:47 2013 -0700 @@ -10,14 +10,22 @@ call m68k_save_context mov %rsi, %rdi xor %esi, %esi + test $8, %esp + jnz adjust_rsp call sync_components + jmp done_adjust +adjust_rsp: + sub $8, %rsp + call sync_components + add $8, %rsp +done_adjust: mov %rax, %rsi call m68k_load_context pop %rdi pop %rcx skip_sync: ret - + sr_msg_int: .asciz "SR set to $%X due to interrupt\n" debug_print_sr_int: @@ -47,7 +55,7 @@ invalid_msg: .asciz "Invalid instruction at %X\n" - + .global m68k_invalid m68k_invalid: lea invalid_msg(%rip), %rdi @@ -60,7 +68,7 @@ .global bcd_add bcd_add: xchg %rax, %rdi - + mov %cl, %ch mov %al, %ah and $0xF, %ch @@ -82,14 +90,14 @@ add $0x60, %cl mov $1, %ch no_adjust_h: - + mov %rdi, %rax ret .global bcd_sub bcd_sub: xchg %rax, %rdi - + mov %cl, %ch mov %al, %ah and $0xF, %ch @@ -111,7 +119,7 @@ sub $0x60, %cl mov $1, %ch no_adjust_hs: - + mov %rdi, %rax ret @@ -168,7 +176,7 @@ and $1, %cl mov %cl, (%rsi) ret - + .global m68k_modified_ret_addr m68k_modified_ret_addr: add $16, %rsp @@ -181,7 +189,15 @@ push %rcx mov %rsi, %rdi xor %esi, %esi + test $8, %rsp + jnz adjust_rsp_na call sync_components + jmp no_adjust_rsp_na +adjust_rsp_na: + sub $8, %rsp + call sync_components + add $8, %rsp +no_adjust_rsp_na: pop %rsi push %rax mov %rax, %rdi @@ -202,7 +218,7 @@ pop %rsi call m68k_load_context ret - + .global m68k_retrans_stub m68k_retrans_stub: call m68k_save_context @@ -255,15 +271,15 @@ push %r13 push %r14 push %r15 - + call m68k_load_context call *%rdi call m68k_save_context - + pop %r15 pop %r14 pop %r13 pop %r12 pop %rbp - + ret diff -r 7696d824489d -r c08a4efeee7f stateview.c --- a/stateview.c Tue Jul 23 23:01:03 2013 -0700 +++ b/stateview.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include "vdp.h" @@ -7,12 +12,48 @@ //not used, but referenced by the renderer since it handles input io_port gamepad_1; io_port gamepad_2; +uint8_t reset = 1; +uint8_t busreq = 0; uint16_t read_dma_value(uint32_t address) { return 0; } +void ym_data_write(ym2612_context * context, uint8_t value) +{ +} + +void ym_address_write_part1(ym2612_context * context, uint8_t address) +{ +} + +void ym_address_write_part2(ym2612_context * context, uint8_t address) +{ +} + +void handle_keydown(int keycode) +{ +} + +void handle_keyup(int keycode) +{ +} + +void handle_joydown(int joystick, int button) +{ +} + +void handle_joyup(int joystick, int button) +{ +} + +void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) +{ +} + +tern_node * config; + int main(int argc, char ** argv) { if (argc < 2) { @@ -24,23 +65,33 @@ fprintf(stderr, "Failed to open %s\n", argv[1]); exit(1); } - int width = 320; - int height = 240; + config = load_config(argv[0]); + int width = -1; + int height = -1; if (argc > 2) { width = atoi(argv[2]); if (argc > 3) { height = atoi(argv[3]); - } else { - height = (width/320) * 240; } } + int def_width = 0; + char *config_width = tern_find_ptr(config, "videowidth"); + if (config_width) { + def_width = atoi(config_width); + } + if (!def_width) { + def_width = 640; + } + width = width < 320 ? def_width : width; + height = height < 240 ? (width/320) * 240 : height; + vdp_context context; + render_init(width, height, "GST State Viewer", 60, 0); init_vdp_context(&context); vdp_load_gst(&context, state_file); vdp_run_to_vblank(&context); vdp_print_sprite_table(&context); printf("Display %s\n", (context.regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); - render_init(width, height); render_context(&context); render_wait_quit(&context); return 0; diff -r 7696d824489d -r c08a4efeee7f tern.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tern.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,120 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "tern.h" +#include +#include + +tern_node * tern_insert(tern_node * head, char * key, tern_val value) +{ + tern_node ** cur = &head; + while(*key) + { + if (*cur) { + while(*cur && (*cur)->el != *key) + { + if (*key < (*cur)->el) { + cur = &(*cur)->left; + } else { + cur = &(*cur)->right; + } + } + } + if (!*cur) { + *cur = malloc(sizeof(tern_node)); + (*cur)->left = NULL; + (*cur)->right = NULL; + (*cur)->straight.next = NULL; + (*cur)->el = *key; + } + cur = &((*cur)->straight.next); + key++; + } + while(*cur && (*cur)->el) + { + cur = &(*cur)->left; + } + if (!*cur) { + *cur = malloc(sizeof(tern_node)); + (*cur)->left = NULL; + (*cur)->right = NULL; + (*cur)->el = 0; + } + (*cur)->straight.value = value; + return head; +} + +int tern_find(tern_node * head, char * key, tern_val *ret) +{ + tern_node * cur = head; + while (cur) + { + if (cur->el == *key) { + if (*key) { + cur = cur->straight.next; + key++; + } else { + *ret = cur->straight.value; + return 1; + } + } else if (*key < cur->el) { + cur = cur->left; + } else { + cur = cur->right; + } + } + return 0; +} + +tern_node * tern_find_prefix(tern_node * head, char * key) +{ + tern_node * cur = head; + while (cur && *key) + { + if (cur->el == *key) { + cur = cur->straight.next; + key++; + } else if (*key < cur->el) { + cur = cur->left; + } else { + cur = cur->right; + } + } + return cur; +} + +intptr_t tern_find_int(tern_node * head, char * key, intptr_t def) +{ + tern_val ret; + if (tern_find(head, key, &ret)) { + return ret.intval; + } + return def; +} + +tern_node * tern_insert_int(tern_node * head, char * key, intptr_t value) +{ + tern_val val; + val.intval = value; + return tern_insert(head, key, val); +} + +void * tern_find_ptr(tern_node * head, char * key) +{ + tern_val ret; + if (tern_find(head, key, &ret)) { + return ret.ptrval; + } + return NULL; +} + +tern_node * tern_insert_ptr(tern_node * head, char * key, void * value) +{ + tern_val val; + val.ptrval = value; + return tern_insert(head, key, val); +} + + diff -r 7696d824489d -r c08a4efeee7f tern.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tern.h Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,34 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#ifndef TERN_H_ +#define TERN_H_ + +#include + +typedef union { + void *ptrval; + intptr_t intval; +} tern_val; + +typedef struct tern_node { + struct tern_node *left; + union { + struct tern_node *next; + tern_val value; + } straight; + struct tern_node *right; + char el; +} tern_node; + +tern_node * tern_insert(tern_node * head, char * key, tern_val value); +int tern_find(tern_node * head, char * key, tern_val *ret); +tern_node * tern_find_prefix(tern_node * head, char * key); +intptr_t tern_find_int(tern_node * head, char * key, intptr_t def); +tern_node * tern_insert_int(tern_node * head, char * key, intptr_t value); +void * tern_find_ptr(tern_node * head, char * key); +tern_node * tern_insert_ptr(tern_node * head, char * key, void * value); + +#endif //TERN_H_ diff -r 7696d824489d -r c08a4efeee7f test_x86.c --- a/test_x86.c Tue Jul 23 23:01:03 2013 -0700 +++ b/test_x86.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "m68k_to_x86.h" #include diff -r 7696d824489d -r c08a4efeee7f testcases.txt --- a/testcases.txt Tue Jul 23 23:01:03 2013 -0700 +++ b/testcases.txt Sat Oct 26 22:38:47 2013 -0700 @@ -48,12 +48,40 @@ #cmpa wl d;a;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) a #cmpi bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l #cmpm bwl (a)+ (a)+ -eor bwl d d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -eori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -exg l d d;a -exg l a a -link w a #n -or bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) d -or bwl d (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -ori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#eor bwl d d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#eori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#exg l d d;a +#exg l a a +#link w a #n +#or bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) d +#or bwl d (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#clr bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ext wl d +#neg bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#negx bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#not bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#pea l (a);(n,a);(n,a,x);(n).w;(n).l;(n,pc);(n,pc,x) +#rol w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ror w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#roxl w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#roxr w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#st b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sf b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#shi b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sls b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#scc b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#scs b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sne b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#seq b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#svc b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#svs b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#spl b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#smi b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sge b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#slt b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sgt b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sle b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#swap w d +tst bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l diff -r 7696d824489d -r c08a4efeee7f testgst.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testgst.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,83 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "gst.h" +#include +#include + +uint8_t busreq; +uint8_t reset; + +int32_t color_map[1 << 12]; + +void latch_mode(vdp_context * context) +{ +} +void ym_data_write(ym2612_context * context, uint8_t value) +{ + if (context->selected_reg >= YM_REG_END) { + return; + } + if (context->selected_part) { + if (context->selected_reg < YM_PART2_START) { + return; + } + context->part2_regs[context->selected_reg - YM_PART2_START] = value; + } else { + if (context->selected_reg < YM_PART1_START) { + return; + } + context->part1_regs[context->selected_reg - YM_PART1_START] = value; + } +} + +void ym_address_write_part1(ym2612_context * context, uint8_t address) +{ + //printf("address_write_part1: %X\n", address); + context->selected_reg = address; + context->selected_part = 0; +} + +void ym_address_write_part2(ym2612_context * context, uint8_t address) +{ + //printf("address_write_part2: %X\n", address); + context->selected_reg = address; + context->selected_part = 1; +} + +uint16_t ram[64*1024]; +uint8_t zram[8*1024]; + + +int main(int argc, char ** argv) +{ + vdp_context vdp; + ym2612_context ym; + psg_context psg; + m68k_context m68k; + z80_context z80; + genesis_context gen; + if (argc < 3) { + fputs("Usage: testgst infile outfile\n", stderr); + return 1; + } + memset(&gen, 0, sizeof(gen)); + memset(&m68k, 0, sizeof(m68k)); + memset(&z80, 0, sizeof(z80)); + memset(&ym, 0, sizeof(ym)); + memset(&vdp, 0, sizeof(vdp)); + memset(&psg, 0, sizeof(psg)); + m68k.mem_pointers[1] = ram; + z80.mem_pointers[0] = zram; + vdp.vdpmem = malloc(VRAM_SIZE); + gen.vdp = &vdp; + gen.ym = &ym; + gen.psg = &psg; + gen.m68k = &m68k; + gen.z80 = &z80; + uint32_t pc = load_gst(&gen, argv[1]); + save_gst(&gen, argv[2], pc); + return 0; +} diff -r 7696d824489d -r c08a4efeee7f testtern.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testtern.c Sat Oct 26 22:38:47 2013 -0700 @@ -0,0 +1,26 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ +#include "tern.h" +#include +#include + +int main(int argc, char ** argv) +{ + tern_node * tree = tern_insert_ptr(NULL, "foo", "bar"); + tree = tern_insert_ptr(tree, "foobar", "baz"); + tree = tern_insert_ptr(tree, "goobar", "qux"); + tree = tern_insert_int(tree, "foobarbaz", 42); + tree = tern_insert_int(tree, "goobarbaz", 21); + printf("foo: %s\n", (char *)tern_find_ptr(tree, "foo")); + printf("foobar: %s\n", (char *)tern_find_ptr(tree, "foobar")); + printf("goobar: %s\n", (char *)tern_find_ptr(tree, "goobar")); + printf("foob: %s\n", (char *)tern_find_ptr(tree, "foob")); + printf("foobarbaz: %d\n", (int)tern_find_int(tree, "foobarbaz", 0)); + printf("goobarbaz: %d\n", (int)tern_find_int(tree, "goobarbaz", 0)); + printf("foobarb: %d\n", (int)tern_find_int(tree, "foobarb", 0)); + return 0; +} + diff -r 7696d824489d -r c08a4efeee7f todo.txt --- a/todo.txt Tue Jul 23 23:01:03 2013 -0700 +++ b/todo.txt Sat Oct 26 22:38:47 2013 -0700 @@ -6,10 +6,10 @@ |--Banked mem complete |--Test gen complete -PSG not started 9 -YM-2612 not started 9 +PSG complete 9 +YM-2612 partial 9 H-Ints complete 8 -Interlace not started 7 +Interlace complete 7 save states partial 6 mappers partial 5 save ram complete 5 diff -r 7696d824489d -r c08a4efeee7f trans.c --- a/trans.c Tue Jul 23 23:01:03 2013 -0700 +++ b/trans.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,8 +1,22 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include "m68k_to_x86.h" #include "mem.h" #include #include +#include + +m68k_context * sync_components(m68k_context * context, uint32_t address) +{ + if (context->current_cycle > 0x80000000) { + context->current_cycle -= 0x80000000; + } + return context; +} int main(int argc, char ** argv) { @@ -16,23 +30,34 @@ fseek(f, 0, SEEK_END); filesize = ftell(f); fseek(f, 0, SEEK_SET); - filebuf = malloc(filesize); + filebuf = malloc(filesize > 0x400000 ? filesize : 0x400000); fread(filebuf, 2, filesize/2, f); fclose(f); for(cur = filebuf; cur - filebuf < (filesize/2); ++cur) { *cur = (*cur >> 8) | (*cur << 8); } - init_x86_68k_opts(&opts); + memmap_chunk memmap[2]; + memset(memmap, 0, sizeof(memmap_chunk)*2); + memmap[0].end = 0x400000; + memmap[0].mask = 0xFFFFFF; + memmap[0].flags = MMAP_READ; + memmap[0].buffer = filebuf; + + memmap[1].start = 0xE00000; + memmap[1].end = 0x1000000; + memmap[1].mask = 0xFFFF; + memmap[1].flags = MMAP_READ | MMAP_WRITE | MMAP_CODE; + memmap[1].buffer = malloc(64 * 1024); + init_x86_68k_opts(&opts, memmap, 2); init_68k_context(&context, opts.native_code_map, &opts); - //cartridge ROM - context.mem_pointers[0] = filebuf; - context.target_cycle = 0x7FFFFFFF; - //work RAM - context.mem_pointers[1] = malloc(64 * 1024); + context.mem_pointers[0] = memmap[0].buffer; + context.mem_pointers[1] = memmap[1].buffer; + context.target_cycle = context.sync_cycle = 0x80000000; uint32_t address; address = filebuf[2] << 16 | filebuf[3]; translate_m68k_stream(address, &context); m68k_reset(&context); return 0; } + diff -r 7696d824489d -r c08a4efeee7f transz80.c --- a/transz80.c Tue Jul 23 23:01:03 2013 -0700 +++ b/transz80.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "mem.h" diff -r 7696d824489d -r c08a4efeee7f vdp.c --- a/vdp.c Tue Jul 23 23:01:03 2013 -0700 +++ b/vdp.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "vdp.h" #include "blastem.h" #include @@ -12,9 +17,8 @@ #define MAP_BIT_V_FLIP 0x1000 #define SCROLL_BUFFER_SIZE 32 -#define SCROLL_BUFFER_DRAW 16 - -#define FIFO_SIZE 4 +#define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) +#define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) #define MCLKS_SLOT_H40 16 #define MCLKS_SLOT_H32 20 @@ -26,10 +30,19 @@ #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32) #define HBLANK_CLEAR_H40 (MCLK_WEIRD_END+61*4) #define HBLANK_CLEAR_H32 (HSYNC_END_H32 + 46*5) +#define FIFO_LATENCY 3 int32_t color_map[1 << 12]; uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; +uint8_t debug_base[][3] = { + {127, 127, 127}, //BG + {0, 0, 127}, //A + {127, 0, 0}, //Window + {0, 127, 0}, //B + {127, 0, 127} //Sprites +}; + uint8_t color_map_init_done; void init_vdp_context(vdp_context * context) @@ -46,8 +59,8 @@ context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; context->sprite_draws = MAX_DRAWS; - context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE); - context->fifo_end = context->fifo_cur + FIFO_SIZE; + context->fifo_write = 0; + context->fifo_read = -1; context->b32 = render_depth() == 32; if (!color_map_init_done) { uint8_t b,g,r; @@ -69,13 +82,67 @@ } color_map_init_done = 1; } + for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++) + { + uint8_t src = color & DBG_SRC_MASK; + if (src > DBG_SRC_S) { + context->debugcolors[color] = 0; + } else { + uint8_t r,g,b; + b = debug_base[src][0]; + g = debug_base[src][1]; + r = debug_base[src][2]; + if (color & DBG_PRIORITY) + { + if (b) { + b += 48; + } + if (g) { + g += 48; + } + if (r) { + r += 48; + } + } + if (color & DBG_SHADOW) { + b /= 2; + g /= 2; + r /=2 ; + } + if (color & DBG_HILIGHT) { + if (b) { + b += 72; + } + if (g) { + g += 72; + } + if (r) { + r += 72; + } + } + context->debugcolors[color] = render_map_color(r, g, b); + } + } +} + +int is_refresh(vdp_context * context, uint32_t slot) +{ + if (context->latched_mode & BIT_H40) { + return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210); + } else { + //TODO: Figure out which slots are refresh when display is off in 32-cell mode + //These numbers are guesses based on H40 numbers + return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152); + //The numbers below are the refresh slots during active display + //return (slot == 66 || slot == 98 || slot == 130 || slot == 162); + } } void render_sprite_cells(vdp_context * context) { if (context->cur_slot >= context->sprite_draws) { sprite_draw * d = context->sprite_draw_list + context->cur_slot; - + uint16_t dir; int16_t x; if (d->h_flip) { @@ -114,7 +181,7 @@ uint16_t link = context->vdpmem[address+3] & 0x7F; uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; uint8_t pri = context->vdpmem[address + 4] >> 7; - uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; + uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); current_index = link; count++; @@ -129,9 +196,9 @@ "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", - context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, + context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", - context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", + context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", hscroll[context->regs[REG_MODE_3] & 0x3], @@ -153,12 +220,17 @@ "0A: %.2X | H-Int Counter: %u\n" "0F: %.2X | Auto-increment: $%X\n" "10: %.2X | Scroll A/B Size: %sx%s\n", - context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, - context->regs[REG_HINT], context->regs[REG_HINT], + context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, + context->regs[REG_HINT], context->regs[REG_HINT], context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); - - //TODO: Window Group, DMA Group + printf("\n**Internal Group**\n" + "Address: %X\n" + "CD: %X\n" + "Pending: %s\n", + context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); + + //TODO: Window Group, DMA Group } void scan_sprite_table(uint32_t line, vdp_context * context) @@ -240,7 +312,7 @@ height *= 2; } uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; - uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; + uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; uint8_t pal_priority = (tileinfo >> 9) & 0x70; uint8_t row; if (tileinfo & MAP_BIT_V_FLIP) { @@ -260,7 +332,7 @@ } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { context->flags |= FLAG_MASKED; } - + context->flags &= ~FLAG_DOT_OFLOW; int16_t i; if (context->flags & FLAG_MASKED) { @@ -306,170 +378,134 @@ context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; } -#define VRAM_READ 0 -#define VRAM_WRITE 1 -#define CRAM_READ 8 -#define CRAM_WRITE 3 -#define VSRAM_READ 4 -#define VSRAM_WRITE 5 +#define VRAM_READ 0 //0000 +#define VRAM_WRITE 1 //0001 +//2 would trigger register write 0010 +#define CRAM_WRITE 3 //0011 +#define VSRAM_READ 4 //0100 +#define VSRAM_WRITE 5//0101 +//6 would trigger regsiter write 0110 +//7 is a mystery +#define CRAM_READ 8 //1000 +//9 is also a mystery //1001 +//A would trigger register write 1010 +//B is a mystery 1011 +#define VRAM_READ8 0xC //1100 +//D is a mystery 1101 +//E would trigger register write 1110 +//F is a mystery 1111 #define DMA_START 0x20 void external_slot(vdp_context * context) { + fifo_entry * start = context->fifo + context->fifo_read; + /*if (context->flags2 & FLAG2_READ_PENDING) { + context->flags2 &= ~FLAG2_READ_PENDING; + context->flags |= FLAG_UNUSED_SLOT; + return; + }*/ + if (context->fifo_read >= 0 && start->cycle <= context->cycles) { + switch (start->cd & 0xF) + { + case VRAM_WRITE: + if (start->partial) { + //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); + context->vdpmem[start->address ^ 1] = start->partial == 2 ? start->value >> 8 : start->value; + } else { + //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); + context->vdpmem[start->address] = start->value >> 8; + start->partial = 1; + //skip auto-increment and removal of entry from fifo + return; + } + break; + case CRAM_WRITE: { + //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); + write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value); + break; + } + case VSRAM_WRITE: + if (((start->address/2) & 63) < VSRAM_SIZE) { + //printf("VSRAM Write: %X to %X\n", start->value, context->address); + context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value; + } + + break; + } + context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1); + if (context->fifo_read == context->fifo_write) { + context->fifo_read = -1; + } + } else { + context->flags |= FLAG_UNUSED_SLOT; + } +} + +void run_dma_src(vdp_context * context, uint32_t slot) +{ //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy - if(context->flags & FLAG_DMA_RUN) { - uint16_t dma_len; - switch(context->regs[REG_DMASRC_H] & 0xC0) - { - //68K -> VDP - case 0: - case 0x40: - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - if (context->flags & FLAG_DMA_PROG) { - context->vdpmem[context->address ^ 1] = context->dma_val; - context->flags &= ~FLAG_DMA_PROG; - } else { - context->dma_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); - context->vdpmem[context->address] = context->dma_val >> 8; - context->flags |= FLAG_DMA_PROG; - } - break; - case CRAM_WRITE: { - write_cram(context, context->address, read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L])); - //printf("CRAM DMA | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->cycles); - break; - } - case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); - } - break; - } - break; - //Fill - case 0x80: - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - //Charles MacDonald's VDP doc says that the low byte gets written first - context->vdpmem[context->address] = context->dma_val; - context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); - break; - case CRAM_WRITE: - write_cram(context, context->address, context->dma_val); - //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); - break; - case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = context->dma_val; - } - break; + if (context->fifo_write == context->fifo_read) { + return; + } + fifo_entry * cur = NULL; + switch(context->regs[REG_DMASRC_H] & 0xC0) + { + //68K -> VDP + case 0: + case 0x40: + if (!slot || !is_refresh(context, slot-1)) { + cur = context->fifo + context->fifo_write; + cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + cur->address = context->address; + cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); + cur->cd = context->cd; + cur->partial = 0; + if (context->fifo_read < 0) { + context->fifo_read = context->fifo_write; } - break; - //Copy - case 0xC0: - if (context->flags & FLAG_DMA_PROG) { - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - context->vdpmem[context->address] = context->dma_val; - break; - case CRAM_WRITE: { - write_cram(context, context->address, context->dma_val); - //printf("CRAM DMA Copy | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->regs[REG_DMASRC_L] & (CRAM_SIZE-1), context->cycles); - break; - } - case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = context->dma_val; - } - break; - } - context->flags &= ~FLAG_DMA_PROG; - } else { - //I assume, that DMA copy copies from the same RAM as the destination - //but it's possible I'm mistaken - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - context->dma_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]]; - break; - case CRAM_WRITE: - context->dma_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)]; - break; - case VSRAM_WRITE: - if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) { - context->dma_val = context->vsram[context->regs[REG_DMASRC_L] & 63]; - } else { - context->dma_val = 0; - } - break; - } - context->flags |= FLAG_DMA_PROG; - } - break; + context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); + } + break; + //Copy + case 0xC0: + if (context->flags & FLAG_UNUSED_SLOT && context->fifo_read < 0) { + //TODO: Fix this to not use the FIFO at all once read-caching is properly implemented + context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1); + cur = context->fifo + context->fifo_read; + cur->cycle = context->cycles; + cur->address = context->address; + cur->partial = 1; + cur->value = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1] | (cur->value & 0xFF00); + cur->cd = VRAM_WRITE; + context->flags &= ~FLAG_UNUSED_SLOT; } - if (!(context->flags & FLAG_DMA_PROG)) { - context->address += context->regs[REG_AUTOINC]; - context->regs[REG_DMASRC_L] += 1; - if (!context->regs[REG_DMASRC_L]) { - context->regs[REG_DMASRC_M] += 1; - } - dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; - context->regs[REG_DMALEN_H] = dma_len >> 8; - context->regs[REG_DMALEN_L] = dma_len; - if (!dma_len) { - context->flags &= ~FLAG_DMA_RUN; - } + break; + case 0x80: + if (context->fifo_read < 0) { + context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1); + cur = context->fifo + context->fifo_read; + cur->cycle = context->cycles; + cur->address = context->address; + cur->partial = 2; } - } else { - fifo_entry * start = (context->fifo_end - FIFO_SIZE); - if (context->fifo_cur != start && start->cycle <= context->cycles) { - if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START)) { - context->flags |= FLAG_DMA_RUN; - context->dma_val = start->value; - context->address = start->address; //undo auto-increment - context->dma_cd = context->cd; - } else { - switch (start->cd & 0xF) - { - case VRAM_WRITE: - if (start->partial) { - //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); - context->vdpmem[start->address ^ 1] = start->value; - } else { - //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); - context->vdpmem[start->address] = start->value >> 8; - start->partial = 1; - //skip auto-increment and removal of entry from fifo - return; - } - break; - case CRAM_WRITE: { - //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); - write_cram(context, start->address, start->value); - break; - } - case VSRAM_WRITE: - if (((start->address/2) & 63) < VSRAM_SIZE) { - //printf("VSRAM Write: %X to %X\n", start->value, context->address); - context->vsram[(start->address/2) & 63] = start->value; - } - break; - } - //context->address += context->regs[REG_AUTOINC]; - } - fifo_entry * cur = start+1; - if (cur < context->fifo_cur) { - memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); - } - context->fifo_cur -= 1; - } else { - context->flags |= FLAG_UNUSED_SLOT; + break; + } + + if (cur) { + context->regs[REG_DMASRC_L] += 1; + if (!context->regs[REG_DMASRC_L]) { + context->regs[REG_DMASRC_M] += 1; + } + context->address += context->regs[REG_AUTOINC]; + uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; + context->regs[REG_DMALEN_H] = dma_len >> 8; + context->regs[REG_DMALEN_L] = dma_len; + if (!dma_len) { + //printf("DMA end at cycle %d\n", context->cycles); + context->flags &= ~FLAG_DMA_RUN; + context->cd &= 0xF; } } } @@ -520,7 +556,7 @@ address &= 0xF000; line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; mask = 0x7F; - + } else { address &= 0xF800; line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; @@ -562,7 +598,7 @@ vscroll <<= 1; vscroll |= 1; } - vscroll &= (context->vsram[(context->regs[REG_MODE_3] & BIT_VSCROLL ? column : 0) + vsram_off] + line); + vscroll &= (context->vsram[(context->regs[REG_MODE_3] & BIT_VSCROLL ? (column-2)&63 : 0) + vsram_off] + line); context->v_offset = vscroll & v_offset_mask; //printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset); vscroll >>= vscroll_shift; @@ -612,7 +648,7 @@ read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context); } -void render_map(uint16_t col, uint8_t * tmp_buf, vdp_context * context) +void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context) { uint16_t address; uint8_t shift, add; @@ -633,33 +669,36 @@ uint16_t pal_priority = (col >> 9) & 0x70; int32_t dir; if (col & MAP_BIT_H_FLIP) { - tmp_buf += 7; + offset += 7; + offset &= SCROLL_BUFFER_MASK; dir = -1; } else { dir = 1; } for (uint32_t i=0; i < 4; i++, address++) { - *tmp_buf = pal_priority | (context->vdpmem[address] >> 4); - tmp_buf += dir; - *tmp_buf = pal_priority | (context->vdpmem[address] & 0xF); - tmp_buf += dir; + tmp_buf[offset] = pal_priority | (context->vdpmem[address] >> 4); + offset += dir; + offset &= SCROLL_BUFFER_MASK; + tmp_buf[offset] = pal_priority | (context->vdpmem[address] & 0xF); + offset += dir; + offset &= SCROLL_BUFFER_MASK; } } void render_map_1(vdp_context * context) { - render_map(context->col_1, context->tmp_buf_a+SCROLL_BUFFER_DRAW, context); + render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context); } void render_map_2(vdp_context * context) { - render_map(context->col_2, context->tmp_buf_a+SCROLL_BUFFER_DRAW+8, context); + render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context); } void render_map_3(vdp_context * context) { - render_map(context->col_1, context->tmp_buf_b+SCROLL_BUFFER_DRAW, context); + render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context); } void render_map_output(uint32_t line, int32_t col, vdp_context * context) @@ -667,10 +706,11 @@ if (line >= 240) { return; } - render_map(context->col_2, context->tmp_buf_b+SCROLL_BUFFER_DRAW+8, context); + render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context); uint16_t *dst; uint32_t *dst32; - uint8_t *sprite_buf, *plane_a, *plane_b; + uint8_t *sprite_buf, *plane_a, *plane_b; + int plane_a_off, plane_b_off; if (col) { col-=2; @@ -682,117 +722,136 @@ dst += line * 320 + col * 8; } sprite_buf = context->linebuf + col * 8; - uint16_t a_src; + uint8_t a_src, src; if (context->flags & FLAG_WINDOW) { - plane_a = context->tmp_buf_a + SCROLL_BUFFER_DRAW; - //a_src = FBUF_SRC_W; + plane_a_off = context->buf_a_off; + a_src = DBG_SRC_W; } else { - plane_a = context->tmp_buf_a + SCROLL_BUFFER_DRAW - (context->hscroll_a & 0xF); - //a_src = FBUF_SRC_A; + plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); + a_src = DBG_SRC_A; } - plane_b = context->tmp_buf_b + SCROLL_BUFFER_DRAW - (context->hscroll_b & 0xF); - uint16_t src; + plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); - + if (context->regs[REG_MODE_4] & BIT_HILIGHT) { - for (int i = 0; i < 16; ++plane_a, ++plane_b, ++sprite_buf, ++i) { + for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; - + plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); + plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); + uint32_t * colors = context->colors; src = 0; uint8_t sprite_color = *sprite_buf & 0x3F; if (sprite_color == 0x3E || sprite_color == 0x3F) { if (sprite_color == 0x3F) { - src = CRAM_SIZE;//FBUF_SHADOW; + colors += CRAM_SIZE; + src = DBG_SHADOW; } else { - src = CRAM_SIZE*2;//FBUF_HILIGHT; + colors += CRAM_SIZE*2; + src = DBG_HILIGHT; } if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else if (*plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src |= FBUF_SRC_BG; + src |= DBG_SRC_BG; } } else { if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else { if (!(*plane_a & BUF_BIT_PRIORITY || *plane_a & BUF_BIT_PRIORITY)) { - src = CRAM_SIZE;//FBUF_SHADOW; + colors += CRAM_SIZE; + src = DBG_SHADOW; } if (*sprite_buf & 0xF) { pixel = *sprite_buf; if (*sprite_buf & 0xF == 0xE) { - src = 0;//FBUF_SRC_S; - } /*else { - src |= FBUF_SRC_S; - }*/ + colors = context->colors; + src = DBG_SRC_S; + } else { + src |= DBG_SRC_S; + } } else if (*plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src |= FBUF_SRC_BG; + src |= DBG_SRC_BG; } } } pixel &= 0x3F; - pixel += src; + uint32_t outpixel; + if (context->debug) { + outpixel = context->debugcolors[src]; + } else { + outpixel = colors[pixel]; + } if (context->b32) { - *(dst32++) = context->colors[pixel]; + *(dst32++) = outpixel; } else { - *(dst++) = context->colors[pixel]; + *(dst++) = outpixel; } //*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src; } } else { - for (int i = 0; i < 16; ++plane_a, ++plane_b, ++sprite_buf, ++i) { + for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; + src = 0; + plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); + plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else if (*sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src = FBUF_SRC_BG; + src = DBG_SRC_BG; + } + uint32_t outpixel; + if (context->debug) { + outpixel = context->debugcolors[src]; + } else { + outpixel = context->colors[pixel & 0x3F]; } if (context->b32) { - *(dst32++) = context->colors[pixel & 0x3F]; + *(dst32++) = outpixel; } else { - *(dst++) = context->colors[pixel & 0x3F]; + *(dst++) = outpixel; } //*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src; } @@ -804,14 +863,8 @@ //plane_b = context->tmp_buf_b + 16 - (context->hscroll_b & 0x7); //end = dst + 8; } - - uint16_t remaining; - if (!(context->flags & FLAG_WINDOW)) { - remaining = context->hscroll_a & 0xF; - memcpy(context->tmp_buf_a + SCROLL_BUFFER_DRAW - remaining, context->tmp_buf_a + SCROLL_BUFFER_SIZE - remaining, remaining); - } - remaining = context->hscroll_b & 0xF; - memcpy(context->tmp_buf_b + SCROLL_BUFFER_DRAW - remaining, context->tmp_buf_b + SCROLL_BUFFER_SIZE - remaining, remaining); + context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; + context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; } #define COLUMN_RENDER_BLOCK(column, startcyc) \ @@ -1164,34 +1217,26 @@ context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); } -int is_refresh(vdp_context * context, uint32_t slot) -{ - if (context->latched_mode & BIT_H40) { - //TODO: Figure out the exact behavior that reduces DMA slots for direct color DMA demos - return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210 || (slot < 6 && (context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) == CRAM_WRITE))); - } else { - //TODO: Figure out which slots are refresh when display is off in 32-cell mode - //These numbers are guesses based on H40 numbers - return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152 || (slot < 5 && (context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) == CRAM_WRITE))); - //The numbers below are the refresh slots during active display - //return (slot == 66 || slot == 98 || slot == 130 || slot == 162); - } -} - void check_render_bg(vdp_context * context, int32_t line, uint32_t slot) { if (line > 0) { line -= 1; int starti = -1; if (context->latched_mode & BIT_H40) { - if (slot >= 50 && slot < 210) { - uint32_t x = (slot-50)*2; + if (slot >= 55 && slot < 210) { + uint32_t x = (slot-55)*2; starti = line * 320 + x; + } else if (slot < 5) { + uint32_t x = (slot + 155)*2; + starti = (line-1)*320 + x; } } else { - if (slot >= 43 && slot < 171) { - uint32_t x = (slot-43)*2; + if (slot >= 48 && slot < 171) { + uint32_t x = (slot-48)*2; starti = line * 320 + x; + } else if (slot < 5) { + uint32_t x = (slot + 123)*2; + starti = (line-1)*320 + x; } } if (starti >= 0) { @@ -1218,6 +1263,7 @@ { while(context->cycles < target_cycles) { + context->flags &= ~FLAG_UNUSED_SLOT; uint32_t line = context->cycles / MCLKS_LINE; uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE; if (!context->cycles) { @@ -1330,9 +1376,9 @@ } if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) { //first sort-of active line is treated as 255 internally - //it's used for gathering sprite info for line + //it's used for gathering sprite info for line line = (line - 1) & 0xFF; - + //Convert to slot number if (context->latched_mode & BIT_H40){ vdp_h40(line, slot, context); @@ -1347,6 +1393,9 @@ check_render_bg(context, line, slot); } } + if (context->flags & FLAG_DMA_RUN && !is_refresh(context, slot)) { + run_dma_src(context, slot); + } context->cycles += inccycles; } } @@ -1386,7 +1435,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) { - //printf("control port write: %X\n", value); + //printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1401,6 +1450,7 @@ //DMA copy or 68K -> VDP, transfer starts immediately context->flags |= FLAG_DMA_RUN; context->dma_cd = context->cd; + //printf("DMA start at cycle %d\n", context->cycles); if (!(context->regs[REG_DMASRC_H] & 0x80)) { //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]); return 1; @@ -1415,18 +1465,19 @@ if ((value & 0xC000) == 0x8000) { //Register write uint8_t reg = (value >> 8) & 0x1F; - if (reg < VDP_REGS) { + if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) { //printf("register %d set to %X\n", reg, value & 0xFF); + if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) { + context->hv_latch = vdp_hv_counter_read(context); + } context->regs[reg] = value; - if (reg == REG_MODE_2) { - //printf("Display is now %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); - } if (reg == REG_MODE_4) { context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); if (!context->double_res) { context->framebuf = context->oddbuf; } } + context->cd &= 0x3C; } } else { context->flags |= FLAG_PENDING; @@ -1439,39 +1490,50 @@ int vdp_data_port_write(vdp_context * context, uint16_t value) { - //printf("data port write: %X\n", value); - if (context->flags & FLAG_DMA_RUN) { + //printf("data port write: %X at %d\n", value, context->cycles); + if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { return -1; } - if (!(context->cd & 1)) { - //ignore writes when cd is configured for read - return 0; - } context->flags &= ~FLAG_PENDING; /*if (context->fifo_cur == context->fifo_end) { printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); }*/ - while (context->fifo_cur == context->fifo_end) { + if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + context->flags &= ~FLAG_DMA_RUN; + } + while (context->fifo_write == context->fifo_read) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } - context->fifo_cur->cycle = context->cycles; - context->fifo_cur->address = context->address; - context->fifo_cur->value = value; - context->fifo_cur->cd = context->cd; - context->fifo_cur->partial = 0; - context->fifo_cur++; + fifo_entry * cur = context->fifo + context->fifo_write; + cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + cur->address = context->address; + cur->value = value; + if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + context->flags |= FLAG_DMA_RUN; + } + cur->cd = context->cd; + cur->partial = 0; + if (context->fifo_read < 0) { + context->fifo_read = context->fifo_write; + } + context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); context->address += context->regs[REG_AUTOINC]; return 0; } +void vdp_test_port_write(vdp_context * context, uint16_t value) +{ + //TODO: implement test register +} + uint16_t vdp_control_port_read(vdp_context * context) { context->flags &= ~FLAG_PENDING; uint16_t value = 0x3400; - if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) { + if (context->fifo_read < 0) { value |= 0x200; } - if (context->fifo_cur == context->fifo_end) { + if (context->fifo_read == context->fifo_write) { value |= 0x100; } if (context->flags2 & FLAG2_VINT_PENDING) { @@ -1482,7 +1544,7 @@ } uint32_t line= context->cycles / MCLKS_LINE; uint32_t linecyc = context->cycles % MCLKS_LINE; - if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE)) { + if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) { value |= 0x8; } if (linecyc < (context->latched_mode & BIT_H40 ? HBLANK_CLEAR_H40 : HBLANK_CLEAR_H32)) { @@ -1494,10 +1556,15 @@ if (context->latched_mode & BIT_PAL) {//Not sure about this, need to verify value |= 0x1; } + //printf("status read at cycle %d returned %X\n", context->cycles, value); //TODO: Sprite overflow, sprite collision, odd frame flag return value; } +#define CRAM_BITS 0xEEE +#define VSRAM_BITS 0x7FF +#define VSRAM_DIRTY_BITS 0xF800 + uint16_t vdp_data_port_read(vdp_context * context) { context->flags &= ~FLAG_PENDING; @@ -1506,6 +1573,7 @@ } //Not sure if the FIFO should be drained before processing a read or not, but it would make sense context->flags &= ~FLAG_UNUSED_SLOT; + //context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } @@ -1513,21 +1581,31 @@ switch (context->cd & 0xF) { case VRAM_READ: - value = context->vdpmem[context->address] << 8; + value = context->vdpmem[context->address & 0xFFFE] << 8; context->flags &= ~FLAG_UNUSED_SLOT; + context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } - value |= context->vdpmem[context->address ^ 1]; + value |= context->vdpmem[context->address | 1]; + break; + case VRAM_READ8: + value = context->vdpmem[context->address ^ 1]; + value |= context->fifo[context->fifo_write].value & 0xFF00; break; case CRAM_READ: - value = context->cram[(context->address/2) & (CRAM_SIZE-1)]; + value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; + value |= context->fifo[context->fifo_write].value & ~CRAM_BITS; break; - case VSRAM_READ: - if (((context->address / 2) & 63) < VSRAM_SIZE) { - value = context->vsram[context->address & 63]; + case VSRAM_READ: { + uint16_t address = (context->address /2) & 63; + if (address >= VSRAM_SIZE) { + address = 0; } + value = context->vsram[address] & VSRAM_BITS; + value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; break; + } } context->address += context->regs[REG_AUTOINC]; return value; @@ -1535,7 +1613,9 @@ uint16_t vdp_hv_counter_read(vdp_context * context) { - //TODO: deal with clock adjustemnts handled in vdp_run_context + if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) { + return context->hv_latch; + } uint32_t line= context->cycles / MCLKS_LINE; if (!line) { line = 0xFF; @@ -1642,15 +1722,25 @@ return (line << 8) | linecyc; } +uint16_t vdp_test_port_read(vdp_context * context) +{ + //TODO: Find out what actually gets returned here + return 0xFFFF; +} + void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) { context->cycles -= deduction; - for(fifo_entry * start = (context->fifo_end - FIFO_SIZE); start < context->fifo_cur; start++) { - if (start->cycle >= deduction) { - start->cycle -= deduction; - } else { - start->cycle = 0; - } + if (context->fifo_read >= 0) { + int32_t idx = context->fifo_read; + do { + if (context->fifo[idx].cycle >= deduction) { + context->fifo[idx].cycle -= deduction; + } else { + context->fifo[idx].cycle = 0; + } + idx = (idx+1) & (FIFO_SIZE-1); + } while(idx != context->fifo_write); } } @@ -1717,64 +1807,3 @@ } } -#define GST_VDP_REGS 0xFA -#define GST_VDP_MEM 0x12478 - -uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) -{ - uint8_t tmp_buf[CRAM_SIZE*2]; - fseek(state_file, GST_VDP_REGS, SEEK_SET); - if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { - fputs("Failed to read VDP registers from savestate\n", stderr); - return 0; - } - context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); - if (!context->double_res) { - context->framebuf = context->oddbuf; - } - latch_mode(context); - if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { - fputs("Failed to read CRAM from savestate\n", stderr); - return 0; - } - for (int i = 0; i < CRAM_SIZE; i++) { - uint16_t value; - context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; - context->colors[i] = color_map[value & 0xEEE]; - context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; - context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; - } - if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { - fputs("Failed to read VSRAM from savestate\n", stderr); - return 0; - } - for (int i = 0; i < VSRAM_SIZE; i++) { - context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; - } - fseek(state_file, GST_VDP_MEM, SEEK_SET); - if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { - fputs("Failed to read VRAM from savestate\n", stderr); - return 0; - } - return 1; -} - -void vdp_save_state(vdp_context * context, FILE * outfile) -{ - uint8_t tmp_buf[CRAM_SIZE*2]; - fseek(outfile, GST_VDP_REGS, SEEK_SET); - fwrite(context->regs, 1, VDP_REGS, outfile); - for (int i = 0; i < CRAM_SIZE; i++) { - tmp_buf[i*2] = context->cram[i]; - tmp_buf[i*2+1] = context->cram[i] >> 8; - } - fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile); - for (int i = 0; i < VSRAM_SIZE; i++) { - tmp_buf[i*2] = context->vsram[i]; - tmp_buf[i*2+1] = context->vsram[i] >> 8; - } - fwrite(tmp_buf, 2, VSRAM_SIZE, outfile); - fseek(outfile, GST_VDP_MEM, SEEK_SET); - fwrite(context->vdpmem, 1, VRAM_SIZE, outfile); -} - diff -r 7696d824489d -r c08a4efeee7f vdp.h --- a/vdp.h Tue Jul 23 23:01:03 2013 -0700 +++ b/vdp.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef VDP_H_ #define VDP_H_ @@ -19,13 +24,15 @@ #define FBUF_SHADOW 0x0001 #define FBUF_HILIGHT 0x0010 -#define FBUF_BIT_PRIORITY 0x1000 -#define FBUF_SRC_MASK 0xE000 -#define FBUF_SRC_A 0x0000 -#define FBUF_SRC_W 0x2000 -#define FBUF_SRC_B 0x4000 -#define FBUF_SRC_S 0x6000 -#define FBUF_SRC_BG 0x8000 +#define DBG_SHADOW 0x10 +#define DBG_HILIGHT 0x20 +#define DBG_PRIORITY 0x8 +#define DBG_SRC_MASK 0x7 +#define DBG_SRC_A 0x1 +#define DBG_SRC_W 0x2 +#define DBG_SRC_B 0x3 +#define DBG_SRC_S 0x4 +#define DBG_SRC_BG 0x0 #define MCLKS_LINE 3420 @@ -40,6 +47,7 @@ #define FLAG2_VINT_PENDING 0x01 #define FLAG2_HINT_PENDING 0x02 +#define FLAG2_READ_PENDING 0x04 #define DISPLAY_ENABLE 0x40 @@ -102,6 +110,8 @@ int16_t y; } sprite_info; +#define FIFO_SIZE 4 + typedef struct { uint32_t cycle; uint16_t address; @@ -111,8 +121,9 @@ } fifo_entry; typedef struct { - fifo_entry *fifo_cur; - fifo_entry *fifo_end; + fifo_entry fifo[FIFO_SIZE]; + int32_t fifo_write; + int32_t fifo_read; uint16_t address; uint8_t cd; uint8_t flags; @@ -128,6 +139,7 @@ void *evenbuf; uint16_t cram[CRAM_SIZE]; uint32_t colors[CRAM_SIZE*3]; + uint32_t debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight uint16_t vsram[VSRAM_SIZE]; uint8_t latched_mode; uint16_t hscroll_a; @@ -140,13 +152,16 @@ sprite_info sprite_info_list[MAX_SPRITES_LINE]; uint16_t col_1; uint16_t col_2; - uint16_t dma_val; + uint16_t hv_latch; uint8_t v_offset; uint8_t dma_cd; uint8_t hint_counter; uint8_t flags2; uint8_t double_res; uint8_t b32; + uint8_t buf_a_off; + uint8_t buf_b_off; + uint8_t debug; uint8_t *tmp_buf_a; uint8_t *tmp_buf_b; } vdp_context; @@ -158,12 +173,14 @@ //runs until the target cycle is reached or the current DMA operation has completed, whicever comes first void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles); uint8_t vdp_load_gst(vdp_context * context, FILE * state_file); -void vdp_save_state(vdp_context * context, FILE * outfile); +uint8_t vdp_save_gst(vdp_context * context, FILE * outfile); int vdp_control_port_write(vdp_context * context, uint16_t value); int vdp_data_port_write(vdp_context * context, uint16_t value); +void vdp_test_port_write(vdp_context * context, uint16_t value); uint16_t vdp_control_port_read(vdp_context * context); uint16_t vdp_data_port_read(vdp_context * context); uint16_t vdp_hv_counter_read(vdp_context * context); +uint16_t vdp_test_port_read(vdp_context * context); void vdp_adjust_cycles(vdp_context * context, uint32_t deduction); uint32_t vdp_next_hint(vdp_context * context); uint32_t vdp_next_vint(vdp_context * context); @@ -171,5 +188,8 @@ void vdp_int_ack(vdp_context * context, uint16_t int_num); void vdp_print_sprite_table(vdp_context * context); void vdp_print_reg_explain(vdp_context * context); +void latch_mode(vdp_context * context); + +extern int32_t color_map[1 << 12]; #endif //VDP_H_ diff -r 7696d824489d -r c08a4efeee7f vgmplay.c --- a/vgmplay.c Tue Jul 23 23:01:03 2013 -0700 +++ b/vgmplay.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,6 +1,12 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "render.h" #include "ym2612.h" #include "psg.h" +#include "config.h" #include #include @@ -72,14 +78,27 @@ { } +void handle_joydown(int joystick, int button) +{ +} + +void handle_joyup(int joystick, int button) +{ +} + +void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) +{ +} + #define CYCLE_LIMIT MCLKS_NTSC/60 +tern_node * config; void wait(ym2612_context * y_context, psg_context * p_context, uint32_t * current_cycle, uint32_t cycles) { *current_cycle += cycles; psg_run(p_context, *current_cycle); ym_run(y_context, *current_cycle); - + if (*current_cycle > CYCLE_LIMIT) { *current_cycle -= CYCLE_LIMIT; p_context->cycles -= CYCLE_LIMIT; @@ -91,15 +110,16 @@ int main(int argc, char ** argv) { uint32_t fps = 60; - render_init(320, 240, "vgm play", 60); - - + config = load_config(argv[0]); + render_init(320, 240, "vgm play", 60, 0); + + ym2612_context y_context; ym_init(&y_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_YM, render_audio_buffer(), 0); - + psg_context p_context; psg_init(&p_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_PSG, render_audio_buffer()); - + FILE * f = fopen(argv[1], "rb"); vgm_header header; fread(&header, sizeof(header), 1, f); @@ -111,9 +131,9 @@ uint8_t * data = malloc(data_size); fread(data, 1, data_size, f); fclose(f); - + uint32_t mclks_sample = MCLKS_NTSC / 44100; - + uint8_t * end = data + data_size; uint8_t * cur = data; uint32_t current_cycle = 0; diff -r 7696d824489d -r c08a4efeee7f wave.c --- a/wave.c Tue Jul 23 23:01:03 2013 -0700 +++ b/wave.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "wave.h" #include #include diff -r 7696d824489d -r c08a4efeee7f wave.h --- a/wave.h Tue Jul 23 23:01:03 2013 -0700 +++ b/wave.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef WAVE_H_ #define WAVE_H_ diff -r 7696d824489d -r c08a4efeee7f x86_backend.c --- a/x86_backend.c Tue Jul 23 23:01:03 2013 -0700 +++ b/x86_backend.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "x86_backend.h" #include diff -r 7696d824489d -r c08a4efeee7f x86_backend.h --- a/x86_backend.h Tue Jul 23 23:01:03 2013 -0700 +++ b/x86_backend.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef X86_BACKEND_H_ #define X86_BACKEND_H_ diff -r 7696d824489d -r c08a4efeee7f ym2612.c --- a/ym2612.c Tue Jul 23 23:01:03 2013 -0700 +++ b/ym2612.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include @@ -122,6 +127,13 @@ } } } +#define BUFFER_INC_RES 1000000000UL + +void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock) +{ + uint64_t old_inc = context->buffer_inc; + context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc; +} void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options) { @@ -129,8 +141,10 @@ memset(context, 0, sizeof(*context)); context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); - context->buffer_inc = ((double)sample_rate / (double)master_clock) * clock_div * 6; + context->sample_rate = sample_rate; context->clock_inc = clock_div * 6; + ym_adjust_master_clock(context, master_clock); + context->sample_limit = sample_limit*2; context->write_cycle = CYCLE_NEVER; for (int i = 0; i < NUM_OPERATORS; i++) { @@ -162,7 +176,7 @@ //populate sine table for (int32_t i = 0; i < 512; i++) { double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); - + //table stores 4.8 fixed pointed representation of the base 2 log sine_table[i] = round_fixed_point(-log2(sine), 8); } @@ -308,7 +322,7 @@ } } else { if (first_key_on) { - dfprintf(debug_file, "Changing op %d envelope %d by %d in %s phase\n", op, operator->envelope, envelope_inc, + dfprintf(debug_file, "Changing op %d envelope %d by %d in %s phase\n", op, operator->envelope, envelope_inc, operator->env_phase == PHASE_SUSTAIN ? "sustain" : (operator->env_phase == PHASE_DECAY ? "decay": "release")); } operator->envelope += envelope_inc; @@ -328,7 +342,7 @@ context->env_counter++; } } - + //Update Phase Generator uint32_t channel = context->current_op / 4; if (channel != 5 || !context->dac_enable) { @@ -348,7 +362,7 @@ } else { lfo_mod >>= 1; } - operator->phase_counter += lfo_mod; + operator->phase_counter += lfo_mod; } int16_t mod = 0; switch (op % 4) @@ -413,7 +427,7 @@ dfprintf(debug_file, "op %d, base phase: %d, mod: %d, sine: %d, out: %d\n", op, phase, mod, sine_table[(phase+mod) & 0x1FF], pow_table[sine_table[phase & 0x1FF] + env]); } phase += mod; - + int16_t output = pow_table[sine_table[phase & 0x1FF] + env]; if (phase & 0x200) { output = -output; @@ -446,29 +460,29 @@ context->buffer_fraction += context->buffer_inc; if (context->current_op == NUM_OPERATORS) { context->current_op = 0; - if (context->buffer_fraction > 1.0) { - context->buffer_fraction -= 1.0; - context->audio_buffer[context->buffer_pos] = 0; - context->audio_buffer[context->buffer_pos + 1] = 0; - for (int i = 0; i < NUM_CHANNELS; i++) { - int16_t value = context->channels[i].output & 0x3FE0; - if (value & 0x2000) { - value |= 0xC000; - } - if (context->channels[i].logfile) { - fwrite(&value, sizeof(value), 1, context->channels[i].logfile); - } - if (context->channels[i].lr & 0x80) { - context->audio_buffer[context->buffer_pos] += value / YM_VOLUME_DIVIDER; - } - if (context->channels[i].lr & 0x40) { - context->audio_buffer[context->buffer_pos+1] += value / YM_VOLUME_DIVIDER; - } + } + if (context->buffer_fraction > BUFFER_INC_RES) { + context->buffer_fraction -= BUFFER_INC_RES; + context->audio_buffer[context->buffer_pos] = 0; + context->audio_buffer[context->buffer_pos + 1] = 0; + for (int i = 0; i < NUM_CHANNELS; i++) { + int16_t value = context->channels[i].output & 0x3FE0; + if (value & 0x2000) { + value |= 0xC000; } - context->buffer_pos += 2; - if (context->buffer_pos == context->sample_limit) { - render_wait_ym(context); + if (context->channels[i].logfile) { + fwrite(&value, sizeof(value), 1, context->channels[i].logfile); + } + if (context->channels[i].lr & 0x80) { + context->audio_buffer[context->buffer_pos] += value / YM_VOLUME_DIVIDER; } + if (context->channels[i].lr & 0x40) { + context->audio_buffer[context->buffer_pos+1] += value / YM_VOLUME_DIVIDER; + } + } + context->buffer_pos += 2; + if (context->buffer_pos == context->sample_limit) { + render_wait_ym(context); } } } @@ -561,7 +575,7 @@ } //detune detune = detune_table[channel->keycode][operator->detune & 0x3]; - } + } if (operator->detune & 0x40) { inc -= detune; //this can underflow, mask to 17-bit result @@ -582,9 +596,20 @@ void ym_data_write(ym2612_context * context, uint8_t value) { - if (context->selected_reg < 0x21 || context->selected_reg > 0xB6 || (context->selected_reg < 0x30 && context->selected_part)) { + if (context->selected_reg >= YM_REG_END) { return; } + if (context->selected_part) { + if (context->selected_reg < YM_PART2_START) { + return; + } + context->part2_regs[context->selected_reg - YM_PART2_START] = value; + } else { + if (context->selected_reg < YM_PART1_START) { + return; + } + context->part1_regs[context->selected_reg - YM_PART1_START] = value; + } dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1); if (context->selected_reg < 0x30) { //Shared regs @@ -600,7 +625,7 @@ context->lfo_am_step = context->lfo_pm_step = 0; } context->lfo_freq = value & 0x7; - + break; case REG_TIMERA_HIGH: context->timer_a_load &= 0x3; @@ -644,11 +669,13 @@ } for (uint8_t op = channel * 4, bit = 0x10; op < (channel + 1) * 4; op++, bit <<= 1) { if (value & bit) { - first_key_on = 1; - //printf("Key On for operator %d in channel %d\n", op, channel); - context->operators[op].phase_counter = 0; - context->operators[op].env_phase = PHASE_ATTACK; - context->operators[op].envelope = MAX_ENVELOPE; + if (context->operators[op].env_phase == PHASE_RELEASE) + { + first_key_on = 1; + //printf("Key On for operator %d in channel %d\n", op, channel); + context->operators[op].phase_counter = 0; + context->operators[op].env_phase = PHASE_ATTACK; + } } else { //printf("Key Off for operator %d in channel %d\n", op, channel); context->operators[op].env_phase = PHASE_RELEASE; @@ -753,7 +780,7 @@ } } } - + context->write_cycle = context->current_cycle; context->status |= 0x80; } @@ -763,24 +790,3 @@ return context->status; } -#define GST_YM_OFFSET 0x1E4 -#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET) - -uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile) -{ - uint8_t regdata[GST_YM_SIZE]; - fseek(gstfile, GST_YM_OFFSET, SEEK_SET); - if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { - return 0; - } - for (int i = 0; i < sizeof(regdata); i++) { - if (i & 0x100) { - ym_address_write_part2(context, i & 0xFF); - } else { - ym_address_write_part1(context, i); - } - ym_data_write(context, regdata[i]); - } - return 1; -} - diff -r 7696d824489d -r c08a4efeee7f ym2612.h --- a/ym2612.h Tue Jul 23 23:01:03 2013 -0700 +++ b/ym2612.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef YM2612_H_ #define YM2612_H_ @@ -45,13 +50,20 @@ uint8_t keycode; } ym_supp; +#define YM_PART1_START 0x21 +#define YM_PART2_START 0x30 +#define YM_REG_END 0xB8 +#define YM_PART1_REGS (YM_REG_END-YM_PART1_START) +#define YM_PART2_REGS (YM_REG_END-YM_PART2_START) + typedef struct { int16_t *audio_buffer; int16_t *back_buffer; - double buffer_fraction; - double buffer_inc; + uint64_t buffer_fraction; + uint64_t buffer_inc; uint32_t clock_inc; uint32_t buffer_pos; + uint32_t sample_rate; uint32_t sample_limit; uint32_t current_cycle; uint32_t write_cycle; @@ -66,7 +78,7 @@ uint8_t ch3_mode; uint8_t current_op; uint8_t current_env_op; - + uint8_t timer_control; uint8_t dac_enable; uint8_t lfo_enable; @@ -77,15 +89,19 @@ uint8_t status; uint8_t selected_reg; uint8_t selected_part; + uint8_t part1_regs[YM_PART1_REGS]; + uint8_t part2_regs[YM_PART2_REGS]; } ym2612_context; void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options); +void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock); void ym_run(ym2612_context * context, uint32_t to_cycle); void ym_address_write_part1(ym2612_context * context, uint8_t address); void ym_address_write_part2(ym2612_context * context, uint8_t address); void ym_data_write(ym2612_context * context, uint8_t value); uint8_t ym_read_status(ym2612_context * context); uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile); +uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile); #endif //YM2612_H_ diff -r 7696d824489d -r c08a4efeee7f z80_to_x86.c --- a/z80_to_x86.c Tue Jul 23 23:01:03 2013 -0700 +++ b/z80_to_x86.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "gen_x86.h" diff -r 7696d824489d -r c08a4efeee7f z80_to_x86.h --- a/z80_to_x86.h Tue Jul 23 23:01:03 2013 -0700 +++ b/z80_to_x86.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef Z80_TO_X86_H_ #define Z80_TO_X86_H_ #include "z80inst.h" @@ -50,6 +55,7 @@ void * system; uint8_t ram_code_flags[(8 * 1024)/128/8]; uint32_t int_enable_cycle; + uint16_t pc; } z80_context; void translate_z80_stream(z80_context * context, uint32_t address); diff -r 7696d824489d -r c08a4efeee7f z80inst.c --- a/z80inst.c Tue Jul 23 23:01:03 2013 -0700 +++ b/z80inst.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff -r 7696d824489d -r c08a4efeee7f z80inst.h --- a/z80inst.h Tue Jul 23 23:01:03 2013 -0700 +++ b/z80inst.h Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef Z80INST_H_ #define Z80INST_H_ diff -r 7696d824489d -r c08a4efeee7f zdis.c --- a/zdis.c Tue Jul 23 23:01:03 2013 -0700 +++ b/zdis.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff -r 7696d824489d -r c08a4efeee7f zruntime.S --- a/zruntime.S Tue Jul 23 23:01:03 2013 -0700 +++ b/zruntime.S Sat Oct 26 22:38:47 2013 -0700 @@ -19,12 +19,13 @@ cmp 112(%rsi), %ebp jb no_sync sync_io: + movw $0, 164(%rsi) call z80_save_context_scratch pop %rax /*return address in read/write func*/ pop 104(%rsi) /*return address in native code*/ sub $5, %rax /* adjust return addres to point to the call instruction that got us here */ mov %rax, (%rsi) - + pop %r15 /* restore callee saved regsiters */ pop %r14 pop %r13 @@ -32,7 +33,7 @@ pop %rbp pop %rbx ret /* return to caller of z80_run */ - + .global z80_handle_cycle_limit_int z80_handle_cycle_limit_int: cmp 116(%rsi), %ebp @@ -63,6 +64,7 @@ zskip_int: cmp 112(%rsi), %ebp jb zskip_sync +mov %r13w, 164(%rsi) .global z80_do_sync z80_do_sync: call z80_save_context @@ -244,7 +246,7 @@ call z_inccycles_io /* genesis Z80 has no IO port hardware and writes have no effect */ ret - + .global z80_retrans_stub z80_retrans_stub: pop %r14 @@ -264,7 +266,7 @@ z80_native_addr: call z80_save_context push %rsi - mov %rsi, %rdi + mov %rsi, %rdi movzx %r13w, %esi call z80_get_native_address_trans mov %rax, %r13 @@ -275,7 +277,7 @@ z80_save_context_scratch: mov %r13w, 98(%rsi) /* scratch1 */ mov %r14w, 100(%rsi) /* scratch2 */ - + .global z80_save_context z80_save_context: mov %r9w, 8(%rsi) /* SP */ @@ -295,7 +297,7 @@ z80_load_context_scratch: mov 98(%rsi), %r13w /* scratch1 */ mov 100(%rsi), %r14w /* scratch2 */ - + .global z80_load_context z80_load_context: mov 8(%rsi), %r9w /* SP */ @@ -328,4 +330,4 @@ movq $0, 104(%rsi) no_extra: jmp *(%rsi) - + diff -r 7696d824489d -r c08a4efeee7f ztestgen.c --- a/ztestgen.c Tue Jul 23 23:01:03 2013 -0700 +++ b/ztestgen.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff -r 7696d824489d -r c08a4efeee7f ztestrun.c --- a/ztestrun.c Tue Jul 23 23:01:03 2013 -0700 +++ b/ztestrun.c Sat Oct 26 22:38:47 2013 -0700 @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "mem.h"