diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter.h index dd4c88d94c..b1ea851041 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.h @@ -288,7 +288,6 @@ private: // flag helper static void Helper_UpdateCR0(u32 value); - static void Helper_UpdateCR1(); // address helper static u32 Helper_Get_EA(const UGeckoInstruction inst); diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index 2d16cbea84..0c98d3a939 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -52,6 +52,11 @@ inline void UpdateFPSCR() (FPSCR.ZX & FPSCR.ZE) | (FPSCR.XX & FPSCR.XE); } +inline void Helper_UpdateCR1() +{ + PowerPC::SetCRField(1, (FPSCR.FX << 3) | (FPSCR.FEX << 2) | (FPSCR.VX << 1) | FPSCR.OX); +} + inline double ForceSingle(double value) { // convert to float... diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index de4f453654..f8f64ed37f 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -11,12 +11,93 @@ #include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h" #include "Core/PowerPC/PowerPC.h" -// Extremely rare - actually, never seen. -// Star Wars : Rogue Leader spams that at some point :| -void Interpreter::Helper_UpdateCR1() +namespace { - PowerPC::SetCRField(1, (FPSCR.FX << 3) | (FPSCR.FEX << 2) | (FPSCR.VX << 1) | FPSCR.OX); +// Apply current rounding mode +enum class RoundingMode +{ + Nearest = 0b00, + TowardsZero = 0b01, + TowardsPositiveInfinity = 0b10, + TowardsNegativeInfinity = 0b11 +}; + +void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode) +{ + const double b = rPS0(inst.FB); + u32 value; + + if (b > static_cast(0x7fffffff)) + { + value = 0x7fffffff; + SetFPException(FPSCR_VXCVI); + FPSCR.FI = 0; + FPSCR.FR = 0; + } + else if (b < -static_cast(0x80000000)) + { + value = 0x80000000; + SetFPException(FPSCR_VXCVI); + FPSCR.FI = 0; + FPSCR.FR = 0; + } + else + { + s32 i = 0; + switch (rounding_mode) + { + case RoundingMode::Nearest: + { + const double t = b + 0.5; + i = static_cast(t); + + if (t - i < 0 || (t - i == 0 && b > 0)) + { + i--; + } + break; + } + case RoundingMode::TowardsZero: + i = static_cast(b); + break; + case RoundingMode::TowardsPositiveInfinity: + i = static_cast(b); + if (b - i > 0) + { + i++; + } + break; + case RoundingMode::TowardsNegativeInfinity: + i = static_cast(b); + if (b - i < 0) + { + i--; + } + break; + } + value = static_cast(i); + const double di = i; + if (di == b) + { + FPSCR.FI = 0; + FPSCR.FR = 0; + } + else + { + SetFI(1); + FPSCR.FR = fabs(di) > fabs(b); + } + } + + // Based on HW tests + // FPRF is not affected + riPS0(inst.FD) = 0xfff8000000000000ull | value; + if (value == 0 && std::signbit(b)) + riPS0(inst.FD) |= 0x100000000ull; + if (inst.Rc) + Helper_UpdateCR1(); } +} // Anonymous namespace void Interpreter::Helper_FloatCompareOrdered(UGeckoInstruction inst, double fa, double fb) { @@ -103,127 +184,14 @@ void Interpreter::fcmpu(UGeckoInstruction inst) Helper_FloatCompareUnordered(inst, rPS0(inst.FA), rPS0(inst.FB)); } -// Apply current rounding mode void Interpreter::fctiwx(UGeckoInstruction inst) { - const double b = rPS0(inst.FB); - u32 value; - - if (b > (double)0x7fffffff) - { - value = 0x7fffffff; - SetFPException(FPSCR_VXCVI); - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else if (b < -(double)0x80000000) - { - value = 0x80000000; - SetFPException(FPSCR_VXCVI); - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else - { - s32 i = 0; - switch (FPSCR.RN) - { - case 0: // nearest - { - double t = b + 0.5; - i = (s32)t; - - if (t - i < 0 || (t - i == 0 && b > 0)) - { - i--; - } - break; - } - case 1: // zero - i = (s32)b; - break; - case 2: // +inf - i = (s32)b; - if (b - i > 0) - { - i++; - } - break; - case 3: // -inf - i = (s32)b; - if (b - i < 0) - { - i--; - } - break; - } - value = (u32)i; - double di = i; - if (di == b) - { - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else - { - SetFI(1); - FPSCR.FR = fabs(di) > fabs(b); - } - } - - // based on HW tests - // FPRF is not affected - riPS0(inst.FD) = 0xfff8000000000000ull | value; - if (value == 0 && std::signbit(b)) - riPS0(inst.FD) |= 0x100000000ull; - if (inst.Rc) - Helper_UpdateCR1(); + ConvertToInteger(inst, static_cast(FPSCR.RN)); } -// Always round toward zero void Interpreter::fctiwzx(UGeckoInstruction inst) { - const double b = rPS0(inst.FB); - u32 value; - - if (b > (double)0x7fffffff) - { - value = 0x7fffffff; - SetFPException(FPSCR_VXCVI); - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else if (b < -(double)0x80000000) - { - value = 0x80000000; - SetFPException(FPSCR_VXCVI); - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else - { - s32 i = (s32)b; - double di = i; - if (di == b) - { - FPSCR.FI = 0; - FPSCR.FR = 0; - } - else - { - SetFI(1); - FPSCR.FR = fabs(di) > fabs(b); - } - value = (u32)i; - } - - // based on HW tests - // FPRF is not affected - riPS0(inst.FD) = 0xfff8000000000000ull | value; - if (value == 0 && std::signbit(b)) - riPS0(inst.FD) |= 0x100000000ull; - if (inst.Rc) - Helper_UpdateCR1(); + ConvertToInteger(inst, RoundingMode::TowardsZero); } void Interpreter::fmrx(UGeckoInstruction inst)