From dc91dc1ce83c9be8224a2502107dadf72a44b2b9 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 21 Aug 2014 23:37:04 -0700 Subject: [PATCH] x86jit: Support fpu rounding modes for mul, etc. Fixes Gods Eater Burst loading PSP savedata, but can no longer load old savedata. --- Core/Config.cpp | 1 + Core/Config.h | 1 + Core/MIPS/JitCommon/JitState.h | 5 +++- Core/MIPS/x86/CompBranch.cpp | 1 + Core/MIPS/x86/CompFPU.cpp | 4 +++ Core/MIPS/x86/Jit.cpp | 47 ++++++++++++++++++++++++++++++++++ Core/MIPS/x86/Jit.h | 2 ++ headless/Headless.cpp | 1 + 8 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 73135f1a82..7782dfadac 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -324,6 +324,7 @@ static ConfigSetting cpuSettings[] = { ConfigSetting("FastMemoryAccess", &g_Config.bFastMemory, true), ReportedConfigSetting("FuncReplacements", &g_Config.bFuncReplacements, true), ReportedConfigSetting("CPUSpeed", &g_Config.iLockedCPUSpeed, 0), + ReportedConfigSetting("SetRoundingMode", &g_Config.bSetRoundingMode, true), ConfigSetting(false), }; diff --git a/Core/Config.h b/Core/Config.h index 9a87a7c952..3afc93ff13 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -86,6 +86,7 @@ public: bool bCheckForNewVersion; bool bForceLagSync; bool bFuncReplacements; + bool bSetRoundingMode; // Definitely cannot be changed while game is running. bool bSeparateCPUThread; diff --git a/Core/MIPS/JitCommon/JitState.h b/Core/MIPS/JitCommon/JitState.h index ab50648775..d51450addb 100644 --- a/Core/MIPS/JitCommon/JitState.h +++ b/Core/MIPS/JitCommon/JitState.h @@ -58,7 +58,8 @@ namespace MIPSComp { : startDefaultPrefix(true), prefixSFlag(PREFIX_UNKNOWN), prefixTFlag(PREFIX_UNKNOWN), - prefixDFlag(PREFIX_UNKNOWN) {} + prefixDFlag(PREFIX_UNKNOWN), + roundingModeSet(false) {} u32 compilerPC; u32 blockStart; @@ -81,6 +82,8 @@ namespace MIPSComp { PrefixState prefixTFlag; PrefixState prefixDFlag; + bool roundingModeSet; + void PrefixStart() { if (startDefaultPrefix) { EatPrefix(); diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 2a2db6a442..24fcd3dcfe 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -681,6 +681,7 @@ void Jit::Comp_Syscall(MIPSOpcode op) // If we're in a delay slot, this is off by one. const int offset = js.inDelaySlot ? -1 : 0; WriteDowncount(offset); + ClearRoundingMode(); js.downcountAmount = -offset; // Skip the CallSyscall where possible. diff --git a/Core/MIPS/x86/CompFPU.cpp b/Core/MIPS/x86/CompFPU.cpp index 54bff6728a..e9dc0dec9a 100644 --- a/Core/MIPS/x86/CompFPU.cpp +++ b/Core/MIPS/x86/CompFPU.cpp @@ -79,6 +79,7 @@ void Jit::CompFPTriArith(MIPSOpcode op, void (XEmitter::*arith)(X64Reg reg, OpAr void Jit::Comp_FPU3op(MIPSOpcode op) { CONDITIONAL_DISABLE; + SetRoundingMode(); switch (op & 0x3f) { case 0: CompFPTriArith(op, &XEmitter::ADDSS, false); break; //F(fd) = F(fs) + F(ft); //add @@ -173,6 +174,8 @@ void Jit::CompFPComp(int lhs, int rhs, u8 compare, bool allowNaN) void Jit::Comp_FPUComp(MIPSOpcode op) { CONDITIONAL_DISABLE; + // TODO: Does this matter here? + SetRoundingMode(); int fs = _FS; int ft = _FT; @@ -357,6 +360,7 @@ void Jit::Comp_mxc1(MIPSOpcode op) case 6: //currentMIPS->WriteFCR(fs, R(rt)); break; //ctc1 if (fs == 31) { + ClearRoundingMode(); if (gpr.IsImm(rt)) { gpr.SetImm(MIPS_REG_FPCOND, (gpr.GetImm(rt) >> 23) & 1); MOV(32, M(&mips_->fcr31), Imm32(gpr.GetImm(rt) & 0x0181FFFF)); diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 74cfd97a6a..38b7c6c338 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -211,6 +211,49 @@ void Jit::WriteDowncount(int offset) SUB(32, M(¤tMIPS->downcount), downcount > 127 ? Imm32(downcount) : Imm8(downcount)); } +void Jit::ClearRoundingMode() +{ + if (js.roundingModeSet && g_Config.bSetRoundingMode) + { + STMXCSR(M(¤tMIPS->temp)); + // Clear the rounding mode bits back to 0. + AND(32, M(¤tMIPS->temp), Imm32(~(3 << 13))); + LDMXCSR(M(¤tMIPS->temp)); + + js.roundingModeSet = false; + } +} + +void Jit::SetRoundingMode() +{ + if (!js.roundingModeSet && g_Config.bSetRoundingMode) + { + MOV(32, R(EAX), M(&mips_->fcr31)); + AND(32, R(EAX), Imm8(3)); + + // If it's 0, we don't actually bother setting. This is the most common. + // We always use nearest as the default rounding mode. + FixupBranch skip = J_CC(CC_Z); + + STMXCSR(M(¤tMIPS->temp)); + + // The MIPS bits don't correspond exactly, so we have to adjust. + // 0 -> 0 (skip), 1 -> 3, 2 -> 2 (skip2), 3 -> 1 + CMP(32, R(EAX), Imm8(2)); + FixupBranch skip2 = J_CC(CC_Z); + XOR(32, R(EAX), Imm8(2)); + SetJumpTarget(skip2); + + SHL(32, R(EAX), Imm8(13)); + OR(32, M(¤tMIPS->temp), R(EAX)); + LDMXCSR(M(¤tMIPS->temp)); + + SetJumpTarget(skip); + + js.roundingModeSet = true; + } +} + void Jit::ClearCache() { blocks.Clear(); @@ -553,6 +596,7 @@ void Jit::WriteExit(u32 destination, int exit_num) } WriteDowncount(); + ClearRoundingMode(); //If nobody has taken care of this yet (this can be removed when all branches are done) JitBlock *b = js.curBlock; @@ -589,6 +633,7 @@ void Jit::WriteExitDestInReg(X64Reg reg) } WriteDowncount(); + ClearRoundingMode(); // Validate the jump to avoid a crash? if (!g_Config.bFastMemory) @@ -627,6 +672,7 @@ void Jit::WriteExitDestInReg(X64Reg reg) void Jit::WriteSyscallExit() { WriteDowncount(); + ClearRoundingMode(); if (js.afterOp & JitState::AFTER_MEMCHECK_CLEANUP) { ABI_CallFunction(&JitMemCheckCleanup); } @@ -646,6 +692,7 @@ bool Jit::CheckJitBreakpoint(u32 addr, int downcountOffset) CMP(32, R(EAX), Imm32(0)); FixupBranch skip = J_CC(CC_Z); WriteDowncount(downcountOffset); + ClearRoundingMode(); // Just to fix the stack. LOAD_FLAGS; JMP(asm_.dispatcherCheckCoreState, true); diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 605015703a..0f83c0e87f 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -181,6 +181,8 @@ private: void FlushAll(); void FlushPrefixV(); void WriteDowncount(int offset = 0); + void ClearRoundingMode(); + void SetRoundingMode(); bool ReplaceJalTo(u32 dest); // See CompileDelaySlotFlags for flags. void CompileDelaySlot(int flags, RegCacheState *state = NULL); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index db2b4c6914..58ba4d6ac2 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -346,6 +346,7 @@ int main(int argc, const char* argv[]) g_Config.bSoftwareSkinning = true; g_Config.bVertexDecoderJit = true; g_Config.bBlockTransferGPU = true; + g_Config.bSetRoundingMode = true; #ifdef _WIN32 InitSysDirectories();