diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index 4987ae11..9caa865b 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -83,6 +83,13 @@ struct Cop1 { friend struct Interpreter; friend struct JIT; + void SetCauseUnimplemented(Registers&); + void SetCauseUnderflow(Registers&); + void SetCauseInexact(Registers&); + void SetCauseDivisionByZero(Registers&); + void SetCauseOverflow(Registers&); + void SetCauseInvalid(Registers&); + template FORCE_INLINE T GetFGR_FR(Cop0& cop0, u8 r) { if constexpr (std::is_same_v || std::is_same_v) { diff --git a/src/backend/core/registers/cop/cop1instructions.cpp b/src/backend/core/registers/cop/cop1instructions.cpp index c38c2bb7..ca94a187 100644 --- a/src/backend/core/registers/cop/cop1instructions.cpp +++ b/src/backend/core/registers/cop/cop1instructions.cpp @@ -19,38 +19,6 @@ FORCE_INLINE bool FireFPUException(Registers& regs) { } #define CheckFPUException() do { if(FireFPUException(regs)) { return; } } while(0) -#define CheckRound(a, b) do { if ((a) != (b)) { fcr31.cause_inexact_operation = true; if(!fcr31.enable_inexact_operation) { fcr31.flag_inexact_operation = true; } } CheckFPUException(); } while(0) -#define SetCauseUnimplemented() do { regs.cop1.fcr31.cause_unimplemented_operation = true; } while(0) -#define SetCauseUnderflow() do { \ - regs.cop1.fcr31.cause_underflow = true; \ - if(!regs.cop1.fcr31.enable_underflow) { \ - regs.cop1.fcr31.flag_underflow = true; \ - } \ -} while(0) -#define SetCauseInexact() do { \ - regs.cop1.fcr31.cause_inexact_operation = true; \ - if(!regs.cop1.fcr31.enable_inexact_operation) { \ - regs.cop1.fcr31.flag_inexact_operation = true; \ - } \ -} while(0) -#define SetCauseDivisionByZero() do { \ - regs.cop1.fcr31.cause_division_by_zero = true; \ - if(!regs.cop1.fcr31.enable_division_by_zero) { \ - regs.cop1.fcr31.flag_division_by_zero = true; \ - } \ -} while(0) -#define SetCauseOverflow() do { \ - regs.cop1.fcr31.cause_overflow = true; \ - if(!regs.cop1.fcr31.enable_overflow) { \ - regs.cop1.fcr31.flag_overflow = true; \ - } \ -} while(0) -#define SetCauseInvalid() do { \ - regs.cop1.fcr31.cause_invalid_operation = true; \ - if(!regs.cop1.fcr31.enable_invalid_operation) { \ - regs.cop1.fcr31.flag_invalid_operation = true; \ - } \ -} while(0) FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) { int og = fegetround(); @@ -64,8 +32,45 @@ FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) { return og; } -#define CheckCVTArg(f) do { SetCauseByArgCVT(regs, f); CheckFPUException(); } while(0) -#define CheckArg(f) do { SetCauseByArg(regs, f); CheckFPUException(); } while(0) +void Cop1::SetCauseUnimplemented(Registers& regs) { + regs.cop1.fcr31.cause_unimplemented_operation = true; +} + +void Cop1::SetCauseUnderflow(Registers& regs) { + regs.cop1.fcr31.cause_underflow = true; + if(!regs.cop1.fcr31.enable_underflow) { + regs.cop1.fcr31.flag_underflow = true; + } +} + +void Cop1::SetCauseInexact(Registers& regs) { + regs.cop1.fcr31.cause_inexact_operation = true; + if(!regs.cop1.fcr31.enable_inexact_operation) { + regs.cop1.fcr31.flag_inexact_operation = true; + } +} + +void Cop1::SetCauseDivisionByZero(Registers& regs) { + regs.cop1.fcr31.cause_division_by_zero = true; + if(!regs.cop1.fcr31.enable_division_by_zero) { + regs.cop1.fcr31.flag_division_by_zero = true; + } +} + +void Cop1::SetCauseOverflow(Registers& regs) { + regs.cop1.fcr31.cause_overflow = true; + if(!regs.cop1.fcr31.enable_overflow) { + regs.cop1.fcr31.flag_overflow = true; + } +} + +void Cop1::SetCauseInvalid(Registers& regs) { + regs.cop1.fcr31.cause_invalid_operation = true; + if(!regs.cop1.fcr31.enable_invalid_operation) { + regs.cop1.fcr31.flag_invalid_operation = true; + } +} + #define PUSHROUNDING int orig_round = PushRoundingMode(regs.cop1.fcr31) #define POPROUNDING fesetround(orig_round) #define OP_CheckExcept(op) do { PUSHROUNDING; feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseRaised(regs, fetestexcept(FE_ALL_EXCEPT)); POPROUNDING; } while(0) @@ -98,13 +103,13 @@ FORCE_INLINE void SetCauseByArgCVT(Registers& regs, T f) { case FP_NAN: case FP_INFINITE: case FP_SUBNORMAL: - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); break; case FP_NORMAL: // Check overflow if (f >= max || f <= min) { - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); } break; @@ -113,6 +118,8 @@ FORCE_INLINE void SetCauseByArgCVT(Registers& regs, T f) { } } +#define CheckCVTArg(f) do { SetCauseByArgCVT(regs, f); CheckFPUException(); } while(0) + FORCE_INLINE void SetFPUCauseRaised(Registers& regs, int raised) { if (raised == 0) { return; @@ -120,33 +127,33 @@ FORCE_INLINE void SetFPUCauseRaised(Registers& regs, int raised) { if (raised & FE_UNDERFLOW) { if (!regs.cop1.fcr31.fs || regs.cop1.fcr31.enable_underflow || regs.cop1.fcr31.enable_inexact_operation) { - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); return; } else { - SetCauseUnderflow(); + regs.cop1.SetCauseUnderflow(regs); } } if (raised & FE_INEXACT) { - SetCauseInexact(); + regs.cop1.SetCauseInexact(regs); } if (raised & FE_DIVBYZERO) { - SetCauseDivisionByZero(); + regs.cop1.SetCauseDivisionByZero(regs); } if (raised & FE_OVERFLOW) { - SetCauseOverflow(); + regs.cop1.SetCauseOverflow(regs); } if (raised & FE_INVALID) { - SetCauseInvalid(); + regs.cop1.SetCauseInvalid(regs); } } FORCE_INLINE void SetFPUCauseCVTRaised(Registers& regs, int raised) { if(raised & FE_INVALID) { - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); return; } @@ -177,13 +184,13 @@ FORCE_INLINE void SetCauseByArg(Registers& regs, T f) { switch(c) { case FP_NAN: if(isqnan(f)) { - SetCauseInvalid(); + regs.cop1.SetCauseInvalid(regs); } else { - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); } break; case FP_SUBNORMAL: - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); break; case FP_INFINITE: case FP_ZERO: @@ -194,6 +201,8 @@ FORCE_INLINE void SetCauseByArg(Registers& regs, T f) { } } +#define CheckArg(f) do { SetCauseByArg(regs, f); CheckFPUException(); } while(0) + template FORCE_INLINE void SetCauseOnResult(Registers& regs, T& d) { Cop1& cop1 = regs.cop1; @@ -214,11 +223,11 @@ FORCE_INLINE void SetCauseOnResult(Registers& regs, T& d) { break; case FP_SUBNORMAL: if (!cop1.fcr31.fs || cop1.fcr31.enable_underflow || cop1.fcr31.enable_inexact_operation) { - SetCauseUnimplemented(); + regs.cop1.SetCauseUnimplemented(regs); } else { // Since the if statement checks for the corresponding enable bits, it's safe to turn these cause bits on here. - SetCauseUnderflow(); - SetCauseInexact(); + regs.cop1.SetCauseUnderflow(regs); + regs.cop1.SetCauseInexact(regs); switch (cop1.fcr31.rounding_mode) { case 0: case 1: @@ -253,6 +262,7 @@ FORCE_INLINE void SetCauseOnResult(Registers& regs, T& d) { #define CheckResult(f) do { SetCauseOnResult(regs, (f)); CheckFPUException(); } while(0) #define any_unordered(fs, ft) (std::isnan(fs) || std::isnan(ft)) +#define CheckRound(a, b) do { if ((a) != (b)) { SetCauseInexact(regs); } CheckFPUException(); } while(0) template FORCE_INLINE bool isnan(T f) { @@ -269,30 +279,24 @@ FORCE_INLINE bool isnan(T f) { #define checknanregs(fs, ft) do { \ if(isnan(fs) || isnan(ft)) { \ - regs.cop1.fcr31.cause_invalid_operation = true; \ - if(!regs.cop1.fcr31.enable_invalid_operation) { \ - regs.cop1.fcr31.flag_invalid_operation = true; \ - } \ + regs.cop1.SetCauseInvalid(regs); \ CheckFPUException(); \ } \ } while(0) #define checkqnanregs(fs, ft) do { \ if(isqnan(fs) || isqnan(ft)) { \ - regs.cop1.fcr31.cause_invalid_operation = true; \ - if(!regs.cop1.fcr31.enable_invalid_operation) { \ - regs.cop1.fcr31.flag_invalid_operation = true; \ - } \ + regs.cop1.SetCauseInvalid(regs); \ CheckFPUException(); \ } \ } while(0) void Cop1::absd(Registers& regs, u32 instr) { - OP(double, std::abs(fs)); + OP(double, std::fabs(fs)); } void Cop1::abss(Registers& regs, u32 instr) { - OP(float, std::abs(fs)); + OP(float, std::fabs(fs)); } void Cop1::adds(Registers& regs, u32 instr) { @@ -391,13 +395,35 @@ void Cop1::cvtsd(Registers& regs, u32 instr) { SetFGR_Raw(FD(instr), result); } +void Cop1::cvtsw(Registers& regs, u32 instr) { + CheckFPUUsable(); + auto fs = GetFGR_FS(regs.cop0, FS(instr)); + float result; + OP_CheckExcept({ result = float(fs); }); + CheckResult(result); + SetFGR_Raw(FD(instr), result); +} + +void Cop1::cvtsl(Registers& regs, u32 instr) { + CheckFPUUsable(); + auto fs = GetFGR_FR(regs.cop0, FS(instr)); + if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { + SetCauseUnimplemented(regs); + CheckFPUException(); + } + float result; + OP_CheckExcept({ result = float(fs); }); + CheckResult(result); + SetFGR_Raw(FD(instr), result); +} + void Cop1::cvtwd(Registers& regs, u32 instr) { CheckFPUUsable(); auto fs = GetFGR_FS(regs.cop0, FS(instr)); CheckCVTArg(fs); s32 result; PUSHROUNDING; - CVT_OP_CheckExcept({ result = s32(fs); }); + CVT_OP_CheckExcept({ result = std::rint(fs); }); POPROUNDING; CheckRound(fs, result); SetFGR(FD(instr), result); @@ -409,7 +435,7 @@ void Cop1::cvtws(Registers& regs, u32 instr) { CheckCVTArg(fs); s32 result; PUSHROUNDING; - CVT_OP_CheckExcept({ result = s32(fs); }); + CVT_OP_CheckExcept({ result = std::rint(fs); }); POPROUNDING; CheckRound(fs, result); SetFGR(FD(instr), result); @@ -427,19 +453,6 @@ void Cop1::cvtls(Registers& regs, u32 instr) { SetFGR(FD(instr), result); } -void Cop1::cvtsl(Registers& regs, u32 instr) { - CheckFPUUsable(); - auto fs = GetFGR_FR(regs.cop0, FS(instr)); - if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { - SetCauseUnimplemented(); - CheckFPUException(); - } - float result; - OP_CheckExcept({ result = float(fs); }); - CheckResult(result); - SetFGR_Raw(FD(instr), result); -} - void Cop1::cvtdw(Registers& regs, u32 instr) { CheckFPUUsable(); auto fs = GetFGR_FS(regs.cop0, FS(instr)); @@ -449,21 +462,12 @@ void Cop1::cvtdw(Registers& regs, u32 instr) { SetFGR_Raw(FD(instr), result); } -void Cop1::cvtsw(Registers& regs, u32 instr) { - CheckFPUUsable(); - auto fs = GetFGR_FS(regs.cop0, FS(instr)); - float result; - OP_CheckExcept({ result = float(fs); }); - CheckResult(result); - SetFGR_Raw(FD(instr), result); -} - void Cop1::cvtdl(Registers& regs, u32 instr) { CheckFPUUsable(); auto fs = GetFGR_FR(regs.cop0, FS(instr)); if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { - SetCauseUnimplemented(); + SetCauseUnimplemented(regs); CheckFPUException(); } double result;