merge latest master changes

This commit is contained in:
Rodolfo Bogado 2015-01-16 15:41:40 -03:00
parent d940e33a50
commit 0c98f9aedb
73 changed files with 6460 additions and 2005 deletions

54
.mailmap Normal file
View 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>

View 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.

View file

@ -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.

View file

@ -368,3 +368,6 @@ EFBCopyEnable = True
[Video_Settings]
FastDepthCalc = False
[Video_Stereoscopy]
StereoConvergenceMinimum = 115

View file

@ -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);
}
}
}

View file

@ -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:

View file

@ -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); }

View file

@ -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

View file

@ -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;

View file

@ -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()

View file

@ -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;

View file

@ -477,7 +477,7 @@ void Idle()
}
}
idledCycles += PowerPC::ppcState.downcount;
idledCycles += DowncountToCycles(PowerPC::ppcState.downcount);
PowerPC::ppcState.downcount = 0;
Advance();

View file

@ -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);

View file

@ -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));

View file

@ -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)
{

View file

@ -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;
}
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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[] =

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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

View file

@ -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;

View file

@ -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();
}

View file

@ -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)

View file

@ -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)

View file

@ -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))
{

View file

@ -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);

View file

@ -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;
}
}

View 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);
}

View file

@ -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);
}
}

View file

@ -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

View 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);
}

View 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);
}

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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);
}

View file

@ -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}},
};

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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

View file

@ -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),
};

View file

@ -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;

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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()

View file

@ -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();

View file

@ -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);
}

View file

@ -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);

View file

@ -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:

View file

@ -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;

View file

@ -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;
};

View file

@ -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());

View file

@ -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,

View file

@ -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())
{

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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

File diff suppressed because it is too large Load diff