Merge pull request #429 from hrydgard/armjit

Arm JIT - merge to master
This commit is contained in:
Henrik Rydgård committed 2013-01-15 23:39:29 -08:00
commit 3135c218f8
71 files changed
+3643 -2435

No files matched your search

+19 -16
View File
@@ -169,11 +169,10 @@ endmacro()
set(CommonExtra)
if(ARM)
set(CommonExtra ${CommonExtra}
Common/ArmABI.h
Common/ArmABI.cpp
Common/ArmCPUDetect.cpp
Common/ArmEmitter.h
Common/ArmEmitter.cpp
Common/ThunkARM.cpp)
Common/ArmThunk.cpp)
elseif(X86)
set(CommonExtra ${CommonExtra}
Common/ABI.cpp
@@ -584,19 +583,20 @@ include_directories(ext/libkirk)
set(CoreExtra)
if(ARM)
set(CoreExtra ${CoreExtra}
Core/MIPS/ARM/Asm.cpp
Core/MIPS/ARM/Asm.h
Core/MIPS/ARM/CompALU.cpp
Core/MIPS/ARM/CompBranch.cpp
Core/MIPS/ARM/CompFPU.cpp
Core/MIPS/ARM/CompLoadStore.cpp
Core/MIPS/ARM/CompVFPU.cpp
Core/MIPS/ARM/Jit.cpp
Core/MIPS/ARM/Jit.h
Core/MIPS/ARM/JitCache.cpp
Core/MIPS/ARM/JitCache.h
Core/MIPS/ARM/RegCache.cpp
Core/MIPS/ARM/RegCache.h)
Core/MIPS/ARM/ArmAsm.cpp
Core/MIPS/ARM/ArmAsm.h
Core/MIPS/ARM/ArmCompALU.cpp
Core/MIPS/ARM/ArmCompBranch.cpp
Core/MIPS/ARM/ArmCompFPU.cpp
Core/MIPS/ARM/ArmCompLoadStore.cpp
Core/MIPS/ARM/ArmCompVFPU.cpp
Core/MIPS/ARM/ArmJit.cpp
Core/MIPS/ARM/ArmJit.h
Core/MIPS/ARM/ArmJitCache.cpp
Core/MIPS/ARM/ArmJitCache.h
Core/MIPS/ARM/ArmRegCache.cpp
Core/MIPS/ARM/ArmRegCache.h
ext/disarm.cpp)
elseif(X86)
set(CoreExtra ${CoreExtra}
Core/MIPS/x86/Asm.cpp
@@ -916,6 +916,9 @@ set(NativeAppSource
android/jni/GamepadEmu.cpp
android/jni/UIShader.cpp
android/jni/ui_atlas.cpp)
if(ANDROID)
set(NativeAppSource ${NativeAppSource} android/jni/ArmEmitterTest.cpp)
endif()
set(NativeAssets
android/assets/ui_atlas.zim
assets/ppge_atlas.zim)
+27 -23
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -23,30 +23,50 @@ using namespace ArmGen;
// If passing arguments, don't use this.
void ARMXEmitter::ARMABI_CallFunction(void *func)
{
ARMABI_MOVI2R(R14, Mem(func));
PUSH(5, R0, R1, R2, R3, _LR);
ARMABI_MOVI2R(R14, (u32)func);
BL(R14);
POP(5, R0, R1, R2, R3, _LR);
}
void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg)
{
ARMABI_MOVI2R(R14, Mem(func));
PUSH(5, R0, R1, R2, R3, _LR);
ARMABI_MOVI2R(R14, (u32)func);
ARMABI_MOVI2R(R0, Arg);
BL(R14);
POP(5, R0, R1, R2, R3, _LR);
}
void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg)
{
PUSH(1, _LR);
ARMABI_MOVI2R(R14, (u32)func);
ARMABI_MOVI2R(R0, Arg);
BL(R14);
POP(1, _LR);
}
void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2)
{
ARMABI_MOVI2R(R14, Mem(func));
PUSH(5, R0, R1, R2, R3, _LR);
ARMABI_MOVI2R(R14, (u32)func);
ARMABI_MOVI2R(R0, Arg1);
ARMABI_MOVI2R(R1, Arg2);
BL(R14);
POP(5, R0, R1, R2, R3, _LR);
}
void ARMXEmitter::ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3)
{
PUSH(5, R0, R1, R2, R3, _LR);
ARMABI_MOVI2R(R14, (u32)func);
ARMABI_MOVI2R(R0, Arg1);
ARMABI_MOVI2R(R1, Arg2);
ARMABI_MOVI2R(R2, Arg3);
BL(R14);
POP(5, R0, R1, R2, R3, _LR);
}
void ARMXEmitter::ARMABI_PushAllCalleeSavedRegsAndAdjustStack() {
// Note: 4 * 4 = 16 bytes, so alignment is preserved.
PUSH(4, R0, R1, R2, R3);
@@ -55,23 +75,7 @@ void ARMXEmitter::ARMABI_PushAllCalleeSavedRegsAndAdjustStack() {
void ARMXEmitter::ARMABI_PopAllCalleeSavedRegsAndAdjustStack() {
POP(4, R0, R1, R2, R3);
}
void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, Operand2 val)
{
// TODO: There are more fancy ways to save calls if we check if
// The imm can be rotated or shifted a certain way.
// Masks tend to be able to be moved in to a reg with one call
MOVW(reg, val);
if(val.Value & 0xFFFF0000)
MOVT(reg, val, true);
}
// Moves IMM to memory location
void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val)
{
// This moves imm to a memory location
MOVW(R14, val); MOVT(R14, val, true);
MOVW(R12, op); MOVT(R12, op, true);
STR(R12, R14); // R10 is what we want to store
}
const char *conditions[] = {"EQ", "NEQ", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL" };
static void ShowCondition(u32 cond)
{
@@ -102,7 +106,7 @@ void ARMXEmitter::UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval)
// Can't update GE with the other ones with a immediate
// Got to use a scratch register
u32 Imm = (Flags << 27) | ((GEval & 0xF) << 16);
ARMABI_MOVI2R(R14, IMM(Imm));
ARMABI_MOVI2R(R14, Imm);
_MSR(true, true, R14);
}
else
+1 -1
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
+197
View File
@@ -0,0 +1,197 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "CPUDetect.h"
#include "StringUtil.h"
// Only Linux platforms have /proc/cpuinfo
#if !defined(BLACKBERRY) && !defined(IOS) && !defined(__SYMBIAN32__)
const char procfile[] = "/proc/cpuinfo";
char *GetCPUString()
{
const char marker[] = "Hardware\t: ";
char *cpu_string = 0;
// Count the number of processor lines in /proc/cpuinfo
char buf[1024];
FILE *fp;
fp = fopen(procfile, "r");
if (!fp)
return 0;
while (fgets(buf, sizeof(buf), fp))
{
if (strncmp(buf, marker, sizeof(marker) - 1))
continue;
cpu_string = buf + sizeof(marker) - 1;
cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline
// INFO_LOG(BOOT, "CPU: %s", cpu_string);
break;
}
return cpu_string;
}
bool CheckCPUFeature(const char *feature)
{
const char marker[] = "Features\t: ";
char buf[1024];
FILE *fp;
fp = fopen(procfile, "r");
if (!fp)
return 0;
while (fgets(buf, sizeof(buf), fp))
{
if (strncmp(buf, marker, sizeof(marker) - 1))
continue;
char *featurestring = buf + sizeof(marker) - 1;
char *token = strtok(featurestring, " ");
while (token != NULL)
{
if (strstr(token, feature))
return true;
token = strtok(NULL, " ");
}
}
return false;
}
#endif
int GetCoreCount()
{
#ifdef __SYMBIAN32__
return 1;
#elif defined(BLACKBERRY) || defined(IOS)
return 2;
#else
const char marker[] = "processor\t: ";
int cores = 0;
char buf[1024];
FILE *fp;
fp = fopen(procfile, "r");
if (!fp)
return 0;
while (fgets(buf, sizeof(buf), fp))
{
if (strncmp(buf, marker, sizeof(marker) - 1))
continue;
++cores;
}
return cores;
#endif
}
CPUInfo cpu_info;
CPUInfo::CPUInfo() {
Detect();
}
// Detects the various cpu features
void CPUInfo::Detect()
{
// Set some defaults here
// When ARMv8 cpus come out, these need to be updated.
HTT = false;
OS64bit = false;
CPU64bit = false;
Mode64bit = false;
vendor = VENDOR_ARM;
// Get the information about the CPU
num_cores = GetCoreCount();
#if !defined(BLACKBERRY) && !defined(IOS) && !defined(__SYMBIAN32__)
strncpy(cpu_string, GetCPUString(), sizeof(cpu_string));
bSwp = CheckCPUFeature("swp");
bHalf = CheckCPUFeature("half");
bThumb = CheckCPUFeature("thumb");
bFastMult = CheckCPUFeature("fastmult");
bVFP = CheckCPUFeature("vfp");
bEDSP = CheckCPUFeature("edsp");
bThumbEE = CheckCPUFeature("thumbee");
bNEON = CheckCPUFeature("neon");
bVFPv3 = CheckCPUFeature("vfpv3");
bTLS = CheckCPUFeature("tls");
bVFPv4 = CheckCPUFeature("vfpv4");
bIDIVa = CheckCPUFeature("idiva");
bIDIVt = CheckCPUFeature("idivt");
// These two require ARMv8 or higher
bFP = CheckCPUFeature("fp");
bASIMD = CheckCPUFeature("asimd");
#else
// Hardcode this for now
bSwp = true;
bHalf = true;
bThumb = false;
bFastMult = true;
bVFP = true;
bEDSP = true;
#ifdef __SYMBIAN32__
bThumbEE = false;
bNEON = false;
#else
bThumbEE = true;
bNEON = true;
#endif
bVFPv3 = true;
bTLS = true;
bVFPv4 = false;
bIDIVa = false;
bIDIVt = false;
bFP = false;
bASIMD = false;
#endif
// On android, we build a separate library for ARMv7 so this is fine.
// TODO: Check for ARMv7 on other platforms.
#if defined(__ARM_ARCH_7A__)
bArmV7 = true;
#else
bArmV7 = false;
#endif
}
// Turn the cpu info into a string we can show
std::string CPUInfo::Summarize()
{
std::string sum;
#if defined(BLACKBERRY) || defined(IOS) || defined(__SYMBIAN32__)
sum = StringFromFormat("%i cores", num_cores);
#else
if (num_cores == 1)
sum = StringFromFormat("%s, %i core", cpu_string, num_cores);
else
sum = StringFromFormat("%s, %i cores", cpu_string, num_cores);
#endif
if (bSwp) sum += ", SWP";
if (bHalf) sum += ", Half";
if (bThumb) sum += ", Thumb";
if (bFastMult) sum += ", FastMult";
if (bVFP) sum += ", VFP";
if (bEDSP) sum += ", EDSP";
if (bThumbEE) sum += ", ThumbEE";
if (bNEON) sum += ", NEON";
if (bVFPv3) sum += ", VFPv3";
if (bTLS) sum += ", TLS";
if (bVFPv4) sum += ", VFPv4";
if (bIDIVa) sum += ", IDIVa";
if (bIDIVt) sum += ", IDIVt";
return sum;
}
+212 -27
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -34,6 +34,93 @@
namespace ArmGen
{
inline u32 RotR(u32 a, int amount) {
if (!amount) return a;
return (a >> amount) | (a << (32 - amount));
}
inline u32 RotL(u32 a, int amount) {
if (!amount) return a;
return (a << amount) | (a >> (32 - amount));
}
bool TryMakeOperand2(u32 imm, Operand2 &op2) {
// Just brute force it.
for (int i = 0; i < 16; i++) {
int mask = RotR(0xFF, i * 2);
if ((imm & mask) == imm) {
op2 = Operand2((u8)(RotL(imm, i * 2)), (u8)i);
return true;
}
}
return false;
}
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse)
{
if (!TryMakeOperand2(imm, op2)) {
*inverse = true;
return TryMakeOperand2(~imm, op2);
} else {
*inverse = false;
return true;
}
}
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated)
{
if (!TryMakeOperand2(imm, op2)) {
*negated = true;
return TryMakeOperand2(-imm, op2);
} else {
*negated = false;
return true;
}
}
void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, u32 val)
{
Operand2 op2;
bool inverse;
if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) {
if (!inverse)
MOV(reg, op2);
else
MVN(reg, op2);
} else {
if (cpu_info.bArmV7) {
// ARMv7 - can use MOVT/MOVW, best choice
MOVW(reg, val & 0xFFFF);
if(val & 0xFFFF0000)
MOVT(reg, val, true);
} else {
// ARMv6 - fallback sequence.
// TODO: Optimize further. Can for example choose negation etc.
// Literal pools is another way to do this but much more complicated
// so I can't really be bothered for an outdated CPU architecture like ARMv6.
bool first = true;
int shift = 16;
for (int i = 0; i < 4; i++) {
if (val & 0xFF) {
if (first) {
MOV(reg, Operand2((u8)val, (u8)(shift & 0xF)));
first = false;
} else {
ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF)));
}
}
shift -= 4;
val >>= 8;
}
}
}
}
void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) {
ARMABI_MOVI2R(reg, (u32)(func));
BL(reg);
}
void ARMXEmitter::SetCodePtr(u8 *ptr)
{
code = ptr;
@@ -68,17 +155,23 @@ const u8 *ARMXEmitter::AlignCodePage()
return code;
}
void ARMXEmitter::Flush()
void ARMXEmitter::FlushIcache()
{
FlushIcacheSection(lastCacheFlushEnd, code);
lastCacheFlushEnd = code;
}
void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end)
{
#ifdef __SYMBIAN32__
User::IMB_Range( startcode, code );
User::IMB_Range( start, end);
#elif defined(BLACKBERRY)
msync(startcode, code-startcode, MS_SYNC | MS_INVALIDATE_ICACHE);
msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE);
#else
__builtin___clear_cache (startcode, code);
__builtin___clear_cache (start, end);
#endif
SLEEP(0);
}
void ARMXEmitter::SetCC(CCFlags cond)
{
condition = cond << 28;
@@ -181,7 +274,7 @@ void ARMXEmitter::B (const void *fnptr)
void ARMXEmitter::B(ARMReg src)
{
Write32(condition | (18 << 20) | (0xFFF << 8) | (1 << 4) | src);
Write32(condition | 0x12FFF10 | src);
}
void ARMXEmitter::BL(const void *fnptr)
@@ -324,12 +417,28 @@ void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, b
break;
}
}
if (op == -1)
if (op == (u32)-1)
_assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType());
Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data);
}
// Data Operations
void ARMXEmitter::WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2)
{
Write32(condition | (0x7 << 24) | (Op << 20) | (dest << 16) | (Op2 << 12) | (r1 << 8) | (Op3 << 5) | (1 << 4) | r2);
}
void ARMXEmitter::UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
{
if (!cpu_info.bIDIVa)
PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
WriteSignedMultiply(3, 0xF, 0, dest, divisor, dividend);
}
void ARMXEmitter::SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor)
{
if (!cpu_info.bIDIVa)
PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer.");
WriteSignedMultiply(1, 0xF, 0, dest, divisor, dividend);
}
void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, false, dest, src, op2);}
void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);}
void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);}
@@ -361,13 +470,13 @@ void ARMXEmitter::REV (ARMReg dest, ARMReg src )
Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src);
}
void ARMXEmitter::_MSR (bool nzcvq, bool g, Operand2 op2)
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2)
{
Write32(condition | (0x320F << 12) | (nzcvq << 19) | (g << 18) | op2.Imm12Mod());
Write32(condition | (0x320F << 12) | (write_nzcvq << 19) | (write_g << 18) | op2.Imm12Mod());
}
void ARMXEmitter::_MSR (bool nzcvq, bool g, ARMReg src)
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, ARMReg src)
{
Write32(condition | (0x120F << 12) | (nzcvq << 19) | (g << 18) | src);
Write32(condition | (0x120F << 12) | (write_nzcvq << 19) | (write_g << 18) | src);
}
void ARMXEmitter::MRS (ARMReg dest)
{
@@ -404,8 +513,7 @@ void ARMXEmitter::DMB ()
void ARMXEmitter::LDR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x41, src, dest, op);}
void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);}
void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index,
bool Add)
void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add)
{
Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset);
}
@@ -443,20 +551,97 @@ void ARMXEmitter::LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...)
va_end(vl);
WriteRegStoreOp(0x89, dest, WriteBack, RegList);
}
// helper routines for setting pointers
void ARMXEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2)
// NEON and ASIMD
void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, Operand2 op)
{
_assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR");
_assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR");
bool single_reg = Dest < D0;
if (single_reg)
Dest = (ARMReg)(Dest - S0);
else
Dest = (ARMReg)(Dest - D0);
Write32(NO_COND | (13 << 24) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \
| (5 << 9) | (!single_reg << 8) | op.Imm8());
}
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
{
if (Dest > R15)
{
if (Src < S0)
{
if (Dest < D0)
{
// Moving to a Neon register FROM ARM Reg
Dest = (ARMReg)(Dest - S0);
Write32(NO_COND | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \
| (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4));
return;
}
else
{
// Move 64bit from Arm reg
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON");
}
}
}
else
{
if (Src > R15)
{
if (Src < D0)
{
// Moving to ARM Reg from Neon Register
Src = (ARMReg)(Src - S0);
Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \
| (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4));
return;
}
else
{
// Move 64bit To Arm reg
_assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON");
}
}
else
{
// Move Arm reg to Arm reg
_assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers");
}
}
// Moving NEON registers
int SrcSize = Src < D0 ? 1 : Src < Q0 ? 2 : 4;
int DestSize = Dest < D0 ? 1 : Dest < Q0 ? 2 : 4;
bool Single = DestSize == 1;
_assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes");
if (Single)
{
Dest = (ARMReg)(Dest - S0);
Src = (ARMReg)(Src - S0);
Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \
| (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1));
}
else
{
// Double and quad
bool Quad = DestSize == 4;
if (Quad)
{
// Gets encoded as a Double register
Dest = (ARMReg)((Dest - Q0) * 2);
Src = (ARMReg)((Src - Q0) * 2);
}
else
{
Dest = (ARMReg)(Dest - D0);
Src = (ARMReg)(Src - D0);
}
Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (1 << 21) | ((Src & 0xF) << 16) \
| ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (Quad << 6) \
| ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF));
}
}
void ARMXEmitter::CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3)
{
}
void ARMXEmitter::CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
}
void ARMXEmitter::CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5)
{
}
}
+94 -65
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -26,12 +26,14 @@
#include <signal.h>
#endif
#undef _IP
#undef R0
#undef _SP
#undef _LR
#undef _PC
namespace ArmGen
{
enum ARMReg
{
// GPRs
@@ -46,17 +48,21 @@ enum ARMReg
// VFP single precision registers
S0 = 0, S1, S2, S3, S4, S5, S6,
S0, S1, S2, S3, S4, S5, S6,
S7, S8, S9, S10, S11, S12, S13,
S14, S15, S16, S17, S18, S19, S20,
S21, S22, S23, S24, S25, S26, S27,
S28, S29, S30, S31,
// VFP Double Precision registers
D0 = 0, D1, D2, D3, D4, D5, D6, D7,
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,
// ASIMD Quad-Word registers
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
INVALID_REG = 0xFFFFFFFF
};
@@ -77,19 +83,19 @@ enum CCFlags
CC_GT, // Signed greater than
CC_LE, // Signed less than or equal
CC_AL, // Always (unconditional) 14
CC_HS = 2, // Alias of CC_CS
CC_LO = 3, // Alias of CC_CC
CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same
CC_LO = CC_CC, // Alias of CC_CC Unsigned lower
};
const u32 NO_COND = 0xE0000000;
enum ShiftType
{
LSL = 0,
ASL = 0,
LSR = 1,
ASR = 2,
ROR = 3,
RRX = 4
ST_LSL = 0,
ST_ASL = 0,
ST_LSR = 1,
ST_ASR = 2,
ST_ROR = 3,
ST_RRX = 4
};
enum
@@ -108,6 +114,7 @@ enum OpType
TYPE_MEM
};
// This is no longer a proper operand2 class. Need to split up.
class Operand2
{
friend class ARMXEmitter;
@@ -151,7 +158,7 @@ public:
Operand2(ARMReg base, ShiftType type, ARMReg shift) // RSR
{
Type = TYPE_RSR;
_assert_msg_(DYNA_REC, type != RRX, "Invalid Operand2: RRX does not take a register shift amount");
_assert_msg_(DYNA_REC, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount");
IndexOrShift = shift;
Shift = type;
Value = base;
@@ -162,31 +169,31 @@ public:
if(shift == 32) shift = 0;
switch (type)
{
case LSL:
case ST_LSL:
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSL %u", shift);
break;
case LSR:
case ST_LSR:
_assert_msg_(DYNA_REC, shift <= 32, "Invalid Operand2: LSR %u", shift);
if (!shift)
type = LSL;
type = ST_LSL;
if (shift == 32)
shift = 0;
break;
case ASR:
case ST_ASR:
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSR %u", shift);
if (!shift)
type = LSL;
type = ST_LSL;
if (shift == 32)
shift = 0;
break;
case ROR:
case ST_ROR:
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: ROR %u", shift);
if (!shift)
type = LSL;
type = ST_LSL;
break;
case RRX:
case ST_RRX:
_assert_msg_(DYNA_REC, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount");
type = ROR;
type = ST_ROR;
break;
}
IndexOrShift = shift;
@@ -198,17 +205,17 @@ public:
{
switch(Type)
{
case TYPE_IMM:
case TYPE_IMM:
return Imm12Mod(); // This'll need to be changed later
case TYPE_REG:
case TYPE_REG:
return Rm();
case TYPE_IMMSREG:
case TYPE_IMMSREG:
return IMMSR();
case TYPE_RSR:
case TYPE_RSR:
return RSR();
default:
_assert_msg_(DYNA_REC, false, "GetData with Invalid Type");
break;
default:
_assert_msg_(DYNA_REC, false, "GetData with Invalid Type");
return 0;
}
}
const u32 IMMSR() // IMM shifted register
@@ -232,6 +239,11 @@ public:
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm5 not IMM value");
return ((Value & 0x0000001F) << 7);
}
const u32 Imm8()
{
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
return Value & 0xFF;
}
const u32 Imm8Rot() // IMM8 with Rotation
{
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
@@ -273,16 +285,30 @@ public:
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
return (Value & 0x0FFFFFFF);
}
// NEON and ASIMD specific
const u32 Imm8ASIMD()
{
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8ASIMD not IMM");
return ((Value & 0x80) << 17) | ((Value & 0x70) << 12) | (Value & 0xF);
}
const u32 Imm8VFP()
{
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8VFP not IMM");
return ((Value & 0xF0) << 12) | (Value & 0xF);
}
};
// Use these when you don't know if an imm can be represented as an operand2.
// This lets you generate both an optimal and a fallback solution by checking
// the return value, which will be false if these fail to find a Operand2 that
// represents your 32-bit imm value.
bool TryMakeOperand2(u32 imm, Operand2 &op2);
bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse);
bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated);
inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); }
inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); }
inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); }
//usage: int a[]; ARRAY_OFFSET(a,10)
#define ARRAY_OFFSET(array,index) ((u32)((u64)&(array)[index]-(u64)&(array)[0]))
//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
#define STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str)))
struct FixupBranch
{
@@ -298,12 +324,14 @@ class ARMXEmitter
friend struct OpArg; // for Write8 etc
private:
u8 *code, *startcode;
u8 *lastCacheFlushEnd;
u32 condition;
void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2);
void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList);
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2);
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2);
void WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2);
// New Ops
void WriteInstruction(u32 op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags = false);
@@ -312,8 +340,15 @@ protected:
inline void Write32(u32 value) {*(u32*)code = value; code+=4;}
public:
ARMXEmitter() { code = NULL; startcode = NULL; condition = CC_AL << 28;}
ARMXEmitter(u8 *code_ptr) { code = code_ptr; startcode = code_ptr; condition = CC_AL << 28;}
ARMXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) {
condition = CC_AL << 28;
}
ARMXEmitter(u8 *code_ptr) {
code = code_ptr;
lastCacheFlushEnd = code_ptr;
startcode = code_ptr;
condition = CC_AL << 28;
}
virtual ~ARMXEmitter() {}
void SetCodePtr(u8 *ptr);
@@ -321,7 +356,8 @@ public:
const u8 *AlignCode16();
const u8 *AlignCodePage();
const u8 *GetCodePtr() const;
void Flush();
void FlushIcache();
void FlushIcacheSection(u8 *start, u8 *end);
u8 *GetWritableCodePtr();
void SetCC(CCFlags cond = CC_AL);
@@ -378,7 +414,7 @@ public:
void LSLS(ARMReg dest, ARMReg src, ARMReg op2);
void SBC (ARMReg dest, ARMReg src, Operand2 op2);
void SBCS(ARMReg dest, ARMReg src, Operand2 op2);
void REV (ARMReg dest, ARMReg src );
void REV (ARMReg dest, ARMReg src);
void RSC (ARMReg dest, ARMReg src, Operand2 op2);
void RSCS(ARMReg dest, ARMReg src, Operand2 op2);
void TST ( ARMReg src, Operand2 op2);
@@ -389,22 +425,26 @@ public:
void ORRS(ARMReg dest, ARMReg src, Operand2 op2);
void MOV (ARMReg dest, Operand2 op2);
void MOVS(ARMReg dest, Operand2 op2);
void BIC (ARMReg dest, ARMReg src, Operand2 op2);
void BIC (ARMReg dest, ARMReg src, Operand2 op2); // BIC = ANDN
void BICS(ARMReg dest, ARMReg src, Operand2 op2);
void MVN (ARMReg dest, Operand2 op2);
void MVNS(ARMReg dest, Operand2 op2);
void MOVW(ARMReg dest, Operand2 op2);
void MOVT(ARMReg dest, Operand2 op2, bool TopBits = false);
// UDIV and SDIV are only available on CPUs that have
// the idiva hardare capacity
void UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
void SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
void MUL (ARMReg dest, ARMReg src, ARMReg op2);
void MULS(ARMReg dest, ARMReg src, ARMReg op2);
void SXTB(ARMReg dest, ARMReg op2);
void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0);
void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0);
// Using just MSR here messes with our defines on the PPC side of stuff
// Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...)
// Just need to put an underscore here, bit annoying.
void _MSR (bool nzcvq, bool g, Operand2 op2);
void _MSR (bool nzcvq, bool g, Operand2 op2);
void _MSR (bool nzcvq, bool g, ARMReg src );
void MRS (ARMReg dest);
@@ -426,34 +466,23 @@ public:
// dest contains the result if the instruction managed to store the value
void STREX(ARMReg dest, ARMReg base, ARMReg op);
void DMB ();
// NEON and ASIMD instructions
// None of these will be created with conditional since ARM
// is deprecating conditional execution of ASIMD instructions.
// Some ASIMD instructions don't even have a conditional encoding.
void VLDR(ARMReg dest, ARMReg Base, Operand2 op);
void VMOV(ARMReg Dest, ARMReg Src);
void QuickCallFunction(ARMReg scratchreg, void *func);
// Utility functions
// The difference between this and CALL is that this aligns the stack
// where appropriate.
void ARMABI_CallFunction(void *func);
void ARMABI_CallFunctionC(void *func, u32 Arg0);
void ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2);
void ARMABI_PushAllCalleeSavedRegsAndAdjustStack();
void ARMABI_PopAllCalleeSavedRegsAndAdjustStack();
void ARMABI_MOVI2R(ARMReg reg, Operand2 val);
void ARMABI_MOVI2M(Operand2 op, Operand2 val);
void ARMABI_MOVI2R(ARMReg reg, u32 val);
void ARMABI_ShowConditions();
void ARMABI_Return();
void UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval);
// Strange call wrappers.
void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2);
void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3);
void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4);
void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5);
#define CallCdeclFunction3_I(a,b,c,d) CallCdeclFunction3((void *)(a), (b), (c), (d))
#define CallCdeclFunction4_I(a,b,c,d,e) CallCdeclFunction4((void *)(a), (b), (c), (d), (e))
#define CallCdeclFunction5_I(a,b,c,d,e,f) CallCdeclFunction5((void *)(a), (b), (c), (d), (e), (f))
#define CallCdeclFunction6_I(a,b,c,d,e,f,g) CallCdeclFunction6((void *)(a), (b), (c), (d), (e), (f), (g))
#define DECLARE_IMPORT(x)
}; // class ARMXEmitter
+1 -1
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
+6 -6
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -29,7 +29,9 @@
#undef _interlockedbittestandreset64
#else
#ifndef _M_GENERIC
#include <xmmintrin.h>
#endif
#if defined __FreeBSD__
#include <sys/types.h>
@@ -38,7 +40,9 @@
static inline void do_cpuid(unsigned int *eax, unsigned int *ebx,
unsigned int *ecx, unsigned int *edx)
{
#ifdef _LP64
#if defined _M_GENERIC
(*eax) = (*ebx) = (*ecx) = (*edx) = 0;
#elif defined _LP64
// Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to
// restored at the end of the asm block.
__asm__ (
@@ -92,10 +96,6 @@ CPUInfo::CPUInfo() {
Detect();
}
#ifdef _WIN32
#include <windows.h>
#endif
// Detects the various cpu features
void CPUInfo::Detect()
{
+23 -2
View File
@@ -2,7 +2,7 @@
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -25,7 +25,8 @@ enum CPUVendor
{
VENDOR_INTEL = 0,
VENDOR_AMD = 1,
VENDOR_OTHER = 2,
VENDOR_ARM = 2,
VENDOR_OTHER = 3,
};
struct CPUInfo
@@ -55,6 +56,26 @@ struct CPUInfo
bool bAES;
bool bLAHFSAHF64;
bool bLongMode;
// ARM specific CPUInfo
bool bSwp;
bool bHalf;
bool bThumb;
bool bFastMult;
bool bVFP;
bool bEDSP;
bool bThumbEE;
bool bNEON;
bool bVFPv3;
bool bTLS;
bool bVFPv4;
bool bIDIVa;
bool bIDIVt;
bool bArmV7; // enable MOVT, MOVW etc
// ARMv8 specific
bool bFP;
bool bASIMD;
// Call Detect()
explicit CPUInfo();
+1 -7
View File
@@ -140,12 +140,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ABI.h" />
<ClInclude Include="ArmABI.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="ArmEmitter.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -195,7 +189,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
<ClCompile Include="ArmABI.cpp">
<ClCompile Include="ArmCPUDetect.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+1 -2
View File
@@ -42,7 +42,6 @@
<ClInclude Include="x64Analyzer.h" />
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="ArmEmitter.h" />
<ClInclude Include="ArmABI.h" />
<ClInclude Include="FixedSizeUnorderedSet.h" />
</ItemGroup>
<ItemGroup>
@@ -70,8 +69,8 @@
<ClCompile Include="x64Analyzer.cpp" />
<ClCompile Include="x64Emitter.cpp" />
<ClCompile Include="ArmEmitter.cpp" />
<ClCompile Include="ArmABI.cpp" />
<ClCompile Include="ThunkArm.cpp" />
<ClCompile Include="ArmCPUDetect.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
+2
View File
@@ -152,6 +152,8 @@ void LogManager::LoadConfig(IniFile::Section *section)
void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char *file, int line, const char *format, va_list args)
{
std::lock_guard<std::mutex> lk(m_log_lock);
char temp[MAX_MSGLEN];
char msg[MAX_MSGLEN * 2];
LogContainer *log = m_Log[type];
+1
View File
@@ -106,6 +106,7 @@ private:
ConsoleListener *m_consoleLog;
DebuggerLogListener *m_debuggerLog;
static LogManager *m_logManager; // Singleton. Ugh.
std::mutex m_log_lock;
LogManager();
~LogManager();
+3 -3
View File
@@ -243,13 +243,13 @@ u8* MemArena::Find4GBBase()
// We are unable to use relative addresses due to lack of mmap()
return NULL;
#else
void* base = mmap(0, 0x5000000, PROT_READ | PROT_WRITE,
void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
if (base == MAP_FAILED) {
PanicAlert("Failed to map 100 MB of memory space: %s", strerror(errno));
PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno));
return 0;
}
munmap(base, 0x5000000);
munmap(base, 0x10000000);
return static_cast<u8*>(base);
#endif
#endif
+1 -179
View File
@@ -1465,184 +1465,6 @@ void XEmitter::FWAIT()
}
void XEmitter::RTDSC() { Write8(0x0F); Write8(0x31); }
// helper routines for setting pointers
void XEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2)
{
using namespace Gen;
#ifdef _M_X64
#ifdef _MSC_VER
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
CALL(fnptr);
#else
MOV(32, R(RDI), Imm32(arg0));
MOV(32, R(RSI), Imm32(arg1));
MOV(32, R(RDX), Imm32(arg2));
CALL(fnptr);
#endif
} // Gen
#else
ABI_AlignStack(3 * 4);
PUSH(32, Imm32(arg2));
PUSH(32, Imm32(arg1));
PUSH(32, Imm32(arg0));
CALL(fnptr);
#ifdef _WIN32
// don't inc stack
#else
ABI_RestoreStack(3 * 4);
#endif
#endif
}
void XEmitter::CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3)
{
using namespace Gen;
#ifdef _M_X64
#ifdef _MSC_VER
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
CALL(fnptr);
#else
MOV(32, R(RDI), Imm32(arg0));
MOV(32, R(RSI), Imm32(arg1));
MOV(32, R(RDX), Imm32(arg2));
MOV(32, R(RCX), Imm32(arg3));
CALL(fnptr);
#endif
#else
ABI_AlignStack(4 * 4);
PUSH(32, Imm32(arg3));
PUSH(32, Imm32(arg2));
PUSH(32, Imm32(arg1));
PUSH(32, Imm32(arg0));
CALL(fnptr);
#ifdef _WIN32
// don't inc stack
#else
ABI_RestoreStack(4 * 4);
#endif
#endif
}
void XEmitter::CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
{
using namespace Gen;
#ifdef _M_X64
#ifdef _MSC_VER
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
MOV(32, MDisp(RSP, 0x20), Imm32(arg4));
CALL(fnptr);
#else
MOV(32, R(RDI), Imm32(arg0));
MOV(32, R(RSI), Imm32(arg1));
MOV(32, R(RDX), Imm32(arg2));
MOV(32, R(RCX), Imm32(arg3));
MOV(32, R(R8), Imm32(arg4));
CALL(fnptr);
#endif
#else
ABI_AlignStack(5 * 4);
PUSH(32, Imm32(arg4));
PUSH(32, Imm32(arg3));
PUSH(32, Imm32(arg2));
PUSH(32, Imm32(arg1));
PUSH(32, Imm32(arg0));
CALL(fnptr);
#ifdef _WIN32
// don't inc stack
#else
ABI_RestoreStack(5 * 4);
#endif
#endif
}
void XEmitter::CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5)
{
using namespace Gen;
#ifdef _M_X64
#ifdef _MSC_VER
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
MOV(32, MDisp(RSP, 0x20), Imm32(arg4));
MOV(32, MDisp(RSP, 0x28), Imm32(arg5));
CALL(fnptr);
#else
MOV(32, R(RDI), Imm32(arg0));
MOV(32, R(RSI), Imm32(arg1));
MOV(32, R(RDX), Imm32(arg2));
MOV(32, R(RCX), Imm32(arg3));
MOV(32, R(R8), Imm32(arg4));
MOV(32, R(R9), Imm32(arg5));
CALL(fnptr);
#endif
#else
ABI_AlignStack(6 * 4);
PUSH(32, Imm32(arg5));
PUSH(32, Imm32(arg4));
PUSH(32, Imm32(arg3));
PUSH(32, Imm32(arg2));
PUSH(32, Imm32(arg1));
PUSH(32, Imm32(arg0));
CALL(fnptr);
#ifdef _WIN32
// don't inc stack
#else
ABI_RestoreStack(6 * 4);
#endif
#endif
}
#ifdef _M_X64
// See header
void XEmitter::___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2) {
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
CALLptr(M(impptr));
}
void XEmitter::___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) {
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
CALLptr(M(impptr));
}
void XEmitter::___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) {
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
MOV(32, MDisp(RSP, 0x20), Imm32(arg4));
CALLptr(M(impptr));
}
void XEmitter::___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) {
MOV(32, R(RCX), Imm32(arg0));
MOV(32, R(RDX), Imm32(arg1));
MOV(32, R(R8), Imm32(arg2));
MOV(32, R(R9), Imm32(arg3));
MOV(32, MDisp(RSP, 0x20), Imm32(arg4));
MOV(32, MDisp(RSP, 0x28), Imm32(arg5));
CALLptr(M(impptr));
}
#endif
}
-37
View File
@@ -687,43 +687,6 @@ public:
#else
inline int ABI_GetNumXMMRegs() { return 16; }
#endif
// Strange call wrappers.
void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2);
void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3);
void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4);
void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5);
#if defined(_M_IX86)
#define CallCdeclFunction3_I(a,b,c,d) CallCdeclFunction3((void *)(a), (b), (c), (d))
#define CallCdeclFunction4_I(a,b,c,d,e) CallCdeclFunction4((void *)(a), (b), (c), (d), (e))
#define CallCdeclFunction5_I(a,b,c,d,e,f) CallCdeclFunction5((void *)(a), (b), (c), (d), (e), (f))
#define CallCdeclFunction6_I(a,b,c,d,e,f,g) CallCdeclFunction6((void *)(a), (b), (c), (d), (e), (f), (g))
#define DECLARE_IMPORT(x)
#else
// Comments from VertexLoader.cpp about these horrors:
// This is a horrible hack that is necessary in 64-bit mode because Opengl32.dll is based way, way above the 32-bit
// address space that is within reach of a CALL, and just doing &fn gives us these high uncallable addresses. So we
// want to grab the function pointers from the import table instead.
void ___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2);
void ___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3);
void ___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4);
void ___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5);
#define CallCdeclFunction3_I(a,b,c,d) ___CallCdeclImport3(&__imp_##a,b,c,d)
#define CallCdeclFunction4_I(a,b,c,d,e) ___CallCdeclImport4(&__imp_##a,b,c,d,e)
#define CallCdeclFunction5_I(a,b,c,d,e,f) ___CallCdeclImport5(&__imp_##a,b,c,d,e,f)
#define CallCdeclFunction6_I(a,b,c,d,e,f,g) ___CallCdeclImport6(&__imp_##a,b,c,d,e,f,g)
#define DECLARE_IMPORT(x) extern "C" void *__imp_##x
#endif
}; // class XEmitter
+1 -1
View File
@@ -20,6 +20,6 @@
// See the MIPS folder, the real stuff is there.
CPU *currentCPU = 0;
MIPSState *currentCPU = 0;
int numCPUs;
+2 -17
View File
@@ -28,24 +28,9 @@ enum
GPR_SIZE_64=1
};
enum CPUType
{
CPUTYPE_INVALID=0,
CPUTYPE_ALLEGREX=1,
CPUTYPE_ME=2,
};
class CPU
{
private:
u32 id;
public:
virtual void SingleStep() = 0;
virtual int RunLoopUntil(u64 globalTicks) = 0;
};
#define MAX_NUM_CPU 2
class MIPSState;
extern CPU *currentCPU;
extern MIPSState *currentCPU;
extern int numCPUs;
+21 -13
View File
@@ -116,6 +116,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ext\disarm.cpp" />
<ClCompile Include="..\ext\snappy\snappy-c.cpp" />
<ClCompile Include="..\ext\snappy\snappy.cpp" />
<ClCompile Include="Config.cpp" />
@@ -185,49 +186,55 @@
<ClCompile Include="Loaders.cpp" />
<ClCompile Include="MemMap.cpp" />
<ClCompile Include="MemmapFunctions.cpp" />
<ClCompile Include="MIPS\ARM\Asm.cpp">
<ClCompile Include="MIPS\ARM\ArmAsm.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompALU.cpp">
<ClCompile Include="MIPS\ARM\ArmCompALU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompBranch.cpp">
<ClCompile Include="MIPS\ARM\ArmCompBranch.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompFPU.cpp">
<ClCompile Include="MIPS\ARM\ArmCompFPU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompLoadStore.cpp">
<ClCompile Include="MIPS\ARM\ArmCompLoadStore.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\Jit.cpp">
<ClCompile Include="MIPS\ARM\ArmCompVFPU.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\JitCache.cpp">
<ClCompile Include="MIPS\ARM\ArmJit.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\RegCache.cpp">
<ClCompile Include="MIPS\ARM\ArmJitCache.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmRegCache.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -262,6 +269,7 @@
<ClCompile Include="Util\ppge_atlas.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Ext\disarm.h" />
<ClInclude Include="..\ext\snappy\snappy-internal.h" />
<ClInclude Include="..\ext\snappy\snappy-sinksource.h" />
<ClInclude Include="..\ext\snappy\snappy-stubs-internal.h" />
@@ -338,25 +346,25 @@
<ClInclude Include="HW\MemoryStick.h" />
<ClInclude Include="Loaders.h" />
<ClInclude Include="MemMap.h" />
<ClInclude Include="MIPS\ARM\Asm.h">
<ClInclude Include="MIPS\ARM\ArmAsm.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="MIPS\ARM\Jit.h">
<ClInclude Include="MIPS\ARM\ArmJit.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="MIPS\ARM\JitCache.h">
<ClInclude Include="MIPS\ARM\ArmJitCache.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="MIPS\ARM\RegCache.h">
<ClInclude Include="MIPS\ARM\ArmRegCache.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -409,4 +417,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>
+46 -37
View File
@@ -111,30 +111,6 @@
<ClCompile Include="MIPS\x86\CompVFPU.cpp">
<Filter>MIPS\x86</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\Asm.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompALU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompBranch.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompFPU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\Jit.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\CompLoadStore.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\JitCache.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\RegCache.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\JitCommon\JitCommon.cpp">
<Filter>MIPS\JitCommon</Filter>
</ClCompile>
@@ -363,6 +339,36 @@
<ClCompile Include="..\ext\snappy\snappy.cpp">
<Filter>Ext\Snappy</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmAsm.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompALU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompBranch.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompFPU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompLoadStore.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmJit.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmJitCache.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmRegCache.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="MIPS\ARM\ArmCompVFPU.cpp">
<Filter>MIPS\ARM</Filter>
</ClCompile>
<ClCompile Include="..\ext\disarm.cpp">
<Filter>Ext</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@@ -413,18 +419,6 @@
<ClInclude Include="MIPS\x86\RegCache.h">
<Filter>MIPS\x86</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\Asm.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\Jit.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\JitCache.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\RegCache.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\JitCommon\JitCommon.h">
<Filter>MIPS\JitCommon</Filter>
</ClInclude>
@@ -674,6 +668,21 @@
<ClInclude Include="..\ext\snappy\snappy-internal.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmAsm.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmJit.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmJitCache.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="MIPS\ARM\ArmRegCache.h">
<Filter>MIPS\ARM</Filter>
</ClInclude>
<ClInclude Include="..\Ext\disarm.h">
<Filter>Ext</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
@@ -681,4 +690,4 @@
<None Include="..\android\jni\Android.mk" />
<None Include="GameLogNotes.txt" />
</ItemGroup>
</Project>
</Project>
+15 -14
View File
@@ -67,7 +67,9 @@ Event *eventPool = 0;
Event *eventTsPool = 0;
int allocatedTsEvents = 0;
int downcount, slicelength;
// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
// as we can already reach that structure through a register.
int slicelength;
s64 globalTimer;
s64 idledCycles;
@@ -153,7 +155,7 @@ void UnregisterAllEvents()
void Init()
{
downcount = INITIAL_SLICE_LENGTH;
currentMIPS->downcount = INITIAL_SLICE_LENGTH;
slicelength = INITIAL_SLICE_LENGTH;
globalTimer = 0;
idledCycles = 0;
@@ -183,7 +185,7 @@ void Shutdown()
u64 GetTicks()
{
return (u64)globalTimer + slicelength - downcount;
return (u64)globalTimer + slicelength - currentMIPS->downcount;
}
u64 GetIdleTicks()
@@ -462,23 +464,23 @@ void MoveEvents()
void Advance()
{
int cyclesExecuted = slicelength - downcount;
int cyclesExecuted = slicelength - currentMIPS->downcount;
globalTimer += cyclesExecuted;
downcount = slicelength;
currentMIPS->downcount = slicelength;
ProcessFifoWaitEvents();
if (!first)
{
// WARN_LOG(CPU, "WARNING - no events in queue. Setting downcount to 10000");
downcount += 10000;
// WARN_LOG(CPU, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
currentMIPS->downcount += 10000;
}
else
{
slicelength = (int)(first->time - globalTimer);
if (slicelength > MAX_SLICE_LENGTH)
slicelength = MAX_SLICE_LENGTH;
downcount = slicelength;
currentMIPS->downcount = slicelength;
}
if (advanceCallback)
advanceCallback(cyclesExecuted);
@@ -496,13 +498,13 @@ void LogPendingEvents()
void Idle(int maxIdle)
{
int cyclesDown = downcount;
int cyclesDown = currentMIPS->downcount;
if (maxIdle != 0 && cyclesDown > maxIdle)
cyclesDown = maxIdle;
if (first && cyclesDown > 0)
{
int cyclesExecuted = slicelength - downcount;
int cyclesExecuted = slicelength - currentMIPS->downcount;
int cyclesNextEvent = (int) (first->time - globalTimer);
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
@@ -517,9 +519,9 @@ void Idle(int maxIdle)
DEBUG_LOG(CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f));
idledCycles += cyclesDown;
downcount -= cyclesDown;
if (downcount == 0)
downcount = -1;
currentMIPS->downcount -= cyclesDown;
if (currentMIPS->downcount == 0)
currentMIPS->downcount = -1;
}
std::string GetScheduledEventsSummary()
@@ -561,7 +563,6 @@ void DoState(PointerWrap &p)
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
p.Do(CPU_HZ);
p.Do(downcount);
p.Do(slicelength);
p.Do(globalTimer);
p.Do(idledCycles);
-1
View File
@@ -119,7 +119,6 @@ namespace CoreTiming
void SetClockFrequencyMHz(int cpuMhz);
int GetClockFrequencyMHz();
extern int downcount;
extern int slicelength;
}; // end of namespace
+1 -1
View File
@@ -2084,7 +2084,7 @@ ActionAfterMipsCall *Thread::getRunningCallbackAction()
if (this->GetUID() == currentThread && g_inCbCount > 0)
{
MipsCall *call = mipsCalls.get(this->currentCallbackId);
ActionAfterMipsCall *action;
ActionAfterMipsCall *action = 0;
if (call)
action = dynamic_cast<ActionAfterMipsCall *>(call->doAfter);
+3 -2
View File
@@ -782,7 +782,7 @@ u32 sceMpegUnRegistStream(u32 mpeg, int streamUid)
return -1;
}
StreamInfo info;
StreamInfo info = {0};
switch (info.type) {
case MPEG_AVC_STREAM:
@@ -1119,7 +1119,8 @@ u32 sceMpegChangeGetAuMode(u32 mpeg, int streamUid, int mode)
return -1;
}
StreamInfo info;
// NOTE: Where is the info supposed to come from?
StreamInfo info = {0};
info.sid = streamUid;
if (info.sid) {
switch (info.type) {
+184
View File
@@ -0,0 +1,184 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ArmEmitter.h"
#include "../../MemMap.h"
#include "../MIPS.h"
#include "../../CoreTiming.h"
#include "MemoryUtil.h"
#include "ArmJit.h"
#include "../JitCommon/JitCommon.h"
#include "../../Core.h"
#include "ArmAsm.h"
using namespace ArmGen;
//static int temp32; // unused?
//TODO - make an option
//#if _DEBUG
static bool enableDebug = false;
//#else
// bool enableDebug = false;
//#endif
//static bool enableStatistics = false; //unused?
//The standard ARM calling convention allocates the 16 ARM registers as:
// r15 is the program counter.
// r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register).
// r13 is the stack pointer. (The Push/Pop instructions in "Thumb" operating mode use this register only).
// r12 is the Intra-Procedure-call scratch register.
// r4 to r11: used to hold local variables.
// r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine.
// R2, R3, R4, R5, R6, R7, R8, R10, R11 // omitting R9?
// STATIC ALLOCATION ARM:
// R11 : Memory base pointer.
// R10 : MIPS state
// R9 :
extern volatile CoreState coreState;
void JitAt()
{
MIPSComp::jit->Compile(currentMIPS->pc);
}
/*
double testD;
u32 DoubleTest(u32 sp) {
volatile double local = 1.0;
testD += local;
return (u32)(&local);
}
void ShowPC(u32 sp) {
if (currentMIPS) {
ERROR_LOG(HLE, "ShowPC : %08x ArmSP : %08x", currentMIPS->pc, sp);
} else {
ERROR_LOG(HLE, "Universe corrupt?");
}
}*/
void DisassembleArm(const u8 *data, int size);
// PLAN: no more block numbers - crazy opcodes just contain offset within
// dynarec buffer
// At this offset - 4, there is an int specifying the block number.
namespace MIPSComp {
void Jit::GenerateFixedCode()
{
enterCode = AlignCode16();
INFO_LOG(HLE, "Base: %08x", (u32)Memory::base);
SetCC(CC_AL);
PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, _LR);
// Take care to 8-byte align stack for function calls.
// This actually misaligns the stack within the JIT itself but that doesn't really matter
// as the JIT does not use the stack at all.
SUB(_SP, _SP, 4);
// Fixed registers, these are always kept when in Jit context.
// R13 cannot be used as it's the stack pointer.
ARMABI_MOVI2R(R11, (u32)Memory::base);
ARMABI_MOVI2R(R10, (u32)mips_);
ARMABI_MOVI2R(R9, (u32)GetBlockCache()->GetCodePointers());
outerLoop = GetCodePtr();
QuickCallFunction(R0, (void *)&CoreTiming::Advance);
FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time
dispatcherCheckCoreState = GetCodePtr();
ARMABI_MOVI2R(R0, (u32)&coreState);
LDR(R0, R0);
CMP(R0, 0);
FixupBranch badCoreState = B_CC(CC_NEQ);
FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time
// At this point : flags = EQ. Fine for the next check, no need to jump over it.
dispatcher = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
FixupBranch bail = B_CC(CC_MI);
SetJumpTarget(skipToRealDispatch);
SetJumpTarget(skipToRealDispatch2);
dispatcherNoCheck = GetCodePtr();
// Debug
// MOV(R0, R13);
// QuickCallFunction(R1, (void *)&ShowPC);
LDR(R0, R10, offsetof(MIPSState, pc));
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
LDR(R0, R11, R0, true, true);
AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments.
BIC(R0, R0, Operand2(0xFC, 4));
CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4));
SetCC(CC_EQ);
// IDEA - we have 26 bits, why not just use offsets from base of code?
// Another idea: Shift the bloc number left by two in the op, this would let us do
// LDR(R0, R9, R0, true, true); here, replacing the two next instructions.
ADD(R0, R9, Operand2(2, ST_LSL, R0));
LDR(R0, R0);
B(R0);
SetCC(CC_AL);
//Ok, no block, let's jit
QuickCallFunction(R2, (void *)&JitAt);
B(dispatcherNoCheck); // no point in special casing this
SetJumpTarget(bail);
ARMABI_MOVI2R(R0, (u32)&coreState);
LDR(R0, R0);
CMP(R0, 0);
B_CC(CC_EQ, outerLoop);
SetJumpTarget(badCoreState);
breakpointBailout = GetCodePtr();
ADD(_SP, _SP, 4);
POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns
// Uncomment if you want to see the output...
// INFO_LOG(HLE, "THE DISASM ========================");
// DisassembleArm(enterCode, GetCodePtr() - enterCode);
// INFO_LOG(HLE, "END OF THE DISASM ========================");
// Don't forget to zap the instruction cache!
FlushIcache();
}
} // namespace
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#ifndef _JIT64ASM_H
#define _JIT64ASM_H
#include <ArmEmitter.h>
#include "../MIPS.h"
// Runtime generated assembly routines, like the Dispatcher.
#endif // _JIT64ASM_H
@@ -15,9 +15,9 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Jit.h"
#include "RegCache.h"
#include <ArmEmitter.h>
#include "ArmJit.h"
#include "ArmRegCache.h"
#include "ArmEmitter.h"
using namespace MIPSAnalyst;
#define _RS ((op>>21) & 0x1F)
@@ -34,28 +34,31 @@ using namespace MIPSAnalyst;
namespace MIPSComp
{
/*
void Jit::CompImmLogic(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &))
{
u32 uimm = (u16)(op & 0xFFFF);
int rt = _RT;
int rs = _RS;
gpr.Lock(rt, rs);
gpr.BindToRegister(rt, rt == rs, true);
if (rt != rs)
MOV(32, gpr.R(rt), gpr.R(rs));
(this->*arith)(32, gpr.R(rt), Imm32(uimm));
gpr.UnlockAll();
static u32 EvalOr(u32 a, u32 b) { return a | b; }
static u32 EvalXor(u32 a, u32 b) { return a ^ b; }
static u32 EvalAnd(u32 a, u32 b) { return a & b; }
void Jit::CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b))
{
if (gpr.IsImm(rs)) {
gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm));
} else {
gpr.MapDirtyIn(rt, rs);
// TODO: Special case when uimm can be represented as an Operand2
Operand2 op2;
if (TryMakeOperand2(uimm, op2)) {
(this->*arith)(gpr.R(rt), gpr.R(rs), op2);
} else {
ARMABI_MOVI2R(R0, (u32)uimm);
(this->*arith)(gpr.R(rt), gpr.R(rs), R0);
}
}
}
*/
void Jit::Comp_IType(u32 op)
{
OLDD
/*
s32 simm = (s16)(op & 0xFFFF);
u32 uimm = (u16)(op & 0xFFFF);
s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension
u32 uimm = op & 0xFFFF;
int rt = _RT;
int rs = _RS;
@@ -63,99 +66,85 @@ namespace MIPSComp
switch (op >> 26)
{
case 8: // same as addiu?
case 9: //R(rt) = R(rs) + simm; break; //addiu
case 9: // R(rt) = R(rs) + simm; break; //addiu
{
if (gpr.R(rs).IsImm())
{
gpr.SetImmediate32(rt, gpr.R(rs).GetImmValue() + simm);
break;
if (gpr.IsImm(rs)) {
gpr.SetImm(rt, gpr.GetImm(rs) + simm);
} else if (rs == 0) { // add to zero register = immediate
gpr.SetImm(rt, (u32)simm);
} else {
gpr.MapDirtyIn(rt, rs);
Operand2 op2;
bool negated;
if (TryMakeOperand2_AllowNegation(simm, op2, &negated)) {
if (!negated)
ADD(gpr.R(rt), gpr.R(rs), op2);
else
SUB(gpr.R(rt), gpr.R(rs), op2);
} else {
ARMABI_MOVI2R(R0, (u32)simm);
ADD(gpr.R(rt), gpr.R(rs), R0);
}
}
gpr.Lock(rt, rs);
if (rs != 0)
{
gpr.BindToRegister(rt, rt == rs, true);
if (rt != rs)
MOV(32, gpr.R(rt), gpr.R(rs));
if (simm != 0)
ADD(32, gpr.R(rt), Imm32((u32)(s32)simm));
// TODO: Can also do LEA if both operands happen to be in registers.
}
else
{
gpr.SetImmediate32(rt, simm);
}
gpr.UnlockAll();
break;
}
break;
case 12: CompImmLogic(rs, rt, uimm, &ARMXEmitter::AND, &EvalAnd); break;
case 13: CompImmLogic(rs, rt, uimm, &ARMXEmitter::ORR, &EvalOr); break;
case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &EvalXor); break;
case 10: // R(rt) = (s32)R(rs) < simm; break; //slti
gpr.Lock(rt, rs);
gpr.BindToRegister(rt, rt == rs, true);
XOR(32, R(EAX), R(EAX));
CMP(32, gpr.R(rs), Imm32(simm));
SETcc(CC_L, R(EAX));
MOV(32, gpr.R(rt), R(EAX));
gpr.UnlockAll();
{
gpr.MapDirtyIn(rt, rs);
Operand2 op2;
if (TryMakeOperand2(simm, op2)) {
CMP(gpr.R(rs), op2);
} else {
ARMABI_MOVI2R(R0, simm);
CMP(gpr.R(rs), R0);
}
SetCC(CC_LT);
ARMABI_MOVI2R(gpr.R(rt), 1);
SetCC(CC_GE);
ARMABI_MOVI2R(gpr.R(rt), 0);
SetCC(CC_AL);
}
break;
/*
case 11: // R(rt) = R(rs) < uimm; break; //sltiu
gpr.Lock(rt, rs);
gpr.BindToRegister(rt, rt == rs, true);
XOR(32, R(EAX), R(EAX));
CMP(32, gpr.R(rs), Imm32((u32)simm));
SETcc(CC_B, R(EAX));
MOV(32, gpr.R(rt), R(EAX));
gpr.UnlockAll();
{
gpr.MapDirtyIn(rt, rs);
Operand2 op2;
if (TryMakeOperand2(uimm, op2)) {
CMP(gpr.R(rs), op2);
} else {
ARMABI_MOVI2R(R0, uimm);
CMP(gpr.R(rs), R0);
}
SetCC(CC_LO);
ARMABI_MOVI2R(gpr.R(rt), 1);
SetCC(CC_HS);
ARMABI_MOVI2R(gpr.R(rt), 0);
SetCC(CC_AL);
}
break;
case 12: CompImmLogic(op, &XEmitter::AND); break;
case 13: CompImmLogic(op, &XEmitter::OR); break;
case 14: CompImmLogic(op, &XEmitter::XOR); break;
case 15: //R(rt) = uimm << 16; break; //lui
gpr.SetImmediate32(rt, uimm << 16);
*/
case 15: // R(rt) = uimm << 16; //lui
gpr.SetImm(rt, uimm << 16);
break;
default:
Comp_Generic(op);
break;
}*/
}
}
//rd = rs X rt
/*
void Jit::CompTriArith(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &))
{
int rt = _RT;
int rs = _RS;
int rd = _RD;
gpr.Lock(rt, rs, rd);
MOV(32, R(EAX), gpr.R(rs));
MOV(32, R(EBX), gpr.R(rt));
gpr.BindToRegister(rd, true, true);
(this->*arith)(32, R(EAX), R(EBX));
MOV(32, gpr.R(rd), R(EAX));
gpr.UnlockAll();
}
*/
void Jit::Comp_RType3(u32 op)
{
OLDD
int rt = _RT;
int rs = _RS;
int rd = _RD;
gpr.Lock(rd, rs, rt);
gpr.BindToRegister(rs, true, false);
gpr.BindToRegister(rt, true, false);
gpr.BindToRegister(rd, true, true);
switch (op & 63)
{
//case 10: if (!R(rt)) R(rd) = R(rs); break; //movz
@@ -163,42 +152,59 @@ namespace MIPSComp
// case 32: //R(rd) = R(rs) + R(rt); break; //add
case 33: //R(rd) = R(rs) + R(rt); break; //addu
ADD(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
// Some optimized special cases
if (rs == 0) {
gpr.MapDirtyIn(rd, rt);
MOV(gpr.R(rd), gpr.R(rt));
} else if (rt == 0) {
gpr.MapDirtyIn(rd, rs);
MOV(gpr.R(rd), gpr.R(rs));
} else {
gpr.MapDirtyInIn(rd, rs, rt);
ADD(gpr.R(rd), gpr.R(rs), gpr.R(rt));
}
break;
case 134: //R(rd) = R(rs) - R(rt); break; //sub
case 135:
SUB(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
case 34: //R(rd) = R(rs) - R(rt); break; //sub
case 35:
gpr.MapDirtyInIn(rd, rs, rt);
SUB(gpr.R(rd), gpr.R(rs), gpr.R(rt));
break;
case 136: //R(rd) = R(rs) & R(rt); break; //and
AND(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
case 36: //R(rd) = R(rs) & R(rt); break; //and
gpr.MapDirtyInIn(rd, rs, rt);
AND(gpr.R(rd), gpr.R(rs), gpr.R(rt));
break;
case 137: //R(rd) = R(rs) | R(rt); break; //or
ORR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
case 37: //R(rd) = R(rs) | R(rt); break; //or
gpr.MapDirtyInIn(rd, rs, rt);
ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
break;
case 138: //R(rd) = R(rs) ^ R(rt); break; //xor/eor
EOR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor
gpr.MapDirtyInIn(rd, rs, rt);
EOR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
break;
case 39: // R(rd) = ~(R(rs) | R(rt)); //nor
ORR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt));
MVN(gpr.RX(rd), gpr.RX(rd));
gpr.MapDirtyInIn(rd, rs, rt);
ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt));
MVN(gpr.R(rd), gpr.R(rd));
break;
case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt
CMP(gpr.RX(rs), gpr.RX(rt));
gpr.MapDirtyInIn(rd, rs, rt);
CMP(gpr.R(rs), gpr.R(rt));
SetCC(CC_LT);
ARMABI_MOVI2R(gpr.RX(rd), 1);
ARMABI_MOVI2R(gpr.R(rd), 1);
SetCC(CC_GE);
ARMABI_MOVI2R(gpr.RX(rd), 0);
ARMABI_MOVI2R(gpr.R(rd), 0);
SetCC(CC_AL);
break;
case 43: //R(rd) = R(rs) < R(rt); break; //sltu
CMP(gpr.RX(rs), gpr.RX(rt));
gpr.MapDirtyInIn(rd, rs, rt);
CMP(gpr.R(rs), gpr.R(rt));
SetCC(CC_LO);
ARMABI_MOVI2R(gpr.RX(rd), 1);
ARMABI_MOVI2R(gpr.R(rd), 1);
SetCC(CC_HS);
ARMABI_MOVI2R(gpr.RX(rd), 0);
ARMABI_MOVI2R(gpr.R(rd), 0);
SetCC(CC_AL);
break;
@@ -209,29 +215,22 @@ namespace MIPSComp
// CMP(a,b); CMOVGT(a,b)
default:
gpr.UnlockAll();
// gpr.UnlockAll();
Comp_Generic(op);
break;
}
gpr.UnlockAll();
}
/*
void Jit::CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg))
void Jit::CompShiftImm(u32 op, ArmGen::ShiftType shiftType)
{
int rd = _RD;
int rt = _RT;
gpr.Lock(rd, rt);
int sa = _SA;
gpr.BindToRegister(rd, rd == rt, true);
if (rd != rt)
MOV(32, gpr.R(rd), gpr.R(rt));
(this->*shift)(32, gpr.R(rd), Imm8(sa));
gpr.UnlockAll();
gpr.MapDirtyIn(rd, rt);
MOV(gpr.R(rd), Operand2(sa, shiftType, gpr.R(rt)));
}
*/
// "over-shifts" work the same as on x86 - only bottom 5 bits are used to get the shift value
/*
void Jit::CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg))
@@ -254,12 +253,11 @@ namespace MIPSComp
void Jit::Comp_ShiftType(u32 op)
{
// WARNIGN : ROTR
OLDD
switch (op & 0x3f)
{
//case 0: CompShiftImm(op, &ARMXEmitter::SHL); break;
//case 2: CompShiftImm(op, &XEmitter::SHR); break; // srl
//case 3: CompShiftImm(op, &XEmitter::SAR); break; // sra
case 0: CompShiftImm(op, ST_LSL); break;
case 2: CompShiftImm(op, ST_LSR); break; // srl
case 3: CompShiftImm(op, ST_ASR); break; // sra
// case 4: CompShiftVar(op, &XEmitter::SHL); break; // R(rd) = R(rt) << R(rs); break; //sllv
// case 6: CompShiftVar(op, &XEmitter::SHR); break; // R(rd) = R(rt) >> R(rs); break; //srlv
+435
View File
@@ -0,0 +1,435 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../../HLE/HLE.h"
#include "../MIPS.h"
#include "../MIPSCodeUtils.h"
#include "../MIPSAnalyst.h"
#include "../MIPSTables.h"
#include "ArmJit.h"
#include "ArmRegCache.h"
#include "ArmJitCache.h"
#include <ArmEmitter.h>
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6 ) & 0x1F)
#define _POS ((op>>6 ) & 0x1F)
#define _SIZE ((op>>11 ) & 0x1F)
#define LOOPOPTIMIZATION 0
using namespace MIPSAnalyst;
namespace MIPSComp
{
void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op&0xFFFF)<<2;
int rt = _RT;
int rs = _RS;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC+4);
//Compile the delay slot
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rt && GetOutReg(delaySlotOp) != rs;
if (!delaySlotIsNice)
{
//ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC);
}
// The delay slot being nice doesn't really matter though...
if (rt == 0)
{
gpr.MapReg(rs);
CMP(gpr.R(rs), Operand2(0, TYPE_IMM));
}
else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable'
{
gpr.MapReg(rt);
CMP(gpr.R(rt), Operand2(0, TYPE_IMM));
}
else
{
gpr.SpillLock(rs, rt);
gpr.MapReg(rs);
gpr.MapReg(rt);
gpr.ReleaseSpillLocks();
CMP(gpr.R(rs), gpr.R(rt));
}
FlushAll();
js.inDelaySlot = true;
ArmGen::FixupBranch ptr;
if (!likely)
{
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
// delay slot, we're screwed.
MRS(R8); // Save flags register. R8 is preserved through function calls and is not allocated.
CompileAt(js.compilerPC + 4);
FlushAll();
_MSR(true, false, R8); // Restore flags register
ptr = B_CC(cc);
}
else
{
ptr = B_CC(cc);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC+8, 1);
js.compiling = false;
}
void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op&0xFFFF)<<2;
int rs = _RS;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs;
if (!delaySlotIsNice)
{
// ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC);
}
gpr.MapReg(rs);
CMP(gpr.R(rs), Operand2(0, TYPE_IMM));
FlushAll();
ArmGen::FixupBranch ptr;
js.inDelaySlot = true;
if (!likely)
{
// preserve flag around the delay slot! Maybe this is not always necessary on ARM where
// we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the
// delay slot, we're screwed.
MRS(R8); // Save flags register
CompileAt(js.compilerPC + 4);
FlushAll();
_MSR(true, false, R8); // Restore flags register
ptr = B_CC(cc);
}
else
{
ptr = B_CC(cc);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC + 8, 1);
js.compiling = false;
}
void Jit::Comp_RelBranch(u32 op)
{
// The CC flags here should be opposite of the actual branch becuase they skip the branching action.
switch (op>>26)
{
case 4: BranchRSRTComp(op, CC_NEQ, false); break;//beq
case 5: BranchRSRTComp(op, CC_EQ, false); break;//bne
case 6: BranchRSZeroComp(op, CC_GT, false); break;//blez
case 7: BranchRSZeroComp(op, CC_LE, false); break;//bgtz
case 20: BranchRSRTComp(op, CC_NEQ, true); break;//beql
case 21: BranchRSRTComp(op, CC_EQ, true); break;//bnel
case 22: BranchRSZeroComp(op, CC_GT, true); break;//blezl
case 23: BranchRSZeroComp(op, CC_LE, true); break;//bgtzl
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
js.compiling = false;
}
void Jit::Comp_RelBranchRI(u32 op)
{
switch ((op >> 16) & 0x1F)
{
case 0: BranchRSZeroComp(op, CC_GE, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
case 1: BranchRSZeroComp(op, CC_LT, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2: BranchRSZeroComp(op, CC_GE, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
case 3: BranchRSZeroComp(op, CC_LT, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
js.compiling = false;
}
// If likely is set, discard the branch slot if NOT taken.
void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op & 0xFFFF) << 2;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp);
if (!delaySlotIsNice)
{
//ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :(");
}
FlushAll();
LDR(R0, R10, offsetof(MIPSState, fpcond));
TST(R0, Operand2(1, TYPE_IMM));
ArmGen::FixupBranch ptr;
js.inDelaySlot = true;
if (!likely)
{
MRS(R8); // Save flags register
CompileAt(js.compilerPC + 4);
FlushAll();
_MSR(true, false, R8); // Restore flags register
ptr = B_CC(cc);
}
else
{
ptr = B_CC(cc);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC + 8, 1);
js.compiling = false;
}
void Jit::Comp_FPUBranch(u32 op)
{
switch((op >> 16) & 0x1f)
{
case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f
case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t
case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl
case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl
default:
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
break;
}
js.compiling = false;
}
// If likely is set, discard the branch slot if NOT taken.
void Jit::BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely)
{
int offset = (signed short)(op & 0xFFFF) << 2;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp);
if (!delaySlotIsNice)
{
//ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :(");
}
FlushAll();
ARMABI_MOVI2R(R0, (u32)&(mips_->vfpuCtrl[VFPU_CTRL_CC]));
LDR(R0, R0, Operand2(0, TYPE_IMM));
int imm3 = (op >> 18) & 7;
TST(R0, Operand2(1 << imm3, TYPE_IMM));
ArmGen::FixupBranch ptr;
js.inDelaySlot = true;
if (!likely)
{
MRS(R8); // Save flags register
CompileAt(js.compilerPC + 4);
FlushAll();
_MSR(true, false, R8); // Restore flags register
ptr = B_CC(cc);
}
else
{
ptr = B_CC(cc);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC + 8, 1);
js.compiling = false;
}
void Jit::Comp_VBranch(u32 op)
{
switch ((op >> 16) & 3)
{
case 0: BranchVFPUFlag(op, CC_NEQ, false); break; // bvf
case 1: BranchVFPUFlag(op, CC_EQ, false); break; // bvt
case 2: BranchVFPUFlag(op, CC_NEQ, true); break; // bvfl
case 3: BranchVFPUFlag(op, CC_EQ, true); break; // bvtl
}
js.compiling = false;
}
void PrintAtExit() {
INFO_LOG(HLE, "at jump");
}
void Jit::Comp_Jump(u32 op)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
return;
}
u32 off = ((op & 0x03FFFFFF) << 2);
u32 targetAddr = (js.compilerPC & 0xF0000000) | off;
// Delay slot
CompileAt(js.compilerPC + 4);
FlushAll();
switch (op >> 26)
{
case 2: //j
WriteExit(targetAddr, 0);
break;
case 3: //jal
ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram
ARMABI_MOVI2R(R0, js.compilerPC + 8);
STR(R1, R0);
WriteExit(targetAddr, 0);
break;
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
js.compiling = false;
}
void Jit::Comp_JumpReg(u32 op)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
return;
}
int rs = _RS;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs;
// Do what with that information?
delaySlotIsNice = false;
if (delaySlotIsNice) {
CompileAt(js.compilerPC + 4);
gpr.MapReg(rs);
MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented
FlushAll();
MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking.
} else {
// Delay slot
gpr.MapReg(rs);
MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented
MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking.
CompileAt(js.compilerPC + 4);
FlushAll();
if (!js.compiling) {
// INFO_LOG(HLE, "Syscall in delay slot!");
return;
}
}
switch (op & 0x3f)
{
case 8: //jr
break;
case 9: //jalr
ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram
ARMABI_MOVI2R(R0, js.compilerPC + 8);
STR(R1, R0);
break;
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
WriteExitDestInR(R8);
js.compiling = false;
}
void Jit::Comp_Syscall(u32 op)
{
FlushAll();
ARMABI_MOVI2R(R0, op);
QuickCallFunction(R1, (void *)&CallSyscall);
WriteSyscallExit();
js.compiling = false;
}
} // namespace Mipscomp
@@ -16,8 +16,8 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../MIPS.h"
#include "Jit.h"
#include "RegCache.h"
#include "ArmJit.h"
#include "ArmRegCache.h"
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
@@ -115,6 +115,7 @@ void Jit::Comp_FPULS(u32 op)
void Jit::Comp_FPU2op(u32 op)
{
OLDD
int fs = _FS;
int fd = _FD;
@@ -173,6 +174,7 @@ void Jit::Comp_FPU2op(u32 op)
void Jit::Comp_mxc1(u32 op)
{
OLDD
int fs = _FS;
int rt = _RT;
+214
View File
@@ -0,0 +1,214 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// Optimization ideas:
//
// It's common to see sequences of stores writing or reading to a contiguous set of
// addresses in function prologues/epilogues:
// sw s5, 104(sp)
// sw s4, 100(sp)
// sw s3, 96(sp)
// sw s2, 92(sp)
// sw s1, 88(sp)
// sw s0, 84(sp)
// sw ra, 108(sp)
// mov s4, a0
// mov s3, a1
// ...
// Such sequences could easily be detected and turned into nice contiguous
// sequences of ARM stores instead of the current 3 instructions per sw/lw.
//
// Also, if we kept track of the likely register content of a cached register,
// (pointer or data), we could avoid many BIC instructions.
#include "../../MemMap.h"
#include "../MIPSAnalyst.h"
#include "../../Config.h"
#include "ArmJit.h"
#include "ArmRegCache.h"
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6 ) & 0x1F)
#define _POS ((op>>6 ) & 0x1F)
#define _SIZE ((op>>11 ) & 0x1F)
#define OLDD Comp_Generic(op); return;
namespace MIPSComp
{
void Jit::Comp_ITypeMem(u32 op)
{
int offset = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;
int o = op>>26;
if (((op >> 29) & 1) == 0 && rt == 0) {
// Don't load anything into $zr
return;
}
switch (o)
{
case 37: //R(rt) = ReadMem16(addr); break; //lhu
Comp_Generic(op);
return;
case 35: //R(rt) = ReadMem32(addr); //lw
case 36: //R(rt) = ReadMem8 (addr); break; //lbu
if (g_Config.bFastMemory) {
if (gpr.IsImm(rs)) {
// We can compute the full address at compile time. Kickass.
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); // must be OK even if rs == rt since we have the value from imm already.
ARMABI_MOVI2R(R0, addr);
} else {
gpr.MapDirtyIn(rt, rs);
Operand2 op2;
if (offset) {
bool negated;
if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) {
if (!negated)
ADD(R0, gpr.R(rs), op2);
else
SUB(R0, gpr.R(rs), op2);
} else {
// Try to avoid using MOVT
if (offset < 0) {
ARMABI_MOVI2R(R0, (u32)(-offset));
SUB(R0, gpr.R(rs), R0);
} else {
ARMABI_MOVI2R(R0, (u32)offset);
ADD(R0, gpr.R(rs), R0);
}
}
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
} else {
BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF
}
}
if (o == 35) {
LDR(gpr.R(rt), R11, R0, true, true);
} else if (o == 36) {
ADD(R0, R0, R11); // TODO: Merge with next instruction
LDRB(gpr.R(rt), R0);
}
} else {
Comp_Generic(op);
return;
}
break;
case 41: //WriteMem16(addr, R(rt)); break; //sh
Comp_Generic(op);
return;
case 40: //sb
case 43: //WriteMem32(addr, R(rt)); break; //sw
if (g_Config.bFastMemory) {
if (gpr.IsImm(rs)) {
// We can compute the full address at compile time. Kickass.
u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF;
gpr.MapReg(rt);
ARMABI_MOVI2R(R0, addr);
} else {
gpr.MapInIn(rt, rs);
Operand2 op2;
if (offset) {
bool negated;
if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) {
if (!negated)
ADD(R0, gpr.R(rs), op2);
else
SUB(R0, gpr.R(rs), op2);
} else {
// Try to avoid using MOVT
if (offset < 0) {
ARMABI_MOVI2R(R0, (u32)(-offset));
SUB(R0, gpr.R(rs), R0);
} else {
ARMABI_MOVI2R(R0, (u32)offset);
ADD(R0, gpr.R(rs), R0);
}
}
BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF
} else {
BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF
}
}
if (o == 43) {
STR(R0, gpr.R(rt), R11, true, true);
} else if (o == 40) {
ADD(R0, R0, R11);
STRB(R0, gpr.R(rt));
}
} else {
Comp_Generic(op);
return;
}
break;
// break;
/*
case 34: //lwl
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//R(rt) = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );
}
break;
case 38: //lwr
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//R(rt) = ( u32(rt) & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );
}
break;
case 42: //swl
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) >> (24 - shift) ) ) |
// ( mem & (0xffffff00 << shift) ));
}
break;
case 46: //swr
{
Crash();
// u32 shift = (addr & 3) << 3;
// u32 mem = ReadMem32(addr & 0xfffffffc);
//
// WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) << shift ) |
// (mem & (0x00ffffff >> (24 - shift)) ) ) );
}
break;*/
default:
Comp_Generic(op);
return ;
}
}
}
@@ -1,8 +1,8 @@
#include "../../MemMap.h"
#include "../MIPSAnalyst.h"
#include "Jit.h"
#include "RegCache.h"
#include "ArmJit.h"
#include "ArmRegCache.h"
#define _RS ((op>>21) & 0x1F)
+283
View File
@@ -0,0 +1,283 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../../Core.h"
#include "../../CoreTiming.h"
#include "../MIPS.h"
#include "../MIPSCodeUtils.h"
#include "../MIPSInt.h"
#include "../MIPSTables.h"
#include "ArmRegCache.h"
#include "ArmJit.h"
#include "../../ext/disarm.h"
void DisassembleArm(const u8 *data, int size) {
char temp[256];
for (int i = 0; i < size; i += 4) {
const u32 *codePtr = (const u32 *)(data + i);
u32 inst = codePtr[0];
u32 next = (i < size - 4) ? codePtr[1] : 0;
// MAGIC SPECIAL CASE for MOVW/MOVT readability!
if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) {
u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF);
u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF);
int reg0 = (inst & 0x0000F000) >> 12;
int reg1 = (next & 0x0000F000) >> 12;
if (reg0 == reg1) {
sprintf(temp, "%08x MOV32? %s, %04x%04x", (u32)inst, ArmRegName(reg0), hi, low);
INFO_LOG(DYNA_REC, "A: %s", temp);
i += 4;
continue;
}
}
ArmDis((u32)codePtr, inst, temp);
INFO_LOG(DYNA_REC, "A: %s", temp);
}
}
namespace MIPSComp
{
Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips)
{
blocks.Init();
gpr.SetEmitter(this);
//fpr.SetEmitter(this);
AllocCodeSpace(1024 * 1024 * 16); // 32MB is the absolute max because that's what an ARM branch instruction can reach, backwards and forwards.
GenerateFixedCode();
}
void Jit::FlushAll()
{
gpr.FlushAll();
//fpr.Flush(FLUSH_ALL);
}
void Jit::ClearCache()
{
blocks.Clear();
ClearCodeSpace();
GenerateFixedCode();
}
void Jit::CompileAt(u32 addr)
{
u32 op = Memory::Read_Instruction(addr);
MIPSCompileOp(op);
}
void Jit::Compile(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull())
{
ClearCache();
}
int block_num = blocks.AllocateBlock(em_address);
ArmJitBlock *b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, b));
}
void Jit::RunLoopUntil(u64 globalticks)
{
// TODO: copy globalticks somewhere
((void (*)())enterCode)();
}
const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b)
{
js.cancel = false;
js.blockStart = js.compilerPC = mips_->pc;
js.downcountAmount = 0;
js.curBlock = b;
js.compiling = true;
js.inDelaySlot = false;
// We add a check before the block, used when entering from a linked block.
b->checkedEntry = GetCodePtr();
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
SetCC(CC_LT);
ARMABI_MOVI2R(R0, js.blockStart);
MovToPC(R0);
B((const void *)outerLoop);
SetCC(CC_AL);
b->normalEntry = GetCodePtr();
// TODO: this needs work
MIPSAnalyst::AnalysisResults analysis; // = MIPSAnalyst::Analyze(em_address);
gpr.Start(analysis);
int numInstructions = 0;
int cycles = 0;
static int dontLogBlocks = 20;
static int logBlocks = 40;
if (logBlocks > 0) logBlocks--;
if (dontLogBlocks > 0) dontLogBlocks--;
#define LOGASM
#ifdef LOGASM
char temp[256];
#endif
while (js.compiling)
{
gpr.SetCompilerPC(js.compilerPC); // Let it know for log messages
u32 inst = Memory::Read_Instruction(js.compilerPC);
#ifdef LOGASM
if (logBlocks > 0 && dontLogBlocks == 0) {
MIPSDisAsm(inst, js.compilerPC, temp, true);
INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp);
}
#endif
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
MIPSCompileOp(inst);
// FlushAll(); ///HACKK
js.compilerPC += 4;
numInstructions++;
}
#ifdef LOGASM
if (logBlocks > 0 && dontLogBlocks == 0) {
MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true);
INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp);
}
#endif
b->codeSize = GetCodePtr() - b->normalEntry;
#ifdef LOGASM
if (logBlocks > 0 && dontLogBlocks == 0) {
INFO_LOG(DYNA_REC, "=============== ARM ===============");
DisassembleArm(b->normalEntry, GetCodePtr() - b->normalEntry);
}
#endif
AlignCode16();
// Don't forget to zap the instruction cache!
FlushIcache();
b->originalSize = numInstructions;
return b->normalEntry;
}
void Jit::Comp_RunBlock(u32 op)
{
// This shouldn't be necessary, the dispatcher should catch us before we get here.
ERROR_LOG(DYNA_REC, "Comp_RunBlock should never be reached!");
}
void Jit::Comp_Generic(u32 op)
{
FlushAll();
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
if (func)
{
ARMABI_MOVI2R(R0, js.compilerPC);
MovToPC(R0);
ARMABI_MOVI2R(R0, op);
QuickCallFunction(R1, (void *)func);
}
}
void Jit::MovFromPC(ARMReg r) {
LDR(r, R10, offsetof(MIPSState, pc));
}
void Jit::MovToPC(ARMReg r) {
STR(R10, r, offsetof(MIPSState, pc));
}
void Jit::DoDownCount()
{
LDR(R1, R10, offsetof(MIPSState, downcount));
Operand2 op2;
if (TryMakeOperand2(js.downcountAmount, op2)) // We can enlarge this if we used rotations
{
SUBS(R1, R1, op2);
STR(R10, R1, offsetof(MIPSState, downcount));
} else {
// Should be fine to use R2 here, flushed the regcache anyway.
// If js.downcountAmount can be expressed as an Imm8, we don't need this anyway.
ARMABI_MOVI2R(R2, js.downcountAmount);
SUBS(R1, R1, R2);
STR(R10, R1, offsetof(MIPSState, downcount));
}
}
// IDEA - could have a WriteDualExit that takes two destinations and two condition flags,
// and just have conditional that set PC "twice". This only works when we fall back to dispatcher
// though, as we need to have the SUBS flag set in the end. So with block linking in the mix,
// I don't think this gives us that much benefit.
void Jit::WriteExit(u32 destination, int exit_num)
{
DoDownCount();
//If nobody has taken care of this yet (this can be removed when all branches are done)
ArmJitBlock *b = js.curBlock;
b->exitAddress[exit_num] = destination;
b->exitPtrs[exit_num] = GetWritableCodePtr();
// Link opportunity!
int block = blocks.GetBlockNumberFromStartAddress(destination);
if (block >= 0 && jo.enableBlocklink) {
// It exists! Joy of joy!
B(blocks.GetBlock(block)->checkedEntry);
b->linkStatus[exit_num] = true;
} else {
ARMABI_MOVI2R(R0, destination);
MovToPC(R0);
B((const void *)dispatcher);
}
}
void Jit::WriteExitDestInR(ARMReg Reg)
{
MovToPC(Reg);
DoDownCount();
// TODO: shouldn't need an indirect branch here...
B((const void *)dispatcher);
}
void Jit::WriteSyscallExit()
{
DoDownCount();
B((const void *)dispatcherCheckCoreState);
}
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6) & 0x1F)
#define _POS ((op>>6) & 0x1F)
#define _SIZE ((op>>11) & 0x1F)
//memory regions:
//
// 08-0A
// 48-4A
// 04-05
// 44-45
// mov eax, addrreg
// shr eax, 28
// mov eax, [table+eax]
// mov dreg, [eax+offreg]
}
+40 -29
View File
@@ -18,31 +18,26 @@
#pragma once
#include "../../../Globals.h"
#include "Asm.h"
#if !defined(ARM)
#error DO NOT BUILD ARM JIT ON NON-ARM
#endif
#include "ArmAsm.h"
#include <ArmEmitter.h>
#include "JitCache.h"
#include "RegCache.h"
#include "ArmJitCache.h"
#include "ArmRegCache.h"
namespace MIPSComp
{
struct JitOptions
struct ArmJitOptions
{
JitOptions()
ArmJitOptions()
{
enableBlocklink = false;
enableBlocklink = true;
}
bool enableBlocklink;
};
struct JitState
struct ArmJitState
{
u32 compilerPC;
u32 blockStart;
@@ -50,7 +45,7 @@ struct JitState
bool inDelaySlot;
int downcountAmount;
bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block
JitBlock *curBlock;
ArmJitBlock *curBlock;
};
class Jit : public ArmGen::ARMXCodeBlock
@@ -66,7 +61,7 @@ public:
void RunLoopUntil(u64 globalticks);
void Compile(u32 em_address); // Compiles a block at current MIPS PC
const u8 *DoJit(u32 em_address, JitBlock *b);
const u8 *DoJit(u32 em_address, ArmJitBlock *b);
void CompileAt(u32 addr);
void Comp_RunBlock(u32 op);
@@ -92,26 +87,33 @@ public:
void Comp_FPU2op(u32 op);
void Comp_mxc1(u32 op);
JitBlockCache *GetBlockCache() { return &blocks; }
AsmRoutineManager &Asm() { return asm_; }
ArmJitBlockCache *GetBlockCache() { return &blocks; }
void ClearCache();
private:
void GenerateFixedCode();
void FlushAll();
// TODO: Split into two parts, the first part can be shared in branches.
void DoDownCount();
void MovFromPC(ARMReg r);
void MovToPC(ARMReg r);
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInEAX();
// void WriteRfiExitDestInEAX();
void WriteExitDestInR(ARMReg Reg);
void WriteSyscallExit();
// Utility compilation functions
//void BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely);
//void BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely);
//void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely);
void BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely);
void BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely);
void BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely);
void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely);
// Utilities to reduce duplicated code
/*
void CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b));
void CompShiftImm(u32 op, ArmGen::ShiftType shiftType);
/*
void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &));
void CompTriArith(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &));
void CompShiftImm(u32 op, void (ARMXEmitter::*shift)(int, OpArg, OpArg));
@@ -120,16 +122,25 @@ private:
void CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters);
*/
JitBlockCache blocks;
JitOptions jo;
JitState js;
ArmJitBlockCache blocks;
ArmJitOptions jo;
ArmJitState js;
GPRRegCache gpr;
FPURegCache fpr;
AsmRoutineManager asm_;
ArmRegCache gpr;
// FPURegCache fpr;
MIPSState *mips_;
public:
// Code pointers
const u8 *enterCode;
const u8 *outerLoop;
const u8 *dispatcherCheckCoreState;
const u8 *dispatcher;
const u8 *dispatcherNoCheck;
const u8 *breakpointBailout;
};
typedef void (Jit::*MIPSCompileFunc)(u32 opcode);
@@ -29,12 +29,11 @@
#include "../MIPSTables.h"
#include "../MIPSAnalyst.h"
#include "JitCache.h"
#include "../JitCommon/JitCommon.h"
#include "Asm.h"
// #include "JitBase.h"
#include "ArmEmitter.h"
#include <ArmEmitter.h>
#include "ArmJitCache.h"
#include "../JitCommon/JitCommon.h"
#include "ArmAsm.h"
#if defined USE_OPROFILE && USE_OPROFILE
#include <opagent.h>
@@ -52,30 +51,30 @@ using namespace ArmGen;
#define INVALID_EXIT 0xFFFFFFFF
bool JitBlock::ContainsAddress(u32 em_address)
bool ArmJitBlock::ContainsAddress(u32 em_address)
{
// WARNING - THIS DOES NOT WORK WITH INLINING ENABLED.
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
}
bool JitBlockCache::IsFull() const
bool ArmJitBlockCache::IsFull() const
{
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
}
void JitBlockCache::Init()
void ArmJitBlockCache::Init()
{
MAX_NUM_BLOCKS = 65536*2;
#if defined USE_OPROFILE && USE_OPROFILE
agent = op_open_agent();
#endif
blocks = new JitBlock[MAX_NUM_BLOCKS];
blocks = new ArmJitBlock[MAX_NUM_BLOCKS];
blockCodePointers = new const u8*[MAX_NUM_BLOCKS];
Clear();
}
void JitBlockCache::Shutdown()
void ArmJitBlockCache::Shutdown()
{
delete[] blocks;
delete[] blockCodePointers;
@@ -91,15 +90,14 @@ void JitBlockCache::Shutdown()
#endif
}
JitBlockCache::~JitBlockCache()
ArmJitBlockCache::~ArmJitBlockCache()
{
Shutdown();
}
// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
// This clears the JIT block cache. It's called from JitCache.cpp when the JIT cache
// is full and when saving and loading states.
void JitBlockCache::Clear()
void ArmJitBlockCache::Clear()
{
for (int i = 0; i < num_blocks; i++)
{
@@ -108,44 +106,33 @@ void JitBlockCache::Clear()
links_to.clear();
block_map.clear();
num_blocks = 0;
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
memset(blockCodePointers, 0xCC, sizeof(u8*)*MAX_NUM_BLOCKS);
}
void JitBlockCache::ClearSafe()
void ArmJitBlockCache::ClearSafe()
{
#ifdef JIT_UNLIMITED_ICACHE
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
#endif
}
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
{
for (int i = 0; i < num_blocks; i++)
{
if (blocks[i].flags & death_flag)
{
DestroyBlock(i, false);
}
}
}*/
void JitBlockCache::Reset()
void ArmJitBlockCache::Reset()
{
Shutdown();
Init();
}
JitBlock *JitBlockCache::GetBlock(int no)
ArmJitBlock *ArmJitBlockCache::GetBlock(int no)
{
return &blocks[no];
}
int JitBlockCache::GetNumBlocks() const
int ArmJitBlockCache::GetNumBlocks() const
{
return num_blocks;
}
bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
bool ArmJitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
{
// check if any endpoint is inside the other range
if ((s1 >= s2 && s1 <= e2) ||
@@ -157,9 +144,9 @@ bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
return false;
}
int JitBlockCache::AllocateBlock(u32 em_address)
int ArmJitBlockCache::AllocateBlock(u32 em_address)
{
JitBlock &b = blocks[num_blocks];
ArmJitBlock &b = blocks[num_blocks];
b.invalid = false;
b.originalAddress = em_address;
b.exitAddress[0] = INVALID_EXIT;
@@ -173,10 +160,10 @@ int JitBlockCache::AllocateBlock(u32 em_address)
return num_blocks - 1;
}
void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
void ArmJitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
{
blockCodePointers[block_num] = code_ptr;
JitBlock &b = blocks[block_num];
ArmJitBlock &b = blocks[block_num];
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
u32 opcode = MIPS_MAKE_EMUHACK(0, block_num);
@@ -222,12 +209,12 @@ void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code
#endif
}
const u8 **JitBlockCache::GetCodePointers()
const u8 **ArmJitBlockCache::GetCodePointers()
{
return blockCodePointers;
}
int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
int ArmJitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
{
if (!blocks)
return -1;
@@ -242,14 +229,14 @@ int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
return bl;
}
void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
void ArmJitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
{
for (int i = 0; i < num_blocks; i++)
if (blocks[i].ContainsAddress(em_address))
block_numbers->push_back(i);
}
u32 JitBlockCache::GetOriginalFirstOp(int block_num)
u32 ArmJitBlockCache::GetOriginalFirstOp(int block_num)
{
if (block_num >= num_blocks)
{
@@ -259,12 +246,12 @@ u32 JitBlockCache::GetOriginalFirstOp(int block_num)
return blocks[block_num].originalFirstOpcode;
}
CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num)
CompiledCode ArmJitBlockCache::GetCompiledCodeFromBlock(int block_num)
{
return (CompiledCode)blockCodePointers[block_num];
}
std::string JitBlockCache::GetCompiledDisassembly(int block_num)
std::string ArmJitBlockCache::GetCompiledDisassembly(int block_num)
{
/*
std::string buf;
@@ -280,16 +267,11 @@ std::string JitBlockCache::GetCompiledDisassembly(int 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 JitBlockCache::LinkBlockExits(int i)
//It's O(1), so it's fast :)
void ArmJitBlockCache::LinkBlockExits(int i)
{
JitBlock &b = blocks[i];
ArmJitBlock &b = blocks[i];
if (b.invalid)
{
// This block is dead. Don't relink it.
@@ -303,7 +285,8 @@ void JitBlockCache::LinkBlockExits(int i)
if (destinationBlock != -1)
{
ARMXEmitter emit(b.exitPtrs[e]);
// emit.JMP(blocks[destinationBlock].checkedEntry, true);
emit.B(blocks[destinationBlock].checkedEntry);
emit.FlushIcache();
b.linkStatus[e] = true;
}
}
@@ -312,10 +295,10 @@ void JitBlockCache::LinkBlockExits(int i)
using namespace std;
void JitBlockCache::LinkBlock(int i)
void ArmJitBlockCache::LinkBlock(int i)
{
LinkBlockExits(i);
JitBlock &b = blocks[i];
ArmJitBlock &b = blocks[i];
std::map<u32, int>::iterator iter;
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
// equal_range(b) returns pair<iterator,iterator> representing the range
@@ -329,16 +312,16 @@ void JitBlockCache::LinkBlock(int i)
}
}
void JitBlockCache::UnlinkBlock(int i)
void ArmJitBlockCache::UnlinkBlock(int i)
{
JitBlock &b = blocks[i];
ArmJitBlock &b = blocks[i];
std::map<u32, int>::iterator iter;
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (multimap<u32, int>::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) {
JitBlock &sourceBlock = blocks[iter2->second];
ArmJitBlock &sourceBlock = blocks[iter2->second];
for (int e = 0; e < 2; e++)
{
if (sourceBlock.exitAddress[e] == b.originalAddress)
@@ -347,14 +330,14 @@ void JitBlockCache::UnlinkBlock(int i)
}
}
void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
void ArmJitBlockCache::DestroyBlock(int block_num, bool invalidate)
{
if (block_num < 0 || block_num >= num_blocks)
{
ERROR_LOG(JIT, "DestroyBlock: Invalid block number %d", block_num);
return;
}
JitBlock &b = blocks[block_num];
ArmJitBlock &b = blocks[block_num];
if (b.invalid)
{
if (invalidate)
@@ -367,25 +350,21 @@ void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
UnlinkBlock(block_num);
blockCodePointers[block_num] = 0;
// 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
// I hope there's enough space...
// checkedEntry is the only "linked" entrance so it's enough to overwrite that.
ARMXEmitter emit((u8 *)b.checkedEntry);
//emit.MOV(32, M(&mips->pc), Imm32(b.originalAddress));
//emit.JMP(MIPSComp::jit->Asm().dispatcher, true);
// this is not needed really
/*
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
emit.JMP(asm_routines.dispatcher, true);
*/
emit.ARMABI_MOVI2R(R0, b.originalAddress);
emit.STR(R10, R0, offsetof(MIPSState, pc));
emit.B(MIPSComp::jit->dispatcher);
emit.FlushIcache();
}
void JitBlockCache::InvalidateICache(u32 address, const u32 length)
void ArmJitBlockCache::InvalidateICache(u32 address, const u32 length)
{
// Convert the logical address to a physical address for the block map
u32 pAddr = address & 0x1FFFFFFF;
u32 pAddr = address & 0x3FFFFFFF;
// destroy JIT blocks
// !! this works correctly under assumption that any two overlapping blocks end at the same address
@@ -22,7 +22,7 @@
#include <string>
#include "../MIPSAnalyst.h"
#include "../MIPS.h"
// Define this in order to get VTune profile support for the Jit generated code.
// Add the VTune include/lib directories to the project directories to get this to build.
// #define USE_VTUNE
@@ -42,7 +42,7 @@
#define JIT_OPCODE 0xFFCCCCCC // yeah this ain't gonna work
struct JitBlock
struct ArmJitBlock
{
const u8 *checkedEntry;
const u8 *normalEntry;
@@ -77,11 +77,11 @@ struct JitBlock
typedef void (*CompiledCode)();
class JitBlockCache
class ArmJitBlockCache
{
MIPSState *mips;
const u8 **blockCodePointers;
JitBlock *blocks;
ArmJitBlock *blocks;
int num_blocks;
std::multimap<u32, int> links_to;
std::map<std::pair<u32,u32>, u32> block_map; // (end_addr, start_addr) -> number
@@ -94,10 +94,10 @@ class JitBlockCache
void UnlinkBlock(int i);
public:
JitBlockCache(MIPSState *mips_) :
ArmJitBlockCache(MIPSState *mips_) :
mips(mips_), blockCodePointers(0), blocks(0), num_blocks(0),
MAX_NUM_BLOCKS(0) { }
~JitBlockCache();
~ArmJitBlockCache();
int AllocateBlock(u32 em_address);
void FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr);
@@ -110,7 +110,7 @@ public:
bool IsFull() const;
// Code Cache
JitBlock *GetBlock(int block_num);
ArmJitBlock *GetBlock(int block_num);
int GetNumBlocks() const;
const u8 **GetCodePointers();
+268
View File
@@ -0,0 +1,268 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "ArmRegCache.h"
#include "ArmEmitter.h"
using namespace ArmGen;
#define CTXREG (R10)
ArmRegCache::ArmRegCache(MIPSState *mips) : mips_(mips) {
}
void ArmRegCache::Init(ARMXEmitter *emitter) {
emit = emitter;
}
void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) {
for (int i = 0; i < 16; i++) {
ar[i].mipsReg = -1;
ar[i].isDirty = false;
}
for (int i = 0; i < NUM_MIPSREG; i++) {
mr[i].loc = ML_MEM;
mr[i].reg = INVALID_REG;
mr[i].imm = -1;
mr[i].spillLock = false;
}
}
static const ARMReg *GetMIPSAllocationOrder(int &count) {
// Note that R0 and R1 are reserved as scratch for now. We can probably free up R1 eventually.
// R8 is used to preserve flags in nasty branches.
// R9 and upwards are reserved for jit basics.
// Six allocated registers should be enough...
static const ARMReg allocationOrder[] = {
R2, R3, R4, R5, R6, R7
};
count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder;
}
ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) {
// Let's see if it's already mapped. If so we just need to update the dirty flag.
// We don't need to check for ML_NOINIT because we assume that anyone who maps
// with that flag immediately writes a "known" value to the register.
if (mr[mipsReg].loc == ML_ARMREG) {
if (ar[mr[mipsReg].reg].mipsReg != mipsReg) {
ERROR_LOG(HLE, "Register mapping out of sync! %i", mipsReg);
}
if (mapFlags & MAP_DIRTY) {
ar[mr[mipsReg].reg].isDirty = true;
}
return mr[mipsReg].reg;
}
// Okay, not mapped, so we need to allocate an ARM register.
int allocCount;
const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount);
allocate:
for (int i = 0; i < allocCount; i++) {
int reg = allocOrder[i];
if (ar[reg].mipsReg == -1) {
// That means it's free. Grab it, and load the value into it (if requested).
ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false;
if (!(mapFlags & MAP_NOINIT)) {
if (mr[mipsReg].loc == ML_MEM) {
if (mipsReg != 0) {
emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg);
} else {
// If we get a request to load the zero register, at least we won't spend
// time on a memory access...
emit->MOV((ARMReg)reg, 0);
}
} else if (mr[mipsReg].loc == ML_IMM) {
emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm);
ar[reg].isDirty = true; // IMM is always dirty.
}
}
ar[reg].mipsReg = mipsReg;
mr[mipsReg].loc = ML_ARMREG;
mr[mipsReg].reg = (ARMReg)reg;
return (ARMReg)reg;
}
}
// Still nothing. Let's spill a reg and goto 10.
// TODO: Use age or something to choose which register to spill?
// TODO: Spill dirty regs first? or opposite?
int bestToSpill = -1;
for (int i = 0; i < allocCount; i++) {
int reg = allocOrder[i];
if (ar[reg].mipsReg != -1 && mr[ar[reg].mipsReg].spillLock)
continue;
bestToSpill = reg;
break;
}
if (bestToSpill != -1) {
// ERROR_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill);
FlushArmReg((ARMReg)bestToSpill);
goto allocate;
}
// Uh oh, we have all them spilllocked....
ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc);
return INVALID_REG;
}
void ArmRegCache::MapInIn(MIPSReg rd, MIPSReg rs) {
SpillLock(rd, rs);
MapReg(rd);
MapReg(rs);
ReleaseSpillLocks();
}
void ArmRegCache::MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad) {
SpillLock(rd, rs);
bool overlap = avoidLoad && rd == rs;
MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT));
MapReg(rs);
ReleaseSpillLocks();
}
void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad) {
SpillLock(rd, rs, rt);
bool overlap = avoidLoad && (rd == rs || rd == rt);
MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT));
MapReg(rt);
MapReg(rs);
ReleaseSpillLocks();
}
void ArmRegCache::FlushArmReg(ARMReg r) {
if (ar[r].mipsReg == -1) {
// Nothing to do, reg not mapped.
return;
}
if (ar[r].mipsReg != -1) {
if (ar[r].isDirty && mr[ar[r].mipsReg].loc == ML_ARMREG)
emit->STR(CTXREG, r, 4 * ar[r].mipsReg);
// IMMs won't be in an ARM reg.
mr[ar[r].mipsReg].loc = ML_MEM;
mr[ar[r].mipsReg].reg = INVALID_REG;
mr[ar[r].mipsReg].imm = 0;
} else {
ERROR_LOG(HLE, "Dirty but no mipsreg?");
}
ar[r].isDirty = false;
ar[r].mipsReg = -1;
}
void ArmRegCache::FlushMipsReg(MIPSReg r) {
switch (mr[r].loc) {
case ML_IMM:
// IMM is always "dirty".
emit->ARMABI_MOVI2R(R0, mr[r].imm);
emit->STR(CTXREG, R0, GetMipsRegOffset(r));
break;
case ML_ARMREG:
if (mr[r].reg == INVALID_REG) {
ERROR_LOG(HLE, "FlushMipsReg: MipsReg had bad ArmReg");
}
if (ar[mr[r].reg].isDirty) {
emit->STR(CTXREG, mr[r].reg, GetMipsRegOffset(r));
ar[mr[r].reg].isDirty = false;
}
ar[mr[r].reg].mipsReg = -1;
break;
case ML_MEM:
// Already there, nothing to do.
break;
default:
//BAD
break;
}
mr[r].loc = ML_MEM;
mr[r].reg = INVALID_REG;
mr[r].imm = 0;
}
void ArmRegCache::FlushAll() {
for (int i = 0; i < NUM_MIPSREG; i++) {
FlushMipsReg(i);
}
// Sanity check
for (int i = 0; i < NUM_ARMREG; i++) {
if (ar[i].mipsReg != -1) {
ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg);
}
}
}
void ArmRegCache::SetImm(MIPSReg r, u32 immVal) {
// Zap existing value if cached in a reg
if (mr[r].loc == ML_ARMREG) {
ar[mr[r].reg].mipsReg = -1;
ar[mr[r].reg].isDirty = false;
}
mr[r].loc = ML_IMM;
mr[r].imm = immVal;
mr[r].reg = INVALID_REG;
}
bool ArmRegCache::IsImm(MIPSReg r) const {
return mr[r].loc == ML_IMM;
}
u32 ArmRegCache::GetImm(MIPSReg r) const {
if (mr[r].loc != ML_IMM) {
ERROR_LOG(JIT, "Trying to get imm from non-imm register %i", r);
}
return mr[r].imm;
}
int ArmRegCache::GetMipsRegOffset(MIPSReg r) {
if (r < 32)
return r * 4;
switch (r) {
case MIPSREG_HI:
return offsetof(MIPSState, hi);
case MIPSREG_LO:
return offsetof(MIPSState, lo);
}
ERROR_LOG(JIT, "bad mips register %i", r);
return 0; // or what?
}
void ArmRegCache::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3) {
mr[r1].spillLock = true;
if (r2 != -1) mr[r2].spillLock = true;
if (r3 != -1) mr[r3].spillLock = true;
}
void ArmRegCache::ReleaseSpillLocks() {
for (int i = 0; i < NUM_MIPSREG; i++) {
mr[i].spillLock = false;
}
}
ARMReg ArmRegCache::R(int mipsReg) {
if (mr[mipsReg].loc == ML_ARMREG) {
return mr[mipsReg].reg;
} else {
ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_);
return INVALID_REG; // BAAAD
}
}
+115
View File
@@ -0,0 +1,115 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#pragma once
#include "ArmEmitter.h"
#include "../MIPS.h"
#include "../MIPSAnalyst.h"
using namespace ArmGen;
// R2 to R8: mapped MIPS regs
// R9 = code pointers
// R10 = MIPS context
// R11 = base pointer
// Special MIPS registers:
enum {
MIPSREG_HI = 32,
MIPSREG_LO = 33,
TOTAL_MAPPABLE_MIPSREGS = 34,
};
typedef int MIPSReg;
struct RegARM {
int mipsReg; // if -1, no mipsreg attached.
bool isDirty; // Should the register be written back?
};
enum RegMIPSLoc {
ML_IMM,
ML_ARMREG,
ML_MEM,
};
struct RegMIPS {
// Where is this MIPS register?
RegMIPSLoc loc;
// Data (only one of these is used, depending on loc. Could make a union).
u32 imm;
ARMReg reg;
bool spillLock; // if true, this register cannot be spilled.
// If loc == ML_MEM, it's back in its location in the CPU context struct.
};
// Initing is the default so the flag is reversed.
enum {
MAP_DIRTY = 1,
MAP_NOINIT = 2,
};
class ArmRegCache
{
public:
ArmRegCache(MIPSState *mips);
~ArmRegCache() {}
void Init(ARMXEmitter *emitter);
void Start(MIPSAnalyst::AnalysisResults &stats);
// Protect the arm register containing a MIPS register from spilling, to ensure that
// it's being kept allocated.
void SpillLock(MIPSReg reg, MIPSReg reg2 = -1, MIPSReg reg3 = -1);
void ReleaseSpillLocks();
void SetImm(MIPSReg reg, u32 immVal);
bool IsImm(MIPSReg reg) const;
u32 GetImm(MIPSReg reg) const;
// Returns an ARM register containing the requested MIPS register.
ARMReg MapReg(MIPSReg reg, int mapFlags = 0);
void MapInIn(MIPSReg rd, MIPSReg rs);
void MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad = true);
void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad = true);
void FlushArmReg(ARMReg r);
void FlushMipsReg(MIPSReg r);
void FlushAll();
ARMReg R(int preg); // Returns a cached register
void SetEmitter(ARMXEmitter *emitter) { emit = emitter; }
// For better log output only.
void SetCompilerPC(u32 compilerPC) { compilerPC_ = compilerPC; }
private:
int GetMipsRegOffset(MIPSReg r);
MIPSState *mips_;
ARMXEmitter *emit;
u32 compilerPC_;
enum {
NUM_ARMREG = 16,
NUM_MIPSREG = TOTAL_MAPPABLE_MIPSREGS,
};
RegARM ar[NUM_ARMREG];
RegMIPS mr[NUM_MIPSREG];
};
-195
View File
@@ -1,195 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ABI.h"
#include <ArmEmitter.h>
#include "../../MemMap.h"
#include "../MIPS.h"
#include "../../CoreTiming.h"
#include "MemoryUtil.h"
#include "ABI.h"
#include "Jit.h"
#include "../JitCommon/JitCommon.h"
#include "../../Core.h"
#include "Asm.h"
using namespace ArmGen;
//static int temp32; // unused?
//TODO - make an option
//#if _DEBUG
static bool enableDebug = false;
//#else
// bool enableDebug = false;
//#endif
//static bool enableStatistics = false; //unused?
//The standard ARM calling convention allocates the 16 ARM registers as:
// r15 is the program counter.
// r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register).
// r13 is the stack pointer. (The Push/Pop instructions in "Thumb" operating mode use this register only).
// r12 is the Intra-Procedure-call scratch register.
// r4 to r11: used to hold local variables.
// r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine.
// R2, R3, R4, R5, R6, R7, R8, R10, R11 // omitting R9?
// STATIC ALLOCATION ARM:
// R11 : Memory base pointer.
extern volatile CoreState coreState;
void Jit()
{
MIPSComp::jit->Compile(currentMIPS->pc);
}
// PLAN: no more block numbers - crazy opcodes just contain offset within
// dynarec buffer
// At this offset - 4, there is an int specifying the block number.
void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit)
{
enterCode = AlignCode16();
//ARMABI_PushAllCalleeSavedRegsAndAdjustStack();
/*
#ifndef _M_IX86
// Two statically allocated registers.
MOV(64, R(RBX), Imm64((u64)Memory::base));
MOV(64, R(R15), Imm64((u64)jit->GetBlockCache()->GetCodePointers())); //It's below 2GB so 32 bits are good enough
#endif
*/
SetCC(CC_AL);
//ARMABI_MOVIMM32(R11, (u32)Memory::base);
//ARMABI_MOVIMM32(R10, (u32)jit->GetBlockCache()->GetCodePointers());
outerLoop = GetCodePtr();
//ARMABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time
dispatcher = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
SetCC(CC_LT);
FixupBranch bail = B();
SetCC(CC_AL);
SetJumpTarget(skipToRealDispatch);
//INT3();
dispatcherNoCheck = GetCodePtr();
// LDR(R0, R11, R0, M(&mips->pc));
dispatcherPcInR0 = GetCodePtr();
_assert_msg_(CPU, Memory::base != 0, "Memory base bogus");
//ARMABI_MOVIMM32(R1, Memory::MEMVIEW32_MASK + 1);
AND(R0, R0, R1);
LDR(R0, R11, R0);
MOV(R1, R0);
//AND(R1, R1, Imm32(MIPS_EMUHACK_MASK));
//AND(R0, R0, Imm32(MIPS_EMUHACK_VALUE_MASK));
//CMP(R0, Imm32(MIPS_EMUHACK_OPCODE));
SetCC(CC_NEQ);
FixupBranch notfound = B();
SetCC(CC_AL);
// IDEA - we have 24 bits, why not just use offsets from base of code?
if (enableDebug)
{
// ADD(32, M(&mips->debugCount), Imm8(1));
}
//grab from list and jump to it
// ADD(R0, R10, LSL(R0, 2));
B(R0);
SetJumpTarget(notfound);
//BL(&Jit);
//B(dispatcherNoCheck); // no point in special casing this
SetJumpTarget(bail);
doTiming = GetCodePtr();
testExternalExceptions = GetCodePtr();
//TEST(32, M((void *)&mips->exceptions), Imm32(MIPSState::BREAKING_EXCEPTIONS));
//FixupBranch noExtException = J_CC(CC_Z);
//MOV(32, R(EAX), M(&mips->pc));
// MOV(32, M(&NPC), R(EAX));
// ABI_CallFunction(reinterpret_cast<void *>(&MIPSState::CheckExternalExceptions));
//SetJumpTarget(noExtException);
//CMP(M((void*)&coreState), Imm8(0));
SetCC(CC_EQ);
//B(outerLoop);
SetCC(CC_AL);
//Landing pad for drec space
//ARMABI_PopAllCalleeSavedRegsAndAdjustStack();
B(_LR);
breakpointBailout = GetCodePtr();
//Landing pad for drec space
//ARMABI_PopAllCalleeSavedRegsAndAdjustStack();
//RET();
GenerateCommon();
}
void AsmRoutineManager::GenerateCommon()
{
/*
fifoDirectWrite8 = AlignCode4();
GenFifoWrite(8);
fifoDirectWrite16 = AlignCode4();
GenFifoWrite(16);
fifoDirectWrite32 = AlignCode4();
GenFifoWrite(32);
fifoDirectWriteFloat = AlignCode4();
GenFifoFloatWrite();
fifoDirectWriteXmm64 = AlignCode4();
GenFifoXmm64Write();
GenQuantizedLoads();
GenQuantizedStores();
GenQuantizedSingleStores();
*/
//CMPSD(R(XMM0), M(&zero),
// TODO
// Fast write routines - special case the most common hardware write
// TODO: use this.
// Even in x86, the param values will be in the right registers.
/*
const u8 *fastMemWrite8 = AlignCode16();
CMP(32, R(ABI_PARAM2), Imm32(0xCC008000));
FixupBranch skip_fast_write = J_CC(CC_NE, false);
MOV(32, EAX, M(&m_gatherPipeCount));
MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1);
ADD(32, 1, M(&m_gatherPipeCount));
RET();
SetJumpTarget(skip_fast_write);
CALL((void *)&Memory::Write_U8);*/
}
-81
View File
@@ -1,81 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#ifndef _JIT64ASM_H
#define _JIT64ASM_H
#include <ArmEmitter.h>
#include "../MIPS.h"
// In PPSSPP, we don't use inline assembly. Instead, we generate all machine-near
// code at runtime. In the case of fixed code like this, after writing it, we write
// protect the memory, essentially making it work just like precompiled code.
// There are some advantages to this approach:
// 1) No need to setup an external assembler in the build.
// 2) Cross platform, as long as it's x86/x64.
// 3) Can optimize code at runtime for the specific CPU model.
// There aren't really any disadvantages other than having to maintain code emitters,
// which we have to do anyway :)
//
// To add a new asm routine, just add another const here, and add the code to Generate.
// Also, possibly increase the size of the code buffer.
namespace MIPSComp
{
class Jit;
}
class AsmRoutineManager : public ArmGen::ARMXCodeBlock
{
private:
void Generate(MIPSState *mips, MIPSComp::Jit *jit);
void GenerateCommon();
public:
AsmRoutineManager()
{
}
void Init(MIPSState *mips, MIPSComp::Jit *jit)
{
AllocCodeSpace(8192);
Generate(mips, jit);
WriteProtect();
}
~AsmRoutineManager()
{
FreeCodeSpace();
}
const u8 *enterCode;
const u8 *outerLoop;
const u8 *dispatcher;
const u8 *dispatcherNoCheck;
const u8 *dispatcherPcInR0;
const u8 *fpException;
const u8 *testExceptions;
const u8 *testExternalExceptions;
const u8 *dispatchPcInEAX;
const u8 *doTiming;
const u8 *breakpointBailout;
};
#endif // _JIT64ASM_H
-356
View File
@@ -1,356 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../../HLE/HLE.h"
#include "../MIPS.h"
#include "../MIPSCodeUtils.h"
#include "../MIPSAnalyst.h"
#include "../MIPSTables.h"
#include "Jit.h"
#include "RegCache.h"
#include "JitCache.h"
#include <ArmEmitter.h>
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6 ) & 0x1F)
#define _POS ((op>>6 ) & 0x1F)
#define _SIZE ((op>>11 ) & 0x1F)
#define LOOPOPTIMIZATION 0
using namespace MIPSAnalyst;
namespace MIPSComp
{
/*
void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely)
{
int offset = (signed short)(op&0xFFFF)<<2;
int rt = _RT;
int rs = _RS;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC+4);
//Compile the delay slot
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rt && GetOutReg(delaySlotOp) != rs;// IsDelaySlotNice(op, delaySlotOp);
if (!delaySlotIsNice)
{
ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC);
}
// The delay slot being nice doesn't really matter though...
if (rs == 0)
{
CMP(32, gpr.R(rt), Imm32(0));
}
else
{
gpr.BindToRegister(rs, true, false);
CMP(32, gpr.R(rs), rt == 0 ? Imm32(0) : gpr.R(rt));
}
FlushAll();
js.inDelaySlot = true;
Gen::FixupBranch ptr;
if (!likely)
{
PUSHF(); // preserve flag around the delay slot!
CompileAt(js.compilerPC + 4);
FlushAll();
POPF(); // restore flag!
ptr = J_CC(cc, true);
}
else
{
ptr = J_CC(cc, true);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC+8, 1);
js.compiling = false;
}
*/
/*
void Jit::BranchRSZeroComp(u32 op, Gen::CCFlags cc, bool likely)
{
int offset = (signed short)(op&0xFFFF)<<2;
int rs = _RS;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs;
if (!delaySlotIsNice)
{
ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC);
}
gpr.BindToRegister(rs, true, false);
CMP(32, gpr.R(rs), Imm32(0));
FlushAll();
Gen::FixupBranch ptr;
js.inDelaySlot = true;
if (!likely)
{
PUSHF(); // preserve flag around the delay slot! Better hope the delay slot instruction doesn't need to fall back to interpreter...
CompileAt(js.compilerPC + 4);
FlushAll();
POPF(); // restore flag!
ptr = J_CC(cc, true);
}
else
{
ptr = J_CC(cc, true);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC + 8, 1);
js.compiling = false;
}
*/
void Jit::Comp_RelBranch(u32 op)
{
/*
switch (op>>26)
{
case 4: BranchRSRTComp(op, CC_NZ, false); break;//beq
case 5: BranchRSRTComp(op, CC_Z, false); break;//bne
case 6: BranchRSZeroComp(op, CC_G, false); break;//blez
case 7: BranchRSZeroComp(op, CC_LE, false); break;//bgtz
case 20: BranchRSRTComp(op, CC_NZ, true); break;//beql
case 21: BranchRSRTComp(op, CC_Z, true); break;//bnel
case 22: BranchRSZeroComp(op, CC_G, true); break;//blezl
case 23: BranchRSZeroComp(op, CC_LE, true); break;//bgtzl
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
*/
js.compiling = false;
}
void Jit::Comp_RelBranchRI(u32 op)
{
/*
switch ((op >> 16) & 0x1F)
{
case 0: BranchRSZeroComp(op, CC_GE, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz
case 1: BranchRSZeroComp(op, CC_L, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez
case 2: BranchRSZeroComp(op, CC_GE, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl
case 3: BranchRSZeroComp(op, CC_L, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
*/
js.compiling = false;
}
// If likely is set, discard the branch slot if NOT taken.
/*
void Jit::BranchFPFlag(u32 op, Gen::CCFlags cc, bool likely)
{
int offset = (signed short)(op & 0xFFFF) << 2;
u32 targetAddr = js.compilerPC + offset + 4;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp);
if (!delaySlotIsNice)
{
ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :(");
}
FlushAll();
TEST(32, M((void *)&(mips_->fpcond)), Imm32(1));
Gen::FixupBranch ptr;
js.inDelaySlot = true;
if (!likely)
{
PUSHF(); // preserve flag around the delay slot!
CompileAt(js.compilerPC + 4);
FlushAll();
POPF(); // restore flag!
ptr = J_CC(cc, true);
}
else
{
ptr = J_CC(cc, true);
CompileAt(js.compilerPC + 4);
FlushAll();
}
js.inDelaySlot = false;
// Take the branch
WriteExit(targetAddr, 0);
SetJumpTarget(ptr);
// Not taken
WriteExit(js.compilerPC + 8, 1);
js.compiling = false;
}
*/
void Jit::Comp_FPUBranch(u32 op)
{
/*
switch((op >> 16) & 0x1f)
{
case 0: BranchFPFlag(op, CC_NZ, false); break; // bc1f
case 1: BranchFPFlag(op, CC_Z, false); break; // bc1t
case 2: BranchFPFlag(op, CC_NZ, true); break; // bc1fl
case 3: BranchFPFlag(op, CC_Z, true); break; // bc1tl
default:
_dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted");
break;
}
*/
js.compiling = false;
}
void Jit::Comp_VBranch(u32 op)
{
/*
Comp_Generic(op + 4);
Comp_Generic(op);
js.compiling = false;
return;
int imm = (signed short)(op&0xFFFF)<<2;
u32 targetAddr = js.compilerPC + imm + 4;
int imm3 = (op >> 18) & 7;
int val = (mips_->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1;
switch ((op >> 16) & 3)
{
//case 0: if (!val) DelayBranchTo(addr); else PC += 4; break; //bvf
//case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt
//case 2: if (!val) DelayBranchTo(addr); else PC += 8; break; //bvfl
//case 3: if ( val) DelayBranchTo(addr); else PC += 8; break; //bvtl
//TODO
}
js.compiling = false;
*/
}
void Jit::Comp_Jump(u32 op)
{
/*
u32 off = ((op & 0x3FFFFFF) << 2);
u32 targetAddr = (js.compilerPC & 0xF0000000) | off;
//Delay slot
CompileAt(js.compilerPC + 4);
FlushAll();
switch (op >> 26)
{
case 2: //j
WriteExit(targetAddr, 0);
break;
case 3: //jal
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8)); // Save return address
WriteExit(targetAddr, 0);
break;
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
js.compiling = false;
*/
}
void Jit::Comp_JumpReg(u32 op)
{
/*
int rs = _RS;
u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4);
bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs;
// Do what with that information?
gpr.BindToRegister(rs, true, false);
PUSH(32, gpr.R(rs));
CompileAt(js.compilerPC + 4);
FlushAll();
POP(32, R(EAX));
switch (op & 0x3f)
{
case 8: //jr
break;
case 9: //jalr
MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8));
break;
default:
_dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled");
break;
}
WriteExitDestInEAX();
js.compiling = false;
*/
}
void Jit::Comp_Syscall(u32 op)
{
/*
FlushAll();
MOV(32, R(EAX), M(&mips_->r[MIPS_REG_RA]));
MOV(32, M(&mips_->pc), R(EAX));
ABI_CallFunctionC(&CallSyscall, op);
WriteSyscallExit();*/
}
} // namespace Mipscomp
-133
View File
@@ -1,133 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../../MemMap.h"
#include "../MIPSAnalyst.h"
#include "Jit.h"
#include "RegCache.h"
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6 ) & 0x1F)
#define _POS ((op>>6 ) & 0x1F)
#define _SIZE ((op>>11 ) & 0x1F)
#define OLDD Comp_Generic(op); return;
namespace MIPSComp
{
void Jit::Comp_ITypeMem(u32 op)
{
OLDD
int offset = (signed short)(op&0xFFFF);
int rt = _RT;
int rs = _RS;
int o = op>>26;
if (((op >> 29) & 1) == 0 && rt == 0) {
// Don't load anything into $zr
return;
}
switch (o)
{
case 37: //R(rt) = ReadMem16(addr); break; //lhu
case 36: //R(rt) = ReadMem8 (addr); break; //lbu
Comp_Generic(op);
return;
case 35: //R(rt) = ReadMem32(addr); break; //lw
/*
gpr.Lock(rt, rs);
gpr.BindToRegister(rt, rt == rs, true);
MOV(32, R(EAX), gpr.R(rs));
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
MOV(32, gpr.R(rt), MDisp(EAX, (u32)Memory::base + offset));
gpr.UnlockAll();*/
break;
case 132: //R(rt) = (u32)(s32)(s8) ReadMem8 (addr); break; //lb
case 133: //R(rt) = (u32)(s32)(s16)ReadMem16(addr); break; //lh
case 136: //R(rt) = ReadMem8 (addr); break; //lbu
case 140: //WriteMem8 (addr, R(rt)); break; //sb
case 40:
case 41: //WriteMem16(addr, R(rt)); break; //sh
Comp_Generic(op);
return;
case 43: //WriteMem32(addr, R(rt)); break; //sw
/*
{
gpr.Lock(rt, rs);
gpr.BindToRegister(rt, true, false);
MOV(32, R(EAX), gpr.R(rs));
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
MOV(32, MDisp(EAX, (u32)Memory::base + offset), gpr.R(rt));
gpr.UnlockAll();
}*/
break;
case 134: //lwl
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//R(rt) = ( u32(R(rt)) & (0x00ffffff >> shift) ) | ( mem << (24 - shift) );
}
break;
case 138: //lwr
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//R(rt) = ( u32(rt) & (0xffffff00 << (24 - shift)) ) | ( mem >> shift );
}
break;
case 142: //swl
{
Crash();
//u32 shift = (addr & 3) << 3;
//u32 mem = ReadMem32(addr & 0xfffffffc);
//WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) >> (24 - shift) ) ) |
// ( mem & (0xffffff00 << shift) ));
}
break;
case 146: //swr
{
Crash();
// u32 shift = (addr & 3) << 3;
// u32 mem = ReadMem32(addr & 0xfffffffc);
//
// WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) << shift ) |
// (mem & (0x00ffffff >> (24 - shift)) ) ) );
}
break;
default:
Comp_Generic(op);
return ;
}
}
}
-236
View File
@@ -1,236 +0,0 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../../Core.h"
#include "../../CoreTiming.h"
#include "../MIPS.h"
#include "../MIPSCodeUtils.h"
#include "../MIPSInt.h"
#include "../MIPSTables.h"
#include "RegCache.h"
#include "Jit.h"
extern u32 *pspmainram;
namespace MIPSComp
{
/*
void Comp_MemRead32KnownRAM(int srcreg, int offset, int reg)
{
MOV_MemoryToReg(1, reg, ModRM_disp32_EAX+srcreg, (u32)pspmainram - 0x08000000 + offset);
}
void Comp_MemWrite32KnownRAM(int srcreg, int offset, int reg)
{
MOV_RegToMemory(1, reg, ModRM_disp32_EAX+srcreg, (u32)pspmainram - 0x08000000 + offset);
}
#define RM(reg) (&(currentMIPS->r[reg]))
struct RegInfo
{
bool cached;
int x86reg;
};
RegInfo regs[32];
void MovToReg(int reg, u32 value)
{
if (regs[reg].cached)
{
::MOV_ImmToReg(1,regs[reg].x86reg,value,0);
}
else
{
::MOV_ImmToMemory(1,ModRM_disp32,(u32)(RM(reg)),value);
}
}
*/
Jit::Jit(MIPSState *mips) : blocks(mips), mips_(mips)
{
blocks.Init();
asm_.Init(mips, this);
gpr.SetEmitter(this);
fpr.SetEmitter(this);
AllocCodeSpace(1024 * 1024 * 16);
}
void Jit::FlushAll()
{
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
}
void Jit::ClearCache()
{
blocks.Clear();
ClearCodeSpace();
}
u8 *codeCache;
#define CACHESIZE 16384*1024
void Jit::CompileAt(u32 addr)
{
u32 op = Memory::Read_Instruction(addr);
MIPSCompileOp(op);
}
void Jit::Compile(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull())
{
ClearCache();
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock *b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, b));
}
void Jit::RunLoopUntil(u64 globalticks)
{
// TODO: copy globalticks somewhere
((void (*)())asm_.enterCode)();
}
const u8 *Jit::DoJit(u32 em_address, JitBlock *b)
{
js.cancel = false;
js.blockStart = js.compilerPC = mips_->pc;
js.downcountAmount = 0;
js.curBlock = b;
js.compiling = true;
js.inDelaySlot = false;
b->normalEntry = GetCodePtr();
// TODO: this needs work
MIPSAnalyst::AnalysisResults analysis; // = MIPSAnalyst::Analyze(em_address);
gpr.Start(mips_, analysis);
fpr.Start(mips_, analysis);
int numInstructions = 0;
int cycles = 0;
while (js.compiling)
{
u32 inst = Memory::Read_Instruction(js.compilerPC);
js.downcountAmount += MIPSGetInstructionCycleEstimate(inst);
MIPSCompileOp(inst);
js.compilerPC += 4;
numInstructions++;
}
b->codeSize = GetCodePtr() - b->normalEntry;
NOP();
AlignCode16();
b->originalSize = numInstructions;
return b->normalEntry;
}
void Jit::Comp_RunBlock(u32 op)
{
// This shouldn't be necessary, the dispatcher should catch us before we get here.
ERROR_LOG(DYNA_REC, "Comp_RunBlock");
}
void Jit::Comp_Generic(u32 op)
{
FlushAll();
MIPSInterpretFunc func = MIPSGetInterpretFunc(op);
if (func)
{
//MOV(32, M(&mips_->pc), Imm32(js.compilerPC));
//ABI_CallFunctionC(func, op);
}
}
void Jit::WriteExit(u32 destination, int exit_num)
{
//SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
//If nobody has taken care of this yet (this can be removed when all branches are done)
JitBlock *b = js.curBlock;
b->exitAddress[exit_num] = destination;
b->exitPtrs[exit_num] = GetWritableCodePtr();
// Link opportunity!
int block = blocks.GetBlockNumberFromStartAddress(destination);
if (block >= 0 && jo.enableBlocklink)
{
// It exists! Joy of joy!
//JMP(blocks.GetBlock(block)->checkedEntry, true);
b->linkStatus[exit_num] = true;
}
else
{
//MOV(32, M(&mips_->pc), Imm32(destination));
//JMP(asm_.dispatcher, true);
}
}
void Jit::WriteExitDestInEAX()
{
/*
MOV(32, M(&mips_->pc), R(EAX));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_.dispatcher, true);
*/
}
/*
void Jit::WriteRfiExitDestInEAX()
{
MOV(32, M(&mips_->pc), R(EAX));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.testExceptions, true);
}*/
void Jit::WriteSyscallExit()
{
/*
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_.dispatcher, true);
*/
}
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define _FS ((op>>11) & 0x1F)
#define _FT ((op>>16) & 0x1F)
#define _FD ((op>>6) & 0x1F)
#define _POS ((op>>6) & 0x1F)
#define _SIZE ((op>>11) & 0x1F)
//memory regions:
//
// 08-0A
// 48-4A
// 04-05
// 44-45
// mov eax, addrreg
// shr eax, 28
// mov eax, [table+eax]
// mov dreg, [eax+offreg]
}
-409
View File
@@ -1,409 +0,0 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "../MIPS.h"
#include "../MIPSTables.h"
#include "../MIPSAnalyst.h"
#include "Jit.h"
#include "Asm.h"
#include "RegCache.h"
#include "CommonFuncs.h"
using namespace ArmGen;
RegCache::RegCache() : emit(0) {
memset(locks, 0, sizeof(locks));
memset(xlocks, 0, sizeof(xlocks));
memset(saved_locks, 0, sizeof(saved_locks));
memset(saved_xlocks, 0, sizeof(saved_xlocks));
memset(regs, 0, sizeof(regs));
memset(xregs, 0, sizeof(xregs));
memset(saved_regs, 0, sizeof(saved_regs));
memset(saved_xregs, 0, sizeof(saved_xregs));
}
static const int allocationOrder[] =
{
R2, R3, R4, R5, R6, R7, R8, R10, R11 // omitting R9?
};
void RegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
for (int i = 0; i < NUMARMREGS; i++)
{
xregs[i].free = true;
xregs[i].dirty = false;
xlocks[i] = false;
}
for (int i = 0; i < 32; i++)
{
regs[i].location = GetDefaultLocation(i);
regs[i].away = false;
}
// todo: sort to find the most popular regs
/*
int maxPreload = 2;
for (int i = 0; i < 32; i++)
{
if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2)
{
LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false);
maxPreload--;
if (!maxPreload)
break;
}
}*/
//Find top regs - preload them (load bursts ain't bad)
//But only preload IF written OR reads >= 3
}
// these are MIPS reg indices
void RegCache::Lock(int p1, int p2, int p3, int p4)
{
locks[p1] = true;
if (p2 != 0xFF) locks[p2] = true;
if (p3 != 0xFF) locks[p3] = true;
if (p4 != 0xFF) locks[p4] = true;
}
// these are x64 reg indices
void RegCache::LockX(int x1, int x2, int x3, int x4)
{
if (xlocks[x1]) {
PanicAlert("RegCache: x %i already locked!", x1);
}
xlocks[x1] = true;
if (x2 != 0xFF) xlocks[x2] = true;
if (x3 != 0xFF) xlocks[x3] = true;
if (x4 != 0xFF) xlocks[x4] = true;
}
bool RegCache::IsFreeX(int xreg) const
{
return xregs[xreg].free && !xlocks[xreg];
}
void RegCache::UnlockAll()
{
for (int i = 0; i < 32; i++)
locks[i] = false;
}
void RegCache::UnlockAllX()
{
for (int i = 0; i < NUMARMREGS; i++)
xlocks[i] = false;
}
ARMReg RegCache::GetFreeXReg()
{
int aCount;
const int *aOrder = GetAllocationOrder(aCount);
for (int i = 0; i < aCount; i++)
{
ARMReg xr = (ARMReg)aOrder[i];
if (!xlocks[xr] && xregs[xr].free)
{
return (ARMReg)xr;
}
}
//Okay, not found :( Force grab one
//TODO - add a pass to grab xregs whose ppcreg is not used in the next 3 instructions
for (int i = 0; i < aCount; i++)
{
ARMReg xr = (ARMReg)aOrder[i];
if (xlocks[xr])
continue;
int preg = xregs[xr].ppcReg;
if (!locks[preg])
{
StoreFromRegister(preg);
return xr;
}
}
//Still no dice? Die!
_assert_msg_(DYNA_REC, 0, "Regcache ran out of regs");
return (ARMReg) -1;
}
void RegCache::SaveState()
{
memcpy(saved_locks, locks, sizeof(locks));
memcpy(saved_xlocks, xlocks, sizeof(xlocks));
memcpy(saved_regs, regs, sizeof(regs));
memcpy(saved_xregs, xregs, sizeof(xregs));
}
void RegCache::LoadState()
{
memcpy(xlocks, saved_xlocks, sizeof(xlocks));
memcpy(locks, saved_locks, sizeof(locks));
memcpy(regs, saved_regs, sizeof(regs));
memcpy(xregs, saved_xregs, sizeof(xregs));
}
void RegCache::FlushR(ARMReg reg)
{
if (reg >= NUMARMREGS)
PanicAlert("Flushing non existent reg");
if (!xregs[reg].free)
{
StoreFromRegister(xregs[reg].ppcReg);
}
}
int RegCache::SanityCheck() const
{
for (int i = 0; i < 32; i++) {
if (regs[i].away) {
if (regs[i].location.IsSimpleReg()) {
ARMReg simple = regs[i].location.GetSimpleReg();
if (xlocks[simple])
return 1;
if (xregs[simple].ppcReg != i)
return 2;
}
else if (regs[i].location.IsImm())
return 3;
}
}
return 0;
}
void RegCache::DiscardRegContentsIfCached(int preg)
{
if (regs[preg].away && regs[preg].location.IsSimpleReg())
{
ARMReg xr = regs[preg].location.GetSimpleReg();
xregs[xr].free = true;
xregs[xr].dirty = false;
xregs[xr].ppcReg = -1;
regs[preg].away = false;
regs[preg].location = GetDefaultLocation(preg);
}
}
void GPRRegCache::SetImmediate32(int preg, u32 immValue)
{
//if (regs[preg].away == true && regs[preg].location.IsImm() && regs[preg].location.offset == immValue)
// return;
DiscardRegContentsIfCached(preg);
regs[preg].away = true;
regs[preg].location.SetImm32(immValue);
}
void GPRRegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
RegCache::Start(mips, stats);
}
void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats)
{
RegCache::Start(mips, stats);
}
const int *GPRRegCache::GetAllocationOrder(int &count)
{
count = sizeof(allocationOrder) / sizeof(const int);
return allocationOrder;
}
const int *FPURegCache::GetAllocationOrder(int &count)
{
static const int allocationOrder[] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
};
count = sizeof(allocationOrder) / sizeof(int);
return allocationOrder;
}
Location GPRRegCache::GetDefaultLocation(int reg) const
{
Location loc;
loc.SetM(&mips->r[reg]);
return loc;
}
Location FPURegCache::GetDefaultLocation(int reg) const
{
Location loc;
loc.SetM(&mips->f[reg]);
return loc;
}
void RegCache::KillImmediate(int preg, bool doLoad, bool makeDirty)
{
if (regs[preg].away)
{
if (regs[preg].location.IsImm())
BindToRegister(preg, doLoad, makeDirty);
else if (regs[preg].location.IsSimpleReg())
xregs[RX(preg)].dirty |= makeDirty;
}
}
void GPRRegCache::BindToRegister(int i, bool doLoad, bool makeDirty)
{
if (!regs[i].away && regs[i].location.IsImm())
PanicAlert("Bad immediate");
if (!regs[i].away || (regs[i].away && regs[i].location.IsImm()))
{
ARMReg xr = GetFreeXReg();
if (xregs[xr].dirty) PanicAlert("Xreg already dirty");
if (xlocks[xr]) PanicAlert("GetFreeXReg returned locked register");
xregs[xr].free = false;
xregs[xr].ppcReg = i;
xregs[xr].dirty = makeDirty || regs[i].location.IsImm();
Location newloc;
newloc.SetReg(xr);
//if (doLoad)
// emit->MOV(32, newloc, regs[i].location);
for (int j = 0; j < 32; j++)
{
if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr)
{
PanicAlert("bad");
}
}
regs[i].away = true;
regs[i].location = newloc;
}
else
{
// reg location must be simplereg; memory locations
// and immediates are taken care of above.
xregs[RX(i)].dirty |= makeDirty;
}
if (xlocks[RX(i)]) {
PanicAlert("Seriously WTF, this reg should have been flushed");
}
}
void GPRRegCache::StoreFromRegister(int i)
{
if (regs[i].away)
{
bool doStore;
if (regs[i].location.IsSimpleReg())
{
ARMReg xr = RX(i);
xregs[xr].free = true;
xregs[xr].ppcReg = -1;
doStore = xregs[xr].dirty;
xregs[xr].dirty = false;
}
else
{
//must be immediate - do nothing
doStore = true;
}
Location newLoc = GetDefaultLocation(i);
if (doStore)
;
// emit->MOV(32, newLoc, regs[i].location);
regs[i].location = newLoc;
regs[i].away = false;
}
}
void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty)
{
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm");
if (!regs[i].away)
{
// Reg is at home in the memory register file. Let's pull it out.
ARMReg xr = GetFreeXReg();
_assert_msg_(DYNA_REC, xr < NUMARMREGS, "WTF - load - invalid reg");
xregs[xr].ppcReg = i;
xregs[xr].free = false;
xregs[xr].dirty = makeDirty;
Location newloc;
newloc.SetReg(xr);
if (doLoad)
{
//if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF))
//{
// PanicAlert("WARNING - misaligned fp register location %i", i);
//}
//emit->MOVAPD(xr, regs[i].location);
}
regs[i].location = newloc;
regs[i].away = true;
} else {
// There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary.
xregs[RX(i)].dirty |= makeDirty;
}
}
void FPURegCache::StoreFromRegister(int i)
{
_assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm");
if (regs[i].away)
{
ARMReg xr = regs[i].location.GetSimpleReg();
_assert_msg_(DYNA_REC, xr < NUMARMREGS, "WTF - store - invalid reg");
xregs[xr].free = true;
xregs[xr].dirty = false;
xregs[xr].ppcReg = -1;
Location newLoc = GetDefaultLocation(i);
// emit->MOVAPD(newLoc, xr);
regs[i].location = newLoc;
regs[i].away = false;
}
else
{
// _assert_msg_(DYNA_REC,0,"already stored");
}
}
void RegCache::Flush(FlushMode mode)
{
for (int i = 0; i < NUMARMREGS; i++) {
if (xlocks[i])
PanicAlert("Someone forgot to unlock X64 reg %i.", i);
}
for (int i = 0; i < 32; i++)
{
if (locks[i])
{
PanicAlert("Somebody forgot to unlock PPC reg %i.", i);
}
if (regs[i].away)
{
if (regs[i].location.IsSimpleReg())
{
ARMReg xr = RX(i);
StoreFromRegister(i);
xregs[xr].dirty = false;
}
else if (regs[i].location.IsImm())
{
StoreFromRegister(i);
}
else
{
_assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc);
}
}
}
}
-170
View File
@@ -1,170 +0,0 @@
// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#pragma once
#include "../MIPSAnalyst.h"
#include <ArmEmitter.h>
using namespace ArmGen;
enum FlushMode
{
FLUSH_ALL
};
enum GrabMode
{
M_READ = 1,
M_WRITE = 2,
M_READWRITE = 3,
};
enum Loc {
LOC_IMM,
LOC_REG,
LOC_MEM
};
struct Location
{
Loc loc;
bool IsSimpleReg() const {return loc == LOC_REG;}
ARMReg GetSimpleReg() const {return reg;}
bool IsImm() const { return loc == LOC_IMM; }
void SetImm32(u32 i) {loc = LOC_IMM; imm = i;}
void SetM(void *p) {loc = LOC_MEM; ptr = (u32 *)p;}
void SetReg(ARMReg r) {loc = LOC_REG; reg = r;}
union {
u32 *ptr;
ARMReg reg;
u32 imm;
};
};
struct MIPSCachedReg
{
Location location;
bool away; // value not in source register
};
struct ARMCachedReg
{
int ppcReg;
bool dirty;
bool free;
};
typedef int XReg;
typedef int PReg;
#define NUMARMREGS 15
class RegCache
{
private:
bool locks[32];
bool saved_locks[32];
bool saved_xlocks[NUMARMREGS];
protected:
bool xlocks[NUMARMREGS];
MIPSCachedReg regs[32];
ARMCachedReg xregs[NUMARMREGS];
MIPSCachedReg saved_regs[32];
ARMCachedReg saved_xregs[NUMARMREGS];
virtual const int *GetAllocationOrder(int &count) = 0;
ARMXEmitter *emit;
public:
MIPSState *mips;
RegCache();
virtual ~RegCache() {}
virtual void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) = 0;
void DiscardRegContentsIfCached(int preg);
void SetEmitter(ARMXEmitter *emitter) {emit = emitter;}
void FlushR(ARMReg reg);
void FlushR(ARMReg reg, ARMReg reg2) {FlushR(reg); FlushR(reg2);}
void FlushLockX(ARMReg reg) {
FlushR(reg);
LockX(reg);
}
void FlushLockX(ARMReg reg1, ARMReg reg2) {
FlushR(reg1); FlushR(reg2);
LockX(reg1); LockX(reg2);
}
virtual void Flush(FlushMode mode);
// virtual void Flush(PPCAnalyst::CodeOp *op) {Flush(FLUSH_ALL);}
int SanityCheck() const;
void KillImmediate(int preg, bool doLoad, bool makeDirty);
//TODO - instead of doload, use "read", "write"
//read only will not set dirty flag
virtual void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true) = 0;
virtual void StoreFromRegister(int preg) = 0;
const Location &R(int preg) const {return regs[preg].location;}
ARMReg RX(int preg) const
{
if (regs[preg].away && regs[preg].location.IsSimpleReg())
return regs[preg].location.GetSimpleReg();
PanicAlert("Not so simple - %i", preg);
return (ARMReg)-1;
}
virtual Location GetDefaultLocation(int reg) const = 0;
// Register locking. A locked registers will not be spilled when trying to find a new free register.
void Lock(int p1, int p2=0xff, int p3=0xff, int p4=0xff);
void LockX(int x1, int x2=0xff, int x3=0xff, int x4=0xff);
void UnlockAll();
void UnlockAllX();
bool IsFreeX(int xreg) const;
ARMReg GetFreeXReg();
void SaveState();
void LoadState();
};
class GPRRegCache : public RegCache
{
public:
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
Location GetDefaultLocation(int reg) const;
const int *GetAllocationOrder(int &count);
void SetImmediate32(int preg, u32 immValue);
};
class FPURegCache : public RegCache
{
public:
void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats);
void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true);
void StoreFromRegister(int preg);
const int *GetAllocationOrder(int &count);
Location GetDefaultLocation(int reg) const;
};
+1 -1
View File
@@ -18,7 +18,7 @@
#pragma once
#if defined(ARM)
#include "../ARM/Jit.h"
#include "../ARM/ArmJit.h"
#else
#include "../x86/Jit.h"
#endif
+6 -8
View File
@@ -24,8 +24,8 @@
#include "../HLE/sceDisplay.h"
#if defined(ARM)
#include "ARM/JitCache.h"
#include "ARM/Jit.h"
#include "ARM/ArmJitCache.h"
#include "ARM/ArmJit.h"
#else
#include "x86/JitCache.h"
#include "x86/Jit.h"
@@ -40,7 +40,6 @@ MIPSDebugInterface *currentDebugMIPS = &debugr4k;
MIPSState::MIPSState()
{
cpuType = CPUTYPE_ALLEGREX;
MIPSComp::jit = 0;
}
@@ -57,7 +56,7 @@ void MIPSState::Reset()
delete MIPSComp::jit;
MIPSComp::jit = 0;
}
if (PSP_CoreParameter().cpuCore == CPU_JIT)
MIPSComp::jit = new MIPSComp::Jit(this);
@@ -90,11 +89,11 @@ void MIPSState::Reset()
fcr0 = 0;
fcr31 = 0;
debugCount = 0;
exceptions = 0;
currentMIPS = this;
inDelaySlot = false;
llBit = 0;
nextPC = 0;
downcount = 0;
// Initialize the VFPU random number generator with .. something?
rng.Init(0x1337);
}
@@ -112,6 +111,7 @@ void MIPSState::DoState(PointerWrap &p)
p.DoArray(vfpuWriteMask, sizeof(vfpuWriteMask) / sizeof(vfpuWriteMask[0]));
p.Do(pc);
p.Do(nextPC);
p.Do(downcount);
p.Do(hi);
p.Do(lo);
p.Do(fpcond);
@@ -120,8 +120,6 @@ void MIPSState::DoState(PointerWrap &p)
rng.DoState(p);
p.Do(inDelaySlot);
p.Do(llBit);
p.Do(cpuType);
p.Do(exceptions);
p.Do(debugCount);
p.DoMarker("MIPSState");
}
@@ -135,7 +133,7 @@ void MIPSState::SetWriteMask(const bool wm[4])
void MIPSState::SingleStep()
{
int cycles = MIPS_SingleStep();
CoreTiming::downcount -= cycles;
currentMIPS->downcount -= cycles;
CoreTiming::Advance();
}
+6 -9
View File
@@ -104,7 +104,7 @@ private:
u32 m_z;
};
class MIPSState : public CPU
class MIPSState
{
public:
MIPSState();
@@ -113,6 +113,7 @@ public:
void Reset();
void DoState(PointerWrap &p);
// MUST start with r!
u32 r[32];
float f[32];
float v[128];
@@ -121,24 +122,20 @@ public:
u32 pc;
u32 nextPC;
u32 downcount; // This really doesn't belong here, it belongs in CoreTiming. But you gotta do what you gotta do, this needs to be reachable in the ARM JIT.
u32 hi;
u32 lo;
u32 fpcond; //separate for speed - TODO: not worth it
u32 fcr0;
u32 fcr31; //fpu control register
GMRng rng; // VFPU hardware random number generator. Probably not the right type.
u32 fpcond; // cache the cond flag of fcr31 (& 1 << 23)
bool inDelaySlot;
int llBit; // ll/sc
CPUType cpuType;
// TODO: How do we handle exceptions?
u32 exceptions;
enum { BREAKING_EXCEPTIONS = 1 };
GMRng rng; // VFPU hardware random number generator. Probably not the right type.
// Debug stuff
u32 debugCount; // can be used to count basic blocks before crashes, etc.
+1 -1
View File
@@ -28,7 +28,7 @@
const char *MIPSDebugInterface::disasm(unsigned int address, unsigned int align)
{
CPU *x = currentCPU;
MIPSState *x = currentCPU;
currentCPU = cpu;
static char mojs[256];
+1
View File
@@ -272,6 +272,7 @@ namespace MIPSInt
// There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored.
if (op == 0x03e00008)
return;
ERROR_LOG(HLE, "Jump in delay slot :(");
_dbg_assert_msg_(CPU,0,"Jump in delay slot :(");
}
+14 -8
View File
@@ -885,7 +885,7 @@ void MIPSCompileOp(u32 op)
}
void MIPSDisAsm(u32 op, u32 pc, char *out)
void MIPSDisAsm(u32 op, u32 pc, char *out, bool tabsToSpaces)
{
if (op == 0)
{
@@ -896,10 +896,16 @@ void MIPSDisAsm(u32 op, u32 pc, char *out)
{
disPC = pc;
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (instr && instr->disasm)
if (instr && instr->disasm) {
instr->disasm(op, out);
else
{
if (tabsToSpaces) {
while (*out) {
if (*out == '\t')
*out = ' ';
out++;
}
}
} else {
strcpy(out, "no instruction :(");
//__asm int 3
MIPSGetInstruction(op);
@@ -942,7 +948,7 @@ int MIPSInterpret_RunUntil(u64 globalTicks)
while (coreState == CORE_RUNNING)
{
// NEVER stop in a delay slot!
while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING)
while (curMips->downcount >= 0 && coreState == CORE_RUNNING)
{
// int cycles = 0;
{
@@ -984,7 +990,7 @@ int MIPSInterpret_RunUntil(u64 globalTicks)
curMips->pc = curMips->nextPC;
curMips->inDelaySlot = false;
}
CoreTiming::downcount -= 1;
curMips->downcount -= 1;
goto again;
}
}
@@ -1019,7 +1025,7 @@ int MIPSInterpret_RunFastUntil(u64 globalTicks)
MIPSState *curMips = currentMIPS;
while (coreState == CORE_RUNNING)
{
while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) // TODO: Try to get rid of the latter check
while (curMips->downcount >= 0 && coreState == CORE_RUNNING) // TODO: Try to get rid of the latter check
{
again:
bool wasInDelaySlot = curMips->inDelaySlot;
@@ -1116,7 +1122,7 @@ int MIPSInterpret_RunFastUntil(u64 globalTicks)
curMips->pc = curMips->nextPC;
curMips->inDelaySlot = false;
}
CoreTiming::downcount -= 1;
curMips->downcount -= 1;
goto again;
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ typedef void (CDECL *MIPSInterpretFunc)(u32 opcode);
void MIPSCompileOp(u32 op);
void MIPSDisAsm(u32 op, u32 pc, char *out);
void MIPSDisAsm(u32 op, u32 pc, char *out, bool tabsToSpaces = false);
u32 MIPSGetInfo(u32 op);
void MIPSInterpret(u32 op); //only for those rare ones
int MIPSInterpret_RunFastUntil(u64 globalTicks);
+4 -46
View File
@@ -83,19 +83,18 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit)
CMP(32, M((void*)&coreState), Imm32(0));
FixupBranch badCoreState = J_CC(CC_NZ, true);
FixupBranch skipToRealDispatch2 = J(); //skip the sync and compare first time
dispatcher = GetCodePtr();
// The result of slice decrementation should be in flags if somebody jumped here
// IMPORTANT - We jump on negative, not carry!!!
FixupBranch bail = J_CC(CC_BE, true);
FixupBranch bail = J_CC(CC_S, true);
SetJumpTarget(skipToRealDispatch);
SetJumpTarget(skipToRealDispatch2);
dispatcherNoCheck = GetCodePtr();
// Debug
// CALL(&ImHere);
MOV(32, R(EAX), M(&mips->pc));
#ifdef _M_IX86
AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK));
@@ -141,51 +140,10 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit)
J_CC(CC_Z, outerLoop, true);
SetJumpTarget(badCoreState);
//Landing pad for drec space
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
breakpointBailout = GetCodePtr();
//Landing pad for drec space
ABI_PopAllCalleeSavedRegsAndAdjustStack();
RET();
GenerateCommon();
}
void AsmRoutineManager::GenerateCommon()
{
/*
fifoDirectWrite8 = AlignCode4();
GenFifoWrite(8);
fifoDirectWrite16 = AlignCode4();
GenFifoWrite(16);
fifoDirectWrite32 = AlignCode4();
GenFifoWrite(32);
fifoDirectWriteFloat = AlignCode4();
GenFifoFloatWrite();
fifoDirectWriteXmm64 = AlignCode4();
GenFifoXmm64Write();
GenQuantizedLoads();
GenQuantizedStores();
GenQuantizedSingleStores();
*/
//CMPSD(R(XMM0), M(&zero),
// TODO
// Fast write routines - special case the most common hardware write
// TODO: use this.
// Even in x86, the param values will be in the right registers.
/*
const u8 *fastMemWrite8 = AlignCode16();
CMP(32, R(ABI_PARAM2), Imm32(0xCC008000));
FixupBranch skip_fast_write = J_CC(CC_NE, false);
MOV(32, EAX, M(&m_gatherPipeCount));
MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1);
ADD(32, 1, M(&m_gatherPipeCount));
RET();
SetJumpTarget(skip_fast_write);
CALL((void *)&Memory::Write_U8);*/
}
}
+5 -18
View File
@@ -20,19 +20,8 @@
#include "x64Emitter.h"
#include "../MIPS.h"
// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near
// code at runtime. In the case of fixed code like this, after writing it, we write
// protect the memory, essentially making it work just like precompiled code.
// There are some advantages to this approach:
// 1) No need to setup an external assembler in the build.
// 2) Cross platform, as long as it's x86/x64.
// 3) Can optimize code at runtime for the specific CPU model.
// There aren't really any disadvantages other than having to maintain a x86 emitter,
// which we have to do anyway :)
//
// To add a new asm routine, just add another const here, and add the code to Generate.
// Also, possibly increase the size of the code buffer.
// Runtime generated assembly routines, like the Dispatcher.
namespace MIPSComp
{
@@ -49,6 +38,10 @@ public:
AsmRoutineManager()
{
}
~AsmRoutineManager()
{
FreeCodeSpace();
}
void Init(MIPSState *mips, MIPSComp::Jit *jit)
{
@@ -56,10 +49,6 @@ public:
Generate(mips, jit);
WriteProtect();
}
~AsmRoutineManager()
{
FreeCodeSpace();
}
const u8 *enterCode;
@@ -68,8 +57,6 @@ public:
const u8 *dispatcherCheckCoreState;
const u8 *dispatcherNoCheck;
const u8 *fpException;
const u8 *breakpointBailout;
};
+5 -11
View File
@@ -80,9 +80,10 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely)
}
delaySlotIsNice = false; // Until we have time to fully fix this
if (rs == 0)
if (rt == 0)
{
CMP(32, gpr.R(rt), Imm32(0));
gpr.KillImmediate(rs, true, true);
CMP(32, gpr.R(rs), Imm32(0));
}
else
{
@@ -342,11 +343,6 @@ void Jit::BranchVFPUFlag(u32 op, Gen::CCFlags cc, bool likely)
void Jit::Comp_VBranch(u32 op)
{
// _dbg_assert_msg_(CPU,0,"comp_vbranch not supported");
//int imm = (signed short)(op&0xFFFF)<<2;
//u32 targetAddr = js.compilerPC + imm + 4;
switch ((op >> 16) & 3)
{
case 0: BranchVFPUFlag(op, CC_NZ, false); break; //bvf
@@ -368,7 +364,6 @@ void Jit::Comp_Jump(u32 op)
}
u32 off = ((op & 0x3FFFFFF) << 2);
u32 targetAddr = (js.compilerPC & 0xF0000000) | off;
//Delay slot
CompileAt(js.compilerPC + 4);
FlushAll();
@@ -447,10 +442,9 @@ void Jit::Comp_JumpReg(u32 op)
void Jit::Comp_Syscall(u32 op)
{
// This will most often be called from Comp_JumpReg (jr ra) so we take over the exit sequence...
FlushAll();
ABI_CallFunctionC((void *)(&CallSyscall), op);
ABI_CallFunctionC((void *)&CallSyscall, op);
WriteSyscallExit();
js.compiling = false;
+11 -20
View File
@@ -25,9 +25,6 @@
#include "RegCache.h"
#include "Jit.h"
extern u32 *pspmainram;
namespace MIPSComp
{
@@ -52,8 +49,6 @@ void Jit::ClearCache()
ClearCodeSpace();
}
u8 *codeCache;
#define CACHESIZE 16384*1024
void Jit::CompileAt(u32 addr)
{
u32 op = Memory::Read_Instruction(addr);
@@ -142,7 +137,7 @@ void Jit::Comp_Generic(u32 op)
void Jit::WriteExit(u32 destination, int exit_num)
{
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
SUB(32, M(&currentMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
//If nobody has taken care of this yet (this can be removed when all branches are done)
JitBlock *b = js.curBlock;
@@ -151,32 +146,28 @@ void Jit::WriteExit(u32 destination, int exit_num)
// Link opportunity!
int block = blocks.GetBlockNumberFromStartAddress(destination);
if (jo.enableBlocklink)
{
if (block >= 0 && jo.enableBlocklink)
{
// It exists! Joy of joy!
JMP(blocks.GetBlock(block)->checkedEntry, true);
b->linkStatus[exit_num] = true;
return;
}
if (block >= 0 && jo.enableBlocklink) {
// It exists! Joy of joy!
JMP(blocks.GetBlock(block)->checkedEntry, true);
b->linkStatus[exit_num] = true;
} else {
// No blocklinking.
MOV(32, M(&mips_->pc), Imm32(destination));
JMP(asm_.dispatcher, true);
}
// No blocklinking.
MOV(32, M(&mips_->pc), Imm32(destination));
JMP(asm_.dispatcher, true);
}
void Jit::WriteExitDestInEAX()
{
// TODO: Some wasted potential, dispatcher will alwa
MOV(32, M(&mips_->pc), R(EAX));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
SUB(32, M(&currentMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_.dispatcher, true);
}
void Jit::WriteSyscallExit()
{
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
SUB(32, M(&currentMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_.dispatcherCheckCoreState, true);
}
+1 -1
View File
@@ -35,7 +35,7 @@ struct JitOptions
{
JitOptions()
{
enableBlocklink = false;
enableBlocklink = true;
}
bool enableBlocklink;
+2 -2
View File
@@ -55,7 +55,7 @@ u8 *GetPointer(const u32 address)
}
else
{
ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
return 0;
}
}
@@ -83,7 +83,7 @@ inline void ReadFromHardware(T &var, const u32 address)
}
else
{
WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]);
if (!g_Config.bIgnoreBadMemAccess) {
Core_EnableStepping(true);
host->SetDebugMode(true);
+14 -13
View File
@@ -9,20 +9,21 @@ include(Settings.pri)
INCLUDEPATH += ../native ../Common ../
arm {
SOURCES += ../Core/MIPS/ARM/Asm.cpp \ #CoreARM
../Core/MIPS/ARM/CompALU.cpp \
../Core/MIPS/ARM/CompBranch.cpp \
../Core/MIPS/ARM/CompFPU.cpp \
../Core/MIPS/ARM/CompLoadStore.cpp \
../Core/MIPS/ARM/CompVFPU.cpp \
../Core/MIPS/ARM/Jit.cpp \
../Core/MIPS/ARM/JitCache.cpp \
../Core/MIPS/ARM/RegCache.cpp
SOURCES += ../Core/MIPS/ARM/ArmAsm.cpp \ #CoreARM
../Core/MIPS/ARM/ArmCompALU.cpp \
../Core/MIPS/ARM/ArmCompBranch.cpp \
../Core/MIPS/ARM/ArmCompFPU.cpp \
../Core/MIPS/ARM/ArmCompLoadStore.cpp \
../Core/MIPS/ARM/ArmCompVFPU.cpp \
../Core/MIPS/ARM/ArmJit.cpp \
../Core/MIPS/ARM/ArmJitCache.cpp \
../Core/MIPS/ARM/ArmRegCache.cpp \
../ext/disarm.cpp
HEADERS += ../Core/MIPS/ARM/Asm.h \
../Core/MIPS/ARM/Jit.h \
../Core/MIPS/ARM/JitCache.h \
../Core/MIPS/ARM/RegCache.h
HEADERS += ../Core/MIPS/ARM/ArmAsm.h \
../Core/MIPS/ARM/ArmJit.h \
../Core/MIPS/ARM/ArmJitCache.h \
../Core/MIPS/ARM/ArmRegCache.h
}
x86 {
SOURCES += ../Core/MIPS/x86/Asm.cpp \
+8
View File
@@ -219,6 +219,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\android\jni\ArmEmitterTest.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\android\jni\EmuScreen.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -297,6 +303,7 @@
<ClCompile Include="XinputDevice.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\android\jni\ARMEmitterTest.h" />
<ClInclude Include="..\android\jni\EmuScreen.h" />
<ClInclude Include="..\android\jni\GamepadEmu.h" />
<ClInclude Include="..\android\jni\MenuScreens.h" />
@@ -329,6 +336,7 @@
<ItemGroup>
<None Include="..\android\atlasscript.txt" />
<None Include="..\android\jni\Android.mk" />
<None Include="..\android\jni\Application.mk" />
<None Include="..\CMakeLists.txt" />
<None Include="..\README.md" />
<None Include="ppsspp.ico" />
+9
View File
@@ -104,6 +104,9 @@
<ClCompile Include="Globals.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="stdafx.cpp" />
<ClCompile Include="..\android\jni\ArmEmitterTest.cpp">
<Filter>Android</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Debugger\CtrlDisAsmView.h">
@@ -182,6 +185,9 @@
<Filter>Windows\Input</Filter>
</ClInclude>
<ClInclude Include="stdafx.h" />
<ClInclude Include="..\android\jni\ARMEmitterTest.h">
<Filter>Android</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="icon1.ico">
@@ -204,6 +210,9 @@
<Filter>Android</Filter>
</None>
<None Include="..\CMakeLists.txt" />
<None Include="..\android\jni\Application.mk">
<Filter>Android</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="ppsspp.rc">
+1
View File
@@ -23,6 +23,7 @@
#include "../Core/Config.h"
#include "../Core/SaveState.h"
#include "EmuThread.h"
#include "ext/disarm.h"
#include "LogManager.h"
#include "ConsoleListener.h"
+14 -11
View File
@@ -25,7 +25,7 @@ NATIVE := ../../native
SRC := ../..
LOCAL_CFLAGS := -DUSE_PROFILER -DARM -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -O2 -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math
LOCAL_CPPFLAGS := -std=gnu++0x
LOCAL_CXXFLAGS := -std=gnu++0x
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../../Common \
$(LOCAL_PATH)/../.. \
@@ -46,8 +46,10 @@ LOCAL_SRC_FILES := \
MenuScreens.cpp \
UIShader.cpp \
GamepadEmu.cpp \
ArmEmitterTest.cpp \
ui_atlas.cpp \
$(SRC)/native/android/app-android.cpp \
$(SRC)/ext/disarm.cpp \
$(SRC)/ext/libkirk/AES.c \
$(SRC)/ext/libkirk/SHA1.c \
$(SRC)/ext/libkirk/bn.c \
@@ -55,8 +57,9 @@ LOCAL_SRC_FILES := \
$(SRC)/ext/libkirk/kirk_engine.c \
$(SRC)/ext/snappy/snappy-c.cpp \
$(SRC)/ext/snappy/snappy.cpp \
$(SRC)/Common/ArmABI.cpp \
$(SRC)/Common/ArmEmitter.cpp \
$(SRC)/Common/ArmCPUDetect.cpp \
$(SRC)/Common/ArmThunk.cpp \
$(SRC)/Common/LogManager.cpp \
$(SRC)/Common/MemArena.cpp \
$(SRC)/Common/MemoryUtil.cpp \
@@ -66,7 +69,6 @@ LOCAL_SRC_FILES := \
$(SRC)/Common/StringUtil.cpp \
$(SRC)/Common/Thread.cpp \
$(SRC)/Common/Timer.cpp \
$(SRC)/Common/ThunkARM.cpp \
$(SRC)/Common/Misc.cpp \
$(SRC)/Common/MathUtil.cpp \
$(SRC)/GPU/Math3D.cpp \
@@ -166,14 +168,15 @@ LOCAL_SRC_FILES := \
$(SRC)/Core/MIPS/MIPSCodeUtils.cpp \
$(SRC)/Core/MIPS/MIPSDebugInterface.cpp \
$(SRC)/Core/MIPS/JitCommon/JitCommon.cpp \
$(SRC)/Core/MIPS/ARM/JitCache.cpp \
$(SRC)/Core/MIPS/ARM/CompALU.cpp \
$(SRC)/Core/MIPS/ARM/CompBranch.cpp \
$(SRC)/Core/MIPS/ARM/CompFPU.cpp \
$(SRC)/Core/MIPS/ARM/Asm.cpp \
$(SRC)/Core/MIPS/ARM/Jit.cpp \
$(SRC)/Core/MIPS/ARM/CompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/RegCache.cpp \
$(SRC)/Core/MIPS/ARM/ArmJitCache.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \
$(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \
$(SRC)/Core/MIPS/ARM/ArmAsm.cpp \
$(SRC)/Core/MIPS/ARM/ArmJit.cpp \
$(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \
$(SRC)/Core/Util/BlockAllocator.cpp \
$(SRC)/Core/Util/ppge_atlas.cpp \
$(SRC)/Core/Util/PPGeDraw.cpp
+3 -1
View File
@@ -1,2 +1,4 @@
APP_STL := stlport_static
APP_ABI := armeabi armeabi-v7a
APP_ABI := armeabi-v7a armeabi
#APP_ABI := armeabi-v7a
+80
View File
@@ -0,0 +1,80 @@
#include "base/logging.h"
#include "ArmEmitterTest.h"
#include "Common/ArmEmitter.h"
static bool functionWasCalled;
using namespace ArmGen;
class TestCode : public ArmGen::ARMXCodeBlock {
public:
TestCode();
void Generate();
const u8 *testCodePtr;
const u8 *testCodePtr2;
};
TestCode::TestCode()
{
AllocCodeSpace(0x10000);
}
u32 TestLeaf(u32 a, u32 b, u32 c)
{
ILOG("TestLeaf: %08x %08x %08x\n", a, b, c);
return 0xFF;
}
void TestLeaf2(u32 a)
{
ILOG("TestLeaf2 %08x\n");
}
void TestCode::Generate()
{
testCodePtr = this->GetCodePtr();
// Sonic1 commented that R11 is the frame pointer in debug mode, whatever "debug mode" means.
PUSH(2, R11, _LR);
ARMABI_MOVI2R(R0, 0x13371338);
AND(R1, R0, Operand2(0xFC, 4));
BIC(R0, R0, Operand2(0xFC, 4));
CMP(R1, Operand2(0x10, 4));
SetCC(CC_EQ);
MOV(R2, Operand2(0x99, 0));
SetCC(CC_NEQ);
MOV(R2, Operand2(0xFF, 0));
SetCC();
QuickCallFunction(R3, (void*)&TestLeaf);
//ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x1, 0x100, 0x1337);
//ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x2, 0x100, 0x31337);
//ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x3, 0x100, 0x1337);
POP(2, R11, _PC); // Yup, this is how you return.
testCodePtr2 = this->GetCodePtr();
PUSH(2, R11, _LR);
QuickCallFunction(R3, (void*)&TestLeaf2);
POP(2, R11, _PC);
}
void CallPtr(const void *ptr)
{
((void(*)())ptr)();
}
void ArmEmitterTest()
{
ILOG("Running ARM emitter test!");
TestCode gen;
gen.ReserveCodeSpace(0x4000);
gen.Generate();
CallPtr(gen.testCodePtr);
ILOG("ARM emitter test 1 passed!");
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
// Just a test of the ARM emitter, playing around with running some code without having the whole emu around.
void ArmEmitterTest();
-4
View File
@@ -48,10 +48,6 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true)
CoreParameter coreParam;
coreParam.cpuCore = (CPUCore)g_Config.iCpuCore;
#if defined(ARM)
if (coreParam.cpuCore == CPU_JIT)
coreParam.cpuCore = CPU_FASTINTERPRETER;
#endif
coreParam.gpuCore = GPU_GLES;
coreParam.enableSound = g_Config.bEnableSound;
coreParam.fileToStart = fileToStart;
+7 -5
View File
@@ -305,16 +305,18 @@ void SettingsScreen::render() {
g_Config.iWindowZoom = doubleRes ? 2 : 1;
}
UICheckBox(GEN_ID, x, y += stride, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform);
UICheckBox(GEN_ID, x, y += stride, "Use VBO for drawing", ALIGN_TOPLEFT, &g_Config.bUseVBO);
UICheckBox(GEN_ID, x, y += stride, "Draw using VBO", ALIGN_TOPLEFT, &g_Config.bUseVBO);
bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER;
UICheckBox(GEN_ID, x, y += stride, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt);
bool useJit = g_Config.iCpuCore == CPU_JIT;
UICheckBox(GEN_ID, x, y += stride, "JIT (Dynarec)", ALIGN_TOPLEFT, &useJit);
if (g_Config.iCpuCore == CPU_JIT)
UICheckBox(GEN_ID, x + 350, y, "Fastmem (unstable)", ALIGN_TOPLEFT, &g_Config.bFastMemory);
g_Config.iCpuCore = useJit ? CPU_JIT : CPU_INTERPRETER;
// ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x, y+=50, 0xcFFFFFFF, ALIGN_LEFT);
UICheckBox(GEN_ID, x, y += stride, "On-screen Touch Controls", ALIGN_TOPLEFT, &g_Config.bShowTouchControls);
if (g_Config.bShowTouchControls)
UICheckBox(GEN_ID, x + 50, y += stride, "Show Analog Stick", ALIGN_TOPLEFT, &g_Config.bShowAnalogStick);
g_Config.iCpuCore = useFastInt ? CPU_FASTINTERPRETER : CPU_INTERPRETER;
// UICheckBox(GEN_ID, x, y += 50, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer);
// UICheckBox(GEN_ID, x, y += stride, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer);
if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres-10), LARGE_BUTTON_WIDTH, "Back", ALIGN_RIGHT | ALIGN_BOTTOM)) {
screenManager()->switchScreen(new MenuScreen());
+11
View File
@@ -50,6 +50,8 @@
#include "MenuScreens.h"
#include "UIShader.h"
#include "ArmEmitterTest.h"
Texture *uiTexture;
ScreenManager *screenManager;
@@ -152,6 +154,8 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo
*app_nice_name = "PPSSPP";
*app_dir_name = "ppsspp";
*landscape = true;
// ArmEmitterTest();
}
void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID)
@@ -190,6 +194,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co
switch (argv[i][1]) {
case 'd':
// Enable debug logging
// Note that you must also change the max log level in Log.h.
logLevel = LogTypes::LDEBUG;
break;
case 'g':
@@ -293,6 +298,9 @@ void NativeInitGraphics()
theme.checkOn = I_CHECKEDBOX;
theme.checkOff = I_SQUARE;
ui_draw2d.Init();
ui_draw2d_front.Init();
UIInit(&ui_atlas, theme);
uiTexture = new Texture();
@@ -372,6 +380,9 @@ void NativeShutdownGraphics()
delete screenManager;
screenManager = 0;
ui_draw2d.Shutdown();
ui_draw2d_front.Shutdown();
UIShader_Shutdown();
gl_lost_manager_shutdown();
@@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity {
{
return false;
}
}
}
+965
View File
@@ -0,0 +1,965 @@
/* disarm -- a simple disassembler for ARM instructions
* (c) 2000 Gareth McCaughan
*
* This file may be distributed and used freely provided:
* 1. You do not distribute any version that lacks this
* copyright notice (exactly as it appears here, extending
* from the start to the end of the C-language comment
* containing these words)); and,
* 2. If you distribute any modified version, its source
* contains a clear description of the ways in which
* it differs from the original version, and a clear
* indication that the changes are not mine.
* There is no restriction on your permission to use and
* distribute object code or executable code derived from
* this.
*
* The original version of this file (or perhaps a later
* version by the original author) may or may not be
* available at http://web.ukonline.co.uk/g.mccaughan/g/software.html .
*
* Share and enjoy! -- g
*/
/* (*This* comment is NOT part of the notice mentioned in the
* distribution conditions above.)
*
* The bulk of this code was ripped brutally from the middle
* of a much more interesting piece of software whose purpose
* is to disassemble object files in the format known as AOF;
* it's quite clever at spotting blocks of non-code embedded
* in code, identifying labels, and so on.
*
* This program, on the other hand, is very much simpler.
* It simply disassembles one instruction at a time. Some
* traces of the original purpose can be seen here and there.
* You might want to make this do a two-phase disassembly,
* adding labels etc the second time around. I've made this
* work by loading the whole file into memory first, partly
* because that makes a two-pass approach easier.
*
* One word of warning: I believe that the syntax this program
* uses for the MSR instruction is now obsolete.
*
* Usage:
* disarm <filename> <base-address>
* will disassemble every word in <filename>.
*
* <base-address> should be something understood by strtol.
* So you can get hex (which is probably what you want)
* by prefixing "0x".
*
* The -r option will byte-reverse each word before it's
* disassembled.
*
* The code is rather unmaintainable. I'm sorry.
*
* Changes since original release:
* ????-??-?? v0.00 Initial release.
* 2007-09-02 v0.11 Change %X to %lX in a format string.
* (Thanks to Vincent Zweije for reporting this.)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef unsigned int word;
typedef unsigned int address;
typedef unsigned int addrdiff;
#define W(x) ((word*)(x))
#define declstruct(name) typedef struct name s##name, * p##name
#define defstruct(name) struct name
#define defequiv(new,old) typedef struct old s##new, * p##new
declstruct(DisOptions);
declstruct(Instruction);
typedef enum {
target_None, /* instruction doesn't refer to an address */
target_Data, /* instruction refers to address of data */
target_FloatS, /* instruction refers to address of single-float */
target_FloatD, /* instruction refers to address of double-float */
target_FloatE, /* blah blah extended-float */
target_FloatP, /* blah blah packed decimal float */
target_Code, /* instruction refers to address of code */
target_Unknown /* instruction refers to address of *something* */
} eTargetType;
defstruct(Instruction) {
char text[128]; /* the disassembled instruction */
int undefined; /* non-0 iff it's an undefined instr */
int badbits; /* non-0 iff something reserved has the wrong value */
int oddbits; /* non-0 iff something unspecified isn't 0 */
int is_SWI; /* non-0 iff it's a SWI */
word swinum; /* only set for SWIs */
address target; /* address instr refers to */
eTargetType target_type; /* and what we expect to be there */
int offset; /* offset from register in LDR or STR or similar */
char * addrstart; /* start of address part of instruction, or 0 */
};
#define disopt_SWInames 1 /* use names, not &nnnn */
#define disopt_CommaSpace 2 /* put spaces after commas */
#define disopt_FIXS 4 /* bogus FIX syntax for ObjAsm */
#define disopt_ReverseBytes 8 /* byte-reverse words first */
defstruct(DisOptions) {
word flags;
const char * * regnames; /* pointer to 16 |char *|s: register names */
};
static pInstruction instr_disassemble(word, address, pDisOptions);
#define INSTR_grok_v4
/* Preprocessor defs you can give to affect this stuff:
* INSTR_grok_v4 understand ARMv4 instructions (halfword & sign-ext LDR/STR)
* INSTR_new_msr be prepared to produce new MSR syntax if asked
* The first of these is supported; the second isn't.
*/
/* Some important single-bit fields. */
#define Sbit (1<<20) /* set condition codes (data processing) */
#define Lbit (1<<20) /* load, not store (data transfer) */
#define Wbit (1<<21) /* writeback (data transfer) */
#define Bbit (1<<22) /* single byte (data transfer, SWP) */
#define Ubit (1<<23) /* up, not down (data transfer) */
#define Pbit (1<<24) /* pre-, not post-, indexed (data transfer) */
#define Ibit (1<<25) /* non-immediate (data transfer) */
/* immediate (data processing) */
#define SPSRbit (1<<22) /* SPSR, not CPSR (MRS, MSR) */
/* Some important 4-bit fields. */
#define RD(x) ((x)<<12) /* destination register */
#define RN(x) ((x)<<16) /* operand/base register */
#define CP(x) ((x)<<8) /* coprocessor number */
#define RDbits RD(15)
#define RNbits RN(15)
#define CPbits CP(15)
#define RD_is(x) ((instr&RDbits)==RD(x))
#define RN_is(x) ((instr&RNbits)==RN(x))
#define CP_is(x) ((instr&CPbits)==CP(x))
/* A slightly efficient way of telling whether two bits are the same
* or not. It's assumed that a<b.
*/
#define BitsDiffer(a,b) ((instr^(instr>>(b-a)))&(1<<a))
/* op = append(op,ip) === op += sprintf(op,"%s",ip),
* except that it's faster.
*/
static char * append(char * op, const char *ip) {
char c;
while ((c=*ip++)!=0) *op++=c;
return op;
}
/* op = hex8(op,w) === op += sprintf(op,"&%08lX",w), but faster.
*/
static char * hex8(char * op, word w) {
int i;
*op++='&';
for (i=28; i>=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15];
return op;
}
/* op = reg(op,'x',n) === op += sprintf(op,"x%lu",n&15).
*/
static char * reg(char * op, char c, word n) {
*op++=c;
n&=15;
if (n>=10) { *op++='1'; n+='0'-10; } else n+='0';
*op++=(char)n;
return op;
}
/* op = num(op,n) appends n in decimal or &n in hex
* depending on whether n<100. It's assumed that n>=0.
*/
static char * num(char * op, word w) {
if (w>=100) {
int i;
word t;
*op++='&';
for (i=28; (t=(w>>i)&15)==0; i-=4) ;
for (; i>=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15];
}
else {
/* divide by 10. You can prove this works by exhaustive search. :-) */
word t = w-(w>>2); t=(t+(t>>4)) >> 3;
{ word u = w-10*t;
if (u==10) { u=0; ++t; }
if (t) *op++=(char)(t+'0');
*op++=(char)(u+'0');
}
}
return op;
}
/* instr_disassemble
* Disassemble a single instruction.
*
* args: instr a single ARM instruction
* addr the address it's presumed to have come from
* opts cosmetic preferences for our output
*
* reqs: opts must be filled in right. In particular, it must contain
* a list of register names.
*
* return: a pointer to a structure containing the disassembled instruction
* and some other information about it.
*
* This is basically a replacement for the SWI Debugger_Disassemble,
* but it has the following advantages:
*
* + it's 3-4 times as fast
* + it's better at identifying undefined instructions,
* and instructions not invariant under { disassemble; ObjAsm; }
* + it provides some other useful information as well
* + its output syntax is the same as ObjAsm's input syntax
* (where possible)
* + it doesn't disassemble FIX incorrectly unless you ask it to
* + it's more configurable in some respects
*
* It also has the following disadvantages:
*
* - it increases the size of ObjDism
* - it doesn't provide so many `helpful' usage comments etc
* - it's less configurable in some respects
* - it doesn't (yet) know about ARMv4 instructions
*
* This function proceeds in two phases. The first is very simple:
* it works out what sort of instruction it's looking at and sets up
* three strings:
* - |mnemonic| (the basic mnemonic: LDR or whatever)
* - |flagchars| (things to go after the cond code: B or whatever)
* - |format| (a string describing how to display the instruction)
* The second phase consists of interpreting |format|, character by
* character. Some characters (e.g., letters) just mean `append this
* character to the output string'; some mean more complicated things
* like `append the name of the register whose number is in bits 12..15'
* or, worse, `append a description of the <op2> field'.
*
* I'm afraid the magic characters in |format| are rather arbitrary.
* One criterion in choosing them was that they should form a contiguous
* subrange of the character set! Sorry.
*
* Things I still want to do:
*
* - more configurability?
* - make it much faster, if possible
* - make it much smaller, if possible
*
* Format characters:
*
* \01..\05 copro register number from nybble (\001 == nybble 0, sorry)
* $ SWI number
* % register set for LDM/STM (takes note of bit 22 for ^)
* & address for B/BL
* ' ! if bit 21 set, else nothing (mnemonic: half a !)
* ( #regs for SFM (bits 22,15 = fpn, assumed already tweaked)
* ) copro opcode in bits 20..23 (for CDP)
* * op2 (takes note of bottom 12 bits, and bit 25)
* + FP register or immediate value: bits 0..3
* , comma or comma-space
* - copro extra info in bits 5..7 preceded by , omitted if 0
* . address in ADR instruction
* / address for LDR/STR (takes note of bit 23 & reg in bits 16..19)
* 0..4 register number from nybble
* 5..9 FP register number from nybble
* : copro opcode in bits 21..23 (for MRC/MCR)
* ; copro number in bits 8..11
*
* ADDED BY HRYDGARD:
* ^ 16-bit immediate
*
* NB that / takes note of bit 22, too, and does its own ! when
* appropriate.
*
* On typical instructions this seems to take about 100us on my ARM6;
* that's about 3000 cycles, which seems grossly excessive. I'm not
* sure where all those cycles are being spent. Perhaps it's possible
* to make it much, much faster. Most of this time is spent on phase 2.
*/
extern pInstruction
instr_disassemble(word instr, address addr, pDisOptions opts) {
static char flagchars[4];
static sInstruction result;
const char * mnemonic = 0;
char * flagp = flagchars;
const char * format = 0;
word fpn;
eTargetType poss_tt = target_None;
#ifdef INSTR_grok_v4
int is_v4 = 0;
#endif
/* PHASE 0. Set up default values for |result|. */
if (opts->flags & disopt_ReverseBytes) {
instr = ((instr & 0xFF00FF00) >> 8) | ((instr & 0x00FF00FF) << 8);
instr = (instr >> 16) | (instr << 16);
}
fpn = ((instr>>15)&1) + ((instr>>21)&2);
result.undefined =
result.badbits =
result.oddbits =
result.is_SWI = 0;
result.target_type = target_None;
result.offset = 0x80000000;
result.addrstart = 0;
/* PHASE 1. Decode and classify instruction. */
switch ((instr>>24)&15) {
case 0:
/* multiply or data processing, or LDRH etc */
if ((instr&(15<<4))!=(9<<4)) goto lMaybeLDRHetc;
/* multiply */
if (instr&(1<<23)) {
/* int multiply */
mnemonic = "UMULL\0UMLAL\0SMULL\0SMLAL" + 6*((instr>>21)&3);
format = "3,4,0,2";
}
else {
if (instr&(1<<22)) goto lUndefined; /* "class C" */
/* short multiply */
if (instr&(1<<21)) {
mnemonic = "MLA";
format = "4,0,2,3";
}
else {
mnemonic = "MUL";
format = "4,0,2";
}
}
if (instr&Sbit) *flagp++='S';
break;
case 1:
if ((instr & 0x0FFFFFF0) == ((18 << 20) | (0xFFF << 8) | (1 << 4))) {
mnemonic = "B";
format = "0";
break;
} else if ((instr & 0x0FFFFFF0) == 0x12FFF30) {
mnemonic = "BL";
format = "0";
break;
}
case 3:
/* SWP or MRS/MSR or data processing */
// hrydgard addition: MOVW/MOVT
if ((instr & 0x0FF00000) == 0x03000000) {
mnemonic = "MOVW";
format = "3,^";
break;
}
else if ((instr & 0x0FF00000) == 0x03400000) {
mnemonic = "MOVT";
format = "3,^";
break;
}
else if ((instr&0x02B00FF0)==0x00000090) {
/* SWP */
mnemonic = "SWP";
format = "3,0,[4]";
if (instr&Bbit) *flagp++='B';
break;
}
else if ((instr&0x02BF0FFF)==0x000F0000) {
/* MRS */
mnemonic = "MRS";
format = (instr&SPSRbit) ? "3,SPSR" : "3,CPSR";
break;
}
else if ((instr&0x02BFFFF0)==0x0029F000) {
/* MSR psr<P=0/1...>,Rs */
mnemonic = "MSR";
format = (instr&SPSRbit) ? "SPSR,0" : "CPSR,0";
break;
}
else if ((instr&0x00BFF000)==0x0028F000) {
/* MSR {C,S}PSR_flag,op2 */
mnemonic = "MSR";
format = (instr&SPSRbit) ? "SPSR_flg,*" : "CPSR_flg,*";
if (!(instr&Ibit) && (instr&(15<<4)))
#ifdef INSTR_grok_v4
goto lMaybeLDRHetc;
#else
goto lUndefined; /* shifted reg in MSR illegal */
#endif
break;
}
/* fall through here */
lMaybeLDRHetc:
#ifdef INSTR_grok_v4
if ((instr&(14<<24))==0
&& ((instr&(9<<4))==(9<<4))) {
/* Might well be LDRH or similar. */
if ((instr&(Wbit+Pbit))==Wbit) goto lUndefined; /* "class E", case 1 */
if ((instr&(Lbit+(1<<6)))==(1<<6)) goto lUndefined; /* STRSH etc */
mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18);
if (instr&(1<<6)) *flagp++='S';
*flagp++ = (instr&(1<<5)) ? 'B' : 'H';
format = "3,/";
/* aargh: */
if (!(instr&(1<<22))) instr |= Ibit;
is_v4=1;
break;
}
#endif
case 2:
/* data processing */
{ word op21 = instr&(15<<21);
if ((op21==(2<<21) || (op21==(4<<21))) /* ADD or SUB */
&& ((instr&(RNbits+Ibit+Sbit))==RN(15)+Ibit) /* imm, no S */
/*&& ((instr&(30<<7))==0 || (instr&3))*/) { /* normal rot */
/* ADD ...,pc,#... or SUB ...,pc,#...: turn into ADR */
mnemonic = "ADR";
format = "3,.";
if ((instr&(30<<7))!=0 && !(instr&3)) result.oddbits=1;
break;
}
mnemonic = "AND\0EOR\0SUB\0RSB\0ADD\0ADC\0SBC\0RSC\0"
"TST\0TEQ\0CMP\0CMN\0ORR\0MOV\0BIC\0MVN" /* \0 */
+ (op21 >> 19);
/* Rd needed for all but TST,TEQ,CMP,CMN (8..11) */
/* Rn needed for all but MOV,MVN (13,15) */
if (op21 < ( 8<<21)) format = "3,4,*";
else if (op21 < (12<<21)) {
format = "4,*";
if (instr&RDbits) {
if ((instr&Sbit) && RD_is(15))
*flagp++='P';
else result.oddbits=1;
}
if (!(instr&Sbit)) goto lUndefined; /* CMP etc, no S bit */
}
else if (op21 & (1<<21)) {
format = "3,*";
if (instr&RNbits) result.oddbits=1;
}
else format = "3,4,*";
if (instr&Sbit && (op21<(8<<21) || op21>=(12<<21))) *flagp++='S';
}
break;
case 4:
case 5:
case 6:
case 7:
/* undefined or STR/LDR */
if ((instr&Ibit) && (instr&(1<<4))) goto lUndefined; /* "class A" */
mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18);
format = "3,/";
if (instr&Bbit) *flagp++='B';
if ((instr&(Wbit+Pbit))==Wbit) *flagp++='T';
poss_tt = target_Data;
break;
case 8:
case 9:
/* STM/LDM */
mnemonic = "STM\0LDM" + ((instr&Lbit) >> 18);
if (RN_is(13)) {
/* r13, so treat as stack */
word x = (instr&(3<<23)) >> 22;
if (instr&Lbit) x^=6;
{ const char * foo = "EDEAFDFA"+x;
*flagp++ = *foo++;
*flagp++ = *foo;
}
}
else {
/* not r13, so don't treat as stack */
*flagp++ = (instr&Ubit) ? 'I' : 'D';
*flagp++ = (instr&Pbit) ? 'B' : 'A';
}
format = "4',%";
break;
case 10:
case 11:
/* B or BL */
mnemonic = "B\0BL"+((instr&(1<<24))>>23);
format = "&";
break;
case 12:
case 13:
/* STC or LDC */
if (CP_is(1)) {
/* copro 1: FPU. This is STF or LDF. */
mnemonic = "STF\0LDF" + ((instr&Lbit) >> 18);
format = "8,/";
*flagp++ = "SDEP"[fpn];
poss_tt = (eTargetType)(target_FloatS+fpn);
}
else if (CP_is(2)) {
/* copro 2: this is LFM or SFM. */
mnemonic = "SFM\0LFM" + ((instr&Lbit) >> 18);
if (!fpn) fpn=4;
if (RN_is(13) && BitsDiffer(23,24)) {
if ((instr&255)!=fpn) goto lNonStackLFM;
/* r13 and U!=P, so treat as stack */
if (BitsDiffer(20,24)) {
/* L != P, so FD */
*flagp++ = 'F'; *flagp++ = 'D';
}
else {
/* L == P, so EA */
*flagp++ = 'E'; *flagp++ = 'A';
}
format = "8,(,[4]'";
}
else {
lNonStackLFM:
/* not r13 or U=P or wrong offset, so don't treat as stack */
format = "8,(,/";
poss_tt = target_FloatE;
}
}
else {
/* some other copro number: STC or LDC. */
mnemonic = "STC\0LDC" + ((instr&Lbit) >> 18);
format = ";,\004,/";
if (instr&(1<<22)) *flagp++ = 'L';
poss_tt = target_Unknown;
}
break;
case 14:
/* CDP or MRC/MCR */
if (instr&(1<<4)) {
/* MRC/MCR. */
if (CP_is(1)) {
/* copro 1: FPU. */
if ((instr&Lbit) && RD_is(15)) {
/* MCR in FPU with Rd=r15: comparison (ugh) */
if (!(instr&(1<<23))) goto lUndefined; /* unused operation */
mnemonic = "CMF\0\0CNF\0\0CMFE\0CNFE" + (5*(instr&(3<<21)) >> 21);
format = "9,+";
if (instr&((1<<19)+(7<<5)))
result.badbits=1; /* size,rmode reseved */
}
else {
/* normal FPU MCR/MRC */
word op20 = instr&(15<<20);
if (op20>=6<<20) goto lUndefined;
mnemonic = "FLT\0FIX\0WFS\0RFS\0WFC\0RFC" + (op20>>18);
if (op20==0) {
/* FLT instruction */
format = "9,3";
{ char c = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)];
if (c=='*') goto lUndefined; else *flagp++=c;
}
if (instr&15) result.oddbits=1; /* Fm and const flag unused */
}
else {
/* not FLT instruction */
if (instr&((1<<7)+(1<<19)))
result.badbits=1; /* size bits reserved */
if (op20==1<<20) {
/* FIX instruction */
format = "3,+";
if (opts->flags&disopt_FIXS)
*flagp++ = "SDEP"[((instr>>7)&1) + ((instr>>18)&2)];
*flagp++ = "\0PMZ"[(instr&(3<<5))>>5];
if (instr&(7<<15)) result.oddbits=1; /* Fn unused */
if (instr&(1<<3)) result.badbits=1; /* no immediate consts */
}
else {
/* neither FLT nor FIX */
format = "3";
if (instr&(3<<5)) result.badbits=1; /* rmode reserved */
if (instr&(15+(7<<15))) result.oddbits=1;/* iFm, Fn unused */
}
}
}
}
else {
/* some other copro number. Not FPU. */
/* NB that ObjAsm documentation gets MCR and MRC the wrong way round!
*/
mnemonic = "MCR\0MRC";
mnemonic += (instr&Lbit) >> 18;
format = ";,:,3,\005,\001-";
}
}
else {
/* CDP. */
if (CP_is(1)) {
/* copro 1: FPU. */
mnemonic = /* dyadics: */
"ADF\0MUF\0SUF\0RSF\0"
"DVF\0RDF\0POW\0RPW\0"
"RMF\0FML\0FDV\0FRD\0"
"POL\0***\0***\0***\0"
/* monadics: */
"MVF\0MNF\0ABS\0RND\0"
"SQT\0LOG\0LGN\0EXP\0"
"SIN\0COS\0TAN\0ASN\0"
"ACS\0ATN\0URD\0NRM\0"
+ ((instr&(15<<20)) >> 18) /* opcode -> bits 5432 */
+ ((instr&(1<<15)) >> 9); /* monadicP -> bit 6 */
format = (instr&(1<<15)) ? "8,+" : "8,9,+";
*flagp++ = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)];
*flagp++ = "\0PMZ"[(instr&(3<<5))>>5];
/* NB that foregoing relies on this being the last flag! */
if (*mnemonic=='*' || *flagchars=='*') goto lUndefined;
}
else {
/* some other copro number. Not FPU. */
mnemonic = "CDP";
format = ";,),\004,\005,\001-";
}
}
break;
case 15:
/* SWI */
mnemonic = "SWI";
format = "$";
break;
/* Nasty hack: this is code that won't be reached in the normal
* course of events, and after the last case of the switch is a
* convenient place for it.
*/
lUndefined:
strcpy(result.text, "Undefined instruction");
result.undefined = 1;
return &result;
}
*flagp=0;
/* PHASE 2. Produce string. */
{ char * op = result.text;
/* 2a. Mnemonic. */
op = append(op,mnemonic);
/* 2b. Condition code. */
{ word cond = instr>>28;
if (cond!=14) {
const char * ip = "EQNECSCCMIPLVSVCHILSGELTGTLEALNV"+2*cond;
*op++ = *ip++;
*op++ = *ip;
}
}
/* 2c. Flags. */
{ const char * ip = flagchars;
while (*ip) *op++ = *ip++;
}
/* 2d. A tab character. */
*op++ = '\t';
/* 2e. Other stuff, determined by format string. */
{ const char * ip = format;
char c;
const char * * regnames = opts->regnames;
word oflags = opts->flags;
while ((c=*ip++) != 0) {
switch(c) {
case '^': // hrydgard addition
{
unsigned short imm16 = ((instr & 0x000F0000) >> 4) | (instr & 0x0FFF);
op += sprintf(op, "%04x", imm16);
}
break;
case '$':
result.is_SWI = 1;
result.swinum = instr&0x00FFFFFF;
result.addrstart = op;
op += sprintf(op, "&%X", result.swinum);
break;
case '%':
*op++='{';
{ word w = instr&0xFFFF;
int i=0;
while (w) {
int j;
while (!(w&(1ul<<i))) ++i;
for (j=i+1; w&(1ul<<j); ++j) ;
--j;
/* registers [i..j] */
op = append(op, regnames[i]);
if (j-i) {
*op++ = (j-i>1) ? '-' : ',';
op = append(op, regnames[j]);
}
i=j; w=(w>>(j+1))<<(j+1);
if (w) *op++=',';
}
}
*op++='}';
if (instr&(1<<22)) *op++='^';
break;
case '&':
{ address target = (addr+8 + ((((int)instr)<<8)>>6)) & 0x03FFFFFC;
result.addrstart = op;
op = hex8(op, target);
result.target_type = target_Code;
result.target = target;
}
break;
case '\'':
lPling:
if (instr&Wbit) *op++='!';
break;
case '(':
*op++ = (char)('0'+fpn);
break;
case ')':
{ word w = (instr>>20)&15;
if (w>=10) { *op++='1'; *op++=(char)('0'-10+w); }
else *op++=(char)(w+'0');
}
break;
case '*':
case '.':
if (instr&Ibit) {
/* immediate constant */
word imm8 = (instr&255);
word rot = (instr>>7)&30;
if (rot && !(imm8&3) && c=='*') {
/* Funny immediate const. Guaranteed not '.', btw */
*op++='#'; *op++='&';
*op++="0123456789ABCDEF"[imm8>>4];
*op++="0123456789ABCDEF"[imm8&15];
*op++=',';
op = num(op, rot);
}
else {
imm8 = (imm8>>rot) | (imm8<<(32-rot));
if (c=='*') {
*op++='#';
if (imm8>256 && ((imm8&(imm8-1))==0)) {
/* only one bit set, and that later than bit 8.
* Represent as 1<<... .
*/
op = append(op,"1<<");
{ int n=0;
while (!(imm8&15)) { n+=4; imm8=imm8>>4; }
/* Now imm8 is 1, 2, 4 or 8. */
n += (0x30002010 >> 4*(imm8-1))&15;
op = num(op, n);
}
}
else {
if (((int)imm8)<0 && ((int)imm8)>-100) {
*op++='-'; imm8=-imm8;
}
op = num(op, imm8);
}
}
else {
address a = addr+8;
if (instr&(1<<22)) a-=imm8; else a+=imm8;
result.addrstart=op;
op = hex8(op, a);
result.target=a; result.target_type=target_Unknown;
}
}
}
else {
/* rotated register */
const char * rot = "LSL\0LSR\0ASR\0ROR" + ((instr&(3<<5)) >> 3);
op = append(op, regnames[instr&15]);
if (instr&(1<<4)) {
/* register rotation */
if (instr&(1<<7)) goto lUndefined;
*op++=','; if (oflags&disopt_CommaSpace) *op++=' ';
op = append(op,rot); *op++=' ';
op = append(op,regnames[(instr&(15<<8))>>8]);
}
else {
/* constant rotation */
word n = instr&(31<<7);
if (!n) {
if (!(instr&(3<<5))) break;
else if ((instr&(3<<5))==(3<<5)) {
op = append(op, ",RRX");
break;
}
else n=32<<7;
}
*op++ = ','; if (oflags&disopt_CommaSpace) *op++=' ';
op = num(append(append(op,rot)," #"),n>>7);
}
}
break;
case '+':
if (instr&(1<<3)) {
word w = instr&7;
*op++='#';
if (w<6) *op++=(char)('0'+w);
else op = append(op, w==6 ? "0.5" : "10");
}
else {
*op++='f';
*op++=(char)('0'+(instr&7));
}
break;
case ',':
*op++=',';
if (oflags&disopt_CommaSpace) *op++=' ';
break;
case '-':
{ word w = instr&(7<<5);
if (w) {
*op++=',';
if (oflags&disopt_CommaSpace) *op++=' ';
*op++ = (char)('0'+(w>>5));
}
}
break;
case '/':
result.addrstart = op;
*op++='[';
op = append(op, regnames[(instr&RNbits)>>16]);
if (!(instr&Pbit)) *op++=']';
*op++=','; if (oflags&disopt_CommaSpace) *op++=' ';
/* For following, NB that bit 25 is always 0 for LDC, SFM etc */
if (instr&Ibit) {
/* shifted offset */
if (!(instr&Ubit)) *op++='-';
/* We're going to transfer to '*', basically. The stupid
* thing is that the meaning of bit 25 is reversed there;
* I don't know why the designers of the ARM did that.
*/
instr ^= Ibit;
if (instr&(1<<4)) {
#ifdef INSTR_grok_v4
if (is_v4 && !(instr&(15<<8))) {
ip = (instr&Pbit) ? "0]" : "0";
break;
}
#else
goto lUndefined; /* LSL r3 forbidden */
#endif
}
/* Need a ] iff it was pre-indexed; and an optional ! iff
* it's pre-indexed *or* a copro instruction,
* except that FPU operations don't need the !. Bletch.
*/
if (instr&Pbit) ip="*]'";
else if (instr&(1<<27)) {
if (CP_is(1) || CP_is(2)) {
if (!(instr&Wbit)) goto lUndefined;
ip="*";
}
else ip="*'";
}
else ip="*";
}
else {
/* immediate offset */
word offset;
if (instr&(1<<27)) {
/* LDF or LFM or similar */
offset = (instr&255)<<2;
}
#ifdef INSTR_grok_v4
else if (is_v4) offset = (instr&15) + ((instr&(15<<8))>>4);
#endif
else {
/* LDR or STR */
offset = instr&0xFFF;
}
*op++='#';
if (!(instr&Ubit)) {
if (offset) *op++='-';
else result.oddbits=1;
result.offset = -offset;
}
else result.offset = offset;
op = num(op, offset);
if (RN_is(15) && (instr&Pbit)) {
/* Immediate, pre-indexed and PC-relative. Set target. */
result.target_type = poss_tt;
result.target = (instr&Ubit) ? addr+8 + offset
: addr+8 - offset;
if (!(instr&Wbit)) {
/* no writeback, either. Use friendly form. */
op = hex8(result.addrstart, result.target);
break;
}
}
if (instr&Pbit) { *op++=']'; goto lPling; }
else if (instr&(1<<27)) {
if (CP_is(1) || CP_is(2)) {
if (!(instr&Wbit)) goto lUndefined;
}
else goto lPling;
}
}
break;
case '0': case '1': case '2': case '3': case '4':
op = append(op, regnames[(instr>>(4*(c-'0')))&15]);
break;
case '5': case '6': case '7': case '8': case '9':
*op++='f';
*op++=(char)('0' + ((instr>>(4*(c-'5')))&7));
break;
case ':':
*op++ = (char)('0' + ((instr>>21)&7));
break;
case ';':
op = reg(op, 'p', instr>>8);
break;
default:
if (c<=5)
op = reg(op, 'c', instr >> (4*(c-1)));
else *op++ = c;
}
}
*op=0;
}
}
/* DONE! */
return &result;
}
static const char * reg_names[16] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "ip", "sp", "lr", "pc"
};
static sDisOptions options = {
disopt_CommaSpace,
reg_names
};
const char *ArmRegName(int r) {
return reg_names[r];
}
void ArmDis(unsigned int addr, unsigned int w, char *output) {
pInstruction instr = instr_disassemble(w, addr, &options);
sprintf(output, "%08x\t%s", w, instr->text);
if (instr->undefined || instr->badbits || instr->oddbits) {
if (instr->undefined) sprintf(output, " [undefined instr %08x]", w);
if (instr->badbits) sprintf(output, " [illegal bits %08x]", w);
// HUH? LDR and STR gets this a lot
// strcat(output, " ? (extra bits)");
// if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w);
}
// zap tabs
while (*output) {
if (*output == '\t')
*output = ' ';
output++;
}
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
// This stuff only disassembles very old ARM but should be sufficient
// for the basics except for MOVW/MOVT.
// Disarm itself has the license you can see in the cpp file.
// I'm not entirely sure it's 100% gpl compatible but it's nearly
// public domain so meh.
// The only changes I've done is C++ compat and replaced the main
// program with this function.
const char *ArmRegName(int r);
void ArmDis(unsigned int addr, unsigned int w, char *output);