mirror of
https://github.com/Tinob/Ishiiruka.git
synced 2024-05-13 10:09:38 -04:00
merge latest master changes
This commit is contained in:
parent
d940e33a50
commit
0c98f9aedb
54
.mailmap
Normal file
54
.mailmap
Normal file
|
@ -0,0 +1,54 @@
|
|||
# This file exists to enable "git shortlog -s" to group by person.
|
||||
Andrew de los Reyes <adlr@gmail.com> <adlr@chromium.org>
|
||||
Augustin Cavalier <waddlesplash@gmail.com> <ajcsweb@gmail.com>
|
||||
Benjamin Przybocki <buddybenj@gmail.com>
|
||||
Dolphin Bots <noreply@dolphin-emu.org> <dolphin-emu-bot@users.noreply.github.com>
|
||||
Dolphin Bots <noreply@dolphin-emu.org> <contact+i18n@dolphin-emu.org>
|
||||
Dolphin Bots <noreply@dolphin-emu.org> <buildbot@ubuntu.ubuntu.dolphin-emu.org>
|
||||
Grant Paul <git@grantpaul.com> <chpwn@chpwn.com>
|
||||
Henrik Rydgård <hrydgard@gmail.com>
|
||||
Jack Frost <bhaal@0x1337.org> <j4ck.fr0st@gmail.com>
|
||||
James Dunne <james.jdunne@gmail.com>
|
||||
John Chadwick <johnwchadwick@gmail.com> <johnwchadwick>
|
||||
John Peterson <john.s.peterson@live.com>
|
||||
John Peterson <john.s.peterson@live.com> <jpeterson57@gmail.com>
|
||||
Jordan Cristiano <jordan.cristi@gmail.com> <jordan.cristi AT [Google mail]>
|
||||
Jordan Cristiano <jordan.cristi@gmail.com> <jordan.cristi [AT] gmail.com>
|
||||
Jordan Woyak <jordan.woyak@gmail.com> <jordan@bill-laptop.lan>
|
||||
Jordan Woyak <jordan.woyak@gmail.com> <Billiard26@gmail.com>
|
||||
Jules Blok <jules.blok@gmail.com> Armada <jules.blok@gmail.com>
|
||||
Lioncash <mathew1800@gmail.com>
|
||||
Lioncash <mathew1800@gmail.com> Lioncash <nope>
|
||||
Lioncash <mathew1800@gmail.com> <mathew1900@hotmail.com>
|
||||
Maarten ter Huurne <maarten@treewalker.org> <mth@lexx-9122.trinair2002>
|
||||
Marcos Vitali <marcosvitali@gmail.com>
|
||||
Markus Wick <markus+github@selfnet.de> <wickmarkus@web.de>
|
||||
Matthew Parlane <parlane@gmail.com>
|
||||
Matthew Parlane <parlane@gmail.com> <matthew@phantuntu.(none)>
|
||||
Oussama Danba <Shadoxfix@gmail.com>
|
||||
Pascal Jouy <pascal.jouy@hotmail.fr>
|
||||
Pierre <pierre@pirsoft.de>
|
||||
Pierre Bourdon <delroth@gmail.com> <delroth@lse.epita.fr>
|
||||
Rodolfo Bogado <rodolfoosvaldobogado@gmail.com>
|
||||
Rog <rdragoon@optonline.net>
|
||||
RolandMunsil <roland@munsil.com> <RolandMunsil@users.noreply.github.com>
|
||||
Runo <i.am.runo@hotmail.com>
|
||||
Ryan Houdek <sonicadvance1@gmail.com>
|
||||
Ryan Houdek <sonicadvance1@gmail.com> <Sonicadvance1@gmail.com>
|
||||
Ryan Houdek <sonicadvance1@gmail.com> <ryan.houdek@codethink.co.uk>
|
||||
Ryan Houdek <sonicadvance1@gmail.com> <sonicadvance1@Ryan-Desktop.(none)>
|
||||
Sacha <xsacha@gmail.com>
|
||||
Sean Maas <seanmaas27@gmail.com>
|
||||
Shawn Hoffman <godisgovernment@gmail.com>
|
||||
Steven V. <Stevoisiak@gmail.com>
|
||||
Steven V. <Stevoisiak@gmail.com> <S.Vascellaro@gmail.com>
|
||||
Tony Wasserka <neobrainx@gmail.com> <NeoBrainX@googlemail.com>
|
||||
Tony Wasserka <neobrainx@gmail.com> <NeoBrainX@gmail.com>
|
||||
TotalNerd <xtrafear@gmail.com>
|
||||
booto <remornicus@gmail.com> <booto+dolphin@justanothercoder.com>
|
||||
i418c <starfield94@aol.com> <i418c@users.noreply.github.com>
|
||||
luisr142004 <luisr142004@gmail.com> <luisr142004@yahoo.com>
|
||||
magumagu <magumagu9@gmail.com>
|
||||
masken <masken3@gmail.com> <masken@emulation64.com>
|
||||
nitsuja <nitsuja-@hotmail.com>
|
||||
skidau <skidau@gmail.com>
|
212
Data/Sys/GC/font-licenses.txt
Normal file
212
Data/Sys/GC/font-licenses.txt
Normal file
|
@ -0,0 +1,212 @@
|
|||
The two fonts in this directory (font_ansi.bin and font_sjis.bin) were
|
||||
generated using gc-font-tool which can be found in the docs/ directory in the
|
||||
dolphin source code.
|
||||
|
||||
Both fonts are based on Droid Sans
|
||||
|
||||
Copyright 2006-2014, Google Corporation
|
||||
Licensed under the Apache License 2.0
|
||||
|
||||
====
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
Binary file not shown.
Binary file not shown.
|
@ -1,19 +0,0 @@
|
|||
# DVDXDV - Unknown
|
||||
|
||||
[Core]
|
||||
# Values set here will override the main dolphin settings.
|
||||
|
||||
[EmuState]
|
||||
# The Emulation State. 1 is worst, 5 is best, 0 is not set.
|
||||
EmulationStateId = 0
|
||||
EmulationIssues =
|
||||
|
||||
[OnLoad]
|
||||
# Add memory patches to be loaded once on boot here.
|
||||
|
||||
[OnFrame]
|
||||
# Add memory patches to be applied every frame here.
|
||||
|
||||
[ActionReplay]
|
||||
# Add action replay cheats here.
|
||||
|
|
@ -368,3 +368,6 @@ EFBCopyEnable = True
|
|||
|
||||
[Video_Settings]
|
||||
FastDepthCalc = False
|
||||
|
||||
[Video_Stereoscopy]
|
||||
StereoConvergenceMinimum = 115
|
||||
|
|
|
@ -377,7 +377,7 @@ void ARM64XEmitter::EncodeLoadStoreExcInst(u32 instenc,
|
|||
void ARM64XEmitter::EncodeLoadStorePairedInst(u32 op, ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn, u32 imm)
|
||||
{
|
||||
bool b64Bit = Is64Bit(Rt);
|
||||
bool b128Bit = Is128Bit(Rt);
|
||||
bool b128Bit = IsQuad(Rt);
|
||||
bool bVec = IsVector(Rt);
|
||||
|
||||
if (b128Bit)
|
||||
|
@ -417,15 +417,17 @@ void ARM64XEmitter::EncodeLoadStoreIndexedInst(u32 op, u32 op2, ARM64Reg Rt, ARM
|
|||
Write32((b64Bit << 30) | (op << 22) | (bVec << 26) | (offset << 12) | (op2 << 10) | (Rn << 5) | Rt);
|
||||
}
|
||||
|
||||
void ARM64XEmitter::EncodeLoadStoreIndexedInst(u32 op, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
void ARM64XEmitter::EncodeLoadStoreIndexedInst(u32 op, ARM64Reg Rt, ARM64Reg Rn, s32 imm, u8 size)
|
||||
{
|
||||
bool b64Bit = Is64Bit(Rt);
|
||||
bool bVec = IsVector(Rt);
|
||||
|
||||
if (b64Bit)
|
||||
if (size == 64)
|
||||
imm >>= 3;
|
||||
else
|
||||
else if (size == 32)
|
||||
imm >>= 2;
|
||||
else if (size == 16)
|
||||
imm >>= 1;
|
||||
|
||||
_assert_msg_(DYNA_REC, imm < 0, "%s(INDEX_UNSIGNED): offset must be positive", __FUNCTION__);
|
||||
_assert_msg_(DYNA_REC, !(imm & ~0xFFF), "%s(INDEX_UNSIGNED): offset too large %d", __FUNCTION__, imm);
|
||||
|
@ -799,7 +801,7 @@ void ARM64XEmitter::ADD(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Optio
|
|||
|
||||
void ARM64XEmitter::ADDS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
ADD(Rd, Rn, Rm, ArithOption(Rd));
|
||||
EncodeArithmeticInst(0, true, Rd, Rn, Rm, ArithOption(Rd));
|
||||
}
|
||||
|
||||
void ARM64XEmitter::ADDS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Option)
|
||||
|
@ -819,7 +821,7 @@ void ARM64XEmitter::SUB(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Optio
|
|||
|
||||
void ARM64XEmitter::SUBS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
SUB(Rd, Rn, Rm, ArithOption(Rd));
|
||||
EncodeArithmeticInst(1, false, Rd, Rn, Rm, ArithOption(Rd));
|
||||
}
|
||||
|
||||
void ARM64XEmitter::SUBS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Option)
|
||||
|
@ -1020,6 +1022,14 @@ void ARM64XEmitter::UMULH(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra)
|
|||
{
|
||||
EncodeData3SrcInst(7, Rd, Rn, Rm, Ra);
|
||||
}
|
||||
void ARM64XEmitter::MUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EncodeData3SrcInst(0, Rd, Rn, Rm, SP);
|
||||
}
|
||||
void ARM64XEmitter::MNEG(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EncodeData3SrcInst(1, Rd, Rn, Rm, SP);
|
||||
}
|
||||
|
||||
// Logical (shifted register)
|
||||
void ARM64XEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Shift)
|
||||
|
@ -1282,7 +1292,7 @@ void ARM64XEmitter::LDNP(ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn, u32 imm)
|
|||
void ARM64XEmitter::STRB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(0x0E4, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(0x0E4, Rt, Rn, imm, 8);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(0x0E0,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1290,7 +1300,7 @@ void ARM64XEmitter::STRB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDRB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(0x0E5, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(0x0E5, Rt, Rn, imm, 8);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(0x0E1,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1298,7 +1308,7 @@ void ARM64XEmitter::LDRB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDRSB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x0E6 : 0x0E7, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x0E6 : 0x0E7, Rt, Rn, imm, 8);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x0E2 : 0x0E3,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1306,7 +1316,7 @@ void ARM64XEmitter::LDRSB(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::STRH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(0x1E4, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(0x1E4, Rt, Rn, imm, 16);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(0x1E0,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1314,7 +1324,7 @@ void ARM64XEmitter::STRH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDRH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(0x1E5, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(0x1E5, Rt, Rn, imm, 16);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(0x1E1,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1322,7 +1332,7 @@ void ARM64XEmitter::LDRH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDRSH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x1E6 : 0x1E7, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x1E6 : 0x1E7, Rt, Rn, imm, 16);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x1E2 : 0x1E3,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1330,7 +1340,7 @@ void ARM64XEmitter::LDRSH(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::STR(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E4 : 0x2E4, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E4 : 0x2E4, Rt, Rn, imm, Is64Bit(Rt) ? 64 : 32);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E0 : 0x2E0,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1338,7 +1348,7 @@ void ARM64XEmitter::STR(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDR(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E5 : 0x2E5, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E5 : 0x2E5, Rt, Rn, imm, Is64Bit(Rt) ? 64 : 32);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(Is64Bit(Rt) ? 0x3E1 : 0x2E1,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1346,7 +1356,7 @@ void ARM64XEmitter::LDR(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
|||
void ARM64XEmitter::LDRSW(IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
if (type == INDEX_UNSIGNED)
|
||||
EncodeLoadStoreIndexedInst(0x2E6, Rt, Rn, imm);
|
||||
EncodeLoadStoreIndexedInst(0x2E6, Rt, Rn, imm, 32);
|
||||
else
|
||||
EncodeLoadStoreIndexedInst(0x2E2,
|
||||
type == INDEX_POST ? 1 : 3, Rt, Rn, imm);
|
||||
|
@ -1431,7 +1441,7 @@ void ARM64XEmitter::MOVI2R(ARM64Reg Rd, u64 imm, bool optimize)
|
|||
// Max unsigned value
|
||||
// Set to ~ZR
|
||||
ARM64Reg ZR = Is64Bit(Rd) ? SP : WSP;
|
||||
ORN(Rd, Rd, ZR, ArithOption(ZR, ST_LSL, 0));
|
||||
ORN(Rd, ZR, ZR, ArithOption(ZR, ST_LSL, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1597,5 +1607,498 @@ void ARM64XEmitter::ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask)
|
|||
}
|
||||
}
|
||||
|
||||
// Float Emitter
|
||||
void ARM64FloatEmitter::EmitLoadStoreImmediate(u8 size, u32 opc, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
Rt = DecodeReg(Rt);
|
||||
Rn = DecodeReg(Rn);
|
||||
u32 encoded_size = 0;
|
||||
u32 encoded_imm = 0;
|
||||
|
||||
if (size == 8)
|
||||
encoded_size = 0;
|
||||
else if (size == 16)
|
||||
encoded_size = 1;
|
||||
else if (size == 32)
|
||||
encoded_size = 2;
|
||||
else if (size == 64)
|
||||
encoded_size = 3;
|
||||
else if (size == 128)
|
||||
encoded_size = 0;
|
||||
|
||||
if (type == INDEX_UNSIGNED)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, imm & (size - 1), "%s(INDEX_UNSIGNED) immediate offset must be aligned to size!", __FUNCTION__);
|
||||
_assert_msg_(DYNA_REC, imm < 0, "%s(INDEX_UNSIGNED) immediate offset must be positive!", __FUNCTION__);
|
||||
if (size == 16)
|
||||
imm >>= 1;
|
||||
else if (size == 32)
|
||||
imm >>= 2;
|
||||
else if (size == 64)
|
||||
imm >>= 3;
|
||||
else if (size == 128)
|
||||
imm >>= 4;
|
||||
encoded_imm = (imm & 0xFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
_assert_msg_(DYNA_REC, imm < -256 || imm > 255, "%s immediate offset must be within range of -256 to 256!", __FUNCTION__);
|
||||
encoded_imm = (imm & 0x1FF) << 2;
|
||||
if (type == INDEX_POST)
|
||||
encoded_imm |= 1;
|
||||
else
|
||||
encoded_imm |= 3;
|
||||
}
|
||||
|
||||
Write32((encoded_size << 30) | (0b1111 << 26) | (type == INDEX_UNSIGNED ? (1 << 24) : 0) | \
|
||||
(size == 128 ? (1 << 23) : 0) | (opc << 22) | (encoded_imm << 10) | (Rn << 5) | Rt);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::Emit2Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsQuad(Rd), "%s only supports double and single registers!", __FUNCTION__);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
Rm = DecodeReg(Rm);
|
||||
|
||||
Write32((M << 31) | (S << 29) | (0b11110001 << 21) | (type << 22) | (Rm << 16) | \
|
||||
(opcode << 12) | (1 << 11) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitThreeSame(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
|
||||
bool quad = IsQuad(Rd);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
Rm = DecodeReg(Rm);
|
||||
|
||||
Write32((quad << 30) | (U << 29) | (0b1110001 << 21) | (size << 22) | \
|
||||
(Rm << 16) | (opcode << 11) | (1 << 10) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitCopy(bool Q, u32 op, u32 imm5, u32 imm4, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, Rn <= SP, "%s only supports VFP registers!", __FUNCTION__);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((Q << 30) | (op << 29) | (0b111 << 25) | (imm5 << 16) | (imm4 << 11) | \
|
||||
(1 << 10) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::Emit2RegMisc(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
|
||||
bool quad = IsQuad(Rd);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((quad << 30) | (U << 29) | (0b1110001 << 21) | (size << 22) | \
|
||||
(opcode << 12) | (1 << 11) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsSingle(Rt), "%s doesn't support singles!", __FUNCTION__);
|
||||
bool quad = IsQuad(Rt);
|
||||
Rt = DecodeReg(Rt);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((quad << 30) | (0b1101 << 24) | (L << 22) | (R << 21) | (opcode << 13) | \
|
||||
(S << 12) | (size << 10) | (Rn << 5) | Rt);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::Emit1Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsQuad(Rd), "%s doesn't support vector!", __FUNCTION__);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((M << 31) | (S << 29) | (0b11110001 << 21) | (type << 22) | (opcode << 15) | \
|
||||
(1 << 14) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitConversion(bool sf, bool S, u32 type, u32 rmode, u32 opcode, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, !(Rn <= SP), "%s only supports GPR as source!", __FUNCTION__);
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
|
||||
Write32((sf << 31) | (S << 29) | (0b11110001 << 21) | (type << 22) | (rmode << 19) | \
|
||||
(opcode << 16) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitCompare(bool M, bool S, u32 op, u32 opcode2, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
bool is_double = !IsSingle(Rn);
|
||||
|
||||
Rn = DecodeReg(Rn);
|
||||
Rm = DecodeReg(Rm);
|
||||
|
||||
Write32((M << 31) | (S << 29) | (0b11110001 << 21) | (is_double << 22) | (Rm << 16) | \
|
||||
(op << 14) | (1 << 13) | (Rn << 5) | opcode2);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
bool is_double = !IsSingle(Rd);
|
||||
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
Rm = DecodeReg(Rm);
|
||||
|
||||
Write32((M << 31) | (S << 29) | (0b11110001 << 21) | (is_double << 22) | (Rm << 16) | \
|
||||
(cond << 12) | (0b11 << 10) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
_assert_msg_(DYNA_REC, IsSingle(Rd), "%s doesn't support singles!", __FUNCTION__);
|
||||
|
||||
bool quad = IsQuad(Rd);
|
||||
|
||||
u32 encoded_size = 0;
|
||||
if (size == 16)
|
||||
encoded_size = 1;
|
||||
else if (size == 32)
|
||||
encoded_size = 2;
|
||||
else if (size == 64)
|
||||
encoded_size = 3;
|
||||
|
||||
Rd = DecodeReg(Rd);
|
||||
Rn = DecodeReg(Rn);
|
||||
Rm = DecodeReg(Rm);
|
||||
|
||||
Write32((quad << 30) | (0b111 << 25) | (encoded_size << 22) | (Rm << 16) | (op << 12) | \
|
||||
(1 << 11) | (Rn << 5) | Rd);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::LDR(u8 size, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
EmitLoadStoreImmediate(size, 1, type, Rt, Rn, imm);
|
||||
}
|
||||
void ARM64FloatEmitter::STR(u8 size, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm)
|
||||
{
|
||||
EmitLoadStoreImmediate(size, 0, type, Rt, Rn, imm);
|
||||
}
|
||||
|
||||
// Loadstore single structure
|
||||
void ARM64FloatEmitter::LD1R(u8 size, ARM64Reg Rt, ARM64Reg Rn)
|
||||
{
|
||||
EmitLoadStoreSingleStructure(1, 0, 0b110, 0, size >> 4, Rt, Rn);
|
||||
}
|
||||
|
||||
// Scalar - 2 Source
|
||||
void ARM64FloatEmitter::FMUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
Emit2Source(0, 0, IsDouble(Rd), 0, Rd, Rn, Rm);
|
||||
}
|
||||
|
||||
// Vector
|
||||
void ARM64FloatEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(0, 0, 0b00011, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::BSL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, 1, 0b00011, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index)
|
||||
{
|
||||
u32 imm5 = 0;
|
||||
|
||||
if (size == 8)
|
||||
{
|
||||
imm5 = 1;
|
||||
imm5 |= index << 1;
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
imm5 = 2;
|
||||
imm5 |= index << 2;
|
||||
}
|
||||
else if (size == 32)
|
||||
{
|
||||
imm5 = 4;
|
||||
imm5 |= index << 3;
|
||||
}
|
||||
else if (size == 64)
|
||||
{
|
||||
imm5 = 8;
|
||||
imm5 |= index << 4;
|
||||
}
|
||||
|
||||
EmitCopy(IsQuad(Rd), 0, imm5, 0, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FABS(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, 2 | (size >> 6), 0b01111, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FADD(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(0, size >> 6, 0b11010, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCVTL(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, size >> 6, 0b10111, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, size >> 6, 0b11111, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, size >> 6, 0b11011, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, 2 | (size >> 6), 0b01111, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FRSQRTE(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, 2 | (size >> 6), 0b11101, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FSUB(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(0, 2 | (size >> 6), 0b11010, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::NOT(ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, 0, 0b00101, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::ORR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(0, 2, 0b00011, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::REV16(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, 1 | (size >> 4), 0, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::REV32(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, size >> 4, 0, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::REV64(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, size >> 4, 0, Rd, Rn);
|
||||
}
|
||||
|
||||
// Move
|
||||
void ARM64FloatEmitter::DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
u32 imm5 = 0;
|
||||
|
||||
if (size == 8)
|
||||
imm5 = 1;
|
||||
else if (size == 16)
|
||||
imm5 = 2;
|
||||
else if (size == 32)
|
||||
imm5 = 4;
|
||||
else if (size == 64)
|
||||
imm5 = 8;
|
||||
|
||||
EmitCopy(IsQuad(Rd), 0, imm5, 0b0001, Rd, Rn);
|
||||
|
||||
}
|
||||
void ARM64FloatEmitter::INS(u8 size, ARM64Reg Rd, u8 index, ARM64Reg Rn)
|
||||
{
|
||||
u32 imm5 = 0;
|
||||
|
||||
if (size == 8)
|
||||
{
|
||||
imm5 = 1;
|
||||
imm5 |= index << 1;
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
imm5 = 2;
|
||||
imm5 |= index << 2;
|
||||
}
|
||||
else if (size == 32)
|
||||
{
|
||||
imm5 = 4;
|
||||
imm5 |= index << 3;
|
||||
}
|
||||
else if (size == 64)
|
||||
{
|
||||
imm5 = 8;
|
||||
imm5 |= index << 4;
|
||||
}
|
||||
|
||||
EmitCopy(1, 0, imm5, 0b0011, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::INS(u8 size, ARM64Reg Rd, u8 index1, ARM64Reg Rn, u8 index2)
|
||||
{
|
||||
u32 imm5 = 0, imm4 = 0;
|
||||
|
||||
if (size == 8)
|
||||
{
|
||||
imm5 = 1;
|
||||
imm5 |= index1 << 1;
|
||||
imm4 = index2;
|
||||
}
|
||||
else if (size == 16)
|
||||
{
|
||||
imm5 = 2;
|
||||
imm5 |= index1 << 2;
|
||||
imm4 = index2 << 1;
|
||||
}
|
||||
else if (size == 32)
|
||||
{
|
||||
imm5 = 4;
|
||||
imm5 |= index1 << 3;
|
||||
imm4 = index2 << 2;
|
||||
}
|
||||
else if (size == 64)
|
||||
{
|
||||
imm5 = 8;
|
||||
imm5 |= index1 << 4;
|
||||
imm4 = index2 << 3;
|
||||
}
|
||||
|
||||
EmitCopy(1, 1, imm5, imm4, Rd, Rn);
|
||||
}
|
||||
|
||||
// One source
|
||||
void ARM64FloatEmitter::FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
u32 dst_encoding = 0;
|
||||
u32 src_encoding = 0;
|
||||
|
||||
if (size_to == 16)
|
||||
dst_encoding = 3;
|
||||
else if (size_to == 32)
|
||||
dst_encoding = 0;
|
||||
else if (size_to == 64)
|
||||
dst_encoding = 1;
|
||||
|
||||
if (size_from == 16)
|
||||
src_encoding = 3;
|
||||
else if (size_from == 32)
|
||||
src_encoding = 0;
|
||||
else if (size_from == 64)
|
||||
src_encoding = 1;
|
||||
|
||||
Emit1Source(0, 0, src_encoding, 0b100 | dst_encoding, Rd, Rn);
|
||||
}
|
||||
|
||||
// Conversion between float and integer
|
||||
void ARM64FloatEmitter::FMOV(u8 size, bool top, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
bool sf = size == 64 ? true : false;
|
||||
u32 type = 0;
|
||||
u32 rmode = top ? 1 : 0;
|
||||
if (size == 64)
|
||||
{
|
||||
if (top)
|
||||
type = 2;
|
||||
else
|
||||
type = 1;
|
||||
}
|
||||
|
||||
EmitConversion(sf, 0, type, rmode, IsVector(Rd) ? 0b111 : 0b110, Rd, Rn);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::FCMP(ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitCompare(0, 0, 0, 0, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMP(ARM64Reg Rn)
|
||||
{
|
||||
EmitCompare(0, 0, 0, 0b01000, Rn, (ARM64Reg)0);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMPE(ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitCompare(0, 0, 0, 0b10000, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMPE(ARM64Reg Rn)
|
||||
{
|
||||
EmitCompare(0, 0, 0, 0b11000, Rn, (ARM64Reg)0);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(0, size >> 6, 0b11100, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, 2 | (size >> 6), 0b01101, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, size >> 6, 0b11100, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, 2 | (size >> 6), 0b01100, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitThreeSame(1, 2 | (size >> 6), 0b11100, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, 2 | (size >> 6), 0b01100, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMLE(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(1, 2 | (size >> 6), 0b01101, Rd, Rn);
|
||||
}
|
||||
void ARM64FloatEmitter::FCMLT(u8 size, ARM64Reg Rd, ARM64Reg Rn)
|
||||
{
|
||||
Emit2RegMisc(0, 2 | (size >> 6), 0b01110, Rd, Rn);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::FCSEL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, CCFlags cond)
|
||||
{
|
||||
EmitCondSelect(0, 0, cond, Rd, Rn, Rm);
|
||||
}
|
||||
|
||||
// Permute
|
||||
void ARM64FloatEmitter::UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b001, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::TRN1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b010, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::ZIP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b011, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::UZP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b101, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::TRN2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b110, Rd, Rn, Rm);
|
||||
}
|
||||
void ARM64FloatEmitter::ZIP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm)
|
||||
{
|
||||
EmitPermute(size, 0b111, Rd, Rn, Rm);
|
||||
}
|
||||
|
||||
void ARM64FloatEmitter::ABI_PushRegisters(BitSet32 registers)
|
||||
{
|
||||
for (auto it : registers)
|
||||
STR(128, INDEX_PRE, (ARM64Reg)(Q0 + it), SP, -16);
|
||||
|
||||
}
|
||||
void ARM64FloatEmitter::ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask)
|
||||
{
|
||||
for (int i = 31; i >= 0; --i)
|
||||
{
|
||||
if (!registers[i])
|
||||
continue;
|
||||
|
||||
if (ignore_mask[i])
|
||||
m_emit->ADD(SP, SP, 16);
|
||||
else
|
||||
LDR(128, INDEX_POST, (ARM64Reg)(Q0 + i), SP, 16);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,9 @@ enum ARM64Reg
|
|||
};
|
||||
|
||||
inline bool Is64Bit(ARM64Reg reg) { return reg & 0x20; }
|
||||
inline bool Is128Bit(ARM64Reg reg) { return reg & 0xC0; }
|
||||
inline bool IsSingle(ARM64Reg reg) { return reg & 0x40; }
|
||||
inline bool IsDouble(ARM64Reg reg) { return reg & 0x80; }
|
||||
inline bool IsQuad(ARM64Reg reg) { return (reg & 0xC0) == 0xC0; }
|
||||
inline bool IsVector(ARM64Reg reg) { return (reg & 0xC0) != 0; }
|
||||
inline ARM64Reg DecodeReg(ARM64Reg reg) { return (ARM64Reg)(reg & 0x1F); }
|
||||
inline ARM64Reg EncodeRegTo64(ARM64Reg reg) { return (ARM64Reg)(reg | 0x20); }
|
||||
|
@ -238,9 +240,17 @@ public:
|
|||
m_shifttype = shift_type;
|
||||
m_type = TYPE_SHIFTEDREG;
|
||||
if (Is64Bit(Rd))
|
||||
{
|
||||
m_width = WIDTH_64BIT;
|
||||
if (shift == 64)
|
||||
m_shift = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_width = WIDTH_32BIT;
|
||||
if (shift == 32)
|
||||
m_shift = 0;
|
||||
}
|
||||
}
|
||||
TypeSpecifier GetType() const
|
||||
{
|
||||
|
@ -270,6 +280,8 @@ public:
|
|||
|
||||
class ARM64XEmitter
|
||||
{
|
||||
friend class ARM64FloatEmitter;
|
||||
|
||||
private:
|
||||
u8* m_code;
|
||||
u8* m_startcode;
|
||||
|
@ -294,7 +306,7 @@ private:
|
|||
void EncodeLoadStoreExcInst(u32 instenc, ARM64Reg Rs, ARM64Reg Rt2, ARM64Reg Rn, ARM64Reg Rt);
|
||||
void EncodeLoadStorePairedInst(u32 op, ARM64Reg Rt, ARM64Reg Rt2, ARM64Reg Rn, u32 imm);
|
||||
void EncodeLoadStoreIndexedInst(u32 op, u32 op2, ARM64Reg Rt, ARM64Reg Rn, s32 imm);
|
||||
void EncodeLoadStoreIndexedInst(u32 op, ARM64Reg Rt, ARM64Reg Rn, s32 imm);
|
||||
void EncodeLoadStoreIndexedInst(u32 op, ARM64Reg Rt, ARM64Reg Rn, s32 imm, u8 size);
|
||||
void EncodeMOVWideInst(u32 op, ARM64Reg Rd, u32 imm, ShiftAmount pos);
|
||||
void EncodeBitfieldMOVInst(u32 op, ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms);
|
||||
void EncodeLoadStoreRegisterOffset(u32 size, u32 opc, ARM64Reg Rt, ARM64Reg Rn, ARM64Reg Rm, ExtendType extend);
|
||||
|
@ -452,6 +464,8 @@ public:
|
|||
void UMADDL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra);
|
||||
void UMSUBL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra);
|
||||
void UMULH(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ARM64Reg Ra);
|
||||
void MUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void MNEG(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
|
||||
// Logical (shifted register)
|
||||
void AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, ArithOption Shift);
|
||||
|
@ -565,6 +579,96 @@ public:
|
|||
void ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask = BitSet32(0));
|
||||
};
|
||||
|
||||
class ARM64FloatEmitter
|
||||
{
|
||||
public:
|
||||
ARM64FloatEmitter(ARM64XEmitter* emit) : m_emit(emit) {}
|
||||
|
||||
void LDR(u8 size, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm);
|
||||
void STR(u8 size, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm);
|
||||
|
||||
// Loadstore single structure
|
||||
void LD1R(u8 size, ARM64Reg Rt, ARM64Reg Rn);
|
||||
|
||||
// Scalar - 2 Source
|
||||
void FMUL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
|
||||
// Vector
|
||||
void AND(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void BSL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn, u8 index);
|
||||
void FABS(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FADD(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCVTL(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FDIV(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FMUL(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FNEG(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FRSQRTE(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FSUB(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void NOT(ARM64Reg Rd, ARM64Reg Rn);
|
||||
void ORR(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void REV16(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void REV32(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void REV64(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
||||
// Move
|
||||
void DUP(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void INS(u8 size, ARM64Reg Rd, u8 index, ARM64Reg Rn);
|
||||
void INS(u8 size, ARM64Reg Rd, u8 index1, ARM64Reg Rn, u8 index2);
|
||||
|
||||
// One source
|
||||
void FCVT(u8 size_to, u8 size_from, ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
||||
// Conversion between float and integer
|
||||
void FMOV(u8 size, bool top, ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
||||
// Float comparison
|
||||
void FCMP(ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCMP(ARM64Reg Rn);
|
||||
void FCMPE(ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCMPE(ARM64Reg Rn);
|
||||
void FCMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCMEQ(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCMGE(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void FCMGT(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCMLE(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void FCMLT(u8 size, ARM64Reg Rd, ARM64Reg Rn);
|
||||
|
||||
// Conditional select
|
||||
void FCSEL(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm, CCFlags cond);
|
||||
|
||||
// Permute
|
||||
void UZP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void TRN1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void ZIP1(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void UZP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void TRN2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void ZIP2(u8 size, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
|
||||
// ABI related
|
||||
void ABI_PushRegisters(BitSet32 registers);
|
||||
void ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask = BitSet32(0));
|
||||
|
||||
private:
|
||||
ARM64XEmitter* m_emit;
|
||||
inline void Write32(u32 value) { m_emit->Write32(value); }
|
||||
|
||||
// Emitting functions
|
||||
void EmitLoadStoreImmediate(u8 size, u32 opc, IndexType type, ARM64Reg Rt, ARM64Reg Rn, s32 imm);
|
||||
void Emit2Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void EmitThreeSame(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void EmitCopy(bool Q, u32 op, u32 imm5, u32 imm4, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void Emit2RegMisc(bool U, u32 size, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void EmitLoadStoreSingleStructure(bool L, bool R, u32 opcode, bool S, u32 size, ARM64Reg Rt, ARM64Reg Rn);
|
||||
void Emit1Source(bool M, bool S, u32 type, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void EmitConversion(bool sf, bool S, u32 type, u32 rmode, u32 opcode, ARM64Reg Rd, ARM64Reg Rn);
|
||||
void EmitCompare(bool M, bool S, u32 op, u32 opcode2, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void EmitCondSelect(bool M, bool S, CCFlags cond, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
void EmitPermute(u32 size, u32 op, ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm);
|
||||
};
|
||||
|
||||
class ARM64CodeBlock : public CodeBlock<ARM64XEmitter>
|
||||
{
|
||||
private:
|
||||
|
|
|
@ -16,10 +16,10 @@ static inline int CountSetBits(T v)
|
|||
// from https://graphics.stanford.edu/~seander/bithacks.html
|
||||
// GCC has this built in, but MSVC's intrinsic will only emit the actual
|
||||
// POPCNT instruction, which we're not depending on
|
||||
v = v - ((v >> 1) & (T)~(T)0 / 3);
|
||||
v = (v & (T)~(T)0 / 15 * 3) + ((v >> 2) & (T)~(T)0 / 15 * 3);
|
||||
v = (v + (v >> 4)) & (T)~(T)0 / 255 * 15;
|
||||
return (T)(v * ((T)~(T)0 / 255)) >> (sizeof(T) - 1) * 8;
|
||||
v = v - ((v >> 1) & (T)~(T)0/3);
|
||||
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);
|
||||
v = (v + (v >> 4)) & (T)~(T)0/255*15;
|
||||
return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8;
|
||||
}
|
||||
static inline int LeastSignificantSetBit(u8 val)
|
||||
{
|
||||
|
@ -148,7 +148,7 @@ public:
|
|||
|
||||
static BitSet AllTrue(size_t count)
|
||||
{
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
return BitSet(count == sizeof(IntTy)*8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](size_t bit) { return Ref(this, (IntTy)1 << bit); }
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
// Valgrind doesn't support MAP_32BIT.
|
||||
// Uncomment the following line to be able to run Dolphin in Valgrind.
|
||||
//#undef MAP_32BIT
|
||||
|
||||
#if !defined(_WIN32) && defined(_M_X86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
|
@ -65,7 +69,7 @@ void* AllocateExecutableMemory(size_t size, bool low)
|
|||
{
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
PanicAlert("Failed to allocate executable memory");
|
||||
PanicAlert("Failed to allocate executable memory. If you are running Dolphin in Valgrind, try '#undef MAP_32BIT'.");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(_M_X86_64) && !defined(MAP_32BIT)
|
||||
else
|
||||
|
|
|
@ -250,6 +250,8 @@ bool BootCore(const std::string& _rFilename)
|
|||
StartUp.bEnableMemcardSaving = g_NetPlaySettings.m_WriteToMemcard;
|
||||
StartUp.iCPUCore = g_NetPlaySettings.m_CPUcore;
|
||||
SConfig::GetInstance().m_DSPEnableJIT = g_NetPlaySettings.m_DSPEnableJIT;
|
||||
SConfig::GetInstance().m_OCEnable = g_NetPlaySettings.m_OCEnable;
|
||||
SConfig::GetInstance().m_OCFactor = g_NetPlaySettings.m_OCFactor;
|
||||
SConfig::GetInstance().m_EXIDevice[0] = g_NetPlaySettings.m_EXIDevice[0];
|
||||
SConfig::GetInstance().m_EXIDevice[1] = g_NetPlaySettings.m_EXIDevice[1];
|
||||
config_cache.bSetEXIDevice[0] = true;
|
||||
|
|
|
@ -225,8 +225,11 @@ elseif(_M_ARM_64)
|
|||
PowerPC/JitArm64/JitArm64_RegCache.cpp
|
||||
PowerPC/JitArm64/JitArm64_BackPatch.cpp
|
||||
PowerPC/JitArm64/JitArm64_Branch.cpp
|
||||
PowerPC/JitArm64/JitArm64_FloatingPoint.cpp
|
||||
PowerPC/JitArm64/JitArm64_Integer.cpp
|
||||
PowerPC/JitArm64/JitArm64_LoadStore.cpp
|
||||
PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp
|
||||
PowerPC/JitArm64/JitArm64_Paired.cpp
|
||||
PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
|
||||
PowerPC/JitArm64/JitArm64_Tables.cpp)
|
||||
endif()
|
||||
|
|
|
@ -50,8 +50,8 @@ struct SConfig : NonCopyable
|
|||
int m_InterfaceLanguage;
|
||||
// framelimit choose
|
||||
unsigned int m_Framelimit;
|
||||
float m_OCFactor;
|
||||
bool m_OCEnable;
|
||||
float m_OCFactor;
|
||||
// other interface settings
|
||||
bool m_InterfaceToolbar;
|
||||
bool m_InterfaceStatusbar;
|
||||
|
|
|
@ -477,7 +477,7 @@ void Idle()
|
|||
}
|
||||
}
|
||||
|
||||
idledCycles += PowerPC::ppcState.downcount;
|
||||
idledCycles += DowncountToCycles(PowerPC::ppcState.downcount);
|
||||
PowerPC::ppcState.downcount = 0;
|
||||
|
||||
Advance();
|
||||
|
|
|
@ -88,7 +88,7 @@ void GetStringVA(std::string& _rOutBuffer, u32 strReg)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (string[i - 1] == 'l' && string[i - 2] == 'l') // hax, just seen this on sysmenu osreport
|
||||
if (string[i-1] == 'l' && string[i-2] == 'l') // hax, just seen this on sysmenu osreport
|
||||
{
|
||||
Parameter = GPR(++ParameterCounter);
|
||||
Parameter = (Parameter<<32)|GPR(++ParameterCounter);
|
||||
|
|
|
@ -243,44 +243,6 @@ void ClearCacheLine(const u32 address)
|
|||
Write_U64(0, address + i);
|
||||
}
|
||||
|
||||
void DMA_LCToMemory(const u32 memAddr, const u32 cacheAddr, const u32 numBlocks)
|
||||
{
|
||||
const u8* src = m_pL1Cache + (cacheAddr & 0x3FFFF);
|
||||
u8* dst = GetPointer(memAddr);
|
||||
|
||||
if ((dst != nullptr) && (src != nullptr) && (memAddr & 3) == 0 && (cacheAddr & 3) == 0)
|
||||
{
|
||||
memcpy(dst, src, 32 * numBlocks);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i++)
|
||||
{
|
||||
u8 Temp = Read_U8(cacheAddr + i);
|
||||
Write_U8(Temp, memAddr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DMA_MemoryToLC(const u32 cacheAddr, const u32 memAddr, const u32 numBlocks)
|
||||
{
|
||||
const u8* src = GetPointer(memAddr);
|
||||
u8* dst = m_pL1Cache + (cacheAddr & 0x3FFFF);
|
||||
|
||||
if ((dst != nullptr) && (src != nullptr) && (memAddr & 3) == 0 && (cacheAddr & 3) == 0)
|
||||
{
|
||||
memcpy(dst, src, 32 * numBlocks);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i++)
|
||||
{
|
||||
u8 Temp = Read_U8(memAddr + i);
|
||||
Write_U8(Temp, cacheAddr + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetString(u32 em_address, size_t size)
|
||||
{
|
||||
const char* ptr = reinterpret_cast<const char*>(GetPointer(em_address));
|
||||
|
|
|
@ -90,6 +90,24 @@ static u32 EFB_Read(const u32 addr)
|
|||
return var;
|
||||
}
|
||||
|
||||
static void EFB_Write(u32 data, u32 addr)
|
||||
{
|
||||
int x = (addr & 0xfff) >> 2;
|
||||
int y = (addr >> 12) & 0x3ff;
|
||||
|
||||
if (addr & 0x00400000)
|
||||
{
|
||||
g_video_backend->Video_AccessEFB(POKE_Z, x, y, data);
|
||||
DEBUG_LOG(MEMMAP, "EFB Z Write %08x @ %i, %i", data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_video_backend->Video_AccessEFB(POKE_COLOR, x, y, data);
|
||||
DEBUG_LOG(MEMMAP, "EFB Color Write %08x @ %i, %i", data, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void GenerateDSIException(u32 _EffectiveAddress, bool _bWrite);
|
||||
|
||||
template <XCheckTLBFlag flag, typename T>
|
||||
|
@ -193,20 +211,8 @@ __forceinline void WriteToHardware(u32 em_address, const T data)
|
|||
{
|
||||
if (em_address < 0xcc000000)
|
||||
{
|
||||
int x = (em_address & 0xfff) >> 2;
|
||||
int y = (em_address >> 12) & 0x3ff;
|
||||
|
||||
// TODO figure out a way to send data without falling into the template trap
|
||||
if (em_address & 0x00400000)
|
||||
{
|
||||
g_video_backend->Video_AccessEFB(POKE_Z, x, y, (u32)data);
|
||||
DEBUG_LOG(MEMMAP, "EFB Z Write %08x @ %i, %i", (u32)data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_video_backend->Video_AccessEFB(POKE_COLOR, x, y, (u32)data);
|
||||
DEBUG_LOG(MEMMAP, "EFB Color Write %08x @ %i, %i", (u32)data, x, y);
|
||||
}
|
||||
// TODO: This only works correctly for 32-bit writes.
|
||||
EFB_Write((u32)data, em_address);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -460,6 +466,78 @@ void WriteUnchecked_U32(const u32 var, const u32 address)
|
|||
WriteToHardware<FLAG_NO_EXCEPTION, u32>(address, var);
|
||||
}
|
||||
|
||||
void DMA_LCToMemory(const u32 memAddr, const u32 cacheAddr, const u32 numBlocks)
|
||||
{
|
||||
// TODO: It's not completely clear this is the right spot for this code;
|
||||
// what would happen if, for example, the DVD drive tried to write to the EFB?
|
||||
// TODO: This is terribly slow.
|
||||
// TODO: Refactor.
|
||||
// Avatar: The Last Airbender (GC) uses this for videos.
|
||||
if ((memAddr & 0x0F000000) == 0x08000000)
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i+=4)
|
||||
{
|
||||
u32 data = bswap(*(u32*)(Memory::m_pL1Cache + ((cacheAddr + i) & 0x3FFFF)));
|
||||
EFB_Write(data, memAddr + i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No known game uses this; here for completeness.
|
||||
// TODO: Refactor.
|
||||
if ((memAddr & 0x0F000000) == 0x0C000000)
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i += 4)
|
||||
{
|
||||
u32 data = bswap(*(u32*)(Memory::m_pL1Cache + ((cacheAddr + i) & 0x3FFFF)));
|
||||
mmio_mapping->Write(memAddr + i, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const u8* src = Memory::m_pL1Cache + (cacheAddr & 0x3FFFF);
|
||||
u8* dst = Memory::GetPointer(memAddr);
|
||||
if (dst == nullptr)
|
||||
return;
|
||||
|
||||
memcpy(dst, src, 32 * numBlocks);
|
||||
}
|
||||
|
||||
void DMA_MemoryToLC(const u32 cacheAddr, const u32 memAddr, const u32 numBlocks)
|
||||
{
|
||||
const u8* src = Memory::GetPointer(memAddr);
|
||||
u8* dst = Memory::m_pL1Cache + (cacheAddr & 0x3FFFF);
|
||||
|
||||
// No known game uses this; here for completeness.
|
||||
// TODO: Refactor.
|
||||
if ((memAddr & 0x0F000000) == 0x08000000)
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i += 4)
|
||||
{
|
||||
u32 data = EFB_Read(memAddr + i);
|
||||
*(u32*)(Memory::m_pL1Cache + ((cacheAddr + i) & 0x3FFFF)) = bswap(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// No known game uses this.
|
||||
// TODO: Refactor.
|
||||
if ((memAddr & 0x0F000000) == 0x0C000000)
|
||||
{
|
||||
for (u32 i = 0; i < 32 * numBlocks; i += 4)
|
||||
{
|
||||
u32 data = mmio_mapping->Read<u32>(memAddr + i);
|
||||
*(u32*)(Memory::m_pL1Cache + ((cacheAddr + i) & 0x3FFFF)) = bswap(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (src == nullptr)
|
||||
return;
|
||||
|
||||
memcpy(dst, src, 32 * numBlocks);
|
||||
}
|
||||
|
||||
// *********************************************************************************
|
||||
// Warning: Test Area
|
||||
//
|
||||
|
@ -616,7 +694,7 @@ enum TLBLookupResult
|
|||
|
||||
static __forceinline TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag flag, const u32 vpa, u32 *paddr)
|
||||
{
|
||||
int tag = vpa >> HW_PAGE_INDEX_SHIFT;
|
||||
u32 tag = vpa >> HW_PAGE_INDEX_SHIFT;
|
||||
PowerPC::tlb_entry *tlbe = &PowerPC::ppcState.tlb[flag == FLAG_OPCODE][tag & HW_PAGE_INDEX_MASK];
|
||||
if (tlbe->tag[0] == tag)
|
||||
{
|
||||
|
|
|
@ -28,16 +28,16 @@ CSIDevice_GCController::CSIDevice_GCController(SIDevices device, int _iDeviceNum
|
|||
memset(&m_Origin, 0, sizeof(SOrigin));
|
||||
memset(&pad_origin, 0, sizeof(GCPadStatus));
|
||||
|
||||
pad_origin.button = 0x00;
|
||||
pad_origin.stickX = 0x80; // center
|
||||
pad_origin.stickY = 0x80;
|
||||
pad_origin.substickX = 0x80;
|
||||
pad_origin.substickY = 0x80;
|
||||
pad_origin.triggerLeft = 0x1F; // 0-30 is the lower deadzone
|
||||
pad_origin.button = 0x00;
|
||||
pad_origin.stickX = 0x80; // center
|
||||
pad_origin.stickY = 0x80;
|
||||
pad_origin.substickX = 0x80;
|
||||
pad_origin.substickY = 0x80;
|
||||
pad_origin.triggerLeft = 0x1F; // 0-30 is the lower deadzone
|
||||
pad_origin.triggerRight = 0x1F;
|
||||
|
||||
// Dunno if we need to do this, game/lib should set it?
|
||||
m_Mode = 0x03;
|
||||
m_Mode = 0x03;
|
||||
|
||||
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||
if (SI_GCAdapter::IsDetected())
|
||||
|
@ -71,48 +71,48 @@ int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)
|
|||
break;
|
||||
|
||||
case CMD_DIRECT:
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength);
|
||||
u32 high, low;
|
||||
GetData(high, low);
|
||||
for (int i = 0; i < (_iLength - 1) / 2; i++)
|
||||
{
|
||||
_pBuffer[i + 0] = (high >> (i * 8)) & 0xff;
|
||||
_pBuffer[i + 4] = (low >> (i * 8)) & 0xff;
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Direct (Length: %d)", _iLength);
|
||||
u32 high, low;
|
||||
GetData(high, low);
|
||||
for (int i = 0; i < (_iLength - 1) / 2; i++)
|
||||
{
|
||||
_pBuffer[i + 0] = (high >> (i * 8)) & 0xff;
|
||||
_pBuffer[i + 4] = (low >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case CMD_ORIGIN:
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
|
||||
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
|
||||
for (int i = 0; i < (int)sizeof(SOrigin); i++)
|
||||
{
|
||||
_pBuffer[i ^ 3] = *pCalibration++;
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Get Origin");
|
||||
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
|
||||
for (int i = 0; i < (int)sizeof(SOrigin); i++)
|
||||
{
|
||||
_pBuffer[i ^ 3] = *pCalibration++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// Recalibrate (FiRES: i am not 100 percent sure about this)
|
||||
case CMD_RECALIBRATE:
|
||||
{
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Recalibrate");
|
||||
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
|
||||
for (int i = 0; i < (int)sizeof(SOrigin); i++)
|
||||
{
|
||||
_pBuffer[i ^ 3] = *pCalibration++;
|
||||
INFO_LOG(SERIALINTERFACE, "PAD - Recalibrate");
|
||||
u8* pCalibration = reinterpret_cast<u8*>(&m_Origin);
|
||||
for (int i = 0; i < (int)sizeof(SOrigin); i++)
|
||||
{
|
||||
_pBuffer[i ^ 3] = *pCalibration++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
// DEFAULT
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "Unknown SI command (0x%x)", command);
|
||||
PanicAlert("SI: Unknown command (0x%x)", command);
|
||||
}
|
||||
break;
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "Unknown SI command (0x%x)", command);
|
||||
PanicAlert("SI: Unknown command (0x%x)", command);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return _iLength;
|
||||
|
@ -168,7 +168,7 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
|||
// Low bits are packed differently per mode
|
||||
if (m_Mode == 0 || m_Mode == 5 || m_Mode == 6 || m_Mode == 7)
|
||||
{
|
||||
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
|
||||
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
|
||||
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
|
||||
_Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 8); // Top 4 bits
|
||||
_Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 12); // Top 4 bits
|
||||
|
@ -177,7 +177,7 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
|||
}
|
||||
else if (m_Mode == 1)
|
||||
{
|
||||
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
|
||||
_Low = (u8)(PadStatus.analogB >> 4); // Top 4 bits
|
||||
_Low |= (u32)((u8)(PadStatus.analogA >> 4) << 4); // Top 4 bits
|
||||
_Low |= (u32)((u8)PadStatus.triggerRight << 8); // All 8 bits
|
||||
_Low |= (u32)((u8)PadStatus.triggerLeft << 16); // All 8 bits
|
||||
|
@ -186,7 +186,7 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
|||
}
|
||||
else if (m_Mode == 2)
|
||||
{
|
||||
_Low = (u8)(PadStatus.analogB); // All 8 bits
|
||||
_Low = (u8)(PadStatus.analogB); // All 8 bits
|
||||
_Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits
|
||||
_Low |= (u32)((u8)(PadStatus.triggerRight >> 4) << 16); // Top 4 bits
|
||||
_Low |= (u32)((u8)(PadStatus.triggerLeft >> 4) << 20); // Top 4 bits
|
||||
|
@ -196,14 +196,14 @@ bool CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
|
|||
else if (m_Mode == 3)
|
||||
{
|
||||
// Analog A/B are always 0
|
||||
_Low = (u8)PadStatus.triggerRight; // All 8 bits
|
||||
_Low = (u8)PadStatus.triggerRight; // All 8 bits
|
||||
_Low |= (u32)((u8)PadStatus.triggerLeft << 8); // All 8 bits
|
||||
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
|
||||
_Low |= (u32)((u8)PadStatus.substickX << 24); // All 8 bits
|
||||
}
|
||||
else if (m_Mode == 4)
|
||||
{
|
||||
_Low = (u8)(PadStatus.analogB); // All 8 bits
|
||||
_Low = (u8)(PadStatus.analogB); // All 8 bits
|
||||
_Low |= (u32)((u8)(PadStatus.analogA) << 8); // All 8 bits
|
||||
// triggerLeft/Right are always 0
|
||||
_Low |= (u32)((u8)PadStatus.substickY << 16); // All 8 bits
|
||||
|
@ -218,7 +218,7 @@ u32 CSIDevice_GCController::MapPadStatus(const GCPadStatus& pad_status)
|
|||
{
|
||||
// Thankfully changing mode does not change the high bits ;)
|
||||
u32 _Hi = 0;
|
||||
_Hi = (u32)((u8)pad_status.stickY);
|
||||
_Hi = (u32)((u8)pad_status.stickY);
|
||||
_Hi |= (u32)((u8)pad_status.stickX << 8);
|
||||
_Hi |= (u32)((u16)(pad_status.button | PAD_USE_ORIGIN) << 16);
|
||||
return _Hi;
|
||||
|
@ -228,9 +228,9 @@ void CSIDevice_GCController::HandleButtonCombos(const GCPadStatus& pad_status)
|
|||
{
|
||||
// Keep track of the special button combos (embedded in controller hardware... :( )
|
||||
EButtonCombo tempCombo;
|
||||
if ((pad_status.button & 0xff00) == (PAD_BUTTON_Y | PAD_BUTTON_X | PAD_BUTTON_START))
|
||||
if ((pad_status.button & 0xff00) == (PAD_BUTTON_Y|PAD_BUTTON_X|PAD_BUTTON_START))
|
||||
tempCombo = COMBO_ORIGIN;
|
||||
else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B | PAD_BUTTON_X | PAD_BUTTON_START))
|
||||
else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B|PAD_BUTTON_X|PAD_BUTTON_START))
|
||||
tempCombo = COMBO_RESET;
|
||||
else
|
||||
tempCombo = COMBO_NONE;
|
||||
|
@ -249,12 +249,12 @@ void CSIDevice_GCController::HandleButtonCombos(const GCPadStatus& pad_status)
|
|||
ProcessorInterface::ResetButton_Tap();
|
||||
else if (m_LastButtonCombo == COMBO_ORIGIN)
|
||||
{
|
||||
m_Origin.uOriginStickX = pad_status.stickX;
|
||||
m_Origin.uOriginStickY = pad_status.stickY;
|
||||
m_Origin.uOriginStickX = pad_status.stickX;
|
||||
m_Origin.uOriginStickY = pad_status.stickY;
|
||||
m_Origin.uSubStickStickX = pad_status.substickX;
|
||||
m_Origin.uSubStickStickY = pad_status.substickY;
|
||||
m_Origin.uTrigger_L = pad_status.triggerLeft;
|
||||
m_Origin.uTrigger_R = pad_status.triggerRight;
|
||||
m_Origin.uTrigger_L = pad_status.triggerLeft;
|
||||
m_Origin.uTrigger_R = pad_status.triggerRight;
|
||||
}
|
||||
m_LastButtonCombo = COMBO_NONE;
|
||||
}
|
||||
|
@ -268,43 +268,43 @@ void CSIDevice_GCController::SendCommand(u32 _Cmd, u8 _Poll)
|
|||
|
||||
switch (command.Command)
|
||||
{
|
||||
// Costis sent it in some demos :)
|
||||
// Costis sent it in some demos :)
|
||||
case 0x00:
|
||||
break;
|
||||
|
||||
case CMD_WRITE:
|
||||
{
|
||||
unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
|
||||
unsigned int uStrength = command.Parameter2;
|
||||
{
|
||||
unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
|
||||
unsigned int uStrength = command.Parameter2;
|
||||
|
||||
// get the correct pad number that should rumble locally when using netplay
|
||||
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
|
||||
// get the correct pad number that should rumble locally when using netplay
|
||||
const u8 numPAD = NetPlay_InGamePadToLocalPad(ISIDevice::m_iDeviceNumber);
|
||||
|
||||
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||
SI_GCAdapter::Output(numPAD, command.Parameter1 & 0xff);
|
||||
SI_GCAdapter::Output(numPAD, command.Parameter1 & 0xff);
|
||||
#endif
|
||||
if (numPAD < 4)
|
||||
{
|
||||
if (uType == 1 && uStrength > 2)
|
||||
Pad::Rumble(numPAD, 1.0);
|
||||
else
|
||||
Pad::Rumble(numPAD, 0.0);
|
||||
}
|
||||
if (numPAD < 4)
|
||||
{
|
||||
if (uType == 1 && uStrength > 2)
|
||||
Pad::Rumble(numPAD, 1.0);
|
||||
else
|
||||
Pad::Rumble(numPAD, 0.0);
|
||||
}
|
||||
|
||||
if (!_Poll)
|
||||
{
|
||||
m_Mode = command.Parameter2;
|
||||
INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode);
|
||||
if (!_Poll)
|
||||
{
|
||||
m_Mode = command.Parameter2;
|
||||
INFO_LOG(SERIALINTERFACE, "PAD %i set to mode %i", ISIDevice::m_iDeviceNumber, m_Mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "Unknown direct command (0x%x)", _Cmd);
|
||||
PanicAlert("SI: Unknown direct command");
|
||||
}
|
||||
break;
|
||||
{
|
||||
ERROR_LOG(SERIALINTERFACE, "Unknown direct command (0x%x)", _Cmd);
|
||||
PanicAlert("SI: Unknown direct command");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ protected:
|
|||
// Commands
|
||||
enum EBufferCommands
|
||||
{
|
||||
CMD_RESET = 0x00,
|
||||
CMD_DIRECT = 0x40,
|
||||
CMD_ORIGIN = 0x41,
|
||||
CMD_RESET = 0x00,
|
||||
CMD_DIRECT = 0x40,
|
||||
CMD_ORIGIN = 0x41,
|
||||
CMD_RECALIBRATE = 0x42,
|
||||
};
|
||||
|
||||
|
@ -47,11 +47,11 @@ protected:
|
|||
{
|
||||
u32 Parameter1 : 8;
|
||||
u32 Parameter2 : 8;
|
||||
u32 Command : 8;
|
||||
u32: 8;
|
||||
u32 Command : 8;
|
||||
u32 : 8;
|
||||
};
|
||||
UCommand() { Hex = 0; }
|
||||
UCommand(u32 _iValue) { Hex = _iValue; }
|
||||
UCommand() {Hex = 0;}
|
||||
UCommand(u32 _iValue) {Hex = _iValue;}
|
||||
};
|
||||
|
||||
enum EButtonCombo
|
||||
|
|
|
@ -24,11 +24,11 @@ int CSIDevice_GCSteeringWheel::RunBuffer(u8* _pBuffer, int _iLength)
|
|||
*(u32*)&_pBuffer[0] = SI_GC_STEERING;
|
||||
break;
|
||||
|
||||
// Seen in F-Zero GX
|
||||
// Seen in F-Zero GX
|
||||
case CMD_MOTOR_OFF:
|
||||
break;
|
||||
|
||||
// DEFAULT
|
||||
// DEFAULT
|
||||
default:
|
||||
{
|
||||
return CSIDevice_GCController::RunBuffer(_pBuffer, _iLength);
|
||||
|
|
|
@ -12,10 +12,10 @@ private:
|
|||
// Commands
|
||||
enum EBufferCommands
|
||||
{
|
||||
CMD_RESET = 0x00,
|
||||
CMD_ORIGIN = 0x41,
|
||||
CMD_RESET = 0x00,
|
||||
CMD_ORIGIN = 0x41,
|
||||
CMD_RECALIBRATE = 0x42,
|
||||
CMD_MOTOR_OFF = 0xff,
|
||||
CMD_MOTOR_OFF = 0xff,
|
||||
};
|
||||
|
||||
enum EDirectCommands
|
||||
|
|
|
@ -174,7 +174,7 @@ bool WiimoteLinux::ConnectInternal()
|
|||
// Output channel
|
||||
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
|
||||
if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to open output socket to wiimote: %s", strerror(errno));
|
||||
close(m_cmd_sock);
|
||||
|
@ -185,7 +185,7 @@ bool WiimoteLinux::ConnectInternal()
|
|||
// Input channel
|
||||
addr.l2_psm = htobs(WM_INPUT_CHANNEL);
|
||||
if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
|
||||
connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
WARN_LOG(WIIMOTE, "Unable to open input socket from wiimote: %s", strerror(errno));
|
||||
close(m_int_sock);
|
||||
|
@ -262,7 +262,7 @@ int WiimoteLinux::IORead(u8* buf)
|
|||
{
|
||||
// This can happen if the bluetooth dongle is disconnected
|
||||
ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. "
|
||||
"Wiimote %i will be disconnected.", m_index + 1);
|
||||
"Wiimote %i will be disconnected.", m_index + 1);
|
||||
}
|
||||
|
||||
r = 0;
|
||||
|
|
|
@ -116,8 +116,7 @@ void Wiimote::ClearReadQueue()
|
|||
|
||||
// The "Clear" function isn't thread-safe :/
|
||||
while (m_read_reports.Pop(rpt))
|
||||
{
|
||||
}
|
||||
{}
|
||||
}
|
||||
|
||||
void Wiimote::ControlChannel(const u16 channel, const void* const data, const u32 size)
|
||||
|
@ -175,8 +174,8 @@ void Wiimote::InterruptChannel(const u16 channel, const void* const _data, const
|
|||
}
|
||||
}
|
||||
else if (rpt[1] == WM_WRITE_SPEAKER_DATA &&
|
||||
(!SConfig::GetInstance().m_WiimoteEnableSpeaker ||
|
||||
(!wm->m_status.speaker || wm->m_speaker_mute)))
|
||||
(!SConfig::GetInstance().m_WiimoteEnableSpeaker ||
|
||||
(!wm->m_status.speaker || wm->m_speaker_mute)))
|
||||
{
|
||||
// Translate speaker data reports into rumble reports.
|
||||
rpt[1] = WM_RUMBLE;
|
||||
|
@ -196,13 +195,13 @@ bool Wiimote::Read()
|
|||
if (result > 0 && m_channel > 0)
|
||||
{
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort > 0 &&
|
||||
m_index == WIIMOTE_BALANCE_BOARD)
|
||||
m_index == WIIMOTE_BALANCE_BOARD)
|
||||
{
|
||||
static sf::UdpSocket Socket;
|
||||
Socket.send((char*)rpt.data(),
|
||||
rpt.size(),
|
||||
sf::IpAddress::LocalHost,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort);
|
||||
rpt.size(),
|
||||
sf::IpAddress::LocalHost,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iBBDumpPort);
|
||||
}
|
||||
|
||||
// Add it to queue
|
||||
|
@ -308,22 +307,22 @@ void Wiimote::Prepare(int _index)
|
|||
bool Wiimote::PrepareOnThread()
|
||||
{
|
||||
// core buttons, no continuous reporting
|
||||
u8 static const mode_report[] = { WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE };
|
||||
u8 static const mode_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REPORT_MODE, 0, WM_REPORT_CORE};
|
||||
|
||||
// Set the active LEDs and turn on rumble.
|
||||
u8 static const led_report[] = { WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << (m_index%WIIMOTE_BALANCE_BOARD) | 0x1) };
|
||||
u8 static const led_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_LEDS, u8(WIIMOTE_LED_1 << (m_index%WIIMOTE_BALANCE_BOARD) | 0x1)};
|
||||
|
||||
// Turn off rumble
|
||||
u8 static const rumble_report[] = { WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0 };
|
||||
u8 static const rumble_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_RUMBLE, 0};
|
||||
|
||||
// Request status report
|
||||
u8 static const req_status_report[] = { WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0 };
|
||||
u8 static const req_status_report[] = {WM_SET_REPORT | WM_BT_OUTPUT, WM_REQUEST_STATUS, 0};
|
||||
// TODO: check for sane response?
|
||||
|
||||
return (IOWrite(mode_report, sizeof(mode_report)) &&
|
||||
IOWrite(led_report, sizeof(led_report)) &&
|
||||
(SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))) &&
|
||||
IOWrite(req_status_report, sizeof(req_status_report)));
|
||||
IOWrite(led_report, sizeof(led_report)) &&
|
||||
(SLEEP(200), IOWrite(rumble_report, sizeof(rumble_report))) &&
|
||||
IOWrite(req_status_report, sizeof(req_status_report)));
|
||||
}
|
||||
|
||||
void Wiimote::EmuStart()
|
||||
|
@ -571,7 +570,7 @@ void LoadSettings()
|
|||
IniFile inifile;
|
||||
inifile.Load(ini_filename);
|
||||
|
||||
for (unsigned int i = 0; i<MAX_WIIMOTES; ++i)
|
||||
for (unsigned int i=0; i<MAX_WIIMOTES; ++i)
|
||||
{
|
||||
std::string secname("Wiimote");
|
||||
secname += (char)('1' + i);
|
||||
|
@ -860,7 +859,7 @@ bool IsValidBluetoothName(const std::string& name)
|
|||
bool IsBalanceBoardName(const std::string& name)
|
||||
{
|
||||
return
|
||||
"Nintendo RVL-WBC-01" == name;
|
||||
"Nintendo RVL-WBC-01" == name;
|
||||
}
|
||||
|
||||
}; // end of namespace
|
||||
|
|
|
@ -107,7 +107,7 @@ IPCCommandResult CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode
|
|||
|
||||
// The file must exist before we can open it
|
||||
// It should be created by ISFS_CreateFile, not here
|
||||
if (File::Exists(m_filepath))
|
||||
if (File::Exists(m_filepath) && !File::IsDirectory(m_filepath))
|
||||
{
|
||||
INFO_LOG(WII_IPC_FILEIO, "FileIO: Open %s (%s == %08X)", m_Name.c_str(), Modes[_Mode], _Mode);
|
||||
ReturnValue = m_DeviceID;
|
||||
|
|
|
@ -137,10 +137,10 @@ static GekkoOPTemplate table4_2[] =
|
|||
|
||||
static GekkoOPTemplate table4_3[] =
|
||||
{
|
||||
{6, Interpreter::psq_lx, { "psq_lx", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0 } },
|
||||
{7, Interpreter::psq_stx, { "psq_stx", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0 } },
|
||||
{38, Interpreter::psq_lux, { "psq_lux", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0 } },
|
||||
{39, Interpreter::psq_stux, { "psq_stux", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0 } },
|
||||
{6, Interpreter::psq_lx, {"psq_lx", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{7, Interpreter::psq_stx, {"psq_stx", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_IN_A0B | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{38, Interpreter::psq_lux, {"psq_lux", OPTYPE_LOADPS, FL_OUT_FLOAT_S | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
{39, Interpreter::psq_stux, {"psq_stux", OPTYPE_STOREPS, FL_IN_FLOAT_S | FL_OUT_A | FL_IN_AB | FL_USE_FPU | FL_LOADSTORE, 1, 0, 0, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table19[] =
|
||||
|
|
|
@ -75,25 +75,25 @@ using namespace PowerPC;
|
|||
|
||||
// Optimization Ideas -
|
||||
/*
|
||||
* Assume SP is in main RAM (in Wii mode too?) - partly done
|
||||
* Assume all floating point loads and double precision loads+stores are to/from main ram
|
||||
(single precision stores can be used in write gather pipe, specialized fast check added)
|
||||
* AMD only - use movaps instead of movapd when loading ps from memory?
|
||||
* HLE functions like floorf, sin, memcpy, etc - they can be much faster
|
||||
* ABI optimizations - drop F0-F13 on blr, for example. Watch out for context switching.
|
||||
CR2-CR4 are non-volatile, rest of CR is volatile -> dropped on blr.
|
||||
R5-R12 are volatile -> dropped on blr.
|
||||
* classic inlining across calls.
|
||||
* Track which registers a block clobbers without using, then take advantage of this knowledge
|
||||
when compiling a block that links to that block.
|
||||
* Track more dependencies between instructions, e.g. avoiding PPC_FP code, single/double
|
||||
conversion, movddup on non-paired singles, etc where possible.
|
||||
* Support loads/stores directly from xmm registers in jit_util and the backpatcher; this might
|
||||
help AMD a lot since gpr/xmm transfers are slower there.
|
||||
* Smarter register allocation in general; maybe learn to drop values once we know they won't be
|
||||
used again before being overwritten?
|
||||
* More flexible reordering; there's limits to how far we can go because of exception handling
|
||||
and such, but it's currently limited to integer ops only. This can definitely be made better.
|
||||
* Assume SP is in main RAM (in Wii mode too?) - partly done
|
||||
* Assume all floating point loads and double precision loads+stores are to/from main ram
|
||||
(single precision stores can be used in write gather pipe, specialized fast check added)
|
||||
* AMD only - use movaps instead of movapd when loading ps from memory?
|
||||
* HLE functions like floorf, sin, memcpy, etc - they can be much faster
|
||||
* ABI optimizations - drop F0-F13 on blr, for example. Watch out for context switching.
|
||||
CR2-CR4 are non-volatile, rest of CR is volatile -> dropped on blr.
|
||||
R5-R12 are volatile -> dropped on blr.
|
||||
* classic inlining across calls.
|
||||
* Track which registers a block clobbers without using, then take advantage of this knowledge
|
||||
when compiling a block that links to that block.
|
||||
* Track more dependencies between instructions, e.g. avoiding PPC_FP code, single/double
|
||||
conversion, movddup on non-paired singles, etc where possible.
|
||||
* Support loads/stores directly from xmm registers in jit_util and the backpatcher; this might
|
||||
help AMD a lot since gpr/xmm transfers are slower there.
|
||||
* Smarter register allocation in general; maybe learn to drop values once we know they won't be
|
||||
used again before being overwritten?
|
||||
* More flexible reordering; there's limits to how far we can go because of exception handling
|
||||
and such, but it's currently limited to integer ops only. This can definitely be made better.
|
||||
*/
|
||||
|
||||
// The BLR optimization is nice, but it means that JITted code can overflow the
|
||||
|
@ -494,11 +494,11 @@ void Jit64::Trace()
|
|||
void Jit64::Jit(u32 em_address)
|
||||
{
|
||||
if (GetSpaceLeft() < 0x10000 ||
|
||||
farcode.GetSpaceLeft() < 0x10000 ||
|
||||
trampolines.GetSpaceLeft() < 0x10000 ||
|
||||
blocks.IsFull() ||
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bJITNoBlockCache ||
|
||||
m_clear_cache_asap)
|
||||
farcode.GetSpaceLeft() < 0x10000 ||
|
||||
trampolines.GetSpaceLeft() < 0x10000 ||
|
||||
blocks.IsFull() ||
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bJITNoBlockCache ||
|
||||
m_clear_cache_asap)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
|
@ -522,6 +522,7 @@ void Jit64::Jit(u32 em_address)
|
|||
jo.enableBlocklink = false;
|
||||
analyzer.ClearOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE);
|
||||
analyzer.ClearOption(PPCAnalyst::PPCAnalyzer::OPTION_BRANCH_MERGE);
|
||||
analyzer.ClearOption(PPCAnalyst::PPCAnalyzer::OPTION_CROR_MERGE);
|
||||
analyzer.ClearOption(PPCAnalyst::PPCAnalyzer::OPTION_CARRY_MERGE);
|
||||
}
|
||||
Trace();
|
||||
|
@ -603,7 +604,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
|
||||
js.downcountAmount += PatchEngine::GetSpeedhackCycles(code_block.m_address);
|
||||
|
||||
js.skipnext = false;
|
||||
js.skipInstructions = 0;
|
||||
js.carryFlagSet = false;
|
||||
js.carryFlagInverted = false;
|
||||
js.assumeNoPairedQuantize = false;
|
||||
|
@ -651,12 +652,9 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
|
||||
if (i == (code_block.m_num_instructions - 1))
|
||||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
js.next_inst_bp = false;
|
||||
if (Profiler::g_ProfileBlocks)
|
||||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
PROFILER_VPUSH;
|
||||
// get end tic
|
||||
PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStop);
|
||||
|
@ -664,14 +662,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
PROFILER_UPDATE_TIME(b);
|
||||
PROFILER_VPOP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
js.next_op = &ops[i + 1];
|
||||
js.next_inst_bp = SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging && breakpoints.IsAddressBreakPoint(ops[i + 1].address);
|
||||
js.isLastInstruction = true;
|
||||
}
|
||||
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
|
@ -856,11 +847,8 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
//NOTICE_LOG(DYNA_REC, "Unflushed register: %s", ppc_inst.c_str());
|
||||
}
|
||||
#endif
|
||||
if (js.skipnext)
|
||||
{
|
||||
js.skipnext = false;
|
||||
i++; // Skip next instruction
|
||||
}
|
||||
i += js.skipInstructions;
|
||||
js.skipInstructions = 0;
|
||||
}
|
||||
|
||||
u32 function = HLE::GetFunctionIndex(js.blockStart);
|
||||
|
@ -919,5 +907,6 @@ void Jit64::EnableOptimization()
|
|||
{
|
||||
analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE);
|
||||
analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_BRANCH_MERGE);
|
||||
analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_CROR_MERGE);
|
||||
analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_CARRY_MERGE);
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ public:
|
|||
void GenerateConstantOverflow(bool overflow);
|
||||
void GenerateConstantOverflow(s64 val);
|
||||
void GenerateOverflow();
|
||||
bool MergeAllowedNextInstructions(int count);
|
||||
void FinalizeCarryOverflow(bool oe, bool inv = false);
|
||||
void FinalizeCarry(Gen::CCFlags cond);
|
||||
void FinalizeCarry(bool ca);
|
||||
|
|
|
@ -346,10 +346,12 @@ void Jit64::FloatCompare(UGeckoInstruction inst, bool upper)
|
|||
int output[4] = { CR_SO, CR_EQ, CR_GT, CR_LT };
|
||||
|
||||
// Merge neighboring fcmp and cror (the primary use of cror).
|
||||
UGeckoInstruction next = js.next_inst;
|
||||
if (next.OPCD == 19 && next.SUBOP10 == 449 && (next.CRBA >> 2) == crf && (next.CRBB >> 2) == crf && (next.CRBD >> 2) == crf)
|
||||
UGeckoInstruction next = js.op[1].inst;
|
||||
if (analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_CROR_MERGE) &&
|
||||
MergeAllowedNextInstructions(1) && next.OPCD == 19 && next.SUBOP10 == 449 &&
|
||||
(next.CRBA >> 2) == crf && (next.CRBB >> 2) == crf && (next.CRBD >> 2) == crf)
|
||||
{
|
||||
js.skipnext = true;
|
||||
js.skipInstructions = 1;
|
||||
js.downcountAmount++;
|
||||
int dst = 3 - (next.CRBD & 3);
|
||||
output[3 - (next.CRBD & 3)] &= ~(1 << dst);
|
||||
|
|
|
@ -50,14 +50,30 @@ void Jit64::GenerateOverflow()
|
|||
SetJumpTarget(exit);
|
||||
}
|
||||
|
||||
bool Jit64::MergeAllowedNextInstructions(int count)
|
||||
{
|
||||
if (PowerPC::GetState() == PowerPC::CPU_STEPPING || js.instructionsLeft < count)
|
||||
return false;
|
||||
// Be careful: a breakpoint kills flags in between instructions
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging &&
|
||||
PowerPC::breakpoints.IsAddressBreakPoint(js.op[i].address))
|
||||
return false;
|
||||
if (js.op[i].isBranchTarget)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Jit64::FinalizeCarry(CCFlags cond)
|
||||
{
|
||||
js.carryFlagSet = false;
|
||||
js.carryFlagInverted = false;
|
||||
if (js.op->wantsCA)
|
||||
{
|
||||
// Be careful: a breakpoint kills flags in between instructions
|
||||
if (!js.isLastInstruction && js.next_op->wantsCAInFlags && !js.next_inst_bp)
|
||||
// Not actually merging instructions, but the effect is equivalent (we can't have breakpoints/etc in between).
|
||||
if (MergeAllowedNextInstructions(1) && js.op[1].wantsCAInFlags)
|
||||
{
|
||||
if (cond == CC_C || cond == CC_NC)
|
||||
{
|
||||
|
@ -86,7 +102,7 @@ void Jit64::FinalizeCarry(bool ca)
|
|||
js.carryFlagInverted = false;
|
||||
if (js.op->wantsCA)
|
||||
{
|
||||
if (!js.isLastInstruction && js.next_op->wantsCAInFlags && !js.next_inst_bp)
|
||||
if (MergeAllowedNextInstructions(1) && js.op[1].wantsCAInFlags)
|
||||
{
|
||||
if (ca)
|
||||
STC();
|
||||
|
@ -331,7 +347,10 @@ bool Jit64::CheckMergedBranch(int crf)
|
|||
if (!analyzer.HasOption(PPCAnalyst::PPCAnalyzer::OPTION_BRANCH_MERGE))
|
||||
return false;
|
||||
|
||||
const UGeckoInstruction& next = js.next_inst;
|
||||
if (!MergeAllowedNextInstructions(1))
|
||||
return false;
|
||||
|
||||
const UGeckoInstruction& next = js.op[1].inst;
|
||||
return (((next.OPCD == 16 /* bcx */) ||
|
||||
((next.OPCD == 19) && (next.SUBOP10 == 528) /* bcctrx */) ||
|
||||
((next.OPCD == 19) && (next.SUBOP10 == 16) /* bclrx */)) &&
|
||||
|
@ -343,33 +362,35 @@ bool Jit64::CheckMergedBranch(int crf)
|
|||
void Jit64::DoMergedBranch()
|
||||
{
|
||||
// Code that handles successful PPC branching.
|
||||
if (js.next_inst.OPCD == 16) // bcx
|
||||
const UGeckoInstruction& next = js.op[1].inst;
|
||||
const u32 nextPC = js.op[1].address;
|
||||
if (next.OPCD == 16) // bcx
|
||||
{
|
||||
if (js.next_inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.next_compilerPC + 4));
|
||||
if (next.LK)
|
||||
MOV(32, M(&LR), Imm32(nextPC + 4));
|
||||
|
||||
u32 destination;
|
||||
if (js.next_inst.AA)
|
||||
destination = SignExt16(js.next_inst.BD << 2);
|
||||
if (next.AA)
|
||||
destination = SignExt16(next.BD << 2);
|
||||
else
|
||||
destination = js.next_compilerPC + SignExt16(js.next_inst.BD << 2);
|
||||
WriteExit(destination, js.next_inst.LK, js.next_compilerPC + 4);
|
||||
destination = nextPC + SignExt16(next.BD << 2);
|
||||
WriteExit(destination, next.LK, nextPC + 4);
|
||||
}
|
||||
else if ((js.next_inst.OPCD == 19) && (js.next_inst.SUBOP10 == 528)) // bcctrx
|
||||
else if ((next.OPCD == 19) && (next.SUBOP10 == 528)) // bcctrx
|
||||
{
|
||||
if (js.next_inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.next_compilerPC + 4));
|
||||
if (next.LK)
|
||||
MOV(32, M(&LR), Imm32(nextPC + 4));
|
||||
MOV(32, R(RSCRATCH), M(&CTR));
|
||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||
WriteExitDestInRSCRATCH(js.next_inst.LK, js.next_compilerPC + 4);
|
||||
WriteExitDestInRSCRATCH(next.LK, nextPC + 4);
|
||||
}
|
||||
else if ((js.next_inst.OPCD == 19) && (js.next_inst.SUBOP10 == 16)) // bclrx
|
||||
else if ((next.OPCD == 19) && (next.SUBOP10 == 16)) // bclrx
|
||||
{
|
||||
MOV(32, R(RSCRATCH), M(&LR));
|
||||
if (!m_enable_blr_optimization)
|
||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||
if (js.next_inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.next_compilerPC + 4));
|
||||
if (next.LK)
|
||||
MOV(32, M(&LR), Imm32(nextPC + 4));
|
||||
WriteBLRExit();
|
||||
}
|
||||
else
|
||||
|
@ -381,9 +402,11 @@ void Jit64::DoMergedBranch()
|
|||
void Jit64::DoMergedBranchCondition()
|
||||
{
|
||||
js.downcountAmount++;
|
||||
js.skipnext = true;
|
||||
int test_bit = 8 >> (js.next_inst.BI & 3);
|
||||
bool condition = !!(js.next_inst.BO & BO_BRANCH_IF_TRUE);
|
||||
js.skipInstructions = 1;
|
||||
const UGeckoInstruction& next = js.op[1].inst;
|
||||
int test_bit = 8 >> (next.BI & 3);
|
||||
bool condition = !!(next.BO & BO_BRANCH_IF_TRUE);
|
||||
const u32 nextPC = js.op[1].address;
|
||||
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
|
@ -408,16 +431,18 @@ void Jit64::DoMergedBranchCondition()
|
|||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
WriteExit(js.next_compilerPC + 4);
|
||||
WriteExit(nextPC + 4);
|
||||
}
|
||||
}
|
||||
|
||||
void Jit64::DoMergedBranchImmediate(s64 val)
|
||||
{
|
||||
js.downcountAmount++;
|
||||
js.skipnext = true;
|
||||
int test_bit = 8 >> (js.next_inst.BI & 3);
|
||||
bool condition = !!(js.next_inst.BO & BO_BRANCH_IF_TRUE);
|
||||
js.skipInstructions = 1;
|
||||
const UGeckoInstruction& next = js.op[1].inst;
|
||||
int test_bit = 8 >> (next.BI & 3);
|
||||
bool condition = !!(next.BO & BO_BRANCH_IF_TRUE);
|
||||
const u32 nextPC = js.op[1].address;
|
||||
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
|
@ -441,7 +466,7 @@ void Jit64::DoMergedBranchImmediate(s64 val)
|
|||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
WriteExit(js.next_compilerPC + 4);
|
||||
WriteExit(nextPC + 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,15 +95,12 @@ void Jit64::lXXx(UGeckoInstruction inst)
|
|||
}
|
||||
|
||||
// PowerPC has no 8-bit sign extended load, but x86 does, so merge extsb with the load if we find it.
|
||||
if (accessSize == 8 && js.next_inst.OPCD == 31 && js.next_inst.SUBOP10 == 954 &&
|
||||
js.next_inst.RS == inst.RD && js.next_inst.RA == inst.RD && !js.next_inst.Rc)
|
||||
if (MergeAllowedNextInstructions(1) && accessSize == 8 && js.op[1].inst.OPCD == 31 && js.op[1].inst.SUBOP10 == 954 &&
|
||||
js.op[1].inst.RS == inst.RD && js.op[1].inst.RA == inst.RD && !js.op[1].inst.Rc)
|
||||
{
|
||||
if (PowerPC::GetState() != PowerPC::CPU_STEPPING)
|
||||
{
|
||||
js.downcountAmount++;
|
||||
js.skipnext = true;
|
||||
signExtend = true;
|
||||
}
|
||||
js.downcountAmount++;
|
||||
js.skipInstructions = 1;
|
||||
signExtend = true;
|
||||
}
|
||||
|
||||
// TODO(ector): Make it dynamically enable/disable idle skipping where appropriate
|
||||
|
|
|
@ -20,7 +20,7 @@ using namespace Gen;
|
|||
void Jit64::psq_stXX(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
|
||||
s32 offset = inst.SIMM_12;
|
||||
bool indexed = inst.OPCD == 4;
|
||||
|
@ -38,7 +38,10 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
|||
int storeOffset = 0;
|
||||
gpr.BindToRegister(a, true, update);
|
||||
X64Reg addr = gpr.RX(a);
|
||||
if (update && js.memcheck)
|
||||
// TODO: this is kind of ugly :/ we should probably create a universal load/store address calculation
|
||||
// function that handles all these weird cases, e.g. how non-fastmem loadstores clobber addresses.
|
||||
bool storeAddress = (update && js.memcheck) || !SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem;
|
||||
if (storeAddress)
|
||||
{
|
||||
addr = RSCRATCH2;
|
||||
MOV(32, R(addr), gpr.R(a));
|
||||
|
@ -86,11 +89,11 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
|||
}
|
||||
|
||||
BitSet32 registersInUse = CallerSavedRegistersInUse();
|
||||
if (update && js.memcheck)
|
||||
if (update && storeAddress)
|
||||
registersInUse[addr] = true;
|
||||
SafeWriteRegToReg(RSCRATCH, addr, w ? 32 : 64, storeOffset, registersInUse);
|
||||
MemoryExceptionCheck();
|
||||
if (update && js.memcheck)
|
||||
if (update && storeAddress)
|
||||
MOV(32, gpr.R(a), R(addr));
|
||||
gpr.UnlockAll();
|
||||
fpr.UnlockAll();
|
||||
|
@ -154,7 +157,7 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
|||
void Jit64::psq_lXX(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
|
||||
s32 offset = inst.SIMM_12;
|
||||
bool indexed = inst.OPCD == 4;
|
||||
|
|
|
@ -282,38 +282,38 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
|||
ADD(64, R(RAX), R(RDX));
|
||||
MOV(64, PPCSTATE(spr[SPR_TL]), R(RAX));
|
||||
|
||||
// Two calls of TU/TL next to each other are extremely common in typical usage, so merge them
|
||||
// if we can.
|
||||
u32 nextIndex = (js.next_inst.SPRU << 5) | (js.next_inst.SPRL & 0x1F);
|
||||
// Be careful; the actual opcode is for mftb (371), not mfspr (339)
|
||||
int n = js.next_inst.RD;
|
||||
if (js.next_inst.OPCD == 31 && js.next_inst.SUBOP10 == 371 && (nextIndex == SPR_TU || nextIndex == SPR_TL) &&
|
||||
PowerPC::GetState() != PowerPC::CPU_STEPPING && n != d)
|
||||
if (MergeAllowedNextInstructions(1))
|
||||
{
|
||||
js.downcountAmount++;
|
||||
js.skipnext = true;
|
||||
gpr.Lock(d, n);
|
||||
gpr.BindToRegister(d, false);
|
||||
gpr.BindToRegister(n, false);
|
||||
if (iIndex == SPR_TL)
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TL)
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
SHR(64, R(RAX), Imm8(32));
|
||||
if (iIndex == SPR_TU)
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TU)
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.Lock(d);
|
||||
gpr.BindToRegister(d, false);
|
||||
if (iIndex == SPR_TU)
|
||||
const UGeckoInstruction& next = js.op[1].inst;
|
||||
// Two calls of TU/TL next to each other are extremely common in typical usage, so merge them
|
||||
// if we can.
|
||||
u32 nextIndex = (next.SPRU << 5) | (next.SPRL & 0x1F);
|
||||
// Be careful; the actual opcode is for mftb (371), not mfspr (339)
|
||||
int n = next.RD;
|
||||
if (next.OPCD == 31 && next.SUBOP10 == 371 && (nextIndex == SPR_TU || nextIndex == SPR_TL) && n != d)
|
||||
{
|
||||
js.downcountAmount++;
|
||||
js.skipInstructions = 1;
|
||||
gpr.Lock(d, n);
|
||||
gpr.BindToRegister(d, false);
|
||||
gpr.BindToRegister(n, false);
|
||||
if (iIndex == SPR_TL)
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TL)
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
SHR(64, R(RAX), Imm8(32));
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (iIndex == SPR_TU)
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TU)
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
break;
|
||||
}
|
||||
}
|
||||
gpr.UnlockAllX();
|
||||
gpr.Lock(d);
|
||||
gpr.BindToRegister(d, false);
|
||||
if (iIndex == SPR_TU)
|
||||
SHR(64, R(RAX), Imm8(32));
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
break;
|
||||
}
|
||||
case SPR_XER:
|
||||
|
@ -341,6 +341,7 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
|||
MOV(32, gpr.R(d), PPCSTATE(spr[iIndex]));
|
||||
break;
|
||||
}
|
||||
gpr.UnlockAllX();
|
||||
gpr.UnlockAll();
|
||||
}
|
||||
|
||||
|
|
|
@ -610,16 +610,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
js.downcountAmount += opinfo->numCycles;
|
||||
|
||||
if (i == (code_block.m_num_instructions - 1))
|
||||
{
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
}
|
||||
|
||||
u32 function = HLE::GetFunctionIndex(ops[i].address);
|
||||
if (function != 0)
|
||||
|
|
|
@ -443,7 +443,7 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
|
|||
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
|
||||
js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address);
|
||||
|
||||
js.skipnext = false;
|
||||
js.skipInstructions = 0;
|
||||
js.compilerPC = nextPC;
|
||||
|
||||
// Translate instructions
|
||||
|
@ -459,13 +459,6 @@ const u8* JitArm::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlo
|
|||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
}
|
||||
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
|
|
|
@ -16,6 +16,7 @@ void JitArm64::Init()
|
|||
{
|
||||
AllocCodeSpace(CODE_SIZE);
|
||||
jo.enableBlocklink = true;
|
||||
jo.optimizeGatherPipe = true;
|
||||
gpr.Init(this);
|
||||
fpr.Init(this);
|
||||
|
||||
|
@ -179,6 +180,14 @@ void JitArm64::WriteExitDestInR(ARM64Reg Reg)
|
|||
BR(EncodeRegTo64(Reg));
|
||||
}
|
||||
|
||||
void JitArm64::DumpCode(const u8* start, const u8* end)
|
||||
{
|
||||
std::string output = "";
|
||||
for (u8* code = (u8*)start; code < end; code += 4)
|
||||
output += StringFromFormat("%08x", Common::swap32(*(u32*)code));
|
||||
WARN_LOG(DYNA_REC, "Code dump from %p to %p:\n%s", start, end, output.c_str());
|
||||
}
|
||||
|
||||
void JitArm64::Run()
|
||||
{
|
||||
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
|
||||
|
@ -223,7 +232,7 @@ const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitB
|
|||
js.blockStart = em_address;
|
||||
js.fifoBytesThisBlock = 0;
|
||||
js.downcountAmount = 0;
|
||||
js.skipnext = false;
|
||||
js.skipInstructions = 0;
|
||||
js.curBlock = b;
|
||||
|
||||
u32 nextPC = em_address;
|
||||
|
@ -272,13 +281,21 @@ const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitB
|
|||
{
|
||||
// WARNING - cmp->branch merging will screw this up.
|
||||
js.isLastInstruction = true;
|
||||
js.next_inst = 0;
|
||||
}
|
||||
else
|
||||
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
// help peephole optimizations
|
||||
js.next_inst = ops[i + 1].inst;
|
||||
js.next_compilerPC = ops[i + 1].address;
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
|
||||
gpr.Lock(W30);
|
||||
BitSet32 regs_in_use = gpr.GetCallerSavedUsed();
|
||||
regs_in_use[W30] = 0;
|
||||
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
MOVI2R(X30, (u64)&GPFifo::CheckGatherPipe);
|
||||
BLR(X30);
|
||||
ABI_PopRegisters(regs_in_use);
|
||||
gpr.Unlock(W30);
|
||||
}
|
||||
|
||||
if (!ops[i].skip)
|
||||
|
@ -294,6 +311,8 @@ const u8* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitB
|
|||
// If we have a register that will never be used again, flush it.
|
||||
for (int j : ~ops[i].gprInUse)
|
||||
gpr.StoreRegister(j);
|
||||
for (int j : ~ops[i].fprInUse)
|
||||
fpr.StoreRegister(j);
|
||||
|
||||
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
|
||||
{
|
||||
|
|
|
@ -21,11 +21,13 @@
|
|||
// Some asserts to make sure we will be able to load everything
|
||||
static_assert(PPCSTATE_OFF(spr[1023]) <= 16380, "LDR(32bit) can't reach the last SPR");
|
||||
static_assert((PPCSTATE_OFF(ps[0][0]) % 8) == 0, "LDR(64bit VFP) requires FPRs to be 8 byte aligned");
|
||||
static_assert(PPCSTATE_OFF(xer_ca) < 4096, "STRB can't store xer_ca!");
|
||||
static_assert(PPCSTATE_OFF(xer_so_ov) < 4096, "STRB can't store xer_so_ov!");
|
||||
|
||||
class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock
|
||||
{
|
||||
public:
|
||||
JitArm64() : code_buffer(32000) {}
|
||||
JitArm64() : code_buffer(32000), m_float_emit(this) {}
|
||||
~JitArm64() {}
|
||||
|
||||
void Init();
|
||||
|
@ -80,6 +82,7 @@ public:
|
|||
// Integer
|
||||
void arith_imm(UGeckoInstruction inst);
|
||||
void boolX(UGeckoInstruction inst);
|
||||
void addx(UGeckoInstruction inst);
|
||||
void extsXx(UGeckoInstruction inst);
|
||||
void cntlzwx(UGeckoInstruction inst);
|
||||
void negx(UGeckoInstruction inst);
|
||||
|
@ -87,6 +90,14 @@ public:
|
|||
void cmpl(UGeckoInstruction inst);
|
||||
void cmpi(UGeckoInstruction inst);
|
||||
void cmpli(UGeckoInstruction inst);
|
||||
void rlwinmx(UGeckoInstruction inst);
|
||||
void srawix(UGeckoInstruction inst);
|
||||
void mullwx(UGeckoInstruction inst);
|
||||
void addic(UGeckoInstruction inst);
|
||||
void mulli(UGeckoInstruction inst);
|
||||
void addzex(UGeckoInstruction inst);
|
||||
void subfx(UGeckoInstruction inst);
|
||||
void addcx(UGeckoInstruction inst);
|
||||
|
||||
// System Registers
|
||||
void mtmsr(UGeckoInstruction inst);
|
||||
|
@ -97,12 +108,66 @@ public:
|
|||
void mfsrin(UGeckoInstruction inst);
|
||||
void mtsrin(UGeckoInstruction inst);
|
||||
void twx(UGeckoInstruction inst);
|
||||
void mfspr(UGeckoInstruction inst);
|
||||
void mftb(UGeckoInstruction inst);
|
||||
void mtspr(UGeckoInstruction inst);
|
||||
|
||||
// LoadStore
|
||||
void icbi(UGeckoInstruction inst);
|
||||
void lXX(UGeckoInstruction inst);
|
||||
void stX(UGeckoInstruction inst);
|
||||
|
||||
// LoadStore floating point
|
||||
void lfXX(UGeckoInstruction inst);
|
||||
void stfXX(UGeckoInstruction inst);
|
||||
|
||||
// Floating point
|
||||
void fabsx(UGeckoInstruction inst);
|
||||
void faddsx(UGeckoInstruction inst);
|
||||
void faddx(UGeckoInstruction inst);
|
||||
void fmaddsx(UGeckoInstruction inst);
|
||||
void fmaddx(UGeckoInstruction inst);
|
||||
void fmrx(UGeckoInstruction inst);
|
||||
void fmsubsx(UGeckoInstruction inst);
|
||||
void fmsubx(UGeckoInstruction inst);
|
||||
void fmulsx(UGeckoInstruction inst);
|
||||
void fmulx(UGeckoInstruction inst);
|
||||
void fnabsx(UGeckoInstruction inst);
|
||||
void fnegx(UGeckoInstruction inst);
|
||||
void fnmaddsx(UGeckoInstruction inst);
|
||||
void fnmaddx(UGeckoInstruction inst);
|
||||
void fnmsubsx(UGeckoInstruction inst);
|
||||
void fnmsubx(UGeckoInstruction inst);
|
||||
void fselx(UGeckoInstruction inst);
|
||||
void fsubsx(UGeckoInstruction inst);
|
||||
void fsubx(UGeckoInstruction inst);
|
||||
|
||||
// Paired
|
||||
void ps_abs(UGeckoInstruction inst);
|
||||
void ps_add(UGeckoInstruction inst);
|
||||
void ps_div(UGeckoInstruction inst);
|
||||
void ps_madd(UGeckoInstruction inst);
|
||||
void ps_madds0(UGeckoInstruction inst);
|
||||
void ps_madds1(UGeckoInstruction inst);
|
||||
void ps_merge00(UGeckoInstruction inst);
|
||||
void ps_merge01(UGeckoInstruction inst);
|
||||
void ps_merge10(UGeckoInstruction inst);
|
||||
void ps_merge11(UGeckoInstruction inst);
|
||||
void ps_mr(UGeckoInstruction inst);
|
||||
void ps_msub(UGeckoInstruction inst);
|
||||
void ps_mul(UGeckoInstruction inst);
|
||||
void ps_muls0(UGeckoInstruction inst);
|
||||
void ps_muls1(UGeckoInstruction inst);
|
||||
void ps_nabs(UGeckoInstruction inst);
|
||||
void ps_nmadd(UGeckoInstruction inst);
|
||||
void ps_nmsub(UGeckoInstruction inst);
|
||||
void ps_neg(UGeckoInstruction inst);
|
||||
void ps_res(UGeckoInstruction inst);
|
||||
void ps_sel(UGeckoInstruction inst);
|
||||
void ps_sub(UGeckoInstruction inst);
|
||||
void ps_sum0(UGeckoInstruction inst);
|
||||
void ps_sum1(UGeckoInstruction inst);
|
||||
|
||||
private:
|
||||
Arm64GPRCache gpr;
|
||||
Arm64FPRCache fpr;
|
||||
|
@ -112,6 +177,11 @@ private:
|
|||
|
||||
PPCAnalyst::CodeBuffer code_buffer;
|
||||
|
||||
ARM64FloatEmitter m_float_emit;
|
||||
|
||||
// Dump a memory range of code
|
||||
void DumpCode(const u8* start, const u8* end);
|
||||
|
||||
// The key is the backpatch flags
|
||||
std::map<u32, BackPatchInfo> m_backpatch_info;
|
||||
|
||||
|
@ -137,6 +207,8 @@ private:
|
|||
|
||||
void ComputeRC(Arm64Gen::ARM64Reg reg, int crf = 0);
|
||||
void ComputeRC(u32 imm, int crf = 0);
|
||||
void ComputeCarry(bool Carry);
|
||||
void ComputeCarry();
|
||||
|
||||
typedef u32 (*Operation)(u32, u32);
|
||||
void reg_imm(u32 d, u32 a, bool binary, u32 value, Operation do_op, void (ARM64XEmitter::*op)(Arm64Gen::ARM64Reg, Arm64Gen::ARM64Reg, Arm64Gen::ARM64Reg, ArithOption), bool Rc = false);
|
||||
|
|
|
@ -29,7 +29,8 @@ static void DoBacktrace(uintptr_t access_address, SContext* ctx)
|
|||
for (u64 pc = (ctx->CTX_PC - 32); pc < (ctx->CTX_PC + 32); pc += 16)
|
||||
{
|
||||
pc_memory += StringFromFormat("%08x%08x%08x%08x",
|
||||
*(u32*)pc, *(u32*)(pc + 4), *(u32*)(pc + 8), *(u32*)(pc + 12));
|
||||
Common::swap32(*(u32*)pc), Common::swap32(*(u32*)(pc + 4)),
|
||||
Common::swap32(*(u32*)(pc + 8)), Common::swap32(*(u32*)(pc + 12)));
|
||||
|
||||
ERROR_LOG(DYNA_REC, "0x%016lx: %08x %08x %08x %08x",
|
||||
pc, *(u32*)pc, *(u32*)(pc + 4), *(u32*)(pc + 8), *(u32*)(pc + 12));
|
||||
|
@ -51,10 +52,34 @@ bool JitArm64::DisasmLoadStore(const u8* ptr, u32* flags, ARM64Reg* reg)
|
|||
*flags |= BackPatchInfo::FLAG_SIZE_8;
|
||||
else if (size == 1) // 16-bit
|
||||
*flags |= BackPatchInfo::FLAG_SIZE_16;
|
||||
else // 32-bit
|
||||
else if (size == 2) // 32-bit
|
||||
*flags |= BackPatchInfo::FLAG_SIZE_32;
|
||||
else if (size == 3) // 64-bit
|
||||
*flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
|
||||
if (op == 0xE5) // Load
|
||||
if (op == 0xF5) // NEON LDR
|
||||
{
|
||||
if (size == 2) // 32-bit float
|
||||
{
|
||||
*flags &= ~BackPatchInfo::FLAG_SIZE_32;
|
||||
*flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
}
|
||||
*flags |= BackPatchInfo::FLAG_LOAD;
|
||||
*reg = (ARM64Reg)(inst & 0x1F);
|
||||
return true;
|
||||
}
|
||||
else if (op == 0xF4) // NEON STR
|
||||
{
|
||||
if (size == 2) // 32-bit float
|
||||
{
|
||||
*flags &= ~BackPatchInfo::FLAG_SIZE_32;
|
||||
*flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
}
|
||||
*flags |= BackPatchInfo::FLAG_STORE;
|
||||
*reg = (ARM64Reg)(inst & 0x1F);
|
||||
return true;
|
||||
}
|
||||
else if (op == 0xE5) // Load
|
||||
{
|
||||
*flags |= BackPatchInfo::FLAG_LOAD;
|
||||
*reg = (ARM64Reg)(inst & 0x1F);
|
||||
|
@ -90,10 +115,38 @@ u32 JitArm64::EmitBackpatchRoutine(ARM64XEmitter* emit, u32 flags, bool fastmem,
|
|||
if (flags & BackPatchInfo::FLAG_STORE &&
|
||||
flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64))
|
||||
{
|
||||
ARM64FloatEmitter float_emit(emit);
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F32)
|
||||
{
|
||||
float_emit.FCVT(32, 64, Q0, RS);
|
||||
float_emit.REV32(8, D0, D0);
|
||||
trouble_offset = (emit->GetCodePtr() - code_base) / 4;
|
||||
float_emit.STR(32, INDEX_UNSIGNED, D0, addr, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
float_emit.REV64(8, Q0, RS);
|
||||
trouble_offset = (emit->GetCodePtr() - code_base) / 4;
|
||||
float_emit.STR(64, INDEX_UNSIGNED, Q0, addr, 0);
|
||||
}
|
||||
}
|
||||
else if (flags & BackPatchInfo::FLAG_LOAD &&
|
||||
flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64))
|
||||
{
|
||||
ARM64FloatEmitter float_emit(emit);
|
||||
trouble_offset = (emit->GetCodePtr() - code_base) / 4;
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F32)
|
||||
{
|
||||
float_emit.LD1R(32, RS, addr);
|
||||
float_emit.REV64(8, RS, RS);
|
||||
float_emit.FCVTL(64, RS, RS);
|
||||
}
|
||||
else
|
||||
{
|
||||
float_emit.LDR(64, INDEX_UNSIGNED, Q0, addr, 0);
|
||||
float_emit.REV64(8, Q0, Q0);
|
||||
float_emit.INS(64, RS, 0, Q0, 0);
|
||||
}
|
||||
}
|
||||
else if (flags & BackPatchInfo::FLAG_STORE)
|
||||
{
|
||||
|
@ -143,10 +196,39 @@ u32 JitArm64::EmitBackpatchRoutine(ARM64XEmitter* emit, u32 flags, bool fastmem,
|
|||
if (flags & BackPatchInfo::FLAG_STORE &&
|
||||
flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64))
|
||||
{
|
||||
ARM64FloatEmitter float_emit(emit);
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F32)
|
||||
{
|
||||
float_emit.FCVT(32, 64, Q0, RS);
|
||||
float_emit.FMOV(32, false, W0, Q0);
|
||||
emit->MOVI2R(X30, (u64)&Memory::Write_U32);
|
||||
emit->BLR(X30);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit->MOVI2R(X30, (u64)&Memory::Write_F64);
|
||||
float_emit.DUP(64, Q0, RS);
|
||||
emit->BLR(X30);
|
||||
}
|
||||
|
||||
}
|
||||
else if (flags & BackPatchInfo::FLAG_LOAD &&
|
||||
flags & (BackPatchInfo::FLAG_SIZE_F32 | BackPatchInfo::FLAG_SIZE_F64))
|
||||
{
|
||||
ARM64FloatEmitter float_emit(emit);
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F32)
|
||||
{
|
||||
emit->MOVI2R(X30, (u64)&Memory::Read_U32);
|
||||
emit->BLR(X30);
|
||||
float_emit.DUP(32, RS, X0);
|
||||
float_emit.FCVTL(64, RS, RS);
|
||||
}
|
||||
else
|
||||
{
|
||||
emit->MOVI2R(X30, (u64)&Memory::Read_F64);
|
||||
emit->BLR(X30);
|
||||
float_emit.INS(64, RS, 0, X0);
|
||||
}
|
||||
}
|
||||
else if (flags & BackPatchInfo::FLAG_STORE)
|
||||
{
|
||||
|
@ -245,7 +327,8 @@ bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
|||
ctx->CTX_PC = new_pc;
|
||||
|
||||
// Wipe the top bits of the addr_register
|
||||
if (flags & BackPatchInfo::FLAG_STORE)
|
||||
if (flags & BackPatchInfo::FLAG_STORE &&
|
||||
!(flags & BackPatchInfo::FLAG_SIZE_F64))
|
||||
ctx->CTX_REG(1) &= 0xFFFFFFFFUll;
|
||||
else
|
||||
ctx->CTX_REG(0) &= 0xFFFFFFFFUll;
|
||||
|
@ -382,6 +465,46 @@ void JitArm64::InitBackpatch()
|
|||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
// 32bit float
|
||||
{
|
||||
flags =
|
||||
BackPatchInfo::FLAG_LOAD |
|
||||
BackPatchInfo::FLAG_SIZE_F32;
|
||||
EmitBackpatchRoutine(this, flags, false, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_slowmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
info.m_fastmem_trouble_inst_offset =
|
||||
EmitBackpatchRoutine(this, flags, true, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_fastmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
// 64bit float
|
||||
{
|
||||
flags =
|
||||
BackPatchInfo::FLAG_LOAD |
|
||||
BackPatchInfo::FLAG_SIZE_F64;
|
||||
EmitBackpatchRoutine(this, flags, false, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_slowmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
info.m_fastmem_trouble_inst_offset =
|
||||
EmitBackpatchRoutine(this, flags, true, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_fastmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
}
|
||||
|
@ -446,6 +569,46 @@ void JitArm64::InitBackpatch()
|
|||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
// 32bit float
|
||||
{
|
||||
flags =
|
||||
BackPatchInfo::FLAG_STORE |
|
||||
BackPatchInfo::FLAG_SIZE_F32;
|
||||
EmitBackpatchRoutine(this, flags, false, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_slowmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
info.m_fastmem_trouble_inst_offset =
|
||||
EmitBackpatchRoutine(this, flags, true, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_fastmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
// 64bit float
|
||||
{
|
||||
flags =
|
||||
BackPatchInfo::FLAG_STORE |
|
||||
BackPatchInfo::FLAG_SIZE_F64;
|
||||
EmitBackpatchRoutine(this, flags, false, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_slowmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
info.m_fastmem_trouble_inst_offset =
|
||||
EmitBackpatchRoutine(this, flags, true, false, Q0, X1);
|
||||
code_end = GetWritableCodePtr();
|
||||
info.m_fastmem_size = (code_end - code_base) / 4;
|
||||
|
||||
SetCodePtr(code_base);
|
||||
|
||||
m_backpatch_info[flags] = info;
|
||||
}
|
||||
}
|
||||
|
|
376
Source/Core/Core/PowerPC/JitArm64/JitArm64_FloatingPoint.cpp
Normal file
376
Source/Core/Core/PowerPC/JitArm64/JitArm64_FloatingPoint.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/PowerPC/PPCTables.h"
|
||||
#include "Core/PowerPC/JitArm64/Jit.h"
|
||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||
#include "Core/PowerPC/JitArm64/JitAsm.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
void JitArm64::fabsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FB);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FABS(64, V0, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::faddsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FB);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
|
||||
m_float_emit.FADD(64, VD, VA, VB);
|
||||
m_float_emit.INS(64, VD, 1, VD, 0);
|
||||
}
|
||||
|
||||
void JitArm64::faddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FB);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FADD(64, V0, VA, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fmaddsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, V0, V0, VB);
|
||||
m_float_emit.DUP(64, VD, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fmaddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, V0, V0, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fmrx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FB);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
|
||||
m_float_emit.INS(64, VD, 0, VB, 0);
|
||||
}
|
||||
|
||||
void JitArm64::fmsubsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, V0, V0, VB);
|
||||
m_float_emit.DUP(64, VD, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fmsubx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, V0, V0, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fmulsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FC);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VC = fpr.R(inst.FC);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
|
||||
m_float_emit.FMUL(64, VD, VA, VC);
|
||||
m_float_emit.INS(64, VD, 1, VD, 0);
|
||||
}
|
||||
|
||||
void JitArm64::fmulx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FC);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VC = fpr.R(inst.FC);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnabsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FB);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FABS(64, V0, VB);
|
||||
m_float_emit.FNEG(64, V0, V0);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnegx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FB);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FNEG(64, V0, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnmaddsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, V0, V0, VB);
|
||||
m_float_emit.FNEG(64, V0, V0);
|
||||
m_float_emit.DUP(64, VD, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnmaddx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, V0, V0, VB);
|
||||
m_float_emit.FNEG(64, V0, V0);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnmsubsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, V0, V0, VB);
|
||||
m_float_emit.FNEG(64, V0, V0);
|
||||
m_float_emit.DUP(64, VD, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fnmsubx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, V0, V0, VB);
|
||||
m_float_emit.FNEG(64, V0, V0);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fselx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
fpr.BindToRegister(inst.FD,
|
||||
inst.FD == inst.FA ||
|
||||
inst.FD == inst.FB ||
|
||||
inst.FD == inst.FC);
|
||||
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VC = gpr.R(inst.FC);
|
||||
|
||||
m_float_emit.FCMPE(VA);
|
||||
m_float_emit.FCSEL(V0, VC, VB, CC_GE);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::fsubsx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FB);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
|
||||
m_float_emit.FSUB(64, VD, VA, VB);
|
||||
m_float_emit.INS(64, VD, 1, VD, 0);
|
||||
}
|
||||
|
||||
void JitArm64::fsubx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITFloatingPointOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
fpr.BindToRegister(inst.FD, inst.FD == inst.FA || inst.FD == inst.FB);
|
||||
ARM64Reg VA = fpr.R(inst.FA);
|
||||
ARM64Reg VB = fpr.R(inst.FB);
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FSUB(64, V0, VA, VB);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
|
@ -39,6 +39,28 @@ void JitArm64::ComputeRC(u32 imm, int crf)
|
|||
gpr.Unlock(WA);
|
||||
}
|
||||
|
||||
void JitArm64::ComputeCarry(bool Carry)
|
||||
{
|
||||
if (Carry)
|
||||
{
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
MOVI2R(WA, 1);
|
||||
STRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_ca));
|
||||
gpr.Unlock(WA);
|
||||
return;
|
||||
}
|
||||
|
||||
STRB(INDEX_UNSIGNED, WSP, X29, PPCSTATE_OFF(xer_ca));
|
||||
}
|
||||
|
||||
void JitArm64::ComputeCarry()
|
||||
{
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
CSINC(WA, WSP, WSP, CC_CC);
|
||||
STRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_ca));
|
||||
gpr.Unlock(WA);
|
||||
}
|
||||
|
||||
// Following static functions are used in conjunction with reg_imm
|
||||
static u32 Add(u32 a, u32 b)
|
||||
{
|
||||
|
@ -245,6 +267,29 @@ void JitArm64::boolX(UGeckoInstruction inst)
|
|||
}
|
||||
}
|
||||
|
||||
void JitArm64::addx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
s32 i = (s32)gpr.GetImm(a), j = (s32)gpr.GetImm(b);
|
||||
gpr.SetImmediate(d, i + j);
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.GetImm(d), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD(gpr.R(d), gpr.R(a), gpr.R(b));
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.R(d), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::extsXx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
|
@ -415,3 +460,237 @@ void JitArm64::cmpli(UGeckoInstruction inst)
|
|||
FALLBACK_IF(true);
|
||||
}
|
||||
|
||||
void JitArm64::rlwinmx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
u32 mask = Helper_Mask(inst.MB,inst.ME);
|
||||
if (gpr.IsImm(inst.RS))
|
||||
{
|
||||
gpr.SetImmediate(inst.RA, _rotl(gpr.GetImm(inst.RS), inst.SH) & mask);
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.GetImm(inst.RA), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
gpr.BindToRegister(inst.RA, inst.RA == inst.RS);
|
||||
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
ArithOption Shift(gpr.R(inst.RS), ST_ROR, 32 - inst.SH);
|
||||
MOVI2R(WA, mask);
|
||||
AND(gpr.R(inst.RA), WA, gpr.R(inst.RS), Shift);
|
||||
gpr.Unlock(WA);
|
||||
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.R(inst.RA), 0);
|
||||
}
|
||||
|
||||
void JitArm64::srawix(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA;
|
||||
int s = inst.RS;
|
||||
int amount = inst.SH;
|
||||
|
||||
if (gpr.IsImm(s))
|
||||
{
|
||||
s32 imm = (s32)gpr.GetImm(s);
|
||||
gpr.SetImmediate(a, imm >> amount);
|
||||
|
||||
if (amount != 0 && (imm < 0) && (imm << (32 - amount)))
|
||||
ComputeCarry(true);
|
||||
else
|
||||
ComputeCarry(false);
|
||||
}
|
||||
else if (amount != 0)
|
||||
{
|
||||
gpr.BindToRegister(a, a == s);
|
||||
ARM64Reg RA = gpr.R(a);
|
||||
ARM64Reg RS = gpr.R(s);
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
|
||||
ORR(WA, WSP, RS, ArithOption(RS, ST_LSL, 32 - amount));
|
||||
ORR(RA, WSP, RS, ArithOption(RS, ST_ASR, amount));
|
||||
if (inst.Rc)
|
||||
ComputeRC(RA, 0);
|
||||
|
||||
ANDS(WSP, WA, RA, ArithOption(RA, ST_LSL, 0));
|
||||
CSINC(WA, WSP, WSP, CC_EQ);
|
||||
STRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_ca));
|
||||
gpr.Unlock(WA);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(a, a == s);
|
||||
ARM64Reg RA = gpr.R(a);
|
||||
ARM64Reg RS = gpr.R(s);
|
||||
MOV(RA, RS);
|
||||
STRB(INDEX_UNSIGNED, WSP, X29, PPCSTATE_OFF(xer_ca));
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::addic(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
bool rc = inst.OPCD == 13;
|
||||
s32 simm = inst.SIMM_16;
|
||||
u32 imm = (u32)simm;
|
||||
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
|
||||
u32 i = gpr.GetImm(a);
|
||||
gpr.SetImmediate(d, i + imm);
|
||||
|
||||
bool has_carry = Interpreter::Helper_Carry(i, imm);
|
||||
ComputeCarry(has_carry);
|
||||
if (rc)
|
||||
ComputeRC(gpr.GetImm(d), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
if (imm < 4096)
|
||||
{
|
||||
ADDS(gpr.R(d), gpr.R(a), imm);
|
||||
}
|
||||
else if (simm > -4096 && simm < 0)
|
||||
{
|
||||
SUBS(gpr.R(d), gpr.R(a), std::abs(simm));
|
||||
}
|
||||
else
|
||||
{
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
MOVI2R(WA, imm);
|
||||
ADDS(gpr.R(d), gpr.R(a), WA);
|
||||
gpr.Unlock(WA);
|
||||
}
|
||||
|
||||
ComputeCarry();
|
||||
if (rc)
|
||||
ComputeRC(gpr.R(d), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::mulli(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a))
|
||||
{
|
||||
s32 i = (s32)gpr.GetImm(a);
|
||||
gpr.SetImmediate(d, i * inst.SIMM_16);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(d, d == a);
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
MOVI2R(WA, (u32)(s32)inst.SIMM_16);
|
||||
MUL(gpr.R(d), gpr.R(a), WA);
|
||||
gpr.Unlock(WA);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::mullwx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
s32 i = (s32)gpr.GetImm(a), j = (s32)gpr.GetImm(b);
|
||||
gpr.SetImmediate(d, i * j);
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.GetImm(d), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(d, d == a || d == b);
|
||||
MUL(gpr.R(d), gpr.R(a), gpr.R(b));
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.R(d), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::addzex(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, d = inst.RD;
|
||||
|
||||
gpr.BindToRegister(d, d == a);
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
LDRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_ca));
|
||||
CMP(WA, 1);
|
||||
CSINC(gpr.R(d), gpr.R(a), gpr.R(a), CC_NEQ);
|
||||
CMP(gpr.R(d), 0);
|
||||
gpr.Unlock(WA);
|
||||
ComputeCarry();
|
||||
}
|
||||
|
||||
void JitArm64::subfx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
||||
gpr.SetImmediate(d, j - i);
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.GetImm(d), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SUB(gpr.R(d), gpr.R(b), gpr.R(a));
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.R(d), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::addcx(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITIntegerOff);
|
||||
FALLBACK_IF(inst.OE);
|
||||
|
||||
int a = inst.RA, b = inst.RB, d = inst.RD;
|
||||
|
||||
if (gpr.IsImm(a) && gpr.IsImm(b))
|
||||
{
|
||||
u32 i = gpr.GetImm(a), j = gpr.GetImm(b);
|
||||
gpr.SetImmediate(d, i * j);
|
||||
|
||||
bool has_carry = Interpreter::Helper_Carry(i, j);
|
||||
ComputeCarry(has_carry);
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.GetImm(d), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(d, d == a || d == b);
|
||||
ADDS(gpr.R(d), gpr.R(a), gpr.R(b));
|
||||
|
||||
ComputeCarry();
|
||||
if (inst.Rc)
|
||||
ComputeRC(gpr.R(d), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o
|
|||
off_reg = gpr.R(offsetReg);
|
||||
|
||||
BitSet32 regs_in_use = gpr.GetCallerSavedUsed();
|
||||
BitSet32 fprs_in_use = fpr.GetCallerSavedUsed();
|
||||
BitSet32 ignore_mask(0);
|
||||
regs_in_use[W0] = 0;
|
||||
regs_in_use[W30] = 0;
|
||||
|
@ -114,25 +115,24 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o
|
|||
if (is_immediate)
|
||||
MOVI2R(XA, imm_addr);
|
||||
|
||||
if (update)
|
||||
MOV(gpr.R(addr), addr_reg);
|
||||
|
||||
if (is_immediate && Memory::IsRAMAddress(imm_addr))
|
||||
{
|
||||
EmitBackpatchRoutine(this, flags, true, false, dest_reg, XA);
|
||||
|
||||
if (update)
|
||||
MOVI2R(up_reg, imm_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (update)
|
||||
MOV(up_reg, addr_reg);
|
||||
|
||||
// Has a chance of being backpatched which will destroy our state
|
||||
// push and pop everything in this instance
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
m_float_emit.ABI_PushRegisters(fprs_in_use);
|
||||
EmitBackpatchRoutine(this, flags,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
dest_reg, XA);
|
||||
m_float_emit.ABI_PopRegisters(fprs_in_use);
|
||||
ABI_PopRegisters(regs_in_use, ignore_mask);
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,7 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s
|
|||
reg_dest = gpr.R(dest);
|
||||
|
||||
BitSet32 regs_in_use = gpr.GetCallerSavedUsed();
|
||||
BitSet32 fprs_in_use = fpr.GetCallerSavedUsed();
|
||||
regs_in_use[W0] = 0;
|
||||
regs_in_use[W1] = 0;
|
||||
regs_in_use[W30] = 0;
|
||||
|
@ -237,10 +238,12 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s
|
|||
// Has a chance of being backpatched which will destroy our state
|
||||
// push and pop everything in this instance
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
m_float_emit.ABI_PushRegisters(fprs_in_use);
|
||||
EmitBackpatchRoutine(this, flags,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
RS, XA);
|
||||
m_float_emit.ABI_PopRegisters(fprs_in_use);
|
||||
ABI_PopRegisters(regs_in_use);
|
||||
}
|
||||
|
||||
|
@ -321,8 +324,6 @@ void JitArm64::lXX(UGeckoInstruction inst)
|
|||
break;
|
||||
}
|
||||
|
||||
FALLBACK_IF(update);
|
||||
|
||||
SafeLoadToReg(d, update ? a : (a ? a : -1), offsetReg, flags, offset, update);
|
||||
|
||||
// LWZ idle skipping
|
||||
|
|
394
Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp
Normal file
394
Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStoreFloating.cpp
Normal file
|
@ -0,0 +1,394 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Common/Common.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/PowerPC/PPCTables.h"
|
||||
#include "Core/PowerPC/JitArm64/Jit.h"
|
||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||
#include "Core/PowerPC/JitArm64/JitAsm.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
void JitArm64::lfXX(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
||||
|
||||
u32 a = inst.RA, b = inst.RB;
|
||||
|
||||
s32 offset = inst.SIMM_16;
|
||||
u32 flags = BackPatchInfo::FLAG_LOAD;
|
||||
bool update = false;
|
||||
s32 offset_reg = -1;
|
||||
|
||||
switch (inst.OPCD)
|
||||
{
|
||||
case 31:
|
||||
switch (inst.SUBOP10)
|
||||
{
|
||||
case 567: // lfsux
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
update = true;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 535: // lfsx
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 631: // lfdux
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
update = true;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 599: // lfdx
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
offset_reg = b;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 49: // lfsu
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
update = true;
|
||||
break;
|
||||
case 48: // lfs
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
break;
|
||||
case 51: // lfdu
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
update = true;
|
||||
break;
|
||||
case 50: // lfd
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 imm_addr = 0;
|
||||
bool is_immediate = false;
|
||||
|
||||
ARM64Reg VD = fpr.R(inst.FD);
|
||||
ARM64Reg addr_reg = W0;
|
||||
|
||||
gpr.Lock(W0, W30);
|
||||
fpr.Lock(Q0);
|
||||
|
||||
if (update)
|
||||
{
|
||||
// Always uses RA
|
||||
if (gpr.IsImm(a) && offset_reg == -1)
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = offset + gpr.GetImm(a);
|
||||
}
|
||||
else if (gpr.IsImm(a) && offset_reg != -1 && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + gpr.GetImm(offset_reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset_reg == -1)
|
||||
{
|
||||
MOVI2R(addr_reg, offset);
|
||||
ADD(addr_reg, addr_reg, gpr.R(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD(addr_reg, gpr.R(offset_reg), gpr.R(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset_reg == -1)
|
||||
{
|
||||
if (a && gpr.IsImm(a))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + offset;
|
||||
}
|
||||
else if (a)
|
||||
{
|
||||
MOVI2R(addr_reg, offset);
|
||||
ADD(addr_reg, addr_reg, gpr.R(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a && gpr.IsImm(a) && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + gpr.GetImm(offset_reg);
|
||||
}
|
||||
else if (!a && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(offset_reg);
|
||||
}
|
||||
else if (a)
|
||||
{
|
||||
ADD(addr_reg, gpr.R(a), gpr.R(offset_reg));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(addr_reg, gpr.R(offset_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg XA = EncodeRegTo64(addr_reg);
|
||||
|
||||
if (is_immediate)
|
||||
MOVI2R(XA, imm_addr);
|
||||
|
||||
if (update)
|
||||
MOV(gpr.R(a), addr_reg);
|
||||
|
||||
BitSet32 regs_in_use = gpr.GetCallerSavedUsed();
|
||||
BitSet32 fprs_in_use = fpr.GetCallerSavedUsed();
|
||||
BitSet32 fpr_ignore_mask(0);
|
||||
regs_in_use[W0] = 0;
|
||||
regs_in_use[W30] = 0;
|
||||
fprs_in_use[0] = 0; // Q0
|
||||
fpr_ignore_mask[VD - Q0] = 1;
|
||||
|
||||
if (is_immediate && Memory::IsRAMAddress(imm_addr))
|
||||
{
|
||||
EmitBackpatchRoutine(this, flags, true, false, VD, XA);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Has a chance of being backpatched which will destroy our state
|
||||
// push and pop everything in this instance
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
m_float_emit.ABI_PushRegisters(fprs_in_use);
|
||||
EmitBackpatchRoutine(this, flags,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
VD, XA);
|
||||
m_float_emit.ABI_PopRegisters(fprs_in_use, fpr_ignore_mask);
|
||||
ABI_PopRegisters(regs_in_use);
|
||||
}
|
||||
|
||||
gpr.Unlock(W0, W30);
|
||||
fpr.Unlock(Q0);
|
||||
}
|
||||
|
||||
void JitArm64::stfXX(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStoreFloatingOff);
|
||||
|
||||
u32 a = inst.RA, b = inst.RB;
|
||||
|
||||
s32 offset = inst.SIMM_16;
|
||||
u32 flags = BackPatchInfo::FLAG_STORE;
|
||||
bool update = false;
|
||||
s32 offset_reg = -1;
|
||||
|
||||
switch (inst.OPCD)
|
||||
{
|
||||
case 31:
|
||||
switch (inst.SUBOP10)
|
||||
{
|
||||
case 663: // stfsx
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 695: // stfsux
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 727: // stfdx
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
offset_reg = b;
|
||||
break;
|
||||
case 759: // stfdux
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
update = true;
|
||||
offset_reg = b;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 53: // stfsu
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
update = true;
|
||||
break;
|
||||
case 52: // stfs
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F32;
|
||||
break;
|
||||
case 55: // stfdu
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
update = true;
|
||||
break;
|
||||
case 54: // stfd
|
||||
flags |= BackPatchInfo::FLAG_SIZE_F64;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 imm_addr = 0;
|
||||
bool is_immediate = false;
|
||||
|
||||
ARM64Reg V0 = fpr.R(inst.FS);
|
||||
ARM64Reg addr_reg;
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F64)
|
||||
addr_reg = W0;
|
||||
else
|
||||
addr_reg = W1;
|
||||
|
||||
gpr.Lock(W0, W1, W30);
|
||||
fpr.Lock(Q0);
|
||||
|
||||
if (update)
|
||||
{
|
||||
// Always uses RA
|
||||
if (gpr.IsImm(a) && offset_reg == -1)
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = offset + gpr.GetImm(a);
|
||||
}
|
||||
else if (gpr.IsImm(a) && offset_reg != -1 && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + gpr.GetImm(offset_reg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset_reg == -1)
|
||||
{
|
||||
MOVI2R(addr_reg, offset);
|
||||
ADD(addr_reg, addr_reg, gpr.R(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
ADD(addr_reg, gpr.R(offset_reg), gpr.R(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (offset_reg == -1)
|
||||
{
|
||||
if (a && gpr.IsImm(a))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + offset;
|
||||
}
|
||||
else if (a)
|
||||
{
|
||||
MOVI2R(addr_reg, offset);
|
||||
ADD(addr_reg, addr_reg, gpr.R(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a && gpr.IsImm(a) && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(a) + gpr.GetImm(offset_reg);
|
||||
}
|
||||
else if (!a && gpr.IsImm(offset_reg))
|
||||
{
|
||||
is_immediate = true;
|
||||
imm_addr = gpr.GetImm(offset_reg);
|
||||
}
|
||||
else if (a)
|
||||
{
|
||||
ADD(addr_reg, gpr.R(a), gpr.R(offset_reg));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(addr_reg, gpr.R(offset_reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg XA = EncodeRegTo64(addr_reg);
|
||||
|
||||
if (is_immediate)
|
||||
MOVI2R(XA, imm_addr);
|
||||
|
||||
if (update)
|
||||
MOV(gpr.R(a), addr_reg);
|
||||
|
||||
BitSet32 regs_in_use = gpr.GetCallerSavedUsed();
|
||||
BitSet32 fprs_in_use = fpr.GetCallerSavedUsed();
|
||||
regs_in_use[W0] = 0;
|
||||
regs_in_use[W1] = 0;
|
||||
regs_in_use[W30] = 0;
|
||||
fprs_in_use[0] = 0; // Q0
|
||||
|
||||
if (is_immediate)
|
||||
{
|
||||
if ((imm_addr & 0xFFFFF000) == 0xCC008000 && jit->jo.optimizeGatherPipe)
|
||||
{
|
||||
int accessSize;
|
||||
if (flags & BackPatchInfo::FLAG_SIZE_F64)
|
||||
accessSize = 64;
|
||||
else
|
||||
accessSize = 32;
|
||||
|
||||
MOVI2R(X30, (u64)&GPFifo::m_gatherPipeCount);
|
||||
MOVI2R(X1, (u64)GPFifo::m_gatherPipe);
|
||||
LDR(INDEX_UNSIGNED, W0, X30, 0);
|
||||
ADD(X1, X1, X0);
|
||||
if (accessSize == 64)
|
||||
{
|
||||
m_float_emit.REV64(8, Q0, V0);
|
||||
m_float_emit.STR(64, INDEX_UNSIGNED, Q0, X1, 0);
|
||||
}
|
||||
else if (accessSize == 32)
|
||||
{
|
||||
m_float_emit.FCVT(32, 64, Q0, V0);
|
||||
m_float_emit.REV32(8, D0, D0);
|
||||
m_float_emit.STR(32, INDEX_UNSIGNED, D0, X1, 0);
|
||||
}
|
||||
ADD(W0, W0, accessSize >> 3);
|
||||
STR(INDEX_UNSIGNED, W0, X30, 0);
|
||||
jit->js.fifoBytesThisBlock += accessSize >> 3;
|
||||
|
||||
}
|
||||
else if (Memory::IsRAMAddress(imm_addr))
|
||||
{
|
||||
EmitBackpatchRoutine(this, flags, true, false, V0, XA);
|
||||
}
|
||||
else
|
||||
{
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
m_float_emit.ABI_PushRegisters(fprs_in_use);
|
||||
EmitBackpatchRoutine(this, flags, false, false, V0, XA);
|
||||
m_float_emit.ABI_PopRegisters(fprs_in_use);
|
||||
ABI_PopRegisters(regs_in_use);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Has a chance of being backpatched which will destroy our state
|
||||
// push and pop everything in this instance
|
||||
ABI_PushRegisters(regs_in_use);
|
||||
m_float_emit.ABI_PushRegisters(fprs_in_use);
|
||||
EmitBackpatchRoutine(this, flags,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bFastmem,
|
||||
V0, XA);
|
||||
m_float_emit.ABI_PopRegisters(fprs_in_use);
|
||||
ABI_PopRegisters(regs_in_use);
|
||||
}
|
||||
gpr.Unlock(W0, W1, W30);
|
||||
fpr.Unlock(Q0);
|
||||
}
|
495
Source/Core/Core/PowerPC/JitArm64/JitArm64_Paired.cpp
Normal file
495
Source/Core/Core/PowerPC/JitArm64/JitArm64_Paired.cpp
Normal file
|
@ -0,0 +1,495 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "Common/Arm64Emitter.h"
|
||||
#include "Common/Common.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/PowerPC/PPCTables.h"
|
||||
#include "Core/PowerPC/JitArm64/Jit.h"
|
||||
#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h"
|
||||
#include "Core/PowerPC/JitArm64/JitAsm.h"
|
||||
|
||||
using namespace Arm64Gen;
|
||||
|
||||
void JitArm64::ps_abs(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == b);
|
||||
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FABS(64, VD, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_add(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FADD(64, VD, VA, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_div(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FDIV(64, VD, VA, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_madd(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, VD, V0, VB);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_madds0(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VC, 0);
|
||||
m_float_emit.FMUL(64, V0, V0, VA);
|
||||
m_float_emit.FADD(64, VD, V0, VB);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_madds1(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VC, 1);
|
||||
m_float_emit.FMUL(64, V0, V0, VA);
|
||||
m_float_emit.FADD(64, VD, V0, VB);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_merge00(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.TRN1(64, VD, VA, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_merge01(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.INS(64, VD, 0, VA, 0);
|
||||
m_float_emit.INS(64, VD, 1, VB, 1);
|
||||
}
|
||||
|
||||
void JitArm64::ps_merge10(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
if (d != a && d != b)
|
||||
{
|
||||
m_float_emit.INS(64, VD, 0, VA, 1);
|
||||
m_float_emit.INS(64, VD, 1, VB, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
m_float_emit.INS(64, V0, 0, VA, 1);
|
||||
m_float_emit.INS(64, V0, 1, VB, 0);
|
||||
m_float_emit.ORR(VD, V0, V0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::ps_merge11(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.TRN2(64, VD, VA, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_mr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 b = inst.FB, d = inst.FD;
|
||||
|
||||
if (d == b)
|
||||
return;
|
||||
|
||||
fpr.BindToRegister(d, false);
|
||||
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.ORR(VD, VB, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_mul(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FMUL(64, VD, VA, VC);
|
||||
}
|
||||
|
||||
void JitArm64::ps_muls0(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VC, 0);
|
||||
m_float_emit.FMUL(64, VD, VA, V0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_muls1(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VC, 1);
|
||||
m_float_emit.FMUL(64, VD, VA, V0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_msub(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, VD, V0, VB);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_nabs(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == b);
|
||||
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FABS(64, VD, VB);
|
||||
m_float_emit.FNEG(64, VD, VD);
|
||||
}
|
||||
|
||||
void JitArm64::ps_neg(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == b);
|
||||
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FNEG(64, VD, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_nmadd(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FADD(64, VD, V0, VB);
|
||||
m_float_emit.FNEG(64, VD, VD);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_nmsub(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.FMUL(64, V0, VA, VC);
|
||||
m_float_emit.FSUB(64, VD, V0, VB);
|
||||
m_float_emit.FNEG(64, VD, VD);
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_res(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == b);
|
||||
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FRSQRTE(64, VD, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_sel(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
if (d != a && d != b && d != c)
|
||||
{
|
||||
m_float_emit.FCMGE(64, VD, VA);
|
||||
m_float_emit.BSL(VD, VC, VB);
|
||||
}
|
||||
else
|
||||
{
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
m_float_emit.FCMGE(64, V0, VA);
|
||||
m_float_emit.BSL(V0, VC, VB);
|
||||
m_float_emit.ORR(VD, V0, V0);
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::ps_sub(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
|
||||
m_float_emit.FSUB(64, VD, VA, VB);
|
||||
}
|
||||
|
||||
void JitArm64::ps_sum0(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VB, 1);
|
||||
if (d != c)
|
||||
{
|
||||
m_float_emit.FADD(64, VD, V0, VA);
|
||||
m_float_emit.INS(64, VD, 1, VC, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_float_emit.FADD(64, V0, V0, VA);
|
||||
m_float_emit.INS(64, VD, 0, V0, 0);
|
||||
}
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
||||
|
||||
void JitArm64::ps_sum1(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITPairedOff);
|
||||
FALLBACK_IF(inst.Rc);
|
||||
|
||||
u32 a = inst.FA, b = inst.FB, c = inst.FC, d = inst.FD;
|
||||
fpr.BindToRegister(d, d == a || d == b || d == c);
|
||||
|
||||
ARM64Reg VA = fpr.R(a);
|
||||
ARM64Reg VB = fpr.R(b);
|
||||
ARM64Reg VC = fpr.R(c);
|
||||
ARM64Reg VD = fpr.R(d);
|
||||
ARM64Reg V0 = fpr.GetReg();
|
||||
|
||||
m_float_emit.DUP(64, V0, VA, 0);
|
||||
if (d != c)
|
||||
{
|
||||
m_float_emit.FADD(64, VD, V0, VB);
|
||||
m_float_emit.INS(64, VD, 0, VC, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_float_emit.FADD(64, V0, V0, VB);
|
||||
m_float_emit.INS(64, VD, 1, V0, 1);
|
||||
}
|
||||
|
||||
fpr.Unlock(V0);
|
||||
}
|
|
@ -10,6 +10,7 @@ using namespace Arm64Gen;
|
|||
void Arm64RegCache::Init(ARM64XEmitter *emitter)
|
||||
{
|
||||
m_emit = emitter;
|
||||
m_float_emit.reset(new ARM64FloatEmitter(m_emit));
|
||||
GetAllocationOrder();
|
||||
}
|
||||
|
||||
|
@ -56,6 +57,23 @@ void Arm64RegCache::UnlockRegister(ARM64Reg host_reg)
|
|||
reg->Unlock();
|
||||
}
|
||||
|
||||
void Arm64RegCache::FlushMostStaleRegister()
|
||||
{
|
||||
u32 most_stale_preg = 0;
|
||||
u32 most_stale_amount = 0;
|
||||
for (u32 i = 0; i < 32; ++i)
|
||||
{
|
||||
u32 last_used = m_guest_registers[i].GetLastUsed();
|
||||
if (last_used > most_stale_amount &&
|
||||
m_guest_registers[i].GetType() == REG_REG)
|
||||
{
|
||||
most_stale_preg = i;
|
||||
most_stale_amount = last_used;
|
||||
}
|
||||
}
|
||||
FlushRegister(most_stale_preg, false);
|
||||
}
|
||||
|
||||
// GPR Cache
|
||||
void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats)
|
||||
{
|
||||
|
@ -212,23 +230,6 @@ void Arm64GPRCache::GetAllocationOrder()
|
|||
m_host_registers.push_back(HostReg(reg));
|
||||
}
|
||||
|
||||
void Arm64GPRCache::FlushMostStaleRegister()
|
||||
{
|
||||
u32 most_stale_preg = 0;
|
||||
u32 most_stale_amount = 0;
|
||||
for (u32 i = 0; i < 32; ++i)
|
||||
{
|
||||
u32 last_used = m_guest_registers[i].GetLastUsed();
|
||||
if (last_used > most_stale_amount &&
|
||||
m_guest_registers[i].GetType() == REG_REG)
|
||||
{
|
||||
most_stale_preg = i;
|
||||
most_stale_amount = last_used;
|
||||
}
|
||||
}
|
||||
FlushRegister(most_stale_preg, false);
|
||||
}
|
||||
|
||||
BitSet32 Arm64GPRCache::GetCallerSavedUsed()
|
||||
{
|
||||
BitSet32 registers(0);
|
||||
|
@ -254,35 +255,120 @@ void Arm64GPRCache::FlushByHost(ARM64Reg host_reg)
|
|||
// FPR Cache
|
||||
void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op)
|
||||
{
|
||||
// XXX: Flush our stuff
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
bool flush = true;
|
||||
if (mode == FLUSH_INTERPRETER)
|
||||
{
|
||||
if (!(op->regsOut[i] || op->regsIn[i]))
|
||||
{
|
||||
// This interpreted instruction doesn't use this register
|
||||
flush = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_guest_registers[i].GetType() == REG_REG)
|
||||
{
|
||||
// Has to be flushed if it isn't in a callee saved register
|
||||
ARM64Reg host_reg = m_guest_registers[i].GetReg();
|
||||
if (flush || !IsCalleeSaved(host_reg))
|
||||
FlushRegister(i, mode == FLUSH_MAINTAIN_STATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ARM64Reg Arm64FPRCache::R(u32 preg)
|
||||
{
|
||||
// XXX: return a host reg holding a guest register
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
IncrementAllUsed();
|
||||
reg.ResetLastUsed();
|
||||
|
||||
switch (reg.GetType())
|
||||
{
|
||||
case REG_REG: // already in a reg
|
||||
return reg.GetReg();
|
||||
break;
|
||||
case REG_NOTLOADED: // Register isn't loaded at /all/
|
||||
{
|
||||
ARM64Reg host_reg = GetReg();
|
||||
reg.LoadToReg(host_reg);
|
||||
m_float_emit->LDR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
|
||||
return host_reg;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_dbg_assert_msg_(DYNA_REC, false, "Invalid OpArg Type!");
|
||||
break;
|
||||
}
|
||||
// We've got an issue if we end up here
|
||||
return INVALID_REG;
|
||||
}
|
||||
|
||||
void Arm64FPRCache::BindToRegister(u32 preg, bool do_load)
|
||||
{
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
|
||||
if (reg.GetType() == REG_NOTLOADED)
|
||||
{
|
||||
ARM64Reg host_reg = GetReg();
|
||||
reg.LoadToReg(host_reg);
|
||||
if (do_load)
|
||||
m_float_emit->LDR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64FPRCache::GetAllocationOrder()
|
||||
{
|
||||
const std::vector<ARM64Reg> allocation_order =
|
||||
{
|
||||
D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10,
|
||||
D11, D12, D13, D14, D15, D16, D17, D18, D19,
|
||||
D20, D21, D22, D23, D24, D25, D26, D27, D28,
|
||||
D29, D30, D31,
|
||||
// Callee saved
|
||||
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
|
||||
|
||||
// Caller saved
|
||||
Q16, Q17, Q18, Q19, Q20, Q21, Q22, Q23,
|
||||
Q24, Q25, Q26, Q27, Q28, Q29, Q30, Q31,
|
||||
Q7, Q6, Q5, Q4, Q3, Q2, Q1, Q0
|
||||
};
|
||||
|
||||
for (ARM64Reg reg : allocation_order)
|
||||
m_host_registers.push_back(HostReg(reg));
|
||||
}
|
||||
|
||||
void Arm64FPRCache::FlushMostStaleRegister()
|
||||
{
|
||||
// XXX: Flush a register
|
||||
}
|
||||
|
||||
void Arm64FPRCache::FlushByHost(ARM64Reg host_reg)
|
||||
{
|
||||
// XXX: Scan guest registers and flush if found
|
||||
}
|
||||
|
||||
bool Arm64FPRCache::IsCalleeSaved(ARM64Reg reg)
|
||||
{
|
||||
static std::vector<ARM64Reg> callee_regs =
|
||||
{
|
||||
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, INVALID_REG,
|
||||
};
|
||||
return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end();
|
||||
}
|
||||
|
||||
void Arm64FPRCache::FlushRegister(u32 preg, bool maintain_state)
|
||||
{
|
||||
OpArg& reg = m_guest_registers[preg];
|
||||
if (reg.GetType() == REG_REG)
|
||||
{
|
||||
ARM64Reg host_reg = reg.GetReg();
|
||||
|
||||
m_float_emit->STR(128, INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(ps[preg][0]));
|
||||
if (!maintain_state)
|
||||
{
|
||||
UnlockRegister(host_reg);
|
||||
reg.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BitSet32 Arm64FPRCache::GetCallerSavedUsed()
|
||||
{
|
||||
BitSet32 registers(0);
|
||||
for (auto& it : m_host_registers)
|
||||
if (it.IsLocked())
|
||||
registers[Q0 - it.GetReg()] = 1;
|
||||
return registers;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ private:
|
|||
class Arm64RegCache
|
||||
{
|
||||
public:
|
||||
Arm64RegCache() : m_emit(nullptr), m_reg_stats(nullptr) {};
|
||||
Arm64RegCache() : m_emit(nullptr), m_float_emit(nullptr), m_reg_stats(nullptr) {};
|
||||
virtual ~Arm64RegCache() {};
|
||||
|
||||
void Init(ARM64XEmitter *emitter);
|
||||
|
@ -133,10 +133,14 @@ public:
|
|||
// Will dump an immediate to the host register as well
|
||||
virtual ARM64Reg R(u32 reg) = 0;
|
||||
|
||||
virtual BitSet32 GetCallerSavedUsed() = 0;
|
||||
|
||||
// Returns a temporary register for use
|
||||
// Requires unlocking after done
|
||||
ARM64Reg GetReg();
|
||||
|
||||
void StoreRegister(u32 preg) { FlushRegister(preg, false); }
|
||||
|
||||
// Locks a register so a cache cannot use it
|
||||
// Useful for function calls
|
||||
template<typename T = ARM64Reg, typename... Args>
|
||||
|
@ -166,7 +170,7 @@ protected:
|
|||
virtual void GetAllocationOrder() = 0;
|
||||
|
||||
// Flushes the most stale register
|
||||
virtual void FlushMostStaleRegister() = 0;
|
||||
void FlushMostStaleRegister();
|
||||
|
||||
// Lock a register
|
||||
void LockRegister(ARM64Reg host_reg);
|
||||
|
@ -177,15 +181,31 @@ protected:
|
|||
// Flushes a guest register by host provided
|
||||
virtual void FlushByHost(ARM64Reg host_reg) = 0;
|
||||
|
||||
virtual void FlushRegister(u32 preg, bool maintain_state) = 0;
|
||||
|
||||
// Get available host registers
|
||||
u32 GetUnlockedRegisterCount();
|
||||
|
||||
void IncrementAllUsed()
|
||||
{
|
||||
for (auto& reg : m_guest_registers)
|
||||
reg.IncrementLastUsed();
|
||||
}
|
||||
|
||||
// Code emitter
|
||||
ARM64XEmitter *m_emit;
|
||||
|
||||
// Float emitter
|
||||
std::unique_ptr<ARM64FloatEmitter> m_float_emit;
|
||||
|
||||
// Host side registers that hold the host registers in order of use
|
||||
std::vector<HostReg> m_host_registers;
|
||||
|
||||
// Our guest GPRs
|
||||
// PowerPC has 32 GPRs
|
||||
// PowerPC also has 32 paired FPRs
|
||||
OpArg m_guest_registers[32];
|
||||
|
||||
// Register stats for the current block
|
||||
PPCAnalyst::BlockRegStats *m_reg_stats;
|
||||
};
|
||||
|
@ -215,34 +235,20 @@ public:
|
|||
|
||||
void BindToRegister(u32 preg, bool do_load);
|
||||
|
||||
void StoreRegister(u32 preg) { FlushRegister(preg, false); }
|
||||
|
||||
BitSet32 GetCallerSavedUsed();
|
||||
BitSet32 GetCallerSavedUsed() override;
|
||||
|
||||
protected:
|
||||
// Get the order of the host registers
|
||||
void GetAllocationOrder();
|
||||
|
||||
// Flushes the most stale register
|
||||
void FlushMostStaleRegister();
|
||||
|
||||
// Flushes a guest register by host provided
|
||||
void FlushByHost(ARM64Reg host_reg) override;
|
||||
|
||||
// Our guest GPRs
|
||||
// PowerPC has 32 GPRs
|
||||
OpArg m_guest_registers[32];
|
||||
void FlushRegister(u32 preg, bool maintain_state) override;
|
||||
|
||||
private:
|
||||
bool IsCalleeSaved(ARM64Reg reg);
|
||||
|
||||
void IncrementAllUsed()
|
||||
{
|
||||
for (auto& reg : m_guest_registers)
|
||||
reg.IncrementLastUsed();
|
||||
}
|
||||
|
||||
void FlushRegister(u32 preg, bool maintain_state);
|
||||
};
|
||||
|
||||
class Arm64FPRCache : public Arm64RegCache
|
||||
|
@ -256,17 +262,19 @@ public:
|
|||
// Will dump an immediate to the host register as well
|
||||
ARM64Reg R(u32 preg);
|
||||
|
||||
void BindToRegister(u32 preg, bool do_load);
|
||||
|
||||
BitSet32 GetCallerSavedUsed() override;
|
||||
|
||||
protected:
|
||||
// Get the order of the host registers
|
||||
void GetAllocationOrder();
|
||||
|
||||
// Flushes the most stale register
|
||||
void FlushMostStaleRegister();
|
||||
|
||||
// Flushes a guest register by host provided
|
||||
void FlushByHost(ARM64Reg host_reg) override;
|
||||
|
||||
// Our guest FPRs
|
||||
// Gekko has 32 paired registers(32x2)
|
||||
OpArg m_guest_registers[32][2];
|
||||
void FlushRegister(u32 preg, bool maintain_state) override;
|
||||
|
||||
private:
|
||||
bool IsCalleeSaved(ARM64Reg reg);
|
||||
};
|
||||
|
|
|
@ -196,3 +196,90 @@ void JitArm64::twx(UGeckoInstruction inst)
|
|||
WriteExit(js.compilerPC + 4);
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::mfspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITSystemRegistersOff);
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_XER:
|
||||
case SPR_WPAR:
|
||||
case SPR_DEC:
|
||||
case SPR_TL:
|
||||
case SPR_TU:
|
||||
FALLBACK_IF(true);
|
||||
default:
|
||||
gpr.BindToRegister(inst.RD, false);
|
||||
ARM64Reg RD = gpr.R(inst.RD);
|
||||
LDR(INDEX_UNSIGNED, RD, X29, PPCSTATE_OFF(spr) + iIndex * 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JitArm64::mftb(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITSystemRegistersOff);
|
||||
mfspr(inst);
|
||||
}
|
||||
|
||||
void JitArm64::mtspr(UGeckoInstruction inst)
|
||||
{
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITSystemRegistersOff);
|
||||
|
||||
u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F);
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
case SPR_DMAU:
|
||||
|
||||
case SPR_SPRG0:
|
||||
case SPR_SPRG1:
|
||||
case SPR_SPRG2:
|
||||
case SPR_SPRG3:
|
||||
|
||||
case SPR_SRR0:
|
||||
case SPR_SRR1:
|
||||
// These are safe to do the easy way, see the bottom of this function.
|
||||
break;
|
||||
|
||||
case SPR_LR:
|
||||
case SPR_CTR:
|
||||
case SPR_GQR0:
|
||||
case SPR_GQR0 + 1:
|
||||
case SPR_GQR0 + 2:
|
||||
case SPR_GQR0 + 3:
|
||||
case SPR_GQR0 + 4:
|
||||
case SPR_GQR0 + 5:
|
||||
case SPR_GQR0 + 6:
|
||||
case SPR_GQR0 + 7:
|
||||
// These are safe to do the easy way, see the bottom of this function.
|
||||
break;
|
||||
case SPR_XER:
|
||||
{
|
||||
FALLBACK_IF(true);
|
||||
ARM64Reg RD = gpr.R(inst.RD);
|
||||
ARM64Reg WA = gpr.GetReg();
|
||||
ARM64Reg mask = gpr.GetReg();
|
||||
MOVI2R(mask, 0xFF7F);
|
||||
AND(WA, RD, mask, ArithOption(mask, ST_LSL, 0));
|
||||
STRH(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_stringctrl));
|
||||
UBFM(WA, RD, XER_CA_SHIFT, XER_CA_SHIFT);
|
||||
STRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_ca));
|
||||
UBFM(WA, RD, XER_OV_SHIFT, 31); // Same as WA = RD >> XER_OV_SHIFT
|
||||
STRB(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(xer_so_ov));
|
||||
gpr.Unlock(WA, mask);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
FALLBACK_IF(true);
|
||||
}
|
||||
|
||||
// OK, this is easy.
|
||||
ARM64Reg RD = gpr.R(inst.RD);
|
||||
STR(INDEX_UNSIGNED, RD, X29, PPCSTATE_OFF(spr) + iIndex * 4);
|
||||
}
|
||||
|
|
|
@ -45,17 +45,17 @@ static GekkoOPTemplate primarytable[] =
|
|||
{3, &JitArm64::twx}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{17, &JitArm64::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}},
|
||||
|
||||
{7, &JitArm64::FallBackToInterpreter}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
|
||||
{7, &JitArm64::mulli}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}},
|
||||
{8, &JitArm64::FallBackToInterpreter}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{10, &JitArm64::cmpli}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{11, &JitArm64::cmpi}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}},
|
||||
{12, &JitArm64::FallBackToInterpreter}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{13, &JitArm64::FallBackToInterpreter}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
|
||||
{12, &JitArm64::addic}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}},
|
||||
{13, &JitArm64::addic}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}},
|
||||
{14, &JitArm64::arith_imm}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
{15, &JitArm64::arith_imm}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}},
|
||||
|
||||
{20, &JitArm64::FallBackToInterpreter}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}},
|
||||
{21, &JitArm64::FallBackToInterpreter}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{21, &JitArm64::rlwinmx}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{23, &JitArm64::FallBackToInterpreter}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}},
|
||||
|
||||
{24, &JitArm64::arith_imm}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}},
|
||||
|
@ -84,15 +84,15 @@ static GekkoOPTemplate primarytable[] =
|
|||
{46, &JitArm64::FallBackToInterpreter}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
{47, &JitArm64::FallBackToInterpreter}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
|
||||
|
||||
{48, &JitArm64::FallBackToInterpreter}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{49, &JitArm64::FallBackToInterpreter}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
{50, &JitArm64::FallBackToInterpreter}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{51, &JitArm64::FallBackToInterpreter}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
{48, &JitArm64::lfXX}, //"lfs", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{49, &JitArm64::lfXX}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
{50, &JitArm64::lfXX}, //"lfd", OPTYPE_LOADFP, FL_IN_A}},
|
||||
{51, &JitArm64::lfXX}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{52, &JitArm64::FallBackToInterpreter}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{53, &JitArm64::FallBackToInterpreter}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
{54, &JitArm64::FallBackToInterpreter}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{55, &JitArm64::FallBackToInterpreter}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
{52, &JitArm64::stfXX}, //"stfs", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{53, &JitArm64::stfXX}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
{54, &JitArm64::stfXX}, //"stfd", OPTYPE_STOREFP, FL_IN_A}},
|
||||
{55, &JitArm64::stfXX}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}},
|
||||
|
||||
{56, &JitArm64::FallBackToInterpreter}, //"psq_l", OPTYPE_PS, FL_IN_A}},
|
||||
{57, &JitArm64::FallBackToInterpreter}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}},
|
||||
|
@ -114,39 +114,39 @@ static GekkoOPTemplate table4[] =
|
|||
{ //SUBOP10
|
||||
{0, &JitArm64::FallBackToInterpreter}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{32, &JitArm64::FallBackToInterpreter}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}},
|
||||
{40, &JitArm64::FallBackToInterpreter}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
|
||||
{136, &JitArm64::FallBackToInterpreter}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{264, &JitArm64::FallBackToInterpreter}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{40, &JitArm64::ps_neg}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}},
|
||||
{136, &JitArm64::ps_nabs}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{264, &JitArm64::ps_abs}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}},
|
||||
{64, &JitArm64::FallBackToInterpreter}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{72, &JitArm64::FallBackToInterpreter}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
|
||||
{72, &JitArm64::ps_mr}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}},
|
||||
{96, &JitArm64::FallBackToInterpreter}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}},
|
||||
{528, &JitArm64::FallBackToInterpreter}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
|
||||
{560, &JitArm64::FallBackToInterpreter}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
|
||||
{592, &JitArm64::FallBackToInterpreter}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
|
||||
{624, &JitArm64::FallBackToInterpreter}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
|
||||
{528, &JitArm64::ps_merge00}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}},
|
||||
{560, &JitArm64::ps_merge01}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}},
|
||||
{592, &JitArm64::ps_merge10}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}},
|
||||
{624, &JitArm64::ps_merge11}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}},
|
||||
|
||||
{1014, &JitArm64::FallBackToInterpreter}, //"dcbz_l", OPTYPE_SYSTEM, 0}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table4_2[] =
|
||||
{
|
||||
{10, &JitArm64::FallBackToInterpreter}, //"ps_sum0", OPTYPE_PS, 0}},
|
||||
{11, &JitArm64::FallBackToInterpreter}, //"ps_sum1", OPTYPE_PS, 0}},
|
||||
{12, &JitArm64::FallBackToInterpreter}, //"ps_muls0", OPTYPE_PS, 0}},
|
||||
{13, &JitArm64::FallBackToInterpreter}, //"ps_muls1", OPTYPE_PS, 0}},
|
||||
{14, &JitArm64::FallBackToInterpreter}, //"ps_madds0", OPTYPE_PS, 0}},
|
||||
{15, &JitArm64::FallBackToInterpreter}, //"ps_madds1", OPTYPE_PS, 0}},
|
||||
{18, &JitArm64::FallBackToInterpreter}, //"ps_div", OPTYPE_PS, 0, 16}},
|
||||
{20, &JitArm64::FallBackToInterpreter}, //"ps_sub", OPTYPE_PS, 0}},
|
||||
{21, &JitArm64::FallBackToInterpreter}, //"ps_add", OPTYPE_PS, 0}},
|
||||
{23, &JitArm64::FallBackToInterpreter}, //"ps_sel", OPTYPE_PS, 0}},
|
||||
{24, &JitArm64::FallBackToInterpreter}, //"ps_res", OPTYPE_PS, 0}},
|
||||
{25, &JitArm64::FallBackToInterpreter}, //"ps_mul", OPTYPE_PS, 0}},
|
||||
{10, &JitArm64::ps_sum0}, //"ps_sum0", OPTYPE_PS, 0}},
|
||||
{11, &JitArm64::ps_sum1}, //"ps_sum1", OPTYPE_PS, 0}},
|
||||
{12, &JitArm64::ps_muls0}, //"ps_muls0", OPTYPE_PS, 0}},
|
||||
{13, &JitArm64::ps_muls1}, //"ps_muls1", OPTYPE_PS, 0}},
|
||||
{14, &JitArm64::ps_madds0}, //"ps_madds0", OPTYPE_PS, 0}},
|
||||
{15, &JitArm64::ps_madds1}, //"ps_madds1", OPTYPE_PS, 0}},
|
||||
{18, &JitArm64::ps_div}, //"ps_div", OPTYPE_PS, 0, 16}},
|
||||
{20, &JitArm64::ps_sub}, //"ps_sub", OPTYPE_PS, 0}},
|
||||
{21, &JitArm64::ps_add}, //"ps_add", OPTYPE_PS, 0}},
|
||||
{23, &JitArm64::ps_sel}, //"ps_sel", OPTYPE_PS, 0}},
|
||||
{24, &JitArm64::ps_res}, //"ps_res", OPTYPE_PS, 0}},
|
||||
{25, &JitArm64::ps_mul}, //"ps_mul", OPTYPE_PS, 0}},
|
||||
{26, &JitArm64::FallBackToInterpreter}, //"ps_rsqrte", OPTYPE_PS, 0, 1}},
|
||||
{28, &JitArm64::FallBackToInterpreter}, //"ps_msub", OPTYPE_PS, 0}},
|
||||
{29, &JitArm64::FallBackToInterpreter}, //"ps_madd", OPTYPE_PS, 0}},
|
||||
{30, &JitArm64::FallBackToInterpreter}, //"ps_nmsub", OPTYPE_PS, 0}},
|
||||
{31, &JitArm64::FallBackToInterpreter}, //"ps_nmadd", OPTYPE_PS, 0}},
|
||||
{28, &JitArm64::ps_msub}, //"ps_msub", OPTYPE_PS, 0}},
|
||||
{29, &JitArm64::ps_madd}, //"ps_madd", OPTYPE_PS, 0}},
|
||||
{30, &JitArm64::ps_nmsub}, //"ps_nmsub", OPTYPE_PS, 0}},
|
||||
{31, &JitArm64::ps_nmadd}, //"ps_nmadd", OPTYPE_PS, 0}},
|
||||
};
|
||||
|
||||
|
||||
|
@ -196,7 +196,7 @@ static GekkoOPTemplate table31[] =
|
|||
{954, &JitArm64::extsXx}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}},
|
||||
{536, &JitArm64::FallBackToInterpreter}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{792, &JitArm64::FallBackToInterpreter}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{824, &JitArm64::FallBackToInterpreter}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{824, &JitArm64::srawix}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
{24, &JitArm64::FallBackToInterpreter}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}},
|
||||
|
||||
{54, &JitArm64::FallBackToInterpreter}, //"dcbst", OPTYPE_DCACHE, 0, 4}},
|
||||
|
@ -208,24 +208,24 @@ static GekkoOPTemplate table31[] =
|
|||
{1014, &JitArm64::FallBackToInterpreter}, //"dcbz", OPTYPE_DCACHE, 0, 4}},
|
||||
|
||||
//load word
|
||||
{23, &JitArm64::FallBackToInterpreter}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{55, &JitArm64::FallBackToInterpreter}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{23, &JitArm64::lXX}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{55, &JitArm64::lXX}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword
|
||||
{279, &JitArm64::FallBackToInterpreter}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{311, &JitArm64::FallBackToInterpreter}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{279, &JitArm64::lXX}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{311, &JitArm64::lXX}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load halfword signextend
|
||||
{343, &JitArm64::FallBackToInterpreter}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{375, &JitArm64::FallBackToInterpreter}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{343, &JitArm64::lXX}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{375, &JitArm64::lXX}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte
|
||||
{87, &JitArm64::FallBackToInterpreter}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{119, &JitArm64::FallBackToInterpreter}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{87, &JitArm64::lXX}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{119, &JitArm64::lXX}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//load byte reverse
|
||||
{534, &JitArm64::FallBackToInterpreter}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{790, &JitArm64::FallBackToInterpreter}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{534, &JitArm64::lXX}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
{790, &JitArm64::lXX}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
// Conditional load/store (Wii SMP)
|
||||
{150, &JitArm64::FallBackToInterpreter}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
|
||||
|
@ -236,16 +236,16 @@ static GekkoOPTemplate table31[] =
|
|||
{597, &JitArm64::FallBackToInterpreter}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
|
||||
|
||||
//store word
|
||||
{151, &JitArm64::FallBackToInterpreter}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{183, &JitArm64::FallBackToInterpreter}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{151, &JitArm64::stX}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{183, &JitArm64::stX}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store halfword
|
||||
{407, &JitArm64::FallBackToInterpreter}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{439, &JitArm64::FallBackToInterpreter}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{407, &JitArm64::stX}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{439, &JitArm64::stX}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store byte
|
||||
{215, &JitArm64::FallBackToInterpreter}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{247, &JitArm64::FallBackToInterpreter}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
{215, &JitArm64::stX}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
{247, &JitArm64::stX}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
|
||||
|
||||
//store bytereverse
|
||||
{662, &JitArm64::FallBackToInterpreter}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
|
||||
|
@ -255,15 +255,15 @@ static GekkoOPTemplate table31[] =
|
|||
{725, &JitArm64::FallBackToInterpreter}, //"stswi", OPTYPE_STORE, FL_EVIL}},
|
||||
|
||||
// fp load/store
|
||||
{535, &JitArm64::FallBackToInterpreter}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{567, &JitArm64::FallBackToInterpreter}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
{599, &JitArm64::FallBackToInterpreter}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{631, &JitArm64::FallBackToInterpreter}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
{535, &JitArm64::lfXX}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{567, &JitArm64::lfXX}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
{599, &JitArm64::lfXX}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}},
|
||||
{631, &JitArm64::lfXX}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}},
|
||||
|
||||
{663, &JitArm64::FallBackToInterpreter}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{695, &JitArm64::FallBackToInterpreter}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{727, &JitArm64::FallBackToInterpreter}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{759, &JitArm64::FallBackToInterpreter}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{663, &JitArm64::stfXX}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{695, &JitArm64::stfXX}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{727, &JitArm64::stfXX}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
{759, &JitArm64::stfXX}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}},
|
||||
{983, &JitArm64::FallBackToInterpreter}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}},
|
||||
|
||||
{19, &JitArm64::FallBackToInterpreter}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
|
||||
|
@ -272,9 +272,9 @@ static GekkoOPTemplate table31[] =
|
|||
{146, &JitArm64::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}},
|
||||
{210, &JitArm64::mtsr}, //"mtsr", OPTYPE_SYSTEM, 0}},
|
||||
{242, &JitArm64::mtsrin}, //"mtsrin", OPTYPE_SYSTEM, 0}},
|
||||
{339, &JitArm64::FallBackToInterpreter}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
|
||||
{467, &JitArm64::FallBackToInterpreter}, //"mtspr", OPTYPE_SPR, 0, 2}},
|
||||
{371, &JitArm64::FallBackToInterpreter}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
|
||||
{339, &JitArm64::mfspr}, //"mfspr", OPTYPE_SPR, FL_OUT_D}},
|
||||
{467, &JitArm64::mtspr}, //"mtspr", OPTYPE_SPR, 0, 2}},
|
||||
{371, &JitArm64::mftb}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}},
|
||||
{512, &JitArm64::FallBackToInterpreter}, //"mcrxr", OPTYPE_SYSTEM, 0}},
|
||||
{595, &JitArm64::mfsr}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
{659, &JitArm64::mfsrin}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}},
|
||||
|
@ -294,25 +294,25 @@ static GekkoOPTemplate table31[] =
|
|||
|
||||
static GekkoOPTemplate table31_2[] =
|
||||
{
|
||||
{266, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{778, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{10, &JitArm64::FallBackToInterpreter}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{522, &JitArm64::FallBackToInterpreter}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{266, &JitArm64::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{778, &JitArm64::addx}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{10, &JitArm64::addcx}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{522, &JitArm64::addcx}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{138, &JitArm64::FallBackToInterpreter}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{650, &JitArm64::FallBackToInterpreter}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{234, &JitArm64::FallBackToInterpreter}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &JitArm64::FallBackToInterpreter}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{202, &JitArm64::addzex}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
{491, &JitArm64::FallBackToInterpreter}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{1003, &JitArm64::FallBackToInterpreter}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{459, &JitArm64::FallBackToInterpreter}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{971, &JitArm64::FallBackToInterpreter}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}},
|
||||
{75, &JitArm64::FallBackToInterpreter}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{11, &JitArm64::FallBackToInterpreter}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{235, &JitArm64::FallBackToInterpreter}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{747, &JitArm64::FallBackToInterpreter}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{235, &JitArm64::mullwx}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{747, &JitArm64::mullwx}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}},
|
||||
{104, &JitArm64::negx}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{40, &JitArm64::FallBackToInterpreter}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{552, &JitArm64::FallBackToInterpreter}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{40, &JitArm64::subfx}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{552, &JitArm64::subfx}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}},
|
||||
{8, &JitArm64::FallBackToInterpreter}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{520, &JitArm64::FallBackToInterpreter}, //"subfcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}},
|
||||
{136, &JitArm64::FallBackToInterpreter}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}},
|
||||
|
@ -323,27 +323,27 @@ static GekkoOPTemplate table31_2[] =
|
|||
static GekkoOPTemplate table59[] =
|
||||
{
|
||||
{18, &JitArm64::FallBackToInterpreter}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
|
||||
{20, &JitArm64::FallBackToInterpreter}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm64::FallBackToInterpreter}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{20, &JitArm64::fsubsx}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm64::faddsx}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
// {22, &JitArm64::FallBackToInterpreter}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{24, &JitArm64::FallBackToInterpreter}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm64::FallBackToInterpreter}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm64::FallBackToInterpreter}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm64::FallBackToInterpreter}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm64::FallBackToInterpreter}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm64::FallBackToInterpreter}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm64::fmulsx}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm64::fmsubsx}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm64::fmaddsx}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm64::fnmsubsx}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm64::fnmaddsx}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
static GekkoOPTemplate table63[] =
|
||||
{
|
||||
{264, &JitArm64::FallBackToInterpreter}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{264, &JitArm64::fabsx}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{32, &JitArm64::FallBackToInterpreter}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{0, &JitArm64::FallBackToInterpreter}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{14, &JitArm64::FallBackToInterpreter}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{15, &JitArm64::FallBackToInterpreter}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{72, &JitArm64::FallBackToInterpreter}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{136, &JitArm64::FallBackToInterpreter}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{40, &JitArm64::FallBackToInterpreter}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{72, &JitArm64::fmrx}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{136, &JitArm64::fnabsx}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{40, &JitArm64::fnegx}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{12, &JitArm64::FallBackToInterpreter}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
|
||||
{64, &JitArm64::FallBackToInterpreter}, //"mcrfs", OPTYPE_SYSTEMFP, 0}},
|
||||
|
@ -357,16 +357,16 @@ static GekkoOPTemplate table63[] =
|
|||
static GekkoOPTemplate table63_2[] =
|
||||
{
|
||||
{18, &JitArm64::FallBackToInterpreter}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}},
|
||||
{20, &JitArm64::FallBackToInterpreter}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm64::FallBackToInterpreter}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{20, &JitArm64::fsubx}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{21, &JitArm64::faddx}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{22, &JitArm64::FallBackToInterpreter}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{23, &JitArm64::FallBackToInterpreter}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm64::FallBackToInterpreter}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{23, &JitArm64::fselx}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{25, &JitArm64::fmulx}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{26, &JitArm64::FallBackToInterpreter}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm64::FallBackToInterpreter}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm64::FallBackToInterpreter}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm64::FallBackToInterpreter}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm64::FallBackToInterpreter}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{28, &JitArm64::fmsubx}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{29, &JitArm64::fmaddx}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{30, &JitArm64::fnmsubx}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
{31, &JitArm64::fnmaddx}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define QUANTIZED_REGS_TO_SAVE \
|
||||
(ABI_ALL_CALLER_SAVED & ~BitSet32 { \
|
||||
RSCRATCH, RSCRATCH2, RSCRATCH_EXTRA, XMM0+16, XMM1+16 \
|
||||
})
|
||||
})
|
||||
|
||||
#define QUANTIZED_REGS_TO_SAVE_LOAD (QUANTIZED_REGS_TO_SAVE | BitSet32 { RSCRATCH2 })
|
||||
|
||||
|
@ -196,9 +196,9 @@ const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = { 3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10
|
|||
|
||||
static const float GC_ALIGNED16(m_quantizeTableS[]) =
|
||||
{
|
||||
(1ULL << 0), (1ULL << 0), (1ULL << 1), (1ULL << 1), (1ULL << 2), (1ULL << 2), (1ULL << 3), (1ULL << 3),
|
||||
(1ULL << 4), (1ULL << 4), (1ULL << 5), (1ULL << 5), (1ULL << 6), (1ULL << 6), (1ULL << 7), (1ULL << 7),
|
||||
(1ULL << 8), (1ULL << 8), (1ULL << 9), (1ULL << 9), (1ULL << 10), (1ULL << 10), (1ULL << 11), (1ULL << 11),
|
||||
(1ULL << 0), (1ULL << 0), (1ULL << 1), (1ULL << 1), (1ULL << 2), (1ULL << 2), (1ULL << 3), (1ULL << 3),
|
||||
(1ULL << 4), (1ULL << 4), (1ULL << 5), (1ULL << 5), (1ULL << 6), (1ULL << 6), (1ULL << 7), (1ULL << 7),
|
||||
(1ULL << 8), (1ULL << 8), (1ULL << 9), (1ULL << 9), (1ULL << 10), (1ULL << 10), (1ULL << 11), (1ULL << 11),
|
||||
(1ULL << 12), (1ULL << 12), (1ULL << 13), (1ULL << 13), (1ULL << 14), (1ULL << 14), (1ULL << 15), (1ULL << 15),
|
||||
(1ULL << 16), (1ULL << 16), (1ULL << 17), (1ULL << 17), (1ULL << 18), (1ULL << 18), (1ULL << 19), (1ULL << 19),
|
||||
(1ULL << 20), (1ULL << 20), (1ULL << 21), (1ULL << 21), (1ULL << 22), (1ULL << 22), (1ULL << 23), (1ULL << 23),
|
||||
|
@ -215,20 +215,20 @@ static const float GC_ALIGNED16(m_quantizeTableS[]) =
|
|||
1.0 / (1ULL << 16), 1.0 / (1ULL << 16), 1.0 / (1ULL << 15), 1.0 / (1ULL << 15),
|
||||
1.0 / (1ULL << 14), 1.0 / (1ULL << 14), 1.0 / (1ULL << 13), 1.0 / (1ULL << 13),
|
||||
1.0 / (1ULL << 12), 1.0 / (1ULL << 12), 1.0 / (1ULL << 11), 1.0 / (1ULL << 11),
|
||||
1.0 / (1ULL << 10), 1.0 / (1ULL << 10), 1.0 / (1ULL << 9), 1.0 / (1ULL << 9),
|
||||
1.0 / (1ULL << 8), 1.0 / (1ULL << 8), 1.0 / (1ULL << 7), 1.0 / (1ULL << 7),
|
||||
1.0 / (1ULL << 6), 1.0 / (1ULL << 6), 1.0 / (1ULL << 5), 1.0 / (1ULL << 5),
|
||||
1.0 / (1ULL << 4), 1.0 / (1ULL << 4), 1.0 / (1ULL << 3), 1.0 / (1ULL << 3),
|
||||
1.0 / (1ULL << 2), 1.0 / (1ULL << 2), 1.0 / (1ULL << 1), 1.0 / (1ULL << 1),
|
||||
1.0 / (1ULL << 10), 1.0 / (1ULL << 10), 1.0 / (1ULL << 9), 1.0 / (1ULL << 9),
|
||||
1.0 / (1ULL << 8), 1.0 / (1ULL << 8), 1.0 / (1ULL << 7), 1.0 / (1ULL << 7),
|
||||
1.0 / (1ULL << 6), 1.0 / (1ULL << 6), 1.0 / (1ULL << 5), 1.0 / (1ULL << 5),
|
||||
1.0 / (1ULL << 4), 1.0 / (1ULL << 4), 1.0 / (1ULL << 3), 1.0 / (1ULL << 3),
|
||||
1.0 / (1ULL << 2), 1.0 / (1ULL << 2), 1.0 / (1ULL << 1), 1.0 / (1ULL << 1),
|
||||
};
|
||||
|
||||
static const float GC_ALIGNED16(m_dequantizeTableS[]) =
|
||||
{
|
||||
1.0 / (1ULL << 0), 1.0 / (1ULL << 0), 1.0 / (1ULL << 1), 1.0 / (1ULL << 1),
|
||||
1.0 / (1ULL << 2), 1.0 / (1ULL << 2), 1.0 / (1ULL << 3), 1.0 / (1ULL << 3),
|
||||
1.0 / (1ULL << 4), 1.0 / (1ULL << 4), 1.0 / (1ULL << 5), 1.0 / (1ULL << 5),
|
||||
1.0 / (1ULL << 6), 1.0 / (1ULL << 6), 1.0 / (1ULL << 7), 1.0 / (1ULL << 7),
|
||||
1.0 / (1ULL << 8), 1.0 / (1ULL << 8), 1.0 / (1ULL << 9), 1.0 / (1ULL << 9),
|
||||
1.0 / (1ULL << 0), 1.0 / (1ULL << 0), 1.0 / (1ULL << 1), 1.0 / (1ULL << 1),
|
||||
1.0 / (1ULL << 2), 1.0 / (1ULL << 2), 1.0 / (1ULL << 3), 1.0 / (1ULL << 3),
|
||||
1.0 / (1ULL << 4), 1.0 / (1ULL << 4), 1.0 / (1ULL << 5), 1.0 / (1ULL << 5),
|
||||
1.0 / (1ULL << 6), 1.0 / (1ULL << 6), 1.0 / (1ULL << 7), 1.0 / (1ULL << 7),
|
||||
1.0 / (1ULL << 8), 1.0 / (1ULL << 8), 1.0 / (1ULL << 9), 1.0 / (1ULL << 9),
|
||||
1.0 / (1ULL << 10), 1.0 / (1ULL << 10), 1.0 / (1ULL << 11), 1.0 / (1ULL << 11),
|
||||
1.0 / (1ULL << 12), 1.0 / (1ULL << 12), 1.0 / (1ULL << 13), 1.0 / (1ULL << 13),
|
||||
1.0 / (1ULL << 14), 1.0 / (1ULL << 14), 1.0 / (1ULL << 15), 1.0 / (1ULL << 15),
|
||||
|
@ -245,12 +245,12 @@ static const float GC_ALIGNED16(m_dequantizeTableS[]) =
|
|||
(1ULL << 24), (1ULL << 24), (1ULL << 23), (1ULL << 23), (1ULL << 22), (1ULL << 22), (1ULL << 21), (1ULL << 21),
|
||||
(1ULL << 20), (1ULL << 20), (1ULL << 19), (1ULL << 19), (1ULL << 18), (1ULL << 18), (1ULL << 17), (1ULL << 17),
|
||||
(1ULL << 16), (1ULL << 16), (1ULL << 15), (1ULL << 15), (1ULL << 14), (1ULL << 14), (1ULL << 13), (1ULL << 13),
|
||||
(1ULL << 12), (1ULL << 12), (1ULL << 11), (1ULL << 11), (1ULL << 10), (1ULL << 10), (1ULL << 9), (1ULL << 9),
|
||||
(1ULL << 8), (1ULL << 8), (1ULL << 7), (1ULL << 7), (1ULL << 6), (1ULL << 6), (1ULL << 5), (1ULL << 5),
|
||||
(1ULL << 4), (1ULL << 4), (1ULL << 3), (1ULL << 3), (1ULL << 2), (1ULL << 2), (1ULL << 1), (1ULL << 1),
|
||||
(1ULL << 12), (1ULL << 12), (1ULL << 11), (1ULL << 11), (1ULL << 10), (1ULL << 10), (1ULL << 9), (1ULL << 9),
|
||||
(1ULL << 8), (1ULL << 8), (1ULL << 7), (1ULL << 7), (1ULL << 6), (1ULL << 6), (1ULL << 5), (1ULL << 5),
|
||||
(1ULL << 4), (1ULL << 4), (1ULL << 3), (1ULL << 3), (1ULL << 2), (1ULL << 2), (1ULL << 1), (1ULL << 1),
|
||||
};
|
||||
|
||||
static const float GC_ALIGNED16(m_65535[4]) = { 65535.0f, 65535.0f, 65535.0f, 65535.0f };
|
||||
static const float GC_ALIGNED16(m_65535[4]) = {65535.0f, 65535.0f, 65535.0f, 65535.0f};
|
||||
static const float GC_ALIGNED16(m_32767) = 32767.0f;
|
||||
static const float GC_ALIGNED16(m_m32768) = -32768.0f;
|
||||
static const float GC_ALIGNED16(m_255) = 255.0f;
|
||||
|
|
|
@ -65,9 +65,7 @@ protected:
|
|||
struct JitState
|
||||
{
|
||||
u32 compilerPC;
|
||||
u32 next_compilerPC;
|
||||
u32 blockStart;
|
||||
UGeckoInstruction next_inst; // for easy peephole opt.
|
||||
int instructionNumber;
|
||||
int instructionsLeft;
|
||||
int downcountAmount;
|
||||
|
@ -88,10 +86,9 @@ protected:
|
|||
bool firstFPInstructionFound;
|
||||
bool isLastInstruction;
|
||||
bool memcheck;
|
||||
bool skipnext;
|
||||
int skipInstructions;
|
||||
bool carryFlagSet;
|
||||
bool carryFlagInverted;
|
||||
bool next_inst_bp;
|
||||
|
||||
int fifoBytesThisBlock;
|
||||
|
||||
|
@ -99,7 +96,6 @@ protected:
|
|||
PPCAnalyst::BlockRegStats gpa;
|
||||
PPCAnalyst::BlockRegStats fpa;
|
||||
PPCAnalyst::CodeOp* op;
|
||||
PPCAnalyst::CodeOp* next_op;
|
||||
u8* rewriteStart;
|
||||
|
||||
JitBlock *curBlock;
|
||||
|
|
|
@ -23,315 +23,315 @@
|
|||
|
||||
using namespace Gen;
|
||||
|
||||
bool JitBaseBlockCache::IsFull() const
|
||||
{
|
||||
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::Init()
|
||||
{
|
||||
if (m_initialized)
|
||||
bool JitBaseBlockCache::IsFull() const
|
||||
{
|
||||
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
|
||||
return;
|
||||
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
|
||||
}
|
||||
|
||||
JitRegister::Init();
|
||||
void JitBaseBlockCache::Init()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
iCache.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
iCacheEx.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
iCacheVMEM.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
Clear();
|
||||
JitRegister::Init();
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
iCache.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
iCacheEx.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
iCacheVMEM.fill(JIT_ICACHE_INVALID_BYTE);
|
||||
Clear();
|
||||
|
||||
void JitBaseBlockCache::Shutdown()
|
||||
{
|
||||
num_blocks = 0;
|
||||
m_initialized = false;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
JitRegister::Shutdown();
|
||||
}
|
||||
void JitBaseBlockCache::Shutdown()
|
||||
{
|
||||
num_blocks = 0;
|
||||
m_initialized = false;
|
||||
|
||||
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
|
||||
// is full and when saving and loading states.
|
||||
void JitBaseBlockCache::Clear()
|
||||
{
|
||||
JitRegister::Shutdown();
|
||||
}
|
||||
|
||||
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
|
||||
// is full and when saving and loading states.
|
||||
void JitBaseBlockCache::Clear()
|
||||
{
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (IsFull())
|
||||
Core::DisplayMessage("Clearing block cache.", 3000);
|
||||
else
|
||||
Core::DisplayMessage("Clearing code cache.", 3000);
|
||||
#endif
|
||||
jit->js.fifoWriteAddresses.clear();
|
||||
jit->js.pairedQuantizeAddresses.clear();
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
DestroyBlock(i, false);
|
||||
}
|
||||
links_to.clear();
|
||||
block_map.clear();
|
||||
|
||||
valid_block.ClearAll();
|
||||
|
||||
num_blocks = 0;
|
||||
blockCodePointers.fill(nullptr);
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::Reset()
|
||||
{
|
||||
Shutdown();
|
||||
Init();
|
||||
}
|
||||
|
||||
JitBlock *JitBaseBlockCache::GetBlock(int no)
|
||||
{
|
||||
return &blocks[no];
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::GetNumBlocks() const
|
||||
{
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
{
|
||||
// check if any endpoint is inside the other range
|
||||
if ((s1 >= s2 && s1 <= e2) ||
|
||||
(e1 >= s2 && e1 <= e2) ||
|
||||
(s2 >= s1 && s2 <= e1) ||
|
||||
(e2 >= s1 && e2 <= e1))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::AllocateBlock(u32 em_address)
|
||||
{
|
||||
JitBlock &b = blocks[num_blocks];
|
||||
b.invalid = false;
|
||||
b.originalAddress = em_address;
|
||||
b.linkData.clear();
|
||||
num_blocks++; //commit the current block
|
||||
return num_blocks - 1;
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
{
|
||||
blockCodePointers[block_num] = code_ptr;
|
||||
JitBlock &b = blocks[block_num];
|
||||
u32* icp = GetICachePtr(b.originalAddress);
|
||||
*icp = block_num;
|
||||
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
|
||||
|
||||
for (u32 block = pAddr / 32; block <= (pAddr + (b.originalSize - 1) * 4) / 32; ++block)
|
||||
valid_block.Set(block);
|
||||
|
||||
block_map[std::make_pair(pAddr + 4 * b.originalSize - 1, pAddr)] = block_num;
|
||||
|
||||
if (block_link)
|
||||
{
|
||||
for (const auto& e : b.linkData)
|
||||
{
|
||||
links_to.insert(std::pair<u32, int>(e.exitAddress, block_num));
|
||||
}
|
||||
|
||||
LinkBlock(block_num);
|
||||
LinkBlockExits(block_num);
|
||||
}
|
||||
|
||||
JitRegister::Register(blockCodePointers[block_num], b.codeSize,
|
||||
"JIT_PPC", b.originalAddress);
|
||||
}
|
||||
|
||||
const u8 **JitBaseBlockCache::GetCodePointers()
|
||||
{
|
||||
return blockCodePointers.data();
|
||||
}
|
||||
|
||||
u32* JitBaseBlockCache::GetICachePtr(u32 addr)
|
||||
{
|
||||
if (addr & JIT_ICACHE_VMEM_BIT)
|
||||
return (u32*)(&jit->GetBlockCache()->iCacheVMEM[addr & JIT_ICACHE_MASK]);
|
||||
else if (addr & JIT_ICACHE_EXRAM_BIT)
|
||||
return (u32*)(&jit->GetBlockCache()->iCacheEx[addr & JIT_ICACHEEX_MASK]);
|
||||
else
|
||||
return (u32*)(&jit->GetBlockCache()->iCache[addr & JIT_ICACHE_MASK]);
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
{
|
||||
u32 inst = *GetICachePtr(addr);
|
||||
if (inst & 0xfc000000) // definitely not a JIT block
|
||||
return -1;
|
||||
|
||||
if ((int)inst >= num_blocks)
|
||||
return -1;
|
||||
|
||||
if (blocks[inst].originalAddress != addr)
|
||||
return -1;
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
{
|
||||
return (CompiledCode)blockCodePointers[block_num];
|
||||
}
|
||||
|
||||
//Block linker
|
||||
//Make sure to have as many blocks as possible compiled before calling this
|
||||
//It's O(N), so it's fast :)
|
||||
//Can be faster by doing a queue for blocks to link up, and only process those
|
||||
//Should probably be done
|
||||
|
||||
void JitBaseBlockCache::LinkBlockExits(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
if (b.invalid)
|
||||
{
|
||||
// This block is dead. Don't relink it.
|
||||
return;
|
||||
}
|
||||
for (auto& e : b.linkData)
|
||||
{
|
||||
if (!e.linkStatus)
|
||||
{
|
||||
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress);
|
||||
if (destinationBlock != -1)
|
||||
{
|
||||
WriteLinkBlock(e.exitPtrs, blocks[destinationBlock].checkedEntry);
|
||||
e.linkStatus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::LinkBlock(int i)
|
||||
{
|
||||
LinkBlockExits(i);
|
||||
JitBlock &b = blocks[i];
|
||||
// equal_range(b) returns pair<iterator,iterator> representing the range
|
||||
// of element with key b
|
||||
auto ppp = links_to.equal_range(b.originalAddress);
|
||||
|
||||
if (ppp.first == ppp.second)
|
||||
return;
|
||||
|
||||
for (auto iter = ppp.first; iter != ppp.second; ++iter)
|
||||
{
|
||||
// PanicAlert("Linking block %i to block %i", iter->second, i);
|
||||
LinkBlockExits(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::UnlinkBlock(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
auto ppp = links_to.equal_range(b.originalAddress);
|
||||
|
||||
if (ppp.first == ppp.second)
|
||||
return;
|
||||
|
||||
for (auto iter = ppp.first; iter != ppp.second; ++iter)
|
||||
{
|
||||
JitBlock &sourceBlock = blocks[iter->second];
|
||||
for (auto& e : sourceBlock.linkData)
|
||||
{
|
||||
if (e.exitAddress == b.originalAddress)
|
||||
e.linkStatus = false;
|
||||
}
|
||||
}
|
||||
links_to.erase(b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
{
|
||||
if (block_num < 0 || block_num >= num_blocks)
|
||||
{
|
||||
PanicAlert("DestroyBlock: Invalid block number %d", block_num);
|
||||
return;
|
||||
}
|
||||
JitBlock &b = blocks[block_num];
|
||||
if (b.invalid)
|
||||
{
|
||||
if (invalidate)
|
||||
PanicAlert("Invalidating invalid block %d", block_num);
|
||||
return;
|
||||
}
|
||||
b.invalid = true;
|
||||
*GetICachePtr(b.originalAddress) = JIT_ICACHE_INVALID_WORD;
|
||||
|
||||
UnlinkBlock(block_num);
|
||||
|
||||
// Send anyone who tries to run this block back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool forced)
|
||||
{
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = address & 0x1FFFFFFF;
|
||||
|
||||
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
|
||||
bool destroy_block = true;
|
||||
if (length == 32)
|
||||
{
|
||||
if (!valid_block.Test(pAddr / 32))
|
||||
destroy_block = false;
|
||||
if (IsFull())
|
||||
Core::DisplayMessage("Clearing block cache.", 3000);
|
||||
else
|
||||
valid_block.Clear(pAddr / 32);
|
||||
Core::DisplayMessage("Clearing code cache.", 3000);
|
||||
#endif
|
||||
jit->js.fifoWriteAddresses.clear();
|
||||
jit->js.pairedQuantizeAddresses.clear();
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
DestroyBlock(i, false);
|
||||
}
|
||||
links_to.clear();
|
||||
block_map.clear();
|
||||
|
||||
valid_block.ClearAll();
|
||||
|
||||
num_blocks = 0;
|
||||
blockCodePointers.fill(nullptr);
|
||||
}
|
||||
|
||||
// destroy JIT blocks
|
||||
// !! this works correctly under assumption that any two overlapping blocks end at the same address
|
||||
if (destroy_block)
|
||||
void JitBaseBlockCache::Reset()
|
||||
{
|
||||
std::map<std::pair<u32, u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
|
||||
while (it2 != block_map.end() && it2->first.second < pAddr + length)
|
||||
Shutdown();
|
||||
Init();
|
||||
}
|
||||
|
||||
JitBlock *JitBaseBlockCache::GetBlock(int no)
|
||||
{
|
||||
return &blocks[no];
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::GetNumBlocks() const
|
||||
{
|
||||
return num_blocks;
|
||||
}
|
||||
|
||||
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
|
||||
{
|
||||
// check if any endpoint is inside the other range
|
||||
if ((s1 >= s2 && s1 <= e2) ||
|
||||
(e1 >= s2 && e1 <= e2) ||
|
||||
(s2 >= s1 && s2 <= e1) ||
|
||||
(e2 >= s1 && e2 <= e1))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::AllocateBlock(u32 em_address)
|
||||
{
|
||||
JitBlock &b = blocks[num_blocks];
|
||||
b.invalid = false;
|
||||
b.originalAddress = em_address;
|
||||
b.linkData.clear();
|
||||
num_blocks++; //commit the current block
|
||||
return num_blocks - 1;
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
|
||||
{
|
||||
blockCodePointers[block_num] = code_ptr;
|
||||
JitBlock &b = blocks[block_num];
|
||||
u32* icp = GetICachePtr(b.originalAddress);
|
||||
*icp = block_num;
|
||||
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
|
||||
|
||||
for (u32 block = pAddr / 32; block <= (pAddr + (b.originalSize - 1) * 4) / 32; ++block)
|
||||
valid_block.Set(block);
|
||||
|
||||
block_map[std::make_pair(pAddr + 4 * b.originalSize - 1, pAddr)] = block_num;
|
||||
|
||||
if (block_link)
|
||||
{
|
||||
JitBlock &b = blocks[it2->second];
|
||||
*GetICachePtr(b.originalAddress) = JIT_ICACHE_INVALID_WORD;
|
||||
DestroyBlock(it2->second, true);
|
||||
++it2;
|
||||
}
|
||||
if (it1 != it2)
|
||||
{
|
||||
block_map.erase(it1, it2);
|
||||
for (const auto& e : b.linkData)
|
||||
{
|
||||
links_to.insert(std::pair<u32, int>(e.exitAddress, block_num));
|
||||
}
|
||||
|
||||
LinkBlock(block_num);
|
||||
LinkBlockExits(block_num);
|
||||
}
|
||||
|
||||
// If the code was actually modified, we need to clear the relevant entries from the
|
||||
// FIFO write address cache, so we don't end up with FIFO checks in places they shouldn't
|
||||
// be (this can clobber flags, and thus break any optimization that relies on flags
|
||||
// being in the right place between instructions).
|
||||
if (!forced)
|
||||
JitRegister::Register(blockCodePointers[block_num], b.codeSize,
|
||||
"JIT_PPC", b.originalAddress);
|
||||
}
|
||||
|
||||
const u8 **JitBaseBlockCache::GetCodePointers()
|
||||
{
|
||||
return blockCodePointers.data();
|
||||
}
|
||||
|
||||
u32* JitBaseBlockCache::GetICachePtr(u32 addr)
|
||||
{
|
||||
if (addr & JIT_ICACHE_VMEM_BIT)
|
||||
return (u32*)(&jit->GetBlockCache()->iCacheVMEM[addr & JIT_ICACHE_MASK]);
|
||||
else if (addr & JIT_ICACHE_EXRAM_BIT)
|
||||
return (u32*)(&jit->GetBlockCache()->iCacheEx[addr & JIT_ICACHEEX_MASK]);
|
||||
else
|
||||
return (u32*)(&jit->GetBlockCache()->iCache[addr & JIT_ICACHE_MASK]);
|
||||
}
|
||||
|
||||
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
|
||||
{
|
||||
u32 inst = *GetICachePtr(addr);
|
||||
if (inst & 0xfc000000) // definitely not a JIT block
|
||||
return -1;
|
||||
|
||||
if ((int)inst >= num_blocks)
|
||||
return -1;
|
||||
|
||||
if (blocks[inst].originalAddress != addr)
|
||||
return -1;
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
|
||||
{
|
||||
return (CompiledCode)blockCodePointers[block_num];
|
||||
}
|
||||
|
||||
//Block linker
|
||||
//Make sure to have as many blocks as possible compiled before calling this
|
||||
//It's O(N), so it's fast :)
|
||||
//Can be faster by doing a queue for blocks to link up, and only process those
|
||||
//Should probably be done
|
||||
|
||||
void JitBaseBlockCache::LinkBlockExits(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
if (b.invalid)
|
||||
{
|
||||
for (u32 i = address; i < address + length; i += 4)
|
||||
// This block is dead. Don't relink it.
|
||||
return;
|
||||
}
|
||||
for (auto& e : b.linkData)
|
||||
{
|
||||
if (!e.linkStatus)
|
||||
{
|
||||
jit->js.fifoWriteAddresses.erase(i);
|
||||
jit->js.pairedQuantizeAddresses.erase(i);
|
||||
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress);
|
||||
if (destinationBlock != -1)
|
||||
{
|
||||
WriteLinkBlock(e.exitPtrs, blocks[destinationBlock].checkedEntry);
|
||||
e.linkStatus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
XEmitter emit(location);
|
||||
if (*location == 0xE8)
|
||||
emit.CALL(address);
|
||||
else
|
||||
emit.JMP(address, true);
|
||||
}
|
||||
void JitBaseBlockCache::LinkBlock(int i)
|
||||
{
|
||||
LinkBlockExits(i);
|
||||
JitBlock &b = blocks[i];
|
||||
// equal_range(b) returns pair<iterator,iterator> representing the range
|
||||
// of element with key b
|
||||
auto ppp = links_to.equal_range(b.originalAddress);
|
||||
|
||||
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
XEmitter emit((u8 *)location);
|
||||
emit.MOV(32, PPCSTATE(pc), Imm32(address));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
}
|
||||
if (ppp.first == ppp.second)
|
||||
return;
|
||||
|
||||
for (auto iter = ppp.first; iter != ppp.second; ++iter)
|
||||
{
|
||||
// PanicAlert("Linking block %i to block %i", iter->second, i);
|
||||
LinkBlockExits(iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::UnlinkBlock(int i)
|
||||
{
|
||||
JitBlock &b = blocks[i];
|
||||
auto ppp = links_to.equal_range(b.originalAddress);
|
||||
|
||||
if (ppp.first == ppp.second)
|
||||
return;
|
||||
|
||||
for (auto iter = ppp.first; iter != ppp.second; ++iter)
|
||||
{
|
||||
JitBlock &sourceBlock = blocks[iter->second];
|
||||
for (auto& e : sourceBlock.linkData)
|
||||
{
|
||||
if (e.exitAddress == b.originalAddress)
|
||||
e.linkStatus = false;
|
||||
}
|
||||
}
|
||||
links_to.erase(b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
|
||||
{
|
||||
if (block_num < 0 || block_num >= num_blocks)
|
||||
{
|
||||
PanicAlert("DestroyBlock: Invalid block number %d", block_num);
|
||||
return;
|
||||
}
|
||||
JitBlock &b = blocks[block_num];
|
||||
if (b.invalid)
|
||||
{
|
||||
if (invalidate)
|
||||
PanicAlert("Invalidating invalid block %d", block_num);
|
||||
return;
|
||||
}
|
||||
b.invalid = true;
|
||||
*GetICachePtr(b.originalAddress) = JIT_ICACHE_INVALID_WORD;
|
||||
|
||||
UnlinkBlock(block_num);
|
||||
|
||||
// Send anyone who tries to run this block back to the dispatcher.
|
||||
// Not entirely ideal, but .. pretty good.
|
||||
// Spurious entrances from previously linked blocks can only come through checkedEntry
|
||||
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
|
||||
}
|
||||
|
||||
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool forced)
|
||||
{
|
||||
// Convert the logical address to a physical address for the block map
|
||||
u32 pAddr = address & 0x1FFFFFFF;
|
||||
|
||||
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
|
||||
bool destroy_block = true;
|
||||
if (length == 32)
|
||||
{
|
||||
if (!valid_block.Test(pAddr / 32))
|
||||
destroy_block = false;
|
||||
else
|
||||
valid_block.Clear(pAddr / 32);
|
||||
}
|
||||
|
||||
// destroy JIT blocks
|
||||
// !! this works correctly under assumption that any two overlapping blocks end at the same address
|
||||
if (destroy_block)
|
||||
{
|
||||
std::map<std::pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
|
||||
while (it2 != block_map.end() && it2->first.second < pAddr + length)
|
||||
{
|
||||
JitBlock &b = blocks[it2->second];
|
||||
*GetICachePtr(b.originalAddress) = JIT_ICACHE_INVALID_WORD;
|
||||
DestroyBlock(it2->second, true);
|
||||
++it2;
|
||||
}
|
||||
if (it1 != it2)
|
||||
{
|
||||
block_map.erase(it1, it2);
|
||||
}
|
||||
|
||||
// If the code was actually modified, we need to clear the relevant entries from the
|
||||
// FIFO write address cache, so we don't end up with FIFO checks in places they shouldn't
|
||||
// be (this can clobber flags, and thus break any optimization that relies on flags
|
||||
// being in the right place between instructions).
|
||||
if (!forced)
|
||||
{
|
||||
for (u32 i = address; i < address + length; i += 4)
|
||||
{
|
||||
jit->js.fifoWriteAddresses.erase(i);
|
||||
jit->js.pairedQuantizeAddresses.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
|
||||
{
|
||||
XEmitter emit(location);
|
||||
if (*location == 0xE8)
|
||||
emit.CALL(address);
|
||||
else
|
||||
emit.JMP(address, true);
|
||||
}
|
||||
|
||||
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
|
||||
{
|
||||
XEmitter emit((u8 *)location);
|
||||
emit.MOV(32, PPCSTATE(pc), Imm32(address));
|
||||
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
|
||||
}
|
||||
|
|
|
@ -41,233 +41,233 @@ bool bMMU = false;
|
|||
|
||||
namespace JitInterface
|
||||
{
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
if (jit && p.GetMode() == PointerWrap::MODE_READ)
|
||||
jit->GetBlockCache()->Clear();
|
||||
}
|
||||
CPUCoreBase *InitJitCore(int core)
|
||||
{
|
||||
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
|
||||
bFakeVMEM = !bMMU;
|
||||
void DoState(PointerWrap &p)
|
||||
{
|
||||
if (jit && p.GetMode() == PointerWrap::MODE_READ)
|
||||
jit->GetBlockCache()->Clear();
|
||||
}
|
||||
CPUCoreBase *InitJitCore(int core)
|
||||
{
|
||||
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
|
||||
bFakeVMEM = !bMMU;
|
||||
|
||||
CPUCoreBase *ptr = nullptr;
|
||||
switch (core)
|
||||
{
|
||||
#if _M_X86
|
||||
case 1:
|
||||
{
|
||||
ptr = new Jit64();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
ptr = new JitIL();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_32
|
||||
case 3:
|
||||
{
|
||||
ptr = new JitArm();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_64
|
||||
case 4:
|
||||
{
|
||||
ptr = new JitArm64();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
jit = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
jit = static_cast<JitBase*>(ptr);
|
||||
jit->Init();
|
||||
return ptr;
|
||||
}
|
||||
void InitTables(int core)
|
||||
{
|
||||
switch (core)
|
||||
{
|
||||
#if _M_X86
|
||||
case 1:
|
||||
{
|
||||
Jit64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
JitILTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_32
|
||||
case 3:
|
||||
{
|
||||
JitArmTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_64
|
||||
case 4:
|
||||
{
|
||||
JitArm64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
CPUCoreBase *GetCore()
|
||||
{
|
||||
return jit;
|
||||
}
|
||||
|
||||
void WriteProfileResults(const std::string& filename)
|
||||
{
|
||||
// Can't really do this with no jit core available
|
||||
if (!jit)
|
||||
return;
|
||||
|
||||
std::vector<BlockStat> stats;
|
||||
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
|
||||
u64 cost_sum = 0;
|
||||
u64 timecost_sum = 0;
|
||||
u64 countsPerSec;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
|
||||
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
|
||||
// Rough heuristic. Mem instructions should cost more.
|
||||
u64 cost = block->originalSize * (block->runCount / 4);
|
||||
u64 timecost = block->ticCounter;
|
||||
// Todo: tweak.
|
||||
if (block->runCount >= 1)
|
||||
stats.push_back(BlockStat(i, cost));
|
||||
cost_sum += cost;
|
||||
timecost_sum += timecost;
|
||||
}
|
||||
|
||||
sort(stats.begin(), stats.end());
|
||||
File::IOFile f(filename, "w");
|
||||
if (!f)
|
||||
{
|
||||
PanicAlert("Failed to open %s", filename.c_str());
|
||||
return;
|
||||
}
|
||||
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
|
||||
for (auto& stat : stats)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(stat.blockNum);
|
||||
if (block)
|
||||
CPUCoreBase *ptr = nullptr;
|
||||
switch (core)
|
||||
{
|
||||
std::string name = g_symbolDB.GetDescription(block->originalAddress);
|
||||
double percent = 100.0 * (double)stat.cost / (double)cost_sum;
|
||||
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%" PRIu64 "\t%" PRIu64 "\t%.2f\t%.2f\t%.2f\t%i\n",
|
||||
block->originalAddress, name.c_str(), stat.cost,
|
||||
block->ticCounter, percent, timePercent,
|
||||
(double)block->ticCounter*1000.0 / (double)countsPerSec, block->codeSize);
|
||||
#if _M_X86
|
||||
case 1:
|
||||
{
|
||||
ptr = new Jit64();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
ptr = new JitIL();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_32
|
||||
case 3:
|
||||
{
|
||||
ptr = new JitArm();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_64
|
||||
case 4:
|
||||
{
|
||||
ptr = new JitArm64();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
jit = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
jit = static_cast<JitBase*>(ptr);
|
||||
jit->Init();
|
||||
return ptr;
|
||||
}
|
||||
void InitTables(int core)
|
||||
{
|
||||
switch (core)
|
||||
{
|
||||
#if _M_X86
|
||||
case 1:
|
||||
{
|
||||
Jit64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
JitILTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_32
|
||||
case 3:
|
||||
{
|
||||
JitArmTables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if _M_ARM_64
|
||||
case 4:
|
||||
{
|
||||
JitArm64Tables::InitTables();
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
PanicAlert("Unrecognizable cpu_core: %d", core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bool HandleFault(uintptr_t access_address, SContext* ctx)
|
||||
{
|
||||
return jit->HandleFault(access_address, ctx);
|
||||
}
|
||||
|
||||
void ClearCache()
|
||||
{
|
||||
if (jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
void ClearSafe()
|
||||
{
|
||||
// This clear is "safe" in the sense that it's okay to run from
|
||||
// inside a JIT'ed block: it clears the instruction cache, but not
|
||||
// the JIT'ed code.
|
||||
// TODO: There's probably a better way to handle this situation.
|
||||
if (jit)
|
||||
jit->GetBlockCache()->Clear();
|
||||
}
|
||||
|
||||
void InvalidateICache(u32 address, u32 size, bool forced)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(address, size, forced);
|
||||
}
|
||||
|
||||
u32 ReadOpcodeJIT(u32 _Address)
|
||||
{
|
||||
if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1))
|
||||
CPUCoreBase *GetCore()
|
||||
{
|
||||
_Address = Memory::TranslateAddress<Memory::FLAG_OPCODE>(_Address);
|
||||
if (_Address == 0)
|
||||
return jit;
|
||||
}
|
||||
|
||||
void WriteProfileResults(const std::string& filename)
|
||||
{
|
||||
// Can't really do this with no jit core available
|
||||
if (!jit)
|
||||
return;
|
||||
|
||||
std::vector<BlockStat> stats;
|
||||
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
|
||||
u64 cost_sum = 0;
|
||||
u64 timecost_sum = 0;
|
||||
u64 countsPerSec;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
|
||||
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
|
||||
{
|
||||
return 0;
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
|
||||
// Rough heuristic. Mem instructions should cost more.
|
||||
u64 cost = block->originalSize * (block->runCount / 4);
|
||||
u64 timecost = block->ticCounter;
|
||||
// Todo: tweak.
|
||||
if (block->runCount >= 1)
|
||||
stats.push_back(BlockStat(i, cost));
|
||||
cost_sum += cost;
|
||||
timecost_sum += timecost;
|
||||
}
|
||||
|
||||
sort(stats.begin(), stats.end());
|
||||
File::IOFile f(filename, "w");
|
||||
if (!f)
|
||||
{
|
||||
PanicAlert("Failed to open %s", filename.c_str());
|
||||
return;
|
||||
}
|
||||
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
|
||||
for (auto& stat : stats)
|
||||
{
|
||||
const JitBlock *block = jit->GetBlockCache()->GetBlock(stat.blockNum);
|
||||
if (block)
|
||||
{
|
||||
std::string name = g_symbolDB.GetDescription(block->originalAddress);
|
||||
double percent = 100.0 * (double)stat.cost / (double)cost_sum;
|
||||
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
|
||||
fprintf(f.GetHandle(), "%08x\t%s\t%" PRIu64 "\t%" PRIu64 "\t%.2f\t%.2f\t%.2f\t%i\n",
|
||||
block->originalAddress, name.c_str(), stat.cost,
|
||||
block->ticCounter, percent, timePercent,
|
||||
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool HandleFault(uintptr_t access_address, SContext* ctx)
|
||||
{
|
||||
return jit->HandleFault(access_address, ctx);
|
||||
}
|
||||
|
||||
void ClearCache()
|
||||
{
|
||||
if (jit)
|
||||
jit->ClearCache();
|
||||
}
|
||||
void ClearSafe()
|
||||
{
|
||||
// This clear is "safe" in the sense that it's okay to run from
|
||||
// inside a JIT'ed block: it clears the instruction cache, but not
|
||||
// the JIT'ed code.
|
||||
// TODO: There's probably a better way to handle this situation.
|
||||
if (jit)
|
||||
jit->GetBlockCache()->Clear();
|
||||
}
|
||||
|
||||
void InvalidateICache(u32 address, u32 size, bool forced)
|
||||
{
|
||||
if (jit)
|
||||
jit->GetBlockCache()->InvalidateICache(address, size, forced);
|
||||
}
|
||||
|
||||
u32 ReadOpcodeJIT(u32 _Address)
|
||||
{
|
||||
if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1))
|
||||
{
|
||||
_Address = Memory::TranslateAddress<Memory::FLAG_OPCODE>(_Address);
|
||||
if (_Address == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 inst;
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
// -- this is stupid, should respect HID0
|
||||
if ( (_Address & 0x0FFFFF00) == 0x00000500 )
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
return inst;
|
||||
}
|
||||
|
||||
void CompileExceptionCheck(ExceptionType type)
|
||||
{
|
||||
if (!jit)
|
||||
return;
|
||||
|
||||
std::unordered_set<u32>* exception_addresses = nullptr;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ExceptionType::EXCEPTIONS_FIFO_WRITE:
|
||||
exception_addresses = &jit->js.fifoWriteAddresses;
|
||||
break;
|
||||
case ExceptionType::EXCEPTIONS_PAIRED_QUANTIZE:
|
||||
exception_addresses = &jit->js.pairedQuantizeAddresses;
|
||||
break;
|
||||
}
|
||||
|
||||
if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end()))
|
||||
{
|
||||
if (type == ExceptionType::EXCEPTIONS_FIFO_WRITE)
|
||||
{
|
||||
// Check in case the code has been replaced since: do we need to do this?
|
||||
int optype = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
|
||||
if (optype != OPTYPE_STORE && optype != OPTYPE_STOREFP && (optype != OPTYPE_STOREPS))
|
||||
return;
|
||||
}
|
||||
exception_addresses->insert(PC);
|
||||
|
||||
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
|
||||
jit->GetBlockCache()->InvalidateICache(PC, 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
u32 inst;
|
||||
// Bypass the icache for the external interrupt exception handler
|
||||
// -- this is stupid, should respect HID0
|
||||
if ((_Address & 0x0FFFFF00) == 0x00000500)
|
||||
inst = Memory::ReadUnchecked_U32(_Address);
|
||||
else
|
||||
inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
|
||||
return inst;
|
||||
}
|
||||
|
||||
void CompileExceptionCheck(ExceptionType type)
|
||||
{
|
||||
if (!jit)
|
||||
return;
|
||||
|
||||
std::unordered_set<u32>* exception_addresses = nullptr;
|
||||
|
||||
switch (type)
|
||||
void Shutdown()
|
||||
{
|
||||
case ExceptionType::EXCEPTIONS_FIFO_WRITE:
|
||||
exception_addresses = &jit->js.fifoWriteAddresses;
|
||||
break;
|
||||
case ExceptionType::EXCEPTIONS_PAIRED_QUANTIZE:
|
||||
exception_addresses = &jit->js.pairedQuantizeAddresses;
|
||||
break;
|
||||
}
|
||||
|
||||
if (PC != 0 && (exception_addresses->find(PC)) == (exception_addresses->end()))
|
||||
{
|
||||
if (type == ExceptionType::EXCEPTIONS_FIFO_WRITE)
|
||||
if (jit)
|
||||
{
|
||||
// Check in case the code has been replaced since: do we need to do this?
|
||||
int optype = GetOpInfo(Memory::ReadUnchecked_U32(PC))->type;
|
||||
if (optype != OPTYPE_STORE && optype != OPTYPE_STOREFP && (optype != OPTYPE_STOREPS))
|
||||
return;
|
||||
jit->Shutdown();
|
||||
delete jit;
|
||||
jit = nullptr;
|
||||
}
|
||||
exception_addresses->insert(PC);
|
||||
|
||||
// Invalidate the JIT block so that it gets recompiled with the external exception check included.
|
||||
jit->GetBlockCache()->InvalidateICache(PC, 4, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (jit)
|
||||
{
|
||||
jit->Shutdown();
|
||||
delete jit;
|
||||
jit = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -79,11 +79,11 @@ struct BlockRegStats
|
|||
bool any;
|
||||
bool anyTimer;
|
||||
|
||||
int GetTotalNumAccesses(int reg) { return numReads[reg] + numWrites[reg]; }
|
||||
int GetTotalNumAccesses(int reg) {return numReads[reg] + numWrites[reg];}
|
||||
int GetUseRange(int reg)
|
||||
{
|
||||
return std::max(lastRead[reg], lastWrite[reg]) -
|
||||
std::min(firstRead[reg], firstWrite[reg]);
|
||||
std::min(firstRead[reg], firstWrite[reg]);
|
||||
}
|
||||
|
||||
bool IsUsed(int reg)
|
||||
|
@ -214,6 +214,9 @@ public:
|
|||
// Reorder carry instructions next to their associated branches and pass
|
||||
// carry flags in the x86 flags between them, instead of in XER.
|
||||
OPTION_CARRY_MERGE = (1 << 5),
|
||||
|
||||
// Reorder cror instructions next to their associated fcmp.
|
||||
OPTION_CROR_MERGE = (1 << 6),
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -206,8 +206,7 @@ u32 CFileSystemGCWii::Read32(u64 _Offset) const
|
|||
|
||||
std::string CFileSystemGCWii::GetStringFromOffset(u64 _Offset) const
|
||||
{
|
||||
std::string data;
|
||||
data.resize(255);
|
||||
std::string data(255, 0x00);
|
||||
m_rVolume->Read(_Offset, data.size(), (u8*)&data[0], m_Wii);
|
||||
data.erase(std::find(data.begin(), data.end(), 0x00), data.end());
|
||||
|
||||
|
@ -263,40 +262,41 @@ bool CFileSystemGCWii::DetectFileSystem()
|
|||
void CFileSystemGCWii::InitFileSystem()
|
||||
{
|
||||
m_Initialized = true;
|
||||
u32 const shift = GetOffsetShift();
|
||||
|
||||
// read the whole FST
|
||||
u64 FSTOffset = (u64)Read32(0x424) << GetOffsetShift();
|
||||
u64 FSTOffset = static_cast<u64>(Read32(0x424)) << shift;
|
||||
// u32 FSTSize = Read32(0x428);
|
||||
// u32 FSTMaxSize = Read32(0x42C);
|
||||
|
||||
|
||||
// read all fileinfos
|
||||
SFileInfo Root;
|
||||
Root.m_NameOffset = Read32(FSTOffset + 0x0);
|
||||
Root.m_Offset = (u64)Read32(FSTOffset + 0x4) << GetOffsetShift();
|
||||
Root.m_FileSize = Read32(FSTOffset + 0x8);
|
||||
|
||||
if (Root.IsDirectory())
|
||||
SFileInfo Root
|
||||
{
|
||||
if (m_FileInfoVector.size())
|
||||
PanicAlert("Wtf?");
|
||||
u64 NameTableOffset = FSTOffset;
|
||||
Read32(FSTOffset + 0x0),
|
||||
static_cast<u64>(FSTOffset + 0x4) << shift,
|
||||
Read32(FSTOffset + 0x8)
|
||||
};
|
||||
|
||||
m_FileInfoVector.reserve((unsigned int)Root.m_FileSize);
|
||||
for (u32 i = 0; i < Root.m_FileSize; i++)
|
||||
{
|
||||
SFileInfo sfi;
|
||||
u64 Offset = FSTOffset + (i * 0xC);
|
||||
sfi.m_NameOffset = Read32(Offset + 0x0);
|
||||
sfi.m_Offset = (u64)Read32(Offset + 0x4) << GetOffsetShift();
|
||||
sfi.m_FileSize = Read32(Offset + 0x8);
|
||||
if (!Root.IsDirectory())
|
||||
return;
|
||||
|
||||
m_FileInfoVector.push_back(sfi);
|
||||
NameTableOffset += 0xC;
|
||||
}
|
||||
if (m_FileInfoVector.size())
|
||||
PanicAlert("Wtf?");
|
||||
u64 NameTableOffset = FSTOffset;
|
||||
|
||||
BuildFilenames(1, m_FileInfoVector.size(), "", NameTableOffset);
|
||||
m_FileInfoVector.reserve((size_t)Root.m_FileSize);
|
||||
for (u32 i = 0; i < Root.m_FileSize; i++)
|
||||
{
|
||||
u64 const read_offset = FSTOffset + (i * 0xC);
|
||||
u64 const name_offset = Read32(read_offset + 0x0);
|
||||
u64 const offset = static_cast<u64>(Read32(read_offset + 0x4)) << shift;
|
||||
u64 const size = Read32(read_offset + 0x8);
|
||||
m_FileInfoVector.emplace_back(name_offset, offset, size);
|
||||
NameTableOffset += 0xC;
|
||||
}
|
||||
|
||||
BuildFilenames(1, m_FileInfoVector.size(), "", NameTableOffset);
|
||||
}
|
||||
|
||||
size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, const std::string& _szDirectory, u64 _NameTableOffset)
|
||||
|
@ -307,19 +307,22 @@ size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _
|
|||
{
|
||||
SFileInfo& rFileInfo = m_FileInfoVector[CurrentIndex];
|
||||
u64 const uOffset = _NameTableOffset + (rFileInfo.m_NameOffset & 0xFFFFFF);
|
||||
std::string const offset_str { GetStringFromOffset(uOffset) };
|
||||
bool const is_dir = rFileInfo.IsDirectory();
|
||||
rFileInfo.m_FullPath.reserve(_szDirectory.size() + offset_str.size());
|
||||
|
||||
rFileInfo.m_FullPath = _szDirectory + GetStringFromOffset(uOffset);
|
||||
rFileInfo.m_FullPath.append(_szDirectory.data(), _szDirectory.size())
|
||||
.append(offset_str.data(), offset_str.size())
|
||||
.append("/", size_t(is_dir));
|
||||
|
||||
// check next index
|
||||
if (rFileInfo.IsDirectory())
|
||||
{
|
||||
rFileInfo.m_FullPath += '/';
|
||||
CurrentIndex = BuildFilenames(CurrentIndex + 1, (size_t) rFileInfo.m_FileSize, rFileInfo.m_FullPath, _NameTableOffset);
|
||||
}
|
||||
else
|
||||
if (!is_dir)
|
||||
{
|
||||
++CurrentIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check next index
|
||||
CurrentIndex = BuildFilenames(CurrentIndex + 1, (size_t) rFileInfo.m_FileSize, rFileInfo.m_FullPath, _NameTableOffset);
|
||||
}
|
||||
|
||||
return CurrentIndex;
|
||||
|
|
|
@ -19,21 +19,21 @@ class IVolume;
|
|||
// file info of an FST entry
|
||||
struct SFileInfo
|
||||
{
|
||||
u64 m_NameOffset;
|
||||
u64 m_Offset;
|
||||
u64 m_FileSize;
|
||||
u64 m_NameOffset = 0u;
|
||||
u64 m_Offset = 0u;
|
||||
u64 m_FileSize = 0u;
|
||||
std::string m_FullPath;
|
||||
|
||||
bool IsDirectory() const { return (m_NameOffset & 0xFF000000) != 0; }
|
||||
|
||||
SFileInfo() : m_NameOffset(0), m_Offset(0), m_FileSize(0)
|
||||
{
|
||||
}
|
||||
SFileInfo(u64 name_offset, u64 offset, u64 filesize) :
|
||||
m_NameOffset(name_offset),
|
||||
m_Offset(offset),
|
||||
m_FileSize(filesize)
|
||||
{ }
|
||||
|
||||
SFileInfo(const SFileInfo& rhs) : m_NameOffset(rhs.m_NameOffset),
|
||||
m_Offset(rhs.m_Offset), m_FileSize(rhs.m_FileSize), m_FullPath(rhs.m_FullPath)
|
||||
{
|
||||
}
|
||||
SFileInfo (SFileInfo const&) = default;
|
||||
SFileInfo () = default;
|
||||
};
|
||||
|
||||
class IFileSystem
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
|
||||
virtual bool ChangePartition(u64 offset) { return false; }
|
||||
|
||||
// Increment CACHE_REVISION if values are changed (ISOFile.cpp)
|
||||
// Increment CACHE_REVISION if the code below is modified (ISOFile.cpp & GameFile.cpp)
|
||||
enum ECountry
|
||||
{
|
||||
COUNTRY_EUROPE = 0,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Common/Logging/Log.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
// Increment CACHE_REVISION if the code below is modified (ISOFile.cpp & GameFile.cpp)
|
||||
namespace DiscIO
|
||||
{
|
||||
IVolume::ECountry CountrySwitch(u8 CountryCode)
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace DiscIO
|
|||
{
|
||||
|
||||
CVolumeDirectory::CVolumeDirectory(const std::string& _rDirectory, bool _bIsWii,
|
||||
const std::string& _rApploader, const std::string& _rDOL)
|
||||
const std::string& _rApploader, const std::string& _rDOL)
|
||||
: m_totalNameSize(0)
|
||||
, m_dataStartAddress(-1)
|
||||
, m_diskHeader(DISKHEADERINFO_ADDRESS)
|
||||
|
@ -194,7 +194,7 @@ void CVolumeDirectory::SetName(const std::string& name)
|
|||
{
|
||||
size_t length = name.length();
|
||||
if (length > MAX_NAME_LENGTH)
|
||||
length = MAX_NAME_LENGTH;
|
||||
length = MAX_NAME_LENGTH;
|
||||
|
||||
memcpy(&m_diskHeader[0x20], name.c_str(), length);
|
||||
m_diskHeader[length + 0x20] = 0;
|
||||
|
@ -357,7 +357,7 @@ void CVolumeDirectory::BuildFST()
|
|||
}
|
||||
|
||||
void CVolumeDirectory::WriteToBuffer(u64 _SrcStartAddress, u64 _SrcLength, const u8* _Src,
|
||||
u64& _Address, u64& _Length, u8*& _pBuffer) const
|
||||
u64& _Address, u64& _Length, u8*& _pBuffer) const
|
||||
{
|
||||
if (_Length == 0)
|
||||
return;
|
||||
|
@ -403,7 +403,7 @@ void CVolumeDirectory::Write32(u32 data, u32 offset, std::vector<u8>* const buff
|
|||
(*buffer)[offset++] = (data >> 24);
|
||||
(*buffer)[offset++] = (data >> 16) & 0xff;
|
||||
(*buffer)[offset++] = (data >> 8) & 0xff;
|
||||
(*buffer)[offset] = (data)& 0xff;
|
||||
(*buffer)[offset] = (data) & 0xff;
|
||||
}
|
||||
|
||||
void CVolumeDirectory::WriteEntryData(u32& entryOffset, u8 type, u32 nameOffset, u64 dataOffset, u32 length)
|
||||
|
@ -412,7 +412,7 @@ void CVolumeDirectory::WriteEntryData(u32& entryOffset, u8 type, u32 nameOffset,
|
|||
|
||||
m_FSTData[entryOffset++] = (nameOffset >> 16) & 0xff;
|
||||
m_FSTData[entryOffset++] = (nameOffset >> 8) & 0xff;
|
||||
m_FSTData[entryOffset++] = (nameOffset)& 0xff;
|
||||
m_FSTData[entryOffset++] = (nameOffset) & 0xff;
|
||||
|
||||
Write32((u32)(dataOffset >> m_addressShift), entryOffset, &m_FSTData);
|
||||
entryOffset += 4;
|
||||
|
|
|
@ -152,7 +152,7 @@ bool GameFile::LoadFromCache()
|
|||
if (!file.open(QFile::ReadOnly))
|
||||
return false;
|
||||
|
||||
// If you modify the code below, you MUST bump the CACHE_REVISION! (ISOFile.cpp)
|
||||
// Increment CACHE_REVISION if the code below is modified (GameFile.cpp)
|
||||
QDataStream stream(&file);
|
||||
stream.setVersion(DATASTREAM_REVISION);
|
||||
|
||||
|
@ -195,7 +195,7 @@ void GameFile::SaveToCache()
|
|||
if (!file.open(QFile::WriteOnly))
|
||||
return;
|
||||
|
||||
// If you modify the code below, you MUST bump the CACHE_REVISION! (ISOFile.cpp)
|
||||
// Increment CACHE_REVISION if the code below is modified (GameFile.cpp)
|
||||
QDataStream stream(&file);
|
||||
stream.setVersion(DATASTREAM_REVISION);
|
||||
stream << CACHE_REVISION;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "DolphinQt/Utils/Resources.h"
|
||||
#include "DolphinQt/Utils/Utils.h"
|
||||
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
// The "g_main_window" object as defined in MainWindow.h
|
||||
DMainWindow* g_main_window = nullptr;
|
||||
|
||||
|
@ -92,39 +94,34 @@ void DMainWindow::StartGame(const QString filename)
|
|||
m_render_widget->setWindowTitle(tr("Dolphin")); // TODO
|
||||
m_render_widget->setWindowIcon(windowIcon());
|
||||
|
||||
// TODO: When rendering to main, this won't resize the parent window..
|
||||
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain)
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen)
|
||||
{
|
||||
connect(m_render_widget.get(), SIGNAL(Closed()), this, SLOT(OnStop()));
|
||||
m_render_widget->move(SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowXPos,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowYPos);
|
||||
m_render_widget->resize(SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowWidth, // TODO: Make sure these are client sizes!
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowHeight);
|
||||
m_render_widget->show();
|
||||
m_render_widget->setWindowFlags(m_render_widget->windowFlags() | Qt::BypassWindowManagerHint);
|
||||
g_Config.bFullscreen = !g_Config.bBorderlessFullscreen;
|
||||
m_render_widget->showFullScreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui->centralWidget->addWidget(m_render_widget.get());
|
||||
m_ui->centralWidget->setCurrentWidget(m_render_widget.get());
|
||||
|
||||
// TODO: When rendering to main, this won't resize the parent window...
|
||||
m_render_widget->resize(SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowWidth,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowHeight);
|
||||
}
|
||||
|
||||
if (!BootManager::BootCore(filename.toStdString()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Fatal error"), tr("Failed to init Core"), QMessageBox::Ok);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain)
|
||||
m_ui->centralWidget->removeWidget(m_render_widget.get());
|
||||
else
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen)
|
||||
m_render_widget->close();
|
||||
else
|
||||
m_ui->centralWidget->removeWidget(m_render_widget.get());
|
||||
m_render_widget.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Disable screensaver!
|
||||
|
||||
// TODO: Fullscreen
|
||||
//DoFullscreen(SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen);
|
||||
|
||||
m_render_widget->focusWidget();
|
||||
emit CoreStateChanged(Core::CORE_RUN);
|
||||
}
|
||||
}
|
||||
|
@ -181,48 +178,53 @@ void DMainWindow::OnPlay()
|
|||
}
|
||||
}
|
||||
|
||||
void DMainWindow::OnStop()
|
||||
bool DMainWindow::OnStop()
|
||||
{
|
||||
if (Core::GetState() != Core::CORE_UNINITIALIZED && !m_isStopping)
|
||||
if (Core::GetState() == Core::CORE_UNINITIALIZED || m_isStopping)
|
||||
return true; // We're already stopped/stopping
|
||||
|
||||
// Ask for confirmation in case the user accidentally clicked Stop / Escape
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bConfirmStop)
|
||||
{
|
||||
m_isStopping = true;
|
||||
// Ask for confirmation in case the user accidentally clicked Stop / Escape
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bConfirmStop)
|
||||
// Pause emulation
|
||||
Core::SetState(Core::CORE_PAUSE);
|
||||
emit CoreStateChanged(Core::CORE_PAUSE);
|
||||
|
||||
QMessageBox::StandardButton ret = QMessageBox::question(m_render_widget.get(), tr("Please confirm..."),
|
||||
tr("Do you want to stop the current emulation?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (ret == QMessageBox::No)
|
||||
{
|
||||
int ret = QMessageBox::question(m_render_widget.get(), tr("Please confirm..."),
|
||||
tr("Do you want to stop the current emulation?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
|
||||
if (ret == QMessageBox::No)
|
||||
return;
|
||||
DoStartPause();
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Movie stuff
|
||||
// TODO: Show the author/description dialog here
|
||||
|
||||
// TODO: Show busy cursor
|
||||
BootManager::Stop();
|
||||
// TODO: Hide busy cursor again
|
||||
|
||||
// TODO: Allow screensaver again
|
||||
// TODO: Restore original window title
|
||||
|
||||
// TODO: Return from fullscreen if necessary (DoFullscreen in the wx code)
|
||||
|
||||
// TODO:
|
||||
// If batch mode was specified on the command-line, exit now.
|
||||
//if (m_bBatchMode)
|
||||
// Close(true);
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain)
|
||||
m_ui->centralWidget->removeWidget(m_render_widget.get());
|
||||
else
|
||||
m_render_widget->close();
|
||||
m_render_widget.reset();
|
||||
|
||||
emit CoreStateChanged(Core::CORE_UNINITIALIZED);
|
||||
}
|
||||
|
||||
m_isStopping = true;
|
||||
|
||||
// TODO: Movie stuff
|
||||
// TODO: Show the author/description dialog here
|
||||
|
||||
BootManager::Stop();
|
||||
|
||||
// TODO: Allow screensaver again
|
||||
// TODO: Restore original window title
|
||||
|
||||
// TODO:
|
||||
// If batch mode was specified on the command-line, exit now.
|
||||
//if (m_bBatchMode)
|
||||
// Close(true);
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFullscreen)
|
||||
m_render_widget->close();
|
||||
else
|
||||
m_ui->centralWidget->removeWidget(m_render_widget.get());
|
||||
m_render_widget.reset();
|
||||
|
||||
emit CoreStateChanged(Core::CORE_UNINITIALIZED);
|
||||
m_isStopping = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DMainWindow::OnGameListStyleChanged()
|
||||
|
|
|
@ -33,6 +33,10 @@ public:
|
|||
signals:
|
||||
void CoreStateChanged(Core::EState state);
|
||||
|
||||
public slots:
|
||||
// Main toolbar (also used by DRenderWidget)
|
||||
bool OnStop();
|
||||
|
||||
private slots:
|
||||
// Emulation
|
||||
void StartGame(const QString filename);
|
||||
|
@ -41,7 +45,6 @@ private slots:
|
|||
// Main toolbar
|
||||
void OnOpen();
|
||||
void OnPlay();
|
||||
void OnStop();
|
||||
|
||||
// View menu
|
||||
void OnGameListStyleChanged();
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
// Licensed under GPLv2
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "DolphinQt/MainWindow.h"
|
||||
#include "DolphinQt/VideoInterface/RenderWidget.h"
|
||||
|
||||
DRenderWidget::DRenderWidget(QWidget* parent_widget)
|
||||
|
@ -16,9 +18,10 @@ DRenderWidget::DRenderWidget(QWidget* parent_widget)
|
|||
|
||||
void DRenderWidget::closeEvent(QCloseEvent* e)
|
||||
{
|
||||
// TODO: update render window positions in config
|
||||
|
||||
// TODO: Do this differently...
|
||||
emit Closed();
|
||||
if (!g_main_window->OnStop())
|
||||
{
|
||||
e->ignore();
|
||||
return;
|
||||
}
|
||||
QWidget::closeEvent(e);
|
||||
}
|
||||
|
|
|
@ -610,7 +610,7 @@ void CConfigMain::CreateGUIControls()
|
|||
DSPEngine = new wxRadioBox(AudioPage, ID_DSPENGINE, _("DSP Emulator Engine"), wxDefaultPosition, wxDefaultSize, arrayStringFor_DSPEngine, 0, wxRA_SPECIFY_ROWS);
|
||||
DPL2Decoder = new wxCheckBox(AudioPage, ID_DPL2DECODER, _("Dolby Pro Logic II decoder"));
|
||||
TimeStretching = new wxCheckBox(AudioPage, ID_TIMESTRETCHING, _("Time Stretching"));
|
||||
VolumeSlider = new wxSlider(AudioPage, ID_VOLUME, 0, 1, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_INVERSE);
|
||||
VolumeSlider = new wxSlider(AudioPage, ID_VOLUME, 0, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_INVERSE);
|
||||
VolumeText = new wxStaticText(AudioPage, wxID_ANY, "");
|
||||
BackendSelection = new wxChoice(AudioPage, ID_BACKEND, wxDefaultPosition, wxDefaultSize, wxArrayBackends, 0, wxDefaultValidator, wxEmptyString);
|
||||
Latency = new wxSpinCtrl(AudioPage, ID_LATENCY, "", wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 30);
|
||||
|
@ -801,14 +801,14 @@ void CConfigMain::CreateGUIControls()
|
|||
EnableOC = new wxCheckBox(AdvancedPage, ID_ENABLEOVERCLOCK, _("Enable CPU Clock Override"));
|
||||
OCSlider = new wxSlider(AdvancedPage, ID_OVERCLOCK, 100, 0, 150, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
|
||||
wxStaticText* OCDescription = new wxStaticText(AdvancedPage, wxID_ANY,
|
||||
("Higher values can make variable-framerate games\n"
|
||||
"run at a higher framerate, at the expense of CPU.\n"
|
||||
"Lower values can make variable-framerate games\n"
|
||||
"run at a lower framerate, saving CPU.\n\n"
|
||||
"WARNING: Changing this from the default (100%)\n"
|
||||
"can and will break games and cause glitches.\n"
|
||||
"Do so at your own risk. Please do not report\n"
|
||||
"bugs that occur with a non-default clock.\n"));
|
||||
_("Higher values can make variable-framerate games\n"
|
||||
"run at a higher framerate, at the expense of CPU.\n"
|
||||
"Lower values can make variable-framerate games\n"
|
||||
"run at a lower framerate, saving CPU.\n\n"
|
||||
"WARNING: Changing this from the default (100%)\n"
|
||||
"can and will break games and cause glitches.\n"
|
||||
"Do so at your own risk. Please do not report\n"
|
||||
"bugs that occur with a non-default clock.\n"));
|
||||
OCText = new wxStaticText(AdvancedPage, wxID_ANY, "");
|
||||
bOverclockEnable->Add(EnableOC);
|
||||
bOverclock->Add(OCSlider, 1, wxALL, 5);
|
||||
|
|
|
@ -144,13 +144,13 @@ private:
|
|||
wxCheckBox* SkipIdle;
|
||||
wxCheckBox* EnableCheats;
|
||||
wxChoice* Framelimit;
|
||||
wxSlider* OCSlider;
|
||||
wxStaticText* OCText;
|
||||
wxCheckBox* EnableOC;
|
||||
|
||||
// Advanced
|
||||
wxRadioBox* CPUEngine;
|
||||
wxCheckBox* _NTSCJ;
|
||||
wxSlider* OCSlider;
|
||||
wxStaticText* OCText;
|
||||
wxCheckBox* EnableOC;
|
||||
|
||||
|
||||
wxBoxSizer* sDisplayPage; // Display settings
|
||||
|
@ -268,7 +268,7 @@ private:
|
|||
void AddRemoveISOPaths(wxCommandEvent& event);
|
||||
void DefaultISOChanged(wxFileDirPickerEvent& event);
|
||||
void DVDRootChanged(wxFileDirPickerEvent& event);
|
||||
void ApploaderPathChanged(wxFileDirPickerEvent& WXUNUSED(event));
|
||||
void ApploaderPathChanged(wxFileDirPickerEvent& WXUNUSED (event));
|
||||
void NANDRootChanged(wxFileDirPickerEvent& event);
|
||||
|
||||
private:
|
||||
|
|
|
@ -77,7 +77,6 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
|||
|
||||
wxStaticText* pad_labels[4];
|
||||
wxChoice* pad_type_choices[4];
|
||||
wxButton* config_buttons[4];
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
|
@ -86,8 +85,8 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
|||
// Create an ID for the config button.
|
||||
const wxWindowID button_id = wxWindow::NewControlId();
|
||||
m_gc_port_config_ids.insert(std::make_pair(button_id, i));
|
||||
config_buttons[i] = new wxButton(this, button_id, _("Configure"), wxDefaultPosition, wxSize(100, 25));
|
||||
config_buttons[i]->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnGameCubeConfigButton, this);
|
||||
gamecube_configure_bt[i] = new wxButton(this, button_id, _("Configure"), wxDefaultPosition, wxSize(100, 25));
|
||||
gamecube_configure_bt[i]->Bind(wxEVT_BUTTON, &ControllerConfigDiag::OnGameCubeConfigButton, this);
|
||||
|
||||
// Create a control ID for the choice boxes on the fly.
|
||||
const wxWindowID choice_id = wxWindow::NewControlId();
|
||||
|
@ -122,19 +121,21 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
|||
break;
|
||||
case SIDEVICE_GC_GBA:
|
||||
pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[5]);
|
||||
gamecube_configure_bt[i]->Disable();
|
||||
break;
|
||||
case SIDEVICE_AM_BASEBOARD:
|
||||
pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[6]);
|
||||
break;
|
||||
default:
|
||||
pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[0]);
|
||||
gamecube_configure_bt[i]->Disable();
|
||||
break;
|
||||
}
|
||||
|
||||
// Add to the sizer
|
||||
gamecube_flex_sizer->Add(pad_labels[i], 0, wxALIGN_CENTER_VERTICAL);
|
||||
gamecube_flex_sizer->Add(pad_type_choices[i], 0, wxALIGN_CENTER_VERTICAL);
|
||||
gamecube_flex_sizer->Add(config_buttons[i], 1, wxEXPAND);
|
||||
gamecube_flex_sizer->Add(gamecube_configure_bt[i], 1, wxEXPAND);
|
||||
}
|
||||
|
||||
gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND, 5);
|
||||
|
@ -488,19 +489,40 @@ void ControllerConfigDiag::OnGameCubePortChanged(wxCommandEvent& event)
|
|||
|
||||
SIDevices tempType;
|
||||
if (device_name == m_gc_pad_type_strs[1])
|
||||
{
|
||||
tempType = SIDEVICE_GC_CONTROLLER;
|
||||
gamecube_configure_bt[device_num]->Enable();
|
||||
}
|
||||
else if (device_name == m_gc_pad_type_strs[2])
|
||||
{
|
||||
tempType = SIDEVICE_GC_STEERING;
|
||||
gamecube_configure_bt[device_num]->Enable();
|
||||
}
|
||||
else if (device_name == m_gc_pad_type_strs[3])
|
||||
{
|
||||
tempType = SIDEVICE_DANCEMAT;
|
||||
gamecube_configure_bt[device_num]->Enable();
|
||||
}
|
||||
else if (device_name == m_gc_pad_type_strs[4])
|
||||
{
|
||||
tempType = SIDEVICE_GC_TARUKONGA;
|
||||
gamecube_configure_bt[device_num]->Enable();
|
||||
}
|
||||
else if (device_name == m_gc_pad_type_strs[5])
|
||||
{
|
||||
tempType = SIDEVICE_GC_GBA;
|
||||
gamecube_configure_bt[device_num]->Disable();
|
||||
}
|
||||
else if (device_name == m_gc_pad_type_strs[6])
|
||||
{
|
||||
tempType = SIDEVICE_AM_BASEBOARD;
|
||||
gamecube_configure_bt[device_num]->Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
tempType = SIDEVICE_NONE;
|
||||
gamecube_configure_bt[device_num]->Disable();
|
||||
}
|
||||
|
||||
SConfig::GetInstance().m_SIDevice[device_num] = tempType;
|
||||
|
||||
|
|
|
@ -89,5 +89,6 @@ private:
|
|||
unsigned int m_orig_wiimote_sources[MAX_BBMOTES];
|
||||
|
||||
wxButton* wiimote_configure_bt[MAX_WIIMOTES];
|
||||
wxButton* gamecube_configure_bt[4];
|
||||
std::map<wxWindowID, unsigned int> m_wiimote_index_from_conf_bt_id;
|
||||
};
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#include <wx/panel.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/slider.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/string.h>
|
||||
|
@ -431,6 +433,25 @@ void CISOProperties::CreateGUIControls(bool IsWad)
|
|||
// Wii Console
|
||||
EnableWideScreen = new wxCheckBox(m_GameConfig, ID_ENABLEWIDESCREEN, _("Enable WideScreen"), wxDefaultPosition, wxDefaultSize, GetElementStyle("Wii", "Widescreen"));
|
||||
|
||||
// Stereoscopy
|
||||
wxBoxSizer* const sDepthPercentage = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText* const DepthPercentageText = new wxStaticText(m_GameConfig, wxID_ANY, _("Depth Percentage: "));
|
||||
DepthPercentage = new wxSlider(m_GameConfig, ID_DEPTHPERCENTAGE, 100, 0, 200);
|
||||
DepthPercentage->SetToolTip(_("This value is multiplied with the depth set in the graphics configuration."));
|
||||
sDepthPercentage->Add(DepthPercentageText);
|
||||
sDepthPercentage->Add(DepthPercentage);
|
||||
|
||||
wxBoxSizer* const sConvergenceMinimum = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText* const ConvergenceMinimumText = new wxStaticText(m_GameConfig, wxID_ANY, _("Convergence Minimum: "));
|
||||
ConvergenceMinimum = new wxSpinCtrl(m_GameConfig, ID_CONVERGENCEMINIMUM);
|
||||
ConvergenceMinimum->SetRange(0, INT32_MAX);
|
||||
ConvergenceMinimum->SetToolTip(_("This value is added to the convergence value set in the graphics configuration."));
|
||||
sConvergenceMinimum->Add(ConvergenceMinimumText);
|
||||
sConvergenceMinimum->Add(ConvergenceMinimum);
|
||||
|
||||
MonoDepth = new wxCheckBox(m_GameConfig, ID_MONODEPTH, _("Monoscopic Shadows"), wxDefaultPosition, wxDefaultSize, GetElementStyle("Video_Stereoscopy", "StereoEFBMonoDepth"));
|
||||
MonoDepth->SetToolTip(_("Use a single depth buffer for both eyes. Needed for a few games."));
|
||||
|
||||
wxBoxSizer* const sEmuState = new wxBoxSizer(wxHORIZONTAL);
|
||||
wxStaticText* const EmuStateText = new wxStaticText(m_GameConfig, wxID_ANY, _("Emulation State: "));
|
||||
arrayStringFor_EmuState.Add(_("Not Set"));
|
||||
|
@ -466,10 +487,17 @@ void CISOProperties::CreateGUIControls(bool IsWad)
|
|||
}
|
||||
sbWiiOverrides->Add(EnableWideScreen, 0, wxLEFT, 5);
|
||||
|
||||
wxStaticBoxSizer* const sbStereoOverrides =
|
||||
new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Stereoscopy"));
|
||||
sbStereoOverrides->Add(sDepthPercentage);
|
||||
sbStereoOverrides->Add(sConvergenceMinimum);
|
||||
sbStereoOverrides->Add(MonoDepth);
|
||||
|
||||
wxStaticBoxSizer * const sbGameConfig = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Game-Specific Settings"));
|
||||
sbGameConfig->Add(OverrideText, 0, wxEXPAND|wxALL, 5);
|
||||
sbGameConfig->Add(sbCoreOverrides, 0, wxEXPAND);
|
||||
sbGameConfig->Add(sbWiiOverrides, 0, wxEXPAND);
|
||||
sbGameConfig->Add(sbStereoOverrides, 0, wxEXPAND);
|
||||
sConfigPage->Add(sbGameConfig, 0, wxEXPAND|wxALL, 5);
|
||||
sEmuState->Add(EmuStateText, 0, wxALIGN_CENTER_VERTICAL);
|
||||
sEmuState->Add(EmuState, 0, wxEXPAND);
|
||||
|
@ -1042,6 +1070,7 @@ void CISOProperties::LoadGameConfig()
|
|||
SetCheckboxValueFromGameini("Core", "BlockMerging", BlockMerging);
|
||||
SetCheckboxValueFromGameini("Core", "DSPHLE", DSPHLE);
|
||||
SetCheckboxValueFromGameini("Wii", "Widescreen", EnableWideScreen);
|
||||
SetCheckboxValueFromGameini("Video_Stereoscopy", "StereoEFBMonoDepth", MonoDepth);
|
||||
|
||||
IniFile::Section* default_video = GameIniDefault.GetOrCreateSection("Video");
|
||||
|
||||
|
@ -1089,6 +1118,14 @@ void CISOProperties::LoadGameConfig()
|
|||
else if (sTemp == "fake-completion")
|
||||
GPUDeterminism->SetSelection(3);
|
||||
|
||||
IniFile::Section* default_stereoscopy = GameIniDefault.GetOrCreateSection("Video_Stereoscopy");
|
||||
default_stereoscopy->Get("StereoDepthPercentage", &iTemp, 100);
|
||||
GameIniLocal.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &iTemp);
|
||||
DepthPercentage->SetValue(iTemp);
|
||||
default_stereoscopy->Get("StereoConvergenceMinimum", &iTemp, 0);
|
||||
GameIniLocal.GetIfExists("Video_Stereoscopy", "StereoConvergenceMinimum", &iTemp);
|
||||
ConvergenceMinimum->SetValue(iTemp);
|
||||
|
||||
PatchList_Load();
|
||||
ActionReplayList_Load();
|
||||
m_geckocode_panel->LoadCodes(GameIniDefault, GameIniLocal, OpenISO->GetUniqueID());
|
||||
|
@ -1130,6 +1167,7 @@ bool CISOProperties::SaveGameConfig()
|
|||
SaveGameIniValueFrom3StateCheckbox("Core", "BlockMerging", BlockMerging);
|
||||
SaveGameIniValueFrom3StateCheckbox("Core", "DSPHLE", DSPHLE);
|
||||
SaveGameIniValueFrom3StateCheckbox("Wii", "Widescreen", EnableWideScreen);
|
||||
SaveGameIniValueFrom3StateCheckbox("Video_Stereoscopy", "StereoEFBMonoDepth", MonoDepth);
|
||||
|
||||
#define SAVE_IF_NOT_DEFAULT(section, key, val, def) do { \
|
||||
if (GameIniDefault.Exists((section), (key))) { \
|
||||
|
@ -1166,6 +1204,10 @@ bool CISOProperties::SaveGameConfig()
|
|||
|
||||
SAVE_IF_NOT_DEFAULT("Core", "GPUDeterminismMode", tmp, "Not Set");
|
||||
|
||||
int depth = DepthPercentage->GetValue() > 0 ? DepthPercentage->GetValue() : 100;
|
||||
SAVE_IF_NOT_DEFAULT("Video_Stereoscopy", "StereoDepthPercentage", depth, 100);
|
||||
SAVE_IF_NOT_DEFAULT("Video_Stereoscopy", "StereoConvergenceMinimum", ConvergenceMinimum->GetValue(), 0);
|
||||
|
||||
PatchList_Save();
|
||||
ActionReplayList_Save();
|
||||
Gecko::SaveCodes(GameIniLocal, m_geckocode_panel->GetCodes());
|
||||
|
|
|
@ -26,6 +26,8 @@ class wxButton;
|
|||
class wxCheckBox;
|
||||
class wxCheckListBox;
|
||||
class wxChoice;
|
||||
class wxSlider;
|
||||
class wxSpinCtrl;
|
||||
class wxStaticBitmap;
|
||||
class wxTextCtrl;
|
||||
class wxTreeCtrl;
|
||||
|
@ -75,6 +77,11 @@ private:
|
|||
// Wii
|
||||
wxCheckBox* EnableWideScreen;
|
||||
|
||||
// Stereoscopy
|
||||
wxSlider* DepthPercentage;
|
||||
wxSpinCtrl* ConvergenceMinimum;
|
||||
wxCheckBox* MonoDepth;
|
||||
|
||||
wxArrayString arrayStringFor_EmuState;
|
||||
wxChoice* EmuState;
|
||||
wxTextCtrl* EmuIssues;
|
||||
|
@ -150,6 +157,9 @@ private:
|
|||
ID_ADDCHEAT,
|
||||
ID_REMOVECHEAT,
|
||||
ID_GPUDETERMINISM,
|
||||
ID_DEPTHPERCENTAGE,
|
||||
ID_CONVERGENCEMINIMUM,
|
||||
ID_MONODEPTH,
|
||||
|
||||
ID_NAME,
|
||||
ID_GAMEID,
|
||||
|
|
|
@ -37,7 +37,7 @@ bool ToggleFullscreen(Display *dpy, Window win)
|
|||
|
||||
// Send the event
|
||||
if (!XSendEvent(dpy, DefaultRootWindow(dpy), False,
|
||||
SubstructureRedirectMask | SubstructureNotifyMask, &event))
|
||||
SubstructureRedirectMask | SubstructureNotifyMask, &event))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to switch fullscreen/windowed mode.");
|
||||
return false;
|
||||
|
@ -56,12 +56,12 @@ void InhibitScreensaver(Display *dpy, Window win, bool suspend)
|
|||
(char *)"xdg-screensaver",
|
||||
(char *)(suspend ? "suspend" : "resume"),
|
||||
id,
|
||||
nullptr };
|
||||
nullptr};
|
||||
pid_t pid;
|
||||
if (!posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ))
|
||||
{
|
||||
int status;
|
||||
while (waitpid(pid, &status, 0) == -1);
|
||||
while (waitpid (pid, &status, 0) == -1);
|
||||
|
||||
DEBUG_LOG(VIDEO, "Started xdg-screensaver (PID = %d)", (int)pid);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ XRRConfiguration::XRRConfiguration(Display *_dpy, Window _win)
|
|||
int XRRMajorVersion, XRRMinorVersion;
|
||||
|
||||
if (!XRRQueryVersion(dpy, &XRRMajorVersion, &XRRMinorVersion) ||
|
||||
(XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3)))
|
||||
(XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3)))
|
||||
{
|
||||
WARN_LOG(VIDEO, "XRRExtension not supported.");
|
||||
bValid = false;
|
||||
|
@ -135,7 +135,7 @@ void XRRConfiguration::Update()
|
|||
char *output_name = nullptr;
|
||||
char auxFlag = '\0';
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.find(':') ==
|
||||
std::string::npos)
|
||||
std::string::npos)
|
||||
{
|
||||
fullWidth = fb_width;
|
||||
fullHeight = fb_height;
|
||||
|
@ -143,7 +143,7 @@ void XRRConfiguration::Update()
|
|||
else
|
||||
{
|
||||
sscanf(SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.c_str(),
|
||||
"%m[^:]: %ux%u%c", &output_name, &fullWidth, &fullHeight, &auxFlag);
|
||||
"%m[^:]: %ux%u%c", &output_name, &fullWidth, &fullHeight, &auxFlag);
|
||||
}
|
||||
bool want_interlaced = ('i' == auxFlag);
|
||||
|
||||
|
@ -173,8 +173,8 @@ void XRRConfiguration::Update()
|
|||
if (output_info->modes[j] == screenResources->modes[k].id)
|
||||
{
|
||||
if (fullWidth == screenResources->modes[k].width &&
|
||||
fullHeight == screenResources->modes[k].height &&
|
||||
want_interlaced == !!(screenResources->modes[k].modeFlags & RR_Interlace))
|
||||
fullHeight == screenResources->modes[k].height &&
|
||||
want_interlaced == !!(screenResources->modes[k].modeFlags & RR_Interlace))
|
||||
{
|
||||
fullMode = screenResources->modes[k].id;
|
||||
if (crtcInfo->x + (int)screenResources->modes[k].width > fs_fb_width)
|
||||
|
@ -213,7 +213,7 @@ void XRRConfiguration::Update()
|
|||
else
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to obtain fullscreen size.\n"
|
||||
"Using current desktop resolution for fullscreen.");
|
||||
"Using current desktop resolution for fullscreen.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,16 +228,16 @@ void XRRConfiguration::ToggleDisplayMode(bool bFullscreen)
|
|||
if (bFullscreen)
|
||||
{
|
||||
XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime,
|
||||
crtcInfo->x, crtcInfo->y, fullMode, crtcInfo->rotation,
|
||||
crtcInfo->outputs, crtcInfo->noutput);
|
||||
crtcInfo->x, crtcInfo->y, fullMode, crtcInfo->rotation,
|
||||
crtcInfo->outputs, crtcInfo->noutput);
|
||||
XRRSetScreenSize(dpy, win, fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm);
|
||||
bIsFullscreen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime,
|
||||
crtcInfo->x, crtcInfo->y, crtcInfo->mode, crtcInfo->rotation,
|
||||
crtcInfo->outputs, crtcInfo->noutput);
|
||||
crtcInfo->x, crtcInfo->y, crtcInfo->mode, crtcInfo->rotation,
|
||||
crtcInfo->outputs, crtcInfo->noutput);
|
||||
XRRSetScreenSize(dpy, win, fb_width, fb_height, fb_width_mm, fb_height_mm);
|
||||
bIsFullscreen = false;
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ void XRRConfiguration::AddResolutions(std::vector<std::string>& resos)
|
|||
bool interlaced = !!(screenResources->modes[k].modeFlags & RR_Interlace);
|
||||
const std::string strRes =
|
||||
std::string(output_info->name) + ": " +
|
||||
std::string(screenResources->modes[k].name) + (interlaced ? "i" : "");
|
||||
std::string(screenResources->modes[k].name) + (interlaced? "i" : "");
|
||||
// Only add unique resolutions
|
||||
if (std::find(resos.begin(), resos.end(), strRes) == resos.end())
|
||||
{
|
||||
|
|
|
@ -18,6 +18,11 @@ namespace ciface
|
|||
namespace SDL
|
||||
{
|
||||
|
||||
// 10ms = 100Hz which homebrew docs very roughly imply is within WiiMote normal
|
||||
// range, used for periodic haptic effects though often ignored by devices
|
||||
static const u16 RUMBLE_PERIOD = 10;
|
||||
static const u16 RUMBLE_LENGTH_MAX = 500; // ms: enough to span multiple frames at low FPS, but still finite
|
||||
|
||||
static std::string GetJoystickName(int index)
|
||||
{
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
@ -27,13 +32,13 @@ static std::string GetJoystickName(int index)
|
|||
#endif
|
||||
}
|
||||
|
||||
void Init( std::vector<Core::Device*>& devices )
|
||||
void Init(std::vector<Core::Device*>& devices)
|
||||
{
|
||||
// this is used to number the joysticks
|
||||
// multiple joysticks with the same name shall get unique ids starting at 0
|
||||
std::map<std::string, int> name_counts;
|
||||
|
||||
if (SDL_Init( SDL_INIT_FLAGS ) >= 0)
|
||||
if (SDL_Init(SDL_INIT_FLAGS) >= 0)
|
||||
{
|
||||
// joysticks
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||
|
@ -44,7 +49,7 @@ void Init( std::vector<Core::Device*>& devices )
|
|||
Joystick* js = new Joystick(dev, i, name_counts[GetJoystickName(i)]++);
|
||||
// only add if it has some inputs/outputs
|
||||
if (js->Inputs().size() || js->Outputs().size())
|
||||
devices.push_back( js );
|
||||
devices.push_back(js);
|
||||
else
|
||||
delete js;
|
||||
}
|
||||
|
@ -68,10 +73,10 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
|
|||
std::transform(lcasename.begin(), lcasename.end(), lcasename.begin(), tolower);
|
||||
|
||||
if ((std::string::npos != lcasename.find("xbox 360")) &&
|
||||
(10 == SDL_JoystickNumButtons(joystick)) &&
|
||||
(5 == SDL_JoystickNumAxes(joystick)) &&
|
||||
(1 == SDL_JoystickNumHats(joystick)) &&
|
||||
(0 == SDL_JoystickNumBalls(joystick)))
|
||||
(10 == SDL_JoystickNumButtons(joystick)) &&
|
||||
(5 == SDL_JoystickNumAxes(joystick)) &&
|
||||
(1 == SDL_JoystickNumHats(joystick)) &&
|
||||
(0 == SDL_JoystickNumBalls(joystick)))
|
||||
{
|
||||
// this device won't be used
|
||||
return;
|
||||
|
@ -79,9 +84,9 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
|
|||
#endif
|
||||
|
||||
if (SDL_JoystickNumButtons(joystick) > 255 ||
|
||||
SDL_JoystickNumAxes(joystick) > 255 ||
|
||||
SDL_JoystickNumHats(joystick) > 255 ||
|
||||
SDL_JoystickNumBalls(joystick) > 255)
|
||||
SDL_JoystickNumAxes(joystick) > 255 ||
|
||||
SDL_JoystickNumHats(joystick) > 255 ||
|
||||
SDL_JoystickNumBalls(joystick) > 255)
|
||||
{
|
||||
// This device is invalid, don't use it
|
||||
// Some crazy devices(HP webcam 2100) end up as HID devices
|
||||
|
@ -111,13 +116,13 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
|
|||
|
||||
#ifdef USE_SDL_HAPTIC
|
||||
// try to get supported ff effects
|
||||
m_haptic = SDL_HapticOpenFromJoystick( m_joystick );
|
||||
m_haptic = SDL_HapticOpenFromJoystick(m_joystick);
|
||||
if (m_haptic)
|
||||
{
|
||||
//SDL_HapticSetGain( m_haptic, 1000 );
|
||||
//SDL_HapticSetAutocenter( m_haptic, 0 );
|
||||
|
||||
const unsigned int supported_effects = SDL_HapticQuery( m_haptic );
|
||||
const unsigned int supported_effects = SDL_HapticQuery(m_haptic);
|
||||
|
||||
// constant effect
|
||||
if (supported_effects & SDL_HAPTIC_CONSTANT)
|
||||
|
@ -126,6 +131,18 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index, const unsi
|
|||
// ramp effect
|
||||
if (supported_effects & SDL_HAPTIC_RAMP)
|
||||
AddOutput(new RampEffect(m_haptic));
|
||||
|
||||
// sine effect
|
||||
if (supported_effects & SDL_HAPTIC_SINE)
|
||||
AddOutput(new SineEffect(m_haptic));
|
||||
|
||||
// triangle effect
|
||||
if (supported_effects & SDL_HAPTIC_TRIANGLE)
|
||||
AddOutput(new TriangleEffect(m_haptic));
|
||||
|
||||
// left-right effect
|
||||
if (supported_effects & SDL_HAPTIC_LEFTRIGHT)
|
||||
AddOutput(new LeftRightEffect(m_haptic));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -178,36 +195,81 @@ std::string Joystick::RampEffect::GetName() const
|
|||
return "Ramp";
|
||||
}
|
||||
|
||||
void Joystick::ConstantEffect::SetState(ControlState state)
|
||||
std::string Joystick::SineEffect::GetName() const
|
||||
{
|
||||
return "Sine";
|
||||
}
|
||||
|
||||
std::string Joystick::TriangleEffect::GetName() const
|
||||
{
|
||||
return "Triangle";
|
||||
}
|
||||
|
||||
std::string Joystick::LeftRightEffect::GetName() const
|
||||
{
|
||||
return "LeftRight";
|
||||
}
|
||||
|
||||
void Joystick::HapticEffect::SetState(ControlState state)
|
||||
{
|
||||
memset(&m_effect, 0, sizeof(m_effect));
|
||||
if (state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_CONSTANT;
|
||||
m_effect.constant.length = SDL_HAPTIC_INFINITY;
|
||||
SetSDLHapticEffect(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this module uses type==0 to indicate 'off'
|
||||
m_effect.type = 0;
|
||||
}
|
||||
|
||||
m_effect.constant.level = (Sint16)(state * 0x7FFF);
|
||||
Update();
|
||||
}
|
||||
|
||||
void Joystick::RampEffect::SetState(ControlState state)
|
||||
void Joystick::ConstantEffect::SetSDLHapticEffect(ControlState state)
|
||||
{
|
||||
if (state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_RAMP;
|
||||
m_effect.ramp.length = SDL_HAPTIC_INFINITY;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_effect.type = 0;
|
||||
}
|
||||
m_effect.type = SDL_HAPTIC_CONSTANT;
|
||||
m_effect.constant.length = RUMBLE_LENGTH_MAX;
|
||||
m_effect.constant.level = (Sint16)(state * 0x7FFF);
|
||||
}
|
||||
|
||||
void Joystick::RampEffect::SetSDLHapticEffect(ControlState state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_RAMP;
|
||||
m_effect.ramp.length = RUMBLE_LENGTH_MAX;
|
||||
m_effect.ramp.start = (Sint16)(state * 0x7FFF);
|
||||
Update();
|
||||
}
|
||||
|
||||
void Joystick::SineEffect::SetSDLHapticEffect(ControlState state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_SINE;
|
||||
m_effect.periodic.period = RUMBLE_PERIOD;
|
||||
m_effect.periodic.magnitude = (Sint16)(state * 0x7FFF);
|
||||
m_effect.periodic.offset = 0;
|
||||
m_effect.periodic.phase = 18000;
|
||||
m_effect.periodic.length = RUMBLE_LENGTH_MAX;
|
||||
m_effect.periodic.delay = 0;
|
||||
m_effect.periodic.attack_length = 0;
|
||||
}
|
||||
|
||||
void Joystick::TriangleEffect::SetSDLHapticEffect(ControlState state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_TRIANGLE;
|
||||
m_effect.periodic.period = RUMBLE_PERIOD;
|
||||
m_effect.periodic.magnitude = (Sint16)(state * 0x7FFF);
|
||||
m_effect.periodic.offset = 0;
|
||||
m_effect.periodic.phase = 18000;
|
||||
m_effect.periodic.length = RUMBLE_LENGTH_MAX;
|
||||
m_effect.periodic.delay = 0;
|
||||
m_effect.periodic.attack_length = 0;
|
||||
}
|
||||
|
||||
void Joystick::LeftRightEffect::SetSDLHapticEffect(ControlState state)
|
||||
{
|
||||
m_effect.type = SDL_HAPTIC_LEFTRIGHT;
|
||||
m_effect.leftright.length = RUMBLE_LENGTH_MAX;
|
||||
// max ranges tuned to 'feel' similar in magnitude to triangle/sine on xbox360 controller
|
||||
m_effect.leftright.large_magnitude = (Uint16)(state * 0x4000);
|
||||
m_effect.leftright.small_magnitude = (Uint16)(state * 0xFFFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
#define USE_SDL_HAPTIC
|
||||
#define USE_SDL_HAPTIC
|
||||
#endif
|
||||
|
||||
#ifdef USE_SDL_HAPTIC
|
||||
#include <SDL_haptic.h>
|
||||
#define SDL_INIT_FLAGS SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC
|
||||
#include <SDL_haptic.h>
|
||||
#define SDL_INIT_FLAGS SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC
|
||||
#else
|
||||
#define SDL_INIT_FLAGS SDL_INIT_JOYSTICK
|
||||
#define SDL_INIT_FLAGS SDL_INIT_JOYSTICK
|
||||
#endif
|
||||
|
||||
namespace ciface
|
||||
|
@ -27,7 +27,7 @@ namespace ciface
|
|||
namespace SDL
|
||||
{
|
||||
|
||||
void Init( std::vector<Core::Device*>& devices );
|
||||
void Init(std::vector<Core::Device*>& devices);
|
||||
|
||||
class Joystick : public Core::Device
|
||||
{
|
||||
|
@ -40,8 +40,8 @@ private:
|
|||
Button(u8 index, SDL_Joystick* js) : m_js(js), m_index(index) {}
|
||||
ControlState GetState() const override;
|
||||
private:
|
||||
SDL_Joystick* const m_js;
|
||||
const u8 m_index;
|
||||
SDL_Joystick* const m_js;
|
||||
const u8 m_index;
|
||||
};
|
||||
|
||||
class Axis : public Core::Device::Input
|
||||
|
@ -77,10 +77,13 @@ private:
|
|||
|
||||
protected:
|
||||
void Update();
|
||||
virtual void SetSDLHapticEffect(ControlState state) = 0;
|
||||
|
||||
SDL_HapticEffect m_effect;
|
||||
SDL_Haptic* m_haptic;
|
||||
int m_id;
|
||||
private:
|
||||
virtual void SetState(ControlState state) override final;
|
||||
};
|
||||
|
||||
class ConstantEffect : public HapticEffect
|
||||
|
@ -88,7 +91,8 @@ private:
|
|||
public:
|
||||
ConstantEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
|
||||
std::string GetName() const override;
|
||||
void SetState(ControlState state) override;
|
||||
private:
|
||||
void SetSDLHapticEffect(ControlState state) override;
|
||||
};
|
||||
|
||||
class RampEffect : public HapticEffect
|
||||
|
@ -96,7 +100,35 @@ private:
|
|||
public:
|
||||
RampEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
|
||||
std::string GetName() const override;
|
||||
void SetState(ControlState state) override;
|
||||
private:
|
||||
void SetSDLHapticEffect(ControlState state) override;
|
||||
};
|
||||
|
||||
class SineEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
SineEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
|
||||
std::string GetName() const override;
|
||||
private:
|
||||
void SetSDLHapticEffect(ControlState state) override;
|
||||
};
|
||||
|
||||
class TriangleEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
TriangleEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
|
||||
std::string GetName() const override;
|
||||
private:
|
||||
void SetSDLHapticEffect(ControlState state) override;
|
||||
};
|
||||
|
||||
class LeftRightEffect : public HapticEffect
|
||||
{
|
||||
public:
|
||||
LeftRightEffect(SDL_Haptic* haptic) : HapticEffect(haptic) {}
|
||||
std::string GetName() const override;
|
||||
private:
|
||||
void SetSDLHapticEffect(ControlState state) override;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -477,7 +477,7 @@ void SetCpStatusRegister()
|
|||
{
|
||||
// Here always there is one fifo attached to the GPU
|
||||
m_CPStatusReg.Breakpoint = fifo.bFF_Breakpoint;
|
||||
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || (fifo.CPReadPointer == fifo.CPWritePointer);
|
||||
m_CPStatusReg.ReadIdle = !fifo.CPReadWriteDistance || (fifo.CPReadPointer == fifo.CPWritePointer);
|
||||
m_CPStatusReg.CommandIdle = !fifo.CPReadWriteDistance || AtBreakpoint() || !fifo.bFF_GPReadEnable;
|
||||
m_CPStatusReg.UnderflowLoWatermark = fifo.bFF_LoWatermark;
|
||||
m_CPStatusReg.OverflowHiWatermark = fifo.bFF_HiWatermark;
|
||||
|
|
|
@ -125,6 +125,11 @@ void VideoBackendHardware::Video_EndField()
|
|||
{
|
||||
if (s_BackendInitialized)
|
||||
{
|
||||
// Wait until the GPU thread has swapped. Prevents FIFO overflows.
|
||||
while (g_ActiveConfig.bUseXFB && SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread && s_swapRequested.IsSet())
|
||||
{
|
||||
Common::YieldCPU();
|
||||
}
|
||||
s_swapRequested.Set();
|
||||
}
|
||||
}
|
||||
|
|
1377
docs/gc-font-tool.cpp
Normal file
1377
docs/gc-font-tool.cpp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue