Improve FGR handling

This commit is contained in:
SimoneN64
2023-11-13 16:08:21 +01:00
parent 0791e83e53
commit 22fe515459
4 changed files with 187 additions and 224 deletions

View File

@@ -254,11 +254,8 @@ void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) {
bool old_exl = regs.cop0.status.exl; bool old_exl = regs.cop0.status.exl;
if(!regs.cop0.status.exl) { if(!regs.cop0.status.exl) {
if(regs.prevDelaySlot) { if(regs.cop0.cause.branchDelay = regs.prevDelaySlot) {
regs.cop0.cause.branchDelay = true;
pc -= 4; pc -= 4;
} else {
regs.cop0.cause.branchDelay = false;
} }
regs.cop0.status.exl = true; regs.cop0.status.exl = true;

View File

@@ -13,7 +13,7 @@ Cop1::Cop1() {
void Cop1::Reset() { void Cop1::Reset() {
fcr0 = 0xa00; fcr0 = 0xa00;
fcr31.write(0x01000800); fcr31.write(0x01000800);
memset(fgr, 0, 32 * sizeof(FGR)); memset(fgr, 0, 32 * sizeof(FloatingPointReg));
} }
template <class T> template <class T>

View File

@@ -3,7 +3,10 @@
#include <cstring> #include <cstring>
namespace n64 { namespace n64 {
struct Cop1;
union FCR31 { union FCR31 {
FCR31() = default;
struct { struct {
unsigned rounding_mode:2; unsigned rounding_mode:2;
unsigned flag_inexact_operation:1; unsigned flag_inexact_operation:1;
@@ -57,13 +60,28 @@ enum CompConds {
LT, NGE, LE, NGT LT, NGE, LE, NGT
}; };
union FGR { union FloatingPointReg {
struct { struct {
s32 lo:32; s32 int32;
s32 hi:32; s32 int32h;
} __attribute__((__packed__));
struct {
u32 uint32;
u32 uint32h;
} __attribute__((__packed__));
struct {
s64 int64;
} __attribute__((__packed__));
struct {
u64 uint64;
} __attribute__((__packed__));
struct {
float float32;
float float32h;
} __attribute__((__packed__));
struct {
double float64;
} __attribute__((__packed__)); } __attribute__((__packed__));
s64 raw;
}; };
struct Interpreter; struct Interpreter;
@@ -76,7 +94,7 @@ struct Cop1 {
Cop1(); Cop1();
u32 fcr0{}; u32 fcr0{};
FCR31 fcr31{}; FCR31 fcr31{};
FGR fgr[32]{}; FloatingPointReg fgr[32]{};
void Reset(); void Reset();
template <class T> // either JIT or Interpreter template <class T> // either JIT or Interpreter
void decode(T&, u32); void decode(T&, u32);
@@ -89,119 +107,8 @@ struct Cop1 {
void SetCauseDivisionByZero(Registers&); void SetCauseDivisionByZero(Registers&);
void SetCauseOverflow(Registers&); void SetCauseOverflow(Registers&);
void SetCauseInvalid(Registers&); void SetCauseInvalid(Registers&);
template<typename T>
FORCE_INLINE T GetFGR_FR(Cop0& cop0, u8 r) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) {
return fgr[r].lo;
} else {
if (r & 1) {
return fgr[r & ~1].hi;
} else {
return fgr[r].lo;
}
}
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) {
// When this bit is not set, accessing odd registers is not allowed.
r &= ~1;
}
return fgr[r].raw;
}
}
template<typename T>
FORCE_INLINE void SetFGR_FR(Cop0& cop0, u8 r, T value) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (cop0.status.fr) {
fgr[r].lo = value;
} else {
if (r & 1) {
fgr[r & ~1].hi = value;
} else {
fgr[r].lo = value;
}
}
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
if (!cop0.status.fr) {
// When this bit is not set, accessing odd registers is not allowed.
r &= ~1;
}
fgr[r].raw = value;
}
}
template<typename T>
FORCE_INLINE void SetFGR(u8 r, T value) {
fgr[r].raw = value;
}
template<typename T>
FORCE_INLINE u64 GetFGR(u8 r) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
return fgr[r].lo;
} else if constexpr (std::is_same_v<T, u64> || std::is_same_v<T, s64>) {
return fgr[r].raw;
}
}
template <typename T>
FORCE_INLINE T GetFGR_FS(Cop0& cop0, u8 fs) {
if constexpr (std::is_same_v<T, u32> || std::is_same_v<T, s32>) {
if (!cop0.status.fr) {
fs &= ~1;
}
return fgr[fs].lo;
} else if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
if (!cop0.status.fr) {
fs &= ~1;
}
return GetFGR_Raw<T>(fs);
}
}
template <typename T>
FORCE_INLINE T GetFGR_Raw(u8 r) {
if constexpr (std::is_same_v<T, float>) {
static_assert(sizeof(float) == sizeof(u32), "float and u32 need to both be 32 bits for this to work.");
auto rawvalue = GetFGR<u32>(r);
float floatvalue;
memcpy(&floatvalue, &rawvalue, sizeof(float));
return floatvalue;
} else if constexpr (std::is_same_v<T, double>) {
static_assert(sizeof(double) == sizeof(u64), "double and u64 need to both be 64 bits for this to work.");
double doublevalue;
auto rawvalue = GetFGR<u64>(r);
memcpy(&doublevalue, &rawvalue, sizeof(double));
return doublevalue;
}
}
template <typename T>
FORCE_INLINE void SetFGR_Raw(u8 r, T val) {
if constexpr (std::is_same_v<T, float>) {
static_assert(sizeof(float) == sizeof(u32), "float and u32 need to both be 32 bits for this to work.");
u32 rawvalue;
memcpy(&rawvalue, &val, sizeof(float));
SetFGR<u32>(r, rawvalue);
} else if constexpr (std::is_same_v<T, double>) {
static_assert(sizeof(double) == sizeof(u64), "double and u64 need to both be 64 bits for this to work.");
u64 rawvalue;
memcpy(&rawvalue, &val, sizeof(double));
SetFGR<u64>(r, rawvalue);
}
}
template <typename T>
FORCE_INLINE T GetFGR_FT(u8 ft) {
return GetFGR_Raw<T>(ft);
}
private: private:
template <typename T> auto FGR(Cop0Status&, u32) -> T&;
void decodeInterp(Interpreter&, u32); void decodeInterp(Interpreter&, u32);
void decodeJIT(JIT&, u32); void decodeJIT(JIT&, u32);
void absd(Registers&, u32 instr); void absd(Registers&, u32 instr);

View File

@@ -7,6 +7,56 @@
#include <cfenv> #include <cfenv>
namespace n64 { namespace n64 {
template<> auto Cop1::FGR<s32>(Cop0Status& status, u32 index) -> s32& {
if (status.fr) {
return fgr[index].int32;
}
else if (index & 1) {
return fgr[index & ~1].int32h;
}
else {
return fgr[index & ~1].int32;
}
}
template<> auto Cop1::FGR<u32>(Cop0Status& status, u32 index) -> u32& {
return (u32&)FGR<s32>(status, index);
}
template<> auto Cop1::FGR<float>(Cop0Status& status, u32 index) -> float& {
if (status.fr) {
return fgr[index].float32;
}
else if (index & 1) {
return fgr[index & ~1].float32h;
}
else {
return fgr[index & ~1].float32;
}
}
template<> auto Cop1::FGR<s64>(Cop0Status& status, u32 index) -> s64& {
if (status.fr) {
return fgr[index].int64;
}
else {
return fgr[index & ~1].int64;
}
}
template<> auto Cop1::FGR<u64>(Cop0Status& status, u32 index) -> u64& {
return (u64&)FGR<s64>(status, index);
}
template<> auto Cop1::FGR<double>(Cop0Status& status, u32 index) -> double& {
if (status.fr) {
return fgr[index].float64;
}
else {
return fgr[index & ~1].float64;
}
}
FORCE_INLINE bool FireFPUException(Registers& regs) { FORCE_INLINE bool FireFPUException(Registers& regs) {
FCR31& fcr31 = regs.cop1.fcr31; FCR31& fcr31 = regs.cop1.fcr31;
u32 enable = fcr31.enable | (1 << 5); u32 enable = fcr31.enable | (1 << 5);
@@ -22,7 +72,7 @@ FORCE_INLINE bool FireFPUException(Registers& regs) {
FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) { FORCE_INLINE int PushRoundingMode(const FCR31& fcr31) {
int og = fegetround(); int og = fegetround();
switch(fcr31.rounding_mode) { switch (fcr31.rounding_mode) {
case 0: fesetround(FE_TONEAREST); break; case 0: fesetround(FE_TONEAREST); break;
case 1: fesetround(FE_TOWARDZERO); break; case 1: fesetround(FE_TOWARDZERO); break;
case 2: fesetround(FE_UPWARD); break; case 2: fesetround(FE_UPWARD); break;
@@ -71,21 +121,21 @@ void Cop1::SetCauseInvalid(Registers& regs) {
} }
} }
#define PUSHROUNDING int orig_round = PushRoundingMode(regs.cop1.fcr31) #define PUSHROUNDING int orig_round = PushRoundingMode(fcr31)
#define POPROUNDING fesetround(orig_round) #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) #define OP_CheckExcept(op) do { PUSHROUNDING; feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseRaised(regs, fetestexcept(FE_ALL_EXCEPT)); POPROUNDING; } while(0)
#define CVT_OP_CheckExcept(op) do { feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseCVTRaised(regs, fetestexcept(FE_ALL_EXCEPT)); CheckFPUException(); } while(0) #define CVT_OP_CheckExcept(op) do { feclearexcept(FE_ALL_EXCEPT); op; SetFPUCauseCVTRaised(regs, fetestexcept(FE_ALL_EXCEPT)); CheckFPUException(); } while(0)
#define OP(T, op) do { \ #define OP(T, op) do { \
CheckFPUUsable(); \ CheckFPUUsable(); \
auto fs = GetFGR_FS<T>(regs.cop0, FS(instr)); \ auto fs = FGR<T>(regs.cop0.status, FS(instr)); \
auto ft = GetFGR_FT<T>(FT(instr)); \ auto ft = FGR<T>(regs.cop0.status, FT(instr)); \
CheckArg(fs); \ CheckArg(fs); \
CheckArg(ft); \ CheckArg(ft); \
T result; \ T result; \
OP_CheckExcept({result = (op);}); \ OP_CheckExcept({result = (op);}); \
CheckResult(result); \ CheckResult(result); \
SetFGR_Raw<T>(FD(instr), result); \ FGR<T>(regs.cop0.status, FD(instr)) = result; \
} while(0) } while(0)
template <typename T> template <typename T>
@@ -322,42 +372,42 @@ void Cop1::addd(Registers& regs, u32 instr) {
void Cop1::ceills(Registers& regs, u32 instr) { void Cop1::ceills(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); }); CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::ceilws(Registers& regs, u32 instr) { void Cop1::ceilws(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); }); CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::ceilld(Registers& regs, u32 instr) { void Cop1::ceilld(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); }); CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::ceilwd(Registers& regs, u32 instr) { void Cop1::ceilwd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::ceil(fs); }); CVT_OP_CheckExcept({ result = std::ceil(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cfc1(Registers& regs, u32 instr) const { void Cop1::cfc1(Registers& regs, u32 instr) const {
@@ -381,7 +431,16 @@ void Cop1::ctc1(Registers& regs, u32 instr) {
switch(fs) { switch(fs) {
case 0: break; case 0: break;
case 31: { case 31: {
u32 prevRound = fcr31.rounding_mode;
fcr31.write(val); fcr31.write(val);
if (prevRound != fcr31.rounding_mode) {
switch (fcr31.rounding_mode) {
case 0: fesetround(FE_TONEAREST); break;
case 1: fesetround(FE_TOWARDZERO); break;
case 2: fesetround(FE_UPWARD); break;
case 3: fesetround(FE_DOWNWARD); break;
}
}
CheckFPUException(); CheckFPUException();
} break; } break;
default: Util::panic("Undefined CTC1 with rd != 0 or 31"); default: Util::panic("Undefined CTC1 with rd != 0 or 31");
@@ -390,36 +449,36 @@ void Cop1::ctc1(Registers& regs, u32 instr) {
void Cop1::cvtds(Registers& regs, u32 instr) { void Cop1::cvtds(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckArg(fs); CheckArg(fs);
double result; double result;
OP_CheckExcept({ result = double(fs); }); OP_CheckExcept({ result = double(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<double>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtsd(Registers& regs, u32 instr) { void Cop1::cvtsd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckArg(fs); CheckArg(fs);
float result; float result;
OP_CheckExcept({ result = float(fs); }); OP_CheckExcept({ result = float(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<float>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtsw(Registers& regs, u32 instr) { void Cop1::cvtsw(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr)); auto fs = FGR<s32>(regs.cop0.status, FS(instr));
float result; float result;
OP_CheckExcept({ result = float(fs); }); OP_CheckExcept({ result = float(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<float>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtsl(Registers& regs, u32 instr) { void Cop1::cvtsl(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr)); auto fs = FGR<s64>(regs.cop0.status, FS(instr));
if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) {
SetCauseUnimplemented(regs); SetCauseUnimplemented(regs);
CheckFPUException(); CheckFPUException();
@@ -427,57 +486,57 @@ void Cop1::cvtsl(Registers& regs, u32 instr) {
float result; float result;
OP_CheckExcept({ result = float(fs); }); OP_CheckExcept({ result = float(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<float>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtwd(Registers& regs, u32 instr) { void Cop1::cvtwd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
PUSHROUNDING; PUSHROUNDING;
CVT_OP_CheckExcept({ result = std::rint(fs); }); CVT_OP_CheckExcept({ result = std::rint(fs); });
POPROUNDING; POPROUNDING;
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtws(Registers& regs, u32 instr) { void Cop1::cvtws(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
PUSHROUNDING; PUSHROUNDING;
CVT_OP_CheckExcept({ result = std::rint(fs); }); CVT_OP_CheckExcept({ result = std::rint(fs); });
POPROUNDING; POPROUNDING;
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtls(Registers& regs, u32 instr) { void Cop1::cvtls(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
PUSHROUNDING; PUSHROUNDING;
CVT_OP_CheckExcept({ result = std::rint(fs); }); CVT_OP_CheckExcept({ result = std::rint(fs); });
POPROUNDING; POPROUNDING;
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtdw(Registers& regs, u32 instr) { void Cop1::cvtdw(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<s32>(regs.cop0, FS(instr)); auto fs = FGR<s32>(regs.cop0.status, FS(instr));
double result; double result;
OP_CheckExcept({ result = double(fs); }); OP_CheckExcept({ result = double(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<double>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtdl(Registers& regs, u32 instr) { void Cop1::cvtdl(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FR<s64>(regs.cop0, FS(instr)); auto fs = FGR<s64>(regs.cop0.status, FS(instr));
if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) { if (fs >= s64(0x0080000000000000) || fs < s64(0xff80000000000000)) {
SetCauseUnimplemented(regs); SetCauseUnimplemented(regs);
@@ -486,26 +545,26 @@ void Cop1::cvtdl(Registers& regs, u32 instr) {
double result; double result;
OP_CheckExcept({ result = double(fs); }); OP_CheckExcept({ result = double(fs); });
CheckResult(result); CheckResult(result);
SetFGR_Raw(FD(instr), result); FGR<double>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::cvtld(Registers& regs, u32 instr) { void Cop1::cvtld(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
PUSHROUNDING; PUSHROUNDING;
CVT_OP_CheckExcept({ result = std::rint(fs); }); CVT_OP_CheckExcept({ result = std::rint(fs); });
POPROUNDING; POPROUNDING;
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
template <typename T> template <typename T>
void Cop1::cf(Registers& regs, u32 instr) { void Cop1::cf(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = false; fcr31.compare = false;
} }
@@ -513,8 +572,8 @@ void Cop1::cf(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cun(Registers& regs, u32 instr) { void Cop1::cun(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = any_unordered(fs, ft); fcr31.compare = any_unordered(fs, ft);
} }
@@ -522,8 +581,8 @@ void Cop1::cun(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::ceq(Registers& regs, u32 instr) { void Cop1::ceq(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs == ft; fcr31.compare = fs == ft;
} }
@@ -531,8 +590,8 @@ void Cop1::ceq(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cueq(Registers& regs, u32 instr) { void Cop1::cueq(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs == ft || any_unordered(fs, ft); fcr31.compare = fs == ft || any_unordered(fs, ft);
} }
@@ -540,8 +599,8 @@ void Cop1::cueq(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::colt(Registers& regs, u32 instr) { void Cop1::colt(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs < ft; fcr31.compare = fs < ft;
} }
@@ -549,8 +608,8 @@ void Cop1::colt(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cult(Registers& regs, u32 instr) { void Cop1::cult(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs < ft || any_unordered(fs, ft); fcr31.compare = fs < ft || any_unordered(fs, ft);
} }
@@ -558,8 +617,8 @@ void Cop1::cult(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cole(Registers& regs, u32 instr) { void Cop1::cole(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs <= ft; fcr31.compare = fs <= ft;
} }
@@ -567,8 +626,8 @@ void Cop1::cole(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cule(Registers& regs, u32 instr) { void Cop1::cule(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checkqnanregs(fs, ft); checkqnanregs(fs, ft);
fcr31.compare = fs <= ft || any_unordered(fs, ft); fcr31.compare = fs <= ft || any_unordered(fs, ft);
} }
@@ -576,8 +635,8 @@ void Cop1::cule(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::csf(Registers& regs, u32 instr) { void Cop1::csf(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = false; fcr31.compare = false;
} }
@@ -585,8 +644,8 @@ void Cop1::csf(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cngle(Registers& regs, u32 instr) { void Cop1::cngle(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = any_unordered(fs, ft); fcr31.compare = any_unordered(fs, ft);
} }
@@ -594,8 +653,8 @@ void Cop1::cngle(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cseq(Registers& regs, u32 instr) { void Cop1::cseq(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs == ft; fcr31.compare = fs == ft;
} }
@@ -603,8 +662,8 @@ void Cop1::cseq(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cngl(Registers& regs, u32 instr) { void Cop1::cngl(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs == ft || any_unordered(fs, ft); fcr31.compare = fs == ft || any_unordered(fs, ft);
} }
@@ -612,8 +671,8 @@ void Cop1::cngl(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::clt(Registers& regs, u32 instr) { void Cop1::clt(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs < ft; fcr31.compare = fs < ft;
} }
@@ -621,8 +680,8 @@ void Cop1::clt(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cnge(Registers& regs, u32 instr) { void Cop1::cnge(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs < ft || any_unordered(fs, ft); fcr31.compare = fs < ft || any_unordered(fs, ft);
} }
@@ -630,8 +689,8 @@ void Cop1::cnge(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cle(Registers& regs, u32 instr) { void Cop1::cle(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs <= ft; fcr31.compare = fs <= ft;
} }
@@ -639,8 +698,8 @@ void Cop1::cle(Registers& regs, u32 instr) {
template <typename T> template <typename T>
void Cop1::cngt(Registers& regs, u32 instr) { void Cop1::cngt(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
T fs = GetFGR_FS<T>(regs.cop0, FS(instr)); T fs = FGR<T>(regs.cop0.status, FS(instr));
T ft = GetFGR_FT<T>(FT(instr)); T ft = FGR<T>(regs.cop0.status, FT(instr));
checknanregs(fs, ft); checknanregs(fs, ft);
fcr31.compare = fs <= ft || any_unordered(fs, ft); fcr31.compare = fs <= ft || any_unordered(fs, ft);
} }
@@ -704,14 +763,14 @@ void Cop1::subd(Registers &regs, u32 instr) {
void Cop1::movs(Registers& regs, u32 instr) { void Cop1::movs(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
auto val = GetFGR_FR<u64>(regs.cop0, FS(instr)); auto val = FGR<u64>(regs.cop0.status, FS(instr));
SetFGR(FD(instr), val); FGR<u64>(regs.cop0.status, FD(instr)) = val;
} }
void Cop1::movd(Registers& regs, u32 instr) { void Cop1::movd(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
auto val = GetFGR_FS<double>(regs.cop0, FS(instr)); auto val = FGR<double>(regs.cop0.status, FS(instr));
SetFGR_Raw(FD(instr), val); FGR<double>(regs.cop0.status, FD(instr)) = val;
} }
void Cop1::negs(Registers &regs, u32 instr) { void Cop1::negs(Registers &regs, u32 instr) {
@@ -732,122 +791,122 @@ void Cop1::sqrtd(Registers &regs, u32 instr) {
void Cop1::roundls(Registers& regs, u32 instr) { void Cop1::roundls(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::roundld(Registers& regs, u32 instr) { void Cop1::roundld(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::roundws(Registers& regs, u32 instr) { void Cop1::roundws(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::roundwd(Registers& regs, u32 instr) { void Cop1::roundwd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::nearbyint(fs); }); CVT_OP_CheckExcept({ result = std::nearbyint(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::floorls(Registers& regs, u32 instr) { void Cop1::floorls(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::floor(fs); }); CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::floorld(Registers& regs, u32 instr) { void Cop1::floorld(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::floor(fs); }); CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::floorws(Registers& regs, u32 instr) { void Cop1::floorws(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::floor(fs); }); CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::floorwd(Registers& regs, u32 instr) { void Cop1::floorwd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::floor(fs); }); CVT_OP_CheckExcept({ result = std::floor(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::truncws(Registers& regs, u32 instr) { void Cop1::truncws(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); }); CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::truncwd(Registers& regs, u32 instr) { void Cop1::truncwd(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckWCVTArg(fs); CheckWCVTArg(fs);
s32 result; s32 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); }); CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s32>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::truncls(Registers& regs, u32 instr) { void Cop1::truncls(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<float>(regs.cop0, FS(instr)); auto fs = FGR<float>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); }); CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
void Cop1::truncld(Registers& regs, u32 instr) { void Cop1::truncld(Registers& regs, u32 instr) {
CheckFPUUsable(); CheckFPUUsable();
auto fs = GetFGR_FS<double>(regs.cop0, FS(instr)); auto fs = FGR<double>(regs.cop0.status, FS(instr));
CheckLCVTArg(fs); CheckLCVTArg(fs);
s64 result; s64 result;
CVT_OP_CheckExcept({ result = std::trunc(fs); }); CVT_OP_CheckExcept({ result = std::trunc(fs); });
CheckRound(fs, result); CheckRound(fs, result);
SetFGR(FD(instr), result); FGR<s64>(regs.cop0.status, FD(instr)) = result;
} }
template<class T> template<class T>
@@ -923,7 +982,7 @@ void Cop1::lwc1Interp(Registers& regs, Mem& mem, u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC);
} else { } else {
u32 data = mem.Read32(regs, physical); u32 data = mem.Read32(regs, physical);
SetFGR_FR<u32>(regs.cop0, FT(instr), data); FGR<u32>(regs.cop0.status, FT(instr)) = data;
} }
} }
@@ -935,7 +994,7 @@ void Cop1::swc1Interp(Registers& regs, Mem& mem, u32 instr) {
HandleTLBException(regs, addr); HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC);
} else { } else {
mem.Write32(regs, physical, GetFGR_FR<u32>(regs.cop0, FT(instr))); mem.Write32(regs, physical, FGR<u32>(regs.cop0.status, FT(instr)));
} }
} }
@@ -954,7 +1013,7 @@ void Cop1::ldc1Interp(Registers& regs, Mem& mem, u32 instr) {
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC);
} else { } else {
u64 data = mem.Read64(regs, physical); u64 data = mem.Read64(regs, physical);
SetFGR_FR<u64>(regs.cop0, FT(instr), data); FGR<u64>(regs.cop0.status, FT(instr)) = data;
} }
} }
@@ -966,28 +1025,28 @@ void Cop1::sdc1Interp(Registers& regs, Mem& mem, u32 instr) {
HandleTLBException(regs, addr); HandleTLBException(regs, addr);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC); FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC);
} else { } else {
mem.Write64(regs, physical, GetFGR_FR<u64>(regs.cop0, FT(instr))); mem.Write64(regs, physical, FGR<u64>(regs.cop0.status, FT(instr)));
} }
} }
void Cop1::mfc1(Registers& regs, u32 instr) { void Cop1::mfc1(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
regs.gpr[RT(instr)] = (s32)GetFGR_FR<u32>(regs.cop0, FS(instr)); regs.gpr[RT(instr)] = FGR<s32>(regs.cop0.status, FS(instr));
} }
void Cop1::dmfc1(Registers& regs, u32 instr) { void Cop1::dmfc1(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
regs.gpr[RT(instr)] = (s64)GetFGR_FR<u64>(regs.cop0, FS(instr)); regs.gpr[RT(instr)] = FGR<s64>(regs.cop0.status, FS(instr));
} }
void Cop1::mtc1(Registers& regs, u32 instr) { void Cop1::mtc1(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
SetFGR_FR<u32>(regs.cop0, FS(instr), regs.gpr[RT(instr)]); FGR<u32>(regs.cop0.status, FS(instr)) = regs.gpr[RT(instr)];
} }
void Cop1::dmtc1(Registers& regs, u32 instr) { void Cop1::dmtc1(Registers& regs, u32 instr) {
CheckFPUUsable_PreserveCause(); CheckFPUUsable_PreserveCause();
SetFGR_FR<u64>(regs.cop0, FS(instr), regs.gpr[RT(instr)]); FGR<u64>(regs.cop0.status, FS(instr)) = regs.gpr[RT(instr)];
} }
} }