From 2e4a23c31508aaf3de782852c00dc812f9c7f137 Mon Sep 17 00:00:00 2001
From: Logan McNaughton <848146+loganmc10@users.noreply.github.com>
Date: Wed, 4 Oct 2023 14:33:17 -0600
Subject: [PATCH] Initial Commit
---
.github/dependabot.yml | 10 +
.gitignore | 19 +
.gitmodules | 3 +
.vscode/settings.json | 5 +
Cargo.toml | 17 +
LICENSE | 674 ++++++++++++
build.rs | 58 ++
build_release.sh | 3 +
parallel-rdp/interface.cpp | 405 ++++++++
parallel-rdp/interface.hpp | 18 +
parallel-rdp/parallel-rdp-standalone | 1 +
parallel-rdp/spirv.hpp | 109 ++
parallel-rdp/wsi_platform.cpp | 60 ++
parallel-rdp/wsi_platform.hpp | 21 +
src/device.rs | 262 +++++
src/device/ai.rs | 169 ++++
src/device/cache.rs | 281 +++++
src/device/cart.rs | 55 +
src/device/cart_rom.rs | 151 +++
src/device/controller.rs | 93 ++
src/device/cop0.rs | 377 +++++++
src/device/cop1.rs | 719 +++++++++++++
src/device/cop2.rs | 137 +++
src/device/cpu.rs | 284 ++++++
src/device/cpu_instructions.rs | 1406 ++++++++++++++++++++++++++
src/device/events.rs | 88 ++
src/device/exceptions.rs | 136 +++
src/device/fpu_instructions.rs | 700 +++++++++++++
src/device/is_viewer.rs | 37 +
src/device/memory.rs | 164 +++
src/device/mi.rs | 168 +++
src/device/pi.rs | 164 +++
src/device/pif.rs | 222 ++++
src/device/pif/rom.rs | 177 ++++
src/device/rdp.rs | 165 +++
src/device/rdram.rs | 86 ++
src/device/ri.rs | 34 +
src/device/rsp_cpu.rs | 429 ++++++++
src/device/rsp_interface.rs | 432 ++++++++
src/device/rsp_su_instructions.rs | 1022 +++++++++++++++++++
src/device/rsp_vu_instructions.rs | 1253 +++++++++++++++++++++++
src/device/si.rs | 119 +++
src/device/sram.rs | 13 +
src/device/tlb.rs | 269 +++++
src/device/unmapped.rs | 12 +
src/device/vi.rs | 163 +++
src/main.rs | 30 +
src/ui.rs | 32 +
src/ui/audio.rs | 50 +
src/ui/input.rs | 56 +
src/ui/video.rs | 40 +
51 files changed, 11398 insertions(+)
create mode 100644 .github/dependabot.yml
create mode 100644 .gitignore
create mode 100644 .gitmodules
create mode 100644 .vscode/settings.json
create mode 100644 Cargo.toml
create mode 100644 LICENSE
create mode 100644 build.rs
create mode 100755 build_release.sh
create mode 100644 parallel-rdp/interface.cpp
create mode 100644 parallel-rdp/interface.hpp
create mode 160000 parallel-rdp/parallel-rdp-standalone
create mode 100644 parallel-rdp/spirv.hpp
create mode 100644 parallel-rdp/wsi_platform.cpp
create mode 100644 parallel-rdp/wsi_platform.hpp
create mode 100644 src/device.rs
create mode 100644 src/device/ai.rs
create mode 100644 src/device/cache.rs
create mode 100644 src/device/cart.rs
create mode 100644 src/device/cart_rom.rs
create mode 100644 src/device/controller.rs
create mode 100644 src/device/cop0.rs
create mode 100644 src/device/cop1.rs
create mode 100644 src/device/cop2.rs
create mode 100644 src/device/cpu.rs
create mode 100644 src/device/cpu_instructions.rs
create mode 100644 src/device/events.rs
create mode 100644 src/device/exceptions.rs
create mode 100644 src/device/fpu_instructions.rs
create mode 100644 src/device/is_viewer.rs
create mode 100644 src/device/memory.rs
create mode 100644 src/device/mi.rs
create mode 100644 src/device/pi.rs
create mode 100644 src/device/pif.rs
create mode 100644 src/device/pif/rom.rs
create mode 100644 src/device/rdp.rs
create mode 100644 src/device/rdram.rs
create mode 100644 src/device/ri.rs
create mode 100644 src/device/rsp_cpu.rs
create mode 100644 src/device/rsp_interface.rs
create mode 100644 src/device/rsp_su_instructions.rs
create mode 100644 src/device/rsp_vu_instructions.rs
create mode 100644 src/device/si.rs
create mode 100644 src/device/sram.rs
create mode 100644 src/device/tlb.rs
create mode 100644 src/device/unmapped.rs
create mode 100644 src/device/vi.rs
create mode 100644 src/main.rs
create mode 100644 src/ui.rs
create mode 100644 src/ui/audio.rs
create mode 100644 src/ui/input.rs
create mode 100644 src/ui/video.rs
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();
+ }
+}