mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #926 from comex/ppcstate-reg
PowerPCState register (and rationalize register usage, and add some registers to replace it)
This commit is contained in:
@@ -353,20 +353,7 @@ void XEmitter::ABI_CallFunctionR(void *func, X64Reg reg1)
|
||||
void XEmitter::ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2, bool noProlog)
|
||||
{
|
||||
ABI_AlignStack(0, noProlog);
|
||||
if (reg2 != ABI_PARAM1)
|
||||
{
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(64, R(ABI_PARAM1), R(reg1));
|
||||
if (reg2 != ABI_PARAM2)
|
||||
MOV(64, R(ABI_PARAM2), R(reg2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reg2 != ABI_PARAM2)
|
||||
MOV(64, R(ABI_PARAM2), R(reg2));
|
||||
if (reg1 != ABI_PARAM1)
|
||||
MOV(64, R(ABI_PARAM1), R(reg1));
|
||||
}
|
||||
MOVTwo(64, ABI_PARAM1, reg1, ABI_PARAM2, reg2, ABI_PARAM3);
|
||||
u64 distance = u64(func) - (u64(code) + 5);
|
||||
if (distance >= 0x0000000080000000ULL &&
|
||||
distance < 0xFFFFFFFF80000000ULL)
|
||||
@@ -382,6 +369,30 @@ void XEmitter::ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2, bool noP
|
||||
ABI_RestoreStack(0, noProlog);
|
||||
}
|
||||
|
||||
void XEmitter::MOVTwo(int bits, Gen::X64Reg dst1, Gen::X64Reg src1, Gen::X64Reg dst2, Gen::X64Reg src2, X64Reg temp)
|
||||
{
|
||||
if (dst1 == src2 && dst2 == src1)
|
||||
{
|
||||
// need a temporary
|
||||
MOV(bits, R(temp), R(src1));
|
||||
src1 = temp;
|
||||
}
|
||||
if (src2 != dst1)
|
||||
{
|
||||
if (dst1 != src1)
|
||||
MOV(bits, R(dst1), R(src1));
|
||||
if (dst2 != src2)
|
||||
MOV(bits, R(dst2), R(src2));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst2 != src2)
|
||||
MOV(bits, R(dst2), R(src2));
|
||||
if (dst1 != src1)
|
||||
MOV(bits, R(dst1), R(src1));
|
||||
}
|
||||
}
|
||||
|
||||
void XEmitter::ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2)
|
||||
{
|
||||
ABI_AlignStack(0);
|
||||
|
||||
@@ -53,3 +53,5 @@
|
||||
|
||||
#endif // WIN32
|
||||
|
||||
#define ABI_RETURN RAX
|
||||
|
||||
|
||||
@@ -753,6 +753,9 @@ public:
|
||||
void ABI_CallFunctionR(void *func, X64Reg reg1);
|
||||
void ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2, bool noProlog = false);
|
||||
|
||||
// Helper method for the above, or can be used separately.
|
||||
void MOVTwo(int bits, Gen::X64Reg dst1, Gen::X64Reg src1, Gen::X64Reg dst2, Gen::X64Reg src2, Gen::X64Reg temp);
|
||||
|
||||
// A function that doesn't have any control over what it will do to regs,
|
||||
// such as the dispatcher, should be surrounded by these.
|
||||
void ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
|
||||
@@ -39,14 +39,6 @@ using namespace PowerPC;
|
||||
|
||||
// Various notes below
|
||||
|
||||
// Register allocation
|
||||
// RAX - Generic quicktemp register
|
||||
// RBX - point to base of memory map
|
||||
// RSI RDI R12 R13 R14 R15 - free for allocation
|
||||
// RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are called.
|
||||
// RSP - stack pointer, do not generally use, very dangerous
|
||||
// RBP - ?
|
||||
|
||||
// IMPORTANT:
|
||||
// Make sure that all generated code and all emulator state sits under the 2GB boundary so that
|
||||
// RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary.
|
||||
@@ -210,8 +202,8 @@ void Jit64::WriteCallInterpreter(UGeckoInstruction inst)
|
||||
fpr.Flush();
|
||||
if (js.isLastInstruction)
|
||||
{
|
||||
MOV(32, M(&PC), Imm32(js.compilerPC));
|
||||
MOV(32, M(&NPC), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC));
|
||||
MOV(32, PPCSTATE(npc), Imm32(js.compilerPC + 4));
|
||||
}
|
||||
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
|
||||
ABI_CallFunctionC((void*)instr, inst.hex);
|
||||
@@ -279,7 +271,7 @@ void Jit64::WriteExit(u32 destination)
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
|
||||
//If nobody has taken care of this yet (this can be removed when all branches are done)
|
||||
JitBlock *b = js.curBlock;
|
||||
@@ -298,48 +290,48 @@ void Jit64::WriteExit(u32 destination)
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(32, M(&PC), Imm32(destination));
|
||||
MOV(32, PPCSTATE(pc), Imm32(destination));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
b->linkData.push_back(linkData);
|
||||
}
|
||||
|
||||
void Jit64::WriteExitDestInEAX()
|
||||
void Jit64::WriteExitDestInRSCRATCH()
|
||||
{
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
MOV(32, PPCSTATE(pc), R(RSCRATCH));
|
||||
Cleanup();
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteRfiExitDestInEAX()
|
||||
void Jit64::WriteRfiExitDestInRSCRATCH()
|
||||
{
|
||||
MOV(32, M(&PC), R(EAX));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
MOV(32, PPCSTATE(pc), R(RSCRATCH));
|
||||
MOV(32, PPCSTATE(npc), R(RSCRATCH));
|
||||
Cleanup();
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteExceptionExit()
|
||||
{
|
||||
Cleanup();
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(pc));
|
||||
MOV(32, PPCSTATE(npc), R(RSCRATCH));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
void Jit64::WriteExternalExceptionExit()
|
||||
{
|
||||
Cleanup();
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(pc));
|
||||
MOV(32, PPCSTATE(npc), R(RSCRATCH));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
@@ -426,7 +418,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
|
||||
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
|
||||
FixupBranch skip = J_CC(CC_NBE);
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
||||
JMP(asm_routines.doTiming, true); // downcount hit zero - go doTiming.
|
||||
SetJumpTarget(skip);
|
||||
|
||||
@@ -452,7 +444,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
}
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(NAN_CHECK)
|
||||
// should help logged stack-traces become more accurate
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
||||
#endif
|
||||
|
||||
// Start up the register allocators
|
||||
@@ -501,7 +493,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
|
||||
{
|
||||
js.fifoBytesThisBlock -= 32;
|
||||
MOV(32, M(&PC), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
|
||||
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC)); // Helps external systems know which instruction triggered the write
|
||||
u32 registersInUse = CallerSavedRegistersInUse();
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, false);
|
||||
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
|
||||
@@ -520,9 +512,9 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
HLEFunction(function);
|
||||
if (type == HLE::HLE_HOOK_REPLACE)
|
||||
{
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(npc));
|
||||
js.downcountAmount += js.st.numCycles;
|
||||
WriteExitDestInEAX();
|
||||
WriteExitDestInRSCRATCH();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -537,13 +529,13 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
fpr.Flush();
|
||||
|
||||
//This instruction uses FPU - needs to add FP exception bailout
|
||||
TEST(32, M(&PowerPC::ppcState.msr), Imm32(1 << 13)); // Test FP enabled bit
|
||||
TEST(32, PPCSTATE(msr), Imm32(1 << 13)); // Test FP enabled bit
|
||||
FixupBranch b1 = J_CC(CC_NZ, true);
|
||||
|
||||
// If a FPU exception occurs, the exception handler will read
|
||||
// from PC. Update PC with the latest value in case that happens.
|
||||
MOV(32, M(&PC), Imm32(ops[i].address));
|
||||
OR(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
|
||||
MOV(32, PPCSTATE(pc), Imm32(ops[i].address));
|
||||
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
|
||||
WriteExceptionExit();
|
||||
|
||||
SetJumpTarget(b1);
|
||||
@@ -557,16 +549,16 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_ISI | EXCEPTION_PROGRAM | EXCEPTION_SYSCALL | EXCEPTION_FPU_UNAVAILABLE | EXCEPTION_DSI | EXCEPTION_ALIGNMENT));
|
||||
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_ISI | EXCEPTION_PROGRAM | EXCEPTION_SYSCALL | EXCEPTION_FPU_UNAVAILABLE | EXCEPTION_DSI | EXCEPTION_ALIGNMENT));
|
||||
FixupBranch clearInt = J_CC(CC_NZ, true);
|
||||
TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT));
|
||||
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_EXTERNAL_INT));
|
||||
FixupBranch noExtException = J_CC(CC_Z, true);
|
||||
TEST(32, M((void *)&PowerPC::ppcState.msr), Imm32(0x0008000));
|
||||
TEST(32, PPCSTATE(msr), Imm32(0x0008000));
|
||||
FixupBranch noExtIntEnable = J_CC(CC_Z, true);
|
||||
TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP | ProcessorInterface::INT_CAUSE_PE_TOKEN | ProcessorInterface::INT_CAUSE_PE_FINISH));
|
||||
FixupBranch noCPInt = J_CC(CC_Z, true);
|
||||
|
||||
MOV(32, M(&PC), Imm32(ops[i].address));
|
||||
MOV(32, PPCSTATE(pc), Imm32(ops[i].address));
|
||||
WriteExternalExceptionExit();
|
||||
|
||||
SetJumpTarget(noCPInt);
|
||||
@@ -580,7 +572,7 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
MOV(32, M(&PC), Imm32(ops[i].address));
|
||||
MOV(32, PPCSTATE(pc), Imm32(ops[i].address));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckBreakPoints));
|
||||
TEST(32, M((void*)PowerPC::GetStatePtr()), Imm32(0xFFFFFFFF));
|
||||
FixupBranch noBreakpoint = J_CC(CC_Z);
|
||||
@@ -597,12 +589,12 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_DSI));
|
||||
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_DSI));
|
||||
FixupBranch noMemException = J_CC(CC_Z, true);
|
||||
|
||||
// If a memory exception occurs, the exception handler will read
|
||||
// from PC. Update PC with the latest value in case that happens.
|
||||
MOV(32, M(&PC), Imm32(ops[i].address));
|
||||
MOV(32, PPCSTATE(pc), Imm32(ops[i].address));
|
||||
WriteExceptionExit();
|
||||
SetJumpTarget(noMemException);
|
||||
}
|
||||
@@ -645,13 +637,13 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
if (code_block.m_memory_exception)
|
||||
{
|
||||
// Address of instruction could not be translated
|
||||
MOV(32, M(&NPC), Imm32(js.compilerPC));
|
||||
MOV(32, PPCSTATE(npc), Imm32(js.compilerPC));
|
||||
|
||||
OR(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_ISI));
|
||||
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_ISI));
|
||||
|
||||
// Remove the invalid instruction from the icache, forcing a recompile
|
||||
MOV(64, R(RAX), ImmPtr(jit->GetBlockCache()->GetICachePtr(js.compilerPC)));
|
||||
MOV(32,MatR(RAX),Imm32(JIT_ICACHE_INVALID_WORD));
|
||||
MOV(64, R(RSCRATCH), ImmPtr(jit->GetBlockCache()->GetICachePtr(js.compilerPC)));
|
||||
MOV(32,MatR(RSCRATCH),Imm32(JIT_ICACHE_INVALID_WORD));
|
||||
|
||||
WriteExceptionExit();
|
||||
}
|
||||
|
||||
@@ -90,10 +90,10 @@ public:
|
||||
// Utilities for use by opcodes
|
||||
|
||||
void WriteExit(u32 destination);
|
||||
void WriteExitDestInEAX();
|
||||
void WriteExitDestInRSCRATCH();
|
||||
void WriteExceptionExit();
|
||||
void WriteExternalExceptionExit();
|
||||
void WriteRfiExitDestInEAX();
|
||||
void WriteRfiExitDestInRSCRATCH();
|
||||
void WriteCallInterpreter(UGeckoInstruction _inst);
|
||||
void Cleanup();
|
||||
|
||||
@@ -101,16 +101,15 @@ public:
|
||||
void GenerateConstantOverflow(s64 val);
|
||||
void GenerateOverflow();
|
||||
void FinalizeCarryOverflow(bool oe, bool inv = false);
|
||||
void GetCarryEAXAndClear();
|
||||
void FinalizeCarryGenerateOverflowEAX(bool oe, bool inv = false);
|
||||
void GetCarryRSCRATCHAndClear();
|
||||
void FinalizeCarryGenerateOverflowRSCRATCH(bool oe, bool inv = false);
|
||||
void GenerateCarry();
|
||||
void GenerateRC();
|
||||
void ComputeRC(const Gen::OpArg & arg);
|
||||
|
||||
// Reads a given bit of a given CR register part. Clobbers ABI_PARAM1,
|
||||
// don't forget to xlock it before.
|
||||
// Reads a given bit of a given CR register part.
|
||||
void GetCRFieldBit(int field, int bit, Gen::X64Reg out, bool negate = false);
|
||||
// Clobbers ABI_PARAM1, xlock it before.
|
||||
// Clobbers RDX.
|
||||
void SetCRFieldBit(int field, int bit, Gen::X64Reg in);
|
||||
|
||||
// Generates a branch that will check if a given bit of a CR register part
|
||||
|
||||
@@ -9,14 +9,6 @@
|
||||
|
||||
using namespace Gen;
|
||||
|
||||
//GLOBAL STATIC ALLOCATIONS x86
|
||||
//EAX - ubiquitous scratch register - EVERYBODY scratches this
|
||||
|
||||
//GLOBAL STATIC ALLOCATIONS x64
|
||||
//EAX - ubiquitous scratch register - EVERYBODY scratches this
|
||||
//RBX - Base pointer of memory
|
||||
//R15 - Pointer to array of block pointers
|
||||
|
||||
// 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.
|
||||
@@ -27,8 +19,9 @@ void Jit64AsmRoutineManager::Generate()
|
||||
ABI_PushAllCalleeSavedRegsAndAdjustStack();
|
||||
|
||||
// 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
|
||||
MOV(64, R(RMEM), Imm64((u64)Memory::base));
|
||||
MOV(64, R(RCODE_POINTERS), Imm64((u64)jit->GetBlockCache()->GetCodePointers())); //It's below 2GB so 32 bits are good enough
|
||||
MOV(64, R(RPPCSTATE), Imm64((u64)&PowerPC::ppcState + 0x80));
|
||||
|
||||
const u8* outerLoop = GetCodePtr();
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&CoreTiming::Advance));
|
||||
@@ -55,8 +48,8 @@ void Jit64AsmRoutineManager::Generate()
|
||||
SetJumpTarget(skipToRealDispatch);
|
||||
|
||||
dispatcherNoCheck = GetCodePtr();
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.pc));
|
||||
dispatcherPcInEAX = GetCodePtr();
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(pc));
|
||||
dispatcherPcInRSCRATCH = GetCodePtr();
|
||||
|
||||
u32 mask = 0;
|
||||
FixupBranch no_mem;
|
||||
@@ -68,12 +61,12 @@ void Jit64AsmRoutineManager::Generate()
|
||||
mask |= JIT_ICACHE_VMEM_BIT;
|
||||
if (Core::g_CoreStartupParameter.bWii || Core::g_CoreStartupParameter.bMMU || Core::g_CoreStartupParameter.bTLBHack)
|
||||
{
|
||||
TEST(32, R(EAX), Imm32(mask));
|
||||
TEST(32, R(RSCRATCH), Imm32(mask));
|
||||
no_mem = J_CC(CC_NZ);
|
||||
}
|
||||
AND(32, R(EAX), Imm32(JIT_ICACHE_MASK));
|
||||
MOV(64, R(RSI), Imm64((u64)jit->GetBlockCache()->iCache));
|
||||
MOV(32, R(EAX), MComplex(RSI, EAX, SCALE_1, 0));
|
||||
AND(32, R(RSCRATCH), Imm32(JIT_ICACHE_MASK));
|
||||
MOV(64, R(RSCRATCH2), Imm64((u64)jit->GetBlockCache()->iCache));
|
||||
MOV(32, R(RSCRATCH), MComplex(RSCRATCH2, RSCRATCH, SCALE_1, 0));
|
||||
|
||||
if (Core::g_CoreStartupParameter.bWii || Core::g_CoreStartupParameter.bMMU || Core::g_CoreStartupParameter.bTLBHack)
|
||||
{
|
||||
@@ -82,22 +75,22 @@ void Jit64AsmRoutineManager::Generate()
|
||||
}
|
||||
if (Core::g_CoreStartupParameter.bMMU || Core::g_CoreStartupParameter.bTLBHack)
|
||||
{
|
||||
TEST(32, R(EAX), Imm32(JIT_ICACHE_VMEM_BIT));
|
||||
TEST(32, R(RSCRATCH), Imm32(JIT_ICACHE_VMEM_BIT));
|
||||
FixupBranch no_vmem = J_CC(CC_Z);
|
||||
AND(32, R(EAX), Imm32(JIT_ICACHE_MASK));
|
||||
MOV(64, R(RSI), Imm64((u64)jit->GetBlockCache()->iCacheVMEM));
|
||||
MOV(32, R(EAX), MComplex(RSI, EAX, SCALE_1, 0));
|
||||
AND(32, R(RSCRATCH), Imm32(JIT_ICACHE_MASK));
|
||||
MOV(64, R(RSCRATCH2), Imm64((u64)jit->GetBlockCache()->iCacheVMEM));
|
||||
MOV(32, R(RSCRATCH), MComplex(RSCRATCH2, RSCRATCH, SCALE_1, 0));
|
||||
|
||||
if (Core::g_CoreStartupParameter.bWii) exit_vmem = J();
|
||||
SetJumpTarget(no_vmem);
|
||||
}
|
||||
if (Core::g_CoreStartupParameter.bWii)
|
||||
{
|
||||
TEST(32, R(EAX), Imm32(JIT_ICACHE_EXRAM_BIT));
|
||||
TEST(32, R(RSCRATCH), Imm32(JIT_ICACHE_EXRAM_BIT));
|
||||
FixupBranch no_exram = J_CC(CC_Z);
|
||||
AND(32, R(EAX), Imm32(JIT_ICACHEEX_MASK));
|
||||
MOV(64, R(RSI), Imm64((u64)jit->GetBlockCache()->iCacheEx));
|
||||
MOV(32, R(EAX), MComplex(RSI, EAX, SCALE_1, 0));
|
||||
AND(32, R(RSCRATCH), Imm32(JIT_ICACHEEX_MASK));
|
||||
MOV(64, R(RSCRATCH2), Imm64((u64)jit->GetBlockCache()->iCacheEx));
|
||||
MOV(32, R(RSCRATCH), MComplex(RSCRATCH2, RSCRATCH, SCALE_1, 0));
|
||||
|
||||
SetJumpTarget(no_exram);
|
||||
}
|
||||
@@ -106,14 +99,14 @@ void Jit64AsmRoutineManager::Generate()
|
||||
if (Core::g_CoreStartupParameter.bWii && (Core::g_CoreStartupParameter.bMMU || Core::g_CoreStartupParameter.bTLBHack))
|
||||
SetJumpTarget(exit_vmem);
|
||||
|
||||
TEST(32, R(EAX), R(EAX));
|
||||
TEST(32, R(RSCRATCH), R(RSCRATCH));
|
||||
FixupBranch notfound = J_CC(CC_L);
|
||||
//grab from list and jump to it
|
||||
JMPptr(MComplex(R15, RAX, 8, 0));
|
||||
JMPptr(MComplex(RCODE_POINTERS, RSCRATCH, 8, 0));
|
||||
SetJumpTarget(notfound);
|
||||
|
||||
//Ok, no block, let's jit
|
||||
MOV(32, R(ABI_PARAM1), M(&PowerPC::ppcState.pc));
|
||||
MOV(32, R(ABI_PARAM1), PPCSTATE(pc));
|
||||
CALL((void *)&Jit);
|
||||
|
||||
JMP(dispatcherNoCheck); // no point in special casing this
|
||||
@@ -122,10 +115,10 @@ void Jit64AsmRoutineManager::Generate()
|
||||
doTiming = GetCodePtr();
|
||||
|
||||
// Test external exceptions.
|
||||
TEST(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
|
||||
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
|
||||
FixupBranch noExtException = J_CC(CC_Z);
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(pc));
|
||||
MOV(32, PPCSTATE(npc), R(RSCRATCH));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExternalExceptions));
|
||||
SetJumpTarget(noExtException);
|
||||
|
||||
@@ -168,8 +161,8 @@ void Jit64AsmRoutineManager::GenerateCommon()
|
||||
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);
|
||||
MOV(32, RSCRATCH, M(&m_gatherPipeCount));
|
||||
MOV(8, MDisp(RSCRATCH, (u32)&m_gatherPipe), ABI_PARAM1);
|
||||
ADD(32, 1, M(&m_gatherPipeCount));
|
||||
RET();
|
||||
SetJumpTarget(skip_fast_write);
|
||||
|
||||
@@ -200,9 +200,9 @@ const int* GPRRegCache::GetAllocationOrder(size_t& count)
|
||||
{
|
||||
// R12, when used as base register, for example in a LEA, can generate bad code! Need to look into this.
|
||||
#ifdef _WIN32
|
||||
RSI, RDI, R13, R14, R8, R9, R10, R11, R12, //, RCX
|
||||
RSI, RDI, R13, R14, R8, R9, R10, R11, R12, RCX
|
||||
#else
|
||||
RBP, R13, R14, R8, R9, R10, R11, R12, //, RCX
|
||||
R12, R13, R14, RSI, RDI, R8, R9, R10, R11, RCX
|
||||
#endif
|
||||
};
|
||||
count = sizeof(allocationOrder) / sizeof(const int);
|
||||
@@ -221,12 +221,12 @@ const int* FPURegCache::GetAllocationOrder(size_t& count)
|
||||
|
||||
OpArg GPRRegCache::GetDefaultLocation(size_t reg) const
|
||||
{
|
||||
return M(&ppcState.gpr[reg]);
|
||||
return PPCSTATE(gpr[reg]);
|
||||
}
|
||||
|
||||
OpArg FPURegCache::GetDefaultLocation(size_t reg) const
|
||||
{
|
||||
return M(&ppcState.ps[reg][0]);
|
||||
return PPCSTATE(ps[reg][0]);
|
||||
}
|
||||
|
||||
void RegCache::KillImmediate(size_t preg, bool doLoad, bool makeDirty)
|
||||
|
||||
@@ -28,9 +28,9 @@ void Jit64::sc(UGeckoInstruction inst)
|
||||
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC + 4));
|
||||
LOCK();
|
||||
OR(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_SYSCALL));
|
||||
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_SYSCALL));
|
||||
WriteExceptionExit();
|
||||
}
|
||||
|
||||
@@ -45,13 +45,13 @@ void Jit64::rfi(UGeckoInstruction inst)
|
||||
const u32 mask = 0x87C0FFFF;
|
||||
const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
|
||||
// MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13;
|
||||
AND(32, M(&MSR), Imm32((~mask) & clearMSR13));
|
||||
MOV(32, R(EAX), M(&SRR1));
|
||||
AND(32, R(EAX), Imm32(mask & clearMSR13));
|
||||
OR(32, M(&MSR), R(EAX));
|
||||
AND(32, PPCSTATE(msr), Imm32((~mask) & clearMSR13));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE_SRR1);
|
||||
AND(32, R(RSCRATCH), Imm32(mask & clearMSR13));
|
||||
OR(32, PPCSTATE(msr), R(RSCRATCH));
|
||||
// NPC = SRR0;
|
||||
MOV(32, R(EAX), M(&SRR0));
|
||||
WriteRfiExitDestInEAX();
|
||||
MOV(32, R(RSCRATCH), PPCSTATE_SRR0);
|
||||
WriteRfiExitDestInRSCRATCH();
|
||||
}
|
||||
|
||||
void Jit64::bx(UGeckoInstruction inst)
|
||||
@@ -62,7 +62,7 @@ void Jit64::bx(UGeckoInstruction inst)
|
||||
// We must always process the following sentence
|
||||
// even if the blocks are merged by PPCAnalyst::Flatten().
|
||||
if (inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4));
|
||||
|
||||
// If this is not the last instruction of a block,
|
||||
// we will skip the rest process.
|
||||
@@ -82,7 +82,7 @@ void Jit64::bx(UGeckoInstruction inst)
|
||||
destination = js.compilerPC + SignExt26(inst.LI << 2);
|
||||
#ifdef ACID_TEST
|
||||
if (inst.LK)
|
||||
AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
AND(32, PPCSTATE(cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
if (destination == js.compilerPC)
|
||||
{
|
||||
@@ -108,7 +108,7 @@ void Jit64::bcx(UGeckoInstruction inst)
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
SUB(32, M(&CTR), Imm8(1));
|
||||
SUB(32, PPCSTATE_CTR, Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = J_CC(CC_NZ, true);
|
||||
else
|
||||
@@ -123,7 +123,7 @@ void Jit64::bcx(UGeckoInstruction inst)
|
||||
}
|
||||
|
||||
if (inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4));
|
||||
|
||||
u32 destination;
|
||||
if (inst.AA)
|
||||
@@ -164,11 +164,11 @@ void Jit64::bcctrx(UGeckoInstruction inst)
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
|
||||
MOV(32, R(EAX), M(&CTR));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE_CTR);
|
||||
if (inst.LK_3)
|
||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||
AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||
WriteExitDestInEAX();
|
||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||
WriteExitDestInRSCRATCH();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -179,15 +179,15 @@ void Jit64::bcctrx(UGeckoInstruction inst)
|
||||
|
||||
FixupBranch b = JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3),
|
||||
!(inst.BO_2 & BO_BRANCH_IF_TRUE));
|
||||
MOV(32, R(EAX), M(&CTR));
|
||||
AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||
//MOV(32, M(&PC), R(EAX)); => Already done in WriteExitDestInEAX()
|
||||
MOV(32, R(RSCRATCH), PPCSTATE_CTR);
|
||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||
//MOV(32, PPCSTATE(pc), R(RSCRATCH)); => Already done in WriteExitDestInRSCRATCH()
|
||||
if (inst.LK_3)
|
||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4)); // LR = PC + 4;
|
||||
|
||||
gpr.Flush(FLUSH_MAINTAIN_STATE);
|
||||
fpr.Flush(FLUSH_MAINTAIN_STATE);
|
||||
WriteExitDestInEAX();
|
||||
WriteExitDestInRSCRATCH();
|
||||
// Would really like to continue the block here, but it ends. TODO.
|
||||
SetJumpTarget(b);
|
||||
|
||||
@@ -204,7 +204,7 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
||||
FixupBranch pCTRDontBranch;
|
||||
if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR
|
||||
{
|
||||
SUB(32, M(&CTR), Imm8(1));
|
||||
SUB(32, PPCSTATE_CTR, Imm8(1));
|
||||
if (inst.BO & BO_BRANCH_IF_CTR_0)
|
||||
pCTRDontBranch = J_CC(CC_NZ, true);
|
||||
else
|
||||
@@ -221,17 +221,17 @@ void Jit64::bclrx(UGeckoInstruction inst)
|
||||
// This below line can be used to prove that blr "eats flags" in practice.
|
||||
// This observation will let us do a lot of fun observations.
|
||||
#ifdef ACID_TEST
|
||||
AND(32, M(&PowerPC::ppcState.cr), Imm32(~(0xFF000000)));
|
||||
AND(32, PPCSTATE(cr), Imm32(~(0xFF000000)));
|
||||
#endif
|
||||
|
||||
MOV(32, R(EAX), M(&LR));
|
||||
AND(32, R(EAX), Imm32(0xFFFFFFFC));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE_LR);
|
||||
AND(32, R(RSCRATCH), Imm32(0xFFFFFFFC));
|
||||
if (inst.LK)
|
||||
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE_LR, Imm32(js.compilerPC + 4));
|
||||
|
||||
gpr.Flush(FLUSH_MAINTAIN_STATE);
|
||||
fpr.Flush(FLUSH_MAINTAIN_STATE);
|
||||
WriteExitDestInEAX();
|
||||
WriteExitDestInRSCRATCH();
|
||||
|
||||
if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0)
|
||||
SetJumpTarget( pConditionDontBranch );
|
||||
|
||||
@@ -248,7 +248,7 @@ void Jit64::fcmpx(UGeckoInstruction inst)
|
||||
fpr.BindToRegister(b, true);
|
||||
|
||||
if (fprf)
|
||||
AND(32, M(&FPSCR), Imm32(~FPRF_MASK));
|
||||
AND(32, PPCSTATE(fpscr), Imm32(~FPRF_MASK));
|
||||
// Are we masking sNaN invalid floating point exceptions? If not this could crash if we don't handle the exception?
|
||||
UCOMISD(fpr.R(b).GetSimpleReg(), fpr.R(a));
|
||||
|
||||
@@ -271,31 +271,31 @@ void Jit64::fcmpx(UGeckoInstruction inst)
|
||||
pGreater = J_CC(CC_B);
|
||||
}
|
||||
|
||||
MOV(64, R(RAX), Imm64(PPCCRToInternal(CR_EQ)));
|
||||
MOV(64, R(RSCRATCH), Imm64(PPCCRToInternal(CR_EQ)));
|
||||
if (fprf)
|
||||
OR(32, M(&FPSCR), Imm32(CR_EQ << FPRF_SHIFT));
|
||||
OR(32, PPCSTATE(fpscr), Imm32(CR_EQ << FPRF_SHIFT));
|
||||
|
||||
continue1 = J();
|
||||
|
||||
SetJumpTarget(pNaN);
|
||||
MOV(64, R(RAX), Imm64(PPCCRToInternal(CR_SO)));
|
||||
MOV(64, R(RSCRATCH), Imm64(PPCCRToInternal(CR_SO)));
|
||||
if (fprf)
|
||||
OR(32, M(&FPSCR), Imm32(CR_SO << FPRF_SHIFT));
|
||||
OR(32, PPCSTATE(fpscr), Imm32(CR_SO << FPRF_SHIFT));
|
||||
|
||||
if (a != b)
|
||||
{
|
||||
continue2 = J();
|
||||
|
||||
SetJumpTarget(pGreater);
|
||||
MOV(64, R(RAX), Imm64(PPCCRToInternal(CR_GT)));
|
||||
MOV(64, R(RSCRATCH), Imm64(PPCCRToInternal(CR_GT)));
|
||||
if (fprf)
|
||||
OR(32, M(&FPSCR), Imm32(CR_GT << FPRF_SHIFT));
|
||||
OR(32, PPCSTATE(fpscr), Imm32(CR_GT << FPRF_SHIFT));
|
||||
continue3 = J();
|
||||
|
||||
SetJumpTarget(pLesser);
|
||||
MOV(64, R(RAX), Imm64(PPCCRToInternal(CR_LT)));
|
||||
MOV(64, R(RSCRATCH), Imm64(PPCCRToInternal(CR_LT)));
|
||||
if (fprf)
|
||||
OR(32, M(&FPSCR), Imm32(CR_LT << FPRF_SHIFT));
|
||||
OR(32, PPCSTATE(fpscr), Imm32(CR_LT << FPRF_SHIFT));
|
||||
}
|
||||
|
||||
SetJumpTarget(continue1);
|
||||
@@ -305,7 +305,7 @@ void Jit64::fcmpx(UGeckoInstruction inst)
|
||||
SetJumpTarget(continue3);
|
||||
}
|
||||
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[crf]), R(RAX));
|
||||
MOV(64, PPCSTATE(cr_val[crf]), R(RSCRATCH));
|
||||
fpr.UnlockAll();
|
||||
}
|
||||
|
||||
@@ -375,8 +375,7 @@ void Jit64::frsqrtex(UGeckoInstruction inst)
|
||||
int b = inst.FB;
|
||||
int d = inst.FD;
|
||||
|
||||
// rsqrtex requires ECX and EDX free
|
||||
gpr.FlushLockX(ECX, EDX);
|
||||
gpr.FlushLockX(RSCRATCH_EXTRA);
|
||||
fpr.Lock(b, d);
|
||||
fpr.BindToRegister(d, d == b);
|
||||
MOVSD(XMM0, fpr.R(b));
|
||||
@@ -395,8 +394,7 @@ void Jit64::fresx(UGeckoInstruction inst)
|
||||
int b = inst.FB;
|
||||
int d = inst.FD;
|
||||
|
||||
// resx requires ECX and EDX free
|
||||
gpr.FlushLockX(ECX, EDX);
|
||||
gpr.FlushLockX(RSCRATCH_EXTRA);
|
||||
fpr.Lock(b, d);
|
||||
fpr.BindToRegister(d, d == b);
|
||||
MOVSD(XMM0, fpr.R(b));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -123,7 +123,7 @@ void Jit64::lXXx(UGeckoInstruction inst)
|
||||
ABI_PopRegistersAndAdjustStack(registersInUse, false);
|
||||
|
||||
// ! we must continue executing of the loop after exception handling, maybe there is still 0 in r0
|
||||
//MOV(32, M(&PowerPC::ppcState.pc), Imm32(js.compilerPC));
|
||||
//MOV(32, PPCSTATE(pc), Imm32(js.compilerPC));
|
||||
WriteExceptionExit();
|
||||
|
||||
SetJumpTarget(noIdle);
|
||||
@@ -197,14 +197,13 @@ void Jit64::lXXx(UGeckoInstruction inst)
|
||||
else
|
||||
{
|
||||
// In this case we need an extra temporary register.
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
opAddress = R(ABI_PARAM1);
|
||||
opAddress = R(RSCRATCH2);
|
||||
storeAddress = true;
|
||||
if (use_constant_offset)
|
||||
{
|
||||
if (gpr.R(a).IsSimpleReg() && offset != 0)
|
||||
{
|
||||
LEA(32, ABI_PARAM1, MDisp(gpr.RX(a), offset));
|
||||
LEA(32, RSCRATCH2, MDisp(gpr.RX(a), offset));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -215,7 +214,7 @@ void Jit64::lXXx(UGeckoInstruction inst)
|
||||
}
|
||||
else if (gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg())
|
||||
{
|
||||
LEA(32, ABI_PARAM1, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
LEA(32, RSCRATCH2, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -228,7 +227,13 @@ void Jit64::lXXx(UGeckoInstruction inst)
|
||||
|
||||
gpr.Lock(a, b, d);
|
||||
gpr.BindToRegister(d, js.memcheck, true);
|
||||
SafeLoadToReg(gpr.RX(d), opAddress, accessSize, loadOffset, CallerSavedRegistersInUse(), signExtend);
|
||||
u32 registersInUse = CallerSavedRegistersInUse();
|
||||
if (update && storeAddress)
|
||||
{
|
||||
// We need to save the (usually scratch) address register for the update.
|
||||
registersInUse |= (1 << RSCRATCH2);
|
||||
}
|
||||
SafeLoadToReg(gpr.RX(d), opAddress, accessSize, loadOffset, registersInUse, signExtend);
|
||||
|
||||
if (update && storeAddress)
|
||||
{
|
||||
@@ -269,11 +274,11 @@ void Jit64::dcbz(UGeckoInstruction inst)
|
||||
if (Core::g_CoreStartupParameter.bMMU || Core::g_CoreStartupParameter.bTLBHack)
|
||||
mem_mask |= Memory::ADDR_MASK_MEM1;
|
||||
|
||||
MOV(32, R(EAX), gpr.R(b));
|
||||
MOV(32, R(RSCRATCH), gpr.R(b));
|
||||
if (a)
|
||||
ADD(32, R(EAX), gpr.R(a));
|
||||
AND(32, R(EAX), Imm32(~31));
|
||||
TEST(32, R(EAX), Imm32(mem_mask));
|
||||
ADD(32, R(RSCRATCH), gpr.R(a));
|
||||
AND(32, R(RSCRATCH), Imm32(~31));
|
||||
TEST(32, R(RSCRATCH), Imm32(mem_mask));
|
||||
FixupBranch fast = J_CC(CC_Z, true);
|
||||
|
||||
// Should this code ever run? I can't find any games that use DCBZ on non-physical addresses, but
|
||||
@@ -281,14 +286,14 @@ void Jit64::dcbz(UGeckoInstruction inst)
|
||||
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
|
||||
u32 registersInUse = CallerSavedRegistersInUse();
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, false);
|
||||
ABI_CallFunctionR((void *)&Memory::ClearCacheLine, EAX);
|
||||
ABI_CallFunctionR((void *)&Memory::ClearCacheLine, RSCRATCH);
|
||||
ABI_PopRegistersAndAdjustStack(registersInUse, false);
|
||||
|
||||
FixupBranch exit = J();
|
||||
SetJumpTarget(fast);
|
||||
PXOR(XMM0, R(XMM0));
|
||||
MOVAPS(MComplex(RBX, RAX, SCALE_1, 0), XMM0);
|
||||
MOVAPS(MComplex(RBX, RAX, SCALE_1, 16), XMM0);
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
|
||||
SetJumpTarget(exit);
|
||||
}
|
||||
|
||||
@@ -331,10 +336,9 @@ void Jit64::stX(UGeckoInstruction inst)
|
||||
if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe)
|
||||
{
|
||||
// Helps external systems know which instruction triggered the write
|
||||
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
|
||||
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
|
||||
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
MOV(32, R(ABI_PARAM1), gpr.R(s));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(s));
|
||||
if (update)
|
||||
gpr.SetImmediate32(a, addr);
|
||||
|
||||
@@ -358,8 +362,8 @@ void Jit64::stX(UGeckoInstruction inst)
|
||||
}
|
||||
else if (Memory::IsRAMAddress(addr))
|
||||
{
|
||||
MOV(32, R(EAX), gpr.R(s));
|
||||
WriteToConstRamAddress(accessSize, EAX, addr, true);
|
||||
MOV(32, R(RSCRATCH), gpr.R(s));
|
||||
WriteToConstRamAddress(accessSize, RSCRATCH, addr, true);
|
||||
if (update)
|
||||
gpr.SetImmediate32(a, addr);
|
||||
return;
|
||||
@@ -367,7 +371,7 @@ void Jit64::stX(UGeckoInstruction inst)
|
||||
else
|
||||
{
|
||||
// Helps external systems know which instruction triggered the write
|
||||
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
|
||||
MOV(32, PPCSTATE(pc), Imm32(jit->js.compilerPC));
|
||||
|
||||
u32 registersInUse = CallerSavedRegistersInUse();
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, false);
|
||||
@@ -390,24 +394,31 @@ void Jit64::stX(UGeckoInstruction inst)
|
||||
}
|
||||
}
|
||||
|
||||
gpr.FlushLockX(ECX, EDX);
|
||||
gpr.Lock(s, a);
|
||||
MOV(32, R(EDX), gpr.R(a));
|
||||
MOV(32, R(ECX), gpr.R(s));
|
||||
SafeWriteRegToReg(ECX, EDX, accessSize, offset, CallerSavedRegistersInUse());
|
||||
gpr.Lock(a, s);
|
||||
gpr.BindToRegister(a, true, false);
|
||||
X64Reg reg_value;
|
||||
if (WriteClobbersRegValue(accessSize, /* swap */ true))
|
||||
{
|
||||
MOV(32, R(RSCRATCH2), gpr.R(s));
|
||||
reg_value = RSCRATCH2;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(s, true, false);
|
||||
reg_value = gpr.RX(s);
|
||||
}
|
||||
SafeWriteRegToReg(reg_value, gpr.RX(a), accessSize, offset, CallerSavedRegistersInUse(), SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR);
|
||||
|
||||
if (update && offset)
|
||||
{
|
||||
gpr.KillImmediate(a, true, true);
|
||||
MEMCHECK_START
|
||||
gpr.KillImmediate(a, true, true);
|
||||
|
||||
ADD(32, gpr.R(a), Imm32((u32)offset));
|
||||
|
||||
MEMCHECK_END
|
||||
}
|
||||
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,24 +435,21 @@ void Jit64::stXx(UGeckoInstruction inst)
|
||||
FALLBACK_IF(!a || a == s || a == b);
|
||||
|
||||
gpr.Lock(a, b, s);
|
||||
gpr.FlushLockX(ECX, EDX);
|
||||
|
||||
if (inst.SUBOP10 & 32)
|
||||
{
|
||||
MEMCHECK_START
|
||||
gpr.BindToRegister(a, true, true);
|
||||
ADD(32, gpr.R(a), gpr.R(b));
|
||||
MOV(32, R(EDX), gpr.R(a));
|
||||
MEMCHECK_END
|
||||
MOV(32, R(RSCRATCH2), gpr.R(a));
|
||||
}
|
||||
else if (gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg())
|
||||
{
|
||||
LEA(32, EDX, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
LEA(32, RSCRATCH2, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(32, R(EDX), gpr.R(a));
|
||||
ADD(32, R(EDX), gpr.R(b));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(a));
|
||||
ADD(32, R(RSCRATCH2), gpr.R(b));
|
||||
}
|
||||
|
||||
int accessSize;
|
||||
@@ -462,8 +470,18 @@ void Jit64::stXx(UGeckoInstruction inst)
|
||||
break;
|
||||
}
|
||||
|
||||
MOV(32, R(ECX), gpr.R(s));
|
||||
SafeWriteRegToReg(ECX, EDX, accessSize, 0, CallerSavedRegistersInUse());
|
||||
X64Reg reg_value;
|
||||
if (WriteClobbersRegValue(accessSize, /* swap */ true))
|
||||
{
|
||||
MOV(32, R(RSCRATCH), gpr.R(s));
|
||||
reg_value = RSCRATCH;
|
||||
}
|
||||
else
|
||||
{
|
||||
gpr.BindToRegister(s, true, false);
|
||||
reg_value = gpr.RX(s);
|
||||
}
|
||||
SafeWriteRegToReg(reg_value, RSCRATCH2, accessSize, 0, CallerSavedRegistersInUse());
|
||||
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
@@ -476,15 +494,14 @@ void Jit64::lmw(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStoreOff);
|
||||
|
||||
// TODO: This doesn't handle rollback on DSI correctly
|
||||
gpr.FlushLockX(ECX);
|
||||
MOV(32, R(ECX), Imm32((u32)(s32)inst.SIMM_16));
|
||||
MOV(32, R(RSCRATCH2), Imm32((u32)(s32)inst.SIMM_16));
|
||||
if (inst.RA)
|
||||
ADD(32, R(ECX), gpr.R(inst.RA));
|
||||
ADD(32, R(RSCRATCH2), gpr.R(inst.RA));
|
||||
for (int i = inst.RD; i < 32; i++)
|
||||
{
|
||||
SafeLoadToReg(EAX, R(ECX), 32, (i - inst.RD) * 4, CallerSavedRegistersInUse(), false);
|
||||
SafeLoadToReg(RSCRATCH, R(RSCRATCH2), 32, (i - inst.RD) * 4, CallerSavedRegistersInUse() | (1 << RSCRATCH_EXTRA), false);
|
||||
gpr.BindToRegister(i, false, true);
|
||||
MOV(32, gpr.R(i), R(EAX));
|
||||
MOV(32, gpr.R(i), R(RSCRATCH));
|
||||
}
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
@@ -495,15 +512,14 @@ void Jit64::stmw(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStoreOff);
|
||||
|
||||
// TODO: This doesn't handle rollback on DSI correctly
|
||||
gpr.FlushLockX(ECX);
|
||||
for (int i = inst.RD; i < 32; i++)
|
||||
{
|
||||
if (inst.RA)
|
||||
MOV(32, R(EAX), gpr.R(inst.RA));
|
||||
MOV(32, R(RSCRATCH), gpr.R(inst.RA));
|
||||
else
|
||||
XOR(32, R(EAX), R(EAX));
|
||||
MOV(32, R(ECX), gpr.R(i));
|
||||
SafeWriteRegToReg(ECX, EAX, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse());
|
||||
XOR(32, R(RSCRATCH), R(RSCRATCH));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(i));
|
||||
SafeWriteRegToReg(RSCRATCH2, RSCRATCH, 32, (i - inst.RD) * 4 + (u32)(s32)inst.SIMM_16, CallerSavedRegistersInUse());
|
||||
}
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
|
||||
@@ -42,9 +42,9 @@ void Jit64::lfXXX(UGeckoInstruction inst)
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = R(EAX);
|
||||
addr = R(RSCRATCH);
|
||||
if (a && gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg())
|
||||
LEA(32, EAX, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
LEA(32, RSCRATCH, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
else
|
||||
{
|
||||
MOV(32, addr, gpr.R(b));
|
||||
@@ -61,18 +61,18 @@ void Jit64::lfXXX(UGeckoInstruction inst)
|
||||
offset = (s32)(s16)inst.SIMM_16;
|
||||
}
|
||||
|
||||
SafeLoadToReg(RAX, addr, single ? 32 : 64, offset, CallerSavedRegistersInUse(), false);
|
||||
SafeLoadToReg(RSCRATCH, addr, single ? 32 : 64, offset, CallerSavedRegistersInUse(), false);
|
||||
fpr.Lock(d);
|
||||
fpr.BindToRegister(d, js.memcheck || !single);
|
||||
|
||||
MEMCHECK_START
|
||||
if (single)
|
||||
{
|
||||
ConvertSingleToDouble(fpr.RX(d), EAX, true);
|
||||
ConvertSingleToDouble(fpr.RX(d), RSCRATCH, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVQ_xmm(XMM0, R(RAX));
|
||||
MOVQ_xmm(XMM0, R(RSCRATCH));
|
||||
MOVSD(fpr.RX(d), R(XMM0));
|
||||
}
|
||||
MEMCHECK_END
|
||||
@@ -96,24 +96,23 @@ void Jit64::stfXXX(UGeckoInstruction inst)
|
||||
FALLBACK_IF(!indexed && !a);
|
||||
|
||||
s32 offset = 0;
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
if (indexed)
|
||||
{
|
||||
if (update)
|
||||
{
|
||||
gpr.BindToRegister(a, true, true);
|
||||
ADD(32, gpr.R(a), gpr.R(b));
|
||||
MOV(32, R(ABI_PARAM1), gpr.R(a));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(a));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a && gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg())
|
||||
LEA(32, ABI_PARAM1, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
LEA(32, RSCRATCH2, MComplex(gpr.RX(a), gpr.RX(b), SCALE_1, 0));
|
||||
else
|
||||
{
|
||||
MOV(32, R(ABI_PARAM1), gpr.R(b));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(b));
|
||||
if (a)
|
||||
ADD(32, R(ABI_PARAM1), gpr.R(a));
|
||||
ADD(32, R(RSCRATCH2), gpr.R(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,23 +127,23 @@ void Jit64::stfXXX(UGeckoInstruction inst)
|
||||
{
|
||||
offset = (s32)(s16)inst.SIMM_16;
|
||||
}
|
||||
MOV(32, R(ABI_PARAM1), gpr.R(a));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(a));
|
||||
}
|
||||
|
||||
if (single)
|
||||
{
|
||||
fpr.BindToRegister(s, true, false);
|
||||
ConvertDoubleToSingle(XMM0, fpr.RX(s));
|
||||
SafeWriteF32ToReg(XMM0, ABI_PARAM1, offset, CallerSavedRegistersInUse());
|
||||
SafeWriteF32ToReg(XMM0, RSCRATCH2, offset, CallerSavedRegistersInUse());
|
||||
fpr.UnlockAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fpr.R(s).IsSimpleReg())
|
||||
MOVQ_xmm(R(RAX), fpr.RX(s));
|
||||
MOVQ_xmm(R(RSCRATCH), fpr.RX(s));
|
||||
else
|
||||
MOV(64, R(RAX), fpr.R(s));
|
||||
SafeWriteRegToReg(RAX, ABI_PARAM1, 64, offset, CallerSavedRegistersInUse());
|
||||
MOV(64, R(RSCRATCH), fpr.R(s));
|
||||
SafeWriteRegToReg(RSCRATCH, RSCRATCH2, 64, offset, CallerSavedRegistersInUse());
|
||||
}
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
@@ -160,15 +159,14 @@ void Jit64::stfiwx(UGeckoInstruction inst)
|
||||
int a = inst.RA;
|
||||
int b = inst.RB;
|
||||
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
MOV(32, R(ABI_PARAM1), gpr.R(b));
|
||||
MOV(32, R(RSCRATCH2), gpr.R(b));
|
||||
if (a)
|
||||
ADD(32, R(ABI_PARAM1), gpr.R(a));
|
||||
ADD(32, R(RSCRATCH2), gpr.R(a));
|
||||
|
||||
if (fpr.R(s).IsSimpleReg())
|
||||
MOVD_xmm(R(EAX), fpr.RX(s));
|
||||
MOVD_xmm(R(RSCRATCH), fpr.RX(s));
|
||||
else
|
||||
MOV(32, R(EAX), fpr.R(s));
|
||||
SafeWriteRegToReg(EAX, ABI_PARAM1, 32, 0, CallerSavedRegistersInUse());
|
||||
MOV(32, R(RSCRATCH), fpr.R(s));
|
||||
SafeWriteRegToReg(RSCRATCH, RSCRATCH2, 32, 0, CallerSavedRegistersInUse());
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
|
||||
@@ -28,37 +28,36 @@ void Jit64::psq_st(UGeckoInstruction inst)
|
||||
int a = inst.RA;
|
||||
int s = inst.RS; // Fp numbers
|
||||
|
||||
gpr.FlushLockX(EAX, EDX);
|
||||
gpr.FlushLockX(ECX);
|
||||
gpr.FlushLockX(RSCRATCH, RSCRATCH_EXTRA);
|
||||
if (update)
|
||||
gpr.BindToRegister(inst.RA, true, true);
|
||||
fpr.BindToRegister(inst.RS, true, false);
|
||||
MOV(32, R(ECX), gpr.R(inst.RA));
|
||||
MOV(32, R(RSCRATCH_EXTRA), gpr.R(inst.RA));
|
||||
if (offset)
|
||||
ADD(32, R(ECX), Imm32((u32)offset));
|
||||
ADD(32, R(RSCRATCH_EXTRA), Imm32((u32)offset));
|
||||
if (update && offset)
|
||||
MOV(32, gpr.R(a), R(ECX));
|
||||
MOV(32, gpr.R(a), R(RSCRATCH_EXTRA));
|
||||
// Some games (e.g. Dirt 2) incorrectly set the unused bits which breaks the lookup table code.
|
||||
// Hence, we need to mask out the unused bits. The layout of the GQR register is
|
||||
// UU[SCALE]UUUUU[TYPE] where SCALE is 6 bits and TYPE is 3 bits, so we have to AND with
|
||||
// 0b0011111100000111, or 0x3F07.
|
||||
MOV(32, R(EAX), Imm32(0x3F07));
|
||||
AND(32, R(EAX), M(&PowerPC::ppcState.spr[SPR_GQR0 + inst.I]));
|
||||
MOVZX(32, 8, EDX, R(AL));
|
||||
MOV(32, R(RSCRATCH), Imm32(0x3F07));
|
||||
AND(32, R(RSCRATCH), PPCSTATE(spr[SPR_GQR0 + inst.I]));
|
||||
MOVZX(32, 8, RSCRATCH2, R(RSCRATCH));
|
||||
|
||||
// FIXME: Fix ModR/M encoding to allow [EDX*4+disp32] without a base register!
|
||||
// FIXME: Fix ModR/M encoding to allow [RSCRATCH2*4+disp32] without a base register!
|
||||
if (inst.W)
|
||||
{
|
||||
// One value
|
||||
PXOR(XMM0, R(XMM0)); // TODO: See if we can get rid of this cheaply by tweaking the code in the singleStore* functions.
|
||||
CVTSD2SS(XMM0, fpr.R(s));
|
||||
CALLptr(MScaled(EDX, SCALE_8, (u32)(u64)asm_routines.singleStoreQuantized));
|
||||
CALLptr(MScaled(RSCRATCH2, SCALE_8, (u32)(u64)asm_routines.singleStoreQuantized));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pair of values
|
||||
CVTPD2PS(XMM0, fpr.R(s));
|
||||
CALLptr(MScaled(EDX, SCALE_8, (u32)(u64)asm_routines.pairedStoreQuantized));
|
||||
CALLptr(MScaled(RSCRATCH2, SCALE_8, (u32)(u64)asm_routines.pairedStoreQuantized));
|
||||
}
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
@@ -73,24 +72,23 @@ void Jit64::psq_l(UGeckoInstruction inst)
|
||||
bool update = inst.OPCD == 57;
|
||||
int offset = inst.SIMM_12;
|
||||
|
||||
gpr.FlushLockX(EAX, EDX);
|
||||
gpr.FlushLockX(ECX);
|
||||
gpr.FlushLockX(RSCRATCH, RSCRATCH_EXTRA);
|
||||
gpr.BindToRegister(inst.RA, true, update && offset);
|
||||
fpr.BindToRegister(inst.RS, false, true);
|
||||
if (offset)
|
||||
LEA(32, ECX, MDisp(gpr.RX(inst.RA), offset));
|
||||
LEA(32, RSCRATCH_EXTRA, MDisp(gpr.RX(inst.RA), offset));
|
||||
else
|
||||
MOV(32, R(ECX), gpr.R(inst.RA));
|
||||
MOV(32, R(RSCRATCH_EXTRA), gpr.R(inst.RA));
|
||||
if (update && offset)
|
||||
MOV(32, gpr.R(inst.RA), R(ECX));
|
||||
MOV(32, R(EAX), Imm32(0x3F07));
|
||||
AND(32, R(EAX), M(((char *)&GQR(inst.I)) + 2));
|
||||
MOVZX(32, 8, EDX, R(AL));
|
||||
MOV(32, gpr.R(inst.RA), R(RSCRATCH_EXTRA));
|
||||
MOV(32, R(RSCRATCH), Imm32(0x3F07));
|
||||
AND(32, R(RSCRATCH), M(((char *)&GQR(inst.I)) + 2));
|
||||
MOVZX(32, 8, RSCRATCH2, R(RSCRATCH));
|
||||
if (inst.W)
|
||||
OR(32, R(EDX), Imm8(8));
|
||||
OR(32, R(RSCRATCH2), Imm8(8));
|
||||
|
||||
ABI_AlignStack(0);
|
||||
CALLptr(MScaled(EDX, SCALE_8, (u32)(u64)asm_routines.pairedLoadQuantized));
|
||||
CALLptr(MScaled(RSCRATCH2, SCALE_8, (u32)(u64)asm_routines.pairedLoadQuantized));
|
||||
ABI_RestoreStack(0);
|
||||
|
||||
// MEMCHECK_START // FIXME: MMU does not work here because of unsafe memory access
|
||||
|
||||
@@ -16,22 +16,22 @@ void Jit64::GetCRFieldBit(int field, int bit, Gen::X64Reg out, bool negate)
|
||||
switch (bit)
|
||||
{
|
||||
case CR_SO_BIT: // check bit 61 set
|
||||
BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(61));
|
||||
BT(64, PPCSTATE(cr_val[field]), Imm8(61));
|
||||
SETcc(negate ? CC_NC : CC_C, R(out));
|
||||
break;
|
||||
|
||||
case CR_EQ_BIT: // check bits 31-0 == 0
|
||||
CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
|
||||
CMP(32, PPCSTATE(cr_val[field]), Imm8(0));
|
||||
SETcc(negate ? CC_NZ : CC_Z, R(out));
|
||||
break;
|
||||
|
||||
case CR_GT_BIT: // check val > 0
|
||||
CMP(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
|
||||
CMP(64, PPCSTATE(cr_val[field]), Imm8(0));
|
||||
SETcc(negate ? CC_NG : CC_G, R(out));
|
||||
break;
|
||||
|
||||
case CR_LT_BIT: // check bit 62 set
|
||||
BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(62));
|
||||
BT(64, PPCSTATE(cr_val[field]), Imm8(62));
|
||||
SETcc(negate ? CC_NC : CC_C, R(out));
|
||||
break;
|
||||
|
||||
@@ -42,40 +42,40 @@ void Jit64::GetCRFieldBit(int field, int bit, Gen::X64Reg out, bool negate)
|
||||
|
||||
void Jit64::SetCRFieldBit(int field, int bit, Gen::X64Reg in)
|
||||
{
|
||||
MOV(64, R(ABI_PARAM1), M(&PowerPC::ppcState.cr_val[field]));
|
||||
MOV(64, R(RSCRATCH2), PPCSTATE(cr_val[field]));
|
||||
MOVZX(32, 8, in, R(in));
|
||||
|
||||
switch (bit)
|
||||
{
|
||||
case CR_SO_BIT: // set bit 61 to input
|
||||
BTR(64, R(ABI_PARAM1), Imm8(61));
|
||||
BTR(64, R(RSCRATCH2), Imm8(61));
|
||||
SHL(64, R(in), Imm8(61));
|
||||
OR(64, R(ABI_PARAM1), R(in));
|
||||
OR(64, R(RSCRATCH2), R(in));
|
||||
break;
|
||||
|
||||
case CR_EQ_BIT: // clear low 32 bits, set bit 0 to !input
|
||||
SHR(64, R(ABI_PARAM1), Imm8(32));
|
||||
SHL(64, R(ABI_PARAM1), Imm8(32));
|
||||
SHR(64, R(RSCRATCH2), Imm8(32));
|
||||
SHL(64, R(RSCRATCH2), Imm8(32));
|
||||
XOR(32, R(in), Imm8(1));
|
||||
OR(64, R(ABI_PARAM1), R(in));
|
||||
OR(64, R(RSCRATCH2), R(in));
|
||||
break;
|
||||
|
||||
case CR_GT_BIT: // set bit 63 to !input
|
||||
BTR(64, R(ABI_PARAM1), Imm8(63));
|
||||
BTR(64, R(RSCRATCH2), Imm8(63));
|
||||
NOT(32, R(in));
|
||||
SHL(64, R(in), Imm8(63));
|
||||
OR(64, R(ABI_PARAM1), R(in));
|
||||
OR(64, R(RSCRATCH2), R(in));
|
||||
break;
|
||||
|
||||
case CR_LT_BIT: // set bit 62 to input
|
||||
BTR(64, R(ABI_PARAM1), Imm8(62));
|
||||
BTR(64, R(RSCRATCH2), Imm8(62));
|
||||
SHL(64, R(in), Imm8(62));
|
||||
OR(64, R(ABI_PARAM1), R(in));
|
||||
OR(64, R(RSCRATCH2), R(in));
|
||||
break;
|
||||
}
|
||||
|
||||
BTS(64, R(ABI_PARAM1), Imm8(32));
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[field]), R(ABI_PARAM1));
|
||||
BTS(64, R(RSCRATCH2), Imm8(32));
|
||||
MOV(64, PPCSTATE(cr_val[field]), R(RSCRATCH2));
|
||||
}
|
||||
|
||||
FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
|
||||
@@ -83,19 +83,19 @@ FixupBranch Jit64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set)
|
||||
switch (bit)
|
||||
{
|
||||
case CR_SO_BIT: // check bit 61 set
|
||||
BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(61));
|
||||
BT(64, PPCSTATE(cr_val[field]), Imm8(61));
|
||||
return J_CC(jump_if_set ? CC_C : CC_NC, true);
|
||||
|
||||
case CR_EQ_BIT: // check bits 31-0 == 0
|
||||
CMP(32, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
|
||||
CMP(32, PPCSTATE(cr_val[field]), Imm8(0));
|
||||
return J_CC(jump_if_set ? CC_Z : CC_NZ, true);
|
||||
|
||||
case CR_GT_BIT: // check val > 0
|
||||
CMP(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(0));
|
||||
CMP(64, PPCSTATE(cr_val[field]), Imm8(0));
|
||||
return J_CC(jump_if_set ? CC_G : CC_LE, true);
|
||||
|
||||
case CR_LT_BIT: // check bit 62 set
|
||||
BT(64, M(&PowerPC::ppcState.cr_val[field]), Imm8(62));
|
||||
BT(64, PPCSTATE(cr_val[field]), Imm8(62));
|
||||
return J_CC(jump_if_set ? CC_C : CC_NC, true);
|
||||
|
||||
default:
|
||||
@@ -154,7 +154,7 @@ void Jit64::mtspr(UGeckoInstruction inst)
|
||||
gpr.Lock(d);
|
||||
gpr.BindToRegister(d, true, false);
|
||||
}
|
||||
MOV(32, M(&PowerPC::ppcState.spr[iIndex]), gpr.R(d));
|
||||
MOV(32, PPCSTATE(spr[iIndex]), gpr.R(d));
|
||||
gpr.UnlockAll();
|
||||
}
|
||||
|
||||
@@ -173,8 +173,10 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||
// typical use of this instruction is to call it three times, e.g. mftbu/mftbl/mftbu/cmpw/bne
|
||||
// to deal with possible timer wraparound. This makes the second two (out of three) completely
|
||||
// redundant for the JIT.
|
||||
// no register choice
|
||||
|
||||
gpr.FlushLockX(RDX, RAX);
|
||||
u32 offset = js.downcountAmount / SystemTimers::TIMER_RATIO;
|
||||
gpr.FlushLockX(EDX);
|
||||
|
||||
// An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the
|
||||
// cost of calling out to C for this is actually significant.
|
||||
@@ -190,7 +192,7 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||
LEA(64, RAX, MComplex(RAX, RDX, SCALE_1, offset));
|
||||
else
|
||||
ADD(64, R(RAX), R(RDX));
|
||||
MOV(64, M(&TL), R(RAX));
|
||||
MOV(64, PPCSTATE(spr[SPR_TL]), R(RAX));
|
||||
|
||||
// Two calls of TU/TL next to each other are extremely common in typical usage, so merge them
|
||||
// if we can.
|
||||
@@ -205,14 +207,14 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||
gpr.BindToRegister(d, false);
|
||||
gpr.BindToRegister(n, false);
|
||||
if (iIndex == SPR_TL)
|
||||
MOV(32, gpr.R(d), R(EAX));
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TL)
|
||||
MOV(32, gpr.R(n), R(EAX));
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
SHR(64, R(RAX), Imm8(32));
|
||||
if (iIndex == SPR_TU)
|
||||
MOV(32, gpr.R(d), R(EAX));
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
if (nextIndex == SPR_TU)
|
||||
MOV(32, gpr.R(n), R(EAX));
|
||||
MOV(32, gpr.R(n), R(RAX));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -220,8 +222,9 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||
gpr.BindToRegister(d, false);
|
||||
if (iIndex == SPR_TU)
|
||||
SHR(64, R(RAX), Imm8(32));
|
||||
MOV(32, gpr.R(d), R(EAX));
|
||||
MOV(32, gpr.R(d), R(RAX));
|
||||
}
|
||||
gpr.UnlockAllX();
|
||||
break;
|
||||
}
|
||||
case SPR_WPAR:
|
||||
@@ -234,11 +237,10 @@ void Jit64::mfspr(UGeckoInstruction inst)
|
||||
default:
|
||||
gpr.Lock(d);
|
||||
gpr.BindToRegister(d, false);
|
||||
MOV(32, gpr.R(d), M(&PowerPC::ppcState.spr[iIndex]));
|
||||
MOV(32, gpr.R(d), PPCSTATE(spr[iIndex]));
|
||||
break;
|
||||
}
|
||||
gpr.UnlockAll();
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
|
||||
void Jit64::mtmsr(UGeckoInstruction inst)
|
||||
@@ -251,7 +253,7 @@ void Jit64::mtmsr(UGeckoInstruction inst)
|
||||
gpr.Lock(inst.RS);
|
||||
gpr.BindToRegister(inst.RS, true, false);
|
||||
}
|
||||
MOV(32, M(&MSR), gpr.R(inst.RS));
|
||||
MOV(32, PPCSTATE(msr), gpr.R(inst.RS));
|
||||
gpr.UnlockAll();
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
@@ -259,17 +261,17 @@ void Jit64::mtmsr(UGeckoInstruction inst)
|
||||
// If some exceptions are pending and EE are now enabled, force checking
|
||||
// external exceptions when going out of mtmsr in order to execute delayed
|
||||
// interrupts as soon as possible.
|
||||
TEST(32, M(&MSR), Imm32(0x8000));
|
||||
TEST(32, PPCSTATE(msr), Imm32(0x8000));
|
||||
FixupBranch eeDisabled = J_CC(CC_Z);
|
||||
|
||||
TEST(32, M((void*)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
|
||||
TEST(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_EXTERNAL_INT | EXCEPTION_PERFORMANCE_MONITOR | EXCEPTION_DECREMENTER));
|
||||
FixupBranch noExceptionsPending = J_CC(CC_Z);
|
||||
|
||||
// Check if a CP interrupt is waiting and keep the GPU emulation in sync (issue 4336)
|
||||
TEST(32, M((void *)&ProcessorInterface::m_InterruptCause), Imm32(ProcessorInterface::INT_CAUSE_CP));
|
||||
FixupBranch cpInt = J_CC(CC_NZ);
|
||||
|
||||
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC + 4));
|
||||
WriteExternalExceptionExit();
|
||||
|
||||
SetJumpTarget(cpInt);
|
||||
@@ -288,7 +290,7 @@ void Jit64::mfmsr(UGeckoInstruction inst)
|
||||
//Privileged?
|
||||
gpr.Lock(inst.RD);
|
||||
gpr.BindToRegister(inst.RD, false, true);
|
||||
MOV(32, gpr.R(inst.RD), M(&MSR));
|
||||
MOV(32, gpr.R(inst.RD), PPCSTATE(msr));
|
||||
gpr.UnlockAll();
|
||||
}
|
||||
|
||||
@@ -308,33 +310,32 @@ void Jit64::mfcr(UGeckoInstruction inst)
|
||||
gpr.BindToRegister(d, false, true);
|
||||
XOR(32, gpr.R(d), gpr.R(d));
|
||||
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
X64Reg cr_val = ABI_PARAM1;
|
||||
// we only need to zero the high bits of EAX once
|
||||
XOR(32, R(EAX), R(EAX));
|
||||
X64Reg cr_val = RSCRATCH2;
|
||||
// we only need to zero the high bits of RSCRATCH once
|
||||
XOR(32, R(RSCRATCH), R(RSCRATCH));
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
static const u8 m_flagTable[8] = {0x0,0x1,0x8,0x9,0x0,0x1,0x8,0x9};
|
||||
if (i != 0)
|
||||
SHL(32, gpr.R(d), Imm8(4));
|
||||
|
||||
MOV(64, R(cr_val), M(&PowerPC::ppcState.cr_val[i]));
|
||||
MOV(64, R(cr_val), PPCSTATE(cr_val[i]));
|
||||
|
||||
// EQ: Bits 31-0 == 0; set flag bit 1
|
||||
TEST(32, R(cr_val), R(cr_val));
|
||||
SETcc(CC_Z, R(EAX));
|
||||
LEA(32, gpr.RX(d), MComplex(gpr.RX(d), EAX, SCALE_2, 0));
|
||||
SETcc(CC_Z, R(RSCRATCH));
|
||||
LEA(32, gpr.RX(d), MComplex(gpr.RX(d), RSCRATCH, SCALE_2, 0));
|
||||
|
||||
// GT: Value > 0; set flag bit 2
|
||||
TEST(64, R(cr_val), R(cr_val));
|
||||
SETcc(CC_G, R(EAX));
|
||||
LEA(32, gpr.RX(d), MComplex(gpr.RX(d), EAX, SCALE_4, 0));
|
||||
SETcc(CC_G, R(RSCRATCH));
|
||||
LEA(32, gpr.RX(d), MComplex(gpr.RX(d), RSCRATCH, SCALE_4, 0));
|
||||
|
||||
// SO: Bit 61 set; set flag bit 0
|
||||
// LT: Bit 62 set; set flag bit 3
|
||||
SHR(64, R(cr_val), Imm8(61));
|
||||
MOVZX(32, 8, EAX, MDisp(cr_val, (u32)(u64)m_flagTable));
|
||||
OR(32, gpr.R(d), R(EAX));
|
||||
MOVZX(32, 8, RSCRATCH, MDisp(cr_val, (u32)(u64)m_flagTable));
|
||||
OR(32, gpr.R(d), R(RSCRATCH));
|
||||
}
|
||||
|
||||
gpr.UnlockAll();
|
||||
@@ -360,12 +361,12 @@ void Jit64::mtcrf(UGeckoInstruction inst)
|
||||
u64 newcrval = PPCCRToInternal(newcr);
|
||||
if ((s64)newcrval == (s32)newcrval)
|
||||
{
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[i]), Imm32((s32)newcrval));
|
||||
MOV(64, PPCSTATE(cr_val[i]), Imm32((s32)newcrval));
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(64, R(RAX), Imm64(newcrval));
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(RAX));
|
||||
MOV(64, R(RSCRATCH), Imm64(newcrval));
|
||||
MOV(64, PPCSTATE(cr_val[i]), R(RSCRATCH));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,13 +379,13 @@ void Jit64::mtcrf(UGeckoInstruction inst)
|
||||
{
|
||||
if ((crm & (0x80 >> i)) != 0)
|
||||
{
|
||||
MOV(32, R(EAX), gpr.R(inst.RS));
|
||||
MOV(32, R(RSCRATCH), gpr.R(inst.RS));
|
||||
if (i != 7)
|
||||
SHR(32, R(EAX), Imm8(28 - (i * 4)));
|
||||
SHR(32, R(RSCRATCH), Imm8(28 - (i * 4)));
|
||||
if (i != 0)
|
||||
AND(32, R(EAX), Imm8(0xF));
|
||||
MOV(64, R(EAX), MScaled(EAX, SCALE_8, (u32)(u64)m_crTable));
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[i]), R(EAX));
|
||||
AND(32, R(RSCRATCH), Imm8(0xF));
|
||||
MOV(64, R(RSCRATCH), MScaled(RSCRATCH, SCALE_8, (u32)(u64)m_crTable));
|
||||
MOV(64, PPCSTATE(cr_val[i]), R(RSCRATCH));
|
||||
}
|
||||
}
|
||||
gpr.UnlockAll();
|
||||
@@ -400,8 +401,8 @@ void Jit64::mcrf(UGeckoInstruction inst)
|
||||
// USES_CR
|
||||
if (inst.CRFS != inst.CRFD)
|
||||
{
|
||||
MOV(64, R(EAX), M(&PowerPC::ppcState.cr_val[inst.CRFS]));
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[inst.CRFD]), R(EAX));
|
||||
MOV(64, R(RSCRATCH), PPCSTATE(cr_val[inst.CRFS]));
|
||||
MOV(64, PPCSTATE(cr_val[inst.CRFD]), R(RSCRATCH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,14 +414,14 @@ void Jit64::mcrxr(UGeckoInstruction inst)
|
||||
// USES_CR
|
||||
|
||||
// Copy XER[0-3] into CR[inst.CRFD]
|
||||
MOV(32, R(EAX), M(&PowerPC::ppcState.spr[SPR_XER]));
|
||||
SHR(32, R(EAX), Imm8(28));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(spr[SPR_XER]));
|
||||
SHR(32, R(RSCRATCH), Imm8(28));
|
||||
|
||||
MOV(64, R(EAX), MScaled(EAX, SCALE_8, (u32)(u64)m_crTable));
|
||||
MOV(64, M(&PowerPC::ppcState.cr_val[inst.CRFD]), R(EAX));
|
||||
MOV(64, R(RSCRATCH), MScaled(RSCRATCH, SCALE_8, (u32)(u64)m_crTable));
|
||||
MOV(64, PPCSTATE(cr_val[inst.CRFD]), R(RSCRATCH));
|
||||
|
||||
// Clear XER[0-3]
|
||||
AND(32, M(&PowerPC::ppcState.spr[SPR_XER]), Imm32(0x0FFFFFFF));
|
||||
AND(32, PPCSTATE(spr[SPR_XER]), Imm32(0x0FFFFFFF));
|
||||
}
|
||||
|
||||
void Jit64::crXXX(UGeckoInstruction inst)
|
||||
@@ -439,9 +440,8 @@ void Jit64::crXXX(UGeckoInstruction inst)
|
||||
// crnand or crnor
|
||||
bool negateB = inst.SUBOP10 == 225 || inst.SUBOP10 == 33;
|
||||
|
||||
gpr.FlushLockX(ABI_PARAM1);
|
||||
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), ABI_PARAM1, negateA);
|
||||
GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), EAX, negateB);
|
||||
GetCRFieldBit(inst.CRBA >> 2, 3 - (inst.CRBA & 3), RSCRATCH2, negateA);
|
||||
GetCRFieldBit(inst.CRBB >> 2, 3 - (inst.CRBB & 3), RSCRATCH, negateB);
|
||||
|
||||
// Compute combined bit
|
||||
switch (inst.SUBOP10)
|
||||
@@ -449,23 +449,23 @@ void Jit64::crXXX(UGeckoInstruction inst)
|
||||
case 33: // crnor: ~(A || B) == (~A && ~B)
|
||||
case 129: // crandc
|
||||
case 257: // crand
|
||||
AND(8, R(EAX), R(ABI_PARAM1));
|
||||
AND(8, R(RSCRATCH), R(RSCRATCH2));
|
||||
break;
|
||||
|
||||
case 193: // crxor
|
||||
case 289: // creqv
|
||||
XOR(8, R(EAX), R(ABI_PARAM1));
|
||||
XOR(8, R(RSCRATCH), R(RSCRATCH2));
|
||||
break;
|
||||
|
||||
case 225: // crnand: ~(A && B) == (~A || ~B)
|
||||
case 417: // crorc
|
||||
case 449: // cror
|
||||
OR(8, R(EAX), R(ABI_PARAM1));
|
||||
OR(8, R(RSCRATCH), R(RSCRATCH2));
|
||||
break;
|
||||
}
|
||||
|
||||
// Store result bit in CRBD
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), EAX);
|
||||
SetCRFieldBit(inst.CRBD >> 2, 3 - (inst.CRBD & 3), RSCRATCH);
|
||||
|
||||
gpr.UnlockAllX();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -313,15 +313,15 @@ void JitIL::WriteCallInterpreter(UGeckoInstruction inst)
|
||||
{
|
||||
if (js.isLastInstruction)
|
||||
{
|
||||
MOV(32, M(&PC), Imm32(js.compilerPC));
|
||||
MOV(32, M(&NPC), Imm32(js.compilerPC + 4));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.compilerPC));
|
||||
MOV(32, PPCSTATE(npc), Imm32(js.compilerPC + 4));
|
||||
}
|
||||
Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst);
|
||||
ABI_CallFunctionC((void*)instr, inst.hex);
|
||||
if (js.isLastInstruction)
|
||||
{
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
WriteRfiExitDestInOpArg(R(EAX));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(npc));
|
||||
WriteRfiExitDestInOpArg(R(RSCRATCH));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,8 +341,8 @@ void JitIL::FallBackToInterpreter(UGeckoInstruction _inst)
|
||||
void JitIL::HLEFunction(UGeckoInstruction _inst)
|
||||
{
|
||||
ABI_CallFunctionCC((void*)&HLE::Execute, js.compilerPC, _inst.hex);
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
WriteExitDestInOpArg(R(EAX));
|
||||
MOV(32, R(RSCRATCH), PPCSTATE(npc));
|
||||
WriteExitDestInOpArg(R(RSCRATCH));
|
||||
}
|
||||
|
||||
void JitIL::DoNothing(UGeckoInstruction _inst)
|
||||
@@ -398,7 +398,7 @@ void JitIL::WriteExit(u32 destination)
|
||||
{
|
||||
ABI_CallFunction((void *)JitILProfiler::End);
|
||||
}
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
|
||||
//If nobody has taken care of this yet (this can be removed when all branches are done)
|
||||
JitBlock *b = js.curBlock;
|
||||
@@ -417,7 +417,7 @@ void JitIL::WriteExit(u32 destination)
|
||||
}
|
||||
else
|
||||
{
|
||||
MOV(32, M(&PC), Imm32(destination));
|
||||
MOV(32, PPCSTATE(pc), Imm32(destination));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
b->linkData.push_back(linkData);
|
||||
@@ -425,27 +425,27 @@ void JitIL::WriteExit(u32 destination)
|
||||
|
||||
void JitIL::WriteExitDestInOpArg(const Gen::OpArg& arg)
|
||||
{
|
||||
MOV(32, M(&PC), arg);
|
||||
MOV(32, PPCSTATE(pc), arg);
|
||||
Cleanup();
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bJITILTimeProfiling)
|
||||
{
|
||||
ABI_CallFunction((void *)JitILProfiler::End);
|
||||
}
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
void JitIL::WriteRfiExitDestInOpArg(const Gen::OpArg& arg)
|
||||
{
|
||||
MOV(32, M(&PC), arg);
|
||||
MOV(32, M(&NPC), arg);
|
||||
MOV(32, PPCSTATE(pc), arg);
|
||||
MOV(32, PPCSTATE(npc), arg);
|
||||
Cleanup();
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bJITILTimeProfiling)
|
||||
{
|
||||
ABI_CallFunction((void *)JitILProfiler::End);
|
||||
}
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
@@ -456,10 +456,10 @@ void JitIL::WriteExceptionExit()
|
||||
{
|
||||
ABI_CallFunction((void *)JitILProfiler::End);
|
||||
}
|
||||
MOV(32, R(EAX), M(&PC));
|
||||
MOV(32, M(&NPC), R(EAX));
|
||||
MOV(32, R(EAX), PPCSTATE(pc));
|
||||
MOV(32, PPCSTATE(npc), R(EAX));
|
||||
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
|
||||
SUB(32, M(&PowerPC::ppcState.downcount), Imm32(js.downcountAmount));
|
||||
SUB(32, PPCSTATE(downcount), Imm32(js.downcountAmount));
|
||||
JMP(asm_routines.dispatcher, true);
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
|
||||
// Downcount flag check. The last block decremented downcounter, and the flag should still be available.
|
||||
FixupBranch skip = J_CC(CC_NBE);
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
||||
JMP(asm_routines.doTiming, true); // downcount hit zero - go doTiming.
|
||||
SetJumpTarget(skip);
|
||||
|
||||
@@ -561,13 +561,13 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
if (js.fpa.any)
|
||||
{
|
||||
// This block uses FPU - needs to add FP exception bailout
|
||||
TEST(32, M(&PowerPC::ppcState.msr), Imm32(1 << 13)); //Test FP enabled bit
|
||||
TEST(32, PPCSTATE(msr), Imm32(1 << 13)); //Test FP enabled bit
|
||||
FixupBranch b1 = J_CC(CC_NZ);
|
||||
|
||||
// If a FPU exception occurs, the exception handler will read
|
||||
// from PC. Update PC with the latest value in case that happens.
|
||||
MOV(32, M(&PC), Imm32(js.blockStart));
|
||||
OR(32, M((void *)&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
|
||||
MOV(32, PPCSTATE(pc), Imm32(js.blockStart));
|
||||
OR(32, PPCSTATE(Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
|
||||
WriteExceptionExit();
|
||||
|
||||
SetJumpTarget(b1);
|
||||
@@ -635,7 +635,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
||||
HLEFunction(function);
|
||||
if (type == HLE::HLE_HOOK_REPLACE)
|
||||
{
|
||||
MOV(32, R(EAX), M(&NPC));
|
||||
MOV(32, R(EAX), PPCSTATE(npc));
|
||||
jit->js.downcountAmount += jit->js.st.numCycles;
|
||||
WriteExitDestInOpArg(R(EAX));
|
||||
break;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,9 @@ public:
|
||||
|
||||
const u8 *dispatcher;
|
||||
const u8 *dispatcherNoCheck;
|
||||
const u8 *dispatcherPcInEAX;
|
||||
const u8 *dispatcherPcInRSCRATCH;
|
||||
|
||||
const u8 *dispatchPcInEAX;
|
||||
const u8 *dispatchPcInRSCRATCH;
|
||||
const u8 *doTiming;
|
||||
|
||||
const u8 *frsqrte;
|
||||
@@ -31,14 +31,14 @@ public:
|
||||
// In: ECX: Address to read from.
|
||||
// Out: XMM0: Bottom two 32-bit slots hold the read value,
|
||||
// converted to a pair of floats.
|
||||
// Trashes: EAX ECX EDX
|
||||
// Trashes: all three RSCRATCH
|
||||
const u8 **pairedLoadQuantized;
|
||||
|
||||
// In: array index: GQR to use.
|
||||
// In: ECX: Address to write to.
|
||||
// In: XMM0: Bottom two 32-bit slots hold the pair of floats to be written.
|
||||
// Out: Nothing.
|
||||
// Trashes: EAX ECX EDX
|
||||
// Trashes: all three RSCRATCH
|
||||
const u8 **pairedStoreQuantized;
|
||||
|
||||
// In: array index: GQR to use.
|
||||
|
||||
@@ -59,6 +59,7 @@ const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 re
|
||||
// It ought to be necessary to align the stack here. Since it seems to not
|
||||
// affect anybody, I'm not going to add it just to be completely safe about
|
||||
// performance.
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, true);
|
||||
|
||||
if (addrReg != ABI_PARAM1)
|
||||
MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg));
|
||||
@@ -66,7 +67,6 @@ const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 re
|
||||
if (info.displacement)
|
||||
ADD(32, R(ABI_PARAM1), Imm32(info.displacement));
|
||||
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, true);
|
||||
switch (info.operandSize)
|
||||
{
|
||||
case 4:
|
||||
@@ -74,7 +74,7 @@ const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 re
|
||||
break;
|
||||
case 2:
|
||||
CALL((void *)&Memory::Read_U16);
|
||||
SHL(32, R(EAX), Imm8(16));
|
||||
SHL(32, R(ABI_RETURN), Imm8(16));
|
||||
break;
|
||||
case 1:
|
||||
CALL((void *)&Memory::Read_U8);
|
||||
@@ -84,11 +84,11 @@ const u8 *TrampolineCache::GetReadTrampoline(const InstructionInfo &info, u32 re
|
||||
if (info.signExtend && info.operandSize == 1)
|
||||
{
|
||||
// Need to sign extend value from Read_U8.
|
||||
MOVSX(32, 8, dataReg, R(EAX));
|
||||
MOVSX(32, 8, dataReg, R(ABI_RETURN));
|
||||
}
|
||||
else if (dataReg != EAX)
|
||||
{
|
||||
MOV(32, R(dataReg), R(EAX));
|
||||
MOV(32, R(dataReg), R(ABI_RETURN));
|
||||
}
|
||||
|
||||
ABI_PopRegistersAndAdjustStack(registersInUse, true);
|
||||
@@ -113,31 +113,17 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info, u32 r
|
||||
// check anyway.
|
||||
|
||||
// PC is used by memory watchpoints (if enabled) or to print accurate PC locations in debug logs
|
||||
MOV(32, M(&PC), Imm32(pc));
|
||||
MOV(32, PPCSTATE(pc), Imm32(pc));
|
||||
|
||||
if (dataReg == ABI_PARAM2)
|
||||
PanicAlert("Incorrect use of SafeWriteRegToReg");
|
||||
if (addrReg != ABI_PARAM1)
|
||||
{
|
||||
if (ABI_PARAM1 != dataReg)
|
||||
MOV(64, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
if (ABI_PARAM2 != addrReg)
|
||||
MOV(64, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ABI_PARAM2 != addrReg)
|
||||
MOV(64, R(ABI_PARAM2), R((X64Reg)addrReg));
|
||||
if (ABI_PARAM1 != dataReg)
|
||||
MOV(64, R(ABI_PARAM1), R((X64Reg)dataReg));
|
||||
}
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, true);
|
||||
|
||||
MOVTwo(64, ABI_PARAM1, dataReg, ABI_PARAM2, addrReg, ABI_PARAM3);
|
||||
|
||||
if (info.displacement)
|
||||
{
|
||||
ADD(32, R(ABI_PARAM2), Imm32(info.displacement));
|
||||
}
|
||||
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, true);
|
||||
switch (info.operandSize)
|
||||
{
|
||||
case 8:
|
||||
@@ -180,9 +166,9 @@ const u8 *Jitx86Base::BackPatch(u8 *codePtr, u32 emAddress, void *ctx_void)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (info.otherReg != RBX)
|
||||
if (info.otherReg != RMEM)
|
||||
{
|
||||
PanicAlert("BackPatch : Base reg not RBX."
|
||||
PanicAlert("BackPatch : Base reg not RMEM."
|
||||
"\n\nAttempted to access %08x.", emAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,23 @@
|
||||
#include "Core/PowerPC/JitCommon/JitBackpatch.h"
|
||||
#include "Core/PowerPC/JitCommon/JitCache.h"
|
||||
|
||||
// TODO: find a better place for x86-specific stuff
|
||||
// The following register assignments are common to Jit64 and Jit64IL:
|
||||
// RSCRATCH and RSCRATCH2 are always scratch registers and can be used without
|
||||
// limitation.
|
||||
#define RSCRATCH RAX
|
||||
#define RSCRATCH2 RDX
|
||||
// RSCRATCH_EXTRA may be in the allocation order, so it has to be flushed
|
||||
// before use.
|
||||
#define RSCRATCH_EXTRA RCX
|
||||
// RMEM points to the start of emulated memory.
|
||||
#define RMEM RBX
|
||||
// RCODE_POINTERS does what it says.
|
||||
#define RCODE_POINTERS R15
|
||||
// RPPCSTATE points to ppcState + 0x80. It's offset because we want to be able
|
||||
// to address as much as possible in a one-byte offset form.
|
||||
#define RPPCSTATE RBP
|
||||
|
||||
// Use these to control the instruction selection
|
||||
// #define INSTRUCTION_START FallBackToInterpreter(inst); return;
|
||||
// #define INSTRUCTION_START PPCTables::CountInstruction(inst);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user