mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
Initial Commit
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
+19
@@ -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
|
||||
@@ -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
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"./Cargo.toml"
|
||||
]
|
||||
}
|
||||
+17
@@ -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
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -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");
|
||||
}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
RUSTFLAGS='-C target-cpu=x86-64-v3' cargo build --release
|
||||
@@ -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<const uint32_t *>(SP_DMEM + offset));
|
||||
cmd_data[2 * cmd_ptr + 1] = SDL_SwapBE32(*reinterpret_cast<const uint32_t *>(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<const uint32_t *>((uint8_t *)rdram + offset);
|
||||
cmd_data[2 * cmd_ptr + 1] = *reinterpret_cast<const uint32_t *>((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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Submodule
+1
Submodule parallel-rdp/parallel-rdp-standalone added at 37bf2967e3
@@ -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};
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "wsi_platform.hpp"
|
||||
#include <SDL_vulkan.h>
|
||||
|
||||
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<const char *> SDL_WSIPlatform::get_instance_extensions()
|
||||
{
|
||||
|
||||
unsigned int extensionCount = 0;
|
||||
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
|
||||
std::vector<const char *> 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;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "wsi.hpp"
|
||||
#include <SDL.h>
|
||||
|
||||
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<const char *> 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;
|
||||
};
|
||||
+262
@@ -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),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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<u8>,
|
||||
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<u8>) {
|
||||
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: Hash>(t: &[T]) -> u64 {
|
||||
let mut s = DefaultHasher::new();
|
||||
t.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<device::tlb::TlbLut>,
|
||||
pub tlb_lut_w: Vec<device::tlb::TlbLut>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
@@ -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<device::events::Event>,
|
||||
pub next_event_count: u64,
|
||||
pub next_event: Option<device::events::Event>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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<usize>,
|
||||
pub tx_buf: Option<usize>,
|
||||
pub rx: Option<usize>,
|
||||
pub rx_buf: Option<usize>,
|
||||
pub process: Option<fn(&mut device::Device, usize)>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -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,
|
||||
];
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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<u8>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {}
|
||||
@@ -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));
|
||||
}
|
||||
});
|
||||
*/
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
#![feature(round_ties_even)]
|
||||
use std::env;
|
||||
use std::fs;
|
||||
mod device;
|
||||
mod ui;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = 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);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
pub mod audio;
|
||||
pub mod input;
|
||||
pub mod video;
|
||||
|
||||
pub struct Ui {
|
||||
pub sdl_context: Option<sdl2::Sdl>,
|
||||
pub video_subsystem: Option<sdl2::VideoSubsystem>,
|
||||
pub audio_subsystem: Option<sdl2::AudioSubsystem>,
|
||||
pub window: Option<sdl2::video::Window>,
|
||||
pub audio_device: Option<sdl2::audio::AudioQueue<i16>>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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::<i16, _>(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<i16> = 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user