mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 23:40:29 +02:00
commit
3135c218f8
71 files changed
+3643
-2435
No files matched your search
+19
-16
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
@@ -20,6 +20,6 @@
|
||||
|
||||
// See the MIPS folder, the real stuff is there.
|
||||
|
||||
CPU *currentCPU = 0;
|
||||
MIPSState *currentCPU = 0;
|
||||
|
||||
int numCPUs;
|
||||
+2
-17
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -119,7 +119,6 @@ namespace CoreTiming
|
||||
|
||||
void SetClockFrequencyMHz(int cpuMhz);
|
||||
int GetClockFrequencyMHz();
|
||||
extern int downcount;
|
||||
extern int slicelength;
|
||||
|
||||
}; // end of namespace
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
@@ -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]
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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);*/
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 :(");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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(¤tMIPS->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(¤tMIPS->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(¤tMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
|
||||
JMP(asm_.dispatcherCheckCoreState, true);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ struct JitOptions
|
||||
{
|
||||
JitOptions()
|
||||
{
|
||||
enableBlocklink = false;
|
||||
enableBlocklink = true;
|
||||
}
|
||||
|
||||
bool enableBlocklink;
|
||||
|
||||
@@ -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
@@ -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 \
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
APP_STL := stlport_static
|
||||
APP_ABI := armeabi armeabi-v7a
|
||||
APP_ABI := armeabi-v7a armeabi
|
||||
|
||||
#APP_ABI := armeabi-v7a
|
||||
@@ -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!");
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
Reference in new issue
Block a user