diff --git a/src/n64/core/cpu/decode.cpp b/src/n64/core/cpu/decode.cpp index 577bf027..c85a81a0 100644 --- a/src/n64/core/cpu/decode.cpp +++ b/src/n64/core/cpu/decode.cpp @@ -115,6 +115,9 @@ void Cpu::Exec(Mem& mem, u32 instr) { case 0x0F: lui(instr); break; case 0x10: regs.cop0.decode(regs, mem, instr); break; case 0x11: regs.cop1.decode(*this, instr); break; + case 0x12: + FireException(regs, ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); + break; case 0x14: bl(instr, regs.gpr[RS(instr)] == regs.gpr[RT(instr)]); break; case 0x15: bl(instr, regs.gpr[RS(instr)] != regs.gpr[RT(instr)]); break; case 0x16: bl(instr, regs.gpr[RS(instr)] <= 0); break; diff --git a/src/n64/core/cpu/registers/Cop1.cpp b/src/n64/core/cpu/registers/Cop1.cpp index 01153d93..84bb29bb 100644 --- a/src/n64/core/cpu/registers/Cop1.cpp +++ b/src/n64/core/cpu/registers/Cop1.cpp @@ -9,7 +9,7 @@ Cop1::Cop1() { } void Cop1::Reset() { - fcr0 = 0; + fcr0 = 0xa00; fcr31.raw = 0; memset(fgr, 0, 32 * sizeof(FGR)); } @@ -29,11 +29,11 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x00: mfc1(regs, instr); break; case 0x01: dmfc1(regs, instr); break; case 0x02: cfc1(regs, instr); break; - case 0x03: FireException(regs, ExceptionCode::FloatingPointError, 0, regs.oldPC); break; + case 0x03: FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); break; case 0x04: mtc1(regs, instr); break; case 0x05: dmtc1(regs, instr); break; case 0x06: ctc1(regs, instr); break; - case 0x07: FireException(regs, ExceptionCode::FloatingPointError, 0, regs.oldPC); break; + case 0x07: FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); break; case 0x08: switch(mask_branch) { case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break; @@ -61,6 +61,9 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x0D: truncws(regs, instr); break; case 0x0E: ceilws(regs, instr); break; case 0x0F: floorws(regs, instr); break; + case 0x20: + FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); + break; case 0x21: cvtds(regs, instr); break; case 0x24: cvtws(regs, instr); break; case 0x25: cvtls(regs, instr); break; @@ -80,7 +83,7 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x3D: ccond(regs, instr, NGE); break; case 0x3E: ccond(regs, instr, LE); break; case 0x3F: ccond(regs, instr, NGT); break; - default: util::panic("Unimplemented COP1 function S[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + default: util::panic("Unimplemented COP1 function S[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); } break; case 0x11: // d @@ -102,6 +105,9 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x0E: ceilwd(regs, instr); break; case 0x0F: floorwd(regs, instr); break; case 0x20: cvtsd(regs, instr); break; + case 0x21: + FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); + break; case 0x24: cvtwd(regs, instr); break; case 0x25: cvtld(regs, instr); break; case 0x30: ccond(regs, instr, F); break; @@ -120,7 +126,7 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x3D: ccond(regs, instr, NGE); break; case 0x3E: ccond(regs, instr, LE); break; case 0x3F: ccond(regs, instr, NGT); break; - default: util::panic("Unimplemented COP1 function D[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + default: util::panic("Unimplemented COP1 function D[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); } break; case 0x14: // w @@ -131,7 +137,10 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x06: movw(regs, instr); break; case 0x20: cvtsw(regs, instr); break; case 0x21: cvtdw(regs, instr); break; - default: util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + case 0x24: + FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); + break; + default: util::panic("Unimplemented COP1 function W[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); } break; case 0x15: // l @@ -142,7 +151,13 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x06: movl(regs, instr); break; case 0x20: cvtsl(regs, instr); break; case 0x21: cvtdl(regs, instr); break; - default: util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC); + case 0x24: + FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); + break; + case 0x25: + FireException(regs, ExceptionCode::ReservedInstruction, 1, regs.oldPC); + break; + default: util::panic("Unimplemented COP1 function L[{} {}] ({:08X}) ({:016X})", mask_fun >> 3, mask_fun & 7, instr, (u64)regs.oldPC); } break; default: util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7); diff --git a/src/n64/core/cpu/registers/Cop1.hpp b/src/n64/core/cpu/registers/Cop1.hpp index 5a8a23cb..bede7515 100644 --- a/src/n64/core/cpu/registers/Cop1.hpp +++ b/src/n64/core/cpu/registers/Cop1.hpp @@ -28,7 +28,8 @@ union FCR31 { } __attribute__((__packed__)); struct { - unsigned:7; + unsigned:2; + unsigned flag:5; unsigned enable:5; unsigned cause:6; unsigned:14; diff --git a/src/n64/core/cpu/registers/cop1instructions.cpp b/src/n64/core/cpu/registers/cop1instructions.cpp index a79d398b..8ff8938e 100644 --- a/src/n64/core/cpu/registers/cop1instructions.cpp +++ b/src/n64/core/cpu/registers/cop1instructions.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include @@ -21,6 +20,41 @@ inline int PushRoundingMode(const FCR31& fcr31) { #define PUSHROUNDINGMODE int og = PushRoundingMode(fcr31) #define POPROUNDINGMODE fesetround(og) +#ifdef _WIN32 +#define isnanf isnan +#define checknanregsf(fs, ft) \ + if(isnanf(fs) || isnanf(ft)) { \ + regs.cop1.fcr31.flag_invalid_operation = true; \ + regs.cop1.fcr31.cause_invalid_operation = true; \ + FireException(regs, ExceptionCode::FloatingPointError, 1, regs.oldPC); \ + return; \ + } + +#define checknanregsd(fs, ft) \ + if(isnan(fs) || isnan(ft)) { \ + regs.cop1.fcr31.flag_invalid_operation = true; \ + regs.cop1.fcr31.cause_invalid_operation = true; \ + FireException(regs, ExceptionCode::FloatingPointError, 1, regs.oldPC); \ + return; \ + } +#else +#define checknanregsf(fs, ft) \ + if(isnanf(fs) || isnanf(ft)) { \ + regs.cop1.fcr31.flag_invalid_operation = true; \ + regs.cop1.fcr31.cause_invalid_operation = true; \ + FireException(regs, ExceptionCode::FloatingPointError, 1, regs.oldPC); \ + return; \ + } + +#define checknanregsd(fs, ft) \ + if(isnan(fs) || isnan(ft)) { \ + regs.cop1.fcr31.flag_invalid_operation = true; \ + regs.cop1.fcr31.cause_invalid_operation = true; \ + FireException(regs, ExceptionCode::FloatingPointError, 1, regs.oldPC); \ + return; \ + } +#endif + void Cop1::absd(Registers& regs, u32 instr) { double fs = GetCop1Reg(regs.cop0, FS(instr)); SetCop1Reg(regs.cop0, FD(instr), fabs(fs)); @@ -44,6 +78,7 @@ void Cop1::absl(Registers& regs, u32 instr) { void Cop1::adds(Registers& regs, u32 instr) { float fs = GetCop1Reg(regs.cop0, FS(instr)); float ft = GetCop1Reg(regs.cop0, FT(instr)); + checknanregsf(fs, ft) float result = fs + ft; SetCop1Reg(regs.cop0, FD(instr), result); } @@ -51,6 +86,7 @@ void Cop1::adds(Registers& regs, u32 instr) { void Cop1::addd(Registers& regs, u32 instr) { double fs = GetCop1Reg(regs.cop0, FS(instr)); double ft = GetCop1Reg(regs.cop0, FT(instr)); + checknanregsf(fs, ft) double result = fs + ft; SetCop1Reg(regs.cop0, FD(instr), result); } @@ -83,8 +119,8 @@ void Cop1::cfc1(Registers& regs, u32 instr) const { u8 fd = FD(instr); s32 val = 0; switch(fd) { - case 0: val = (s32)fcr0; break; - case 31: val = (s32)fcr31.raw; break; + case 0: val = fcr0; break; + case 31: val = fcr31.raw; break; default: util::panic("Undefined CFC1 with rd != 0 or 31\n"); } regs.gpr[RT(instr)] = val; @@ -228,7 +264,8 @@ inline bool CalculateCondition(Registers& regs, T fs, T ft, CompConds cond) { if(std::isnan(fs) || std::isnan(ft)) { regs.cop1.fcr31.flag_invalid_operation = true; regs.cop1.fcr31.cause_invalid_operation = true; - FireException(regs, ExceptionCode::FloatingPointError, 0, regs.oldPC); + FireException(regs, ExceptionCode::FloatingPointError, 1, regs.oldPC); + return false; } return CalculateCondition(regs, fs, ft, static_cast(cond - 8));