diff --git a/Source/Core/Core/PowerPC/Gekko.h b/Source/Core/Core/PowerPC/Gekko.h index a046b883c2..ca083b1219 100644 --- a/Source/Core/Core/PowerPC/Gekko.h +++ b/Source/Core/Core/PowerPC/Gekko.h @@ -476,6 +476,12 @@ union UReg_FPSCR UReg_FPSCR() = default; explicit UReg_FPSCR(u32 hex_) : Hex{hex_} {} + + void ClearFIFR() + { + FI = 0; + FR = 0; + } }; // Hardware Implementation-Dependent Register 0 diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index abc798d220..ed15cef6da 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -103,7 +103,10 @@ inline double NI_mul(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -111,6 +114,7 @@ inline double NI_mul(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXIMZ); + FPSCR.ClearFIFR(); return PPC_NAN; } return t; @@ -123,7 +127,10 @@ inline double NI_div(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -135,17 +142,18 @@ inline double NI_div(double a, double b) if (a == 0.0) { SetFPException(FPSCR_VXZDZ); + FPSCR.ClearFIFR(); } else { SetFPException(FPSCR_ZX); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } } else if (std::isinf(a) && std::isinf(b)) { SetFPException(FPSCR_VXIDI); + FPSCR.ClearFIFR(); } return PPC_NAN; @@ -161,7 +169,10 @@ inline double NI_add(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -169,6 +180,7 @@ inline double NI_add(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -182,7 +194,10 @@ inline double NI_sub(double a, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -190,6 +205,7 @@ inline double NI_sub(double a, double b) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -206,7 +222,10 @@ inline double NI_madd(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -216,6 +235,7 @@ inline double NI_madd(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -224,12 +244,16 @@ inline double NI_madd(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -243,7 +267,10 @@ inline double NI_msub(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(a) || Common::IsSNAN(b) || Common::IsSNAN(c)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(a)) return MakeQuiet(a); @@ -253,6 +280,7 @@ inline double NI_msub(double a, double c, double b) return MakeQuiet(c); SetFPException(FPSCR_VXIMZ); + FPSCR.ClearFIFR(); return PPC_NAN; } @@ -261,12 +289,16 @@ inline double NI_msub(double a, double c, double b) if (std::isnan(t)) { if (Common::IsSNAN(b)) + { SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } if (std::isnan(b)) return MakeQuiet(b); SetFPException(FPSCR_VXISI); + FPSCR.ClearFIFR(); return PPC_NAN; } diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index 3782c748a5..32f78343e9 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -92,8 +92,7 @@ void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode) const double di = i; if (di == b) { - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } else { @@ -105,8 +104,7 @@ void ConvertToInteger(UGeckoInstruction inst, RoundingMode rounding_mode) if (exception_occurred) { - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } if (!exception_occurred || FPSCR.VE == 0) @@ -285,8 +283,7 @@ void Interpreter::frspx(UGeckoInstruction inst) // round to single PowerPC::UpdateFPRF(b); } - SetFI(0); - FPSCR.FR = 0; + FPSCR.ClearFIFR(); } else { @@ -397,6 +394,7 @@ void Interpreter::fresx(UGeckoInstruction inst) if (b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.ClearFIFR(); if (FPSCR.ZE == 0) compute_result(b); @@ -404,8 +402,7 @@ void Interpreter::fresx(UGeckoInstruction inst) else if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); @@ -432,8 +429,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) if (b < 0.0) { SetFPException(FPSCR_VXSQRT); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); @@ -441,6 +437,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) else if (b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.ClearFIFR(); if (FPSCR.ZE == 0) compute_result(b); @@ -448,8 +445,7 @@ void Interpreter::frsqrtex(UGeckoInstruction inst) else if (Common::IsSNAN(b)) { SetFPException(FPSCR_VXSNAN); - FPSCR.FI = 0; - FPSCR.FR = 0; + FPSCR.ClearFIFR(); if (FPSCR.VE == 0) compute_result(b); diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp index 6a84132c96..380c9ac103 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -115,12 +115,19 @@ void Interpreter::ps_div(UGeckoInstruction inst) void Interpreter::ps_res(UGeckoInstruction inst) { // this code is based on the real hardware tests - double a = rPS0(inst.FB); - double b = rPS1(inst.FB); + const double a = rPS0(inst.FB); + const double b = rPS1(inst.FB); if (a == 0.0 || b == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.ClearFIFR(); + } + + if (Common::IsSNAN(a) || Common::IsSNAN(b)) + { + SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); } rPS0(inst.FD) = Common::ApproximateReciprocal(a); @@ -133,18 +140,29 @@ void Interpreter::ps_res(UGeckoInstruction inst) void Interpreter::ps_rsqrte(UGeckoInstruction inst) { - if (rPS0(inst.FB) == 0.0 || rPS1(inst.FB) == 0.0) + const double ps0 = rPS0(inst.FB); + const double ps1 = rPS1(inst.FB); + + if (ps0 == 0.0 || ps1 == 0.0) { SetFPException(FPSCR_ZX); + FPSCR.ClearFIFR(); } - if (rPS0(inst.FB) < 0.0 || rPS1(inst.FB) < 0.0) + if (ps0 < 0.0 || ps1 < 0.0) { SetFPException(FPSCR_VXSQRT); + FPSCR.ClearFIFR(); } - rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS0(inst.FB))); - rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(rPS1(inst.FB))); + if (Common::IsSNAN(ps0) || Common::IsSNAN(ps1)) + { + SetFPException(FPSCR_VXSNAN); + FPSCR.ClearFIFR(); + } + + rPS0(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps0)); + rPS1(inst.FD) = ForceSingle(Common::ApproximateReciprocalSquareRoot(ps1)); PowerPC::UpdateFPRF(rPS0(inst.FD));