commit 2e4a23c31508aaf3de782852c00dc812f9c7f137 Author: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Wed Oct 4 14:33:17 2023 -0600 Initial Commit diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..13b4ef51 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "daily" diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..196e176d --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + + +# Added by cargo + +/target diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..208959eb --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "parallel-rdp/parallel-rdp-standalone"] + path = parallel-rdp/parallel-rdp-standalone + url = https://github.com/Themaister/parallel-rdp-standalone.git diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..08b453c3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.linkedProjects": [ + "./Cargo.toml" + ] +} diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..36d93931 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "rust64" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sdl2 = "0.35" + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +pkg-config = "0.3" + +[profile.release] +lto = true +codegen-units = 1 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE @@ -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 --git a/build.rs b/build.rs new file mode 100644 index 00000000..448bc870 --- /dev/null +++ b/build.rs @@ -0,0 +1,58 @@ +fn main() { + let sdl = pkg_config::Config::new().probe("sdl2").unwrap(); + let mut build = cc::Build::new(); + build + .cpp(true) + .flag("-Wno-missing-field-initializers") + .flag("-Wno-unused-parameter") + .file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/command_ring.cpp") + .file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_device.cpp") + .file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_dump_write.cpp") + .file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp") + .file("parallel-rdp/parallel-rdp-standalone/parallel-rdp/video_interface.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/buffer.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/buffer_pool.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/command_buffer.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/command_pool.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/context.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/cookie.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/descriptor_set.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/event_manager.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/fence.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/fence_manager.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/image.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/indirect_layout.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/memory_allocator.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/pipeline_event.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/query_pool.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/render_pass.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/sampler.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/semaphore.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/semaphore_manager.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/shader.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/texture/texture_format.cpp") + .file("parallel-rdp/parallel-rdp-standalone/vulkan/wsi.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/arena_allocator.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/logging.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/thread_id.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/aligned_alloc.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/timer.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/timeline_trace_file.cpp") + .file("parallel-rdp/parallel-rdp-standalone/util/thread_name.cpp") + .file("parallel-rdp/parallel-rdp-standalone/volk/volk.c") + .file("parallel-rdp/interface.cpp") + .file("parallel-rdp/wsi_platform.cpp") + .include("parallel-rdp/parallel-rdp-standalone/parallel-rdp") + .include("parallel-rdp/parallel-rdp-standalone/volk") + .include("parallel-rdp/parallel-rdp-standalone/vulkan") + .include("parallel-rdp/parallel-rdp-standalone/vulkan-headers/include") + .include("parallel-rdp/parallel-rdp-standalone/util") + .includes(sdl.include_paths); + + #[cfg(target_os = "windows")] + { + build.flag("-DVK_USE_PLATFORM_WIN32_KHR"); + } + build.compile("parallel-rdp"); +} diff --git a/build_release.sh b/build_release.sh new file mode 100755 index 00000000..adacbd7d --- /dev/null +++ b/build_release.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +RUSTFLAGS='-C target-cpu=x86-64-v3' cargo build --release diff --git a/parallel-rdp/interface.cpp b/parallel-rdp/interface.cpp new file mode 100644 index 00000000..a5118ece --- /dev/null +++ b/parallel-rdp/interface.cpp @@ -0,0 +1,405 @@ +#include "wsi_platform.hpp" +#include "wsi.hpp" +#include "rdp_device.hpp" +#include "interface.hpp" +#include "spirv.hpp" + +using namespace Vulkan; + +#define DP_STATUS_XBUS_DMA 0x01 +#define DP_STATUS_FREEZE 0x02 +#define DP_STATUS_FLUSH 0x04 +#define DP_STATUS_START_GCLK 0x008 +#define DP_STATUS_TMEM_BUSY 0x010 +#define DP_STATUS_PIPE_BUSY 0x020 +#define DP_STATUS_CMD_BUSY 0x040 +#define DP_STATUS_CBUF_READY 0x080 +#define DP_STATUS_DMA_BUSY 0x100 +#define DP_STATUS_END_VALID 0x200 +#define DP_STATUS_START_VALID 0x400 + +enum dpc_registers +{ + DPC_START_REG, + DPC_END_REG, + DPC_CURRENT_REG, + DPC_STATUS_REG, + DPC_CLOCK_REG, + DPC_BUFBUSY_REG, + DPC_PIPEBUSY_REG, + DPC_TMEM_REG, + DPC_REGS_COUNT +}; + +enum vi_registers +{ + VI_STATUS_REG, + VI_ORIGIN_REG, + VI_WIDTH_REG, + VI_V_INTR_REG, + VI_CURRENT_REG, + VI_BURST_REG, + VI_V_SYNC_REG, + VI_H_SYNC_REG, + VI_LEAP_REG, + VI_H_START_REG, + VI_V_START_REG, + VI_V_BURST_REG, + VI_X_SCALE_REG, + VI_Y_SCALE_REG, + VI_REGS_COUNT +}; + +static void *rdram; +static SDL_Window *window; +static RDP::CommandProcessor *processor; +static WSI *wsi; +static uint32_t cmd_data[0x00040000 >> 2]; +static int cmd_cur; +static int cmd_ptr; +static uint8_t emu_running; +static uint64_t rdp_sync_signal; +static uint32_t vi_registers[14]; + +static uint64_t last_frame_counter; +static uint64_t frame_counter; + +static const unsigned cmd_len_lut[64] = { + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 6, + 12, + 14, + 12, + 14, + 20, + 22, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, +}; + +enum +{ + MB_RDRAM_DRAM_ALIGNMENT_REQUIREMENT = 64 * 1024 +}; + +void vk_init(void *mem_base, uint32_t rdram_size) +{ + rdram = mem_base; + bool window_vsync = 0; + wsi = new WSI; + SDL_WSIPlatform *wsi_platform = new SDL_WSIPlatform; + wsi_platform->set_window(window); + wsi->set_platform(wsi_platform); + wsi->set_present_mode(window_vsync ? PresentMode::SyncToVBlank : PresentMode::UnlockedMaybeTear); + wsi->set_backbuffer_srgb(false); + Context::SystemHandles handles = {}; + if (!::Vulkan::Context::init_loader(nullptr)) + { + vk_close(); + } + if (!wsi->init_simple(1, handles)) + { + vk_close(); + } + RDP::CommandProcessorFlags flags = 0; + processor = new RDP::CommandProcessor(wsi->get_device(), rdram, 0, rdram_size, rdram_size / 2, flags); + + if (!processor->device_is_supported()) + { + delete processor; + delete wsi; + processor = nullptr; + vk_close(); + } + wsi->begin_frame(); + + emu_running = 1; + last_frame_counter = 0; + frame_counter = 0; +} + +void vk_close() +{ + delete processor; + delete wsi; +} + +int sdl_event_filter(void *userdata, SDL_Event *event) +{ + if (event->type == SDL_WINDOWEVENT) + { + switch (event->window.event) + { + case SDL_WINDOWEVENT_CLOSE: + emu_running = 0; + break; + default: + break; + } + } + + return 0; +} + +void set_sdl_window(void *_window) +{ + window = (SDL_Window *)_window; + SDL_SetEventFilter(sdl_event_filter, nullptr); +} + +static void render_frame(Vulkan::Device &device) +{ + RDP::ScanoutOptions options = {}; + Vulkan::ImageHandle image = processor->scanout(options); + + // Normally reflection is automated. + Vulkan::ResourceLayout vertex_layout = {}; + Vulkan::ResourceLayout fragment_layout = {}; + fragment_layout.output_mask = 1 << 0; + fragment_layout.sets[0].sampled_image_mask = 1 << 0; + + // This request is cached. + auto *program = device.request_program(vertex_spirv, sizeof(vertex_spirv), + fragment_spirv, sizeof(fragment_spirv), + &vertex_layout, + &fragment_layout); + + // Blit image on screen. + auto cmd = device.request_command_buffer(); + { + auto rp = device.get_swapchain_render_pass(Vulkan::SwapchainRenderPass::ColorOnly); + cmd->begin_render_pass(rp); + + VkViewport vp = cmd->get_viewport(); + // Adjust the viewport here for aspect ratio correction. + + cmd->set_program(program); + + // Basic default render state. + cmd->set_opaque_state(); + cmd->set_depth_test(false, false); + cmd->set_cull_mode(VK_CULL_MODE_NONE); + + // If we don't have an image, we just get a cleared screen in the render pass. + if (image) + { + cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp); + cmd->set_viewport(vp); + // The vertices are constants in the shader. + // Draws fullscreen quad using oversized triangle. + cmd->draw(3); + } + + cmd->end_render_pass(); + } + device.submit(cmd); +} + +void rdp_set_vi_register(uint32_t reg, uint32_t value) +{ + processor->set_vi_register(RDP::VIRegister(reg), value); + vi_registers[reg] = value; +} + +uint8_t rdp_update_screen() +{ + auto &device = wsi->get_device(); + render_frame(device); + wsi->end_frame(); + wsi->begin_frame(); + frame_counter++; + return emu_running; +} + +static uint32_t viCalculateHorizonalWidth(uint32_t hstart, uint32_t xscale, uint32_t width) +{ + if (xscale == 0) + return 320; + + uint32_t start = ((hstart & 0x03FF0000) >> 16) & 0x3FF; + uint32_t end = (hstart & 0x3FF); + uint32_t delta; + if (end > start) + delta = end - start; + else + delta = start - end; + uint32_t scale = (xscale & 0xFFF); + + if (delta == 0) + { + delta = width; + } + + return (delta * scale) / 0x400; +} + +static uint32_t viCalculateVerticalHeight(uint32_t vstart, uint32_t yscale) +{ + if (yscale == 0) + return 240; + + uint32_t start = ((vstart & 0x03FF0000) >> 16) & 0x3FF; + uint32_t end = (vstart & 0x3FF); + uint32_t delta; + if (end > start) + delta = end - start; + else + delta = start - end; + uint32_t scale = (yscale & 0xFFF); + + return (delta * scale) / 0x800; +} + +uint64_t rdp_process_commands(uint32_t *dpc_regs, uint8_t *SP_DMEM) +{ + uint64_t interrupt_timer = 0; + const uint32_t DP_CURRENT = dpc_regs[DPC_CURRENT_REG] & 0x00FFFFF8; + const uint32_t DP_END = dpc_regs[DPC_END_REG] & 0x00FFFFF8; + + int length = DP_END - DP_CURRENT; + if (length <= 0) + return interrupt_timer; + + length = unsigned(length) >> 3; + if ((cmd_ptr + length) & ~(0x0003FFFF >> 3)) + return interrupt_timer; + + dpc_regs[DPC_STATUS_REG] |= DP_STATUS_PIPE_BUSY | DP_STATUS_START_GCLK; + + uint32_t offset = DP_CURRENT; + if (dpc_regs[DPC_STATUS_REG] & DP_STATUS_XBUS_DMA) + { + do + { + offset &= 0xFF8; + cmd_data[2 * cmd_ptr + 0] = SDL_SwapBE32(*reinterpret_cast(SP_DMEM + offset)); + cmd_data[2 * cmd_ptr + 1] = SDL_SwapBE32(*reinterpret_cast(SP_DMEM + offset + 4)); + offset += sizeof(uint64_t); + cmd_ptr++; + } while (--length > 0); + } + else + { + if (DP_END > 0x7ffffff || DP_CURRENT > 0x7ffffff) + { + return interrupt_timer; + } + else + { + do + { + offset &= 0xFFFFF8; + cmd_data[2 * cmd_ptr + 0] = *reinterpret_cast((uint8_t *)rdram + offset); + cmd_data[2 * cmd_ptr + 1] = *reinterpret_cast((uint8_t *)rdram + offset + 4); + offset += sizeof(uint64_t); + cmd_ptr++; + } while (--length > 0); + } + } + + while (cmd_cur - cmd_ptr < 0) + { + uint32_t w1 = cmd_data[2 * cmd_cur]; + uint32_t command = (w1 >> 24) & 63; + int cmd_length = cmd_len_lut[command]; + + if (cmd_ptr - cmd_cur - cmd_length < 0) + { + dpc_regs[DPC_START_REG] = dpc_regs[DPC_CURRENT_REG] = dpc_regs[DPC_END_REG]; + return interrupt_timer; + } + + if (command >= 8) + processor->enqueue_command(cmd_length * 2, &cmd_data[2 * cmd_cur]); + + if (RDP::Op(command) == RDP::Op::SyncFull) + { + if (frame_counter != last_frame_counter) // Only sync once per frame + { + rdp_sync_signal = processor->signal_timeline(); + last_frame_counter = frame_counter; + } + else + { + rdp_sync_signal = 0; + } + + uint32_t width = viCalculateHorizonalWidth(vi_registers[VI_H_START_REG], vi_registers[VI_X_SCALE_REG], vi_registers[VI_WIDTH_REG]); + uint32_t height = viCalculateVerticalHeight(vi_registers[VI_V_START_REG], vi_registers[VI_Y_SCALE_REG]); + interrupt_timer = width * height * 4; + + dpc_regs[DPC_STATUS_REG] &= ~(DP_STATUS_PIPE_BUSY | DP_STATUS_START_GCLK); + } + + cmd_cur += cmd_length; + } + + cmd_ptr = 0; + cmd_cur = 0; + dpc_regs[DPC_CURRENT_REG] = dpc_regs[DPC_END_REG]; + dpc_regs[DPC_STATUS_REG] |= DP_STATUS_CBUF_READY; + + return interrupt_timer; +} + +void full_sync() +{ + if (rdp_sync_signal) + { + processor->wait_for_timeline(rdp_sync_signal); + } +} diff --git a/parallel-rdp/interface.hpp b/parallel-rdp/interface.hpp new file mode 100644 index 00000000..c084e858 --- /dev/null +++ b/parallel-rdp/interface.hpp @@ -0,0 +1,18 @@ +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + + void vk_init(void *mem_base, uint32_t rdram_size); + void vk_close(); + void set_sdl_window(void *_window); + void rdp_set_vi_register(uint32_t reg, uint32_t value); + uint8_t rdp_update_screen(); + uint64_t rdp_process_commands(uint32_t *dpc_regs, uint8_t *SP_DMEM); + void full_sync(); + +#ifdef __cplusplus +} +#endif diff --git a/parallel-rdp/parallel-rdp-standalone b/parallel-rdp/parallel-rdp-standalone new file mode 160000 index 00000000..37bf2967 --- /dev/null +++ b/parallel-rdp/parallel-rdp-standalone @@ -0,0 +1 @@ +Subproject commit 37bf2967e3dd18e059cf5563717bb6c88c261f33 diff --git a/parallel-rdp/spirv.hpp b/parallel-rdp/spirv.hpp new file mode 100644 index 00000000..50619fc9 --- /dev/null +++ b/parallel-rdp/spirv.hpp @@ -0,0 +1,109 @@ +#pragma once + +static const uint32_t vertex_spirv[] = + {0x07230203, 0x00010000, 0x000d000a, 0x00000034, + 0x00000000, 0x00020011, 0x00000001, 0x0006000b, + 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, + 0x00000000, 0x0003000e, 0x00000000, 0x00000001, + 0x0008000f, 0x00000000, 0x00000004, 0x6e69616d, + 0x00000000, 0x00000008, 0x00000016, 0x0000002b, + 0x00040047, 0x00000008, 0x0000000b, 0x0000002a, + 0x00050048, 0x00000014, 0x00000000, 0x0000000b, + 0x00000000, 0x00050048, 0x00000014, 0x00000001, + 0x0000000b, 0x00000001, 0x00050048, 0x00000014, + 0x00000002, 0x0000000b, 0x00000003, 0x00050048, + 0x00000014, 0x00000003, 0x0000000b, 0x00000004, + 0x00030047, 0x00000014, 0x00000002, 0x00040047, + 0x0000002b, 0x0000001e, 0x00000000, 0x00020013, + 0x00000002, 0x00030021, 0x00000003, 0x00000002, + 0x00040015, 0x00000006, 0x00000020, 0x00000001, + 0x00040020, 0x00000007, 0x00000001, 0x00000006, + 0x0004003b, 0x00000007, 0x00000008, 0x00000001, + 0x0004002b, 0x00000006, 0x0000000a, 0x00000000, + 0x00020014, 0x0000000b, 0x00030016, 0x0000000f, + 0x00000020, 0x00040017, 0x00000010, 0x0000000f, + 0x00000004, 0x00040015, 0x00000011, 0x00000020, + 0x00000000, 0x0004002b, 0x00000011, 0x00000012, + 0x00000001, 0x0004001c, 0x00000013, 0x0000000f, + 0x00000012, 0x0006001e, 0x00000014, 0x00000010, + 0x0000000f, 0x00000013, 0x00000013, 0x00040020, + 0x00000015, 0x00000003, 0x00000014, 0x0004003b, + 0x00000015, 0x00000016, 0x00000003, 0x0004002b, + 0x0000000f, 0x00000017, 0xbf800000, 0x0004002b, + 0x0000000f, 0x00000018, 0x00000000, 0x0004002b, + 0x0000000f, 0x00000019, 0x3f800000, 0x0007002c, + 0x00000010, 0x0000001a, 0x00000017, 0x00000017, + 0x00000018, 0x00000019, 0x00040020, 0x0000001b, + 0x00000003, 0x00000010, 0x0004002b, 0x00000006, + 0x0000001f, 0x00000001, 0x0004002b, 0x0000000f, + 0x00000023, 0x40400000, 0x0007002c, 0x00000010, + 0x00000024, 0x00000017, 0x00000023, 0x00000018, + 0x00000019, 0x0007002c, 0x00000010, 0x00000027, + 0x00000023, 0x00000017, 0x00000018, 0x00000019, + 0x00040017, 0x00000029, 0x0000000f, 0x00000002, + 0x00040020, 0x0000002a, 0x00000003, 0x00000029, + 0x0004003b, 0x0000002a, 0x0000002b, 0x00000003, + 0x0004002b, 0x0000000f, 0x0000002f, 0x3f000000, + 0x0005002c, 0x00000029, 0x00000033, 0x0000002f, + 0x0000002f, 0x00050036, 0x00000002, 0x00000004, + 0x00000000, 0x00000003, 0x000200f8, 0x00000005, + 0x0004003d, 0x00000006, 0x00000009, 0x00000008, + 0x000500aa, 0x0000000b, 0x0000000c, 0x00000009, + 0x0000000a, 0x000300f7, 0x0000000e, 0x00000000, + 0x000400fa, 0x0000000c, 0x0000000d, 0x0000001d, + 0x000200f8, 0x0000000d, 0x00050041, 0x0000001b, + 0x0000001c, 0x00000016, 0x0000000a, 0x0003003e, + 0x0000001c, 0x0000001a, 0x000200f9, 0x0000000e, + 0x000200f8, 0x0000001d, 0x000500aa, 0x0000000b, + 0x00000020, 0x00000009, 0x0000001f, 0x000300f7, + 0x00000022, 0x00000000, 0x000400fa, 0x00000020, + 0x00000021, 0x00000026, 0x000200f8, 0x00000021, + 0x00050041, 0x0000001b, 0x00000025, 0x00000016, + 0x0000000a, 0x0003003e, 0x00000025, 0x00000024, + 0x000200f9, 0x00000022, 0x000200f8, 0x00000026, + 0x00050041, 0x0000001b, 0x00000028, 0x00000016, + 0x0000000a, 0x0003003e, 0x00000028, 0x00000027, + 0x000200f9, 0x00000022, 0x000200f8, 0x00000022, + 0x000200f9, 0x0000000e, 0x000200f8, 0x0000000e, + 0x00050041, 0x0000001b, 0x0000002c, 0x00000016, + 0x0000000a, 0x0004003d, 0x00000010, 0x0000002d, + 0x0000002c, 0x0007004f, 0x00000029, 0x0000002e, + 0x0000002d, 0x0000002d, 0x00000000, 0x00000001, + 0x0005008e, 0x00000029, 0x00000030, 0x0000002e, + 0x0000002f, 0x00050081, 0x00000029, 0x00000032, + 0x00000030, 0x00000033, 0x0003003e, 0x0000002b, + 0x00000032, 0x000100fd, 0x00010038}; + +static const uint32_t fragment_spirv[] = + {0x07230203, 0x00010000, 0x000d000a, 0x00000015, + 0x00000000, 0x00020011, 0x00000001, 0x0006000b, + 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, + 0x00000000, 0x0003000e, 0x00000000, 0x00000001, + 0x0007000f, 0x00000004, 0x00000004, 0x6e69616d, + 0x00000000, 0x00000009, 0x00000011, 0x00030010, + 0x00000004, 0x00000007, 0x00040047, 0x00000009, + 0x0000001e, 0x00000000, 0x00040047, 0x0000000d, + 0x00000022, 0x00000000, 0x00040047, 0x0000000d, + 0x00000021, 0x00000000, 0x00040047, 0x00000011, + 0x0000001e, 0x00000000, 0x00020013, 0x00000002, + 0x00030021, 0x00000003, 0x00000002, 0x00030016, + 0x00000006, 0x00000020, 0x00040017, 0x00000007, + 0x00000006, 0x00000004, 0x00040020, 0x00000008, + 0x00000003, 0x00000007, 0x0004003b, 0x00000008, + 0x00000009, 0x00000003, 0x00090019, 0x0000000a, + 0x00000006, 0x00000001, 0x00000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000000, 0x0003001b, + 0x0000000b, 0x0000000a, 0x00040020, 0x0000000c, + 0x00000000, 0x0000000b, 0x0004003b, 0x0000000c, + 0x0000000d, 0x00000000, 0x00040017, 0x0000000f, + 0x00000006, 0x00000002, 0x00040020, 0x00000010, + 0x00000001, 0x0000000f, 0x0004003b, 0x00000010, + 0x00000011, 0x00000001, 0x0004002b, 0x00000006, + 0x00000013, 0x00000000, 0x00050036, 0x00000002, + 0x00000004, 0x00000000, 0x00000003, 0x000200f8, + 0x00000005, 0x0004003d, 0x0000000b, 0x0000000e, + 0x0000000d, 0x0004003d, 0x0000000f, 0x00000012, + 0x00000011, 0x00070058, 0x00000007, 0x00000014, + 0x0000000e, 0x00000012, 0x00000002, 0x00000013, + 0x0003003e, 0x00000009, 0x00000014, 0x000100fd, + 0x00010038}; diff --git a/parallel-rdp/wsi_platform.cpp b/parallel-rdp/wsi_platform.cpp new file mode 100644 index 00000000..4cfc2136 --- /dev/null +++ b/parallel-rdp/wsi_platform.cpp @@ -0,0 +1,60 @@ +#include "wsi_platform.hpp" +#include + +VkSurfaceKHR SDL_WSIPlatform::create_surface(VkInstance instance, VkPhysicalDevice gpu) +{ + VkSurfaceKHR surface = nullptr; + SDL_bool result = SDL_Vulkan_CreateSurface(window, instance, &surface); + if (result != SDL_TRUE) + { + printf("Error creating surface\n"); + } + return surface; +} + +void SDL_WSIPlatform::destroy_surface(VkInstance instance, VkSurfaceKHR surface) +{ +} + +std::vector SDL_WSIPlatform::get_instance_extensions() +{ + + unsigned int extensionCount = 0; + SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr); + std::vector extensionNames(extensionCount); + SDL_bool result = SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensionNames.data()); + if (result != SDL_TRUE) + { + printf("Error creating surface\n"); + } + return extensionNames; +} + +uint32_t SDL_WSIPlatform::get_surface_width() +{ + int w, h; + SDL_GetWindowSize(window, &w, &h); + return w; +} + +uint32_t SDL_WSIPlatform::get_surface_height() +{ + int w, h; + SDL_GetWindowSize(window, &w, &h); + return h; +} + +bool SDL_WSIPlatform::alive(Vulkan::WSI &wsi) +{ + return true; +} + +void SDL_WSIPlatform::poll_input() +{ + SDL_PumpEvents(); +} + +void SDL_WSIPlatform::set_window(SDL_Window *_window) +{ + window = _window; +} diff --git a/parallel-rdp/wsi_platform.hpp b/parallel-rdp/wsi_platform.hpp new file mode 100644 index 00000000..5c1c0fe8 --- /dev/null +++ b/parallel-rdp/wsi_platform.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "wsi.hpp" +#include + +class SDL_WSIPlatform : public Vulkan::WSIPlatform +{ +public: + VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override; + void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override; + std::vector get_instance_extensions() override; + uint32_t get_surface_width() override; + uint32_t get_surface_height() override; + bool alive(Vulkan::WSI &wsi) override; + void poll_input() override; + void set_window(SDL_Window *_window); + +private: + VkSurfaceKHR surface; + SDL_Window *window; +}; diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 00000000..3ed2e224 --- /dev/null +++ b/src/device.rs @@ -0,0 +1,262 @@ +use crate::ui; + +pub mod ai; +pub mod cache; +pub mod cart; +pub mod cart_rom; +pub mod controller; +pub mod cop0; +pub mod cop1; +pub mod cop2; +pub mod cpu; +pub mod cpu_instructions; +pub mod events; +pub mod exceptions; +pub mod fpu_instructions; +pub mod is_viewer; +pub mod memory; +pub mod mi; +pub mod pi; +pub mod pif; +pub mod rdp; +pub mod rdram; +pub mod ri; +pub mod rsp_cpu; +pub mod rsp_interface; +pub mod rsp_su_instructions; +pub mod rsp_vu_instructions; +pub mod si; +pub mod sram; +pub mod tlb; +pub mod unmapped; +pub mod vi; + +pub struct Device { + pub ui: ui::Ui, + byte_swap: usize, + cpu: cpu::Cpu, + pif: pif::Pif, + cart: cart_rom::Cart, + memory: memory::Memory, + rsp: rsp_interface::Rsp, + rdp: rdp::Rdp, + pub rdram: rdram::Rdram, + mi: mi::Mi, + pi: pi::Pi, + vi: vi::Vi, + ai: ai::Ai, + si: si::Si, + ri: ri::Ri, +} + +impl Device { + pub fn new() -> Device { + let mut byte_swap: usize = 0; + let test: [u8; 4] = [1, 2, 3, 4]; + // if the host computer is little endian, that means the RDRAM will be stored as little endian + // when accessing bytes in RDRAM, we need to swap them by XORing with the byte_swap value (3) + if u32::from_le_bytes(test) == u32::from_ne_bytes(test) { + byte_swap = 3; + } + Device { + ui: ui::Ui::new(), + byte_swap: byte_swap, + cpu: cpu::Cpu { + cop0: cop0::Cop0 { + regs: [0; cop0::COP0_REGS_COUNT as usize], + reg_write_masks: [0; cop0::COP0_REGS_COUNT as usize], + reg_latch: 0, + instrs: [cop0::reserved; 32], + instrs2: [cop0::reserved; 32], + tlb_lut_w: vec![ + tlb::TlbLut { + address: 0, + cached: false, + }; + 0x100000 + ], + tlb_lut_r: vec![ + tlb::TlbLut { + address: 0, + cached: false, + }; + 0x100000 + ], + tlb_entries: [tlb::TlbEntry { + mask: 0, + vpn2: 0, + region: 0, + g: 0, + asid: 0, + pfn_even: 0, + c_even: 0, + d_even: 0, + v_even: 0, + pfn_odd: 0, + c_odd: 0, + d_odd: 0, + v_odd: 0, + start_even: 0, + end_even: 0, + phys_even: 0, + start_odd: 0, + end_odd: 0, + phys_odd: 0, + }; 32], + }, + cop1: cop1::Cop1 { + fcr0: 0, + fcr31: 0, + fgr32: [[0; 4]; 32], + fgr64: [[0; 8]; 32], + instrs: [cop1::reserved; 32], + b_instrs: [cop1::reserved; 4], + s_instrs: [cop1::reserved; 64], + d_instrs: [cop1::reserved; 64], + w_instrs: [cop1::reserved; 64], + l_instrs: [cop1::reserved; 64], + }, + cop2: cop2::Cop2 { + instrs: [cop2::reserved; 32], + reg_latch: 0, + }, + branch_state: cpu::BranchState { + state: cpu::State::Step, + pc: 0, + }, + gpr: [0; 32], + clock_rate: 0, + pc: 0xBFC00000, + llbit: false, + lo: 0, + hi: 0, + running: 0, + instrs: [cop0::reserved; 64], + special_instrs: [cop0::reserved; 64], + regimm_instrs: [cop0::reserved; 32], + events: vec![], + next_event_count: u64::MAX, + next_event: None, + }, + pif: pif::Pif { + rom: [0; 1984], + ram: [0; 64], + channels: [pif::PifChannel { + tx: None, + tx_buf: None, + rx: None, + rx_buf: None, + process: None, + }; 5], + }, + cart: cart_rom::Cart { + rom: Vec::new(), + is_viewer_buffer: [0; 0xFFFF], + pal: false, + latch: 0, + cic_seed: 0, + cic_type: cart_rom::CicType::CicNus6102, + rdram_size_offset: 0, + }, + memory: memory::Memory { + fast_read: [unmapped::read_mem; 0x2000], + memory_map_read: [unmapped::read_mem; 0x2000], + memory_map_write: [unmapped::write_mem; 0x2000], + icache: [cache::ICache { + valid: false, + index: 0, + tag: 0, + words: [0; 8], + instruction: [cop0::reserved; 8], + }; 512], + dcache: [cache::DCache { + valid: false, + dirty: false, + tag: 0, + index: 0, + words: [0; 4], + }; 512], + }, + rdram: rdram::Rdram { mem: vec![] }, + rsp: rsp_interface::Rsp { + cpu: rsp_cpu::Cpu { + branch_state: rsp_cpu::BranchState { + state: cpu::State::Step, + pc: 0, + }, + broken: false, + halted: false, + sync_point: false, + gpr: [0; 32], + vpr: [0; 32], + reciprocals: [0; 512], + inverse_square_roots: [0; 512], + divdp: false, + divin: 0, + divout: 0, + vcol: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + vcoh: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + vccl: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + vcch: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + vce: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + accl: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + accm: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + acch: unsafe { std::arch::x86_64::_mm_setzero_si128() }, + special_instrs: [rsp_su_instructions::reserved; 48], + regimm_instrs: [rsp_su_instructions::reserved; 18], + cop0_instrs: [rsp_su_instructions::reserved; 5], + cop2_instrs: [rsp_su_instructions::reserved; 32], + lwc2_instrs: [rsp_su_instructions::reserved; 12], + swc2_instrs: [rsp_su_instructions::reserved; 12], + instrs: [rsp_su_instructions::reserved; 64], + vec_instrs: [rsp_vu_instructions::reserved; 64], + }, + regs: [0; rsp_interface::SP_REGS_COUNT as usize], + regs2: [0; rsp_interface::SP_REGS2_COUNT as usize], + mem: [0; 0x2000], + fifo: [rsp_interface::RspDma { + dir: rsp_interface::DmaDir::None, + length: 0, + memaddr: 0, + dramaddr: 0, + }; 2], + }, + rdp: rdp::Rdp { + regs_dpc: [0; rdp::DPC_REGS_COUNT as usize], + regs_dps: [0; rdp::DPS_REGS_COUNT as usize], + }, + mi: mi::Mi { + regs: [0; mi::MI_REGS_COUNT as usize], + }, + pi: pi::Pi { + regs: [0; pi::PI_REGS_COUNT as usize], + }, + ai: ai::Ai { + regs: [0; ai::AI_REGS_COUNT as usize], + last_read: 0, + delayed_carry: false, + fifo: [ai::AiDma { + address: 0, + length: 0, + duration: 0, + }; 2], + }, + si: si::Si { + regs: [0; si::SI_REGS_COUNT as usize], + dma_dir: si::DmaDir::None, + }, + ri: ri::Ri { + regs: [0; ri::RI_REGS_COUNT as usize], + }, + vi: vi::Vi { + regs: [0; vi::VI_REGS_COUNT as usize], + clock: 0, + field: 0, + delay: 0, + count_per_scanline: 0, + last_vi_time: std::time::Instant::now(), + vi_period: std::time::Duration::from_secs_f64(0.0), + }, + } + } +} diff --git a/src/device/ai.rs b/src/device/ai.rs new file mode 100644 index 00000000..2b192b41 --- /dev/null +++ b/src/device/ai.rs @@ -0,0 +1,169 @@ +use crate::device; +use crate::ui; + +pub const AI_DRAM_ADDR_REG: u32 = 0; +pub const AI_LEN_REG: u32 = 1; +//pub const AI_CONTROL_REG: u32 = 2; +pub const AI_STATUS_REG: u32 = 3; +pub const AI_DACRATE_REG: u32 = 4; +//pub const AI_BITRATE_REG: u32 = 5; +pub const AI_REGS_COUNT: u32 = 6; + +pub const AI_STATUS_BUSY: u32 = 0x40000000; +pub const AI_STATUS_FULL: u32 = 0x80000000; + +pub struct Ai { + pub regs: [u32; AI_REGS_COUNT as usize], + pub fifo: [AiDma; 2], + pub last_read: u64, + pub delayed_carry: bool, +} + +#[derive(Copy, Clone)] +pub struct AiDma { + pub address: u64, + pub length: u64, + pub duration: u64, +} + +pub fn get_remaining_dma_length(device: &mut device::Device) -> u64 { + if device.ai.fifo[0].duration == 0 { + return 0; + } + + let next_ai_event = device::events::get_event(device, device::events::EventType::AI); + if next_ai_event == None { + return 0; + } + + let remaining_dma_duration = + next_ai_event.unwrap().count - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize]; + + let dma_length = remaining_dma_duration * device.ai.fifo[0].length / device.ai.fifo[0].duration; + return dma_length & !7; +} + +pub fn get_dma_duration(device: &mut device::Device) -> u64 { + let samples_per_sec = device.vi.clock / (1 + device.ai.regs[AI_DACRATE_REG as usize]) as u64; + let bytes_per_sample = 4; /* XXX: assume 16bit stereo - should depends on bitrate instead */ + let length = (device.ai.regs[AI_LEN_REG as usize] & !7) as u64; + + return length * (device.cpu.clock_rate / (bytes_per_sample * samples_per_sec)); +} + +pub fn do_dma(device: &mut device::Device) { + device.ai.last_read = device.ai.fifo[0].length; + + if device.ai.delayed_carry { + device.ai.fifo[0].address += 0x2000; + } + + if ((device.ai.fifo[0].address + device.ai.fifo[0].length) & 0x1FFF) == 0 { + device.ai.delayed_carry = true; + } else { + device.ai.delayed_carry = false; + } + + /* schedule end of dma event */ + device::events::create_event( + device, + device::events::EventType::AI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.ai.fifo[0].duration, + dma_event, + ) +} + +pub fn fifo_push(device: &mut device::Device) { + let duration = get_dma_duration(device); + + if (device.ai.regs[AI_STATUS_REG as usize] & AI_STATUS_BUSY) != 0 { + device.ai.fifo[1].address = + device.ai.regs[AI_DRAM_ADDR_REG as usize] as u64 & device::rdram::RDRAM_MASK as u64; + device.ai.fifo[1].length = (device.ai.regs[AI_LEN_REG as usize] & !7) as u64; + device.ai.fifo[1].duration = duration; + device.ai.regs[AI_STATUS_REG as usize] |= AI_STATUS_FULL; + } else { + device.ai.fifo[0].address = + device.ai.regs[AI_DRAM_ADDR_REG as usize] as u64 & device::rdram::RDRAM_MASK as u64; + device.ai.fifo[0].length = (device.ai.regs[AI_LEN_REG as usize] & !7) as u64; + device.ai.fifo[0].duration = duration; + device.ai.regs[AI_STATUS_REG as usize] |= AI_STATUS_BUSY; + + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI); + do_dma(device); + } +} + +pub fn fifo_pop(device: &mut device::Device) { + if device.ai.regs[AI_STATUS_REG as usize] & AI_STATUS_FULL != 0 { + device.ai.fifo[0].address = device.ai.fifo[1].address; + device.ai.fifo[0].length = device.ai.fifo[1].length; + device.ai.fifo[0].duration = device.ai.fifo[1].duration; + device.ai.regs[AI_STATUS_REG as usize] &= !AI_STATUS_FULL; + + do_dma(device); + } else { + device.ai.regs[AI_STATUS_REG as usize] &= !AI_STATUS_BUSY; + device.ai.delayed_carry = false; + } +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + AI_LEN_REG => { + let value = get_remaining_dma_length(device); + let diff = device.ai.fifo[0].length - device.ai.last_read; + + ui::audio::play_audio( + device, + (device.ai.fifo[0].address + diff) as usize, + device.ai.last_read - value, + ); + + device.ai.last_read = value; + return value as u32; + } + _ => return device.ai.regs[reg as usize], + } +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + AI_LEN_REG => { + device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask); + if device.ai.regs[reg as usize] != 0 { + fifo_push(device) + } + } + AI_STATUS_REG => device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_AI), + AI_DACRATE_REG => { + if device.ai.regs[reg as usize] != value & mask { + let frequency = device.vi.clock / (1 + (value & mask)) as u64; + ui::audio::init(&mut device.ui, frequency) + } + device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask) + } + _ => device::memory::masked_write_32(&mut device.ai.regs[reg as usize], value, mask), + } +} + +pub fn dma_event(device: &mut device::Device) { + if device.ai.last_read != 0 { + let diff = device.ai.fifo[0].length - device.ai.last_read; + ui::audio::play_audio( + device, + (device.ai.fifo[0].address + diff) as usize, + device.ai.last_read, + ); + device.ai.last_read = 0; + } + + fifo_pop(device); + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_AI); +} diff --git a/src/device/cache.rs b/src/device/cache.rs new file mode 100644 index 00000000..3c94da0f --- /dev/null +++ b/src/device/cache.rs @@ -0,0 +1,281 @@ +use crate::device; + +#[derive(Copy, Clone)] +pub struct ICache { + pub valid: bool, + pub tag: u32, + pub index: u16, + pub words: [u32; 8], + pub instruction: [fn(&mut device::Device, u32); 8], +} + +#[derive(Copy, Clone)] +pub struct DCache { + pub valid: bool, + pub dirty: bool, + pub tag: u32, + pub index: u16, + pub words: [u32; 4], +} + +pub fn icache_hit(device: &mut device::Device, line_index: usize, phys_address: u64) -> bool { + return device.memory.icache[line_index].valid + && (device.memory.icache[line_index].tag & 0x1ffffffc) == (phys_address & !0xFFF) as u32; +} + +pub fn icache_writeback(device: &mut device::Device, line_index: usize) { + device::cop0::add_cycles(device, device::rdram::rdram_calculate_cycles(32)); + + let cache_address = ((device.memory.icache[line_index].tag + | (device.memory.icache[line_index].index) as u32) + & 0x1ffffffc) as u64; + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x0, + device.memory.icache[line_index].words[0], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x4, + device.memory.icache[line_index].words[1], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x8, + device.memory.icache[line_index].words[2], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0xC, + device.memory.icache[line_index].words[3], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x10, + device.memory.icache[line_index].words[4], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x14, + device.memory.icache[line_index].words[5], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x18, + device.memory.icache[line_index].words[6], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x1C, + device.memory.icache[line_index].words[7], + 0xFFFFFFFF, + ); +} + +pub fn icache_fill(device: &mut device::Device, line_index: usize, phys_address: u64) { + device::cop0::add_cycles(device, 8); + + device.memory.icache[line_index].valid = true; + device.memory.icache[line_index].tag = (phys_address & !0xFFF) as u32; + let cache_address = ((device.memory.icache[line_index].tag + | (device.memory.icache[line_index].index) as u32) + & 0x1ffffffc) as u64; + device.memory.icache[line_index].words[0] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x0, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[1] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x4, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[2] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x8, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[3] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0xC, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[4] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x10, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[5] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x14, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[6] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x18, + device::memory::AccessSize::Icache, + ); + device.memory.icache[line_index].words[7] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x1C, + device::memory::AccessSize::Icache, + ); + + device.memory.icache[line_index].instruction[0] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[0]); + device.memory.icache[line_index].instruction[1] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[1]); + device.memory.icache[line_index].instruction[2] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[2]); + device.memory.icache[line_index].instruction[3] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[3]); + device.memory.icache[line_index].instruction[4] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[4]); + device.memory.icache[line_index].instruction[5] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[5]); + device.memory.icache[line_index].instruction[6] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[6]); + device.memory.icache[line_index].instruction[7] = + device::cpu::decode_opcode(device, device.memory.icache[line_index].words[7]); +} + +pub fn icache_fetch(device: &mut device::Device, phys_address: u64) { + let line_index = ((phys_address >> 5) & 0x1FF) as usize; + if !icache_hit(device, line_index, phys_address) { + icache_fill(device, line_index, phys_address) + } + device.memory.icache[line_index].instruction[((phys_address >> 2) & 7) as usize]( + device, + device.memory.icache[line_index].words[((phys_address >> 2) & 7) as usize], + ); +} + +pub fn dcache_hit(device: &mut device::Device, line_index: usize, phys_address: u64) -> bool { + return device.memory.dcache[line_index].valid + && (device.memory.dcache[line_index].tag & 0x1ffffffc) == (phys_address & !0xFFF) as u32; +} + +pub fn dcache_writeback(device: &mut device::Device, line_index: usize) { + device::cop0::add_cycles(device, device::rdram::rdram_calculate_cycles(16)); + + device.memory.dcache[line_index].dirty = false; + + let cache_address = ((device.memory.dcache[line_index].tag + | (device.memory.dcache[line_index].index) as u32) + & 0x1ffffffc) as u64; + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x0, + device.memory.dcache[line_index].words[0], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x4, + device.memory.dcache[line_index].words[1], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0x8, + device.memory.dcache[line_index].words[2], + 0xFFFFFFFF, + ); + device.memory.memory_map_write[(cache_address >> 16) as usize]( + device, + cache_address | 0xC, + device.memory.dcache[line_index].words[3], + 0xFFFFFFFF, + ); +} + +pub fn dcache_fill(device: &mut device::Device, line_index: usize, phys_address: u64) { + device::cop0::add_cycles(device, 7); + + device.memory.dcache[line_index].valid = true; + device.memory.dcache[line_index].dirty = false; + + device.memory.dcache[line_index].tag = (phys_address & !0xFFF) as u32; + let cache_address = ((device.memory.dcache[line_index].tag + | (device.memory.dcache[line_index].index) as u32) + & 0x1ffffffc) as u64; + device.memory.dcache[line_index].words[0] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x0, + device::memory::AccessSize::Dcache, + ); + device.memory.dcache[line_index].words[1] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x4, + device::memory::AccessSize::Dcache, + ); + device.memory.dcache[line_index].words[2] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0x8, + device::memory::AccessSize::Dcache, + ); + device.memory.dcache[line_index].words[3] = device.memory.memory_map_read + [(cache_address >> 16) as usize]( + device, + cache_address | 0xC, + device::memory::AccessSize::Dcache, + ); +} + +pub fn dcache_read(device: &mut device::Device, phys_address: u64) -> u32 { + let line_index = ((phys_address >> 4) & 0x1FF) as usize; + if !dcache_hit(device, line_index, phys_address) { + if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty { + dcache_writeback(device, line_index) + } + dcache_fill(device, line_index, phys_address) + } else { + device::cop0::add_cycles(device, 1) + } + return device.memory.dcache[line_index].words[((phys_address >> 2) & 3) as usize]; +} + +pub fn dcache_write(device: &mut device::Device, phys_address: u64, value: u32, mask: u32) { + let line_index = ((phys_address >> 4) & 0x1FF) as usize; + if !dcache_hit(device, line_index, phys_address) { + if device.memory.dcache[line_index].valid && device.memory.dcache[line_index].dirty { + dcache_writeback(device, line_index) + } + dcache_fill(device, line_index, phys_address) + } else { + device::cop0::add_cycles(device, 1) + } + device::memory::masked_write_32( + &mut device.memory.dcache[line_index].words[((phys_address >> 2) & 3) as usize], + value, + mask, + ); + device.memory.dcache[line_index].dirty = true; +} + +pub fn init(device: &mut device::Device) { + for (pos, i) in device.memory.icache.iter_mut().enumerate() { + i.index = (pos << 5) as u16 & 0xFE0 + } + for (pos, i) in device.memory.dcache.iter_mut().enumerate() { + i.index = (pos << 4) as u16 & 0xFF0 + } +} diff --git a/src/device/cart.rs b/src/device/cart.rs new file mode 100644 index 00000000..5d1dde2d --- /dev/null +++ b/src/device/cart.rs @@ -0,0 +1,55 @@ +use crate::device; + +pub const JCMD_STATUS: u8 = 0x00; +pub const JCMD_EEPROM_READ: u8 = 0x04; +pub const JCMD_EEPROM_WRITE: u8 = 0x05; +pub const JCMD_RESET: u8 = 0xff; + +//pub const JDT_AF_RTC: u16 = 0x1000; /* RTC */ +pub const JDT_EEPROM_4K: u16 = 0x8000; /* 4k EEPROM */ +//pub const JDT_EEPROM_16K: u16 = 0xc000; /* 16k EEPROM */ +pub const EEPROM_TYPE: u16 = JDT_EEPROM_4K; // todo, support 16k eeprom + //pub const EEPROM_BLOCK_SIZE: usize = 8; + +pub fn process(device: &mut device::Device, channel: usize) { + let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()]; + + match cmd { + JCMD_RESET => { /* reset */ } + JCMD_STATUS => { + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = EEPROM_TYPE as u8; + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] = + (EEPROM_TYPE >> 8) as u8; + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = 0; + } + JCMD_EEPROM_READ => { + eeprom_read_block( + device, + device.pif.channels[channel].tx_buf.unwrap() + 1, + device.pif.channels[channel].rx_buf.unwrap(), + ); + } + JCMD_EEPROM_WRITE => eeprom_write_block( + device, + device.pif.channels[channel].tx_buf.unwrap() + 1, + device.pif.channels[channel].tx_buf.unwrap() + 2, + device.pif.channels[channel].rx_buf.unwrap(), + ), + _ => { + panic!("unknown cart command") + } + } +} + +pub fn eeprom_read_block(_device: &mut device::Device, _block: usize, _data: usize) { + panic!("eeprom read") +} + +pub fn eeprom_write_block( + _device: &mut device::Device, + _block: usize, + _data: usize, + _status: usize, +) { + panic!("eeprom write") +} diff --git a/src/device/cart_rom.rs b/src/device/cart_rom.rs new file mode 100644 index 00000000..6d25e925 --- /dev/null +++ b/src/device/cart_rom.rs @@ -0,0 +1,151 @@ +use crate::device; +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +pub enum CicType { + CicNus6101, + CicNus6102, + CicNus6103, + CicNus6105, + CicNus6106, +} + +pub const CART_MASK: usize = 0xFFFFFFF; +pub struct Cart { + pub rom: Vec, + pub is_viewer_buffer: [u8; 0xFFFF], + pub pal: bool, + pub latch: u32, + pub cic_type: CicType, + pub cic_seed: u8, + pub rdram_size_offset: usize, +} + +pub fn read_mem_fast( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let masked_address = address as usize & CART_MASK; + return u32::from_be_bytes( + device.cart.rom[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); +} + +pub fn read_mem( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let cycles = device::pi::calculate_cycles(device, 1, 4); + device::cop0::add_cycles(device, cycles); + + // well known cart ROM oddity, if a read is perfomed while PI_STATUS_IO_BUSY is set, the latched value is returned rather than the data at the specified address + if device.pi.regs[device::pi::PI_STATUS_REG as usize] & device::pi::PI_STATUS_IO_BUSY != 0 { + return device.cart.latch; + } else { + let masked_address = address as usize & CART_MASK; + return u32::from_be_bytes( + device.cart.rom[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + } +} + +pub fn write_mem(device: &mut device::Device, _address: u64, value: u32, mask: u32) { + device.cart.latch = value & mask; + + device.pi.regs[device::pi::PI_STATUS_REG as usize] |= device::pi::PI_STATUS_IO_BUSY; + + let cycles = device::pi::calculate_cycles(device, 1, 4); + device::events::create_event( + device, + device::events::EventType::PI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles, + device::pi::dma_event, + ); +} + +pub fn dma_read(device: &mut device::Device, _cart_addr: u32, _dram_addr: u32, length: u32) -> u64 { + return device::pi::calculate_cycles(device, 1, length); +} + +// cart is big endian, rdram is native endian +pub fn dma_write(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 { + let mut i = dram_addr & device::rdram::RDRAM_MASK as u32; + let mut j = cart_addr & CART_MASK as u32; + while i < (dram_addr & device::rdram::RDRAM_MASK as u32) + length + && j < device.cart.rom.len() as u32 + { + device.rdram.mem[i as usize ^ device.byte_swap] = device.cart.rom[j as usize]; + i += 1; + j += 1; + } + while i < (dram_addr & device::rdram::RDRAM_MASK as u32) + length { + // DMAs that extend past the end of the ROM return 0's for the portion that extends past the ROM length + device.rdram.mem[i as usize ^ device.byte_swap] = 0; + i += 1; + } + return device::pi::calculate_cycles(device, 1, length); +} + +pub fn init(device: &mut device::Device, rom_file: Vec) { + device.cart.rom = rom_file; + set_system_region(device, device.cart.rom[0x3E]); + set_cic(device) +} + +pub fn set_system_region(device: &mut device::Device, country: u8) { + let pal_codes: [u8; 8] = [b'D', b'F', b'I', b'P', b'S', b'U', b'X', b'Y']; + for i in pal_codes { + if country == i { + device.cart.pal = true + } + } +} + +pub fn set_cic(device: &mut device::Device) { + let hash = calculate_hash(&device.cart.rom[0x40..0x1000]); + match hash { + 0x83a9a60ad75b3ed9 => { + device.cart.cic_type = CicType::CicNus6101; + device.cart.cic_seed = 0x3F; + device.cart.rdram_size_offset = 0x318; + } + 0x3c1e2f5171ec2b8 => { + device.cart.cic_type = CicType::CicNus6102; + device.cart.cic_seed = 0x3F; + device.cart.rdram_size_offset = 0x318; + } + 0xdbdb0cb696006257 => { + device.cart.cic_type = CicType::CicNus6103; + device.cart.cic_seed = 0x78; + device.cart.rdram_size_offset = 0x318; + } + 0x3d51ac3a48960357 => { + device.cart.cic_type = CicType::CicNus6105; + device.cart.cic_seed = 0x91; + device.cart.rdram_size_offset = 0x3F0; + } + 0x90135a02ea97ba9f => { + device.cart.cic_type = CicType::CicNus6106; + device.cart.cic_seed = 0x85; + device.cart.rdram_size_offset = 0x318; + } + _ => { + device.cart.cic_type = CicType::CicNus6102; + device.cart.cic_seed = 0x3F; + device.cart.rdram_size_offset = 0x318; + println!("unknown IPL3 {:#01x}", hash) + } + } +} + +fn calculate_hash(t: &[T]) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} diff --git a/src/device/controller.rs b/src/device/controller.rs new file mode 100644 index 00000000..b9f3da78 --- /dev/null +++ b/src/device/controller.rs @@ -0,0 +1,93 @@ +use crate::device; +use crate::ui; + +pub const JCMD_STATUS: u8 = 0x00; +pub const JCMD_CONTROLLER_READ: u8 = 0x01; +pub const JCMD_PAK_READ: u8 = 0x02; +pub const JCMD_PAK_WRITE: u8 = 0x03; +//pub const JCMD_VRU_READ: u8 = 0x09; +//pub const JCMD_VRU_WRITE: u8 = 0x0A; +//pub const JCMD_VRU_READ_STATUS: u8 = 0x0B; +//pub const JCMD_VRU_WRITE_CONFIG: u8 = 0x0C; +//pub const JCMD_VRU_WRITE_INIT: u8 = 0x0D; +pub const JCMD_RESET: u8 = 0xff; + +//pub const JDT_NONE: u16 = 0x0000; +pub const JDT_JOY_ABS_COUNTERS: u16 = 0x0001; /* joystick with absolute coordinates */ +//pub const JDT_JOY_REL_COUNTERS: u16 = 0x0002; /* joystick with relative coordinates (= mouse) */ +pub const JDT_JOY_PORT: u16 = 0x0004; /* has port for external paks */ +//pub const JDT_VRU: u16 = 0x0100; /* VRU */ +pub const PAK_CHUNK_SIZE: usize = 0x20; +pub const CONT_STATUS: u8 = 0; +pub const CONT_FLAVOR: u16 = JDT_JOY_ABS_COUNTERS | JDT_JOY_PORT; + +pub fn process(device: &mut device::Device, channel: usize) { + let cmd = device.pif.ram[device.pif.channels[channel].tx_buf.unwrap()]; + + match cmd { + JCMD_RESET => { /* reset */ } + JCMD_STATUS => { + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = CONT_FLAVOR as u8; + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 1] = + (CONT_FLAVOR >> 8) as u8; + device.pif.ram[device.pif.channels[channel].rx_buf.unwrap() + 2] = CONT_STATUS; + } + JCMD_CONTROLLER_READ => { + let offset = device.pif.channels[channel].rx_buf.unwrap(); + let input = ui::input::get(&mut device.ui, channel); + device.pif.ram[offset..offset + 4].copy_from_slice(&input.to_ne_bytes()); + } + JCMD_PAK_READ => pak_read_block( + device, + device.pif.channels[channel].tx_buf.unwrap() + 1, + device.pif.channels[channel].rx_buf.unwrap(), + device.pif.channels[channel].rx_buf.unwrap() + 32, + ), + JCMD_PAK_WRITE => pak_write_block( + device, + device.pif.channels[channel].tx_buf.unwrap() + 1, + device.pif.channels[channel].tx_buf.unwrap() + 3, + device.pif.channels[channel].rx_buf.unwrap(), + ), + _ => panic!("unknown controller command"), + } +} + +pub fn pak_read_block(device: &mut device::Device, _addr_acrc: usize, data: usize, dcrc: usize) { + //let address = (device.pif.ram[addr_acrc] << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0); + + //TODO: paks + device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) +} + +pub fn pak_write_block(device: &mut device::Device, _addr_acrc: usize, data: usize, dcrc: usize) { + //let address = (device.pif.ram[addr_acrc] << 8) | (device.pif.ram[addr_acrc + 1] & 0xe0); + + //TODO: paks + device.pif.ram[dcrc] = !pak_data_crc(device, data, PAK_CHUNK_SIZE) +} + +pub fn pak_data_crc(device: &mut device::Device, data_offset: usize, size: usize) -> u8 { + let mut i = 0; + let mut crc = 0; + + while i <= size { + let mut mask = 0x80; + while mask >= 1 { + let xor_tap; + if crc & 0x80 != 0 { + xor_tap = 0x85 + } else { + xor_tap = 0x00 + } + crc <<= 1; + if i != size && (device.pif.ram[data_offset + i] & mask) != 0 { + crc |= 1; + } + crc ^= xor_tap; + mask >>= 1 + } + i += 1; + } + return crc; +} diff --git a/src/device/cop0.rs b/src/device/cop0.rs new file mode 100644 index 00000000..7d85cf00 --- /dev/null +++ b/src/device/cop0.rs @@ -0,0 +1,377 @@ +use crate::device; + +pub const COP0_INDEX_REG: u32 = 0; +pub const COP0_RANDOM_REG: u32 = 1; +pub const COP0_ENTRYLO0_REG: u32 = 2; +pub const COP0_ENTRYLO1_REG: u32 = 3; +pub const COP0_CONTEXT_REG: u32 = 4; +pub const COP0_PAGEMASK_REG: u32 = 5; +pub const COP0_WIRED_REG: u32 = 6; +//pub const COP0_UNUSED_7: u32 = 7; +pub const COP0_BADVADDR_REG: u32 = 8; +pub const COP0_COUNT_REG: u32 = 9; +pub const COP0_ENTRYHI_REG: u32 = 10; +pub const COP0_COMPARE_REG: u32 = 11; +pub const COP0_STATUS_REG: u32 = 12; +pub const COP0_CAUSE_REG: u32 = 13; +pub const COP0_EPC_REG: u32 = 14; +pub const COP0_PREVID_REG: u32 = 15; +pub const COP0_CONFIG_REG: u32 = 16; +pub const COP0_LLADDR_REG: u32 = 17; +//pub const COP0_WATCHLO_REG: u32 = 18; +//pub const COP0_WATCHHI_REG: u32 = 19; +pub const COP0_XCONTEXT_REG: u32 = 20; +//pub const COP0_UNUSED_21: u32 = 21; +//pub const COP0_UNUSED_22: u32 = 22; +//pub const COP0_UNUSED_23: u32 = 23; +//pub const COP0_UNUSED_24: u32 = 24; +//pub const COP0_UNUSED_25: u32 = 25; +//pub const COP0_PARITYERR_REG: u32 = 26; +//pub const COP0_CACHEERR_REG: u32 = 27; +pub const COP0_TAGLO_REG: u32 = 28; +//pub const COP0_TAGHI_REG: u32 = 29; +pub const COP0_ERROREPC_REG: u32 = 30; +//pub const COP0_UNUSED_31: u32 = 31; +pub const COP0_REGS_COUNT: u32 = 32; + +pub const COP0_STATUS_IE: u64 = 1 << 0; +pub const COP0_STATUS_EXL: u64 = 1 << 1; +pub const COP0_STATUS_ERL: u64 = 1 << 2; +pub const COP0_STATUS_BEV: u64 = 1 << 22; +pub const COP0_STATUS_FR: u64 = 1 << 26; +pub const COP0_STATUS_CU1: u64 = 1 << 29; +pub const COP0_STATUS_CU2: u64 = 1 << 30; + +//pub const COP0_CAUSE_EXCCODE_INTR: u64 = 0 << 2; +pub const COP0_CAUSE_EXCCODE_TLBL: u64 = 2 << 2; +pub const COP0_CAUSE_EXCCODE_TLBS: u64 = 3 << 2; +//pub const COP0_CAUSE_EXCCODE_ADEL: u64 = 4 << 2; +//pub const COP0_CAUSE_EXCCODE_ADES: u64 = 5 << 2; +pub const COP0_CAUSE_EXCCODE_SYS: u64 = 8 << 2; +pub const COP0_CAUSE_EXCCODE_BP: u64 = 9 << 2; +pub const COP0_CAUSE_EXCCODE_RI: u64 = 10 << 2; +pub const COP0_CAUSE_EXCCODE_CPU: u64 = 11 << 2; +pub const COP0_CAUSE_EXCCODE_TR: u64 = 13 << 2; +pub const COP0_CAUSE_EXCCODE_FPE: u64 = 15 << 2; +pub const COP0_CAUSE_IP2: u64 = 1 << 10; +pub const COP0_CAUSE_IP7: u64 = 1 << 15; +pub const COP0_CAUSE_BD: u64 = 1 << 31; + +pub const COP0_CAUSE_CE1: u64 = 1 << 28; +pub const COP0_CAUSE_CE2: u64 = 1 << 29; + +pub const COP0_CAUSE_IP_MASK: u64 = 0b00000000000000001111111100000000; +pub const COP0_CAUSE_EXCCODE_MASK: u64 = 0x1F << 2; +pub const COP0_CONTEXT_BADVPN2_MASK: u64 = 0b00000000011111111111111111110000; +pub const COP0_XCONTEXT_BADVPN2_MASK: u64 = 0b01111111111111111111111111110000; +pub const COP0_XCONTEXT_REGION_MASK: u64 = 0b110000000000000000000000000000000; + +const COP0_INDEX_REG_MASK: u64 = 0b10000000000000000000000000111111; +//const COP0_RANDOM_REG_MASK: u64 = 0b00000000000000000000000000111111; +const COP0_ENTRYLO_REG_MASK: u64 = + 0b0000000000000000000000000000000000111111111111111111111111111111; +const COP0_CONTEXT_REG_MASK: u64 = + 0b1111111111111111111111111111111111111111100000000000000000000000; +const COP0_PAGEMASK_REG_MASK: u64 = 0b00000001111111111110000000000000; +const COP0_WIRED_REG_MASK: u64 = 0b00000000000000000000000000111111; +const COP0_ENTRYHI_REG_MASK: u64 = + 0b1100000000000000000000001111111111111111111111111110000011111111; +const COP0_STATUS_REG_MASK: u64 = 0b11111111010101111111111111111111; +const COP0_CAUSE_REG_MASK: u64 = 0b00000000000000000000001100000000; +const COP0_CONFIG_REG_MASK: u64 = 0b00001111000000001000000000001111; +const COP0_LLADDR_REG_MASK: u64 = 0b11111111111111111111111111111111; +const COP0_WATCHLO_REG_MASK: u64 = 0b11111111111111111111111111111011; +const COP0_WATCHHI_REG_MASK: u64 = 0b00000000000000000000000000001111; +const COP0_XCONTEXT_REG_MASK: u64 = + 0b1111111111111111111111111111111000000000000000000000000000000000; +const COP0_PARITYERR_REG_MASK: u64 = 0b00000000000000000000000011111111; +const COP0_TAGLO_REG_MASK: u64 = 0b00001111111111111111111111000000; + +pub struct Cop0 { + pub reg_latch: u64, + pub regs: [u64; COP0_REGS_COUNT as usize], + pub reg_write_masks: [u64; COP0_REGS_COUNT as usize], + pub instrs: [fn(&mut device::Device, u32); 32], + pub instrs2: [fn(&mut device::Device, u32); 32], + pub tlb_lut_r: Vec, + pub tlb_lut_w: Vec, + pub tlb_entries: [device::tlb::TlbEntry; 32], +} + +pub fn mfc0(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = device::cpu_instructions::se32( + (get_control_registers(device, device::cpu_instructions::rd(opcode))) as u32 as i32, + ) +} + +pub fn dmfc0(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = + get_control_registers(device, device::cpu_instructions::rd(opcode)) +} + +pub fn mtc0(device: &mut device::Device, opcode: u32) { + device::cop0::set_control_registers( + device, + device::cpu_instructions::rd(opcode), + device::cpu_instructions::se32( + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32 as i32, + ), + ) +} + +pub fn dmtc0(device: &mut device::Device, opcode: u32) { + device::cop0::set_control_registers( + device, + device::cpu_instructions::rd(opcode), + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize], + ) +} + +pub fn tlbr(device: &mut device::Device, _opcode: u32) { + device::tlb::read(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize]) +} + +pub fn tlbwi(device: &mut device::Device, _opcode: u32) { + device::tlb::write(device, device.cpu.cop0.regs[COP0_INDEX_REG as usize]) +} + +pub fn tlbwr(device: &mut device::Device, _opcode: u32) { + let random = set_random_register(device); + device::tlb::write(device, random) +} + +pub fn tlbp(device: &mut device::Device, _opcode: u32) { + device::tlb::probe(device) +} + +pub fn eret(device: &mut device::Device, _opcode: u32) { + if device.cpu.cop0.regs[COP0_STATUS_REG as usize] & COP0_STATUS_ERL != 0 { + device.cpu.pc = device.cpu.cop0.regs[COP0_ERROREPC_REG as usize]; + device.cpu.cop0.regs[COP0_STATUS_REG as usize] &= !COP0_STATUS_ERL + } else { + device.cpu.pc = device.cpu.cop0.regs[COP0_EPC_REG as usize]; + device.cpu.cop0.regs[COP0_STATUS_REG as usize] &= !COP0_STATUS_EXL + } + device.cpu.branch_state.state = device::cpu::State::Exception; + device.cpu.llbit = false; + device::exceptions::check_pending_interrupts(device) +} + +pub fn execute_cp0(device: &mut device::Device, opcode: u32) { + device.cpu.cop0.instrs2[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn syscall(device: &mut device::Device, _opcode: u32) { + device::exceptions::syscall_exception(device) +} + +pub fn break_(device: &mut device::Device, _opcode: u32) { + device::exceptions::break_exception(device) +} + +pub fn reserved(device: &mut device::Device, _opcode: u32) { + device::exceptions::reserved_exception(device, 0); +} + +pub fn get_control_registers(device: &mut device::Device, index: u32) -> u64 { + match index { + COP0_COUNT_REG => return device.cpu.cop0.regs[index as usize] >> 1, + COP0_RANDOM_REG => return set_random_register(device), + 7 | 21 | 22 | 23 | 24 | 25 | 31 => return device.cpu.cop0.reg_latch, + _ => return device.cpu.cop0.regs[index as usize], + } +} + +pub fn set_control_registers(device: &mut device::Device, index: u32, mut data: u64) { + device.cpu.cop0.reg_latch = data; + match index { + COP0_COUNT_REG => { + data <<= 1; + device::events::translate_events( + device, + device.cpu.cop0.regs[COP0_COUNT_REG as usize], + data, + ) + } + COP0_WIRED_REG => device.cpu.cop0.regs[COP0_RANDOM_REG as usize] = 31, + COP0_COMPARE_REG => { + let current_count = (device.cpu.cop0.regs[COP0_COUNT_REG as usize] >> 1) & 0xFFFFFFFF; + let mut event_time = + (device.cpu.cop0.regs[COP0_COUNT_REG as usize] & 0xFFFFFFFF00000000) | (data << 1); + if current_count >= data { + event_time += !0 as u32 as u64 + } + let compare_event: &mut device::events::Event = + device::events::get_event(device, device::events::EventType::COMPARE).unwrap(); + compare_event.count = event_time; // reschedule the next compare interrupt event + device::events::set_next_event(device); + device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_IP7; + } + COP0_STATUS_REG => { + if data & COP0_STATUS_FR != device.cpu.cop0.regs[index as usize] & COP0_STATUS_FR { + device::cop1::set_fgr_registers(device, data) + } + if data & COP0_STATUS_CU1 != device.cpu.cop0.regs[index as usize] & COP0_STATUS_CU1 { + device::cop1::set_usable(device, data & COP0_STATUS_CU1 != 0) + } + if data & COP0_STATUS_CU2 != device.cpu.cop0.regs[index as usize] & COP0_STATUS_CU2 { + device::cop2::set_usable(device, data & COP0_STATUS_CU2 != 0) + } + } + _ => {} + } + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[index as usize], + data, + device.cpu.cop0.reg_write_masks[index as usize], + ); + device::exceptions::check_pending_interrupts(device); +} + +pub fn compare_event(device: &mut device::Device) { + device.cpu.cop0.regs[COP0_CAUSE_REG as usize] &= !COP0_CAUSE_EXCCODE_MASK; + device.cpu.cop0.regs[COP0_CAUSE_REG as usize] |= COP0_CAUSE_IP7; + + device::events::create_event( + device, + device::events::EventType::COMPARE, + device.cpu.next_event_count + (!0 as u32 as u64), + compare_event, + ); + device::exceptions::check_pending_interrupts(device); +} + +pub fn set_random_register(device: &mut device::Device) -> u64 { + if device.cpu.cop0.regs[COP0_WIRED_REG as usize] > 31 { + return (u64::MAX - device.cpu.cop0.regs[COP0_COUNT_REG as usize]) & 0x3F; + } else { + return (u64::MAX - device.cpu.cop0.regs[COP0_COUNT_REG as usize]) + % (32 - device.cpu.cop0.regs[COP0_WIRED_REG as usize]) + + device.cpu.cop0.regs[COP0_WIRED_REG as usize]; + } +} + +pub fn add_cycles(device: &mut device::Device, cycles: u64) { + device.cpu.cop0.regs[COP0_COUNT_REG as usize] += cycles // COUNT_REG is shifted right 1 bit when read by MFC0 +} + +pub fn init(device: &mut device::Device) { + device.cpu.cop0.reg_write_masks = [ + COP0_INDEX_REG_MASK, + 0, // Random, read only + COP0_ENTRYLO_REG_MASK, + COP0_ENTRYLO_REG_MASK, + COP0_CONTEXT_REG_MASK, + COP0_PAGEMASK_REG_MASK, + COP0_WIRED_REG_MASK, + 0, // 7 + 0, // BadVAddr, read only + !0 as u32 as u64, // count + COP0_ENTRYHI_REG_MASK, + !0 as u32 as u64, // compare + COP0_STATUS_REG_MASK, + COP0_CAUSE_REG_MASK, + !0, // EPC + 0, // previd, read only + COP0_CONFIG_REG_MASK, + COP0_LLADDR_REG_MASK, + COP0_WATCHLO_REG_MASK, + COP0_WATCHHI_REG_MASK, + COP0_XCONTEXT_REG_MASK, + 0, // 21 + 0, // 22 + 0, // 23 + 0, // 24 + 0, // 25 + COP0_PARITYERR_REG_MASK, + 0, // cache error + COP0_TAGLO_REG_MASK, + 0, // taghi + !0, // ErrorPC + 0, // 31 + ]; + + device.cpu.cop0.instrs = [ + device::cop0::mfc0, // 0 + device::cop0::dmfc0, // 1 + device::cop0::reserved, // 2 + device::cop0::reserved, // 3 + device::cop0::mtc0, // 4 + device::cop0::dmtc0, // 5 + device::cop0::reserved, // 6 + device::cop0::reserved, // 7 + device::cop0::reserved, // 8 + device::cop0::reserved, // 9 + device::cop0::reserved, // 10 + device::cop0::reserved, // 11 + device::cop0::reserved, // 12 + device::cop0::reserved, // 13 + device::cop0::reserved, // 14 + device::cop0::reserved, // 15 + device::cop0::execute_cp0, // 16 + device::cop0::reserved, // 17 + device::cop0::reserved, // 18 + device::cop0::reserved, // 19 + device::cop0::reserved, // 20 + device::cop0::reserved, // 21 + device::cop0::reserved, // 22 + device::cop0::reserved, // 23 + device::cop0::reserved, // 24 + device::cop0::reserved, // 25 + device::cop0::reserved, // 26 + device::cop0::reserved, // 27 + device::cop0::reserved, // 28 + device::cop0::reserved, // 29 + device::cop0::reserved, // 30 + device::cop0::reserved, // 31 + ]; + + device.cpu.cop0.instrs2 = [ + device::cop0::reserved, // 0 + device::cop0::tlbr, // 1 + device::cop0::tlbwi, // 2 + device::cop0::reserved, // 3 + device::cop0::reserved, // 4 + device::cop0::reserved, // 5 + device::cop0::tlbwr, // 6 + device::cop0::reserved, // 7 + device::cop0::tlbp, // 8 + device::cop0::reserved, // 9 + device::cop0::reserved, // 10 + device::cop0::reserved, // 11 + device::cop0::reserved, // 12 + device::cop0::reserved, // 13 + device::cop0::reserved, // 14 + device::cop0::reserved, // 15 + device::cop0::reserved, // 16 + device::cop0::reserved, // 17 + device::cop0::reserved, // 18 + device::cop0::reserved, // 19 + device::cop0::reserved, // 20 + device::cop0::reserved, // 21 + device::cop0::reserved, // 22 + device::cop0::reserved, // 23 + device::cop0::eret, // 24 + device::cop0::reserved, // 25 + device::cop0::reserved, // 26 + device::cop0::reserved, // 27 + device::cop0::reserved, // 28 + device::cop0::reserved, // 29 + device::cop0::reserved, // 30 + device::cop0::reserved, // 31 + ]; + // taken from VR4300 manual + device.cpu.cop0.regs[COP0_RANDOM_REG as usize] = 0b00000000000000000000000000011111; + device.cpu.cop0.regs[COP0_CONFIG_REG as usize] = 0b01110000000001101110010001100000; + device.cpu.cop0.regs[COP0_STATUS_REG as usize] = 0b00000000010000000000000000000100; + device.cpu.cop0.regs[COP0_PREVID_REG as usize] = 0b00000000000000000000101100100010; + device.cpu.cop0.regs[COP0_EPC_REG as usize] = 0b11111111111111111111111111111111; + device.cpu.cop0.regs[COP0_ERROREPC_REG as usize] = 0b11111111111111111111111111111111; + + device::events::create_event( + device, + device::events::EventType::COMPARE, + !0 as u32 as u64, + compare_event, + ) +} diff --git a/src/device/cop1.rs b/src/device/cop1.rs new file mode 100644 index 00000000..3b66e078 --- /dev/null +++ b/src/device/cop1.rs @@ -0,0 +1,719 @@ +use crate::device; + +//pub const FCR31_FLAG_INEXACT_BIT: u32 = 1 << 2; +//pub const FCR31_FLAG_UNDERFLOW_BIT: u32 = 1 << 3; +//pub const FCR31_FLAG_OVERFLOW_BIT: u32 = 1 << 4; +//pub const FCR31_FLAG_DIVBYZERO_BIT: u32 = 1 << 5; +//pub const FCR31_FLAG_INVALID_BIT: u32 = 1 << 6; +//pub const FCR31_ENABLE_INEXACT_BIT: u32 = 1 << 7; +//pub const FCR31_ENABLE_UNDERFLOW_BIT: u32 = 1 << 8; +//pub const FCR31_ENABLE_OVERFLOW_BIT: u32 = 1 << 9; +//pub const FCR31_ENABLE_DIVBYZERO_BIT: u32 = 1 << 10; +//pub const FCR31_ENABLE_INVALID_BIT: u32 = 1 << 11; +//pub const FCR31_CAUSE_INEXACT_BIT: u32 = 1 << 12; +//pub const FCR31_CAUSE_UNDERFLOW_BIT: u32 = 1 << 13; +//pub const FCR31_CAUSE_OVERFLOW_BIT: u32 = 1 << 14; +//pub const FCR31_CAUSE_DIVBYZERO_BIT: u32 = 1 << 15; +//pub const FCR31_CAUSE_INVALID_BIT: u32 = 1 << 16; +pub const FCR31_CAUSE_UNIMP_BIT: u32 = 1 << 17; +pub const FCR31_CMP_BIT: u32 = 1 << 23; + +pub const FCR31_CAUSE_MASK: u32 = 0b00000000000000111111000000000000; +pub const FCR31_ENABLE_MASK: u32 = 0b00000000000000000000111110000000; +pub const FCR31_WRITE_MASK: u32 = 0b00000001100000111111111111111111; + +pub struct Cop1 { + pub fcr0: u32, + pub fcr31: u32, + pub fgr32: [[u8; 4]; 32], + pub fgr64: [[u8; 8]; 32], + pub instrs: [fn(&mut device::Device, u32); 32], + pub b_instrs: [fn(&mut device::Device, u32); 4], + pub s_instrs: [fn(&mut device::Device, u32); 64], + pub d_instrs: [fn(&mut device::Device, u32); 64], + pub w_instrs: [fn(&mut device::Device, u32); 64], + pub l_instrs: [fn(&mut device::Device, u32); 64], +} + +pub fn lwc1(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[device::cpu_instructions::rs(opcode) as usize].wrapping_add( + device::cpu_instructions::se16(device::cpu_instructions::imm(opcode) as i16), + ), + device::memory::AccessType::Read, + ); + if err { + return; + } + let value = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) + .to_ne_bytes(); + set_fpr_single( + device, + device::fpu_instructions::ft(opcode) as usize, + f32::from_ne_bytes(value), + false, + ) +} + +pub fn ldc1(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[device::cpu_instructions::rs(opcode) as usize].wrapping_add( + device::cpu_instructions::se16(device::cpu_instructions::imm(opcode) as i16), + ), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let mut w = [2; 32]; + w[0] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Dword, + cached, + ); + w[1] = device::memory::data_read( + device, + phys_address + 4, + device::memory::AccessSize::Dword, + cached, + ); + let value = ((w[0] as u64) << 32) | (w[1]) as u64; + set_fpr_double( + device, + device::fpu_instructions::ft(opcode) as usize, + f64::from_ne_bytes(value.to_ne_bytes()), + ) +} + +pub fn swc1(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[device::cpu_instructions::rs(opcode) as usize].wrapping_add( + device::cpu_instructions::se16(device::cpu_instructions::imm(opcode) as i16), + ), + device::memory::AccessType::Write, + ); + if err { + return; + } + let value = get_fpr_single(device, device::fpu_instructions::ft(opcode) as usize).to_ne_bytes(); + device::memory::data_write( + device, + phys_address, + u32::from_ne_bytes(value), + 0xFFFFFFFF, + cached, + ); +} + +pub fn sdc1(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[device::cpu_instructions::rs(opcode) as usize].wrapping_add( + device::cpu_instructions::se16(device::cpu_instructions::imm(opcode) as i16), + ), + device::memory::AccessType::Write, + ); + if err { + return; + } + + let value = get_fpr_double(device, device::fpu_instructions::ft(opcode) as usize).to_ne_bytes(); + + device::memory::data_write( + device, + phys_address, + u32::from_ne_bytes(value[4..8].try_into().unwrap()), + 0xFFFFFFFF, + cached, + ); + device::memory::data_write( + device, + phys_address + 4, + u32::from_ne_bytes(value[0..4].try_into().unwrap()), + 0xFFFFFFFF, + cached, + ) +} + +pub fn mfc1(device: &mut device::Device, opcode: u32) { + let value = get_fpr_single(device, device::fpu_instructions::fs(opcode) as usize); + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = + device::cpu_instructions::se32(u32::from_ne_bytes(value.to_ne_bytes()) as i32); +} + +pub fn dmfc1(device: &mut device::Device, opcode: u32) { + let value = get_fpr_double(device, device::fpu_instructions::fs(opcode) as usize); + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = + u64::from_ne_bytes(value.to_ne_bytes()); +} + +pub fn cfc1(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = device::cpu_instructions::se32( + get_control_registers_fpu(device, device::fpu_instructions::fs(opcode)) as i32, + ) +} + +pub fn dcfc1(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !FCR31_CAUSE_MASK; + device.cpu.cop1.fcr31 |= FCR31_CAUSE_UNIMP_BIT; + device::exceptions::floating_point_exception(device) +} + +pub fn mtc1(device: &mut device::Device, opcode: u32) { + let value = f32::from_ne_bytes( + (device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32).to_ne_bytes(), + ); + set_fpr_single( + device, + device::fpu_instructions::fs(opcode) as usize, + value, + false, + ) +} + +pub fn dmtc1(device: &mut device::Device, opcode: u32) { + let value = f64::from_ne_bytes( + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize].to_ne_bytes(), + ); + set_fpr_double(device, device::fpu_instructions::fs(opcode) as usize, value) +} + +pub fn ctc1(device: &mut device::Device, opcode: u32) { + set_control_registers_fpu( + device, + device::fpu_instructions::fs(opcode), + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] as u32, + ) +} + +pub fn dctc1(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !FCR31_CAUSE_MASK; + device.cpu.cop1.fcr31 |= FCR31_CAUSE_UNIMP_BIT; + device::exceptions::floating_point_exception(device) +} + +pub fn execute_cop1_b(device: &mut device::Device, opcode: u32) { + device.cpu.cop1.b_instrs[((opcode >> 16) & 0x3) as usize](device, opcode) +} + +pub fn execute_cop1_s(device: &mut device::Device, opcode: u32) { + device.cpu.cop1.s_instrs[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn execute_cop1_d(device: &mut device::Device, opcode: u32) { + device.cpu.cop1.d_instrs[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn execute_cop1_l(device: &mut device::Device, opcode: u32) { + device.cpu.cop1.l_instrs[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn execute_cop1_w(device: &mut device::Device, opcode: u32) { + device.cpu.cop1.w_instrs[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn unusable(device: &mut device::Device, _opcode: u32) { + device::exceptions::cop_unusable_exception(device, device::cop0::COP0_CAUSE_CE1) +} + +pub fn reserved(_device: &mut device::Device, _opcode: u32) { + panic!("cop1 reserved"); +} + +pub fn get_control_registers_fpu(device: &mut device::Device, index: u32) -> u32 { + match index { + 0 => return device.cpu.cop1.fcr0, + 31 => return device.cpu.cop1.fcr31, + _ => { + panic!("unknown FCR register") + } + } +} + +pub fn set_control_registers_fpu(device: &mut device::Device, index: u32, data: u32) { + match index { + 0 => { // read only + } + + 31 => { + device.cpu.cop1.fcr31 = data & FCR31_WRITE_MASK as u32; + // the Cause bits are ANDed with the Enable bits to check for exceptions + // "Unimplemented Operation" has no Enable bit and always causes an exception + if (device.cpu.cop1.fcr31 & FCR31_CAUSE_MASK) >> 5 + & (device.cpu.cop1.fcr31 & FCR31_ENABLE_MASK) + != 0 + || device.cpu.cop1.fcr31 & FCR31_CAUSE_UNIMP_BIT != 0 + { + device::exceptions::floating_point_exception(device) + } + } + _ => { + panic!("unknown FCR register") + } + } +} + +pub fn set_fpr_single(device: &mut device::Device, index: usize, value: f32, clear_high: bool) { + let bytes = value.to_ne_bytes(); + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_FR + == 0 + { + device.cpu.cop1.fgr32[index] = bytes; + } else { + let bytes_lo = bytes; + let bytes_hi: [u8; 4]; + if clear_high { + bytes_hi = [0, 0, 0, 0]; + } else { + bytes_hi = device.cpu.cop1.fgr64[index][4..8].try_into().unwrap() + } + device.cpu.cop1.fgr64[index] = [bytes_lo, bytes_hi].concat().try_into().unwrap(); + } +} + +pub fn get_fpr_single(device: &mut device::Device, index: usize) -> f32 { + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_FR + == 0 + { + return f32::from_ne_bytes(device.cpu.cop1.fgr32[index]); + } else { + return f32::from_ne_bytes(device.cpu.cop1.fgr64[index][0..4].try_into().unwrap()); + } +} + +pub fn set_fpr_double(device: &mut device::Device, index: usize, value: f64) { + let bytes = value.to_ne_bytes(); + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_FR + == 0 + { + device.cpu.cop1.fgr32[index & !1] = bytes[0..4].try_into().unwrap(); + device.cpu.cop1.fgr32[(index & !1) + 1] = bytes[4..8].try_into().unwrap(); + } else { + device.cpu.cop1.fgr64[index] = bytes; + } +} + +pub fn get_fpr_double(device: &mut device::Device, index: usize) -> f64 { + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_FR + == 0 + { + let bytes_lo = device.cpu.cop1.fgr32[index & !1]; + let bytes_hi = device.cpu.cop1.fgr32[(index & !1) + 1]; + return f64::from_ne_bytes([bytes_lo, bytes_hi].concat().try_into().unwrap()); + } else { + return f64::from_ne_bytes(device.cpu.cop1.fgr64[index]); + } +} + +pub fn init(device: &mut device::Device) { + set_fgr_registers(device, 0); + set_usable(device, false); + device.cpu.cop1.fcr0 = 0b101000000000; + + device.cpu.cop1.b_instrs = [ + device::fpu_instructions::bc1f, // 0 + device::fpu_instructions::bc1t, // 1 + device::fpu_instructions::bc1fl, // 2 + device::fpu_instructions::bc1tl, // 3 + ]; + + device.cpu.cop1.s_instrs = [ + device::fpu_instructions::add_s, // 0 + device::fpu_instructions::sub_s, // 1 + device::fpu_instructions::mul_s, // 2 + device::fpu_instructions::div_s, // 3 + device::fpu_instructions::sqrt_s, // 4 + device::fpu_instructions::abs_s, // 5 + device::fpu_instructions::mov_s, // 6 + device::fpu_instructions::neg_s, // 7 + device::fpu_instructions::round_l_s, // 8 + device::fpu_instructions::trunc_l_s, // 9 + device::fpu_instructions::ceil_l_s, // 10 + device::fpu_instructions::floor_l_s, // 11 + device::fpu_instructions::round_w_s, // 12 + device::fpu_instructions::trunc_w_s, // 13 + device::fpu_instructions::ceil_w_s, // 14 + device::fpu_instructions::floor_w_s, // 15 + device::cop1::reserved, // 16 + device::cop1::reserved, // 17 + device::cop1::reserved, // 18 + device::cop1::reserved, // 19 + device::cop1::reserved, // 20 + device::cop1::reserved, // 21 + device::cop1::reserved, // 22 + device::cop1::reserved, // 23 + device::cop1::reserved, // 24 + device::cop1::reserved, // 25 + device::cop1::reserved, // 26 + device::cop1::reserved, // 27 + device::cop1::reserved, // 28 + device::cop1::reserved, // 29 + device::cop1::reserved, // 30 + device::cop1::reserved, // 31 + device::cop1::reserved, // 32 + device::fpu_instructions::cvt_d_s, // 33 + device::cop1::reserved, // 34 + device::cop1::reserved, // 35 + device::fpu_instructions::cvt_w_s, // 36 + device::fpu_instructions::cvt_l_s, // 37 + device::cop1::reserved, // 38 + device::cop1::reserved, // 38 + device::cop1::reserved, // 40 + device::cop1::reserved, // 41 + device::cop1::reserved, // 42 + device::cop1::reserved, // 43 + device::cop1::reserved, // 44 + device::cop1::reserved, // 45 + device::cop1::reserved, // 46 + device::cop1::reserved, // 47 + device::fpu_instructions::c_f_s, // 48 + device::fpu_instructions::c_un_s, // 49 + device::fpu_instructions::c_eq_s, // 50 + device::fpu_instructions::c_ueq_s, // 51 + device::fpu_instructions::c_olt_s, // 52 + device::fpu_instructions::c_ult_s, // 53 + device::fpu_instructions::c_ole_s, // 54 + device::fpu_instructions::c_ule_s, // 55 + device::fpu_instructions::c_sf_s, // 56 + device::fpu_instructions::c_ngle_s, // 57 + device::fpu_instructions::c_seq_s, // 58 + device::fpu_instructions::c_ngl_s, // 59 + device::fpu_instructions::c_lt_s, // 60 + device::fpu_instructions::c_nge_s, // 61 + device::fpu_instructions::c_le_s, // 62 + device::fpu_instructions::c_ngt_s, // 63 + ]; + + device.cpu.cop1.d_instrs = [ + device::fpu_instructions::add_d, // 0 + device::fpu_instructions::sub_d, // 1 + device::fpu_instructions::mul_d, // 2 + device::fpu_instructions::div_d, // 3 + device::fpu_instructions::sqrt_d, // 4 + device::fpu_instructions::abs_d, // 5 + device::fpu_instructions::mov_d, // 6 + device::fpu_instructions::neg_d, // 7 + device::fpu_instructions::round_l_d, // 8 + device::fpu_instructions::trunc_l_d, // 9 + device::fpu_instructions::ceil_l_d, // 10 + device::fpu_instructions::floor_l_d, // 11 + device::fpu_instructions::round_w_d, // 12 + device::fpu_instructions::trunc_w_d, // 13 + device::fpu_instructions::ceil_w_d, // 14 + device::fpu_instructions::floor_w_d, // 15 + device::cop1::reserved, // 16 + device::cop1::reserved, // 17 + device::cop1::reserved, // 18 + device::cop1::reserved, // 19 + device::cop1::reserved, // 20 + device::cop1::reserved, // 21 + device::cop1::reserved, // 22 + device::cop1::reserved, // 23 + device::cop1::reserved, // 24 + device::cop1::reserved, // 25 + device::cop1::reserved, // 26 + device::cop1::reserved, // 27 + device::cop1::reserved, // 28 + device::cop1::reserved, // 29 + device::cop1::reserved, // 30 + device::cop1::reserved, // 31 + device::fpu_instructions::cvt_s_d, // 32 + device::cop1::reserved, // 33 + device::cop1::reserved, // 34 + device::cop1::reserved, // 35 + device::fpu_instructions::cvt_w_d, // 36 + device::fpu_instructions::cvt_l_d, // 37 + device::cop1::reserved, // 38 + device::cop1::reserved, // 38 + device::cop1::reserved, // 40 + device::cop1::reserved, // 41 + device::cop1::reserved, // 42 + device::cop1::reserved, // 43 + device::cop1::reserved, // 44 + device::cop1::reserved, // 45 + device::cop1::reserved, // 46 + device::cop1::reserved, // 47 + device::fpu_instructions::c_f_d, // 48 + device::fpu_instructions::c_un_d, // 49 + device::fpu_instructions::c_eq_d, // 50 + device::fpu_instructions::c_ueq_d, // 51 + device::fpu_instructions::c_olt_d, // 52 + device::fpu_instructions::c_ult_d, // 53 + device::fpu_instructions::c_ole_d, // 54 + device::fpu_instructions::c_ule_d, // 55 + device::fpu_instructions::c_sf_d, // 56 + device::fpu_instructions::c_ngle_d, // 57 + device::fpu_instructions::c_seq_d, // 58 + device::fpu_instructions::c_ngl_d, // 59 + device::fpu_instructions::c_lt_d, // 60 + device::fpu_instructions::c_nge_d, // 61 + device::fpu_instructions::c_le_d, // 62 + device::fpu_instructions::c_ngt_d, // 63 + ]; + + device.cpu.cop1.l_instrs = [ + device::cop1::reserved, // 0 + device::cop1::reserved, // 1 + device::cop1::reserved, // 2 + device::cop1::reserved, // 3 + device::cop1::reserved, // 4 + device::cop1::reserved, // 5 + device::cop1::reserved, // 6 + device::cop1::reserved, // 7 + device::cop1::reserved, // 8 + device::cop1::reserved, // 9 + device::cop1::reserved, // 10 + device::cop1::reserved, // 11 + device::cop1::reserved, // 12 + device::cop1::reserved, // 13 + device::cop1::reserved, // 14 + device::cop1::reserved, // 15 + device::cop1::reserved, // 16 + device::cop1::reserved, // 17 + device::cop1::reserved, // 18 + device::cop1::reserved, // 19 + device::cop1::reserved, // 20 + device::cop1::reserved, // 21 + device::cop1::reserved, // 22 + device::cop1::reserved, // 23 + device::cop1::reserved, // 24 + device::cop1::reserved, // 25 + device::cop1::reserved, // 26 + device::cop1::reserved, // 27 + device::cop1::reserved, // 28 + device::cop1::reserved, // 29 + device::cop1::reserved, // 30 + device::cop1::reserved, // 31 + device::fpu_instructions::cvt_s_l, // 32 + device::fpu_instructions::cvt_d_l, // 33 + device::cop1::reserved, // 34 + device::cop1::reserved, // 35 + device::cop1::reserved, // 36 + device::cop1::reserved, // 37 + device::cop1::reserved, // 38 + device::cop1::reserved, // 39 + device::cop1::reserved, // 40 + device::cop1::reserved, // 41 + device::cop1::reserved, // 42 + device::cop1::reserved, // 43 + device::cop1::reserved, // 44 + device::cop1::reserved, // 45 + device::cop1::reserved, // 46 + device::cop1::reserved, // 47 + device::cop1::reserved, // 48 + device::cop1::reserved, // 49 + device::cop1::reserved, // 50 + device::cop1::reserved, // 51 + device::cop1::reserved, // 52 + device::cop1::reserved, // 53 + device::cop1::reserved, // 54 + device::cop1::reserved, // 55 + device::cop1::reserved, // 56 + device::cop1::reserved, // 57 + device::cop1::reserved, // 58 + device::cop1::reserved, // 59 + device::cop1::reserved, // 60 + device::cop1::reserved, // 61 + device::cop1::reserved, // 62 + device::cop1::reserved, // 63 + ]; + + device.cpu.cop1.w_instrs = [ + device::cop1::reserved, // 0 + device::cop1::reserved, // 1 + device::cop1::reserved, // 2 + device::cop1::reserved, // 3 + device::cop1::reserved, // 4 + device::cop1::reserved, // 5 + device::cop1::reserved, // 6 + device::cop1::reserved, // 7 + device::cop1::reserved, // 8 + device::cop1::reserved, // 9 + device::cop1::reserved, // 10 + device::cop1::reserved, // 11 + device::cop1::reserved, // 12 + device::cop1::reserved, // 13 + device::cop1::reserved, // 14 + device::cop1::reserved, // 15 + device::cop1::reserved, // 16 + device::cop1::reserved, // 17 + device::cop1::reserved, // 18 + device::cop1::reserved, // 19 + device::cop1::reserved, // 20 + device::cop1::reserved, // 21 + device::cop1::reserved, // 22 + device::cop1::reserved, // 23 + device::cop1::reserved, // 24 + device::cop1::reserved, // 25 + device::cop1::reserved, // 26 + device::cop1::reserved, // 27 + device::cop1::reserved, // 28 + device::cop1::reserved, // 29 + device::cop1::reserved, // 30 + device::cop1::reserved, // 31 + device::fpu_instructions::cvt_s_w, // 32 + device::fpu_instructions::cvt_d_w, // 33 + device::cop1::reserved, // 34 + device::cop1::reserved, // 35 + device::cop1::reserved, // 36 + device::cop1::reserved, // 37 + device::cop1::reserved, // 38 + device::cop1::reserved, // 39 + device::cop1::reserved, // 40 + device::cop1::reserved, // 41 + device::cop1::reserved, // 42 + device::cop1::reserved, // 43 + device::cop1::reserved, // 44 + device::cop1::reserved, // 45 + device::cop1::reserved, // 46 + device::cop1::reserved, // 47 + device::cop1::reserved, // 48 + device::cop1::reserved, // 49 + device::cop1::reserved, // 50 + device::cop1::reserved, // 51 + device::cop1::reserved, // 52 + device::cop1::reserved, // 53 + device::cop1::reserved, // 54 + device::cop1::reserved, // 55 + device::cop1::reserved, // 56 + device::cop1::reserved, // 57 + device::cop1::reserved, // 58 + device::cop1::reserved, // 59 + device::cop1::reserved, // 60 + device::cop1::reserved, // 61 + device::cop1::reserved, // 62 + device::cop1::reserved, // 63 + ]; +} + +pub fn set_fgr_registers(device: &mut device::Device, status_reg: u64) { + // this method doesn't account for undocumented behaviour (accessing odd numbered registers in half mode) + if (status_reg & device::cop0::COP0_STATUS_FR) == 0 { + let mut i = 0; + while i < 32 { + let bytes = device.cpu.cop1.fgr64[i]; + device.cpu.cop1.fgr32[i] = bytes[0..4].try_into().unwrap(); + device.cpu.cop1.fgr32[i + 1] = bytes[4..8].try_into().unwrap(); + i += 2; + } + } else { + let mut i = 0; + while i < 32 { + let bytes_lo = device.cpu.cop1.fgr32[i]; + let bytes_hi = device.cpu.cop1.fgr32[i + 1]; + device.cpu.cop1.fgr64[i] = [bytes_lo, bytes_hi].concat().try_into().unwrap(); + i += 2; + } + } +} + +pub fn set_usable(device: &mut device::Device, usable: bool) { + if !usable { + device.cpu.instrs[49] = device::cop1::unusable; // lwc1 + device.cpu.instrs[53] = device::cop1::unusable; // ldc1 + device.cpu.instrs[57] = device::cop1::unusable; // swc1 + device.cpu.instrs[61] = device::cop1::unusable; // sdc1 + device.cpu.cop1.instrs = [ + device::cop1::unusable, // 0 + device::cop1::unusable, // 1 + device::cop1::unusable, // 2 + device::cop1::unusable, // 3 + device::cop1::unusable, // 4 + device::cop1::unusable, // 5 + device::cop1::unusable, // 6 + device::cop1::unusable, // 7 + device::cop1::unusable, // 8 + device::cop1::unusable, // 9 + device::cop1::unusable, // 10 + device::cop1::unusable, // 11 + device::cop1::unusable, // 12 + device::cop1::unusable, // 13 + device::cop1::unusable, // 14 + device::cop1::unusable, // 15 + device::cop1::unusable, // 16 + device::cop1::unusable, // 17 + device::cop1::unusable, // 18 + device::cop1::unusable, // 19 + device::cop1::unusable, // 20 + device::cop1::unusable, // 21 + device::cop1::unusable, // 22 + device::cop1::unusable, // 23 + device::cop1::unusable, // 24 + device::cop1::unusable, // 25 + device::cop1::unusable, // 26 + device::cop1::unusable, // 27 + device::cop1::unusable, // 28 + device::cop1::unusable, // 29 + device::cop1::unusable, // 30 + device::cop1::unusable, // 31 + ] + } else { + device.cpu.instrs[49] = device::cop1::lwc1; + device.cpu.instrs[53] = device::cop1::ldc1; + device.cpu.instrs[57] = device::cop1::swc1; + device.cpu.instrs[61] = device::cop1::sdc1; + device.cpu.cop1.instrs = [ + device::cop1::mfc1, // 0 + device::cop1::dmfc1, // 1 + device::cop1::cfc1, // 2 + device::cop1::dcfc1, // 3 + device::cop1::mtc1, // 4 + device::cop1::dmtc1, // 5 + device::cop1::ctc1, // 6 + device::cop1::dctc1, // 7 + device::cop1::execute_cop1_b, // 8 + device::cop1::reserved, // 9 + device::cop1::reserved, // 10 + device::cop1::reserved, // 11 + device::cop1::reserved, // 12 + device::cop1::reserved, // 13 + device::cop1::reserved, // 14 + device::cop1::reserved, // 15 + device::cop1::execute_cop1_s, // 16 + device::cop1::execute_cop1_d, // 17 + device::cop1::reserved, // 18 + device::cop1::reserved, // 19 + device::cop1::execute_cop1_w, // 20 + device::cop1::execute_cop1_l, // 21 + device::cop1::reserved, // 22 + device::cop1::reserved, // 23 + device::cop1::reserved, // 24 + device::cop1::reserved, // 25 + device::cop1::reserved, // 26 + device::cop1::reserved, // 27 + device::cop1::reserved, // 28 + device::cop1::reserved, // 29 + device::cop1::reserved, // 30 + device::cop1::reserved, // 31 + ] + } + // we have to recalculate the instruction pointers in the icache + for i in 0..512 { + device.memory.icache[i].instruction[0] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[0]); + device.memory.icache[i].instruction[1] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[1]); + device.memory.icache[i].instruction[2] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[2]); + device.memory.icache[i].instruction[3] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[3]); + device.memory.icache[i].instruction[4] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[4]); + device.memory.icache[i].instruction[5] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[5]); + device.memory.icache[i].instruction[6] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[6]); + device.memory.icache[i].instruction[7] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[7]) + } +} diff --git a/src/device/cop2.rs b/src/device/cop2.rs new file mode 100644 index 00000000..cc9bd1d2 --- /dev/null +++ b/src/device/cop2.rs @@ -0,0 +1,137 @@ +use crate::device; + +pub struct Cop2 { + pub instrs: [fn(&mut device::Device, u32); 32], + pub reg_latch: u64, +} + +pub fn mfc2(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = + device::cpu_instructions::se32((device.cpu.cop2.reg_latch) as u32 as i32) +} + +pub fn dmfc2(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = device.cpu.cop2.reg_latch +} + +pub fn cfc2(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] = + device::cpu_instructions::se32((device.cpu.cop2.reg_latch) as u32 as i32) +} + +pub fn mtc2(device: &mut device::Device, opcode: u32) { + device.cpu.cop2.reg_latch = device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] +} + +pub fn dmtc2(device: &mut device::Device, opcode: u32) { + device.cpu.cop2.reg_latch = device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] +} + +pub fn ctc2(device: &mut device::Device, opcode: u32) { + device.cpu.cop2.reg_latch = device.cpu.gpr[device::cpu_instructions::rt(opcode) as usize] +} + +pub fn reserved(device: &mut device::Device, _opcode: u32) { + device::exceptions::reserved_exception(device, device::cop0::COP0_CAUSE_CE2); +} + +pub fn unusable(device: &mut device::Device, _opcode: u32) { + device::exceptions::cop_unusable_exception(device, device::cop0::COP0_CAUSE_CE2) +} + +pub fn init(device: &mut device::Device) { + set_usable(device, false); +} + +pub fn set_usable(device: &mut device::Device, usable: bool) { + if !usable { + device.cpu.cop2.instrs = [ + device::cop2::unusable, // 0 + device::cop2::unusable, // 1 + device::cop2::unusable, // 2 + device::cop2::unusable, // 3 + device::cop2::unusable, // 4 + device::cop2::unusable, // 5 + device::cop2::unusable, // 6 + device::cop2::unusable, // 7 + device::cop2::unusable, // 8 + device::cop2::unusable, // 9 + device::cop2::unusable, // 10 + device::cop2::unusable, // 11 + device::cop2::unusable, // 12 + device::cop2::unusable, // 13 + device::cop2::unusable, // 14 + device::cop2::unusable, // 15 + device::cop2::unusable, // 16 + device::cop2::unusable, // 17 + device::cop2::unusable, // 18 + device::cop2::unusable, // 19 + device::cop2::unusable, // 20 + device::cop2::unusable, // 21 + device::cop2::unusable, // 22 + device::cop2::unusable, // 23 + device::cop2::unusable, // 24 + device::cop2::unusable, // 25 + device::cop2::unusable, // 26 + device::cop2::unusable, // 27 + device::cop2::unusable, // 28 + device::cop2::unusable, // 29 + device::cop2::unusable, // 30 + device::cop2::unusable, // 31 + ] + } else { + device.cpu.cop2.instrs = [ + device::cop2::mfc2, // 0 + device::cop2::dmfc2, // 1 + device::cop2::cfc2, // 2 + device::cop2::reserved, // 3 + device::cop2::mtc2, // 4 + device::cop2::dmtc2, // 5 + device::cop2::ctc2, // 6 + device::cop2::reserved, // 7 + device::cop2::reserved, // 8 + device::cop2::reserved, // 9 + device::cop2::reserved, // 10 + device::cop2::reserved, // 11 + device::cop2::reserved, // 12 + device::cop2::reserved, // 13 + device::cop2::reserved, // 14 + device::cop2::reserved, // 15 + device::cop2::reserved, // 16 + device::cop2::reserved, // 17 + device::cop2::reserved, // 18 + device::cop2::reserved, // 19 + device::cop2::reserved, // 20 + device::cop2::reserved, // 21 + device::cop2::reserved, // 22 + device::cop2::reserved, // 23 + device::cop2::reserved, // 24 + device::cop2::reserved, // 25 + device::cop2::reserved, // 26 + device::cop2::reserved, // 27 + device::cop2::reserved, // 28 + device::cop2::reserved, // 29 + device::cop2::reserved, // 30 + device::cop2::reserved, // 31 + ] + } + // we have to recalculate the instruction pointers in the icache + for i in 0..512 { + device.memory.icache[i].instruction[0] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[0]); + device.memory.icache[i].instruction[1] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[1]); + device.memory.icache[i].instruction[2] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[2]); + device.memory.icache[i].instruction[3] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[3]); + device.memory.icache[i].instruction[4] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[4]); + device.memory.icache[i].instruction[5] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[5]); + device.memory.icache[i].instruction[6] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[6]); + device.memory.icache[i].instruction[7] = + device::cpu::decode_opcode(device, device.memory.icache[i].words[7]) + } +} diff --git a/src/device/cpu.rs b/src/device/cpu.rs new file mode 100644 index 00000000..12fe8aed --- /dev/null +++ b/src/device/cpu.rs @@ -0,0 +1,284 @@ +use crate::device; + +#[derive(PartialEq)] +pub enum State { + Step, + Take, + NotTaken, + DelaySlotTaken, + DelaySlotNotTaken, + Discard, + Exception, +} + +pub struct BranchState { + pub state: State, + pub pc: u64, +} +pub struct Cpu { + pub cop0: device::cop0::Cop0, + pub cop1: device::cop1::Cop1, + pub cop2: device::cop2::Cop2, + pub branch_state: BranchState, + pub gpr: [u64; 32], + pub pc: u64, + pub lo: u64, + pub hi: u64, + pub running: u8, + pub llbit: bool, + pub clock_rate: u64, + pub instrs: [fn(&mut device::Device, u32); 64], + pub special_instrs: [fn(&mut device::Device, u32); 64], + pub regimm_instrs: [fn(&mut device::Device, u32); 32], + pub events: Vec, + pub next_event_count: u64, + pub next_event: Option, +} + +pub fn decode_opcode(device: &mut device::Device, opcode: u32) -> fn(&mut device::Device, u32) { + match opcode >> 26 { + 0 => device.cpu.special_instrs[(opcode & 0x3F) as usize], // SPECIAL + 1 => device.cpu.regimm_instrs[((opcode >> 16) & 0x1F) as usize], // REGIMM + 16 => device.cpu.cop0.instrs[((opcode >> 21) & 0x1F) as usize], // COP0 + 17 => device.cpu.cop1.instrs[((opcode >> 21) & 0x1F) as usize], // COP1 + 18 => device.cpu.cop2.instrs[((opcode >> 21) & 0x1F) as usize], // COP2 + _ => device.cpu.instrs[(opcode >> 26) as usize], + } +} + +pub fn init(device: &mut device::Device) { + device.cpu.clock_rate = 93750000; + + device.cpu.instrs = [ + device::cop0::reserved, // SPECIAL + device::cop0::reserved, // REGIMM + device::cpu_instructions::j, // 2 + device::cpu_instructions::jal, // 3 + device::cpu_instructions::beq, // 4 + device::cpu_instructions::bne, // 5 + device::cpu_instructions::blez, // 6 + device::cpu_instructions::bgtz, // 7 + device::cpu_instructions::addi, // 8 + device::cpu_instructions::addiu, // 9 + device::cpu_instructions::slti, // 10 + device::cpu_instructions::sltiu, // 11 + device::cpu_instructions::andi, // 12 + device::cpu_instructions::ori, // 13 + device::cpu_instructions::xori, // 14 + device::cpu_instructions::lui, // 15 + device::cop0::reserved, // COP0 + device::cop0::reserved, // COP1 + device::cop0::reserved, // COP2 + device::cop0::reserved, // 19 + device::cpu_instructions::beql, // 20 + device::cpu_instructions::bnel, // 21 + device::cpu_instructions::blezl, // 22 + device::cpu_instructions::bgtzl, // 23 + device::cpu_instructions::daddi, // 24 + device::cpu_instructions::daddiu, // 25 + device::cpu_instructions::ldl, // 26 + device::cpu_instructions::ldr, // 27 + device::cop0::reserved, // 28 + device::cop0::reserved, // 29 + device::cop0::reserved, // 30 + device::cop0::reserved, // 31 + device::cpu_instructions::lb, // 32 + device::cpu_instructions::lh, // 33 + device::cpu_instructions::lwl, // 34 + device::cpu_instructions::lw, // 35 + device::cpu_instructions::lbu, // 36 + device::cpu_instructions::lhu, // 37 + device::cpu_instructions::lwr, // 38 + device::cpu_instructions::lwu, // 39 + device::cpu_instructions::sb, // 40 + device::cpu_instructions::sh, // 41 + device::cpu_instructions::swl, // 42 + device::cpu_instructions::sw, // 43 + device::cpu_instructions::sdl, // 44 + device::cpu_instructions::sdr, // 45 + device::cpu_instructions::swr, // 46 + device::cpu_instructions::cache, // 47 + device::cpu_instructions::ll, // 48 + device::cop1::lwc1, // 49 + device::cop0::reserved, // 50 + device::cop0::reserved, // 51 + device::cpu_instructions::lld, // 52 + device::cop1::ldc1, // 53 + device::cop0::reserved, // 54 + device::cpu_instructions::ld, // 55 + device::cpu_instructions::sc, // 56 + device::cop1::swc1, // 57 + device::cop0::reserved, // 58 + device::cop0::reserved, // 59 + device::cpu_instructions::scd, // 60 + device::cop1::sdc1, // 61 + device::cop0::reserved, // 62 + device::cpu_instructions::sd, // 63 + ]; + device.cpu.special_instrs = [ + device::cpu_instructions::sll, // 0 + device::cop0::reserved, // 1 + device::cpu_instructions::srl, // 2 + device::cpu_instructions::sra, // 3 + device::cpu_instructions::sllv, // 4 + device::cop0::reserved, // 5 + device::cpu_instructions::srlv, // 6 + device::cpu_instructions::srav, // 7 + device::cpu_instructions::jr, // 8 + device::cpu_instructions::jalr, // 9 + device::cop0::reserved, // 10 + device::cop0::reserved, // 11 + device::cop0::syscall, // 12 + device::cop0::break_, // 13 + device::cop0::reserved, // 14 + device::cpu_instructions::sync, // 15 + device::cpu_instructions::mfhi, // 16 + device::cpu_instructions::mthi, // 17 + device::cpu_instructions::mflo, // 18 + device::cpu_instructions::mtlo, // 19 + device::cpu_instructions::dsllv, // 20 + device::cop0::reserved, // 21 + device::cpu_instructions::dsrlv, // 22 + device::cpu_instructions::dsrav, // 23 + device::cpu_instructions::mult, // 24 + device::cpu_instructions::multu, // 25 + device::cpu_instructions::div, // 26 + device::cpu_instructions::divu, // 27 + device::cpu_instructions::dmult, // 28 + device::cpu_instructions::dmultu, // 29 + device::cpu_instructions::ddiv, // 30 + device::cpu_instructions::ddivu, // 31 + device::cpu_instructions::add, // 32 + device::cpu_instructions::addu, // 33 + device::cpu_instructions::sub, // 34 + device::cpu_instructions::subu, // 35 + device::cpu_instructions::and, // 36 + device::cpu_instructions::or, // 37 + device::cpu_instructions::xor, // 38 + device::cpu_instructions::nor, // 39 + device::cop0::reserved, // 40 + device::cop0::reserved, // 41 + device::cpu_instructions::slt, // 42 + device::cpu_instructions::sltu, // 43 + device::cpu_instructions::dadd, // 44 + device::cpu_instructions::daddu, // 45 + device::cpu_instructions::dsub, // 46 + device::cpu_instructions::dsubu, // 47 + device::cpu_instructions::tge, // 48 + device::cpu_instructions::tgeu, // 49 + device::cpu_instructions::tlt, // 50 + device::cpu_instructions::tltu, // 51 + device::cpu_instructions::teq, // 52 + device::cop0::reserved, // 53 + device::cpu_instructions::tne, // 54 + device::cop0::reserved, // 55 + device::cpu_instructions::dsll, // 56 + device::cop0::reserved, // 57 + device::cpu_instructions::dsrl, // 58 + device::cpu_instructions::dsra, // 59 + device::cpu_instructions::dsll32, // 60 + device::cop0::reserved, // 61 + device::cpu_instructions::dsrl32, // 62 + device::cpu_instructions::dsra32, // 63 + ]; + device.cpu.regimm_instrs = [ + device::cpu_instructions::bltz, // 0 + device::cpu_instructions::bgez, // 1 + device::cpu_instructions::bltzl, // 2 + device::cpu_instructions::bgezl, // 3 + device::cop0::reserved, // 4 + device::cop0::reserved, // 5 + device::cop0::reserved, // 6 + device::cop0::reserved, // 7 + device::cpu_instructions::tgei, // 8 + device::cpu_instructions::tgeiu, // 9 + device::cpu_instructions::tlti, // 10 + device::cpu_instructions::tltiu, // 11 + device::cpu_instructions::teqi, // 12 + device::cop0::reserved, // 13 + device::cpu_instructions::tnei, // 14 + device::cop0::reserved, // 15 + device::cpu_instructions::bltzal, // 16 + device::cpu_instructions::bgezal, // 17 + device::cpu_instructions::bltzall, // 18 + device::cpu_instructions::bgezall, // 19 + device::cop0::reserved, // 20 + device::cop0::reserved, // 21 + device::cop0::reserved, // 22 + device::cop0::reserved, // 23 + device::cop0::reserved, // 24 + device::cop0::reserved, // 25 + device::cop0::reserved, // 26 + device::cop0::reserved, // 27 + device::cop0::reserved, // 28 + device::cop0::reserved, // 29 + device::cop0::reserved, // 30 + device::cop0::reserved, // 31 + ]; + device::cop0::init(device); + device::cop1::init(device); + device::cop2::init(device); +} + +pub fn in_delay_slot(device: &mut device::Device) -> bool { + return device.cpu.branch_state.state == State::DelaySlotTaken + || device.cpu.branch_state.state == State::DelaySlotNotTaken; +} + +pub fn in_delay_slot_taken(device: &mut device::Device) -> bool { + return device.cpu.branch_state.state == State::DelaySlotTaken; +} + +pub fn run(device: &mut device::Device) { + device.cpu.running = 1; + while device.cpu.running == 1 { + device.cpu.gpr[0] = 0; // gpr 0 is read only + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.pc, + device::memory::AccessType::Read, + ); + if err { + continue; // TLB exception + } + if cached { + device::cache::icache_fetch(device, phys_address) + } else { + let opcode = device.memory.memory_map_read[(phys_address >> 16) as usize]( + device, + phys_address, + device::memory::AccessSize::Word, + ); + device::cpu::decode_opcode(device, opcode)(device, opcode); + } + match device.cpu.branch_state.state { + State::Step => device.cpu.pc += 4, + State::Take => { + device.cpu.pc += 4; + device.cpu.branch_state.state = State::DelaySlotTaken + } + State::NotTaken => { + device.cpu.pc += 4; + device.cpu.branch_state.state = State::DelaySlotNotTaken + } + State::DelaySlotTaken => { + device.cpu.pc = device.cpu.branch_state.pc; + device.cpu.branch_state.state = State::Step + } + State::DelaySlotNotTaken => { + device.cpu.pc += 4; + device.cpu.branch_state.state = State::Step + } + State::Discard => { + device.cpu.pc += 8; + device.cpu.branch_state.state = State::Step + } + State::Exception => device.cpu.branch_state.state = State::Step, + } + device::cop0::add_cycles(device, 1); + if device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] > device.cpu.next_event_count + { + device::events::trigger_event(device) + } + } +} diff --git a/src/device/cpu_instructions.rs b/src/device/cpu_instructions.rs new file mode 100644 index 00000000..79aba19f --- /dev/null +++ b/src/device/cpu_instructions.rs @@ -0,0 +1,1406 @@ +use crate::device; + +pub fn rd(opcode: u32) -> u32 { + return (opcode >> 11) & 0x1F; +} + +pub fn rs(opcode: u32) -> u32 { + return (opcode >> 21) & 0x1F; +} + +pub fn rt(opcode: u32) -> u32 { + return (opcode >> 16) & 0x1F; +} + +pub fn sa(opcode: u32) -> u32 { + return (opcode >> 6) & 0x1F; +} + +pub fn imm(opcode: u32) -> u16 { + return opcode as u16; +} + +pub fn se32(value: i32) -> u64 { + return value as i64 as u64; +} + +pub fn se16(value: i16) -> u64 { + return value as i64 as u64; +} + +pub fn se8(value: i8) -> u64 { + return value as i64 as u64; +} + +pub fn bshift>(address: T) -> u64 { + return ((address.into() & 3) ^ 3) << 3; +} + +pub fn hshift>(address: T) -> u64 { + return ((address.into() & 2) ^ 2) << 3; +} + +pub fn bits_below_mask>(x: T) -> u64 { + return (1 << x.into()) - 1; +} + +pub fn bits_above_mask>(x: T) -> u64 { + return !bits_below_mask(x); +} + +pub fn check_relative_idle_loop(device: &mut device::Device, opcode: u32) { + let (phys_address, _, err) = device::memory::translate_address( + device, + device.cpu.pc, + device::memory::AccessType::Lookup, + ); + if err { + return; + } + if imm(opcode) as i16 == -1 + && device.memory.fast_read[(phys_address >> 16) as usize]( + device, + phys_address + 4, + device::memory::AccessSize::Word, + ) == 0 + { + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] = device.cpu.next_event_count + } +} + +pub fn check_absolute_idle_loop(device: &mut device::Device, opcode: u32) { + let (phys_address, _, err) = device::memory::translate_address( + device, + device.cpu.pc, + device::memory::AccessType::Lookup, + ); + if err { + return; + } + if (opcode & 0x3FFFFFF) as u64 == (phys_address & 0x0FFFFFFF) >> 2 + && device.memory.fast_read[(phys_address >> 16) as usize]( + device, + phys_address + 4, + device::memory::AccessSize::Word, + ) == 0 + { + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] = device.cpu.next_event_count + } +} + +pub fn j(device: &mut device::Device, opcode: u32) { + if device::cpu::in_delay_slot_taken(device) { + return; + } + check_absolute_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = + (device.cpu.pc + 4) & 0xFFFFFFFFF0000000 | ((opcode & 0x3FFFFFF) << 2) as u64 +} + +pub fn jal(device: &mut device::Device, opcode: u32) { + if device::cpu::in_delay_slot_taken(device) { + device.cpu.gpr[31] = device.cpu.branch_state.pc + 4 + } else { + device.cpu.gpr[31] = device.cpu.pc + 8 + } + if !device::cpu::in_delay_slot_taken(device) { + check_absolute_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = + (device.cpu.pc + 4) & 0xFFFFFFFFF0000000 | ((opcode & 0x3FFFFFF) << 2) as u64 + } else if !device::cpu::in_delay_slot(device) { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn beq(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] == device.cpu.gpr[rt(opcode) as usize] { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bne(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] != device.cpu.gpr[rt(opcode) as usize] { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn blez(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] as i64 <= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bgtz(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] as i64 > 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn addi(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + se32(device.cpu.gpr[rs(opcode) as usize] as i32).wrapping_add(se16(imm(opcode) as i16)) +} + +pub fn addiu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + se32(device.cpu.gpr[rs(opcode) as usize] as i32).wrapping_add(se16(imm(opcode) as i16)) +} + +pub fn slti(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + ((device.cpu.gpr[rs(opcode) as usize] as i64) < (imm(opcode) as i16 as i64)) as u64 +} + +pub fn sltiu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + (device.cpu.gpr[rs(opcode) as usize] < se16(imm(opcode) as i16)) as u64 +} + +pub fn andi(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = device.cpu.gpr[rs(opcode) as usize] & imm(opcode) as u64 +} + +pub fn ori(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = device.cpu.gpr[rs(opcode) as usize] | imm(opcode) as u64 +} + +pub fn xori(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = device.cpu.gpr[rs(opcode) as usize] ^ imm(opcode) as u64 +} + +pub fn lui(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = se32(((imm(opcode) as u32) << 16) as i32) +} + +pub fn beql(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] == device.cpu.gpr[rt(opcode) as usize] { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn bnel(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] != device.cpu.gpr[rt(opcode) as usize] { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn blezl(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] as i64 <= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn bgtzl(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] as i64 > 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn daddi(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)) +} + +pub fn daddiu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rt(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)) +} + +pub fn ldl(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 7; + let shift = 8 * n; + let mask = bits_below_mask(8 * n); + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !7, device::memory::AccessType::Read); + if err { + return; + } + + let mut w = [0; 2]; + w[0] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Dword, + cached, + ); + w[1] = device::memory::data_read( + device, + phys_address + 4, + device::memory::AccessSize::Dword, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = (device.cpu.gpr[rt(opcode) as usize] & mask) + | (((((w[0]) as u64) << 32) | (w[1]) as u64) << shift) +} + +pub fn ldr(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 7; + let shift = 8 * (7 - n); + let mask; + if n == 7 { + mask = 0 + } else { + mask = bits_above_mask(8 * (n + 1)) + } + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !7, device::memory::AccessType::Read); + if err { + return; + } + + let mut w = [0; 2]; + w[0] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Dword, + cached, + ); + w[1] = device::memory::data_read( + device, + phys_address + 4, + device::memory::AccessSize::Dword, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = (device.cpu.gpr[rt(opcode) as usize] & mask) + | (((((w[0]) as u64) << 32) | (w[1]) as u64) >> shift) +} + +pub fn lb(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let shift = bshift(phys_address); + device.cpu.gpr[rt(opcode) as usize] = se8((device::memory::data_read( + device, + phys_address & !0x3, + device::memory::AccessSize::Word, + cached, + ) >> shift) as u8 as i8) +} + +pub fn lh(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let shift = hshift(phys_address); + device.cpu.gpr[rt(opcode) as usize] = se16( + (device::memory::data_read( + device, + phys_address & !0x3, + device::memory::AccessSize::Word, + cached, + ) >> shift) as u16 as i16, + ) +} + +pub fn lwl(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 3; + let shift = 8 * n; + let mask = bits_below_mask(8 * n) as u32; + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !3, device::memory::AccessType::Read); + if err { + return; + } + + device.cpu.gpr[rt(opcode) as usize] = se32( + ((device.cpu.gpr[rt(opcode) as usize] as u32) & mask + | device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) << shift) as i32, + ) +} + +pub fn lw(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + device.cpu.gpr[rt(opcode) as usize] = se32(device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) as i32) +} + +pub fn lbu(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let shift = bshift(phys_address); + device.cpu.gpr[rt(opcode) as usize] = (device::memory::data_read( + device, + phys_address & !0x3, + device::memory::AccessSize::Word, + cached, + ) >> shift) as u8 as u64 +} + +pub fn lhu(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let shift = hshift(phys_address); + device.cpu.gpr[rt(opcode) as usize] = (device::memory::data_read( + device, + phys_address & !0x3, + device::memory::AccessSize::Word, + cached, + ) >> shift) as u16 as u64 +} + +pub fn lwr(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 3; + let shift = 8 * (3 - n); + let mask; + if n == 3 { + mask = 0 + } else { + mask = bits_above_mask(8 * (n + 1)) + } + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !3, device::memory::AccessType::Read); + if err { + return; + } + + device.cpu.gpr[rt(opcode) as usize] = se32( + ((device.cpu.gpr[rt(opcode) as usize] as u32) & (mask as u32) + | device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) >> shift) as i32, + ) +} + +pub fn lwu(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + device.cpu.gpr[rt(opcode) as usize] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) as u64 +} + +pub fn sb(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + let shift = bshift(phys_address); + + device::memory::data_write( + device, + phys_address & !0x3, + (device.cpu.gpr[rt(opcode) as usize] as u32) << shift, + 0xFF << shift, + cached, + ) +} + +pub fn sh(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + let shift = hshift(phys_address); + + device::memory::data_write( + device, + phys_address & !0x3, + (device.cpu.gpr[rt(opcode) as usize] as u32) << shift, + 0xFFFF << shift, + cached, + ) +} + +pub fn swl(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 3; + let shift = 8 * n; + + let mask; + if n == 0 { + mask = !0 as u32 + } else { + mask = bits_below_mask(8 * (4 - n)) as u32 + } + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !3, device::memory::AccessType::Write); + if err { + return; + } + device::memory::data_write( + device, + phys_address, + (device.cpu.gpr[rt(opcode) as usize] >> shift) as u32, + mask, + cached, + ) +} + +pub fn sw(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + device::memory::data_write( + device, + phys_address, + device.cpu.gpr[rt(opcode) as usize] as u32, + 0xFFFFFFFF, + cached, + ) +} + +pub fn sdl(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 7; + let shift = 8 * n; + + let mask; + if n == 0 { + mask = !0 as u64 + } else { + mask = bits_below_mask(8 * (8 - n)) + } + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !7, device::memory::AccessType::Write); + if err { + return; + } + + let value = device.cpu.gpr[rt(opcode) as usize] >> shift; + device::memory::data_write( + device, + phys_address, + (value >> 32) as u32, + (mask >> 32) as u32, + cached, + ); + device::memory::data_write(device, phys_address + 4, value as u32, mask as u32, cached) +} + +pub fn sdr(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 7; + let shift = 8 * (7 - n); + + let mask = bits_above_mask(8 * (7 - n)); + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !7, device::memory::AccessType::Write); + if err { + return; + } + + let value = device.cpu.gpr[rt(opcode) as usize] << shift; + device::memory::data_write( + device, + phys_address, + (value >> 32) as u32, + (mask >> 32) as u32, + cached, + ); + device::memory::data_write(device, phys_address + 4, value as u32, mask as u32, cached) +} + +pub fn swr(device: &mut device::Device, opcode: u32) { + let addr = device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)); + let n = addr & 3; + let shift = 8 * (3 - n); + + let mask = bits_above_mask(8 * (3 - n)) as u32; + + let (phys_address, cached, err) = + device::memory::translate_address(device, addr & !3, device::memory::AccessType::Write); + if err { + return; + } + device::memory::data_write( + device, + phys_address, + (device.cpu.gpr[rt(opcode) as usize] << shift) as u32, + mask, + cached, + ) +} + +pub fn cache(device: &mut device::Device, opcode: u32) { + let (phys_address, _, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let dcache_line = ((phys_address >> 4) & 0x1FF) as usize; + let icache_line = ((phys_address >> 5) & 0x1FF) as usize; + match rt(opcode) { + 0x00 => { + //icache index invalidate + device.memory.icache[icache_line].valid = false + } + 0x04 => { + //icache load tag + let mut valid = 0; + if device.memory.icache[icache_line].valid { + valid = 1 + } + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + valid << 7, + 0x80, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + 0, + 0x40, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + (device.memory.icache[icache_line].tag >> 4) as u64, + 0xFFFFF00, + ) + } + 0x08 => { + //icache store tag + device.memory.icache[icache_line].valid = + (device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize] & 0x80) >> 7 != 0; + device.memory.icache[icache_line].tag = + ((device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize] & 0xFFFFF00) << 4) + as u32 + } + 0x10 => { + //icache hit invalidate + if device::cache::icache_hit(device, icache_line, phys_address) { + device.memory.icache[icache_line].valid = false + } + } + 0x14 => { + //icache fill + device::cache::icache_fill(device, icache_line, phys_address) + } + 0x18 => { + //icache hit write back + if device::cache::icache_hit(device, icache_line, phys_address) { + device::cache::icache_writeback(device, icache_line) + } + } + 0x01 => { + //dcache index write back invalidate + if device.memory.dcache[dcache_line].dirty && device.memory.dcache[dcache_line].valid { + device::cache::dcache_writeback(device, dcache_line) + } + device.memory.dcache[dcache_line].valid = false + } + 0x05 => { + //dcache index load tag + let mut valid = 0; + let mut dirty = 0; + if device.memory.dcache[dcache_line].valid { + valid = 1 + } + if device.memory.dcache[dcache_line].dirty { + dirty = 1 + } + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + valid << 7, + 0x80, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + dirty << 6, + 0x40, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize], + (device.memory.dcache[dcache_line].tag >> 4) as u64, + 0xFFFFF00, + ) + } + 0x09 => { + //dcache index store tag + device.memory.dcache[dcache_line].valid = + (device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize] & 0x80) >> 7 != 0; + device.memory.dcache[dcache_line].dirty = + (device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize] & 0x40) >> 6 != 0; + device.memory.dcache[dcache_line].tag = + ((device.cpu.cop0.regs[device::cop0::COP0_TAGLO_REG as usize] & 0xFFFFF00) << 4) + as u32 + } + 0x0D => { + //dcache create dirty exclusive + if !device::cache::dcache_hit(device, dcache_line, phys_address) + && device.memory.dcache[dcache_line].dirty + { + device::cache::dcache_writeback(device, dcache_line) + } + device.memory.dcache[dcache_line].tag = (phys_address & !0xFFF) as u32; + device.memory.dcache[dcache_line].valid = true; + device.memory.dcache[dcache_line].dirty = true + } + 0x11 => { + //dcache hit invalidate + if device::cache::dcache_hit(device, dcache_line, phys_address) { + device.memory.dcache[dcache_line].valid = false; + device.memory.dcache[dcache_line].dirty = false + } + } + 0x15 => { + //dcache hit write back invalidate + if device::cache::dcache_hit(device, dcache_line, phys_address) { + if device.memory.dcache[dcache_line].dirty { + device::cache::dcache_writeback(device, dcache_line) + } + device.memory.dcache[dcache_line].valid = false + } + } + 0x19 => { + //dcache hit write back + if device::cache::dcache_hit(device, dcache_line, phys_address) { + if device.memory.dcache[dcache_line].dirty { + device::cache::dcache_writeback(device, dcache_line) + } + } + } + _ => { + panic!("unknown cache code {:#01x}", rt(opcode)) + } + } +} + +pub fn ll(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + device.cpu.gpr[rt(opcode) as usize] = se32(device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Word, + cached, + ) as i32); + device.cpu.llbit = true; + device.cpu.cop0.regs[device::cop0::COP0_LLADDR_REG as usize] = phys_address >> 4 +} + +pub fn lld(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let mut w = [0; 2]; + w[0] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Dword, + cached, + ); + w[1] = device::memory::data_read( + device, + phys_address + 4, + device::memory::AccessSize::Dword, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = ((w[0] as u64) << 32) | (w[1]) as u64; + + device.cpu.llbit = true; + device.cpu.cop0.regs[device::cop0::COP0_LLADDR_REG as usize] = phys_address >> 4 +} + +pub fn ld(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Read, + ); + if err { + return; + } + + let mut w = [0; 2]; + w[0] = device::memory::data_read( + device, + phys_address, + device::memory::AccessSize::Dword, + cached, + ); + w[1] = device::memory::data_read( + device, + phys_address + 4, + device::memory::AccessSize::Dword, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = ((w[0] as u64) << 32) | (w[1]) as u64 +} + +pub fn sc(device: &mut device::Device, opcode: u32) { + if device.cpu.llbit { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + device.cpu.llbit = false; + device::memory::data_write( + device, + phys_address, + device.cpu.gpr[rt(opcode) as usize] as u32, + 0xFFFFFFFF, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = 1 + } else { + device.cpu.gpr[rt(opcode) as usize] = 0 + } +} + +pub fn scd(device: &mut device::Device, opcode: u32) { + if device.cpu.llbit { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + device.cpu.llbit = false; + device::memory::data_write( + device, + phys_address, + (device.cpu.gpr[rt(opcode) as usize] >> 32) as u32, + 0xFFFFFFFF, + cached, + ); + device::memory::data_write( + device, + phys_address + 4, + device.cpu.gpr[rt(opcode) as usize] as u32, + 0xFFFFFFFF, + cached, + ); + device.cpu.gpr[rt(opcode) as usize] = 1 + } else { + device.cpu.gpr[rt(opcode) as usize] = 0 + } +} + +pub fn sd(device: &mut device::Device, opcode: u32) { + let (phys_address, cached, err) = device::memory::translate_address( + device, + device.cpu.gpr[rs(opcode) as usize].wrapping_add(se16(imm(opcode) as i16)), + device::memory::AccessType::Write, + ); + if err { + return; + } + + device::memory::data_write( + device, + phys_address, + (device.cpu.gpr[rt(opcode) as usize] >> 32) as u32, + 0xFFFFFFFF, + cached, + ); + device::memory::data_write( + device, + phys_address + 4, + device.cpu.gpr[rt(opcode) as usize] as u32, + 0xFFFFFFFF, + cached, + ) +} + +pub fn sll(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + se32(((device.cpu.gpr[rt(opcode) as usize] as u32) << sa(opcode)) as i32) +} + +pub fn srl(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + se32(((device.cpu.gpr[rt(opcode) as usize] as u32) >> sa(opcode)) as i32) +} + +pub fn sra(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + se32(((device.cpu.gpr[rt(opcode) as usize] as i64) >> sa(opcode)) as i32) +} + +pub fn sllv(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + ((device.cpu.gpr[rt(opcode) as usize] as u32) + << (device.cpu.gpr[rs(opcode) as usize] as u32 & 31)) as i32, + ) +} + +pub fn srlv(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + ((device.cpu.gpr[rt(opcode) as usize] as u32) + >> (device.cpu.gpr[rs(opcode) as usize] as u32 & 31)) as i32, + ) +} + +pub fn srav(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + ((device.cpu.gpr[rt(opcode) as usize] as i64) >> (device.cpu.gpr[rs(opcode) as usize] & 31)) + as i32, + ) +} + +pub fn jr(device: &mut device::Device, opcode: u32) { + if !device::cpu::in_delay_slot_taken(device) { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.gpr[rs(opcode) as usize]; + } else if !device::cpu::in_delay_slot(device) { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn jalr(device: &mut device::Device, opcode: u32) { + let in_delay_slot_taken = device::cpu::in_delay_slot_taken(device); + + if !in_delay_slot_taken { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.gpr[rs(opcode) as usize]; + } else if !device::cpu::in_delay_slot(device) { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } + + if in_delay_slot_taken { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.branch_state.pc + 4 + } else { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.pc + 8 + } +} + +pub fn sync(_device: &mut device::Device, _opcode: u32) {} + +pub fn mfhi(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.hi +} + +pub fn mthi(device: &mut device::Device, opcode: u32) { + device.cpu.hi = device.cpu.gpr[rs(opcode) as usize] +} + +pub fn mflo(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.lo +} + +pub fn mtlo(device: &mut device::Device, opcode: u32) { + device.cpu.lo = device.cpu.gpr[rs(opcode) as usize] +} + +pub fn dsllv(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rt(opcode) as usize] << (device.cpu.gpr[rs(opcode) as usize] & 63) +} + +pub fn dsrlv(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rt(opcode) as usize] >> (device.cpu.gpr[rs(opcode) as usize] & 63) +} + +pub fn dsrav(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = ((device.cpu.gpr[rt(opcode) as usize] as i64) + >> (device.cpu.gpr[rs(opcode) as usize] & 63)) + as u64 +} + +pub fn mult(device: &mut device::Device, opcode: u32) { + let result = ((device.cpu.gpr[rs(opcode) as usize]) as i32 as i64) + .wrapping_mul(device.cpu.gpr[rt(opcode) as usize] as i32 as i64); + + device.cpu.lo = se32((result) as i32); + device.cpu.hi = se32((result >> 32) as i32); + + device::cop0::add_cycles(device, 4); +} + +pub fn multu(device: &mut device::Device, opcode: u32) { + let result = + device.cpu.gpr[rs(opcode) as usize].wrapping_mul(device.cpu.gpr[rt(opcode) as usize]); + + device.cpu.lo = se32((result) as u32 as i32); + device.cpu.hi = se32((result >> 32) as u32 as i32); + + device::cop0::add_cycles(device, 4); +} + +pub fn div(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rt(opcode) as usize]) as i32 != 0 { + device.cpu.lo = se32( + (device.cpu.gpr[rs(opcode) as usize] as i32) + .wrapping_div(device.cpu.gpr[rt(opcode) as usize] as i32), + ); + device.cpu.hi = se32( + (device.cpu.gpr[rs(opcode) as usize] as i32) + .wrapping_rem(device.cpu.gpr[rt(opcode) as usize] as i32), + ); + } else { + if (device.cpu.gpr[rs(opcode) as usize] as i32) < 0 { + device.cpu.lo = 1 + } else { + device.cpu.lo = !0 as u64 + } + device.cpu.hi = se32(device.cpu.gpr[rs(opcode) as usize] as i32) + } + + device::cop0::add_cycles(device, 36) +} + +pub fn divu(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rt(opcode) as usize]) as u32 != 0 { + device.cpu.lo = se32( + ((device.cpu.gpr[rs(opcode) as usize] as u32) + / (device.cpu.gpr[rt(opcode) as usize] as u32)) as i32, + ); + device.cpu.hi = se32( + ((device.cpu.gpr[rs(opcode) as usize] as u32) + % (device.cpu.gpr[rt(opcode) as usize] as u32)) as i32, + ); + } else { + device.cpu.lo = !0 as u64; + device.cpu.hi = se32(device.cpu.gpr[rs(opcode) as usize] as u32 as i32) + } + + device::cop0::add_cycles(device, 36) +} + +pub fn dmult(device: &mut device::Device, opcode: u32) { + let result = ((device.cpu.gpr[rs(opcode) as usize] as i64 as i128) + * (device.cpu.gpr[rt(opcode) as usize] as i64 as i128)) as u128; + device.cpu.lo = result as u64; + device.cpu.hi = (result >> 64) as u64; + device::cop0::add_cycles(device, 7) +} + +pub fn dmultu(device: &mut device::Device, opcode: u32) { + let result = (device.cpu.gpr[rs(opcode) as usize] as u128) + * (device.cpu.gpr[rt(opcode) as usize] as u128); + device.cpu.lo = result as u64; + device.cpu.hi = (result >> 64) as u64; + device::cop0::add_cycles(device, 7) +} + +pub fn ddiv(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rt(opcode) as usize] as i64 != 0 { + device.cpu.lo = (((device.cpu.gpr[rs(opcode) as usize] as i64) as i128) + / (device.cpu.gpr[rt(opcode) as usize] as i64) as i128) as u64; + device.cpu.hi = (((device.cpu.gpr[rs(opcode) as usize] as i64) as i128) + % (device.cpu.gpr[rt(opcode) as usize] as i64) as i128) as u64; + } else { + if ((device.cpu.gpr[rs(opcode) as usize]) as i64) < 0 { + device.cpu.lo = 1 + } else { + device.cpu.lo = !0 as u64 + } + device.cpu.hi = device.cpu.gpr[rs(opcode) as usize] + } + device::cop0::add_cycles(device, 68); +} + +pub fn ddivu(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rt(opcode) as usize] != 0 { + device.cpu.lo = device.cpu.gpr[rs(opcode) as usize] / device.cpu.gpr[rt(opcode) as usize]; + device.cpu.hi = device.cpu.gpr[rs(opcode) as usize] % device.cpu.gpr[rt(opcode) as usize] + } else { + device.cpu.lo = !0 as u64; + device.cpu.hi = device.cpu.gpr[rs(opcode) as usize] + } + device::cop0::add_cycles(device, 68); +} + +pub fn add(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + (device.cpu.gpr[rs(opcode) as usize] as u32) + .wrapping_add(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, + ) +} + +pub fn addu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + (device.cpu.gpr[rs(opcode) as usize] as u32) + .wrapping_add(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, + ) +} + +pub fn sub(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + (device.cpu.gpr[rs(opcode) as usize] as u32) + .wrapping_sub(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, + ) +} + +pub fn subu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = se32( + (device.cpu.gpr[rs(opcode) as usize] as u32) + .wrapping_sub(device.cpu.gpr[rt(opcode) as usize] as u32) as i32, + ) +} + +pub fn and(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize] & device.cpu.gpr[rt(opcode) as usize] +} + +pub fn or(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize] | device.cpu.gpr[rt(opcode) as usize] +} + +pub fn xor(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize] ^ device.cpu.gpr[rt(opcode) as usize] +} + +pub fn nor(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + !(device.cpu.gpr[rs(opcode) as usize] | device.cpu.gpr[rt(opcode) as usize]) +} + +pub fn slt(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = ((device.cpu.gpr[rs(opcode) as usize] as i64) + < (device.cpu.gpr[rt(opcode) as usize] as i64)) + as u64 +} + +pub fn sltu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + (device.cpu.gpr[rs(opcode) as usize] < device.cpu.gpr[rt(opcode) as usize]) as u64 +} + +pub fn dadd(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_add(device.cpu.gpr[rt(opcode) as usize]) +} + +pub fn daddu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_add(device.cpu.gpr[rt(opcode) as usize]) +} + +pub fn dsub(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_sub(device.cpu.gpr[rt(opcode) as usize]) +} + +pub fn dsubu(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + device.cpu.gpr[rs(opcode) as usize].wrapping_sub(device.cpu.gpr[rt(opcode) as usize]) +} + +pub fn tge(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) >= (device.cpu.gpr[rt(opcode) as usize] as i64) + { + device::exceptions::trap_exception(device) + } +} + +pub fn tgeu(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] >= device.cpu.gpr[rt(opcode) as usize] { + device::exceptions::trap_exception(device) + } +} + +pub fn tlt(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) < (device.cpu.gpr[rt(opcode) as usize] as i64) { + device::exceptions::trap_exception(device) + } +} + +pub fn tltu(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] < device.cpu.gpr[rt(opcode) as usize] { + device::exceptions::trap_exception(device) + } +} + +pub fn teq(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] == device.cpu.gpr[rt(opcode) as usize] { + device::exceptions::trap_exception(device) + } +} + +pub fn tne(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] != device.cpu.gpr[rt(opcode) as usize] { + device::exceptions::trap_exception(device) + } +} + +pub fn dsll(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.gpr[rt(opcode) as usize] << sa(opcode) +} + +pub fn dsrl(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.gpr[rt(opcode) as usize] >> sa(opcode) +} + +pub fn dsra(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + ((device.cpu.gpr[rt(opcode) as usize] as i64) >> sa(opcode)) as u64 +} + +pub fn dsll32(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.gpr[rt(opcode) as usize] << (32 + sa(opcode)) +} + +pub fn dsrl32(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = device.cpu.gpr[rt(opcode) as usize] >> (32 + sa(opcode)) +} + +pub fn dsra32(device: &mut device::Device, opcode: u32) { + device.cpu.gpr[rd(opcode) as usize] = + ((device.cpu.gpr[rt(opcode) as usize] as i64) >> (32 + sa(opcode))) as u64 +} + +pub fn bltz(device: &mut device::Device, opcode: u32) { + if ((device.cpu.gpr[rs(opcode) as usize]) as i64) < 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bgez(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) >= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bltzl(device: &mut device::Device, opcode: u32) { + if ((device.cpu.gpr[rs(opcode) as usize]) as i64) < 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn bgezl(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) >= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn tgei(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) >= (imm(opcode) as i16 as i64) { + device::exceptions::trap_exception(device) + } +} + +pub fn tgeiu(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] >= se16(imm(opcode) as i16) { + device::exceptions::trap_exception(device) + } +} + +pub fn tlti(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) < (imm(opcode) as i16 as i64) { + device::exceptions::trap_exception(device) + } +} + +pub fn tltiu(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] < se16(imm(opcode) as i16) { + device::exceptions::trap_exception(device) + } +} + +pub fn teqi(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) == (imm(opcode) as i16 as i64) { + device::exceptions::trap_exception(device) + } +} + +pub fn tnei(device: &mut device::Device, opcode: u32) { + if (device.cpu.gpr[rs(opcode) as usize] as i64) != (imm(opcode) as i16 as i64) { + device::exceptions::trap_exception(device) + } +} + +pub fn bltzal(device: &mut device::Device, opcode: u32) { + if ((device.cpu.gpr[rs(opcode) as usize]) as i64) < 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } + device.cpu.gpr[31] = device.cpu.pc + 8 +} + +pub fn bgezal(device: &mut device::Device, opcode: u32) { + let in_delay_slot = device::cpu::in_delay_slot(device); + + if device.cpu.gpr[rs(opcode) as usize] as i64 >= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } + + if in_delay_slot { + device.cpu.gpr[31] = device.cpu.branch_state.pc + 4 + } else { + device.cpu.gpr[31] = device.cpu.pc + 8 + } +} + +pub fn bltzall(device: &mut device::Device, opcode: u32) { + if ((device.cpu.gpr[rs(opcode) as usize]) as i64) < 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } + device.cpu.gpr[31] = device.cpu.pc + 8 +} + +pub fn bgezall(device: &mut device::Device, opcode: u32) { + if device.cpu.gpr[rs(opcode) as usize] as i64 >= 0 { + check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(se16(imm(opcode << 2) as i16)) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } + device.cpu.gpr[31] = device.cpu.pc + 8 +} diff --git a/src/device/events.rs b/src/device/events.rs new file mode 100644 index 00000000..900e23be --- /dev/null +++ b/src/device/events.rs @@ -0,0 +1,88 @@ +use crate::device; + +#[derive(PartialEq, Copy, Clone)] +pub enum EventType { + AI, + SI, + VI, + PI, + DP, + SP, + SPDma, + COMPARE, +} + +#[derive(PartialEq, Copy, Clone)] +pub struct Event { + pub name: EventType, + pub count: u64, + pub handler: fn(&mut device::Device), +} + +pub fn create_event( + device: &mut device::Device, + name: EventType, + when: u64, + handler: fn(&mut device::Device), +) { + let event = Event { + name: name, + count: when, + handler: handler, + }; + if get_event(device, name) != None { + panic! {"duplicate event {}", name as usize} + } + device.cpu.events.push(event); + set_next_event(device); +} + +pub fn get_event(device: &mut device::Device, name: EventType) -> Option<&mut Event> { + for i in device.cpu.events.iter_mut() { + if i.name == name { + return Some(i); + } + } + return None; +} + +pub fn remove_event(device: &mut device::Device, name: EventType) { + let mut remove_index: usize = 0; + let mut found = false; + for (pos, i) in device.cpu.events.iter_mut().enumerate() { + if i.name == name { + found = true; + remove_index = pos; + break; + } + } + if found { + device.cpu.events.remove(remove_index); + } +} + +pub fn trigger_event(device: &mut device::Device) { + let next_event_name = device.cpu.next_event.unwrap().name; + remove_event(device, next_event_name); + + let handler = device.cpu.next_event.unwrap().handler; + handler(device); + set_next_event(device); +} + +pub fn set_next_event(device: &mut device::Device) { + device.cpu.next_event_count = u64::MAX; + for i in device.cpu.events.iter() { + if i.count < device.cpu.next_event_count { + device.cpu.next_event_count = i.count; + device.cpu.next_event = Some(*i); + } + } +} + +pub fn translate_events(device: &mut device::Device, old_count: u64, new_count: u64) { + for i in device.cpu.events.iter_mut() { + i.count = i.count - old_count + new_count + } + set_next_event(device); +} diff --git a/src/device/exceptions.rs b/src/device/exceptions.rs new file mode 100644 index 00000000..18497cdb --- /dev/null +++ b/src/device/exceptions.rs @@ -0,0 +1,136 @@ +use crate::device; + +pub fn check_pending_interrupts(device: &mut device::Device) { + if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] + & device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] + & device::cop0::COP0_CAUSE_IP_MASK) + == 0 + { + // interrupt disabled, or no pending interrupts + return; + } + + if (device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] + & (device::cop0::COP0_STATUS_IE + | device::cop0::COP0_STATUS_EXL + | device::cop0::COP0_STATUS_ERL)) + != device::cop0::COP0_STATUS_IE + { + // interrupts disabled globally, or error/exception is already set + return; + } + + exception_general(device); +} + +pub fn floating_point_exception(device: &mut device::Device) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_FPE; + + exception_general(device) +} + +pub fn trap_exception(device: &mut device::Device) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_TR; + + exception_general(device) +} + +pub fn syscall_exception(device: &mut device::Device) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_SYS; + + exception_general(device) +} + +pub fn break_exception(device: &mut device::Device) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_BP; + + exception_general(device) +} + +pub fn reserved_exception(device: &mut device::Device, cop: u64) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_RI | cop; + + exception_general(device) +} + +pub fn cop_unusable_exception(device: &mut device::Device, cop: u64) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_CPU | cop; + + exception_general(device) +} + +pub fn tlb_miss_exception( + device: &mut device::Device, + address: u64, + access_type: device::memory::AccessType, +) { + if access_type == device::memory::AccessType::Read { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_TLBL + } else { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] = + device::cop0::COP0_CAUSE_EXCCODE_TLBS + } + + device.cpu.cop0.regs[device::cop0::COP0_BADVADDR_REG as usize] = address; + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_CONTEXT_REG as usize], + address >> 9, + device::cop0::COP0_CONTEXT_BADVPN2_MASK, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG as usize], + address >> 9, + device::cop0::COP0_XCONTEXT_BADVPN2_MASK, + ); + device::memory::masked_write_64( + &mut device.cpu.cop0.regs[device::cop0::COP0_XCONTEXT_REG as usize], + address >> 31, + device::cop0::COP0_XCONTEXT_REGION_MASK, + ); + + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_EXL + == 0 + { + device.cpu.pc -= 0x180 + } + + exception_general(device) +} + +pub fn exception_general(device: &mut device::Device) { + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_EXL + == 0 + { + device.cpu.cop0.regs[device::cop0::COP0_EPC_REG as usize] = device.cpu.pc; + if device::cpu::in_delay_slot(device) { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= + device::cop0::COP0_CAUSE_BD; + device.cpu.cop0.regs[device::cop0::COP0_EPC_REG as usize] -= 4; + } else { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= + !device::cop0::COP0_CAUSE_BD; + } + } + + device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] |= device::cop0::COP0_STATUS_EXL; + + let vector_offset: u32 = 0x180; + if device.cpu.cop0.regs[device::cop0::COP0_STATUS_REG as usize] & device::cop0::COP0_STATUS_BEV + == 0 + { + device.cpu.pc = device::cpu_instructions::se32((0x80000000 + vector_offset) as i32); + } else { + device.cpu.pc = device::cpu_instructions::se32((0xBFC00200 + vector_offset) as i32); + } + + device.cpu.branch_state.state = device::cpu::State::Exception; + + device::cop0::add_cycles(device, 2); +} diff --git a/src/device/fpu_instructions.rs b/src/device/fpu_instructions.rs new file mode 100644 index 00000000..824fa8be --- /dev/null +++ b/src/device/fpu_instructions.rs @@ -0,0 +1,700 @@ +use crate::device; + +pub fn fs(opcode: u32) -> u32 { + return (opcode >> 11) & 0x1F; +} + +pub fn ft(opcode: u32) -> u32 { + return (opcode >> 16) & 0x1F; +} + +pub fn fd(opcode: u32) -> u32 { + return (opcode >> 6) & 0x1F; +} + +pub fn bc1f(device: &mut device::Device, opcode: u32) { + if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT == 0 { + device::cpu_instructions::check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(device::cpu_instructions::se16( + device::cpu_instructions::imm(opcode << 2) as i16, + )) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bc1t(device: &mut device::Device, opcode: u32) { + if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT != 0 { + device::cpu_instructions::check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(device::cpu_instructions::se16( + device::cpu_instructions::imm(opcode << 2) as i16, + )) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bc1fl(device: &mut device::Device, opcode: u32) { + if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT == 0 { + device::cpu_instructions::check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(device::cpu_instructions::se16( + device::cpu_instructions::imm(opcode << 2) as i16, + )) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn bc1tl(device: &mut device::Device, opcode: u32) { + if device.cpu.cop1.fcr31 & device::cop1::FCR31_CMP_BIT != 0 { + device::cpu_instructions::check_relative_idle_loop(device, opcode); + device.cpu.branch_state.state = device::cpu::State::Take; + device.cpu.branch_state.pc = device.cpu.pc.wrapping_add(device::cpu_instructions::se16( + device::cpu_instructions::imm(opcode << 2) as i16, + )) + 4; + } else { + device.cpu.branch_state.state = device::cpu::State::Discard; + } +} + +pub fn add_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs + ft, true); + device::cop0::add_cycles(device, 2) +} + +pub fn sub_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs - ft, true); + device::cop0::add_cycles(device, 2) +} + +pub fn mul_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs * ft, true); + device::cop0::add_cycles(device, 4) +} + +pub fn div_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs / ft, true); + device::cop0::add_cycles(device, 28) +} + +pub fn sqrt_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs.sqrt(), true); + device::cop0::add_cycles(device, 28) +} + +pub fn abs_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs.abs(), true); + device::cop0::add_cycles(device, 2) +} + +pub fn mov_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs); +} + +pub fn neg_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, -fs, true); +} + +pub fn round_l_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.round_ties_even() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn trunc_l_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.trunc() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn ceil_l_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.ceil() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn floor_l_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.floor() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn round_w_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.round_ties_even() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn trunc_w_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.trunc() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn ceil_w_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.ceil() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn floor_w_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.floor() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_d_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_w_s(device: &mut device::Device, opcode: u32) { + match device.cpu.cop1.fcr31 & 3 { + 0 => round_w_s(device, opcode), + 1 => trunc_w_s(device, opcode), + 2 => ceil_w_s(device, opcode), + 3 => floor_w_s(device, opcode), + _ => panic!("unknown cvt_w_s"), + } +} + +pub fn cvt_l_s(device: &mut device::Device, opcode: u32) { + match device.cpu.cop1.fcr31 & 3 { + 0 => round_l_s(device, opcode), + 1 => trunc_l_s(device, opcode), + 2 => ceil_l_s(device, opcode), + 3 => floor_l_s(device, opcode), + _ => panic!("unknown cvt_l_s"), + } +} + +pub fn c_f_s(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT +} + +pub fn c_un_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_eq_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ueq_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_olt_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ult_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ole_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ule_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_sf_s(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; +} + +pub fn c_ngle_s(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; +} + +pub fn c_seq_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ngl_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_lt_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_nge_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_le_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ngt_s(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_single(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_single(device, ft(opcode) as usize); + + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn add_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs + ft); + device::cop0::add_cycles(device, 2) +} + +pub fn sub_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs - ft); + device::cop0::add_cycles(device, 2) +} + +pub fn mul_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs * ft); + device::cop0::add_cycles(device, 7) +} + +pub fn div_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs / ft); + device::cop0::add_cycles(device, 57) +} + +pub fn sqrt_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs.sqrt()); + device::cop0::add_cycles(device, 57) +} + +pub fn abs_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs.abs()); + device::cop0::add_cycles(device, 2) +} + +pub fn mov_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs); +} + +pub fn neg_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_double(device, fd(opcode) as usize, -fs); +} + +pub fn round_l_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.round_ties_even() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn trunc_l_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.trunc() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn ceil_l_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.ceil() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn floor_l_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f64::from_ne_bytes((fs.floor() as i64).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, value); + device::cop0::add_cycles(device, 4) +} + +pub fn round_w_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.round_ties_even() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn trunc_w_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.trunc() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn ceil_w_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.ceil() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn floor_w_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let value = f32::from_ne_bytes((fs.floor() as i32).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, value, true); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_s_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_w_d(device: &mut device::Device, opcode: u32) { + match device.cpu.cop1.fcr31 & 3 { + 0 => round_w_d(device, opcode), + 1 => trunc_w_d(device, opcode), + 2 => ceil_w_d(device, opcode), + 3 => floor_w_d(device, opcode), + _ => panic!("unknown cvt_w_d"), + } +} + +pub fn cvt_l_d(device: &mut device::Device, opcode: u32) { + match device.cpu.cop1.fcr31 & 3 { + 0 => round_l_d(device, opcode), + 1 => trunc_l_d(device, opcode), + 2 => ceil_l_d(device, opcode), + 3 => floor_l_d(device, opcode), + _ => panic!("unknown cvt_l_d"), + } +} + +pub fn c_f_d(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; +} + +pub fn c_un_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_eq_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ueq_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_olt_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ult_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ole_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + return; + } + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ule_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + if fs.is_nan() || ft.is_nan() { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + return; + } + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_sf_d(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; +} + +pub fn c_ngle_d(device: &mut device::Device, _opcode: u32) { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; +} + +pub fn c_seq_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ngl_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs == ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_lt_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_nge_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs < ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_le_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn c_ngt_d(device: &mut device::Device, opcode: u32) { + let fs = device::cop1::get_fpr_double(device, fs(opcode) as usize); + let ft = device::cop1::get_fpr_double(device, ft(opcode) as usize); + + if fs <= ft { + device.cpu.cop1.fcr31 |= device::cop1::FCR31_CMP_BIT; + } else { + device.cpu.cop1.fcr31 &= !device::cop1::FCR31_CMP_BIT; + } +} + +pub fn cvt_s_l(device: &mut device::Device, opcode: u32) { + let fs = + i64::from_ne_bytes(device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_d_l(device: &mut device::Device, opcode: u32) { + let fs = + i64::from_ne_bytes(device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_s_w(device: &mut device::Device, opcode: u32) { + let fs = + i32::from_ne_bytes(device::cop1::get_fpr_single(device, fs(opcode) as usize).to_ne_bytes()); + device::cop1::set_fpr_single(device, fd(opcode) as usize, fs as f32, true); + device::cop0::add_cycles(device, 4) +} + +pub fn cvt_d_w(device: &mut device::Device, opcode: u32) { + let fs = i32::from_ne_bytes( + device::cop1::get_fpr_double(device, fs(opcode) as usize).to_ne_bytes()[0..4] + .try_into() + .unwrap(), + ); + device::cop1::set_fpr_double(device, fd(opcode) as usize, fs as f64); + device::cop0::add_cycles(device, 4) +} diff --git a/src/device/is_viewer.rs b/src/device/is_viewer.rs new file mode 100644 index 00000000..8aeba975 --- /dev/null +++ b/src/device/is_viewer.rs @@ -0,0 +1,37 @@ +use crate::device; +use std::str; + +pub const IS_VIEWER_MASK: usize = 0xFFFF; + +pub fn read_mem( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let masked_address = address as usize & IS_VIEWER_MASK; + return u32::from_be_bytes( + device.cart.is_viewer_buffer[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); +} + +pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let masked_address = address as usize & IS_VIEWER_MASK; + if masked_address == 0x14 { + let length = (value & mask) as u64; + let data = + str::from_utf8(&device.cart.is_viewer_buffer[0x20 as usize..(0x20 + length) as usize]) + .unwrap(); + print!("{}", data); + } else { + let mut data = u32::from_be_bytes( + device.cart.is_viewer_buffer[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + device::memory::masked_write_32(&mut data, value, mask); + device.cart.is_viewer_buffer[masked_address..masked_address + 4] + .copy_from_slice(&data.to_be_bytes()); + } +} diff --git a/src/device/memory.rs b/src/device/memory.rs new file mode 100644 index 00000000..39c0f0a4 --- /dev/null +++ b/src/device/memory.rs @@ -0,0 +1,164 @@ +use crate::device; + +pub const MM_RDRAM_DRAM: usize = 0x00000000; +pub const MM_RDRAM_REGS: usize = 0x03f00000; +pub const MM_RSP_MEM: usize = 0x04000000; +pub const MM_RSP_REGS: usize = 0x04040000; +pub const MM_RSP_REGS_PC: usize = 0x04080000; +pub const MM_DPC_REGS: usize = 0x04100000; +pub const MM_DPS_REGS: usize = 0x04200000; +pub const MM_MI_REGS: usize = 0x04300000; +pub const MM_VI_REGS: usize = 0x04400000; +pub const MM_AI_REGS: usize = 0x04500000; +pub const MM_PI_REGS: usize = 0x04600000; +pub const MM_RI_REGS: usize = 0x04700000; +pub const MM_SI_REGS: usize = 0x04800000; +//const MM_DOM2_ADDR1: usize = 0x05000000; +pub const MM_DOM2_ADDR2: usize = 0x08000000; +pub const MM_CART_ROM: usize = 0x10000000; +pub const MM_PIF_MEM: usize = 0x1fc00000; +pub const MM_DOM1_ADDR3: usize = 0x1fd00000; +pub const MM_IS_VIEWER: usize = 0x13ff0000; + +#[derive(PartialEq)] +pub enum AccessType { + Write, + Read, + Lookup, +} + +#[derive(Copy, Clone)] +pub enum AccessSize { + // None = 0, + Word = 4, + Dword = 8, + Dcache = 16, + Icache = 32, +} + +pub struct Memory { + pub fast_read: [fn(&mut device::Device, u64, AccessSize) -> u32; 0x2000], // fast_read is used for lookups that try to detect idle loops + pub memory_map_read: [fn(&mut device::Device, u64, AccessSize) -> u32; 0x2000], + pub memory_map_write: [fn(&mut device::Device, u64, u32, u32); 0x2000], + pub icache: [device::cache::ICache; 512], + pub dcache: [device::cache::DCache; 512], +} + +pub fn masked_write_32(dst: &mut u32, value: u32, mask: u32) { + *dst = (*dst & !mask) | (value & mask); +} + +pub fn masked_write_64(dst: &mut u64, value: u64, mask: u64) { + *dst = (*dst & !mask) | (value & mask); +} + +pub fn translate_address( + device: &mut device::Device, + address: u64, + access_type: AccessType, +) -> (u64, bool, bool) { + let mut cached = false; + if (address & 0xc0000000) != 0x80000000 { + return device::tlb::get_physical_address(device, address, access_type); + } else if address & 0x20000000 == 0 { + cached = true; + } + return (address & 0x1FFFFFFF, cached, false); +} + +pub fn data_read( + device: &mut device::Device, + phys_address: u64, + access_size: AccessSize, + cached: bool, +) -> u32 { + if cached { + device::cache::dcache_read(device, phys_address) + } else { + return device.memory.memory_map_read[(phys_address >> 16) as usize]( + device, + phys_address, + access_size, + ); + } +} + +pub fn data_write( + device: &mut device::Device, + phys_address: u64, + value: u32, + mask: u32, + cached: bool, +) { + if cached { + device::cache::dcache_write(device, phys_address, value, mask) + } else { + device.memory.memory_map_write[(phys_address >> 16) as usize]( + device, + phys_address, + value, + mask, + ) + } +} + +pub fn init(device: &mut device::Device) { + for i in 0..0x2000 { + if i >= MM_RDRAM_DRAM >> 16 && i <= (MM_RDRAM_DRAM + 0x03EFFFFF) >> 16 { + device.memory.fast_read[i] = device::rdram::read_mem_fast; + device.memory.memory_map_read[i] = device::rdram::read_mem; + device.memory.memory_map_write[i] = device::rdram::write_mem; + } else if i >= MM_RDRAM_REGS >> 16 && i <= (MM_RDRAM_REGS + 0xFFFFF) >> 16 { + device.memory.memory_map_read[i] = device::rdram::read_regs; + device.memory.memory_map_write[i] = device::rdram::write_regs; + } else if i >= MM_RSP_MEM >> 16 && i <= (MM_RSP_MEM + 0x3FFFF) >> 16 { + device.memory.fast_read[i] = device::rsp_interface::read_mem_fast; + device.memory.memory_map_read[i] = device::rsp_interface::read_mem; + device.memory.memory_map_write[i] = device::rsp_interface::write_mem; + } else if i >= MM_RSP_REGS >> 16 && i <= (MM_RSP_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::rsp_interface::read_regs; + device.memory.memory_map_write[i] = device::rsp_interface::write_regs; + } else if i >= MM_RSP_REGS_PC >> 16 && i <= (MM_RSP_REGS_PC + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::rsp_interface::read_regs2; + device.memory.memory_map_write[i] = device::rsp_interface::write_regs2; + } else if i >= MM_DPC_REGS >> 16 && i <= (MM_DPC_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::rdp::read_regs_dpc; + device.memory.memory_map_write[i] = device::rdp::write_regs_dpc; + } else if i >= MM_DPS_REGS >> 16 && i <= (MM_DPS_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::rdp::read_regs_dps; + device.memory.memory_map_write[i] = device::rdp::write_regs_dps; + } else if i >= MM_MI_REGS >> 16 && i <= (MM_MI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::mi::read_regs; + device.memory.memory_map_write[i] = device::mi::write_regs; + } else if i >= MM_VI_REGS >> 16 && i <= (MM_VI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::vi::read_regs; + device.memory.memory_map_write[i] = device::vi::write_regs; + } else if i >= MM_AI_REGS >> 16 && i <= (MM_AI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::ai::read_regs; + device.memory.memory_map_write[i] = device::ai::write_regs; + } else if i >= MM_PI_REGS >> 16 && i <= (MM_PI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::pi::read_regs; + device.memory.memory_map_write[i] = device::pi::write_regs; + } else if i >= MM_RI_REGS >> 16 && i <= (MM_RI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::ri::read_regs; + device.memory.memory_map_write[i] = device::ri::write_regs; + } else if i >= MM_SI_REGS >> 16 && i <= (MM_SI_REGS + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::si::read_regs; + device.memory.memory_map_write[i] = device::si::write_regs; + } else if i >= MM_DOM2_ADDR2 >> 16 && i <= (MM_DOM2_ADDR2 + 0x1FFFF) >> 16 { + device.memory.memory_map_read[i] = device::sram::read_mem; + device.memory.memory_map_write[i] = device::sram::write_mem; + } else if i >= MM_CART_ROM >> 16 && i <= (MM_CART_ROM + device.cart.rom.len() - 1) >> 16 { + device.memory.fast_read[i] = device::cart_rom::read_mem_fast; + device.memory.memory_map_read[i] = device::cart_rom::read_mem; + device.memory.memory_map_write[i] = device::cart_rom::write_mem; + } else if i >= MM_IS_VIEWER >> 16 && i <= (MM_IS_VIEWER + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::is_viewer::read_mem; + device.memory.memory_map_write[i] = device::is_viewer::write_mem; + } else if i >= MM_PIF_MEM >> 16 && i <= (MM_PIF_MEM + 0xFFFF) >> 16 { + device.memory.memory_map_read[i] = device::pif::read_mem; + device.memory.memory_map_write[i] = device::pif::write_mem; + } + } + device::cache::init(device) +} diff --git a/src/device/mi.rs b/src/device/mi.rs new file mode 100644 index 00000000..6c34397f --- /dev/null +++ b/src/device/mi.rs @@ -0,0 +1,168 @@ +use crate::device; + +pub const MI_INIT_MODE_REG: u32 = 0; +pub const MI_VERSION_REG: u32 = 1; +pub const MI_INTR_REG: u32 = 2; +pub const MI_INTR_MASK_REG: u32 = 3; +pub const MI_REGS_COUNT: u32 = 4; + +/* read */ +pub const MI_INTR_SP: u32 = 1 << 0; +pub const MI_INTR_SI: u32 = 1 << 1; +pub const MI_INTR_AI: u32 = 1 << 2; +pub const MI_INTR_VI: u32 = 1 << 3; +pub const MI_INTR_PI: u32 = 1 << 4; +pub const MI_INTR_DP: u32 = 1 << 5; + +/* write */ +pub const MI_CLR_SP: u32 = 1 << 0; +pub const MI_SET_SP: u32 = 1 << 1; +pub const MI_CLR_SI: u32 = 1 << 2; +pub const MI_SET_SI: u32 = 1 << 3; +pub const MI_CLR_AI: u32 = 1 << 4; +pub const MI_SET_AI: u32 = 1 << 5; +pub const MI_CLR_VI: u32 = 1 << 6; +pub const MI_SET_VI: u32 = 1 << 7; +pub const MI_CLR_PI: u32 = 1 << 8; +pub const MI_SET_PI: u32 = 1 << 9; +pub const MI_CLR_DP: u32 = 1 << 10; +pub const MI_SET_DP: u32 = 1 << 11; + +/* mode read */ +pub const MI_INIT_MODE: u32 = 1 << 7; +pub const MI_EBUS_MODE: u32 = 1 << 8; +pub const MI_RDRAM_MODE: u32 = 1 << 9; + +/* mode write */ +pub const MI_CLR_INIT: u32 = 1 << 7; +pub const MI_SET_INIT: u32 = 1 << 8; +pub const MI_CLR_EBUS: u32 = 1 << 9; +pub const MI_SET_EBUS: u32 = 1 << 10; +pub const MI_CLR_DP_INTR: u32 = 1 << 11; +pub const MI_CLR_RDRAM: u32 = 1 << 12; +pub const MI_SET_RDRAM: u32 = 1 << 13; + +pub const MI_INIT_LENGTH_MASK: u32 = 0b1111111; + +pub struct Mi { + pub regs: [u32; MI_REGS_COUNT as usize], +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.mi.regs[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + MI_INIT_MODE_REG => update_init_mode(device, value), + MI_INTR_MASK_REG => update_intr_mask(device, value), + _ => device::memory::masked_write_32(&mut device.mi.regs[reg as usize], value, mask), + } + + if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= + !device::cop0::COP0_CAUSE_EXCCODE_MASK; + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2; + } else { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= + !device::cop0::COP0_CAUSE_IP2; + } + device::exceptions::check_pending_interrupts(device) +} + +pub fn update_init_mode(device: &mut device::Device, w: u32) { + device.mi.regs[MI_INIT_MODE_REG as usize] &= !MI_INIT_LENGTH_MASK; + device.mi.regs[MI_INIT_MODE_REG as usize] |= w & MI_INIT_LENGTH_MASK; + + if w & MI_CLR_INIT != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] &= !MI_INIT_MODE + } + if w & MI_SET_INIT != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] |= MI_INIT_MODE + } + if w & MI_CLR_EBUS != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] &= !MI_EBUS_MODE + } + if w & MI_SET_EBUS != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] |= MI_EBUS_MODE + } + if w & MI_CLR_RDRAM != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] &= !MI_RDRAM_MODE + } + if w & MI_SET_RDRAM != 0 { + device.mi.regs[MI_INIT_MODE_REG as usize] |= MI_RDRAM_MODE + } + + if w & MI_CLR_DP_INTR != 0 { + clear_rcp_interrupt(device, MI_INTR_DP) + } +} + +pub fn update_intr_mask(device: &mut device::Device, w: u32) { + if w & MI_CLR_SP != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_SP + } + if w & MI_SET_SP != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_SP + } + if w & MI_CLR_SI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_SI + } + if w & MI_SET_SI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_SI + } + if w & MI_CLR_AI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_AI + } + if w & MI_SET_AI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_AI + } + if w & MI_CLR_VI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_VI + } + if w & MI_SET_VI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_VI + } + if w & MI_CLR_PI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_PI + } + if w & MI_SET_PI != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_PI + } + if w & MI_CLR_DP != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] &= !MI_INTR_DP + } + if w & MI_SET_DP != 0 { + device.mi.regs[MI_INTR_MASK_REG as usize] |= MI_INTR_DP + } +} + +pub fn clear_rcp_interrupt(device: &mut device::Device, interrupt: u32) { + device.mi.regs[MI_INTR_REG as usize] &= !interrupt; + + if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] == 0 { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= + !device::cop0::COP0_CAUSE_IP2; + } + device::exceptions::check_pending_interrupts(device) +} + +pub fn set_rcp_interrupt(device: &mut device::Device, interrupt: u32) { + device.mi.regs[MI_INTR_REG as usize] |= interrupt; + + if device.mi.regs[MI_INTR_REG as usize] & device.mi.regs[MI_INTR_MASK_REG as usize] != 0 { + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] &= + !device::cop0::COP0_CAUSE_EXCCODE_MASK; + device.cpu.cop0.regs[device::cop0::COP0_CAUSE_REG as usize] |= device::cop0::COP0_CAUSE_IP2; + } + device::exceptions::check_pending_interrupts(device) +} + +pub fn init(device: &mut device::Device) { + device.mi.regs[MI_VERSION_REG as usize] = 0x02020102 +} diff --git a/src/device/pi.rs b/src/device/pi.rs new file mode 100644 index 00000000..7406169f --- /dev/null +++ b/src/device/pi.rs @@ -0,0 +1,164 @@ +use crate::device; + +pub const PI_DRAM_ADDR_REG: u32 = 0; +pub const PI_CART_ADDR_REG: u32 = 1; +pub const PI_RD_LEN_REG: u32 = 2; +pub const PI_WR_LEN_REG: u32 = 3; +pub const PI_STATUS_REG: u32 = 4; +pub const PI_BSD_DOM1_LAT_REG: u32 = 5; +pub const PI_BSD_DOM1_PWD_REG: u32 = 6; +pub const PI_BSD_DOM1_PGS_REG: u32 = 7; +pub const PI_BSD_DOM1_RLS_REG: u32 = 8; +pub const PI_BSD_DOM2_LAT_REG: u32 = 9; +pub const PI_BSD_DOM2_PWD_REG: u32 = 10; +pub const PI_BSD_DOM2_PGS_REG: u32 = 11; +pub const PI_BSD_DOM2_RLS_REG: u32 = 12; +pub const PI_REGS_COUNT: u32 = 13; + +/* PI_STATUS - read */ +pub const PI_STATUS_DMA_BUSY: u32 = 1 << 0; +pub const PI_STATUS_IO_BUSY: u32 = 1 << 1; +//pub const PI_STATUS_ERROR: u32 = 1 << 2; +pub const PI_STATUS_INTERRUPT: u32 = 1 << 3; + +/* PI_STATUS - write */ +pub const PI_STATUS_RESET: u32 = 1 << 0; +pub const PI_STATUS_CLR_INTR: u32 = 1 << 1; + +pub struct Pi { + pub regs: [u32; PI_REGS_COUNT as usize], +} + +pub struct PiHandler { + read: fn(&mut device::Device, u32, u32, u32) -> u64, + write: fn(&mut device::Device, u32, u32, u32) -> u64, +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.pi.regs[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn dma_read(device: &mut device::Device) { + let handler = get_handler(device.pi.regs[PI_CART_ADDR_REG as usize]); + let cycles = (handler.read)( + device, + device.pi.regs[PI_CART_ADDR_REG as usize], + device.pi.regs[PI_DRAM_ADDR_REG as usize], + device.pi.regs[PI_RD_LEN_REG as usize] + 1, + ); + + device::events::create_event( + device, + device::events::EventType::PI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles, + dma_event, + ); + + device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_DMA_BUSY +} + +pub fn dma_write(device: &mut device::Device) { + let handler = get_handler(device.pi.regs[PI_CART_ADDR_REG as usize]); + let cycles = (handler.write)( + device, + device.pi.regs[PI_CART_ADDR_REG as usize], + device.pi.regs[PI_DRAM_ADDR_REG as usize], + device.pi.regs[PI_WR_LEN_REG as usize] + 1, + ); + + device::events::create_event( + device, + device::events::EventType::PI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + cycles, + dma_event, + ); + + device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_DMA_BUSY +} + +pub fn get_handler(address: u32) -> PiHandler { + let mut handler = PiHandler { + read: device::cart_rom::dma_read, + write: device::cart_rom::dma_write, + }; + if address >= device::memory::MM_CART_ROM as u32 { + if address >= device::memory::MM_DOM1_ADDR3 as u32 { + panic!("unimplemented handler") + //RW(cart, cart_dom3); + } else { + handler.read = device::cart_rom::dma_read; + handler.write = device::cart_rom::dma_write; + } + } else if address >= device::memory::MM_DOM2_ADDR2 as u32 { + panic!("sram handler") + //RW(cart, cart_dom2); + } else { + panic!("unknown pi handler") + } + return handler; +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + PI_RD_LEN_REG => { + device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask); + device::pi::dma_read(device) + } + PI_WR_LEN_REG => { + device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask); + device::pi::dma_write(device) + } + PI_STATUS_REG => { + if value & mask & PI_STATUS_CLR_INTR != 0 { + device.pi.regs[reg as usize] &= !PI_STATUS_INTERRUPT; + device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_PI); + } + if value & mask & PI_STATUS_RESET != 0 { + device.pi.regs[PI_STATUS_REG as usize] = 0 + } + } + PI_BSD_DOM1_LAT_REG | PI_BSD_DOM1_PWD_REG | PI_BSD_DOM1_PGS_REG | PI_BSD_DOM1_RLS_REG + | PI_BSD_DOM2_LAT_REG | PI_BSD_DOM2_PWD_REG | PI_BSD_DOM2_PGS_REG | PI_BSD_DOM2_RLS_REG => { + device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value & 0xFF, mask) + } + _ => device::memory::masked_write_32(&mut device.pi.regs[reg as usize], value, mask), + } +} + +pub fn calculate_cycles(device: &mut device::Device, domain: i32, length: u32) -> u64 { + let mut cycles: f64 = 0.0; + let (page_size, latency, pulse_width, release, pages); + let page_size_base: f64 = 2.0; + + if domain == 1 { + latency = (device.pi.regs[PI_BSD_DOM1_LAT_REG as usize] + 1) as f64; + pulse_width = (device.pi.regs[PI_BSD_DOM1_PWD_REG as usize] + 1) as f64; + release = (device.pi.regs[PI_BSD_DOM1_RLS_REG as usize] + 1) as f64; + page_size = page_size_base.powf((device.pi.regs[PI_BSD_DOM1_PGS_REG as usize] + 2) as f64); + } else if domain == 2 { + latency = (device.pi.regs[PI_BSD_DOM2_LAT_REG as usize] + 1) as f64; + pulse_width = (device.pi.regs[PI_BSD_DOM2_PWD_REG as usize] + 1) as f64; + release = (device.pi.regs[PI_BSD_DOM2_RLS_REG as usize] + 1) as f64; + page_size = page_size_base.powf((device.pi.regs[PI_BSD_DOM2_PGS_REG as usize] + 2) as f64); + } else { + panic!("unknown pi dma") + } + pages = (length as f64 / page_size).ceil(); + + cycles += (14.0 + latency) * pages; + cycles += (pulse_width + release) * (length as f64 / 2.0); + cycles += 5.0 * pages; + return (cycles * 1.5) as u64; // Converting RCP clock speed to CPU clock speed +} + +pub fn dma_event(device: &mut device::Device) { + device.pi.regs[PI_STATUS_REG as usize] &= !(PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY); + device.pi.regs[PI_STATUS_REG as usize] |= PI_STATUS_INTERRUPT; + + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_PI) +} diff --git a/src/device/pif.rs b/src/device/pif.rs new file mode 100644 index 00000000..ccdd985e --- /dev/null +++ b/src/device/pif.rs @@ -0,0 +1,222 @@ +mod rom; +use crate::device; + +pub struct Pif { + pub rom: [u8; 1984], + pub ram: [u8; 64], + pub channels: [PifChannel; 5], +} + +#[derive(Copy, Clone)] +pub struct PifChannel { + pub tx: Option, + pub tx_buf: Option, + pub rx: Option, + pub rx_buf: Option, + pub process: Option, +} + +pub const PIF_RAM_SIZE: usize = 64; +const PIF_CHANNELS_COUNT: usize = 5; +const PIF_RAM_OFFSET: usize = 0x7C0; +const PIF_MASK: usize = 0xFFFF; + +pub fn read_mem( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + device::cop0::add_cycles(device, 3000); //based on https://github.com/rasky/n64-systembench + + let value; + let mut masked_address = address as usize & PIF_MASK; + if masked_address < PIF_RAM_OFFSET { + value = u32::from_be_bytes( + device.pif.rom[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + } else { + masked_address -= PIF_RAM_OFFSET; + value = u32::from_be_bytes( + device.pif.ram[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + } + return value; +} + +pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let mut masked_address = address as usize & PIF_MASK; + if masked_address < PIF_RAM_OFFSET { + panic! {"write to pif rom"} + } + masked_address -= PIF_RAM_OFFSET; + let mut data = u32::from_be_bytes( + device.pif.ram[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + device::memory::masked_write_32(&mut data, value, mask); + device.pif.ram[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes()); + + device.si.dma_dir = device::si::DmaDir::Write; + device::events::create_event( + device, + device::events::EventType::SI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + 3200, + device::si::dma_event, + ); //based on https://github.com/rasky/n64-systembench + device.si.regs[device::si::SI_STATUS_REG as usize] |= + device::si::SI_STATUS_DMA_BUSY | device::si::SI_STATUS_IO_BUSY +} + +pub fn process_channel(device: &mut device::Device, channel: usize) -> usize { + /* don't process channel if it has been disabled */ + if device.pif.channels[channel].tx == None { + return 0; + } + + /* reset Tx/Rx just in case */ + device.pif.ram[device.pif.channels[channel].tx.unwrap()] &= 0x3f; + device.pif.ram[device.pif.channels[channel].rx.unwrap()] &= 0x3f; + + /* set NoResponse if no device is connected */ + if device.pif.channels[channel].process == None { + device.pif.ram[device.pif.channels[channel].rx.unwrap()] |= 0x80; + return 0; + } + + /* do device processing */ + let process_handler = device.pif.channels[channel].process.unwrap(); + process_handler(device, channel); + return 1; +} + +pub fn update_pif_ram(device: &mut device::Device) -> u64 { + let mut active_channels = 0; + for k in 0..PIF_CHANNELS_COUNT { + active_channels += process_channel(device, k) + } + return (24000 + (active_channels * 28000)) as u64; +} + +pub fn disable_pif_channel(channel: &mut PifChannel) { + channel.tx = None; + channel.rx = None; + channel.tx_buf = None; + channel.rx_buf = None; +} + +pub fn setup_pif_channel(device: &mut device::Device, channel: usize, buf: usize) -> usize { + let tx = device.pif.ram[buf] & 0x3f; + let rx = device.pif.ram[buf + 1] & 0x3f; + + /* XXX: check out of bounds accesses */ + + device.pif.channels[channel].tx = Some(buf); + device.pif.channels[channel].rx = Some(buf + 1); + device.pif.channels[channel].tx_buf = Some(buf + 2); + device.pif.channels[channel].rx_buf = Some(buf + 2 + tx as usize); + + return (2 + tx + rx) as usize; +} + +pub fn setup_channels_format(device: &mut device::Device) { + let mut i: usize = 0; + let mut k: usize = 0; + while i < PIF_RAM_SIZE && k < PIF_CHANNELS_COUNT { + match device.pif.ram[i] { + 0x00 => { + /* skip channel */ + disable_pif_channel(&mut device.pif.channels[k]); + k += 1; + i += 1; + } + + 0xff => { + /* dummy data */ + i += 1; + } + + 0xfe => { + /* end of channel setup - remaining channels are disabled */ + while k < PIF_CHANNELS_COUNT { + disable_pif_channel(&mut device.pif.channels[k]); + k += 1; + } + } + + 0xfd => { + /* channel reset - send reset command and discard the results */ + { + panic!("channel reset") + } + } + + _ => { + /* setup channel */ + + /* HACK?: some games sends bogus PIF commands while accessing controller paks + * Yoshi Story, Top Gear Rally 2, Indiana Jones, ... + * When encountering such commands, we skip this bogus byte. + */ + if (i + 1 < PIF_RAM_SIZE) && (device.pif.ram[i + 1] == 0xfe) { + i += 1; + continue; + } + + if (i + 2) >= PIF_RAM_SIZE { + i = PIF_RAM_SIZE; + continue; + } + + i += setup_pif_channel(device, k, i); + k += 1; + } + } + } +} + +pub fn process_ram(device: &mut device::Device) { + let mut clrmask = 0x00; + let command = device.pif.ram[0x3F]; + if command & 0x01 != 0 { + // Configure joybus protocol + setup_channels_format(device); + clrmask |= 0x01 + } + if command & 0x02 != 0 { + // Challenge / response for protection + panic!("pif command 0x02"); + } + if command & 0x08 != 0 { + // Terminate boot process + clrmask |= 0x08; + } + if command & 0x10 != 0 { + // ROM lockout + for i in device.pif.rom.iter_mut() { + *i = 0 + } + } + if command & 0x20 != 0 { + // Acquire checksum + device.pif.ram[0x3F] = 0x80 + } + device.pif.ram[0x3F] &= !clrmask +} + +pub fn init(device: &mut device::Device, pal: bool) { + if pal { + device.pif.rom = rom::PAL_PIF_ROM; + } else { + device.pif.rom = rom::NTSC_PIF_ROM; + } + device.pif.ram[0x26] = device.cart.cic_seed; + device.pif.ram[0x27] = device.cart.cic_seed; + + device.pif.channels[0].process = Some(device::controller::process); + device.pif.channels[4].process = Some(device::cart::process) +} diff --git a/src/device/pif/rom.rs b/src/device/pif/rom.rs new file mode 100644 index 00000000..f4b519b2 --- /dev/null +++ b/src/device/pif/rom.rs @@ -0,0 +1,177 @@ +pub const NTSC_PIF_ROM: [u8; 1984] = [ + 60, 9, 52, 0, 64, 137, 96, 0, 60, 9, 0, 6, 53, 41, 228, 99, 64, 137, 128, 0, 60, 8, 164, 4, + 141, 8, 0, 16, 49, 8, 0, 1, 81, 0, 255, 253, 60, 8, 164, 4, 36, 8, 0, 10, 60, 1, 164, 4, 172, + 40, 0, 16, 60, 8, 164, 4, 141, 8, 0, 24, 49, 8, 0, 1, 85, 0, 255, 253, 60, 8, 164, 4, 36, 8, 0, + 3, 60, 1, 164, 96, 172, 40, 0, 16, 36, 8, 3, 255, 60, 1, 164, 64, 172, 40, 0, 12, 60, 1, 164, + 64, 172, 32, 0, 36, 60, 1, 164, 64, 172, 32, 0, 16, 60, 1, 164, 80, 172, 32, 0, 0, 60, 1, 164, + 80, 172, 32, 0, 4, 60, 8, 164, 4, 141, 8, 0, 16, 49, 8, 0, 4, 85, 0, 255, 253, 60, 8, 164, 4, + 60, 11, 164, 0, 60, 12, 191, 192, 60, 13, 191, 192, 37, 107, 16, 0, 37, 140, 0, 212, 37, 173, + 7, 28, 141, 137, 0, 0, 37, 140, 0, 4, 37, 107, 0, 4, 21, 141, 255, 252, 173, 105, 255, 252, 60, + 11, 164, 0, 60, 29, 164, 0, 37, 107, 16, 0, 1, 96, 0, 8, 55, 189, 31, 240, 60, 13, 191, 192, + 141, 168, 7, 252, 37, 173, 7, 192, 49, 8, 0, 128, 85, 0, 255, 252, 60, 13, 191, 192, 141, 168, + 0, 36, 60, 11, 176, 0, 0, 8, 156, 194, 50, 115, 0, 1, 0, 8, 188, 130, 18, 96, 0, 2, 50, 247, 0, + 1, 60, 11, 166, 0, 0, 8, 178, 2, 49, 10, 0, 255, 0, 8, 172, 66, 141, 168, 0, 60, 36, 9, 0, 16, + 50, 214, 0, 255, 50, 181, 0, 1, 36, 20, 0, 1, 1, 9, 64, 37, 60, 9, 164, 128, 141, 41, 0, 24, + 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, 128, 173, 168, 0, 60, 60, 12, 164, 96, 36, 8, 0, + 255, 173, 136, 0, 20, 173, 136, 0, 24, 36, 8, 0, 15, 173, 136, 0, 28, 36, 8, 0, 3, 173, 136, 0, + 32, 141, 105, 0, 0, 60, 13, 164, 16, 37, 140, 0, 0, 49, 40, 0, 255, 173, 136, 0, 20, 0, 9, 66, + 2, 173, 136, 0, 24, 0, 9, 68, 2, 173, 136, 0, 28, 0, 9, 69, 2, 173, 136, 0, 32, 141, 175, 0, + 12, 32, 8, 15, 192, 33, 107, 0, 64, 49, 239, 0, 1, 17, 224, 0, 7, 37, 173, 0, 12, 60, 13, 164, + 16, 141, 175, 0, 12, 37, 173, 0, 12, 49, 239, 0, 32, 85, 224, 255, 252, 60, 13, 164, 16, 60, + 13, 164, 0, 37, 173, 0, 0, 0, 8, 48, 37, 33, 173, 0, 64, 141, 105, 0, 0, 33, 8, 255, 252, 33, + 107, 0, 4, 33, 173, 0, 4, 21, 0, 255, 251, 173, 169, 255, 252, 60, 8, 108, 7, 53, 8, 137, 101, + 1, 72, 0, 25, 0, 0, 32, 18, 36, 132, 0, 1, 60, 5, 164, 0, 36, 165, 0, 64, 4, 17, 0, 19, 0, 0, + 0, 0, 39, 189, 255, 208, 20, 160, 0, 2, 175, 191, 0, 28, 0, 192, 40, 37, 39, 166, 0, 44, 4, 17, + 0, 255, 39, 167, 0, 40, 143, 164, 0, 40, 143, 174, 0, 44, 1, 196, 16, 35, 20, 64, 0, 2, 0, 64, + 24, 37, 0, 128, 24, 37, 143, 191, 0, 28, 39, 189, 0, 48, 0, 96, 16, 37, 3, 224, 0, 8, 0, 0, 0, + 0, 39, 189, 255, 32, 175, 191, 0, 60, 175, 183, 0, 52, 175, 182, 0, 48, 175, 181, 0, 44, 175, + 180, 0, 40, 175, 179, 0, 36, 175, 178, 0, 32, 175, 177, 0, 28, 175, 176, 0, 24, 140, 174, 0, 0, + 0, 0, 24, 37, 39, 163, 0, 180, 39, 162, 0, 116, 1, 196, 128, 38, 36, 66, 0, 16, 172, 80, 255, + 244, 172, 80, 255, 248, 172, 80, 255, 252, 20, 67, 255, 251, 172, 80, 255, 240, 140, 176, 0, 0, + 0, 0, 136, 37, 0, 160, 176, 37, 36, 23, 0, 32, 2, 0, 160, 37, 142, 208, 0, 0, 38, 49, 0, 1, 36, + 15, 3, 239, 142, 211, 0, 4, 38, 214, 0, 4, 1, 241, 32, 35, 2, 32, 48, 37, 4, 17, 255, 204, 2, + 0, 40, 37, 143, 163, 0, 116, 143, 164, 0, 120, 2, 0, 40, 37, 0, 67, 24, 33, 175, 163, 0, 116, + 4, 17, 255, 197, 2, 32, 48, 37, 143, 184, 0, 124, 60, 5, 108, 7, 175, 162, 0, 120, 3, 16, 200, + 38, 175, 185, 0, 124, 52, 165, 137, 101, 38, 4, 0, 5, 4, 17, 255, 188, 2, 32, 48, 37, 143, 168, + 0, 128, 2, 144, 8, 43, 0, 72, 72, 33, 16, 32, 0, 7, 175, 169, 0, 128, 143, 164, 0, 152, 2, 0, + 40, 37, 4, 17, 255, 179, 2, 32, 48, 37, 16, 0, 0, 4, 175, 162, 0, 152, 143, 170, 0, 152, 1, 80, + 88, 33, 175, 171, 0, 152, 50, 130, 0, 31, 143, 174, 0, 132, 2, 226, 24, 35, 0, 112, 104, 4, 0, + 80, 96, 6, 1, 141, 168, 37, 0, 112, 200, 6, 0, 80, 192, 4, 1, 213, 120, 33, 175, 175, 0, 132, + 3, 25, 40, 37, 143, 164, 0, 144, 4, 17, 255, 160, 2, 32, 48, 37, 143, 163, 0, 140, 175, 162, 0, + 144, 2, 3, 8, 43, 80, 32, 0, 8, 143, 171, 0, 132, 143, 168, 0, 128, 2, 17, 80, 33, 1, 3, 72, + 33, 1, 42, 24, 38, 16, 0, 0, 5, 175, 163, 0, 140, 143, 171, 0, 132, 1, 112, 96, 33, 1, 131, 24, + 38, 175, 163, 0, 140, 0, 20, 22, 194, 143, 175, 0, 136, 2, 226, 24, 35, 0, 112, 112, 6, 0, 80, + 104, 4, 1, 174, 144, 37, 0, 112, 64, 4, 0, 80, 200, 6, 1, 242, 192, 33, 175, 184, 0, 136, 3, + 40, 40, 37, 143, 164, 0, 148, 4, 17, 255, 131, 2, 32, 48, 37, 36, 1, 3, 240, 18, 33, 0, 57, + 175, 162, 0, 148, 143, 164, 0, 176, 2, 64, 40, 37, 4, 17, 255, 124, 2, 32, 48, 37, 0, 16, 30, + 194, 2, 227, 80, 35, 1, 83, 88, 6, 0, 115, 72, 4, 1, 43, 40, 37, 0, 64, 32, 37, 4, 17, 255, + 116, 2, 32, 48, 37, 175, 162, 0, 176, 143, 164, 0, 172, 2, 160, 40, 37, 4, 17, 255, 111, 2, 32, + 48, 37, 50, 18, 0, 31, 2, 242, 160, 35, 2, 147, 104, 4, 2, 83, 96, 6, 1, 141, 40, 37, 0, 64, + 32, 37, 4, 17, 255, 103, 2, 32, 48, 37, 143, 169, 0, 168, 50, 99, 0, 31, 2, 80, 112, 6, 2, 144, + 120, 4, 2, 227, 200, 35, 1, 207, 56, 37, 3, 51, 64, 4, 0, 115, 192, 6, 143, 173, 0, 156, 3, 8, + 80, 37, 1, 39, 88, 33, 1, 106, 96, 33, 175, 162, 0, 172, 175, 172, 0, 168, 2, 96, 40, 37, 2, + 32, 48, 37, 4, 17, 255, 85, 1, 176, 32, 33, 143, 174, 0, 160, 175, 162, 0, 156, 2, 96, 40, 37, + 2, 32, 48, 37, 4, 17, 255, 79, 1, 208, 32, 38, 143, 175, 0, 148, 143, 184, 0, 164, 175, 162, 0, + 160, 1, 240, 200, 38, 3, 56, 64, 33, 16, 0, 255, 115, 175, 168, 0, 164, 143, 163, 0, 116, 0, 0, + 136, 37, 39, 179, 0, 116, 36, 21, 0, 16, 36, 20, 0, 1, 175, 163, 0, 100, 175, 163, 0, 104, 175, + 163, 0, 108, 175, 163, 0, 112, 142, 112, 0, 0, 143, 173, 0, 100, 50, 2, 0, 31, 2, 226, 88, 35, + 1, 112, 80, 4, 0, 80, 72, 6, 1, 42, 96, 37, 1, 172, 112, 33, 2, 14, 8, 43, 16, 32, 0, 5, 175, + 174, 0, 100, 143, 175, 0, 104, 1, 240, 200, 33, 16, 0, 0, 6, 175, 185, 0, 104, 143, 164, 0, + 104, 2, 0, 40, 37, 4, 17, 255, 44, 2, 32, 48, 37, 175, 162, 0, 104, 50, 24, 0, 2, 0, 24, 64, + 66, 50, 18, 0, 1, 85, 18, 0, 6, 143, 164, 0, 108, 143, 171, 0, 108, 1, 112, 72, 33, 16, 0, 0, + 6, 175, 169, 0, 108, 143, 164, 0, 108, 2, 0, 40, 37, 4, 17, 255, 30, 2, 32, 48, 37, 175, 162, + 0, 108, 86, 146, 0, 6, 143, 164, 0, 112, 143, 170, 0, 112, 1, 80, 104, 38, 16, 0, 0, 6, 175, + 173, 0, 112, 143, 164, 0, 112, 2, 0, 40, 37, 4, 17, 255, 19, 2, 32, 48, 37, 175, 162, 0, 112, + 38, 49, 0, 1, 22, 53, 255, 209, 38, 115, 0, 4, 143, 164, 0, 100, 143, 165, 0, 104, 4, 17, 255, + 11, 2, 32, 48, 37, 143, 172, 0, 112, 143, 174, 0, 108, 143, 176, 0, 24, 143, 177, 0, 28, 143, + 178, 0, 32, 143, 179, 0, 36, 143, 180, 0, 40, 143, 181, 0, 44, 143, 182, 0, 48, 143, 183, 0, + 52, 143, 191, 0, 60, 0, 64, 32, 37, 39, 189, 0, 224, 4, 17, 0, 8, 1, 142, 40, 38, 0, 133, 0, + 25, 0, 0, 112, 16, 172, 206, 0, 0, 0, 0, 120, 18, 172, 239, 0, 0, 3, 224, 0, 8, 0, 0, 0, 0, 60, + 11, 191, 192, 141, 104, 7, 240, 60, 10, 255, 255, 48, 132, 255, 255, 1, 10, 64, 36, 0, 136, 32, + 37, 37, 107, 7, 192, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, + 164, 128, 173, 100, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 9, + 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, 128, 141, 104, 0, 60, 36, + 9, 0, 32, 173, 101, 0, 52, 1, 9, 64, 37, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, 85, 32, + 255, 253, 60, 9, 164, 128, 173, 104, 0, 60, 32, 9, 0, 16, 33, 41, 255, 255, 85, 32, 255, 255, + 33, 41, 255, 255, 141, 104, 0, 60, 49, 10, 0, 128, 80, 10, 255, 250, 32, 9, 0, 16, 36, 10, 0, + 64, 1, 10, 64, 37, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, + 128, 173, 104, 0, 60, 60, 11, 164, 0, 37, 107, 0, 0, 33, 107, 0, 64, 1, 96, 0, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +]; + +pub const PAL_PIF_ROM: [u8; 1984] = [ + 60, 9, 52, 0, 64, 137, 96, 0, 60, 9, 0, 6, 53, 41, 228, 99, 64, 137, 128, 0, 60, 8, 164, 4, + 141, 8, 0, 16, 49, 8, 0, 1, 81, 0, 255, 253, 60, 8, 164, 4, 36, 8, 0, 10, 60, 1, 164, 4, 172, + 40, 0, 16, 60, 8, 164, 4, 141, 8, 0, 24, 49, 8, 0, 1, 85, 0, 255, 253, 60, 8, 164, 4, 36, 8, 0, + 3, 60, 1, 164, 96, 172, 40, 0, 16, 36, 8, 3, 255, 60, 1, 164, 64, 172, 40, 0, 12, 60, 1, 164, + 64, 172, 32, 0, 36, 60, 1, 164, 64, 172, 32, 0, 16, 60, 1, 164, 80, 172, 32, 0, 0, 60, 1, 164, + 80, 172, 32, 0, 4, 60, 8, 164, 4, 141, 8, 0, 16, 49, 8, 0, 4, 85, 0, 255, 253, 60, 8, 164, 4, + 60, 11, 164, 0, 60, 12, 191, 192, 60, 13, 191, 192, 37, 107, 16, 0, 37, 140, 0, 212, 37, 173, + 7, 32, 141, 137, 0, 0, 37, 140, 0, 4, 37, 107, 0, 4, 21, 141, 255, 252, 173, 105, 255, 252, 60, + 11, 164, 0, 60, 29, 164, 0, 37, 107, 16, 0, 1, 96, 0, 8, 55, 189, 31, 240, 60, 13, 191, 192, + 141, 168, 7, 252, 37, 173, 7, 192, 49, 8, 0, 128, 85, 0, 255, 252, 60, 13, 191, 192, 141, 168, + 0, 36, 60, 11, 176, 0, 0, 8, 188, 130, 0, 8, 156, 194, 50, 247, 0, 1, 50, 115, 0, 1, 18, 96, 0, + 2, 54, 247, 0, 6, 60, 11, 166, 0, 0, 8, 178, 2, 49, 10, 0, 255, 0, 8, 172, 66, 141, 168, 0, 60, + 36, 9, 0, 16, 50, 214, 0, 255, 50, 181, 0, 1, 36, 20, 0, 0, 1, 9, 64, 37, 60, 9, 164, 128, 141, + 41, 0, 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, 128, 173, 168, 0, 60, 60, 12, 164, 96, + 36, 8, 0, 255, 173, 136, 0, 20, 173, 136, 0, 24, 36, 8, 0, 15, 173, 136, 0, 28, 36, 8, 0, 3, + 173, 136, 0, 32, 141, 105, 0, 0, 60, 13, 164, 16, 37, 140, 0, 0, 49, 40, 0, 255, 173, 136, 0, + 20, 0, 9, 66, 2, 173, 136, 0, 24, 0, 9, 68, 2, 173, 136, 0, 28, 0, 9, 69, 2, 173, 136, 0, 32, + 141, 175, 0, 12, 32, 8, 15, 192, 33, 107, 0, 64, 49, 239, 0, 1, 17, 224, 0, 7, 37, 173, 0, 12, + 60, 13, 164, 16, 141, 175, 0, 12, 37, 173, 0, 12, 49, 239, 0, 32, 85, 224, 255, 252, 60, 13, + 164, 16, 60, 13, 164, 0, 37, 173, 0, 0, 0, 8, 48, 37, 33, 173, 0, 64, 141, 105, 0, 0, 33, 8, + 255, 252, 33, 107, 0, 4, 33, 173, 0, 4, 21, 0, 255, 251, 173, 169, 255, 252, 60, 8, 108, 7, 53, + 8, 137, 101, 1, 72, 0, 25, 0, 0, 32, 18, 36, 132, 0, 1, 60, 5, 164, 0, 36, 165, 0, 64, 4, 17, + 0, 19, 0, 0, 0, 0, 39, 189, 255, 208, 20, 160, 0, 2, 175, 191, 0, 28, 0, 192, 40, 37, 39, 166, + 0, 44, 4, 17, 0, 255, 39, 167, 0, 40, 143, 164, 0, 40, 143, 174, 0, 44, 1, 196, 16, 35, 20, 64, + 0, 2, 0, 64, 24, 37, 0, 128, 24, 37, 143, 191, 0, 28, 39, 189, 0, 48, 0, 96, 16, 37, 3, 224, 0, + 8, 0, 0, 0, 0, 39, 189, 255, 32, 175, 191, 0, 60, 175, 183, 0, 52, 175, 182, 0, 48, 175, 181, + 0, 44, 175, 180, 0, 40, 175, 179, 0, 36, 175, 178, 0, 32, 175, 177, 0, 28, 175, 176, 0, 24, + 140, 174, 0, 0, 0, 0, 24, 37, 39, 163, 0, 180, 39, 162, 0, 116, 1, 196, 128, 38, 36, 66, 0, 16, + 172, 80, 255, 244, 172, 80, 255, 248, 172, 80, 255, 252, 20, 67, 255, 251, 172, 80, 255, 240, + 140, 176, 0, 0, 0, 0, 136, 37, 0, 160, 176, 37, 36, 23, 0, 32, 2, 0, 160, 37, 142, 208, 0, 0, + 38, 49, 0, 1, 36, 15, 3, 239, 142, 211, 0, 4, 38, 214, 0, 4, 1, 241, 32, 35, 2, 32, 48, 37, 4, + 17, 255, 204, 2, 0, 40, 37, 143, 163, 0, 116, 143, 164, 0, 120, 2, 0, 40, 37, 0, 67, 24, 33, + 175, 163, 0, 116, 4, 17, 255, 197, 2, 32, 48, 37, 143, 184, 0, 124, 60, 5, 108, 7, 175, 162, 0, + 120, 3, 16, 200, 38, 175, 185, 0, 124, 52, 165, 137, 101, 38, 4, 0, 5, 4, 17, 255, 188, 2, 32, + 48, 37, 143, 168, 0, 128, 2, 144, 8, 43, 0, 72, 72, 33, 16, 32, 0, 7, 175, 169, 0, 128, 143, + 164, 0, 152, 2, 0, 40, 37, 4, 17, 255, 179, 2, 32, 48, 37, 16, 0, 0, 4, 175, 162, 0, 152, 143, + 170, 0, 152, 1, 80, 88, 33, 175, 171, 0, 152, 50, 130, 0, 31, 143, 174, 0, 132, 2, 226, 24, 35, + 0, 112, 104, 4, 0, 80, 96, 6, 1, 141, 168, 37, 0, 112, 200, 6, 0, 80, 192, 4, 1, 213, 120, 33, + 175, 175, 0, 132, 3, 25, 40, 37, 143, 164, 0, 144, 4, 17, 255, 160, 2, 32, 48, 37, 143, 163, 0, + 140, 175, 162, 0, 144, 2, 3, 8, 43, 80, 32, 0, 8, 143, 171, 0, 132, 143, 168, 0, 128, 2, 17, + 80, 33, 1, 3, 72, 33, 1, 42, 24, 38, 16, 0, 0, 5, 175, 163, 0, 140, 143, 171, 0, 132, 1, 112, + 96, 33, 1, 131, 24, 38, 175, 163, 0, 140, 0, 20, 22, 194, 143, 175, 0, 136, 2, 226, 24, 35, 0, + 112, 112, 6, 0, 80, 104, 4, 1, 174, 144, 37, 0, 112, 64, 4, 0, 80, 200, 6, 1, 242, 192, 33, + 175, 184, 0, 136, 3, 40, 40, 37, 143, 164, 0, 148, 4, 17, 255, 131, 2, 32, 48, 37, 36, 1, 3, + 240, 18, 33, 0, 57, 175, 162, 0, 148, 143, 164, 0, 176, 2, 64, 40, 37, 4, 17, 255, 124, 2, 32, + 48, 37, 0, 16, 30, 194, 2, 227, 80, 35, 1, 83, 88, 6, 0, 115, 72, 4, 1, 43, 40, 37, 0, 64, 32, + 37, 4, 17, 255, 116, 2, 32, 48, 37, 175, 162, 0, 176, 143, 164, 0, 172, 2, 160, 40, 37, 4, 17, + 255, 111, 2, 32, 48, 37, 50, 18, 0, 31, 2, 242, 160, 35, 2, 147, 104, 4, 2, 83, 96, 6, 1, 141, + 40, 37, 0, 64, 32, 37, 4, 17, 255, 103, 2, 32, 48, 37, 143, 169, 0, 168, 50, 99, 0, 31, 2, 80, + 112, 6, 2, 144, 120, 4, 2, 227, 200, 35, 1, 207, 56, 37, 3, 51, 64, 4, 0, 115, 192, 6, 143, + 173, 0, 156, 3, 8, 80, 37, 1, 39, 88, 33, 1, 106, 96, 33, 175, 162, 0, 172, 175, 172, 0, 168, + 2, 96, 40, 37, 2, 32, 48, 37, 4, 17, 255, 85, 1, 176, 32, 33, 143, 174, 0, 160, 175, 162, 0, + 156, 2, 96, 40, 37, 2, 32, 48, 37, 4, 17, 255, 79, 1, 208, 32, 38, 143, 175, 0, 148, 143, 184, + 0, 164, 175, 162, 0, 160, 1, 240, 200, 38, 3, 56, 64, 33, 16, 0, 255, 115, 175, 168, 0, 164, + 143, 163, 0, 116, 0, 0, 136, 37, 39, 179, 0, 116, 36, 21, 0, 16, 36, 20, 0, 1, 175, 163, 0, + 100, 175, 163, 0, 104, 175, 163, 0, 108, 175, 163, 0, 112, 142, 112, 0, 0, 143, 173, 0, 100, + 50, 2, 0, 31, 2, 226, 88, 35, 1, 112, 80, 4, 0, 80, 72, 6, 1, 42, 96, 37, 1, 172, 112, 33, 2, + 14, 8, 43, 16, 32, 0, 5, 175, 174, 0, 100, 143, 175, 0, 104, 1, 240, 200, 33, 16, 0, 0, 6, 175, + 185, 0, 104, 143, 164, 0, 104, 2, 0, 40, 37, 4, 17, 255, 44, 2, 32, 48, 37, 175, 162, 0, 104, + 50, 24, 0, 2, 0, 24, 64, 66, 50, 18, 0, 1, 85, 18, 0, 6, 143, 164, 0, 108, 143, 171, 0, 108, 1, + 112, 72, 33, 16, 0, 0, 6, 175, 169, 0, 108, 143, 164, 0, 108, 2, 0, 40, 37, 4, 17, 255, 30, 2, + 32, 48, 37, 175, 162, 0, 108, 86, 146, 0, 6, 143, 164, 0, 112, 143, 170, 0, 112, 1, 80, 104, + 38, 16, 0, 0, 6, 175, 173, 0, 112, 143, 164, 0, 112, 2, 0, 40, 37, 4, 17, 255, 19, 2, 32, 48, + 37, 175, 162, 0, 112, 38, 49, 0, 1, 22, 53, 255, 209, 38, 115, 0, 4, 143, 164, 0, 100, 143, + 165, 0, 104, 4, 17, 255, 11, 2, 32, 48, 37, 143, 172, 0, 112, 143, 174, 0, 108, 143, 176, 0, + 24, 143, 177, 0, 28, 143, 178, 0, 32, 143, 179, 0, 36, 143, 180, 0, 40, 143, 181, 0, 44, 143, + 182, 0, 48, 143, 183, 0, 52, 143, 191, 0, 60, 0, 64, 32, 37, 39, 189, 0, 224, 4, 17, 0, 8, 1, + 142, 40, 38, 0, 133, 0, 25, 0, 0, 112, 16, 172, 206, 0, 0, 0, 0, 120, 18, 172, 239, 0, 0, 3, + 224, 0, 8, 0, 0, 0, 0, 60, 11, 191, 192, 141, 104, 7, 240, 60, 10, 255, 255, 48, 132, 255, 255, + 1, 10, 64, 36, 0, 136, 32, 37, 37, 107, 7, 192, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, + 85, 32, 255, 253, 60, 9, 164, 128, 173, 100, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, + 128, 141, 104, 0, 60, 36, 9, 0, 32, 173, 101, 0, 52, 1, 9, 64, 37, 60, 9, 164, 128, 141, 41, 0, + 24, 49, 41, 0, 2, 85, 32, 255, 253, 60, 9, 164, 128, 173, 104, 0, 60, 32, 9, 0, 16, 33, 41, + 255, 255, 85, 32, 255, 255, 33, 41, 255, 255, 141, 104, 0, 60, 49, 10, 0, 128, 80, 10, 255, + 250, 32, 9, 0, 16, 36, 10, 0, 64, 1, 10, 64, 37, 60, 9, 164, 128, 141, 41, 0, 24, 49, 41, 0, 2, + 85, 32, 255, 253, 60, 9, 164, 128, 173, 104, 0, 60, 60, 11, 164, 0, 37, 107, 0, 0, 33, 107, 0, + 64, 1, 96, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +]; diff --git a/src/device/rdp.rs b/src/device/rdp.rs new file mode 100644 index 00000000..b258a013 --- /dev/null +++ b/src/device/rdp.rs @@ -0,0 +1,165 @@ +use crate::device; +use crate::ui; + +pub const DPC_START_REG: u32 = 0; +pub const DPC_END_REG: u32 = 1; +pub const DPC_CURRENT_REG: u32 = 2; +pub const DPC_STATUS_REG: u32 = 3; +pub const DPC_CLOCK_REG: u32 = 4; +pub const DPC_BUFBUSY_REG: u32 = 5; +pub const DPC_PIPEBUSY_REG: u32 = 6; +pub const DPC_TMEM_REG: u32 = 7; +pub const DPC_REGS_COUNT: u32 = 8; + +//pub const DPS_TBIST_REG: u32 = 0; +//pub const DPS_TEST_MODE_REG: u32 = 1; +//pub const DPS_BUFTEST_ADDR_REG: u32 = 2; +//pub const DPS_BUFTEST_DATA_REG: u32 = 3; +pub const DPS_REGS_COUNT: u32 = 4; + +/* DPC status - read */ +pub const DPC_STATUS_XBUS_DMEM_DMA: u32 = 1 << 0; +pub const DPC_STATUS_FREEZE: u32 = 1 << 1; +pub const DPC_STATUS_FLUSH: u32 = 1 << 2; +pub const DPC_STATUS_START_GCLK: u32 = 1 << 3; +pub const DPC_STATUS_TMEM_BUSY: u32 = 1 << 4; +pub const DPC_STATUS_PIPE_BUSY: u32 = 1 << 5; +pub const DPC_STATUS_CMD_BUSY: u32 = 1 << 6; +pub const DPC_STATUS_CBUF_READY: u32 = 1 << 7; +//pub const DPC_STATUS_DMA_BUSY: u32 = 1 << 8; +//pub const DPC_STATUS_END_VALID: u32 = 1 << 9; +pub const DPC_STATUS_START_VALID: u32 = 1 << 10; +/* DPC status - write */ +pub const DPC_CLR_XBUS_DMEM_DMA: u32 = 1 << 0; +pub const DPC_SET_XBUS_DMEM_DMA: u32 = 1 << 1; +pub const DPC_CLR_FREEZE: u32 = 1 << 2; +pub const DPC_SET_FREEZE: u32 = 1 << 3; +pub const DPC_CLR_FLUSH: u32 = 1 << 4; +pub const DPC_SET_FLUSH: u32 = 1 << 5; +pub const DPC_CLR_TMEM_CTR: u32 = 1 << 6; +pub const DPC_CLR_PIPE_CTR: u32 = 1 << 7; +pub const DPC_CLR_CMD_CTR: u32 = 1 << 8; +pub const DPC_CLR_CLOCK_CTR: u32 = 1 << 9; + +pub struct Rdp { + pub regs_dpc: [u32; DPC_REGS_COUNT as usize], + pub regs_dps: [u32; DPS_REGS_COUNT as usize], +} + +pub fn read_regs_dpc( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.rdp.regs_dpc[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn write_regs_dpc(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + DPC_CURRENT_REG | DPC_CLOCK_REG | DPC_BUFBUSY_REG | DPC_PIPEBUSY_REG | DPC_TMEM_REG => {} + DPC_STATUS_REG => update_dpc_status(device, value & mask), + DPC_START_REG => { + if (device.rdp.regs_dpc[DPC_STATUS_REG as usize] & DPC_STATUS_START_VALID) == 0 { + device::memory::masked_write_32( + &mut device.rdp.regs_dpc[reg as usize], + value & 0xFFFFF8, + mask, + ) + } + device.rdp.regs_dpc[DPC_STATUS_REG as usize] |= DPC_STATUS_START_VALID + } + DPC_END_REG => { + device::memory::masked_write_32( + &mut device.rdp.regs_dpc[reg as usize], + value & 0xFFFFF8, + mask, + ); + if (device.rdp.regs_dpc[DPC_STATUS_REG as usize] & DPC_STATUS_START_VALID) != 0 { + device.rdp.regs_dpc[DPC_CURRENT_REG as usize] = + device.rdp.regs_dpc[DPC_START_REG as usize]; + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_START_VALID + } + let timer = ui::video::process_rdp_list(&mut device.rdp.regs_dpc, &mut device.rsp.mem); + if timer != 0 { + device::events::create_event( + device, + device::events::EventType::DP, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + timer, + rdp_interrupt_event, + ) + } + } + _ => device::memory::masked_write_32(&mut device.rdp.regs_dpc[reg as usize], value, mask), + } +} + +pub fn read_regs_dps( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.rdp.regs_dps[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn write_regs_dps(device: &mut device::Device, address: u64, value: u32, mask: u32) { + device::memory::masked_write_32( + &mut device.rdp.regs_dps[((address & 0xFFFF) >> 2) as usize], + value, + mask, + ) +} + +pub fn update_dpc_status(device: &mut device::Device, w: u32) { + /* clear / set xbus_dmem_dma */ + if w & DPC_CLR_XBUS_DMEM_DMA != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_XBUS_DMEM_DMA + } + if w & DPC_SET_XBUS_DMEM_DMA != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] |= DPC_STATUS_XBUS_DMEM_DMA + } + + /* clear / set freeze */ + if w & DPC_CLR_FREEZE != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_FREEZE + } + if w & DPC_SET_FREEZE != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] |= DPC_STATUS_FREEZE + } + + /* clear / set flush */ + if w & DPC_CLR_FLUSH != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_FLUSH + } + if w & DPC_SET_FLUSH != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] |= DPC_STATUS_FLUSH + } + + if w & DPC_CLR_TMEM_CTR != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_TMEM_BUSY; + device.rdp.regs_dpc[DPC_TMEM_REG as usize] = 0 + } + if w & DPC_CLR_PIPE_CTR != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_PIPE_BUSY; + device.rdp.regs_dpc[DPC_PIPEBUSY_REG as usize] = 0 + } + if w & DPC_CLR_CMD_CTR != 0 { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] &= !DPC_STATUS_CMD_BUSY; + device.rdp.regs_dpc[DPC_BUFBUSY_REG as usize] = 0 + } + + /* clear clock counter */ + if w & DPC_CLR_CLOCK_CTR != 0 { + device.rdp.regs_dpc[DPC_CLOCK_REG as usize] = 0 + } +} + +pub fn init(device: &mut device::Device) { + device.rdp.regs_dpc[DPC_STATUS_REG as usize] |= + DPC_STATUS_START_GCLK | DPC_STATUS_PIPE_BUSY | DPC_STATUS_CBUF_READY; +} + +pub fn rdp_interrupt_event(device: &mut device::Device) { + ui::video::rdp_full_sync(); + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_DP) +} diff --git a/src/device/rdram.rs b/src/device/rdram.rs new file mode 100644 index 00000000..86b2eb09 --- /dev/null +++ b/src/device/rdram.rs @@ -0,0 +1,86 @@ +use crate::device; +use std::alloc::{alloc, Layout}; + +pub const RDRAM_MASK: usize = 0xFFFFFF; + +pub const RDRAM_SIZE: usize = 0x800000; // 8MB + +pub struct Rdram { + pub mem: Vec, +} + +pub fn read_mem_fast( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let masked_address = address as usize & RDRAM_MASK; + return u32::from_ne_bytes( + device.rdram.mem[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); +} + +pub fn read_mem( + device: &mut device::Device, + address: u64, + access_size: device::memory::AccessSize, +) -> u32 { + device::cop0::add_cycles( + device, + rdram_calculate_cycles(access_size as u64) / (access_size as u64 / 4), + ); + let masked_address = address as usize & RDRAM_MASK; + if address < RDRAM_SIZE as u64 { + return u32::from_ne_bytes( + device.rdram.mem[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + } else { + return 0; + } +} + +pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { + if address < RDRAM_SIZE as u64 { + let mut data = u32::from_ne_bytes( + device.rdram.mem[address as usize..(address + 4) as usize] + .try_into() + .unwrap(), + ); + device::memory::masked_write_32(&mut data, value, mask); + device.rdram.mem[address as usize..(address + 4) as usize] + .copy_from_slice(&data.to_ne_bytes()); + } +} + +pub fn read_regs( + _device: &mut device::Device, + _address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + panic!("rdram read reg"); +} + +pub fn write_regs(_device: &mut device::Device, _address: u64, _value: u32, _mask: u32) { + panic!("rdram write reg"); +} + +pub fn init(device: &mut device::Device) -> (*mut u8, usize) { + let alignment = 64 * 1024; + let layout = Layout::from_size_align(RDRAM_SIZE, alignment).expect("Invalid layout"); + let ptr = unsafe { alloc(layout) }; + device.rdram.mem = unsafe { Vec::from_raw_parts(ptr, RDRAM_SIZE, RDRAM_SIZE) }; + + // hack, skip RDRAM initialization + let data: u32 = RDRAM_SIZE as u32; + device.rdram.mem[device.cart.rdram_size_offset..device.cart.rdram_size_offset + 4] + .copy_from_slice(&data.to_ne_bytes()); + return (ptr, RDRAM_SIZE); +} + +pub fn rdram_calculate_cycles(length: u64) -> u64 { + return 31 + (length / 3); // https://hcs64.com/dma.html, https://github.com/rasky/n64-systembench +} diff --git a/src/device/ri.rs b/src/device/ri.rs new file mode 100644 index 00000000..aff8efc6 --- /dev/null +++ b/src/device/ri.rs @@ -0,0 +1,34 @@ +use crate::device; + +//pub const RI_MODE_REG: u32 = 0; +//pub const RI_CONFIG_REG: u32 = 1; +//pub const RI_CURRENT_LOAD_REG: u32 = 2; +pub const RI_SELECT_REG: u32 = 3; +//pub const RI_REFRESH_REG: u32 = 4; +//pub const RI_LATENCY_REG: u32 = 5; +//pub const RI_ERROR_REG: u32 = 6; +//pub const RI_WERROR_REG: u32 = 7; +pub const RI_REGS_COUNT: u32 = 8; + +pub struct Ri { + pub regs: [u32; RI_REGS_COUNT as usize], +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + if ((address & 0xFFFF) >> 2) as u32 == RI_SELECT_REG { + return 0x14; // hack, skip RDRAM initialization + } + return device.ri.regs[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + device::memory::masked_write_32( + &mut device.ri.regs[((address & 0xFFFF) >> 2) as usize], + value, + mask, + ); +} diff --git a/src/device/rsp_cpu.rs b/src/device/rsp_cpu.rs new file mode 100644 index 00000000..bda2de31 --- /dev/null +++ b/src/device/rsp_cpu.rs @@ -0,0 +1,429 @@ +use crate::device; + +pub struct BranchState { + pub state: device::cpu::State, + pub pc: u32, +} + +pub struct Cpu { + pub branch_state: BranchState, + pub broken: bool, + pub halted: bool, + pub sync_point: bool, + pub gpr: [u32; 32], + pub vpr: [u128; 32], + pub reciprocals: [u16; 512], + pub inverse_square_roots: [u16; 512], + pub vcol: std::arch::x86_64::__m128i, + pub vcoh: std::arch::x86_64::__m128i, + pub vccl: std::arch::x86_64::__m128i, + pub vcch: std::arch::x86_64::__m128i, + pub vce: std::arch::x86_64::__m128i, + pub accl: std::arch::x86_64::__m128i, + pub accm: std::arch::x86_64::__m128i, + pub acch: std::arch::x86_64::__m128i, + pub divdp: bool, + pub divin: i16, + pub divout: i16, + pub special_instrs: [fn(&mut device::Device, u32); 48], + pub regimm_instrs: [fn(&mut device::Device, u32); 18], + pub cop0_instrs: [fn(&mut device::Device, u32); 5], + pub cop2_instrs: [fn(&mut device::Device, u32); 32], + pub lwc2_instrs: [fn(&mut device::Device, u32); 12], + pub swc2_instrs: [fn(&mut device::Device, u32); 12], + pub instrs: [fn(&mut device::Device, u32); 64], + pub vec_instrs: [fn(&mut device::Device, u32); 64], +} + +pub fn in_delay_slot(device: &mut device::Device) -> bool { + return device.rsp.cpu.branch_state.state == device::cpu::State::DelaySlotTaken + || device.rsp.cpu.branch_state.state == device::cpu::State::DelaySlotNotTaken; +} + +pub fn in_delay_slot_taken(device: &mut device::Device) -> bool { + return device.rsp.cpu.branch_state.state == device::cpu::State::DelaySlotTaken; +} + +pub fn run(device: &mut device::Device) -> u64 { + device.rsp.cpu.broken = false; + let mut cycle_counter = 0; + while !device.rsp.cpu.halted && !device.rsp.cpu.sync_point { + device.rsp.cpu.gpr[0] = 0; // gpr 0 is read only + let offset = 0x1000 + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] as usize; + let opcode = u32::from_be_bytes(device.rsp.mem[offset..offset + 4].try_into().unwrap()); + execute_opcode(device, opcode)(device, opcode); + match device.rsp.cpu.branch_state.state { + device::cpu::State::Step => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] += 4 + } + device::cpu::State::Take => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] += 4; + device.rsp.cpu.branch_state.state = device::cpu::State::DelaySlotTaken + } + device::cpu::State::NotTaken => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] += 4; + device.rsp.cpu.branch_state.state = device::cpu::State::DelaySlotNotTaken + } + device::cpu::State::DelaySlotTaken => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] = + device.rsp.cpu.branch_state.pc; + device.rsp.cpu.branch_state.state = device::cpu::State::Step + } + device::cpu::State::DelaySlotNotTaken => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] += 4; + device.rsp.cpu.branch_state.state = device::cpu::State::Step + } + device::cpu::State::Discard => { + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] += 8; + device.rsp.cpu.branch_state.state = device::cpu::State::Step + } + device::cpu::State::Exception => { + device.rsp.cpu.branch_state.state = device::cpu::State::Step + } + } + device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] &= 0xFFC; + + cycle_counter += 1; + + if device.rsp.cpu.broken && device.rsp.cpu.branch_state.state == device::cpu::State::Step { + break; + } + } + return (cycle_counter as f64 * 1.5) as u64; // converting RCP clock to CPU clock +} + +pub fn execute_opcode(device: &mut device::Device, opcode: u32) -> fn(&mut device::Device, u32) { + match opcode >> 26 { + 0 => { + // SPECIAL + return device.rsp.cpu.special_instrs[(opcode & 0x3F) as usize]; + } + 1 => { + // REGIMM + return device.rsp.cpu.regimm_instrs[((opcode >> 16) & 0x1F) as usize]; + } + 16 => { + // COP0 + return device.rsp.cpu.cop0_instrs[((opcode >> 21) & 0x1F) as usize]; + } + 18 => { + // COP2 + return device.rsp.cpu.cop2_instrs[((opcode >> 21) & 0x1F) as usize]; + } + 50 => { + // LWC2 + return device.rsp.cpu.lwc2_instrs[((opcode >> 11) & 0x1F) as usize]; + } + 58 => { + // SWC2 + return device.rsp.cpu.swc2_instrs[((opcode >> 11) & 0x1F) as usize]; + } + _ => return device.rsp.cpu.instrs[(opcode >> 26) as usize], + } +} + +pub fn init(device: &mut device::Device) { + device.rsp.cpu.reciprocals[0] = !0 as u16; + let mut index = 1; + while index < 512 { + let a = (index + 512) as u64; + let b = ((1 as u64) << 34) / a; + device.rsp.cpu.reciprocals[index] = ((b + 1) >> 8) as u16; + index += 1; + } + + index = 0; + while index < 512 { + let mut shift = 0; + if index % 2 == 1 { + shift = 1 + } + let a = ((index + 512) >> shift) as u64; + let mut b = (1 << 17) as u64; + //find the largest b where b < 1.0 / sqrt(a) + while a * (b + 1) * (b + 1) < ((1 as u64) << 44) { + b += 1; + } + device.rsp.cpu.inverse_square_roots[index] = (b >> 1) as u16; + index += 1; + } + + device.rsp.cpu.instrs = [ + device::rsp_su_instructions::reserved, // SPECIAL + device::rsp_su_instructions::reserved, // REGIMM + device::rsp_su_instructions::j, // 2 + device::rsp_su_instructions::jal, // 3 + device::rsp_su_instructions::beq, // 4 + device::rsp_su_instructions::bne, // 5 + device::rsp_su_instructions::blez, // 6 + device::rsp_su_instructions::bgtz, // 7 + device::rsp_su_instructions::addi, // 8 + device::rsp_su_instructions::addiu, // 9 + device::rsp_su_instructions::slti, // 10 + device::rsp_su_instructions::sltiu, // 11 + device::rsp_su_instructions::andi, // 12 + device::rsp_su_instructions::ori, // 13 + device::rsp_su_instructions::xori, // 14 + device::rsp_su_instructions::lui, // 15 + device::rsp_su_instructions::reserved, // COP0 + device::rsp_su_instructions::reserved, // COP1 + device::rsp_su_instructions::reserved, // COP2 + device::rsp_su_instructions::reserved, // 19 + device::rsp_su_instructions::reserved, // 20 + device::rsp_su_instructions::reserved, // 21 + device::rsp_su_instructions::reserved, // 22 + device::rsp_su_instructions::reserved, // 23 + device::rsp_su_instructions::reserved, // 24 + device::rsp_su_instructions::reserved, // 25 + device::rsp_su_instructions::reserved, // 26 + device::rsp_su_instructions::reserved, // 27 + device::rsp_su_instructions::reserved, // 28 + device::rsp_su_instructions::reserved, // 29 + device::rsp_su_instructions::reserved, // 30 + device::rsp_su_instructions::reserved, // 31 + device::rsp_su_instructions::lb, // 32 + device::rsp_su_instructions::lh, // 33 + device::rsp_su_instructions::reserved, // 34 + device::rsp_su_instructions::lw, // 35 + device::rsp_su_instructions::lbu, // 36 + device::rsp_su_instructions::lhu, // 37 + device::rsp_su_instructions::reserved, // 38 + device::rsp_su_instructions::lwu, // 39 + device::rsp_su_instructions::sb, // 40 + device::rsp_su_instructions::sh, // 41 + device::rsp_su_instructions::reserved, // 42 + device::rsp_su_instructions::sw, // 43 + device::rsp_su_instructions::reserved, // 44 + device::rsp_su_instructions::reserved, // 45 + device::rsp_su_instructions::reserved, // 46 + device::rsp_su_instructions::reserved, // 47 + device::rsp_su_instructions::reserved, // 48 + device::rsp_su_instructions::reserved, // 49 + device::rsp_su_instructions::reserved, // lwc2 + device::rsp_su_instructions::reserved, // 51 + device::rsp_su_instructions::reserved, // 52 + device::rsp_su_instructions::reserved, // 53 + device::rsp_su_instructions::reserved, // 54 + device::rsp_su_instructions::reserved, // 55 + device::rsp_su_instructions::reserved, // 56 + device::rsp_su_instructions::reserved, // 57 + device::rsp_su_instructions::reserved, // swc2 + device::rsp_su_instructions::reserved, // 59 + device::rsp_su_instructions::reserved, // 60 + device::rsp_su_instructions::reserved, // 61 + device::rsp_su_instructions::reserved, // 62 + device::rsp_su_instructions::reserved, // 63 + ]; + + device.rsp.cpu.special_instrs = [ + device::rsp_su_instructions::sll, // 0 + device::rsp_su_instructions::reserved, // 1 + device::rsp_su_instructions::srl, // 2 + device::rsp_su_instructions::sra, // 3 + device::rsp_su_instructions::sllv, // 4 + device::rsp_su_instructions::reserved, // 5 + device::rsp_su_instructions::srlv, // 6 + device::rsp_su_instructions::srav, // 7 + device::rsp_su_instructions::jr, // 8 + device::rsp_su_instructions::jalr, // 9 + device::rsp_su_instructions::reserved, // 10 + device::rsp_su_instructions::reserved, // 11 + device::rsp_su_instructions::reserved, // 12 + device::rsp_su_instructions::break_, // 13 + device::rsp_su_instructions::reserved, // 14 + device::rsp_su_instructions::reserved, // 15 + device::rsp_su_instructions::reserved, // 16 + device::rsp_su_instructions::reserved, // 17 + device::rsp_su_instructions::reserved, // 18 + device::rsp_su_instructions::reserved, // 19 + device::rsp_su_instructions::reserved, // 20 + device::rsp_su_instructions::reserved, // 21 + device::rsp_su_instructions::reserved, // 22 + device::rsp_su_instructions::reserved, // 23 + device::rsp_su_instructions::reserved, // 24 + device::rsp_su_instructions::reserved, // 25 + device::rsp_su_instructions::reserved, // 26 + device::rsp_su_instructions::reserved, // 27 + device::rsp_su_instructions::reserved, // 28 + device::rsp_su_instructions::reserved, // 29 + device::rsp_su_instructions::reserved, // 30 + device::rsp_su_instructions::reserved, // 31 + device::rsp_su_instructions::add, // 32 + device::rsp_su_instructions::addu, // 33 + device::rsp_su_instructions::sub, // 34 + device::rsp_su_instructions::subu, // 35 + device::rsp_su_instructions::and, // 36 + device::rsp_su_instructions::or, // 37 + device::rsp_su_instructions::xor, // 38 + device::rsp_su_instructions::nor, // 39 + device::rsp_su_instructions::reserved, // 40 + device::rsp_su_instructions::reserved, // 41 + device::rsp_su_instructions::slt, // 42 + device::rsp_su_instructions::sltu, // 43 + device::rsp_su_instructions::reserved, // 44 + device::rsp_su_instructions::reserved, // 45 + device::rsp_su_instructions::reserved, // 46 + device::rsp_su_instructions::reserved, // 47 + ]; + + device.rsp.cpu.regimm_instrs = [ + device::rsp_su_instructions::bltz, // 0 + device::rsp_su_instructions::bgez, // 1 + device::rsp_su_instructions::reserved, // 2 + device::rsp_su_instructions::reserved, // 3 + device::rsp_su_instructions::reserved, // 4 + device::rsp_su_instructions::reserved, // 5 + device::rsp_su_instructions::reserved, // 6 + device::rsp_su_instructions::reserved, // 7 + device::rsp_su_instructions::reserved, // 8 + device::rsp_su_instructions::reserved, // 9 + device::rsp_su_instructions::reserved, // 10 + device::rsp_su_instructions::reserved, // 11 + device::rsp_su_instructions::reserved, // 12 + device::rsp_su_instructions::reserved, // 13 + device::rsp_su_instructions::reserved, // 14 + device::rsp_su_instructions::reserved, // 15 + device::rsp_su_instructions::bltzal, // 16 + device::rsp_su_instructions::bgezal, // 17 + ]; + + device.rsp.cpu.cop0_instrs = [ + device::rsp_su_instructions::mfc0, // 0 + device::rsp_su_instructions::reserved, // 1 + device::rsp_su_instructions::reserved, // 2 + device::rsp_su_instructions::reserved, // 3 + device::rsp_su_instructions::mtc0, // 4 + ]; + + device.rsp.cpu.cop2_instrs = [ + device::rsp_su_instructions::mfc2, // 0 + device::rsp_su_instructions::reserved, // 1 + device::rsp_su_instructions::cfc2, // 2 + device::rsp_su_instructions::reserved, // 3 + device::rsp_su_instructions::mtc2, // 4 + device::rsp_su_instructions::reserved, // 5 + device::rsp_su_instructions::ctc2, // 6 + device::rsp_su_instructions::reserved, // 7 + device::rsp_su_instructions::reserved, // 8 + device::rsp_su_instructions::reserved, // 9 + device::rsp_su_instructions::reserved, // 10 + device::rsp_su_instructions::reserved, // 11 + device::rsp_su_instructions::reserved, // 12 + device::rsp_su_instructions::reserved, // 13 + device::rsp_su_instructions::reserved, // 14 + device::rsp_su_instructions::reserved, // 15 + device::rsp_vu_instructions::execute_vec, // 16 + device::rsp_vu_instructions::execute_vec, // 17 + device::rsp_vu_instructions::execute_vec, // 18 + device::rsp_vu_instructions::execute_vec, // 19 + device::rsp_vu_instructions::execute_vec, // 20 + device::rsp_vu_instructions::execute_vec, // 21 + device::rsp_vu_instructions::execute_vec, // 22 + device::rsp_vu_instructions::execute_vec, // 23 + device::rsp_vu_instructions::execute_vec, // 24 + device::rsp_vu_instructions::execute_vec, // 25 + device::rsp_vu_instructions::execute_vec, // 26 + device::rsp_vu_instructions::execute_vec, // 27 + device::rsp_vu_instructions::execute_vec, // 28 + device::rsp_vu_instructions::execute_vec, // 29 + device::rsp_vu_instructions::execute_vec, // 30 + device::rsp_vu_instructions::execute_vec, // 31 + ]; + + device.rsp.cpu.lwc2_instrs = [ + device::rsp_su_instructions::lbv, // 0 + device::rsp_su_instructions::lsv, // 1 + device::rsp_su_instructions::llv, // 2 + device::rsp_su_instructions::ldv, // 3 + device::rsp_su_instructions::lqv, // 4 + device::rsp_su_instructions::lrv, // 5 + device::rsp_su_instructions::lpv, // 6 + device::rsp_su_instructions::luv, // 7 + device::rsp_su_instructions::lhv, // 8 + device::rsp_su_instructions::lfv, // 9 + device::rsp_su_instructions::lwv, // 10 + device::rsp_su_instructions::ltv, // 11 + ]; + + device.rsp.cpu.swc2_instrs = [ + device::rsp_su_instructions::sbv, // 0 + device::rsp_su_instructions::ssv, // 1 + device::rsp_su_instructions::slv, // 2 + device::rsp_su_instructions::sdv, // 3 + device::rsp_su_instructions::sqv, // 4 + device::rsp_su_instructions::srv, // 5 + device::rsp_su_instructions::spv, // 6 + device::rsp_su_instructions::suv, // 7 + device::rsp_su_instructions::shv, // 8 + device::rsp_su_instructions::sfv, // 9 + device::rsp_su_instructions::swv, // 10 + device::rsp_su_instructions::stv, // 11 + ]; + + device.rsp.cpu.vec_instrs = [ + device::rsp_vu_instructions::vmulf, // 0 + device::rsp_vu_instructions::vmulu, // 1 + device::rsp_vu_instructions::vrndp, // 2 + device::rsp_vu_instructions::vmulq, // 3 + device::rsp_vu_instructions::vmudl, // 4 + device::rsp_vu_instructions::vmudm, // 5 + device::rsp_vu_instructions::vmudn, // 6 + device::rsp_vu_instructions::vmudh, // 7 + device::rsp_vu_instructions::vmacf, // 8 + device::rsp_vu_instructions::vmacu, // 9 + device::rsp_vu_instructions::vrndn, // 10 + device::rsp_vu_instructions::vmacq, // 11 + device::rsp_vu_instructions::vmadl, // 12 + device::rsp_vu_instructions::vmadm, // 13 + device::rsp_vu_instructions::vmadn, // 14 + device::rsp_vu_instructions::vmadh, // 15 + device::rsp_vu_instructions::vadd, // 16 + device::rsp_vu_instructions::vsub, // 17 + device::rsp_vu_instructions::vzero, // 18 + device::rsp_vu_instructions::vabs, // 19 + device::rsp_vu_instructions::vaddc, // 20 + device::rsp_vu_instructions::vsubc, // 21 + device::rsp_vu_instructions::vzero, // 22 + device::rsp_vu_instructions::vzero, // 23 + device::rsp_vu_instructions::vzero, // 24 + device::rsp_vu_instructions::vzero, // 25 + device::rsp_vu_instructions::vzero, // 26 + device::rsp_vu_instructions::vzero, // 27 + device::rsp_vu_instructions::vzero, // 28 + device::rsp_vu_instructions::vsar, // 29 + device::rsp_vu_instructions::vzero, // 30 + device::rsp_vu_instructions::vzero, // 31 + device::rsp_vu_instructions::vlt, // 32 + device::rsp_vu_instructions::veq, // 33 + device::rsp_vu_instructions::vne, // 34 + device::rsp_vu_instructions::vge, // 35 + device::rsp_vu_instructions::vcl, // 36 + device::rsp_vu_instructions::vch, // 37 + device::rsp_vu_instructions::vcr, // 38 + device::rsp_vu_instructions::vmrg, // 39 + device::rsp_vu_instructions::vand, // 40 + device::rsp_vu_instructions::vnand, // 41 + device::rsp_vu_instructions::vor, // 42 + device::rsp_vu_instructions::vnor, // 43 + device::rsp_vu_instructions::vxor, // 44 + device::rsp_vu_instructions::vnxor, // 45 + device::rsp_vu_instructions::vzero, // 46 + device::rsp_vu_instructions::vzero, // 47 + device::rsp_vu_instructions::vrcp, // 48 + device::rsp_vu_instructions::vrcpl, // 49 + device::rsp_vu_instructions::vrcph, // 50 + device::rsp_vu_instructions::vmov, // 51 + device::rsp_vu_instructions::vrsq, // 52 + device::rsp_vu_instructions::vrsql, // 53 + device::rsp_vu_instructions::vrsqh, // 54 + device::rsp_vu_instructions::vnop, // 55 + device::rsp_vu_instructions::vzero, // 56 + device::rsp_vu_instructions::vzero, // 57 + device::rsp_vu_instructions::vzero, // 58 + device::rsp_vu_instructions::vzero, // 59 + device::rsp_vu_instructions::vzero, // 60 + device::rsp_vu_instructions::vzero, // 61 + device::rsp_vu_instructions::vzero, // 62 + device::rsp_vu_instructions::vnop, // 63 + ] +} diff --git a/src/device/rsp_interface.rs b/src/device/rsp_interface.rs new file mode 100644 index 00000000..6969f55d --- /dev/null +++ b/src/device/rsp_interface.rs @@ -0,0 +1,432 @@ +use crate::device; + +pub const SP_MEM_ADDR_REG: u32 = 0; +pub const SP_DRAM_ADDR_REG: u32 = 1; +pub const SP_RD_LEN_REG: u32 = 2; +pub const SP_WR_LEN_REG: u32 = 3; +pub const SP_STATUS_REG: u32 = 4; +pub const SP_DMA_FULL_REG: u32 = 5; +pub const SP_DMA_BUSY_REG: u32 = 6; +pub const SP_SEMAPHORE_REG: u32 = 7; +pub const SP_REGS_COUNT: u32 = 8; + +pub const SP_PC_REG: u32 = 0; +//pub const SP_IBIST_REG: u32 = 1; +pub const SP_REGS2_COUNT: u32 = 2; + +/* SP_STATUS - read */ +pub const SP_STATUS_HALT: u32 = 1 << 0; +pub const SP_STATUS_BROKE: u32 = 1 << 1; +pub const SP_STATUS_DMA_BUSY: u32 = 1 << 2; +pub const SP_STATUS_DMA_FULL: u32 = 1 << 3; +//pub const SP_STATUS_IO_FULL: u32 = 1 << 4; +pub const SP_STATUS_SSTEP: u32 = 1 << 5; +pub const SP_STATUS_INTR_BREAK: u32 = 1 << 6; +pub const SP_STATUS_SIG0: u32 = 1 << 7; +pub const SP_STATUS_SIG1: u32 = 1 << 8; +pub const SP_STATUS_SIG2: u32 = 1 << 9; +pub const SP_STATUS_SIG3: u32 = 1 << 10; +pub const SP_STATUS_SIG4: u32 = 1 << 11; +pub const SP_STATUS_SIG5: u32 = 1 << 12; +pub const SP_STATUS_SIG6: u32 = 1 << 13; +pub const SP_STATUS_SIG7: u32 = 1 << 14; + +/* SP_STATUS - write */ +pub const SP_CLR_HALT: u32 = 1 << 0; +pub const SP_SET_HALT: u32 = 1 << 1; +pub const SP_CLR_BROKE: u32 = 1 << 2; +pub const SP_CLR_INTR: u32 = 1 << 3; +pub const SP_SET_INTR: u32 = 1 << 4; +pub const SP_CLR_SSTEP: u32 = 1 << 5; +pub const SP_SET_SSTEP: u32 = 1 << 6; +pub const SP_CLR_INTR_BREAK: u32 = 1 << 7; +pub const SP_SET_INTR_BREAK: u32 = 1 << 8; +pub const SP_CLR_SIG0: u32 = 1 << 9; +pub const SP_SET_SIG0: u32 = 1 << 10; +pub const SP_CLR_SIG1: u32 = 1 << 11; +pub const SP_SET_SIG1: u32 = 1 << 12; +pub const SP_CLR_SIG2: u32 = 1 << 13; +pub const SP_SET_SIG2: u32 = 1 << 14; +pub const SP_CLR_SIG3: u32 = 1 << 15; +pub const SP_SET_SIG3: u32 = 1 << 16; +pub const SP_CLR_SIG4: u32 = 1 << 17; +pub const SP_SET_SIG4: u32 = 1 << 18; +pub const SP_CLR_SIG5: u32 = 1 << 19; +pub const SP_SET_SIG5: u32 = 1 << 20; +pub const SP_CLR_SIG6: u32 = 1 << 21; +pub const SP_SET_SIG6: u32 = 1 << 22; +pub const SP_CLR_SIG7: u32 = 1 << 23; +pub const SP_SET_SIG7: u32 = 1 << 24; + +pub const RSP_MEM_MASK: usize = 0x1FFF; + +#[derive(PartialEq, Copy, Clone)] +pub enum DmaDir { + None, + Write, + Read, +} + +#[derive(Copy, Clone)] +pub struct RspDma { + pub dir: DmaDir, + pub length: u32, + pub memaddr: u32, + pub dramaddr: u32, +} + +pub struct Rsp { + pub cpu: device::rsp_cpu::Cpu, + pub regs: [u32; SP_REGS_COUNT as usize], + pub regs2: [u32; SP_REGS2_COUNT as usize], + pub mem: [u8; 0x2000], + pub fifo: [RspDma; 2], +} + +pub fn read_mem_fast( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let masked_address = address as usize & RSP_MEM_MASK; + return u32::from_be_bytes( + device.rsp.mem[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); +} + +pub fn read_mem( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let masked_address = address as usize & RSP_MEM_MASK; + return u32::from_be_bytes( + device.rsp.mem[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); +} + +pub fn write_mem(device: &mut device::Device, address: u64, value: u32, _mask: u32) { + let masked_address = address as usize & RSP_MEM_MASK; + let mut data = u32::from_be_bytes( + device.rsp.mem[masked_address..masked_address + 4] + .try_into() + .unwrap(), + ); + device::memory::masked_write_32(&mut data, value, 0xFFFFFFFF); + device.rsp.mem[masked_address..masked_address + 4].copy_from_slice(&data.to_be_bytes()) + + // SH/SB are broken: They overwrite the whole 32 bit, filling everything that isn't written with zeroes +} + +pub fn do_dma(device: &mut device::Device, dma: RspDma) { + let l = dma.length; + + let length = ((l & 0xfff) | 7) + 1; + let count = ((l >> 12) & 0xff) + 1; + let skip = (l >> 20) & 0xff8; + + let mut mem_addr = dma.memaddr & 0xff8; + let mut dram_addr = dma.dramaddr & 0xfffff8; + let offset = dma.memaddr & 0x1000; + + if dma.dir == DmaDir::Read { + let mut j = 0; + while j < count { + let mut i = 0; + while i < length { + device.rdram.mem[dram_addr as usize ^ device.byte_swap] = + device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize]; + mem_addr += 1; + dram_addr += 1; + i += 1; + } + dram_addr += skip; + j += 1; + } + } else { + let mut j = 0; + while j < count { + let mut i = 0; + while i < length { + device.rsp.mem[(offset + (mem_addr & 0xFFF)) as usize] = + device.rdram.mem[dram_addr as usize ^ device.byte_swap]; + mem_addr += 1; + dram_addr += 1; + i += 1; + } + dram_addr += skip; + j += 1; + } + } + device.rsp.regs[SP_MEM_ADDR_REG as usize] = (mem_addr & 0xfff) + (dma.memaddr & 0x1000); + device.rsp.regs[SP_DRAM_ADDR_REG as usize] = dram_addr; + device.rsp.regs[SP_RD_LEN_REG as usize] = 0xff8; + device.rsp.regs[SP_WR_LEN_REG as usize] = 0xff8; + + device::events::create_event( + device, + device::events::EventType::SPDma, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + + device::rdram::rdram_calculate_cycles((count * length) as u64) + + 9, + device::rsp_interface::fifo_pop, + ); +} + +pub fn fifo_push(device: &mut device::Device, dir: DmaDir) { + if device.rsp.regs[SP_DMA_FULL_REG as usize] != 0 { + panic!("RSP DMA already full") + } + + if device.rsp.regs[SP_DMA_BUSY_REG as usize] != 0 { + device.rsp.fifo[1].dir = dir; + if dir == DmaDir::Read { + device.rsp.fifo[1].length = device.rsp.regs[SP_WR_LEN_REG as usize] + } else { + device.rsp.fifo[1].length = device.rsp.regs[SP_RD_LEN_REG as usize] + } + device.rsp.fifo[1].memaddr = device.rsp.regs[SP_MEM_ADDR_REG as usize]; + device.rsp.fifo[1].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG as usize]; + device.rsp.regs[SP_DMA_FULL_REG as usize] = 1; + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_DMA_FULL + } else { + device.rsp.fifo[0].dir = dir; + if dir == DmaDir::Read { + device.rsp.fifo[0].length = device.rsp.regs[SP_WR_LEN_REG as usize] + } else { + device.rsp.fifo[0].length = device.rsp.regs[SP_RD_LEN_REG as usize] + } + device.rsp.fifo[0].memaddr = device.rsp.regs[SP_MEM_ADDR_REG as usize]; + device.rsp.fifo[0].dramaddr = device.rsp.regs[SP_DRAM_ADDR_REG as usize]; + device.rsp.regs[SP_DMA_BUSY_REG as usize] = 1; + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_DMA_BUSY; + + do_dma(device, device.rsp.fifo[0]) + } +} + +pub fn fifo_pop(device: &mut device::Device) { + if device.rsp.regs[SP_DMA_FULL_REG as usize] != 0 { + device.rsp.fifo[0].dir = device.rsp.fifo[1].dir; + device.rsp.fifo[0].length = device.rsp.fifo[1].length; + device.rsp.fifo[0].memaddr = device.rsp.fifo[1].memaddr; + device.rsp.fifo[0].dramaddr = device.rsp.fifo[1].dramaddr; + device.rsp.regs[SP_DMA_FULL_REG as usize] = 0; + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_DMA_FULL; + + do_dma(device, device.rsp.fifo[0]) + } else { + device.rsp.regs[SP_DMA_BUSY_REG as usize] = 0; + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_DMA_BUSY + } +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + SP_SEMAPHORE_REG => { + let value = device.rsp.regs[reg as usize]; + device.rsp.regs[reg as usize] = 1; + return value; + } + _ => return device.rsp.regs[reg as usize], + } +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + SP_STATUS_REG => update_sp_status(device, value), + SP_RD_LEN_REG => { + device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask); + fifo_push(device, DmaDir::Write) + } + SP_WR_LEN_REG => { + device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask); + fifo_push(device, DmaDir::Read) + } + SP_SEMAPHORE_REG => { + device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], 0, mask) + } + _ => { + device::memory::masked_write_32(&mut device.rsp.regs[reg as usize], value, mask); + } + } +} + +pub fn read_regs2( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.rsp.regs2[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn write_regs2(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + SP_PC_REG => { + device::memory::masked_write_32( + &mut device.rsp.regs2[reg as usize], + value & 0xFFC, + mask, + ); + } + _ => device::memory::masked_write_32(&mut device.rsp.regs2[reg as usize], value, mask), + } +} + +pub fn update_sp_status(device: &mut device::Device, w: u32) { + let was_halted = device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_HALT != 0; + + /* clear / set halt */ + if w & SP_CLR_HALT != 0 && w & SP_SET_HALT == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_HALT + } + if w & SP_SET_HALT != 0 && w & SP_CLR_HALT == 0 { + device::events::remove_event(device, device::events::EventType::SP); + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT + } + + /* clear broke */ + if w & SP_CLR_BROKE != 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_BROKE + } + + /* clear SP interrupt */ + if (w & SP_CLR_INTR) != 0 && (w & SP_SET_INTR) == 0 { + device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_SP) + } + /* set SP interrupt */ + if (w & SP_SET_INTR) != 0 && (w & SP_CLR_INTR) == 0 { + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP) + } + + /* clear / set single step */ + if w & SP_CLR_SSTEP != 0 && w & SP_SET_SSTEP == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SSTEP + } + if w & SP_SET_SSTEP != 0 && w & SP_CLR_SSTEP == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SSTEP + } + + /* clear / set interrupt on break */ + if w & SP_CLR_INTR_BREAK != 0 && w & SP_SET_INTR_BREAK == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_INTR_BREAK + } + if w & SP_SET_INTR_BREAK != 0 && w & SP_CLR_INTR_BREAK == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_INTR_BREAK + } + + /* clear / set signal 0 */ + if w & SP_CLR_SIG0 != 0 && w & SP_SET_SIG0 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG0 + } + if w & SP_SET_SIG0 != 0 && w & SP_CLR_SIG0 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG0 + } + + /* clear / set signal 1 */ + if w & SP_CLR_SIG1 != 0 && w & SP_SET_SIG1 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG1 + } + if w & SP_SET_SIG1 != 0 && w & SP_CLR_SIG1 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG1 + } + + /* clear / set signal 2 */ + if w & SP_CLR_SIG2 != 0 && w & SP_SET_SIG2 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG2 + } + if w & SP_SET_SIG2 != 0 && w & SP_CLR_SIG2 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG2 + } + + /* clear / set signal 3 */ + if w & SP_CLR_SIG3 != 0 && w & SP_SET_SIG3 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG3 + } + if w & SP_SET_SIG3 != 0 && w & SP_CLR_SIG3 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG3 + } + + /* clear / set signal 4 */ + if w & SP_CLR_SIG4 != 0 && w & SP_SET_SIG4 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG4 + } + if w & SP_SET_SIG4 != 0 && w & SP_CLR_SIG4 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG4 + } + + /* clear / set signal 5 */ + if w & SP_CLR_SIG5 != 0 && w & SP_SET_SIG5 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG5 + } + if w & SP_SET_SIG5 != 0 && w & SP_CLR_SIG5 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG5 + } + + /* clear / set signal 6 */ + if w & SP_CLR_SIG6 != 0 && w & SP_SET_SIG6 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG6 + } + if w & SP_SET_SIG6 != 0 && w & SP_CLR_SIG6 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG6 + } + + /* clear / set signal 7 */ + if w & SP_CLR_SIG7 != 0 && w & SP_SET_SIG7 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] &= !SP_STATUS_SIG7 + } + if w & SP_SET_SIG7 != 0 && w & SP_CLR_SIG7 == 0 { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_SIG7 + } + + if device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_HALT == 0 && was_halted { + device.rsp.cpu.broken = false; + device.rsp.cpu.halted = false; + device.rsp.cpu.sync_point = false; + do_task(device); + } +} + +pub fn do_task(device: &mut device::Device) { + let timer = device::rsp_cpu::run(device); + + device::events::create_event( + device, + device::events::EventType::SP, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + timer, + rsp_event, + ) +} + +pub fn rsp_event(device: &mut device::Device) { + if device.rsp.cpu.broken { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT | SP_STATUS_BROKE; + + if device.rsp.regs[SP_STATUS_REG as usize] & SP_STATUS_INTR_BREAK != 0 { + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SP) + } + return; + } + if device.rsp.cpu.halted { + device.rsp.regs[SP_STATUS_REG as usize] |= SP_STATUS_HALT; + return; + } + device.rsp.cpu.sync_point = false; + do_task(device) +} + +pub fn init(device: &mut device::Device) { + device.rsp.regs[SP_STATUS_REG as usize] = 1; + device::rsp_cpu::init(device); +} diff --git a/src/device/rsp_su_instructions.rs b/src/device/rsp_su_instructions.rs new file mode 100644 index 00000000..697e2e9e --- /dev/null +++ b/src/device/rsp_su_instructions.rs @@ -0,0 +1,1022 @@ +use crate::device; +use std::arch::x86_64::*; + +pub fn rd(opcode: u32) -> u32 { + return (opcode >> 11) & 0x1F; +} + +pub fn rs(opcode: u32) -> u32 { + return (opcode >> 21) & 0x1F; +} + +pub fn rt(opcode: u32) -> u32 { + return (opcode >> 16) & 0x1F; +} + +pub fn sa(opcode: u32) -> u32 { + return (opcode >> 6) & 0x1F; +} + +pub fn imm(opcode: u32) -> u16 { + return opcode as u16; +} + +pub fn voffset(opcode: u32) -> u8 { + return (opcode & 0x7F) as u8; +} + +pub fn velement(opcode: u32) -> u8 { + return ((opcode >> 7) & 0xF) as u8; +} + +pub fn sign_extend_7bit_offset(offset: u8, shift_amount: u32) -> u32 { + let soffset = (((offset << 1) & 0x80) | offset) as i8; + + return (((soffset) as i32) as u32) << shift_amount; +} + +pub fn modify_vpr_byte(vpr: &mut u128, value: u8, element: u8) { + let pos = 15 - (element & 15); + let mask = 0xFF << (pos * 8); + *vpr &= !mask; + *vpr |= (value as u128) << (pos * 8); +} + +pub fn get_vpr_byte(vpr: u128, element: u8) -> u8 { + let pos = 15 - (element & 15); + return (vpr >> (pos * 8)) as u8; +} + +pub fn modify_vpr_element(vpr: &mut u128, value: u16, element: u8) { + let pos = 7 - (element & 7); + let mask = 0xFFFF << (pos * 16); + *vpr &= !mask; + *vpr |= (value as u128) << (pos * 16); +} + +pub fn get_vpr_element(vpr: u128, element: u8) -> u16 { + let pos = 7 - (element & 7); + return (vpr >> (pos * 16)) as u16; +} + +pub fn j(device: &mut device::Device, opcode: u32) { + if device::rsp_cpu::in_delay_slot_taken(device) { + return; + } + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 4) & 0xF0000000 + | ((opcode & 0x3FFFFFF) << 2) as u32 +} + +pub fn jal(device: &mut device::Device, opcode: u32) { + if device::rsp_cpu::in_delay_slot_taken(device) { + device.rsp.cpu.gpr[31] = (device.rsp.cpu.branch_state.pc + 4) & 0xFFF + } else { + device.rsp.cpu.gpr[31] = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 8) & 0xFFF + } + if !device::rsp_cpu::in_delay_slot_taken(device) { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 4) & 0xF0000000 + | ((opcode & 0x3FFFFFF) << 2) as u32 + } else if !device::rsp_cpu::in_delay_slot(device) { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn beq(device: &mut device::Device, opcode: u32) { + if device.rsp.cpu.gpr[rs(opcode) as usize] == device.rsp.cpu.gpr[rt(opcode) as usize] { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bne(device: &mut device::Device, opcode: u32) { + if device.rsp.cpu.gpr[rs(opcode) as usize] != device.rsp.cpu.gpr[rt(opcode) as usize] { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn blez(device: &mut device::Device, opcode: u32) { + if device.rsp.cpu.gpr[rs(opcode) as usize] as i32 <= 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bgtz(device: &mut device::Device, opcode: u32) { + if device.rsp.cpu.gpr[rs(opcode) as usize] as i32 > 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn addi(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32) +} + +pub fn addiu(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32) +} + +pub fn slti(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + ((device.rsp.cpu.gpr[rs(opcode) as usize] as i32) < (imm(opcode) as i16 as i32)) as u32 +} + +pub fn sltiu(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + (device.rsp.cpu.gpr[rs(opcode) as usize] < (imm(opcode) as i16 as i32 as u32)) as u32 +} + +pub fn andi(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] & (imm(opcode)) as u32 +} + +pub fn ori(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] | (imm(opcode)) as u32 +} + +pub fn xori(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] ^ (imm(opcode)) as u32 +} + +pub fn lui(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rt(opcode) as usize] = (imm(opcode) as u32) << 16 +} + +pub fn lb(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + device.rsp.cpu.gpr[rt(opcode) as usize] = + device.rsp.mem[address as usize & 0xFFF] as i8 as i32 as u32 +} + +pub fn lh(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + let mut w = [0; 2]; + w[0] = device.rsp.mem[address as usize & 0xFFF]; + w[1] = device.rsp.mem[(address as usize + 1) & &0xFFF]; + + device.rsp.cpu.gpr[rt(opcode) as usize] = + (((w[0] as u16) << 8) | w[1] as u16) as i16 as i32 as u32 +} + +pub fn lw(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + let mut w = [0; 4]; + w[0] = device.rsp.mem[address as usize & 0xFFF]; + w[1] = device.rsp.mem[(address as usize + 1) & &0xFFF]; + w[2] = device.rsp.mem[(address as usize + 2) & &0xFFF]; + w[3] = device.rsp.mem[(address as usize + 3) & &0xFFF]; + + device.rsp.cpu.gpr[rt(opcode) as usize] = + (w[0] as u32) << 24 | (w[1] as u32) << 16 | (w[2] as u32) << 8 | (w[3] as u32) +} + +pub fn lbu(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + device.rsp.cpu.gpr[rt(opcode) as usize] = device.rsp.mem[address as usize & 0xFFF] as u32 +} + +pub fn lhu(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + let mut w = [0; 2]; + w[0] = device.rsp.mem[address as usize & 0xFFF]; + w[1] = device.rsp.mem[(address as usize + 1) & &0xFFF]; + + device.rsp.cpu.gpr[rt(opcode) as usize] = (((w[0] as u16) << 8) | w[1] as u16) as u32 +} + +pub fn lwu(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + let mut w = [0; 4]; + w[0] = device.rsp.mem[address as usize & 0xFFF]; + w[1] = device.rsp.mem[(address as usize + 1) & &0xFFF]; + w[2] = device.rsp.mem[(address as usize + 2) & &0xFFF]; + w[3] = device.rsp.mem[(address as usize + 3) & &0xFFF]; + + device.rsp.cpu.gpr[rt(opcode) as usize] = + (w[0] as u32) << 24 | (w[1] as u32) << 16 | (w[2] as u32) << 8 | (w[3] as u32) +} + +pub fn sb(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + device.rsp.mem[address as usize & 0xFFF] = (device.rsp.cpu.gpr[rt(opcode) as usize]) as u8; +} + +pub fn sh(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + device.rsp.mem[address as usize & 0xFFF] = (device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as u8; + device.rsp.mem[(address as usize + 1) & 0xFFF] = + (device.rsp.cpu.gpr[rt(opcode) as usize]) as u8; +} + +pub fn sw(device: &mut device::Device, opcode: u32) { + let address = + device.rsp.cpu.gpr[rs(opcode) as usize].wrapping_add(imm(opcode) as i16 as i32 as u32); + + device.rsp.mem[address as usize & 0xFFF] = + (device.rsp.cpu.gpr[rt(opcode) as usize] >> 24) as u8; + device.rsp.mem[(address as usize + 1) & 0xFFF] = + (device.rsp.cpu.gpr[rt(opcode) as usize] >> 16) as u8; + device.rsp.mem[(address as usize + 2) & 0xFFF] = + (device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as u8; + device.rsp.mem[(address as usize + 3) & 0xFFF] = + (device.rsp.cpu.gpr[rt(opcode) as usize]) as u8; +} + +pub fn sll(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + (device.rsp.cpu.gpr[rt(opcode) as usize] as u32) << sa(opcode) +} + +pub fn srl(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + (device.rsp.cpu.gpr[rt(opcode) as usize] as u32) >> sa(opcode) +} + +pub fn sra(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + ((device.rsp.cpu.gpr[rt(opcode) as usize] as i32) >> sa(opcode)) as u32 +} + +pub fn sllv(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + device.rsp.cpu.gpr[rt(opcode) as usize] << (device.rsp.cpu.gpr[rs(opcode) as usize] & 31) +} + +pub fn srlv(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + device.rsp.cpu.gpr[rt(opcode) as usize] >> (device.rsp.cpu.gpr[rs(opcode) as usize] & 31) +} + +pub fn srav(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = ((device.rsp.cpu.gpr[rt(opcode) as usize] as i32) + >> (device.rsp.cpu.gpr[rs(opcode) as usize] & 31)) + as u32 +} + +pub fn jr(device: &mut device::Device, opcode: u32) { + if !device::rsp_cpu::in_delay_slot_taken(device) { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.cpu.gpr[rs(opcode) as usize] + } else if !device::rsp_cpu::in_delay_slot(device) { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn jalr(device: &mut device::Device, opcode: u32) { + let in_delay_slot_taken = device::rsp_cpu::in_delay_slot_taken(device); + + if !in_delay_slot_taken { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.cpu.gpr[rs(opcode) as usize] + } else if !device::rsp_cpu::in_delay_slot(device) { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } + + if in_delay_slot_taken { + device.rsp.cpu.gpr[rd(opcode) as usize] = (device.rsp.cpu.branch_state.pc + 4) & 0xFFF + } else { + device.rsp.cpu.gpr[rd(opcode) as usize] = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 8) & 0xFFF + } +} + +pub fn break_(device: &mut device::Device, _opcode: u32) { + device.rsp.cpu.broken = true; +} + +pub fn add(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(device.rsp.cpu.gpr[rt(opcode) as usize]) +} + +pub fn addu(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(device.rsp.cpu.gpr[rt(opcode) as usize]) +} + +pub fn sub(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_sub(device.rsp.cpu.gpr[rt(opcode) as usize]) +} + +pub fn subu(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_sub(device.rsp.cpu.gpr[rt(opcode) as usize]) +} + +pub fn and(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] & device.rsp.cpu.gpr[rt(opcode) as usize] +} + +pub fn or(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] | device.rsp.cpu.gpr[rt(opcode) as usize] +} + +pub fn xor(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + device.rsp.cpu.gpr[rs(opcode) as usize] ^ device.rsp.cpu.gpr[rt(opcode) as usize] +} + +pub fn nor(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + !(device.rsp.cpu.gpr[rs(opcode) as usize] | device.rsp.cpu.gpr[rt(opcode) as usize]) +} + +pub fn slt(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = ((device.rsp.cpu.gpr[rs(opcode) as usize] as i32) + < (device.rsp.cpu.gpr[rt(opcode) as usize] as i32)) + as u32 +} + +pub fn sltu(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.gpr[rd(opcode) as usize] = + (device.rsp.cpu.gpr[rs(opcode) as usize] < device.rsp.cpu.gpr[rt(opcode) as usize]) as u32 +} + +pub fn bltz(device: &mut device::Device, opcode: u32) { + if (device.rsp.cpu.gpr[rs(opcode) as usize] as i32) < 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bgez(device: &mut device::Device, opcode: u32) { + if device.rsp.cpu.gpr[rs(opcode) as usize] as i32 >= 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } +} + +pub fn bltzal(device: &mut device::Device, opcode: u32) { + if (device.rsp.cpu.gpr[rs(opcode) as usize] as i32) < 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } + device.rsp.cpu.gpr[31] = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 8) & 0xFFF +} + +pub fn bgezal(device: &mut device::Device, opcode: u32) { + if (device.rsp.cpu.gpr[rs(opcode) as usize] as i32) >= 0 { + device.rsp.cpu.branch_state.state = device::cpu::State::Take; + device.rsp.cpu.branch_state.pc = device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + .wrapping_add(imm(opcode << 2) as i16 as i32 as u32) + + 4 + } else { + device.rsp.cpu.branch_state.state = device::cpu::State::NotTaken; + } + device.rsp.cpu.gpr[31] = + (device.rsp.regs2[device::rsp_interface::SP_PC_REG as usize] + 8) & 0xFFF +} + +pub fn mfc0(device: &mut device::Device, opcode: u32) { + if rd(opcode) < device::rsp_interface::SP_REGS_COUNT { + device.rsp.cpu.gpr[rt(opcode) as usize] = device::rsp_interface::read_regs( + device, + (rd(opcode) << 2) as u64, + device::memory::AccessSize::Word, + ) + } else { + device.rsp.cpu.gpr[rt(opcode) as usize] = device::rdp::read_regs_dpc( + device, + ((rd(opcode) - device::rsp_interface::SP_REGS_COUNT) << 2) as u64, + device::memory::AccessSize::Word, + ) + } + device.rsp.cpu.sync_point = true; +} + +pub fn mtc0(device: &mut device::Device, opcode: u32) { + if rd(opcode) < device::rsp_interface::SP_REGS_COUNT { + device::rsp_interface::write_regs( + device, + (rd(opcode) << 2) as u64, + device.rsp.cpu.gpr[rt(opcode) as usize], + 0xFFFFFFFF, + ) + } else { + device::rdp::write_regs_dpc( + device, + ((rd(opcode) - device::rsp_interface::SP_REGS_COUNT) << 2) as u64, + device.rsp.cpu.gpr[rt(opcode) as usize], + 0xFFFFFFFF, + ) + } + if rd(opcode) == device::rsp_interface::SP_STATUS_REG + && device.rsp.cpu.gpr[rt(opcode) as usize] & device::rsp_interface::SP_SET_HALT != 0 + { + device.rsp.regs[device::rsp_interface::SP_STATUS_REG as usize] &= + !device::rsp_interface::SP_STATUS_HALT; // set halt when event happens + device.rsp.cpu.halted = true // the RSP can halt itself by setting SP_SET_HALT + } + device.rsp.cpu.sync_point = true; +} + +pub fn mfc2(device: &mut device::Device, opcode: u32) { + let hi = get_vpr_byte(device.rsp.cpu.vpr[rd(opcode) as usize], velement(opcode)); + let lo = get_vpr_byte( + device.rsp.cpu.vpr[rd(opcode) as usize], + velement(opcode) + 1, + ); + device.rsp.cpu.gpr[rt(opcode) as usize] = ((hi as u16) << 8 | (lo as u16)) as i16 as i32 as u32 +} + +pub fn cfc2(device: &mut device::Device, opcode: u32) { + let hi; + let lo; + let mut zero = unsafe { _mm_setzero_si128() }; + match rd(opcode) & 3 { + 0x00 => { + hi = &mut device.rsp.cpu.vcoh; + lo = &mut device.rsp.cpu.vcol; + } + 0x01 => { + hi = &mut device.rsp.cpu.vcch; + lo = &mut device.rsp.cpu.vccl; + } + 0x02 | 0x03 => { + hi = &mut zero; + lo = &mut device.rsp.cpu.vce; + } + _ => { + panic!("unknown ctc2") + } + } + + unsafe { + let reverse = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + device.rsp.cpu.gpr[rt(opcode) as usize] = + (_mm_movemask_epi8(_mm_shuffle_epi8(_mm_packs_epi16(*hi, *lo), reverse))) as i16 as u32; + } +} + +pub fn mtc2(device: &mut device::Device, opcode: u32) { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rd(opcode) as usize], + (device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as u8, + velement(opcode), + ); + if velement(opcode) != 15 { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rd(opcode) as usize], + device.rsp.cpu.gpr[rt(opcode) as usize] as u8, + velement(opcode) + 1, + ); + } +} + +pub fn ctc2(device: &mut device::Device, opcode: u32) { + let hi; + let lo; + let mut zero = unsafe { _mm_setzero_si128() }; + match rd(opcode) & 3 { + 0x00 => { + hi = &mut device.rsp.cpu.vcoh; + lo = &mut device.rsp.cpu.vcol; + } + 0x01 => { + hi = &mut device.rsp.cpu.vcch; + lo = &mut device.rsp.cpu.vccl; + } + 0x02 | 0x03 => { + hi = &mut zero; + lo = &mut device.rsp.cpu.vce; + } + _ => { + panic!("unknown ctc2") + } + } + + unsafe { + let mask = _mm_set_epi16( + 0x0101, + 0x0202, + 0x0404, + 0x0808, + 0x1010, + 0x2020, + 0x4040, + 0x8080u16 as i16, + ); + *lo = _mm_cmpeq_epi8( + _mm_and_si128( + _mm_set1_epi8(!device.rsp.cpu.gpr[rt(opcode) as usize] as i8), + mask, + ), + _mm_setzero_si128(), + ); + *hi = std::arch::x86_64::_mm_cmpeq_epi8( + _mm_and_si128( + _mm_set1_epi8(!(device.rsp.cpu.gpr[rt(opcode) as usize] >> 8) as i8), + mask, + ), + _mm_setzero_si128(), + ); + } +} + +pub fn lbv(device: &mut device::Device, opcode: u32) { + let address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 0)); + + let element = velement(opcode); + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); +} + +pub fn lsv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 1)); + + let mut element = velement(opcode); + let end = std::cmp::min(element + 2, 16); + while element < end { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); + address += 1; + element += 1; + } +} + +pub fn llv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 2)); + + let mut element = velement(opcode); + let end = std::cmp::min(element + 4, 16); + while element < end { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); + address += 1; + element += 1; + } +} + +pub fn ldv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let mut element = velement(opcode); + let end = std::cmp::min(element + 8, 16); + while element < end { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); + address += 1; + element += 1; + } +} + +pub fn lqv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let mut element = velement(opcode); + let end = std::cmp::min(16 + element - ((address & 15) as u8), 16); + while element < end { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); + address += 1; + element += 1; + } +} + +pub fn lrv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let mut element = 16u8.wrapping_sub(((address & 15) as u8).wrapping_sub(velement(opcode))); + address &= !15; + while element < 16 { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + element, + ); + address += 1; + element += 1; + } +} + +pub fn lpv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let index = ((address & 7) as u8).wrapping_sub(velement(opcode)); + address &= !7; + let mut offset: u8 = 0; + while offset < 8 { + modify_vpr_element( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + (device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset)) & 15) as u32)) + & 0xFFF) as usize] as u16) + << 8, + offset, + ); + offset += 1; + } +} + +pub fn luv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let index = ((address & 7) as u8).wrapping_sub(velement(opcode)); + address &= !7; + let mut offset: u8 = 0; + while offset < 8 { + modify_vpr_element( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + (device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset)) & 15) as u32)) + & 0xFFF) as usize] as u16) + << 7, + offset, + ); + offset += 1; + } +} + +pub fn lhv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let index = ((address & 7) as u8).wrapping_sub(velement(opcode)); + address &= !7; + let mut offset: u8 = 0; + while offset < 8 { + modify_vpr_element( + &mut device.rsp.cpu.vpr[rt(opcode) as usize], + (device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset * 2)) & 15) as u32)) + & 0xFFF) as usize] as u16) + << 7, + offset, + ); + offset += 1; + } +} + +pub fn lfv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let index = ((address & 7) as u8).wrapping_sub(velement(opcode)); + address &= !7; + let start = velement(opcode); + let end = std::cmp::min(start + 8, 16); + let mut tmp: u128 = 0; + let mut offset: u8 = 0; + while offset < 4 { + modify_vpr_element( + &mut tmp, + (device.rsp.mem[((address.wrapping_add(((index.wrapping_add(offset * 4)) & 15) as u32)) + & 0xFFF) as usize] as u16) + << 7, + offset, + ); + modify_vpr_element( + &mut tmp, + (device.rsp.mem[((address + .wrapping_add(((index.wrapping_add(offset * 4).wrapping_add(8)) & 15) as u32)) + & 0xFFF) as usize] as u16) + << 7, + offset + 4, + ); + offset += 1; + } + offset = start; + while offset < end { + let value = get_vpr_byte(tmp, offset); + modify_vpr_byte(&mut device.rsp.cpu.vpr[rt(opcode) as usize], value, offset); + offset += 1; + } +} + +pub fn lwv(_device: &mut device::Device, _opcode: u32) {} + +pub fn ltv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let begin = address & !7; + address = begin + (((velement(opcode)) as u32 + (address & 8)) & 15); + let vtbase = rt(opcode) & !7; + let mut vtoff = (velement(opcode)) as u32 >> 1; + let mut i = 0; + while i < 8 { + modify_vpr_byte( + &mut device.rsp.cpu.vpr[(vtbase + vtoff) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + i * 2, + ); + address += 1; + if address == begin + 16 { + address = begin + } + modify_vpr_byte( + &mut device.rsp.cpu.vpr[(vtbase + vtoff) as usize], + device.rsp.mem[(address & 0xFFF) as usize], + i * 2 + 1, + ); + address += 1; + if address == begin + 16 { + address = begin + } + vtoff = (vtoff + 1) & 7; + i += 1; + } +} + +pub fn sbv(device: &mut device::Device, opcode: u32) { + let address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 0)); + + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], velement(opcode)) +} + +pub fn ssv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 1)); + + let mut element = velement(opcode); + let end = element + 2; + while element < end { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element); + address += 1; + element += 1; + } +} + +pub fn slv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 2)); + + let mut element = velement(opcode); + let end = element + 4; + while element < end { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element); + address += 1; + element += 1; + } +} + +pub fn sdv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let mut element = velement(opcode); + let end = element + 8; + while element < end { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element); + address += 1; + element += 1; + } +} + +pub fn sqv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let mut element = velement(opcode); + let end = element + (16 - (address & 15)) as u8; + while element < end { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element); + address += 1; + element += 1; + } +} + +pub fn srv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let mut element = velement(opcode); + let end = element + (address & 15) as u8; + let base = (16 - (address & 15)) as u8; + address &= !15; + while element < end { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element + base); + address += 1; + element += 1; + } +} + +pub fn spv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let mut element = velement(opcode); + let end = element + 8; + while element < end { + if (element & 15) < 8 { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1); + } else { + device.rsp.mem[(address & 0xFFF) as usize] = + (get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8; + } + address += 1; + element += 1; + } +} + +pub fn suv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 3)); + + let mut element = velement(opcode); + let end = element + 8; + while element < end { + if (element & 15) < 8 { + device.rsp.mem[(address & 0xFFF) as usize] = + (get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], element) >> 7) as u8; + } else { + device.rsp.mem[(address & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], (element & 7) << 1); + } + address += 1; + element += 1; + } +} + +pub fn shv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let element = velement(opcode); + let index = (address & 7) as u8; + address &= !7; + let mut offset = 0; + while offset < 8 { + let byte_val = element + offset * 2; + let value = get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val) << 1 + | get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], byte_val + 1) >> 7; + device.rsp.mem[((address + ((index + offset * 2) & 15) as u32) & 0xFFF) as usize] = value; + offset += 1; + } +} + +pub fn sfv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + let base = address & 7; + address &= !7; + let element = velement(opcode); + let elements; + match element { + 0 | 15 => { + elements = [0, 1, 2, 3]; + } + 1 => { + elements = [6, 7, 4, 5]; + } + 4 => { + elements = [1, 2, 3, 0]; + } + 5 => { + elements = [7, 4, 5, 6]; + } + 8 => { + elements = [4, 5, 6, 7]; + } + 11 => { + elements = [3, 0, 1, 2]; + } + 12 => { + elements = [5, 6, 7, 4]; + } + _ => { + device.rsp.mem[((address + ((base + 0) & 15)) & 0xFFF) as usize] = 0; + device.rsp.mem[((address + ((base + 4) & 15)) & 0xFFF) as usize] = 0; + device.rsp.mem[((address + ((base + 8) & 15)) & 0xFFF) as usize] = 0; + device.rsp.mem[((address + ((base + 12) & 15)) & 0xFFF) as usize] = 0; + return; + } + } + let mut offset = 0; + let mut i = 0; + while i < 4 { + device.rsp.mem[((address + ((base + offset) & 15)) & 0xFFF) as usize] = + (get_vpr_element(device.rsp.cpu.vpr[rt(opcode) as usize], elements[i]) >> 7) as u8; + offset += 4; + i += 1; + } +} + +pub fn swv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + + let mut element = velement(opcode); + let end = element + 16; + let mut base = address & 7; + address &= !7; + while element < end { + device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[rt(opcode) as usize], element); + base += 1; + element += 1; + } +} + +pub fn stv(device: &mut device::Device, opcode: u32) { + let mut address = device.rsp.cpu.gpr[rs(opcode) as usize] + .wrapping_add(sign_extend_7bit_offset(voffset(opcode), 4)); + let start = rt(opcode) & !7; + let end = start + 8; + let mut element = 16 - (velement(opcode) & !1); + let mut base = (address & 7).wrapping_sub((velement(opcode) & !1) as u32); + address &= !7; + let mut offset = start; + while offset < end { + device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[offset as usize], element); + base = base.wrapping_add(1); + element += 1; + device.rsp.mem[((address + (base & 15)) & 0xFFF) as usize] = + get_vpr_byte(device.rsp.cpu.vpr[offset as usize], element); + base = base.wrapping_add(1); + element += 1; + offset += 1; + } +} + +pub fn reserved(_device: &mut device::Device, _opcode: u32) { + panic!("rsp su reserved") +} diff --git a/src/device/rsp_vu_instructions.rs b/src/device/rsp_vu_instructions.rs new file mode 100644 index 00000000..4c1419fa --- /dev/null +++ b/src/device/rsp_vu_instructions.rs @@ -0,0 +1,1253 @@ +use crate::device; +use std::arch::x86_64::*; + +pub fn vt(opcode: u32) -> u32 { + return (opcode >> 16) & 0x1F; +} + +pub fn ve(opcode: u32) -> u32 { + return (opcode >> 21) & 0xF; +} + +pub fn vs(opcode: u32) -> u32 { + return (opcode >> 11) & 0x1F; +} + +pub fn vd(opcode: u32) -> u32 { + return (opcode >> 6) & 0x1F; +} + +pub fn de(opcode: u32) -> u32 { + return (opcode >> 11) & 0x7; +} + +pub fn clamp_signed_32(value: i32) -> i16 { + if value < -32768 { + return -32768; + } + if value > 32767 { + return 32767; + } + return value as i16; +} + +pub fn clamp_signed_64(value: i64) -> i16 { + if value < -32768 { + return -32768; + } + if value > 32767 { + return 32767; + } + return value as i16; +} + +pub fn count_leading_zeros(value: u32) -> u32 { + let mut index = 31; + while index >= 0 { + if (value >> index) & 1 != 0 { + break; + } + index -= 1; + } + return (31 - index) as u32; +} + +pub fn s_clip(x: i64, bits: u32) -> i64 { + let b = (1 as u64) << (bits - 1); + let m = b * 2 - 1; + return ((((x as u64) & m) ^ b).wrapping_sub(b)) as i64; +} + +pub fn modify_vpr_element(vpr: &mut u128, value: u16, element: u8) { + let pos = 7 - (element & 7); + let mask = 0xFFFF << (pos * 16); + *vpr &= !mask; + *vpr |= (value as u128) << (pos * 16); +} + +pub fn get_vpr_element(vpr: u128, element: u8) -> u16 { + let pos = 7 - (element & 7); + return (vpr >> (pos * 16)) as u16; +} + +pub fn vte(vt: u128, index: usize) -> __m128i { + unsafe { + let shuffle = [ + //vector + _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567 + _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0), //01234567 + //scalar quarter + _mm_set_epi8(15, 14, 15, 14, 11, 10, 11, 10, 7, 6, 7, 6, 3, 2, 3, 2), //00224466 + _mm_set_epi8(13, 12, 13, 12, 9, 8, 9, 8, 5, 4, 5, 4, 1, 0, 1, 0), //11335577 + //scalar half + _mm_set_epi8(15, 14, 15, 14, 15, 14, 15, 14, 7, 6, 7, 6, 7, 6, 7, 6), //00004444 + _mm_set_epi8(13, 12, 13, 12, 13, 12, 13, 12, 5, 4, 5, 4, 5, 4, 5, 4), //11115555 + _mm_set_epi8(11, 10, 11, 10, 11, 10, 11, 10, 3, 2, 3, 2, 3, 2, 3, 2), //22226666 + _mm_set_epi8(9, 8, 9, 8, 9, 8, 9, 8, 1, 0, 1, 0, 1, 0, 1, 0), //33337777 + //scalar whole + _mm_set_epi8( + 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, 15, 14, + ), //00000000 + _mm_set_epi8( + 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, 13, 12, + ), //11111111 + _mm_set_epi8( + 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, + ), //22222222 + _mm_set_epi8(9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8), //33333333 + _mm_set_epi8(7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 7, 6), //44444444 + _mm_set_epi8(5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4), //55555555 + _mm_set_epi8(3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2), //66666666 + _mm_set_epi8(1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), //77777777 + ]; + return _mm_shuffle_epi8(std::mem::transmute(vt), shuffle[index]); + } +} + +pub fn vmulf(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut hi, mut round, mut sign1, sign2, neq, eq, neg); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + round = _mm_cmpeq_epi16(_mm_setzero_si128(), _mm_setzero_si128()); + sign1 = _mm_srli_epi16(lo, 15); + lo = _mm_add_epi16(lo, lo); + round = _mm_slli_epi16(round, 15); + hi = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign2 = _mm_srli_epi16(lo, 15); + device.rsp.cpu.accl = _mm_add_epi16(round, lo); + sign1 = _mm_add_epi16(sign1, sign2); + hi = _mm_slli_epi16(hi, 1); + neq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accm = _mm_add_epi16(hi, sign1); + neg = _mm_srai_epi16(device.rsp.cpu.accm, 15); + + eq = _mm_and_si128(neq, neg); + device.rsp.cpu.acch = _mm_andnot_si128(neq, neg); + device.rsp.cpu.vpr[vd(opcode) as usize] = + std::mem::transmute(_mm_add_epi16(device.rsp.cpu.accm, eq)); + } +} + +pub fn vmulu(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut hi, mut round, mut sign1, sign2, neq, neg); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + round = _mm_cmpeq_epi16(_mm_setzero_si128(), _mm_setzero_si128()); + sign1 = _mm_srli_epi16(lo, 15); + lo = _mm_add_epi16(lo, lo); + round = _mm_slli_epi16(round, 15); + hi = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign2 = _mm_srli_epi16(lo, 15); + device.rsp.cpu.accl = _mm_add_epi16(round, lo); + sign1 = _mm_add_epi16(sign1, sign2); + hi = _mm_slli_epi16(hi, 1); + neq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accm = _mm_add_epi16(hi, sign1); + neg = _mm_srai_epi16(device.rsp.cpu.accm, 15); + + device.rsp.cpu.acch = _mm_andnot_si128(neq, neg); + hi = _mm_or_si128(device.rsp.cpu.accm, neg); + device.rsp.cpu.vpr[vd(opcode) as usize] = + std::mem::transmute(_mm_andnot_si128(device.rsp.cpu.acch, hi)); + } +} + +pub fn vrndp(device: &mut device::Device, opcode: u32) { + let vte = unsafe { + std::mem::transmute(vte( + device.rsp.cpu.vpr[vt(opcode) as usize], + ve(opcode) as usize, + )) + }; + let acch: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.acch) }; + let accm: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accm) }; + let accl: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accl) }; + + let mut n = 0; + while n < 8 { + let mut product = get_vpr_element(vte, n) as i16 as i32; + if vs(opcode) & 1 != 0 { + product <<= 16 + } + let mut acc = 0; + acc |= get_vpr_element(*acch, n) as i64; + acc <<= 16; + acc |= get_vpr_element(*accm, n) as i64; + acc <<= 16; + acc |= get_vpr_element(*accl, n) as i64; + acc <<= 16; + acc >>= 16; + if acc >= 0 { + acc = s_clip(acc + (product as i64), 48) + } + modify_vpr_element(acch, (acc >> 32) as u16, n); + modify_vpr_element(accm, (acc >> 16) as u16, n); + modify_vpr_element(accl, acc as u16, n); + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + clamp_signed_64(acc >> 16) as u16, + n, + ); + + n += 1; + } +} + +pub fn vmulq(device: &mut device::Device, opcode: u32) { + let vte = unsafe { + std::mem::transmute(vte( + device.rsp.cpu.vpr[vt(opcode) as usize], + ve(opcode) as usize, + )) + }; + let acch: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.acch) }; + let accm: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accm) }; + let accl: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accl) }; + + let mut n = 0; + while n < 8 { + let mut product = (get_vpr_element(device.rsp.cpu.vpr[vs(opcode) as usize], n) as i16 + as i32) + .wrapping_mul(get_vpr_element(vte, n) as i16 as i32); + if product < 0 { + product += 31; + } + modify_vpr_element(acch, (product >> 16) as u16, n); + modify_vpr_element(accm, (product) as u16, n); + modify_vpr_element(accl, 0, n); + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + (clamp_signed_32(product >> 1) & !15) as u16, + n, + ); + n += 1; + } +} + +pub fn vmudl(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accm = _mm_setzero_si128(); + device.rsp.cpu.acch = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vmudm(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (sign, vta); + unsafe { + device.rsp.cpu.accl = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accm = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign = _mm_srai_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + 15, + ); + vta = _mm_and_si128(vte, sign); + device.rsp.cpu.accm = _mm_sub_epi16(device.rsp.cpu.accm, vta); + device.rsp.cpu.acch = _mm_srai_epi16(device.rsp.cpu.accm, 15); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accm); + } +} + +pub fn vmudn(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (sign, vsa); + unsafe { + device.rsp.cpu.accl = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accm = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign = _mm_srai_epi16(vte, 15); + vsa = _mm_and_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + sign, + ); + device.rsp.cpu.accm = _mm_sub_epi16(device.rsp.cpu.accm, vsa); + device.rsp.cpu.acch = _mm_srai_epi16(device.rsp.cpu.accm, 15); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vmudh(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (lo, hi); + unsafe { + device.rsp.cpu.accl = _mm_setzero_si128(); + device.rsp.cpu.accm = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.acch = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + lo = _mm_unpacklo_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + hi = _mm_unpackhi_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_packs_epi32(lo, hi)); + } +} + +pub fn vmacf(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut md, mut hi, mut carry, mut omask); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + hi = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + md = _mm_slli_epi16(hi, 1); + carry = _mm_srli_epi16(lo, 15); + hi = _mm_srai_epi16(hi, 15); + md = _mm_or_si128(md, carry); + lo = _mm_slli_epi16(lo, 1); + omask = _mm_adds_epu16(device.rsp.cpu.accl, lo); + device.rsp.cpu.accl = _mm_add_epi16(device.rsp.cpu.accl, lo); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accl, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + md = _mm_sub_epi16(md, omask); + carry = _mm_cmpeq_epi16(md, _mm_setzero_si128()); + carry = _mm_and_si128(carry, omask); + hi = _mm_sub_epi16(hi, carry); + omask = _mm_adds_epu16(device.rsp.cpu.accm, md); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, md); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + device.rsp.cpu.acch = _mm_add_epi16(device.rsp.cpu.acch, hi); + device.rsp.cpu.acch = _mm_sub_epi16(device.rsp.cpu.acch, omask); + + lo = _mm_unpacklo_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + hi = _mm_unpackhi_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_packs_epi32(lo, hi)); + } +} + +pub fn vmacu(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut md, mut hi, mut carry, mut omask, mmask, hmask); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + hi = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + md = _mm_slli_epi16(hi, 1); + carry = _mm_srli_epi16(lo, 15); + hi = _mm_srai_epi16(hi, 15); + md = _mm_or_si128(md, carry); + lo = _mm_slli_epi16(lo, 1); + omask = _mm_adds_epu16(device.rsp.cpu.accl, lo); + device.rsp.cpu.accl = _mm_add_epi16(device.rsp.cpu.accl, lo); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accl, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + md = _mm_sub_epi16(md, omask); + carry = _mm_cmpeq_epi16(md, _mm_setzero_si128()); + carry = _mm_and_si128(carry, omask); + hi = _mm_sub_epi16(hi, carry); + omask = _mm_adds_epu16(device.rsp.cpu.accm, md); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, md); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + device.rsp.cpu.acch = _mm_add_epi16(device.rsp.cpu.acch, hi); + device.rsp.cpu.acch = _mm_sub_epi16(device.rsp.cpu.acch, omask); + + mmask = _mm_srai_epi16(device.rsp.cpu.accm, 15); + hmask = _mm_srai_epi16(device.rsp.cpu.acch, 15); + md = _mm_or_si128(mmask, device.rsp.cpu.accm); + omask = _mm_cmpgt_epi16(device.rsp.cpu.acch, _mm_setzero_si128()); + md = _mm_andnot_si128(hmask, md); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_or_si128(omask, md)); + } +} + +pub fn vrndn(device: &mut device::Device, opcode: u32) { + let vte = unsafe { + std::mem::transmute(vte( + device.rsp.cpu.vpr[vt(opcode) as usize], + ve(opcode) as usize, + )) + }; + let acch: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.acch) }; + let accm: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accm) }; + let accl: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accl) }; + + let mut n = 0; + while n < 8 { + let mut product = get_vpr_element(vte, n) as i16 as i32; + if vs(opcode) & 1 != 0 { + product <<= 16 + } + let mut acc = 0; + acc |= get_vpr_element(*acch, n) as i64; + acc <<= 16; + acc |= get_vpr_element(*accm, n) as i64; + acc <<= 16; + acc |= get_vpr_element(*accl, n) as i64; + acc <<= 16; + acc >>= 16; + if acc < 0 { + acc = s_clip(acc + (product as i64), 48) + } + modify_vpr_element(acch, (acc >> 32) as u16, n); + modify_vpr_element(accm, (acc >> 16) as u16, n); + modify_vpr_element(accl, acc as u16, n); + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + clamp_signed_64(acc >> 16) as u16, + n, + ); + + n += 1; + } +} + +pub fn vmacq(device: &mut device::Device, opcode: u32) { + let acch: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.acch) }; + let accm: &mut u128 = unsafe { std::mem::transmute(&mut device.rsp.cpu.accm) }; + + let mut n = 0; + while n < 8 { + let mut product = + (get_vpr_element(*acch, n) as i32) << 16 | (get_vpr_element(*accm, n) as i32); + if product < 0 && (product & (1 << 5)) == 0 { + product += 32 + } else if product >= 32 && (product & (1 << 5)) == 0 { + product -= 32 + } + modify_vpr_element(acch, (product >> 16) as u16, n); + modify_vpr_element(accm, (product) as u16, n); + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + (clamp_signed_32(product >> 1) & !15) as u16, + n, + ); + n += 1; + } +} + +pub fn vmadl(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut hi, mut omask, nhi, nmd, shi, smd, cmask, cval); + unsafe { + hi = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + omask = _mm_adds_epu16(device.rsp.cpu.accl, hi); + device.rsp.cpu.accl = _mm_add_epi16(device.rsp.cpu.accl, hi); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accl, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_sub_epi16(_mm_setzero_si128(), omask); + omask = _mm_adds_epu16(device.rsp.cpu.accm, hi); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, hi); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + device.rsp.cpu.acch = _mm_sub_epi16(device.rsp.cpu.acch, omask); + nhi = _mm_srai_epi16(device.rsp.cpu.acch, 15); + nmd = _mm_srai_epi16(device.rsp.cpu.accm, 15); + shi = _mm_cmpeq_epi16(nhi, device.rsp.cpu.acch); + smd = _mm_cmpeq_epi16(nhi, nmd); + cmask = _mm_and_si128(smd, shi); + cval = _mm_cmpeq_epi16(nhi, _mm_setzero_si128()); + device.rsp.cpu.vpr[vd(opcode) as usize] = + std::mem::transmute(_mm_blendv_epi8(cval, device.rsp.cpu.accl, cmask)); + } +} + +pub fn vmadm(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut hi, sign, vta, mut omask); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + hi = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign = _mm_srai_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + 15, + ); + vta = _mm_and_si128(vte, sign); + hi = _mm_sub_epi16(hi, vta); + omask = _mm_adds_epu16(device.rsp.cpu.accl, lo); + device.rsp.cpu.accl = _mm_add_epi16(device.rsp.cpu.accl, lo); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accl, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_sub_epi16(hi, omask); + omask = _mm_adds_epu16(device.rsp.cpu.accm, hi); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, hi); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_srai_epi16(hi, 15); + device.rsp.cpu.acch = _mm_add_epi16(device.rsp.cpu.acch, hi); + device.rsp.cpu.acch = _mm_sub_epi16(device.rsp.cpu.acch, omask); + lo = _mm_unpacklo_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + hi = _mm_unpackhi_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_packs_epi32(lo, hi)); + } +} + +pub fn vmadn(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (lo, mut hi, sign, vsa, mut omask, nhi, nmd, shi, smd, cmask, cval); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + hi = _mm_mulhi_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign = _mm_srai_epi16(vte, 15); + vsa = _mm_and_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + sign, + ); + hi = _mm_sub_epi16(hi, vsa); + omask = _mm_adds_epu16(device.rsp.cpu.accl, lo); + device.rsp.cpu.accl = _mm_add_epi16(device.rsp.cpu.accl, lo); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accl, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_sub_epi16(hi, omask); + omask = _mm_adds_epu16(device.rsp.cpu.accm, hi); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, hi); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_srai_epi16(hi, 15); + device.rsp.cpu.acch = _mm_add_epi16(device.rsp.cpu.acch, hi); + device.rsp.cpu.acch = _mm_sub_epi16(device.rsp.cpu.acch, omask); + nhi = _mm_srai_epi16(device.rsp.cpu.acch, 15); + nmd = _mm_srai_epi16(device.rsp.cpu.accm, 15); + shi = _mm_cmpeq_epi16(nhi, device.rsp.cpu.acch); + smd = _mm_cmpeq_epi16(nhi, nmd); + cmask = _mm_and_si128(smd, shi); + cval = _mm_cmpeq_epi16(nhi, _mm_setzero_si128()); + device.rsp.cpu.vpr[vd(opcode) as usize] = + std::mem::transmute(_mm_blendv_epi8(cval, device.rsp.cpu.accl, cmask)); + } +} + +pub fn vmadh(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut lo, mut hi, mut omask); + unsafe { + lo = _mm_mullo_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + hi = _mm_mulhi_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + omask = _mm_adds_epu16(device.rsp.cpu.accm, lo); + device.rsp.cpu.accm = _mm_add_epi16(device.rsp.cpu.accm, lo); + omask = _mm_cmpeq_epi16(device.rsp.cpu.accm, omask); + omask = _mm_cmpeq_epi16(omask, _mm_setzero_si128()); + hi = _mm_sub_epi16(hi, omask); + device.rsp.cpu.acch = _mm_add_epi16(device.rsp.cpu.acch, hi); + lo = _mm_unpacklo_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + hi = _mm_unpackhi_epi16(device.rsp.cpu.accm, device.rsp.cpu.acch); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_packs_epi32(lo, hi)); + } +} + +pub fn vadd(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (sum, mut min, max); + unsafe { + sum = _mm_add_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accl = _mm_sub_epi16(sum, device.rsp.cpu.vcol); + min = _mm_min_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + max = _mm_max_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + min = _mm_subs_epi16(min, device.rsp.cpu.vcol); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_adds_epi16(min, max)); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + } +} + +pub fn vsub(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (udiff, sdiff, ov); + unsafe { + udiff = _mm_sub_epi16(vte, device.rsp.cpu.vcol); + sdiff = _mm_subs_epi16(vte, device.rsp.cpu.vcol); + device.rsp.cpu.accl = _mm_sub_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + udiff, + ); + ov = _mm_cmpgt_epi16(sdiff, udiff); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_subs_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + sdiff, + )); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_adds_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + ov, + )); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + } +} + +pub fn vzero(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_add_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + )); + } +} + +pub fn vabs(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (vs0, slt); + unsafe { + vs0 = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + _mm_setzero_si128(), + ); + slt = _mm_srai_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + 15, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_andnot_si128(vs0, vte)); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + slt, + )); + device.rsp.cpu.accl = _mm_sub_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + slt, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(_mm_subs_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vd(opcode) as usize]), + slt, + )); + } +} + +pub fn vaddc(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let sum; + unsafe { + sum = _mm_adds_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accl = _mm_add_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vcol = _mm_cmpeq_epi16(sum, device.rsp.cpu.accl); + device.rsp.cpu.vcol = _mm_cmpeq_epi16(device.rsp.cpu.vcol, _mm_setzero_si128()); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vsubc(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (equal, udiff, diff0); + unsafe { + udiff = _mm_subs_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + equal = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + diff0 = _mm_cmpeq_epi16(udiff, _mm_setzero_si128()); + device.rsp.cpu.vcoh = _mm_cmpeq_epi16(equal, _mm_setzero_si128()); + device.rsp.cpu.vcol = _mm_andnot_si128(equal, diff0); + device.rsp.cpu.accl = _mm_sub_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vsar(device: &mut device::Device, opcode: u32) { + match ve(opcode) { + 0x8 => { + device.rsp.cpu.vpr[vd(opcode) as usize] = + unsafe { std::mem::transmute(device.rsp.cpu.acch) }; + } + 0x9 => { + device.rsp.cpu.vpr[vd(opcode) as usize] = + unsafe { std::mem::transmute(device.rsp.cpu.accm) }; + } + 0xa => { + device.rsp.cpu.vpr[vd(opcode) as usize] = + unsafe { std::mem::transmute(device.rsp.cpu.accl) }; + } + _ => { + device.rsp.cpu.vpr[vd(opcode) as usize] = 0; + } + } +} + +pub fn vlt(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut eq, lt); + unsafe { + eq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + lt = _mm_cmplt_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + eq = _mm_and_si128(device.rsp.cpu.vcoh, eq); + eq = _mm_and_si128(device.rsp.cpu.vcol, eq); + device.rsp.cpu.vccl = _mm_or_si128(lt, eq); + device.rsp.cpu.accl = _mm_blendv_epi8( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + device.rsp.cpu.vccl, + ); + device.rsp.cpu.vcch = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn veq(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let eq; + unsafe { + eq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vccl = _mm_andnot_si128(device.rsp.cpu.vcoh, eq); + device.rsp.cpu.accl = _mm_blendv_epi8( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + device.rsp.cpu.vccl, + ); + device.rsp.cpu.vcch = _mm_setzero_si128(); //unverified + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vne(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (eq, ne); + unsafe { + eq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + ne = _mm_cmpeq_epi16(eq, _mm_setzero_si128()); + device.rsp.cpu.vccl = _mm_and_si128(device.rsp.cpu.vcoh, eq); + device.rsp.cpu.vccl = _mm_or_si128(device.rsp.cpu.vccl, ne); + device.rsp.cpu.accl = _mm_blendv_epi8( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + device.rsp.cpu.vccl, + ); + device.rsp.cpu.vcch = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vge(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut eq, gt, es); + unsafe { + eq = _mm_cmpeq_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + gt = _mm_cmpgt_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + es = _mm_and_si128(device.rsp.cpu.vcoh, device.rsp.cpu.vcol); + eq = _mm_andnot_si128(es, eq); + device.rsp.cpu.vccl = _mm_or_si128(gt, eq); + device.rsp.cpu.accl = _mm_blendv_epi8( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + device.rsp.cpu.vccl, + ); + device.rsp.cpu.vcch = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vcl(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let ( + mut nvt, + diff, + mut ncarry, + nvce, + diff0, + mut lec1, + mut lec2, + leeq, + mut geeq, + mut le, + mut ge, + mask, + ); + unsafe { + nvt = _mm_xor_si128(vte, device.rsp.cpu.vcol); + nvt = _mm_sub_epi16(nvt, device.rsp.cpu.vcol); + diff = _mm_sub_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + nvt, + ); + ncarry = _mm_adds_epu16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + ncarry = _mm_cmpeq_epi16(diff, ncarry); + nvce = _mm_cmpeq_epi16(device.rsp.cpu.vce, _mm_setzero_si128()); + diff0 = _mm_cmpeq_epi16(diff, _mm_setzero_si128()); + lec1 = _mm_and_si128(diff0, ncarry); + lec1 = _mm_and_si128(nvce, lec1); + lec2 = _mm_or_si128(diff0, ncarry); + lec2 = _mm_and_si128(device.rsp.cpu.vce, lec2); + leeq = _mm_or_si128(lec1, lec2); + geeq = _mm_subs_epu16( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + ); + geeq = _mm_cmpeq_epi16(geeq, _mm_setzero_si128()); + le = _mm_andnot_si128(device.rsp.cpu.vcoh, device.rsp.cpu.vcol); + le = _mm_blendv_epi8(device.rsp.cpu.vccl, leeq, le); + ge = _mm_or_si128(device.rsp.cpu.vcol, device.rsp.cpu.vcoh); + ge = _mm_blendv_epi8(geeq, device.rsp.cpu.vcch, ge); + mask = _mm_blendv_epi8(ge, le, device.rsp.cpu.vcol); + device.rsp.cpu.accl = _mm_blendv_epi8( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + nvt, + mask, + ); + device.rsp.cpu.vcch = ge; + device.rsp.cpu.vccl = le; + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vce = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vch(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut nvt, diff, diff0, vtn, mut dlez, dgez, mask); + unsafe { + device.rsp.cpu.vcol = _mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vcol = _mm_cmplt_epi16(device.rsp.cpu.vcol, _mm_setzero_si128()); + nvt = _mm_xor_si128(vte, device.rsp.cpu.vcol); + nvt = _mm_sub_epi16(nvt, device.rsp.cpu.vcol); + diff = _mm_sub_epi16( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + nvt, + ); + diff0 = _mm_cmpeq_epi16(diff, _mm_setzero_si128()); + vtn = _mm_cmplt_epi16(vte, _mm_setzero_si128()); + dlez = _mm_cmpgt_epi16(diff, _mm_setzero_si128()); + dgez = _mm_or_si128(dlez, diff0); + dlez = _mm_cmpeq_epi16(_mm_setzero_si128(), dlez); + device.rsp.cpu.vcch = _mm_blendv_epi8(dgez, vtn, device.rsp.cpu.vcol); + device.rsp.cpu.vccl = _mm_blendv_epi8(vtn, dlez, device.rsp.cpu.vcol); + device.rsp.cpu.vce = _mm_cmpeq_epi16(diff, device.rsp.cpu.vcol); + device.rsp.cpu.vce = _mm_and_si128(device.rsp.cpu.vce, device.rsp.cpu.vcol); + device.rsp.cpu.vcoh = _mm_or_si128(diff0, device.rsp.cpu.vce); + device.rsp.cpu.vcoh = _mm_cmpeq_epi16(device.rsp.cpu.vcoh, _mm_setzero_si128()); + mask = _mm_blendv_epi8( + device.rsp.cpu.vcch, + device.rsp.cpu.vccl, + device.rsp.cpu.vcol, + ); + device.rsp.cpu.accl = _mm_blendv_epi8( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + nvt, + mask, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vcr(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let (mut sign, mut dlez, mut dgez, nvt, mask); + unsafe { + sign = _mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + sign = _mm_srai_epi16(sign, 15); + dlez = _mm_and_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + sign, + ); + dlez = _mm_add_epi16(dlez, vte); + device.rsp.cpu.vccl = _mm_srai_epi16(dlez, 15); + dgez = _mm_or_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + sign, + ); + dgez = _mm_min_epi16(dgez, vte); + device.rsp.cpu.vcch = _mm_cmpeq_epi16(dgez, vte); + nvt = _mm_xor_si128(vte, sign); + mask = _mm_blendv_epi8(device.rsp.cpu.vcch, device.rsp.cpu.vccl, sign); + device.rsp.cpu.accl = _mm_blendv_epi8( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + nvt, + mask, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vce = _mm_setzero_si128(); + } +} + +pub fn vmrg(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_blendv_epi8( + vte, + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + device.rsp.cpu.vccl, + ); + device.rsp.cpu.vcoh = _mm_setzero_si128(); + device.rsp.cpu.vcol = _mm_setzero_si128(); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vand(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_and_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vnand(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_and_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accl = _mm_xor_si128(device.rsp.cpu.accl, _mm_set1_epi32(-1)); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vor(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_or_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vnor(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_or_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accl = _mm_xor_si128(device.rsp.cpu.accl, _mm_set1_epi32(-1)); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vxor(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vnxor(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + unsafe { + device.rsp.cpu.accl = _mm_xor_si128( + std::mem::transmute(device.rsp.cpu.vpr[vs(opcode) as usize]), + vte, + ); + device.rsp.cpu.accl = _mm_xor_si128(device.rsp.cpu.accl, _mm_set1_epi32(-1)); + device.rsp.cpu.vpr[vd(opcode) as usize] = std::mem::transmute(device.rsp.cpu.accl); + } +} + +pub fn vrcp(device: &mut device::Device, opcode: u32) { + let mut result; + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let input = + get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16 as i32; + let mask = input >> 31; + let mut data = input ^ mask; + if input > -32768 { + data -= mask + } + if data == 0 { + result = 0x7fffffff + } else if input == -32768 { + result = 0xffff0000 + } else { + let shift = count_leading_zeros(data as u32); + let index = (((data as u64) << shift) & 0x7fc00000) >> 22; + result = device.rsp.cpu.reciprocals[index as usize] as u32; + result = (0x10000 | result) << 14; + result = (result >> (31 - shift)) ^ mask as u32 + } + device.rsp.cpu.divdp = false; + device.rsp.cpu.divout = (result >> 16) as i16; + device.rsp.cpu.accl = vte; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + result as u16, + de(opcode) as u8, + ); +} + +pub fn vrcpl(device: &mut device::Device, opcode: u32) { + let mut result; + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let input; + if device.rsp.cpu.divdp { + input = (device.rsp.cpu.divin as i32) << 16 + | get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as u16 + as i32 + } else { + input = get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16 + as i32; + } + let mask = input >> 31; + let mut data = input ^ mask; + if input > -32768 { + data -= mask + } + if data == 0 { + result = 0x7fffffff + } else if input == -32768 { + result = 0xffff0000 + } else { + let shift = count_leading_zeros(data as u32); + let index = (((data as u64) << shift) & 0x7fc00000) >> 22; + result = device.rsp.cpu.reciprocals[index as usize] as u32; + result = (0x10000 | result) << 14; + result = (result >> (31 - shift)) ^ mask as u32 + } + device.rsp.cpu.divdp = false; + device.rsp.cpu.divout = (result >> 16) as i16; + device.rsp.cpu.accl = vte; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + result as u16, + de(opcode) as u8, + ); +} + +pub fn vrcph(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + device.rsp.cpu.accl = vte; + device.rsp.cpu.divdp = true; + + device.rsp.cpu.divin = + get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + device.rsp.cpu.divout as u16, + de(opcode) as u8, + ); +} + +pub fn vmov(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let value = get_vpr_element(unsafe { std::mem::transmute(vte) }, de(opcode) as u8); + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + value, + de(opcode) as u8, + ); + device.rsp.cpu.accl = vte; +} + +pub fn vrsq(device: &mut device::Device, opcode: u32) { + let mut result; + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let input = + get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16 as i32; + let mask = input >> 31; + let mut data = input ^ mask; + if input > -32768 { + data -= mask + } + if data == 0 { + result = 0x7fffffff + } else if input == -32768 { + result = 0xffff0000 + } else { + let shift = count_leading_zeros(data as u32); + let index = (((data as u64) << shift) & 0x7fc00000) as u32 >> 22; + result = + (device.rsp.cpu.inverse_square_roots[((index & 0x1fe) | (shift & 1)) as usize]) as u32; + result = (0x10000 | result) << 14; + result = (result >> ((31 - shift) >> 1)) ^ mask as u32 + } + device.rsp.cpu.divdp = false; + device.rsp.cpu.divout = (result >> 16) as i16; + device.rsp.cpu.accl = vte; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + result as u16, + de(opcode) as u8, + ); +} + +pub fn vrsql(device: &mut device::Device, opcode: u32) { + let mut result; + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + let input; + if device.rsp.cpu.divdp { + input = (device.rsp.cpu.divin as i32) << 16 + | get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as u16 + as i32; + } else { + input = get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16 + as i32; + } + let mask = input >> 31; + let mut data = input ^ mask; + if input > -32768 { + data -= mask + } + if data == 0 { + result = 0x7fffffff + } else if input == -32768 { + result = 0xffff0000 + } else { + let shift = count_leading_zeros(data as u32); + let index = (((data as u64) << shift) & 0x7fc00000) as u32 >> 22; + result = + (device.rsp.cpu.inverse_square_roots[((index & 0x1fe) | (shift & 1)) as usize]) as u32; + result = (0x10000 | result) << 14; + result = (result >> ((31 - shift) >> 1)) ^ mask as u32 + } + device.rsp.cpu.divdp = false; + device.rsp.cpu.divout = (result >> 16) as i16; + device.rsp.cpu.accl = vte; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + result as u16, + de(opcode) as u8, + ); +} + +pub fn vrsqh(device: &mut device::Device, opcode: u32) { + let vte = vte(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as usize); + device.rsp.cpu.accl = vte; + device.rsp.cpu.divdp = true; + + device.rsp.cpu.divin = + get_vpr_element(device.rsp.cpu.vpr[vt(opcode) as usize], ve(opcode) as u8) as i16; + modify_vpr_element( + &mut device.rsp.cpu.vpr[vd(opcode) as usize], + device.rsp.cpu.divout as u16, + de(opcode) as u8, + ); +} + +pub fn vnop(_device: &mut device::Device, _opcode: u32) {} + +pub fn execute_vec(device: &mut device::Device, opcode: u32) { + device.rsp.cpu.vec_instrs[(opcode & 0x3F) as usize](device, opcode) +} + +pub fn reserved(_device: &mut device::Device, _opcode: u32) { + panic!("rsp vu reserved") +} diff --git a/src/device/si.rs b/src/device/si.rs new file mode 100644 index 00000000..97cab139 --- /dev/null +++ b/src/device/si.rs @@ -0,0 +1,119 @@ +use crate::device; + +pub const SI_DRAM_ADDR_REG: u32 = 0; +pub const SI_PIF_ADDR_RD64B_REG: u32 = 1; +//pub const SI_R2_REG: u32 = 2; +//pub const SI_R3_REG: u32 = 3; +pub const SI_PIF_ADDR_WR64B_REG: u32 = 4; +//pub const SI_R5_REG: u32 = 5; +pub const SI_STATUS_REG: u32 = 6; +pub const SI_REGS_COUNT: u32 = 7; + +pub const SI_STATUS_DMA_BUSY: u32 = 1 << 0; +pub const SI_STATUS_IO_BUSY: u32 = 1 << 1; +//pub const SI_STATUS_DMA_ERROR: u32 = 1 << 3; +pub const SI_STATUS_INTERRUPT: u32 = 1 << 12; + +#[derive(PartialEq)] +pub enum DmaDir { + None, + Write, + Read, +} + +pub struct Si { + pub regs: [u32; SI_REGS_COUNT as usize], + pub dma_dir: DmaDir, +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + return device.si.regs[((address & 0xFFFF) >> 2) as usize]; +} + +pub fn dma_read(device: &mut device::Device) { + device.si.dma_dir = DmaDir::Read; + + let duration = device::pif::update_pif_ram(device); + + device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY; + + device::events::create_event( + device, + device::events::EventType::SI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + duration, + dma_event, + ) +} + +pub fn dma_write(device: &mut device::Device) { + device.si.dma_dir = DmaDir::Write; + + copy_pif_rdram(device); + + device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_DMA_BUSY; + + device::events::create_event( + device, + device::events::EventType::SI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + 5200, //based on https://github.com/rasky/n64-systembench + dma_event, + ) +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + SI_STATUS_REG => { + device.si.regs[reg as usize] &= !SI_STATUS_INTERRUPT; + device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_SI) + } + SI_PIF_ADDR_RD64B_REG => dma_read(device), + SI_PIF_ADDR_WR64B_REG => dma_write(device), + _ => device::memory::masked_write_32(&mut device.si.regs[reg as usize], value, mask), + } +} + +//rdram is in native endian format, and pif memory is in big endian format +pub fn copy_pif_rdram(device: &mut device::Device) { + let dram_addr = device.si.regs[SI_DRAM_ADDR_REG as usize] as usize & device::rdram::RDRAM_MASK; + if device.si.dma_dir == DmaDir::Write { + let mut i = 0; + while i < device::pif::PIF_RAM_SIZE { + let data = u32::from_ne_bytes( + device.rdram.mem[dram_addr + i..dram_addr + i + 4] + .try_into() + .unwrap(), + ); + device.pif.ram[i..i + 4].copy_from_slice(&data.to_be_bytes()); + i += 4; + } + } else if device.si.dma_dir == DmaDir::Read { + let mut i = 0; + while i < device::pif::PIF_RAM_SIZE { + let data = u32::from_be_bytes(device.pif.ram[i..i + 4].try_into().unwrap()); + device.rdram.mem[dram_addr + i..dram_addr + i + 4].copy_from_slice(&data.to_ne_bytes()); + i += 4; + } + } else { + panic!("si dma unknown") + } +} + +pub fn dma_event(device: &mut device::Device) { + if device.si.dma_dir == DmaDir::Write { + device::pif::process_ram(device); + } else if device.si.dma_dir == DmaDir::Read { + device::si::copy_pif_rdram(device); + } else { + panic!("si dma unknown") + } + device.si.dma_dir = DmaDir::None; + device.si.regs[SI_STATUS_REG as usize] &= !(SI_STATUS_DMA_BUSY | SI_STATUS_IO_BUSY); + device.si.regs[SI_STATUS_REG as usize] |= SI_STATUS_INTERRUPT; + + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_SI) +} diff --git a/src/device/sram.rs b/src/device/sram.rs new file mode 100644 index 00000000..7a06aed2 --- /dev/null +++ b/src/device/sram.rs @@ -0,0 +1,13 @@ +use crate::device; + +pub fn read_mem( + _device: &mut device::Device, + _address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + panic!("sream read"); +} + +pub fn write_mem(_device: &mut device::Device, _address: u64, _value: u32, _mask: u32) { + panic!("sram write"); +} diff --git a/src/device/tlb.rs b/src/device/tlb.rs new file mode 100644 index 00000000..35d8e7a8 --- /dev/null +++ b/src/device/tlb.rs @@ -0,0 +1,269 @@ +use crate::device; + +#[derive(Copy, Clone)] +pub struct TlbLut { + pub address: u64, + pub cached: bool, +} + +#[derive(Copy, Clone)] +pub struct TlbEntry { + pub mask: u64, + pub vpn2: u64, + pub region: u8, + pub g: u8, + pub asid: u8, + pub pfn_even: u64, + pub c_even: u8, + pub d_even: u8, + pub v_even: u8, + pub pfn_odd: u64, + pub c_odd: u8, + pub d_odd: u8, + pub v_odd: u8, + + pub start_even: u64, + pub end_even: u64, + pub phys_even: u64, + pub start_odd: u64, + pub end_odd: u64, + pub phys_odd: u64, +} + +pub fn read(device: &mut device::Device, index: u64) { + if index > 31 { + return; + } + device.cpu.cop0.regs[device::cop0::COP0_PAGEMASK_REG as usize] = + (device.cpu.cop0.tlb_entries[index as usize].mask << 13) as u64; + + device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] = + (device.cpu.cop0.tlb_entries[index as usize].region as u64) << 62 + | device.cpu.cop0.tlb_entries[index as usize].vpn2 << 13 + | (device.cpu.cop0.tlb_entries[index as usize].asid) as u64; + + device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] = + (device.cpu.cop0.tlb_entries[index as usize].pfn_even << 6) + | (device.cpu.cop0.tlb_entries[index as usize].c_even << 3) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].d_even << 2) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].v_even << 1) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].g) as u64; + + device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] = + (device.cpu.cop0.tlb_entries[index as usize].pfn_odd << 6) + | (device.cpu.cop0.tlb_entries[index as usize].c_odd << 3) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].d_odd << 2) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].v_odd << 1) as u64 + | (device.cpu.cop0.tlb_entries[index as usize].g) as u64 +} + +pub fn write(device: &mut device::Device, index: u64) { + if index > 31 { + return; + } + tlb_unmap(device, index); + + device.cpu.cop0.tlb_entries[index as usize].g = (device.cpu.cop0.regs + [device::cop0::COP0_ENTRYLO0_REG as usize] + & device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] + & 1) as u8; + + device.cpu.cop0.tlb_entries[index as usize].pfn_even = + (device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 6) & 0xFFFFF; + device.cpu.cop0.tlb_entries[index as usize].pfn_odd = + (device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 6) & 0xFFFFF; + device.cpu.cop0.tlb_entries[index as usize].c_even = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 3) & 7) as u8; + device.cpu.cop0.tlb_entries[index as usize].c_odd = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 3) & 7) as u8; + device.cpu.cop0.tlb_entries[index as usize].d_even = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 2) & 1) as u8; + device.cpu.cop0.tlb_entries[index as usize].d_odd = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 2) & 1) as u8; + device.cpu.cop0.tlb_entries[index as usize].v_even = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 1) & 1) as u8; + device.cpu.cop0.tlb_entries[index as usize].v_odd = + ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 1) & 1) as u8; + device.cpu.cop0.tlb_entries[index as usize].asid = + device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] as u8; + + device.cpu.cop0.tlb_entries[index as usize].mask = + (device.cpu.cop0.regs[device::cop0::COP0_PAGEMASK_REG as usize] >> 13) & 0xFFF; + + device.cpu.cop0.tlb_entries[index as usize].vpn2 = + (device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 13) & 0x7FFFFFF; + + device.cpu.cop0.tlb_entries[index as usize].region = + (device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 62) as u8; + + device.cpu.cop0.tlb_entries[index as usize].mask &= 0b101010101010; + device.cpu.cop0.tlb_entries[index as usize].mask |= + device.cpu.cop0.tlb_entries[index as usize].mask >> 1; + + device.cpu.cop0.tlb_entries[index as usize].vpn2 &= + !device.cpu.cop0.tlb_entries[index as usize].mask; + + device.cpu.cop0.tlb_entries[index as usize].start_even = + (device.cpu.cop0.tlb_entries[index as usize].vpn2 << 13) & 0xFFFFFFFF; + device.cpu.cop0.tlb_entries[index as usize].end_even = + device.cpu.cop0.tlb_entries[index as usize].start_even + + (device.cpu.cop0.tlb_entries[index as usize].mask << 12) + + 0xFFF; + device.cpu.cop0.tlb_entries[index as usize].phys_even = + device.cpu.cop0.tlb_entries[index as usize].pfn_even << 12; + + device.cpu.cop0.tlb_entries[index as usize].start_odd = + device.cpu.cop0.tlb_entries[index as usize].end_even + 1; + device.cpu.cop0.tlb_entries[index as usize].end_odd = + device.cpu.cop0.tlb_entries[index as usize].start_odd + + (device.cpu.cop0.tlb_entries[index as usize].mask << 12) + + 0xFFF; + device.cpu.cop0.tlb_entries[index as usize].phys_odd = + device.cpu.cop0.tlb_entries[index as usize].pfn_odd << 12; + + tlb_map(device, index); +} + +pub fn probe(device: &mut device::Device) { + device.cpu.cop0.regs[device::cop0::COP0_INDEX_REG as usize] = 0x80000000; // set probe failure + for (pos, e) in device.cpu.cop0.tlb_entries.iter().enumerate() { + if e.vpn2 & !e.mask + != ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 13) & 0x7FFFFFF) + & !e.mask + { + continue; + } + if e.region != (device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 62) as u8 { + continue; + } + if e.g == 0 { + if e.asid != device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] as u8 { + continue; + } + } + device.cpu.cop0.regs[device::cop0::COP0_INDEX_REG as usize] = pos as u64; + break; + } +} + +pub fn tlb_unmap(device: &mut device::Device, index: u64) { + let e = &mut device.cpu.cop0.tlb_entries[index as usize]; + + if e.v_even != 0 { + let mut i = e.start_even; + while i < e.end_even { + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = 0; + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = false; + i += 0x1000 + } + if e.d_even != 0 { + let mut i = e.start_even; + while i < e.end_even { + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = 0; + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = false; + i += 0x1000 + } + } + } + + if e.v_odd != 0 { + let mut i = e.start_odd; + while i < e.end_odd { + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = 0; + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = false; + i += 0x1000 + } + if e.d_odd != 0 { + let mut i = e.start_odd; + while i < e.end_odd { + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = 0; + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = false; + i += 0x1000 + } + } + } +} + +pub fn tlb_map(device: &mut device::Device, index: u64) { + let e = &mut device.cpu.cop0.tlb_entries[index as usize]; + + if e.v_even != 0 { + if e.start_even < e.end_even + && !(e.start_even >= 0x80000000 && e.end_even < 0xC0000000) + && e.phys_even < 0x20000000 + { + let mut i = e.start_even; + while i < e.end_even { + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = + 0x80000000 | (e.phys_even + (i - e.start_even) + 0xFFF) as u64; + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = e.c_even != 2; + i += 0x1000 + } + if e.d_even != 0 { + let mut i = e.start_even; + while i < e.end_even { + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = + 0x80000000 | (e.phys_even + (i - e.start_even) + 0xFFF) as u64; + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = e.c_even != 2; + i += 0x1000 + } + } + } + } + + if e.v_odd != 0 { + if e.start_odd < e.end_odd + && !(e.start_odd >= 0x80000000 && e.end_odd < 0xC0000000) + && e.phys_odd < 0x20000000 + { + let mut i = e.start_odd; + while i < e.end_odd { + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = + 0x80000000 | (e.phys_odd + (i - e.start_odd) + 0xFFF) as u64; + device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = e.c_odd != 2; + i += 0x1000 + } + if e.d_odd != 0 { + let mut i = e.start_odd; + while i < e.end_odd { + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = + 0x80000000 | (e.phys_odd + (i - e.start_odd) + 0xFFF) as u64; + device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = e.c_odd != 2; + i += 0x1000 + } + } + } + } +} + +pub fn get_physical_address( + device: &mut device::Device, + address: u64, + access_type: device::memory::AccessType, +) -> (u64, bool, bool) { + if access_type == device::memory::AccessType::Write { + if device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].address != 0 { + return ( + (device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].address & 0xFFFFF000) + | (address & 0xFFF), + device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].cached, + false, + ); + } + } else { + if device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].address != 0 { + return ( + (device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].address & 0xFFFFF000) + | (address & 0xFFF), + device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].cached, + false, + ); + } + } + + if access_type != device::memory::AccessType::Lookup { + device::exceptions::tlb_miss_exception(device, address, access_type) + } + + return (0, false, true); +} diff --git a/src/device/unmapped.rs b/src/device/unmapped.rs new file mode 100644 index 00000000..26b49d94 --- /dev/null +++ b/src/device/unmapped.rs @@ -0,0 +1,12 @@ +use crate::device; + +pub fn read_mem( + _device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let value = (address & 0xFFFF) as u32; + return value | (value << 16); +} + +pub fn write_mem(_device: &mut device::Device, _address: u64, _value: u32, _mask: u32) {} diff --git a/src/device/vi.rs b/src/device/vi.rs new file mode 100644 index 00000000..f87b1832 --- /dev/null +++ b/src/device/vi.rs @@ -0,0 +1,163 @@ +use crate::device; +use crate::ui; + +pub const VI_STATUS_REG: u32 = 0; +//pub const VI_ORIGIN_REG: u32 = 1; +//pub const VI_WIDTH_REG: u32 = 2; +pub const VI_V_INTR_REG: u32 = 3; +pub const VI_CURRENT_REG: u32 = 4; +//pub const VI_BURST_REG: u32 = 5; +pub const VI_V_SYNC_REG: u32 = 6; +pub const VI_H_SYNC_REG: u32 = 7; +//pub const VI_LEAP_REG: u32 = 8; +//pub const VI_H_START_REG: u32 = 9; +//pub const VI_V_START_REG: u32 = 10; +//pub const VI_V_BURST_REG: u32 = 11; +//pub const VI_X_SCALE_REG: u32 = 12; +//pub const VI_Y_SCALE_REG: u32 = 13; +pub const VI_REGS_COUNT: u32 = 14; + +pub struct Vi { + pub regs: [u32; VI_REGS_COUNT as usize], + pub clock: u64, + pub delay: u64, + pub field: u32, + pub count_per_scanline: u64, + pub last_vi_time: std::time::Instant, + pub vi_period: std::time::Duration, +} + +//static mut FRAME_COUNTER: u64 = 0; + +pub fn set_expected_refresh_rate(device: &mut device::Device) { + let expected_refresh_rate = device.vi.clock as f64 + / (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as f64 + / ((device.vi.regs[VI_H_SYNC_REG as usize] & 0xFFF) + 1) as f64 + * 2.0; + device.vi.delay = (device.cpu.clock_rate as f64 / expected_refresh_rate) as u64; + device.vi.count_per_scanline = + device.vi.delay / (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as u64; + + device.vi.vi_period = std::time::Duration::from_secs_f64(1.0 / expected_refresh_rate); +} + +pub fn set_vertical_interrupt(device: &mut device::Device) { + if device::events::get_event(device, device::events::EventType::VI) == None + && device.vi.regs[VI_V_INTR_REG as usize] < device.vi.regs[VI_V_SYNC_REG as usize] + { + device::events::create_event( + device, + device::events::EventType::VI, + device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.vi.delay, + vertical_interrupt_event, + ) + } +} + +pub fn set_current_line(device: &mut device::Device) { + let delay = device.vi.delay; + let next_vi = device::events::get_event(device, device::events::EventType::VI); + if next_vi != None { + device.vi.regs[VI_CURRENT_REG as usize] = ((delay + - (next_vi.unwrap().count + - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize])) + / device.vi.count_per_scanline) + as u32; + + // wrap around VI_CURRENT_REG if needed + if device.vi.regs[VI_CURRENT_REG as usize] >= device.vi.regs[VI_V_SYNC_REG as usize] { + device.vi.regs[VI_CURRENT_REG as usize] -= device.vi.regs[VI_V_SYNC_REG as usize] + } + } + /* update current field */ + device.vi.regs[VI_CURRENT_REG as usize] = + (device.vi.regs[VI_CURRENT_REG as usize] & !1) | device.vi.field +} + +pub fn read_regs( + device: &mut device::Device, + address: u64, + _access_size: device::memory::AccessSize, +) -> u32 { + let reg = (address & 0xFFFF) >> 2; + if reg as u32 == VI_CURRENT_REG { + set_current_line(device) + } + return device.vi.regs[reg as usize]; +} + +pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { + let reg = (address & 0xFFFF) >> 2; + match reg as u32 { + VI_CURRENT_REG => device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_VI), + VI_V_INTR_REG => { + device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); + set_vertical_interrupt(device); + ui::video::set_register(reg as u32, device.vi.regs[reg as usize]) + } + VI_V_SYNC_REG => { + device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); + set_vertical_interrupt(device); + set_expected_refresh_rate(device); + ui::video::set_register(reg as u32, device.vi.regs[reg as usize]) + } + VI_H_SYNC_REG => { + device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); + set_expected_refresh_rate(device); + ui::video::set_register(reg as u32, device.vi.regs[reg as usize]) + } + _ => { + device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); + ui::video::set_register(reg as u32, device.vi.regs[reg as usize]) + } + } +} + +pub fn vertical_interrupt_event(device: &mut device::Device) { + device.cpu.running = ui::video::update_screen(); + + /* + unsafe { + FRAME_COUNTER += 1; + } + */ + + let elapsed = device.vi.last_vi_time.elapsed(); + if elapsed < device.vi.vi_period { + let diff = device.vi.vi_period - elapsed; + std::thread::sleep(diff); + } + device.vi.last_vi_time = std::time::Instant::now(); + + /* toggle vi field if in interlaced mode */ + device.vi.field ^= (device.vi.regs[VI_STATUS_REG as usize] >> 6) & 0x1; + + device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_VI); + + device::events::create_event( + device, + device::events::EventType::VI, + device.cpu.next_event_count + device.vi.delay, + vertical_interrupt_event, + ) +} + +pub fn init(device: &mut device::Device) { + if device.cart.pal { + device.vi.clock = 49656530 + } else { + device.vi.clock = 48681812 + } + /* + std::thread::spawn(move || { + let mut last_count = 0; + loop { + unsafe { + println!("{:?}", FRAME_COUNTER - last_count); + last_count = FRAME_COUNTER; + } + std::thread::sleep(std::time::Duration::from_secs(1)); + } + }); + */ +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..3c3cad5c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,30 @@ +#![feature(round_ties_even)] +use std::env; +use std::fs; +mod device; +mod ui; + +fn main() { + let args: Vec = env::args().collect(); + let file_path = &args[1]; + let contents = fs::read(file_path).expect("Should have been able to read the file"); + + let mut device = device::Device::new(); + + device::cart_rom::init(&mut device, contents); // cart needs to come before rdram + + // rdram pointer is shared with parallel-rdp + let (rdram_ptr, rdram_size) = device::rdram::init(&mut device); + + ui::audio::init(&mut device.ui, 33600); + ui::video::init(&mut device.ui, rdram_ptr, rdram_size); + + device::mi::init(&mut device); + device::pif::init(&mut device, false); + device::memory::init(&mut device); + device::rsp_interface::init(&mut device); + device::rdp::init(&mut device); + device::vi::init(&mut device); + device::cpu::init(&mut device); + device::cpu::run(&mut device); +} diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 00000000..f0b4f16a --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,32 @@ +pub mod audio; +pub mod input; +pub mod video; + +pub struct Ui { + pub sdl_context: Option, + pub video_subsystem: Option, + pub audio_subsystem: Option, + pub window: Option, + pub audio_device: Option>, +} + +impl Ui { + pub fn new() -> Ui { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + let audio_subsystem = sdl_context.audio().unwrap(); + let window = video_subsystem + .window("gopher64", 640, 480) + .position_centered() + .vulkan() + .build() + .unwrap(); + Ui { + sdl_context: Some(sdl_context), + video_subsystem: Some(video_subsystem), + audio_subsystem: Some(audio_subsystem), + window: Some(window), + audio_device: None, + } + } +} diff --git a/src/ui/audio.rs b/src/ui/audio.rs new file mode 100644 index 00000000..9a4d96a7 --- /dev/null +++ b/src/ui/audio.rs @@ -0,0 +1,50 @@ +use crate::device; +use crate::ui; + +pub fn init(ui: &mut ui::Ui, frequency: u64) { + let desired_spec = sdl2::audio::AudioSpecDesired { + freq: Some(frequency as i32), + channels: Some(2), + samples: None, + }; + ui.audio_device = Some( + ui.audio_subsystem + .as_mut() + .unwrap() + .open_queue::(None, &desired_spec) + .unwrap(), + ); + ui.audio_device.as_mut().unwrap().resume(); +} + +pub fn play_audio(device: &mut device::Device, dram_addr: usize, length: u64) { + let audio_device = device.ui.audio_device.as_mut().unwrap(); + let mut primary_buffer: Vec = vec![0; length as usize / 2]; + let mut i = 0; + while i < length as usize / 2 { + // Left channel + primary_buffer[i] = device.rdram.mem[dram_addr + (i * 2) + 2] as i16 + | (device.rdram.mem[dram_addr + (i * 2) + 3] as i16) << 8; + + // Right channel + primary_buffer[i + 1] = device.rdram.mem[dram_addr + (i * 2)] as i16 + | (device.rdram.mem[dram_addr + (i * 2) + 1] as i16) << 8; + i += 2; + } + + let audio_queued = audio_device.size() as f64; + let acceptable_latency = (audio_device.spec().freq as f64 * 0.2) * 4.0; + let min_latency = (audio_device.spec().freq as f64 * 0.02) * 4.0; + + if audio_device.status() != sdl2::audio::AudioStatus::Paused && audio_queued < min_latency { + audio_device.pause(); + } else if audio_device.status() == sdl2::audio::AudioStatus::Paused + && audio_queued >= (min_latency * 2.0) + { + audio_device.resume(); + } + + if audio_queued < acceptable_latency { + let _ = audio_device.queue_audio(&primary_buffer); + } +} diff --git a/src/ui/input.rs b/src/ui/input.rs new file mode 100644 index 00000000..9d902c62 --- /dev/null +++ b/src/ui/input.rs @@ -0,0 +1,56 @@ +use crate::ui; + +pub const MAX_AXIS_VALUE: f64 = 85.0; + +pub fn bound_axis(x: &mut f64, y: &mut f64) { + let radius = 95.0; // this is roughly the maxium diagonal distance of the controller + + // Calculate the distance from the origin (0, 0) + let distance = f64::sqrt((*x) * (*x) + (*y) * (*y)); + + // If the distance is greater than the radius, scale the coordinates + if distance > radius { + let scale_factor = radius / distance; + *x *= scale_factor; + *y *= scale_factor; + } +} +pub fn get(ui: &mut ui::Ui, _channel: usize) -> u32 { + let context = ui.sdl_context.as_mut().unwrap(); + let events = context.event_pump().unwrap(); + let keyboard_state = events.keyboard_state(); + + let mut keys = 0; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::D) as u32) << 0; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::A) as u32) << 1; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::S) as u32) << 2; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::W) as u32) << 3; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Return) as u32) << 4; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Z) as u32) << 5; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::LCtrl) as u32) << 6; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::LShift) as u32) << 7; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::L) as u32) << 8; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::J) as u32) << 9; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::K) as u32) << 10; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::I) as u32) << 11; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::C) as u32) << 12; + keys |= (keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::X) as u32) << 13; + + let mut x: f64 = 0.0; + let mut y: f64 = 0.0; + if keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Left) { + x = -MAX_AXIS_VALUE + } else if keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Right) { + x = MAX_AXIS_VALUE + } + if keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Down) { + y = -MAX_AXIS_VALUE + } else if keyboard_state.is_scancode_pressed(sdl2::keyboard::Scancode::Up) { + y = MAX_AXIS_VALUE + } + bound_axis(&mut x, &mut y); + + keys |= (x.round() as i8 as u32) << 16; + keys |= (y.round() as i8 as u32) << 24; + return keys; +} diff --git a/src/ui/video.rs b/src/ui/video.rs new file mode 100644 index 00000000..3e98e51d --- /dev/null +++ b/src/ui/video.rs @@ -0,0 +1,40 @@ +use crate::ui; + +extern "C" { + pub fn vk_init(rdram_ptr: usize, rdram_size: u32); + pub fn set_sdl_window(window: usize); + pub fn rdp_update_screen() -> u8; + pub fn rdp_set_vi_register(reg: u32, value: u32); + pub fn rdp_process_commands(dpc_regs: &mut [u32; 8], SP_DMEM: &mut [u8; 8192]) -> u64; + pub fn full_sync(); +} + +pub fn init(ui: &mut ui::Ui, rdram_ptr: *mut u8, rdram_size: usize) { + unsafe { + set_sdl_window(ui.window.as_mut().unwrap().raw() as usize); + vk_init(rdram_ptr as usize, rdram_size as u32) + } +} + +pub fn update_screen() -> u8 { + // when the window is closed, running is set to 0 + let running = unsafe { rdp_update_screen() }; + return running; +} + +pub fn set_register(reg: u32, value: u32) { + unsafe { + rdp_set_vi_register(reg, value); + } +} + +pub fn process_rdp_list(dpc_regs: &mut [u32; 8], sp_dmem: &mut [u8; 8192]) -> u64 { + let timer = unsafe { rdp_process_commands(dpc_regs, sp_dmem) }; + return timer; +} + +pub fn rdp_full_sync() { + unsafe { + full_sync(); + } +}