From 6ad8d00a926e0fc518922f2b4f0244eb235408cf Mon Sep 17 00:00:00 2001 From: CocoSimone Date: Sat, 24 Sep 2022 20:41:54 +0200 Subject: [PATCH] More FPU exceptions --- resources/game_db.json | 47 +-- src/n64/core/Cpu.cpp | 57 ---- src/n64/core/Cpu.hpp | 21 -- src/n64/core/Mem.cpp | 2 +- src/n64/core/RSP.cpp | 7 +- src/n64/core/cpu/instructions.cpp | 19 +- src/n64/core/cpu/registers/Cop0.cpp | 57 ++++ src/n64/core/cpu/registers/Cop0.hpp | 23 +- src/n64/core/cpu/registers/Cop1.cpp | 66 ++-- src/n64/core/cpu/registers/Cop1.hpp | 68 ++--- .../core/cpu/registers/cop1instructions.cpp | 283 ++++++++++-------- src/n64/core/rsp/instructions.cpp | 18 +- 12 files changed, 360 insertions(+), 308 deletions(-) diff --git a/resources/game_db.json b/resources/game_db.json index 079be31b..f12a96e8 100644 --- a/resources/game_db.json +++ b/resources/game_db.json @@ -119,6 +119,9 @@ "6d25b36f":{ "name": "All-Star Baseball 99 (USA)" }, + "46239627": { + "name": "Animal Forest (Japan)" + }, "600bc49e":{ "name": "Armorines - Project S.W.A.R.M. (Europe)" }, @@ -741,7 +744,7 @@ "name": "F-Zero X (USA)" }, "68fe1cec":{ - "name": "F-Zero X (USA) (Beta) (The Legend of Zelda Ocarina of Time Overdump)" + "name": "F-Zero X (USA) (Beta) (The Legend of Zelda - Ocarina of Time Overdump)" }, "ed750623":{ "name": "F1 Pole Position 64 (Europe) (En,Fr,De)" @@ -1266,67 +1269,67 @@ "name": "Last Legion UX (Japan)" }, "9ead1608":{ - "name": "Legend of Zelda, The - Majora's Mask (Europe) (En,Fr,De,Es)" + "name": "The Legend of Zelda - Majora's Mask (Europe) (En,Fr,De,Es)" }, "e2e6823d":{ - "name": "Legend of Zelda, The - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1)" + "name": "The Legend of Zelda - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1)" }, "b428d8a7":{ - "name": "Legend of Zelda, The - Majora's Mask (USA)" + "name": "The Legend of Zelda - Majora's Mask (USA)" }, "dcc110a0":{ - "name": "Legend of Zelda, The - Majora's Mask (USA) (Demo) (Kiosk)" + "name": "The Legend of Zelda - Majora's Mask (USA) (Demo) (Kiosk)" }, "04ea55ea":{ - "name": "Legend of Zelda, The - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1) (Debug)" + "name": "The Legend of Zelda - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1) (Debug)" }, "b008458f":{ - "name": "Legend of Zelda, The - Majora's Mask (USA) (GameCube)" + "name": "The Legend of Zelda - Majora's Mask (USA) (GameCube)" }, "12836e19":{ - "name": "Legend of Zelda, The - Majora's Mask (Europe) (GameCube)" + "name": "The Legend of Zelda - Majora's Mask (Europe) (GameCube)" }, "a83abf72":{ - "name": "Legend of Zelda, The - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1) (Wii Virtual Console)" + "name": "The Legend of Zelda - Majora's Mask (Europe) (En,Fr,De,Es) (Rev 1) (Wii Virtual Console)" }, "946fd0f7":{ - "name": "Legend of Zelda, The - Ocarina of Time (Europe) (En,Fr,De)" + "name": "The Legend of Zelda - Ocarina of Time (Europe) (En,Fr,De)" }, "a108f6e3":{ - "name": "Legend of Zelda, The - Ocarina of Time (Europe) (En,Fr,De) (Rev 1)" + "name": "The Legend of Zelda - Ocarina of Time (Europe) (En,Fr,De) (Rev 1)" }, "cd16c529":{ - "name": "Legend of Zelda, The - Ocarina of Time (USA)" + "name": "The Legend of Zelda - Ocarina of Time (USA)" }, "3fd2151e":{ - "name": "Legend of Zelda, The - Ocarina of Time (USA) (Rev 1)" + "name": "The Legend of Zelda - Ocarina of Time (USA) (Rev 1)" }, "32120c23":{ - "name": "Legend of Zelda, The - Ocarina of Time (USA) (Rev 2)" + "name": "The Legend of Zelda - Ocarina of Time (USA) (Rev 2)" }, "346de3ae":{ - "name": "Legend of Zelda, The - Ocarina of Time (USA) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time (USA) (GameCube)" }, "3fbd519f":{ - "name": "Legend of Zelda, The - Ocarina of Time (Europe) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time (Europe) (GameCube)" }, "5d1b2996":{ - "name": "Legend of Zelda, The - Ocarina of Time (Europe) (Debug) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time (Europe) (Debug) (GameCube)" }, "c188acda":{ - "name": "Legend of Zelda, The - Ocarina of Time (Europe) (Beta) (2003-02-13) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time (Europe) (Beta) (2003-02-13) (GameCube)" }, "6c348aa8":{ - "name": "Legend of Zelda, The - Ocarina of Time (USA) (Beta)" + "name": "The Legend of Zelda - Ocarina of Time (USA) (Beta)" }, "a1d34e08":{ - "name": "Legend of Zelda, The - Ocarina of Time - Master Quest (Europe) (Debug) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time - Master Quest (Europe) (Debug) (GameCube)" }, "c744c4db":{ - "name": "Legend of Zelda, The - Ocarina of Time - Master Quest (USA) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time - Master Quest (USA) (GameCube)" }, "832d6449":{ - "name": "Legend of Zelda, The - Ocarina of Time - Master Quest (Europe) (GameCube)" + "name": "The Legend of Zelda - Ocarina of Time - Master Quest (Europe) (GameCube)" }, "c7d9b21c":{ "name": "LEGO Racers (Europe) (En,Fr,De,Es,It,Nl,Sv,No,Da,Fi)" diff --git a/src/n64/core/Cpu.cpp b/src/n64/core/Cpu.cpp index de0ce05e..15c27508 100644 --- a/src/n64/core/Cpu.cpp +++ b/src/n64/core/Cpu.cpp @@ -27,63 +27,6 @@ inline void CheckCompareInterrupt(MI& mi, Registers& regs) { } } -inline bool Is64BitAddressing(Cop0& cp0, u64 addr) { - u8 region = (addr >> 62) & 3; - switch(region) { - case 0b00: return cp0.status.ux; - case 0b01: return cp0.status.sx; - case 0b11: return cp0.status.kx; - default: return false; - } -} - -void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { - bool old_exl = regs.cop0.status.exl; - - if(!regs.cop0.status.exl) { - if(regs.prevDelaySlot) { - regs.cop0.cause.branchDelay = true; - pc -= 4; - } else { - regs.cop0.cause.branchDelay = false; - } - - regs.cop0.status.exl = true; - regs.cop0.EPC = pc; - } - - regs.cop0.cause.copError = cop; - regs.cop0.cause.exceptionCode = code; - - s64 exceptionVector = 0; - - if(regs.cop0.status.bev) { - util::panic("BEV bit set!\n"); - } else { - switch(code) { - case Interrupt: case TLBModification: - case AddressErrorLoad: case AddressErrorStore: - case InstructionBusError: case DataBusError: - case Syscall: case Breakpoint: - case ReservedInstruction: case CoprocessorUnusable: - case Overflow: case Trap: - case FloatingPointError: case Watch: - regs.SetPC(s64(s32(0x80000180))); - break; - case TLBLoad: case TLBStore: - if(old_exl || regs.cop0.tlbError == INVALID) { - regs.SetPC(s64(s32(0x80000180))); - } else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) { - regs.SetPC(s64(s32(0x80000080))); - } else { - regs.SetPC(s64(s32(0x80000000))); - } - break; - default: util::panic("Unhandled exception! {}\n", code); - } - } -} - inline void HandleInterrupt(Registers& regs) { if(ShouldServiceInterrupt(regs)) { FireException(regs, ExceptionCode::Interrupt, 0, regs.pc); diff --git a/src/n64/core/Cpu.hpp b/src/n64/core/Cpu.hpp index 1cd1294b..f6766946 100644 --- a/src/n64/core/Cpu.hpp +++ b/src/n64/core/Cpu.hpp @@ -117,25 +117,4 @@ private: void xor_(u32); void xori(u32); }; - -enum ExceptionCode : u8 { - Interrupt, - TLBModification, - TLBLoad, - TLBStore, - AddressErrorLoad, - AddressErrorStore, - InstructionBusError, - DataBusError, - Syscall, - Breakpoint, - ReservedInstruction, - CoprocessorUnusable, - Overflow, - Trap, - FloatingPointError = 15, - Watch = 23 -}; - -void FireException(Registers& regs_, ExceptionCode code, int cop, s64 pc); } diff --git a/src/n64/core/Mem.cpp b/src/n64/core/Mem.cpp index 7e851101..49bc3758 100644 --- a/src/n64/core/Mem.cpp +++ b/src/n64/core/Mem.cpp @@ -50,7 +50,7 @@ bool MapVAddr(Registers& regs, TLBAccessType accessType, u32 vaddr, u32& paddr) switch(vaddr >> 29) { case 0 ... 3: case 7: - return ProbeTLB(regs, accessType, s64(s32(vaddr)), paddr, nullptr); + return ProbeTLB(regs, accessType, vaddr, paddr, nullptr); case 4 ... 5: return true; case 6: util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})\n", vaddr); default: diff --git a/src/n64/core/RSP.cpp b/src/n64/core/RSP.cpp index 36dbe548..aa8362c7 100644 --- a/src/n64/core/RSP.cpp +++ b/src/n64/core/RSP.cpp @@ -46,10 +46,10 @@ auto RSP::Read(u32 addr) -> u32{ case 0x04040008: case 0x0404000C: return spDMALen.raw; case 0x04040010: return spStatus.raw; + case 0x04040014: return spStatus.dmaFull; case 0x04040018: return 0; case 0x0404001C: return AcquireSemaphore(); - case 0x04080000: - return pc & 0xFFC; + case 0x04080000: return pc & 0xFFC; default: util::panic("Unimplemented SP register read {:08X}\n", addr); } } @@ -133,7 +133,8 @@ void RSP::Write(Mem& mem, Registers& regs, u32 addr, u32 value) { pc = value & 0xFFC; nextPC = value & 0xFFC; } break; - default: util::panic("Unimplemented SP register write {:08X}, val: {:08X}\n", addr, value); + default: + util::panic("Unimplemented SP register write {:08X}, val: {:08X}\n", addr, value); } } diff --git a/src/n64/core/cpu/instructions.cpp b/src/n64/core/cpu/instructions.cpp index 1e07426a..fa609fca 100644 --- a/src/n64/core/cpu/instructions.cpp +++ b/src/n64/core/cpu/instructions.cpp @@ -7,13 +7,12 @@ #define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) namespace n64 { - void Cpu::add(u32 instr) { u32 rs = (s32)regs.gpr[RS(instr)]; u32 rt = (s32)regs.gpr[RT(instr)]; u32 result = rs + rt; if(check_signed_overflow(rs, rt, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.gpr[RD(instr)] = s32(result); } @@ -27,13 +26,13 @@ void Cpu::addu(u32 instr) { } void Cpu::addi(u32 instr) { - u32 rs = (s32)regs.gpr[RS(instr)]; + u32 rs = regs.gpr[RS(instr)]; u32 imm = s32(s16(instr)); u32 result = rs + imm; if(check_signed_overflow(rs, imm, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { - regs.gpr[RD(instr)] = s32(result); + regs.gpr[RT(instr)] = s32(result); } } @@ -49,7 +48,7 @@ void Cpu::dadd(u32 instr) { u64 rt = regs.gpr[RT(instr)]; u64 result = rt + rs; if(check_signed_overflow(rs, rt, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.gpr[RD(instr)] = result; } @@ -66,9 +65,9 @@ void Cpu::daddi(u32 instr) { u64 rs = regs.gpr[RS(instr)]; u64 result = imm + rs; if(check_signed_overflow(rs, imm, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { - regs.gpr[RD(instr)] = result; + regs.gpr[RT(instr)] = result; } } @@ -658,7 +657,7 @@ void Cpu::dsub(u32 instr) { s64 rs = regs.gpr[RS(instr)]; s64 result = rs - rt; if(check_signed_underflow(rs, rt, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.gpr[RD(instr)] = result; } @@ -676,7 +675,7 @@ void Cpu::sub(u32 instr) { s32 rs = regs.gpr[RS(instr)]; s32 result = rs - rt; if(check_signed_underflow(rs, rt, result)) { - FireException(regs, Overflow, 0, regs.oldPC); + FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC); } else { regs.gpr[RD(instr)] = result; } diff --git a/src/n64/core/cpu/registers/Cop0.cpp b/src/n64/core/cpu/registers/Cop0.cpp index 0d3aa6da..760c9039 100644 --- a/src/n64/core/cpu/registers/Cop0.cpp +++ b/src/n64/core/cpu/registers/Cop0.cpp @@ -232,6 +232,63 @@ bool ProbeTLB(Registers& regs, TLBAccessType access_type, s64 vaddr, u32& paddr, return true; } +inline bool Is64BitAddressing(Cop0& cp0, u64 addr) { + u8 region = (addr >> 62) & 3; + switch(region) { + case 0b00: return cp0.status.ux; + case 0b01: return cp0.status.sx; + case 0b11: return cp0.status.kx; + default: return false; + } +} + +void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc) { + bool old_exl = regs.cop0.status.exl; + + if(!regs.cop0.status.exl) { + if(regs.prevDelaySlot) { + regs.cop0.cause.branchDelay = true; + pc -= 4; + } else { + regs.cop0.cause.branchDelay = false; + } + + regs.cop0.status.exl = true; + regs.cop0.EPC = pc; + } + + regs.cop0.cause.copError = cop; + regs.cop0.cause.exceptionCode = static_cast(code); + + s64 exceptionVector = 0; + + if(regs.cop0.status.bev) { + util::panic("BEV bit set!\n"); + } else { + switch(code) { + case ExceptionCode::Interrupt: case ExceptionCode::TLBModification: + case ExceptionCode::AddressErrorLoad: case ExceptionCode::AddressErrorStore: + case ExceptionCode::InstructionBusError: case ExceptionCode::DataBusError: + case ExceptionCode::Syscall: case ExceptionCode::Breakpoint: + case ExceptionCode::ReservedInstruction: case ExceptionCode::CoprocessorUnusable: + case ExceptionCode::Overflow: case ExceptionCode::Trap: + case ExceptionCode::FloatingPointError: case ExceptionCode::Watch: + regs.SetPC(s64(s32(0x80000180))); + break; + case ExceptionCode::TLBLoad: case ExceptionCode::TLBStore: + if(old_exl || regs.cop0.tlbError == INVALID) { + regs.SetPC(s64(s32(0x80000180))); + } else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) { + regs.SetPC(s64(s32(0x80000080))); + } else { + regs.SetPC(s64(s32(0x80000000))); + } + break; + default: util::panic("Unhandled exception! {}\n", static_cast(code)); + } + } +} + void HandleTLBException(Registers& regs, u64 vaddr) { u64 vpn2 = (vaddr >> 13) & 0x7FFFF; u64 xvpn2 = (vaddr >> 13) & 0x7FFFFFF; diff --git a/src/n64/core/cpu/registers/Cop0.hpp b/src/n64/core/cpu/registers/Cop0.hpp index 157d7060..b6157a10 100644 --- a/src/n64/core/cpu/registers/Cop0.hpp +++ b/src/n64/core/cpu/registers/Cop0.hpp @@ -157,6 +157,27 @@ enum TLBError : u8 { DISALLOWED_ADDRESS }; +enum class ExceptionCode : u8 { + Interrupt, + TLBModification, + TLBLoad, + TLBStore, + AddressErrorLoad, + AddressErrorStore, + InstructionBusError, + DataBusError, + Syscall, + Breakpoint, + ReservedInstruction, + CoprocessorUnusable, + Overflow, + Trap, + FloatingPointError = 15, + Watch = 23 +}; + +void FireException(Registers& regs, ExceptionCode code, int cop, s64 pc); + union Cop0Context { u64 raw; struct { @@ -244,7 +265,7 @@ private: }; struct Registers; -enum ExceptionCode : u8; +enum class ExceptionCode : u8; TLBEntry* TLBTryMatch(Registers& regs, s64 vaddr, int* match); bool ProbeTLB(Registers& regs, TLBAccessType access_type, s64 vaddr, u32& paddr, int* match); diff --git a/src/n64/core/cpu/registers/Cop1.cpp b/src/n64/core/cpu/registers/Cop1.cpp index f9b972ef..01153d93 100644 --- a/src/n64/core/cpu/registers/Cop1.cpp +++ b/src/n64/core/cpu/registers/Cop1.cpp @@ -29,9 +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 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 0x08: switch(mask_branch) { case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break; @@ -62,22 +64,22 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x21: cvtds(regs, instr); break; case 0x24: cvtws(regs, instr); break; case 0x25: cvtls(regs, instr); break; - case 0x30: cconds(regs, instr, F); break; - case 0x31: cconds(regs, instr, UN); break; - case 0x32: cconds(regs, instr, EQ); break; - case 0x33: cconds(regs, instr, UEQ); break; - case 0x34: cconds(regs, instr, OLT); break; - case 0x35: cconds(regs, instr, ULT); break; - case 0x36: cconds(regs, instr, OLE); break; - case 0x37: cconds(regs, instr, ULE); break; - case 0x38: cconds(regs, instr, SF); break; - case 0x39: cconds(regs, instr, NGLE); break; - case 0x3A: cconds(regs, instr, SEQ); break; - case 0x3B: cconds(regs, instr, NGL); break; - case 0x3C: cconds(regs, instr, LT); break; - case 0x3D: cconds(regs, instr, NGE); break; - case 0x3E: cconds(regs, instr, LE); break; - case 0x3F: cconds(regs, instr, NGT); break; + case 0x30: ccond(regs, instr, F); break; + case 0x31: ccond(regs, instr, UN); break; + case 0x32: ccond(regs, instr, EQ); break; + case 0x33: ccond(regs, instr, UEQ); break; + case 0x34: ccond(regs, instr, OLT); break; + case 0x35: ccond(regs, instr, ULT); break; + case 0x36: ccond(regs, instr, OLE); break; + case 0x37: ccond(regs, instr, ULE); break; + case 0x38: ccond(regs, instr, SF); break; + case 0x39: ccond(regs, instr, NGLE); break; + case 0x3A: ccond(regs, instr, SEQ); break; + case 0x3B: ccond(regs, instr, NGL); break; + case 0x3C: ccond(regs, instr, LT); break; + 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); } break; @@ -102,22 +104,22 @@ void Cop1::decode(Cpu& cpu, u32 instr) { case 0x20: cvtsd(regs, instr); break; case 0x24: cvtwd(regs, instr); break; case 0x25: cvtld(regs, instr); break; - case 0x30: ccondd(regs, instr, F); break; - case 0x31: ccondd(regs, instr, UN); break; - case 0x32: ccondd(regs, instr, EQ); break; - case 0x33: ccondd(regs, instr, UEQ); break; - case 0x34: ccondd(regs, instr, OLT); break; - case 0x35: ccondd(regs, instr, ULT); break; - case 0x36: ccondd(regs, instr, OLE); break; - case 0x37: ccondd(regs, instr, ULE); break; - case 0x38: ccondd(regs, instr, SF); break; - case 0x39: ccondd(regs, instr, NGLE); break; - case 0x3A: ccondd(regs, instr, SEQ); break; - case 0x3B: ccondd(regs, instr, NGL); break; - case 0x3C: ccondd(regs, instr, LT); break; - case 0x3D: ccondd(regs, instr, NGE); break; - case 0x3E: ccondd(regs, instr, LE); break; - case 0x3F: ccondd(regs, instr, NGT); break; + case 0x30: ccond(regs, instr, F); break; + case 0x31: ccond(regs, instr, UN); break; + case 0x32: ccond(regs, instr, EQ); break; + case 0x33: ccond(regs, instr, UEQ); break; + case 0x34: ccond(regs, instr, OLT); break; + case 0x35: ccond(regs, instr, ULT); break; + case 0x36: ccond(regs, instr, OLE); break; + case 0x37: ccond(regs, instr, ULE); break; + case 0x38: ccond(regs, instr, SF); break; + case 0x39: ccond(regs, instr, NGLE); break; + case 0x3A: ccond(regs, instr, SEQ); break; + case 0x3B: ccond(regs, instr, NGL); break; + case 0x3C: ccond(regs, instr, LT); break; + 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); } break; diff --git a/src/n64/core/cpu/registers/Cop1.hpp b/src/n64/core/cpu/registers/Cop1.hpp index 74c085ef..5a8a23cb 100644 --- a/src/n64/core/cpu/registers/Cop1.hpp +++ b/src/n64/core/cpu/registers/Cop1.hpp @@ -37,6 +37,13 @@ union FCR31 { u32 raw; }; +enum CompConds { + F, UN, EQ, UEQ, + OLT, ULT, OLE, ULE, + SF, NGLE, SEQ, NGL, + LT, NGE, LE, NGT +}; + union FGR { struct { s32 lo:32; @@ -100,43 +107,32 @@ private: } } - inline void SetCop1RegDouble(Cop0& cop0, u8 index, double value) { - u64 raw; - memcpy(&raw, &value, sizeof(double)); - SetReg(cop0, index, raw); + template + inline void SetCop1Reg(Cop0& cop0, u8 index, T value) { + if constexpr (sizeof(T) == 4) { + u32 raw; + memcpy(&raw, &value, sizeof(T)); + SetReg(cop0, index, raw); + } else if constexpr (sizeof(T) == 8) { + u64 raw; + memcpy(&raw, &value, sizeof(T)); + SetReg(cop0, index, raw); + } } - inline double GetCop1RegDouble(Cop0& cop0, u8 index) { - double doublevalue; - u64 raw = GetReg(cop0, index); - memcpy(&doublevalue, &raw, sizeof(double)); - return doublevalue; + template + inline T GetCop1Reg(Cop0& cop0, u8 index) { + T value; + if constexpr (sizeof(T) == 4) { + u32 raw = GetReg(cop0, index); + memcpy(&value, &raw, sizeof(T)); + } else if constexpr (sizeof(T) == 8) { + u64 raw = GetReg(cop0, index); + memcpy(&value, &raw, sizeof(T)); + } + return value; } - inline void SetCop1RegFloat(Cop0& cop0, u8 index, float value) { - u32 raw; - memcpy(&raw, &value, sizeof(float)); - SetReg(cop0, index, raw); - } - - inline float GetCop1RegFloat(Cop0& cop0, u8 index) { - u32 raw = GetReg(cop0, index); - float floatvalue; - memcpy(&floatvalue, &raw, sizeof(float)); - return floatvalue; - } - - enum CompConds { - T, UN, EQ, UEQ, - OLT, ULT, OLE, ULE, - SF, NGLE, SEQ, NGL, - LT, NGE, LE, NGT, - F, OR, NEQ, OLG, - UGE, OGE, UGT, OGT, - ST, GLE, SNE, GL, - NLT, GE, NLE, GT - }; - void absd(Registers&, u32 instr); void abss(Registers&, u32 instr); void absw(Registers&, u32 instr); @@ -151,7 +147,7 @@ private: void ceilws(Registers&, u32 instr); void ceilld(Registers&, u32 instr); void ceilwd(Registers&, u32 instr); - void cfc1(Registers&, u32 instr); + void cfc1(Registers&, u32 instr) const; void ctc1(Registers&, u32 instr); void roundls(Registers&, u32 instr); void roundld(Registers&, u32 instr); @@ -171,8 +167,8 @@ private: void cvtld(Registers&, u32 instr); void cvtdl(Registers&, u32 instr); void cvtsl(Registers&, u32 instr); - void ccondd(Registers&, u32 instr, CompConds); - void cconds(Registers&, u32 instr, CompConds); + template + void ccond(Registers&, u32 instr, CompConds); void divs(Registers&, u32 instr); void divd(Registers&, u32 instr); void muls(Registers&, u32 instr); diff --git a/src/n64/core/cpu/registers/cop1instructions.cpp b/src/n64/core/cpu/registers/cop1instructions.cpp index 8c16de54..23e9729d 100644 --- a/src/n64/core/cpu/registers/cop1instructions.cpp +++ b/src/n64/core/cpu/registers/cop1instructions.cpp @@ -6,13 +6,13 @@ namespace n64 { void Cop1::absd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - SetCop1RegDouble(regs.cop0, FD(instr), fabs(fs)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + SetCop1Reg(regs.cop0, FD(instr), fabs(fs)); } void Cop1::abss(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - SetCop1RegFloat(regs.cop0, FD(instr), fabsf(fs)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + SetCop1Reg(regs.cop0, FD(instr), fabsf(fs)); } void Cop1::absw(Registers& regs, u32 instr) { @@ -26,44 +26,44 @@ void Cop1::absl(Registers& regs, u32 instr) { } void Cop1::adds(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - float ft = GetCop1RegFloat(regs.cop0, FT(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + float ft = GetCop1Reg(regs.cop0, FT(instr)); float result = fs + ft; - SetCop1RegFloat(regs.cop0, FD(instr), result); + SetCop1Reg(regs.cop0, FD(instr), result); } void Cop1::addd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - double ft = GetCop1RegDouble(regs.cop0, FT(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + double ft = GetCop1Reg(regs.cop0, FT(instr)); double result = fs + ft; - SetCop1RegDouble(regs.cop0, FD(instr), result); + SetCop1Reg(regs.cop0, FD(instr), result); } void Cop1::ceills(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); s64 result = ceilf(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::ceilws(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); s32 result = ceilf(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::ceilld(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); s64 result = ceil(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::ceilwd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); s32 result = ceil(fs); SetReg(regs.cop0, FD(instr), result); } -void Cop1::cfc1(Registers& regs, u32 instr) { +void Cop1::cfc1(Registers& regs, u32 instr) const { u8 fd = FD(instr); s32 val = 0; switch(fd) { @@ -78,7 +78,7 @@ void Cop1::ctc1(Registers& regs, u32 instr) { u8 fs = FS(instr); u32 val = regs.gpr[RT(instr)]; switch(fs) { - case 0: util::panic("CTC1 attempt to write to FCR0 which is read only!\n"); + case 0: break; case 31: { val &= 0x183ffff; fcr31.raw = val; @@ -88,10 +88,10 @@ void Cop1::ctc1(Registers& regs, u32 instr) { } void Cop1::cvtds(Registers& regs, u32 instr) { - SetCop1RegDouble( + SetCop1Reg( regs.cop0, FD(instr), - GetCop1RegFloat( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -99,10 +99,10 @@ void Cop1::cvtds(Registers& regs, u32 instr) { } void Cop1::cvtsd(Registers& regs, u32 instr) { - SetCop1RegFloat( + SetCop1Reg( regs.cop0, FD(instr), - GetCop1RegDouble( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -113,7 +113,7 @@ void Cop1::cvtwd(Registers& regs, u32 instr) { SetReg( regs.cop0, FD(instr), - GetCop1RegDouble( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -124,7 +124,7 @@ void Cop1::cvtws(Registers& regs, u32 instr) { SetReg( regs.cop0, FD(instr), - GetCop1RegFloat( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -135,7 +135,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) { SetReg( regs.cop0, FD(instr), - GetCop1RegFloat( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -143,7 +143,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) { } void Cop1::cvtsl(Registers& regs, u32 instr) { - SetCop1RegFloat( + SetCop1Reg( regs.cop0, FD(instr), (s64)GetReg( @@ -154,7 +154,7 @@ void Cop1::cvtsl(Registers& regs, u32 instr) { } void Cop1::cvtdw(Registers& regs, u32 instr) { - SetCop1RegDouble( + SetCop1Reg( regs.cop0, FD(instr), (s32)GetReg( @@ -165,7 +165,7 @@ void Cop1::cvtdw(Registers& regs, u32 instr) { } void Cop1::cvtsw(Registers& regs, u32 instr) { - SetCop1RegFloat( + SetCop1Reg( regs.cop0, FD(instr), (s32)GetReg( @@ -176,7 +176,7 @@ void Cop1::cvtsw(Registers& regs, u32 instr) { } void Cop1::cvtdl(Registers& regs, u32 instr) { - SetCop1RegDouble( + SetCop1Reg( regs.cop0, FD(instr), (s64)GetReg( @@ -188,77 +188,62 @@ void Cop1::cvtdl(Registers& regs, u32 instr) { void Cop1::cvtld(Registers& regs, u32 instr) { SetReg( - regs.cop0, + regs.cop0, FD(instr), - GetCop1RegDouble( - regs.cop0, + GetCop1Reg( + regs.cop0, FS(instr) ) ); } -void Cop1::ccondd(Registers& regs, u32 instr, CompConds cond) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - double ft = GetCop1RegDouble(regs.cop0, FT(instr)); - - bool less, equal, unordered; - if(std::isnan(fs) || std::isnan(ft)) { - less = false; - equal = false; - unordered = true; - } else { - less = fs < ft; - equal = fs == ft; - unordered = false; +template +inline bool CalculateCondition(T fs, T ft, u8 cond) { + switch(cond) { + case F: return false; + case UN: return std::isnan(fs) || std::isnan(ft); + case EQ: return fs == ft; + case UEQ: return (std::isnan(fs) || std::isnan(ft)) || (fs == ft); + case OLT: return (!std::isnan(fs) && !std::isnan(ft)) && (fs < ft); + case ULT: return (std::isnan(fs) || std::isnan(ft)) || (fs < ft); + case OLE: return (!std::isnan(fs) && !std::isnan(ft)) && (fs <= ft); + default: return CalculateCondition(fs, ft, cond - 8); } - - bool condition = ((cond >> 2) && less) || ((cond >> 1) && equal) || ((cond & 1) && unordered); - - fcr31.compare = condition; } -void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - float ft = GetCop1RegFloat(regs.cop0, FT(instr)); +template +void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) { + T fs = GetCop1Reg(regs.cop0, FS(instr)); + T ft = GetCop1Reg(regs.cop0, FT(instr)); - bool less, equal, unordered; - if(std::isnan(fs) || std::isnan(ft)) { - less = false; - equal = false; - unordered = true; - } else { - less = fs < ft; - equal = fs == ft; - unordered = false; - } - - bool condition = ((cond >> 2) && less) || ((cond >> 1) && equal) || ((cond & 1) && unordered); - - fcr31.compare = condition; + fcr31.compare = CalculateCondition(fs, ft, cond); } +template void Cop1::ccond(Registers& regs, u32 instr, CompConds cond); +template void Cop1::ccond(Registers& regs, u32 instr, CompConds cond); + void Cop1::divs(Registers ®s, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - float ft = GetCop1RegFloat(regs.cop0, FT(instr)); - SetCop1RegFloat(regs.cop0, FD(instr), fs / ft); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + float ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs / ft); } void Cop1::divd(Registers ®s, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - double ft = GetCop1RegDouble(regs.cop0, FT(instr)); - SetCop1RegDouble(regs.cop0, FD(instr), fs / ft); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + double ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs / ft); } void Cop1::muls(Registers ®s, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - float ft = GetCop1RegFloat(regs.cop0, FT(instr)); - SetCop1RegFloat(regs.cop0, FD(instr), fs * ft); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + float ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs * ft); } void Cop1::muld(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - double ft = GetCop1RegDouble(regs.cop0, FT(instr)); - SetCop1RegDouble(regs.cop0, FD(instr), fs * ft); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + double ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs * ft); } void Cop1::mulw(Registers ®s, u32 instr) { @@ -274,15 +259,15 @@ void Cop1::mull(Registers ®s, u32 instr) { } void Cop1::subs(Registers ®s, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - float ft = GetCop1RegFloat(regs.cop0, FT(instr)); - SetCop1RegFloat(regs.cop0, FD(instr), fs - ft); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + float ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs - ft); } void Cop1::subd(Registers ®s, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - double ft = GetCop1RegDouble(regs.cop0, FT(instr)); - SetCop1RegDouble(regs.cop0, FD(instr), fs - ft); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + double ft = GetCop1Reg(regs.cop0, FT(instr)); + SetCop1Reg(regs.cop0, FD(instr), fs - ft); } void Cop1::subw(Registers ®s, u32 instr) { @@ -298,10 +283,10 @@ void Cop1::subl(Registers ®s, u32 instr) { } void Cop1::movs(Registers& regs, u32 instr) { - SetCop1RegFloat( + SetCop1Reg( regs.cop0, FD(instr), - GetCop1RegFloat( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -309,10 +294,10 @@ void Cop1::movs(Registers& regs, u32 instr) { } void Cop1::movd(Registers& regs, u32 instr) { - SetCop1RegDouble( + SetCop1Reg( regs.cop0, FD(instr), - GetCop1RegDouble( + GetCop1Reg( regs.cop0, FS(instr) ) @@ -342,10 +327,10 @@ void Cop1::movl(Registers& regs, u32 instr) { } void Cop1::negs(Registers ®s, u32 instr) { - SetCop1RegFloat( + SetCop1Reg( regs.cop0, FD(instr), - -GetCop1RegFloat( + -GetCop1Reg( regs.cop0, FS(instr) ) @@ -353,10 +338,10 @@ void Cop1::negs(Registers ®s, u32 instr) { } void Cop1::negd(Registers ®s, u32 instr) { - SetCop1RegDouble( + SetCop1Reg( regs.cop0, FD(instr), - -GetCop1RegDouble( + -GetCop1Reg( regs.cop0, FS(instr) ) @@ -364,101 +349,161 @@ void Cop1::negd(Registers ®s, u32 instr) { } void Cop1::sqrts(Registers ®s, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); - SetCop1RegFloat(regs.cop0, FD(instr), sqrtf(fs)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); + SetCop1Reg(regs.cop0, FD(instr), sqrtf(fs)); } void Cop1::sqrtd(Registers ®s, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); - SetCop1RegDouble(regs.cop0, FD(instr), sqrt(fs)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); + SetCop1Reg(regs.cop0, FD(instr), sqrt(fs)); } void Cop1::roundls(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s32)roundf(fs)); } void Cop1::roundld(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)round(fs)); } void Cop1::roundws(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s32)roundf(fs)); } void Cop1::roundwd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)round(fs)); } void Cop1::floorls(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)floorf(fs)); } void Cop1::floorld(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)floor(fs)); } void Cop1::floorws(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)floorf(fs)); } void Cop1::floorwd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); SetReg(regs.cop0, FD(instr), (s64)floor(fs)); } void Cop1::lwc1(Registers& regs, Mem& mem, u32 instr) { - u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; - u32 data = mem.Read32(regs, addr, regs.oldPC); - SetReg(regs.cop0, FT(instr), data); + if(!regs.cop0.status.cu1) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return; + } + + u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + if(addr & 3) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + } + + u32 physical; + if(!MapVAddr(regs, LOAD, addr, physical)) { + HandleTLBException(regs, addr); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC); + } else { + u32 data = mem.Read32(regs, physical, regs.oldPC); + SetReg(regs.cop0, FT(instr), data); + } } void Cop1::swc1(Registers& regs, Mem& mem, u32 instr) { - u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; - mem.Write32(regs, addr, GetReg(regs.cop0, FT(instr)), regs.oldPC); + if(!regs.cop0.status.cu1) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return; + } + + u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + if(addr & 3) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + } + + u32 physical; + if(!MapVAddr(regs, STORE, addr, physical)) { + HandleTLBException(regs, addr); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC); + } else { + mem.Write32(regs, physical, GetReg(regs.cop0, FT(instr)), regs.oldPC); + } } void Cop1::ldc1(Registers& regs, Mem& mem, u32 instr) { - u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; - u64 data = mem.Read64(regs, addr, regs.oldPC); - SetReg(regs.cop0, FT(instr), data); + if(!regs.cop0.status.cu1) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return; + } + + u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + if(addr & 7) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + } + + u32 physical; + if(!MapVAddr(regs, LOAD, addr, physical)) { + HandleTLBException(regs, addr); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC); + } else { + u64 data = mem.Read64(regs, physical, regs.oldPC); + SetReg(regs.cop0, FT(instr), data); + } +} + +void Cop1::sdc1(Registers& regs, Mem& mem, u32 instr) { + if(!regs.cop0.status.cu1) { + FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return; + } + + u64 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; + if(addr & 7) { + FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + } + + u32 physical; + if(!MapVAddr(regs, STORE, addr, physical)) { + HandleTLBException(regs, addr); + FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, STORE), 0, regs.oldPC); + } else { + mem.Write64(regs, physical, GetReg(regs.cop0, FT(instr)), regs.oldPC); + } } void Cop1::truncws(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); s32 result = (s32)truncf(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::truncwd(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); s32 result = (s32)trunc(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::truncls(Registers& regs, u32 instr) { - float fs = GetCop1RegFloat(regs.cop0, FS(instr)); + float fs = GetCop1Reg(regs.cop0, FS(instr)); s64 result = (s64)truncf(fs); SetReg(regs.cop0, FD(instr), result); } void Cop1::truncld(Registers& regs, u32 instr) { - double fs = GetCop1RegDouble(regs.cop0, FS(instr)); + double fs = GetCop1Reg(regs.cop0, FS(instr)); s64 result = (s64)trunc(fs); SetReg(regs.cop0, FD(instr), result); } -void Cop1::sdc1(Registers& regs, Mem& mem, u32 instr) { - u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; - mem.Write64(regs, addr, GetReg(regs.cop0, FT(instr)), regs.oldPC); -} - void Cop1::mfc1(Registers& regs, u32 instr) { regs.gpr[RT(instr)] = (s32)GetReg(regs.cop0, FS(instr)); } diff --git a/src/n64/core/rsp/instructions.cpp b/src/n64/core/rsp/instructions.cpp index c6bf83d3..1d5d0383 100644 --- a/src/n64/core/rsp/instructions.cpp +++ b/src/n64/core/rsp/instructions.cpp @@ -226,18 +226,24 @@ void RSP::sqv(u32 instr) { } void RSP::sllv(u32 instr) { - u8 sa = gpr[RS(instr)] & 0x1F; - gpr[RD(instr)] = (u32)gpr[RT(instr)] << sa; + u8 sa = (gpr[RS(instr)]) & 0x1F; + u32 rt = gpr[RT(instr)]; + u32 result = rt << sa; + gpr[RD(instr)] = result; } void RSP::srlv(u32 instr) { - u8 sa = gpr[RS(instr)] & 0x1F; - gpr[RD(instr)] = (u32)gpr[RT(instr)] >> sa; + u8 sa = (gpr[RS(instr)]) & 0x1F; + u32 rt = gpr[RT(instr)]; + u32 result = rt >> sa; + gpr[RD(instr)] = result; } void RSP::srav(u32 instr) { - u8 sa = gpr[RS(instr)] & 0x1F; - gpr[RD(instr)] = gpr[RT(instr)] >> sa; + u8 sa = (gpr[RS(instr)]) & 0x1F; + s32 rt = gpr[RT(instr)]; + s32 result = rt >> sa; + gpr[RD(instr)] = result; } void RSP::sll(u32 instr) {