More FPU exceptions

This commit is contained in:
CocoSimone
2022-09-24 20:41:54 +02:00
parent 19bf3e55e5
commit 6ad8d00a92
12 changed files with 360 additions and 308 deletions

View File

@@ -119,6 +119,9 @@
"6d25b36f":{ "6d25b36f":{
"name": "All-Star Baseball 99 (USA)" "name": "All-Star Baseball 99 (USA)"
}, },
"46239627": {
"name": "Animal Forest (Japan)"
},
"600bc49e":{ "600bc49e":{
"name": "Armorines - Project S.W.A.R.M. (Europe)" "name": "Armorines - Project S.W.A.R.M. (Europe)"
}, },
@@ -741,7 +744,7 @@
"name": "F-Zero X (USA)" "name": "F-Zero X (USA)"
}, },
"68fe1cec":{ "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":{ "ed750623":{
"name": "F1 Pole Position 64 (Europe) (En,Fr,De)" "name": "F1 Pole Position 64 (Europe) (En,Fr,De)"
@@ -1266,67 +1269,67 @@
"name": "Last Legion UX (Japan)" "name": "Last Legion UX (Japan)"
}, },
"9ead1608":{ "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":{ "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":{ "b428d8a7":{
"name": "Legend of Zelda, The - Majora's Mask (USA)" "name": "The Legend of Zelda - Majora's Mask (USA)"
}, },
"dcc110a0":{ "dcc110a0":{
"name": "Legend of Zelda, The - Majora's Mask (USA) (Demo) (Kiosk)" "name": "The Legend of Zelda - Majora's Mask (USA) (Demo) (Kiosk)"
}, },
"04ea55ea":{ "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":{ "b008458f":{
"name": "Legend of Zelda, The - Majora's Mask (USA) (GameCube)" "name": "The Legend of Zelda - Majora's Mask (USA) (GameCube)"
}, },
"12836e19":{ "12836e19":{
"name": "Legend of Zelda, The - Majora's Mask (Europe) (GameCube)" "name": "The Legend of Zelda - Majora's Mask (Europe) (GameCube)"
}, },
"a83abf72":{ "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":{ "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":{ "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":{ "cd16c529":{
"name": "Legend of Zelda, The - Ocarina of Time (USA)" "name": "The Legend of Zelda - Ocarina of Time (USA)"
}, },
"3fd2151e":{ "3fd2151e":{
"name": "Legend of Zelda, The - Ocarina of Time (USA) (Rev 1)" "name": "The Legend of Zelda - Ocarina of Time (USA) (Rev 1)"
}, },
"32120c23":{ "32120c23":{
"name": "Legend of Zelda, The - Ocarina of Time (USA) (Rev 2)" "name": "The Legend of Zelda - Ocarina of Time (USA) (Rev 2)"
}, },
"346de3ae":{ "346de3ae":{
"name": "Legend of Zelda, The - Ocarina of Time (USA) (GameCube)" "name": "The Legend of Zelda - Ocarina of Time (USA) (GameCube)"
}, },
"3fbd519f":{ "3fbd519f":{
"name": "Legend of Zelda, The - Ocarina of Time (Europe) (GameCube)" "name": "The Legend of Zelda - Ocarina of Time (Europe) (GameCube)"
}, },
"5d1b2996":{ "5d1b2996":{
"name": "Legend of Zelda, The - Ocarina of Time (Europe) (Debug) (GameCube)" "name": "The Legend of Zelda - Ocarina of Time (Europe) (Debug) (GameCube)"
}, },
"c188acda":{ "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":{ "6c348aa8":{
"name": "Legend of Zelda, The - Ocarina of Time (USA) (Beta)" "name": "The Legend of Zelda - Ocarina of Time (USA) (Beta)"
}, },
"a1d34e08":{ "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":{ "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":{ "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":{ "c7d9b21c":{
"name": "LEGO Racers (Europe) (En,Fr,De,Es,It,Nl,Sv,No,Da,Fi)" "name": "LEGO Racers (Europe) (En,Fr,De,Es,It,Nl,Sv,No,Da,Fi)"

View File

@@ -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) { inline void HandleInterrupt(Registers& regs) {
if(ShouldServiceInterrupt(regs)) { if(ShouldServiceInterrupt(regs)) {
FireException(regs, ExceptionCode::Interrupt, 0, regs.pc); FireException(regs, ExceptionCode::Interrupt, 0, regs.pc);

View File

@@ -117,25 +117,4 @@ private:
void xor_(u32); void xor_(u32);
void xori(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);
} }

View File

@@ -50,7 +50,7 @@ bool MapVAddr(Registers& regs, TLBAccessType accessType, u32 vaddr, u32& paddr)
switch(vaddr >> 29) { switch(vaddr >> 29) {
case 0 ... 3: case 7: 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 4 ... 5: return true;
case 6: util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})\n", vaddr); case 6: util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})\n", vaddr);
default: default:

View File

@@ -46,10 +46,10 @@ auto RSP::Read(u32 addr) -> u32{
case 0x04040008: case 0x04040008:
case 0x0404000C: return spDMALen.raw; case 0x0404000C: return spDMALen.raw;
case 0x04040010: return spStatus.raw; case 0x04040010: return spStatus.raw;
case 0x04040014: return spStatus.dmaFull;
case 0x04040018: return 0; case 0x04040018: return 0;
case 0x0404001C: return AcquireSemaphore(); case 0x0404001C: return AcquireSemaphore();
case 0x04080000: case 0x04080000: return pc & 0xFFC;
return pc & 0xFFC;
default: util::panic("Unimplemented SP register read {:08X}\n", addr); 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; pc = value & 0xFFC;
nextPC = value & 0xFFC; nextPC = value & 0xFFC;
} break; } 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);
} }
} }

View File

@@ -7,13 +7,12 @@
#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) #define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1)
namespace n64 { namespace n64 {
void Cpu::add(u32 instr) { void Cpu::add(u32 instr) {
u32 rs = (s32)regs.gpr[RS(instr)]; u32 rs = (s32)regs.gpr[RS(instr)];
u32 rt = (s32)regs.gpr[RT(instr)]; u32 rt = (s32)regs.gpr[RT(instr)];
u32 result = rs + rt; u32 result = rs + rt;
if(check_signed_overflow(rs, rt, result)) { if(check_signed_overflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } else {
regs.gpr[RD(instr)] = s32(result); regs.gpr[RD(instr)] = s32(result);
} }
@@ -27,13 +26,13 @@ void Cpu::addu(u32 instr) {
} }
void Cpu::addi(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 imm = s32(s16(instr));
u32 result = rs + imm; u32 result = rs + imm;
if(check_signed_overflow(rs, imm, result)) { if(check_signed_overflow(rs, imm, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } 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 rt = regs.gpr[RT(instr)];
u64 result = rt + rs; u64 result = rt + rs;
if(check_signed_overflow(rs, rt, result)) { if(check_signed_overflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } else {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }
@@ -66,9 +65,9 @@ void Cpu::daddi(u32 instr) {
u64 rs = regs.gpr[RS(instr)]; u64 rs = regs.gpr[RS(instr)];
u64 result = imm + rs; u64 result = imm + rs;
if(check_signed_overflow(rs, imm, result)) { if(check_signed_overflow(rs, imm, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } 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 rs = regs.gpr[RS(instr)];
s64 result = rs - rt; s64 result = rs - rt;
if(check_signed_underflow(rs, rt, result)) { if(check_signed_underflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } else {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }
@@ -676,7 +675,7 @@ void Cpu::sub(u32 instr) {
s32 rs = regs.gpr[RS(instr)]; s32 rs = regs.gpr[RS(instr)];
s32 result = rs - rt; s32 result = rs - rt;
if(check_signed_underflow(rs, rt, result)) { if(check_signed_underflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC); FireException(regs, ExceptionCode::Overflow, 0, regs.oldPC);
} else { } else {
regs.gpr[RD(instr)] = result; regs.gpr[RD(instr)] = result;
} }

View File

@@ -232,6 +232,63 @@ bool ProbeTLB(Registers& regs, TLBAccessType access_type, s64 vaddr, u32& paddr,
return true; 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<u8>(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<u8>(code));
}
}
}
void HandleTLBException(Registers& regs, u64 vaddr) { void HandleTLBException(Registers& regs, u64 vaddr) {
u64 vpn2 = (vaddr >> 13) & 0x7FFFF; u64 vpn2 = (vaddr >> 13) & 0x7FFFF;
u64 xvpn2 = (vaddr >> 13) & 0x7FFFFFF; u64 xvpn2 = (vaddr >> 13) & 0x7FFFFFF;

View File

@@ -157,6 +157,27 @@ enum TLBError : u8 {
DISALLOWED_ADDRESS 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 { union Cop0Context {
u64 raw; u64 raw;
struct { struct {
@@ -244,7 +265,7 @@ private:
}; };
struct Registers; struct Registers;
enum ExceptionCode : u8; enum class ExceptionCode : u8;
TLBEntry* TLBTryMatch(Registers& regs, s64 vaddr, int* match); TLBEntry* TLBTryMatch(Registers& regs, s64 vaddr, int* match);
bool ProbeTLB(Registers& regs, TLBAccessType access_type, s64 vaddr, u32& paddr, int* match); bool ProbeTLB(Registers& regs, TLBAccessType access_type, s64 vaddr, u32& paddr, int* match);

View File

@@ -29,9 +29,11 @@ void Cop1::decode(Cpu& cpu, u32 instr) {
case 0x00: mfc1(regs, instr); break; case 0x00: mfc1(regs, instr); break;
case 0x01: dmfc1(regs, instr); break; case 0x01: dmfc1(regs, instr); break;
case 0x02: cfc1(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 0x04: mtc1(regs, instr); break;
case 0x05: dmtc1(regs, instr); break; case 0x05: dmtc1(regs, instr); break;
case 0x06: ctc1(regs, instr); break; case 0x06: ctc1(regs, instr); break;
case 0x07: FireException(regs, ExceptionCode::FloatingPointError, 0, regs.oldPC); break;
case 0x08: case 0x08:
switch(mask_branch) { switch(mask_branch) {
case 0: cpu.b(instr, !regs.cop1.fcr31.compare); break; 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 0x21: cvtds(regs, instr); break;
case 0x24: cvtws(regs, instr); break; case 0x24: cvtws(regs, instr); break;
case 0x25: cvtls(regs, instr); break; case 0x25: cvtls(regs, instr); break;
case 0x30: cconds(regs, instr, F); break; case 0x30: ccond<float>(regs, instr, F); break;
case 0x31: cconds(regs, instr, UN); break; case 0x31: ccond<float>(regs, instr, UN); break;
case 0x32: cconds(regs, instr, EQ); break; case 0x32: ccond<float>(regs, instr, EQ); break;
case 0x33: cconds(regs, instr, UEQ); break; case 0x33: ccond<float>(regs, instr, UEQ); break;
case 0x34: cconds(regs, instr, OLT); break; case 0x34: ccond<float>(regs, instr, OLT); break;
case 0x35: cconds(regs, instr, ULT); break; case 0x35: ccond<float>(regs, instr, ULT); break;
case 0x36: cconds(regs, instr, OLE); break; case 0x36: ccond<float>(regs, instr, OLE); break;
case 0x37: cconds(regs, instr, ULE); break; case 0x37: ccond<float>(regs, instr, ULE); break;
case 0x38: cconds(regs, instr, SF); break; case 0x38: ccond<float>(regs, instr, SF); break;
case 0x39: cconds(regs, instr, NGLE); break; case 0x39: ccond<float>(regs, instr, NGLE); break;
case 0x3A: cconds(regs, instr, SEQ); break; case 0x3A: ccond<float>(regs, instr, SEQ); break;
case 0x3B: cconds(regs, instr, NGL); break; case 0x3B: ccond<float>(regs, instr, NGL); break;
case 0x3C: cconds(regs, instr, LT); break; case 0x3C: ccond<float>(regs, instr, LT); break;
case 0x3D: cconds(regs, instr, NGE); break; case 0x3D: ccond<float>(regs, instr, NGE); break;
case 0x3E: cconds(regs, instr, LE); break; case 0x3E: ccond<float>(regs, instr, LE); break;
case 0x3F: cconds(regs, instr, NGT); break; case 0x3F: ccond<float>(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}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC);
} }
break; break;
@@ -102,22 +104,22 @@ void Cop1::decode(Cpu& cpu, u32 instr) {
case 0x20: cvtsd(regs, instr); break; case 0x20: cvtsd(regs, instr); break;
case 0x24: cvtwd(regs, instr); break; case 0x24: cvtwd(regs, instr); break;
case 0x25: cvtld(regs, instr); break; case 0x25: cvtld(regs, instr); break;
case 0x30: ccondd(regs, instr, F); break; case 0x30: ccond<double>(regs, instr, F); break;
case 0x31: ccondd(regs, instr, UN); break; case 0x31: ccond<double>(regs, instr, UN); break;
case 0x32: ccondd(regs, instr, EQ); break; case 0x32: ccond<double>(regs, instr, EQ); break;
case 0x33: ccondd(regs, instr, UEQ); break; case 0x33: ccond<double>(regs, instr, UEQ); break;
case 0x34: ccondd(regs, instr, OLT); break; case 0x34: ccond<double>(regs, instr, OLT); break;
case 0x35: ccondd(regs, instr, ULT); break; case 0x35: ccond<double>(regs, instr, ULT); break;
case 0x36: ccondd(regs, instr, OLE); break; case 0x36: ccond<double>(regs, instr, OLE); break;
case 0x37: ccondd(regs, instr, ULE); break; case 0x37: ccond<double>(regs, instr, ULE); break;
case 0x38: ccondd(regs, instr, SF); break; case 0x38: ccond<double>(regs, instr, SF); break;
case 0x39: ccondd(regs, instr, NGLE); break; case 0x39: ccond<double>(regs, instr, NGLE); break;
case 0x3A: ccondd(regs, instr, SEQ); break; case 0x3A: ccond<double>(regs, instr, SEQ); break;
case 0x3B: ccondd(regs, instr, NGL); break; case 0x3B: ccond<double>(regs, instr, NGL); break;
case 0x3C: ccondd(regs, instr, LT); break; case 0x3C: ccond<double>(regs, instr, LT); break;
case 0x3D: ccondd(regs, instr, NGE); break; case 0x3D: ccond<double>(regs, instr, NGE); break;
case 0x3E: ccondd(regs, instr, LE); break; case 0x3E: ccond<double>(regs, instr, LE); break;
case 0x3F: ccondd(regs, instr, NGT); break; case 0x3F: ccond<double>(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}) ({:016lX})", mask_fun >> 3, mask_fun & 7, instr, regs.oldPC);
} }
break; break;

View File

@@ -37,6 +37,13 @@ union FCR31 {
u32 raw; u32 raw;
}; };
enum CompConds {
F, UN, EQ, UEQ,
OLT, ULT, OLE, ULE,
SF, NGLE, SEQ, NGL,
LT, NGE, LE, NGT
};
union FGR { union FGR {
struct { struct {
s32 lo:32; s32 lo:32;
@@ -100,43 +107,32 @@ private:
} }
} }
inline void SetCop1RegDouble(Cop0& cop0, u8 index, double value) { template <typename T>
u64 raw; inline void SetCop1Reg(Cop0& cop0, u8 index, T value) {
memcpy(&raw, &value, sizeof(double)); if constexpr (sizeof(T) == 4) {
SetReg<u64>(cop0, index, raw); u32 raw;
memcpy(&raw, &value, sizeof(T));
SetReg<u32>(cop0, index, raw);
} else if constexpr (sizeof(T) == 8) {
u64 raw;
memcpy(&raw, &value, sizeof(T));
SetReg<u64>(cop0, index, raw);
}
} }
inline double GetCop1RegDouble(Cop0& cop0, u8 index) { template <typename T>
double doublevalue; inline T GetCop1Reg(Cop0& cop0, u8 index) {
u64 raw = GetReg<u64>(cop0, index); T value;
memcpy(&doublevalue, &raw, sizeof(double)); if constexpr (sizeof(T) == 4) {
return doublevalue; u32 raw = GetReg<u32>(cop0, index);
memcpy(&value, &raw, sizeof(T));
} else if constexpr (sizeof(T) == 8) {
u64 raw = GetReg<u64>(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<u32>(cop0, index, raw);
}
inline float GetCop1RegFloat(Cop0& cop0, u8 index) {
u32 raw = GetReg<u32>(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 absd(Registers&, u32 instr);
void abss(Registers&, u32 instr); void abss(Registers&, u32 instr);
void absw(Registers&, u32 instr); void absw(Registers&, u32 instr);
@@ -151,7 +147,7 @@ private:
void ceilws(Registers&, u32 instr); void ceilws(Registers&, u32 instr);
void ceilld(Registers&, u32 instr); void ceilld(Registers&, u32 instr);
void ceilwd(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 ctc1(Registers&, u32 instr);
void roundls(Registers&, u32 instr); void roundls(Registers&, u32 instr);
void roundld(Registers&, u32 instr); void roundld(Registers&, u32 instr);
@@ -171,8 +167,8 @@ private:
void cvtld(Registers&, u32 instr); void cvtld(Registers&, u32 instr);
void cvtdl(Registers&, u32 instr); void cvtdl(Registers&, u32 instr);
void cvtsl(Registers&, u32 instr); void cvtsl(Registers&, u32 instr);
void ccondd(Registers&, u32 instr, CompConds); template <typename T>
void cconds(Registers&, u32 instr, CompConds); void ccond(Registers&, u32 instr, CompConds);
void divs(Registers&, u32 instr); void divs(Registers&, u32 instr);
void divd(Registers&, u32 instr); void divd(Registers&, u32 instr);
void muls(Registers&, u32 instr); void muls(Registers&, u32 instr);

View File

@@ -6,13 +6,13 @@
namespace n64 { namespace n64 {
void Cop1::absd(Registers& regs, u32 instr) { void Cop1::absd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetCop1RegDouble(regs.cop0, FD(instr), fabs(fs)); SetCop1Reg<double>(regs.cop0, FD(instr), fabs(fs));
} }
void Cop1::abss(Registers& regs, u32 instr) { void Cop1::abss(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fabsf(fs)); SetCop1Reg<float>(regs.cop0, FD(instr), fabsf(fs));
} }
void Cop1::absw(Registers& regs, u32 instr) { 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) { void Cop1::adds(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
float result = fs + ft; float result = fs + ft;
SetCop1RegFloat(regs.cop0, FD(instr), result); SetCop1Reg<float>(regs.cop0, FD(instr), result);
} }
void Cop1::addd(Registers& regs, u32 instr) { void Cop1::addd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1RegDouble(regs.cop0, FT(instr)); double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
double result = fs + ft; double result = fs + ft;
SetCop1RegDouble(regs.cop0, FD(instr), result); SetCop1Reg<double>(regs.cop0, FD(instr), result);
} }
void Cop1::ceills(Registers& regs, u32 instr) { void Cop1::ceills(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = ceilf(fs); s64 result = ceilf(fs);
SetReg<u64>(regs.cop0, FD(instr), result); SetReg<u64>(regs.cop0, FD(instr), result);
} }
void Cop1::ceilws(Registers& regs, u32 instr) { void Cop1::ceilws(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = ceilf(fs); s32 result = ceilf(fs);
SetReg<u32>(regs.cop0, FD(instr), result); SetReg<u32>(regs.cop0, FD(instr), result);
} }
void Cop1::ceilld(Registers& regs, u32 instr) { void Cop1::ceilld(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = ceil(fs); s64 result = ceil(fs);
SetReg<u64>(regs.cop0, FD(instr), result); SetReg<u64>(regs.cop0, FD(instr), result);
} }
void Cop1::ceilwd(Registers& regs, u32 instr) { void Cop1::ceilwd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = ceil(fs); s32 result = ceil(fs);
SetReg<u32>(regs.cop0, FD(instr), result); SetReg<u32>(regs.cop0, FD(instr), result);
} }
void Cop1::cfc1(Registers& regs, u32 instr) { void Cop1::cfc1(Registers& regs, u32 instr) const {
u8 fd = FD(instr); u8 fd = FD(instr);
s32 val = 0; s32 val = 0;
switch(fd) { switch(fd) {
@@ -78,7 +78,7 @@ void Cop1::ctc1(Registers& regs, u32 instr) {
u8 fs = FS(instr); u8 fs = FS(instr);
u32 val = regs.gpr[RT(instr)]; u32 val = regs.gpr[RT(instr)];
switch(fs) { switch(fs) {
case 0: util::panic("CTC1 attempt to write to FCR0 which is read only!\n"); case 0: break;
case 31: { case 31: {
val &= 0x183ffff; val &= 0x183ffff;
fcr31.raw = val; fcr31.raw = val;
@@ -88,10 +88,10 @@ void Cop1::ctc1(Registers& regs, u32 instr) {
} }
void Cop1::cvtds(Registers& regs, u32 instr) { void Cop1::cvtds(Registers& regs, u32 instr) {
SetCop1RegDouble( SetCop1Reg<double>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegFloat( GetCop1Reg<float>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -99,10 +99,10 @@ void Cop1::cvtds(Registers& regs, u32 instr) {
} }
void Cop1::cvtsd(Registers& regs, u32 instr) { void Cop1::cvtsd(Registers& regs, u32 instr) {
SetCop1RegFloat( SetCop1Reg<float>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegDouble( GetCop1Reg<double>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -113,7 +113,7 @@ void Cop1::cvtwd(Registers& regs, u32 instr) {
SetReg<u32>( SetReg<u32>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegDouble( GetCop1Reg<double>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -124,7 +124,7 @@ void Cop1::cvtws(Registers& regs, u32 instr) {
SetReg<u32>( SetReg<u32>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegFloat( GetCop1Reg<float>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -135,7 +135,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) {
SetReg<u64>( SetReg<u64>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegFloat( GetCop1Reg<float>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -143,7 +143,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) {
} }
void Cop1::cvtsl(Registers& regs, u32 instr) { void Cop1::cvtsl(Registers& regs, u32 instr) {
SetCop1RegFloat( SetCop1Reg<float>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
(s64)GetReg<u64>( (s64)GetReg<u64>(
@@ -154,7 +154,7 @@ void Cop1::cvtsl(Registers& regs, u32 instr) {
} }
void Cop1::cvtdw(Registers& regs, u32 instr) { void Cop1::cvtdw(Registers& regs, u32 instr) {
SetCop1RegDouble( SetCop1Reg<double>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
(s32)GetReg<u32>( (s32)GetReg<u32>(
@@ -165,7 +165,7 @@ void Cop1::cvtdw(Registers& regs, u32 instr) {
} }
void Cop1::cvtsw(Registers& regs, u32 instr) { void Cop1::cvtsw(Registers& regs, u32 instr) {
SetCop1RegFloat( SetCop1Reg<float>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
(s32)GetReg<u32>( (s32)GetReg<u32>(
@@ -176,7 +176,7 @@ void Cop1::cvtsw(Registers& regs, u32 instr) {
} }
void Cop1::cvtdl(Registers& regs, u32 instr) { void Cop1::cvtdl(Registers& regs, u32 instr) {
SetCop1RegDouble( SetCop1Reg<double>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
(s64)GetReg<u64>( (s64)GetReg<u64>(
@@ -188,77 +188,62 @@ void Cop1::cvtdl(Registers& regs, u32 instr) {
void Cop1::cvtld(Registers& regs, u32 instr) { void Cop1::cvtld(Registers& regs, u32 instr) {
SetReg<u64>( SetReg<u64>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegDouble( GetCop1Reg<double>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
); );
} }
void Cop1::ccondd(Registers& regs, u32 instr, CompConds cond) { template <typename T>
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); inline bool CalculateCondition(T fs, T ft, u8 cond) {
double ft = GetCop1RegDouble(regs.cop0, FT(instr)); switch(cond) {
case F: return false;
bool less, equal, unordered; case UN: return std::isnan(fs) || std::isnan(ft);
if(std::isnan(fs) || std::isnan(ft)) { case EQ: return fs == ft;
less = false; case UEQ: return (std::isnan(fs) || std::isnan(ft)) || (fs == ft);
equal = false; case OLT: return (!std::isnan(fs) && !std::isnan(ft)) && (fs < ft);
unordered = true; case ULT: return (std::isnan(fs) || std::isnan(ft)) || (fs < ft);
} else { case OLE: return (!std::isnan(fs) && !std::isnan(ft)) && (fs <= ft);
less = fs < ft; default: return CalculateCondition(fs, ft, cond - 8);
equal = fs == ft;
unordered = false;
} }
bool condition = ((cond >> 2) && less) || ((cond >> 1) && equal) || ((cond & 1) && unordered);
fcr31.compare = condition;
} }
void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) { template <typename T>
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) {
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); T fs = GetCop1Reg<T>(regs.cop0, FS(instr));
T ft = GetCop1Reg<T>(regs.cop0, FT(instr));
bool less, equal, unordered; fcr31.compare = CalculateCondition(fs, ft, cond);
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;
} }
template void Cop1::ccond<float>(Registers& regs, u32 instr, CompConds cond);
template void Cop1::ccond<double>(Registers& regs, u32 instr, CompConds cond);
void Cop1::divs(Registers &regs, u32 instr) { void Cop1::divs(Registers &regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fs / ft); SetCop1Reg<float>(regs.cop0, FD(instr), fs / ft);
} }
void Cop1::divd(Registers &regs, u32 instr) { void Cop1::divd(Registers &regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1RegDouble(regs.cop0, FT(instr)); double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1RegDouble(regs.cop0, FD(instr), fs / ft); SetCop1Reg<double>(regs.cop0, FD(instr), fs / ft);
} }
void Cop1::muls(Registers &regs, u32 instr) { void Cop1::muls(Registers &regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fs * ft); SetCop1Reg<float>(regs.cop0, FD(instr), fs * ft);
} }
void Cop1::muld(Registers& regs, u32 instr) { void Cop1::muld(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1RegDouble(regs.cop0, FT(instr)); double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1RegDouble(regs.cop0, FD(instr), fs * ft); SetCop1Reg<double>(regs.cop0, FD(instr), fs * ft);
} }
void Cop1::mulw(Registers &regs, u32 instr) { void Cop1::mulw(Registers &regs, u32 instr) {
@@ -274,15 +259,15 @@ void Cop1::mull(Registers &regs, u32 instr) {
} }
void Cop1::subs(Registers &regs, u32 instr) { void Cop1::subs(Registers &regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr)); float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fs - ft); SetCop1Reg<float>(regs.cop0, FD(instr), fs - ft);
} }
void Cop1::subd(Registers &regs, u32 instr) { void Cop1::subd(Registers &regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
double ft = GetCop1RegDouble(regs.cop0, FT(instr)); double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1RegDouble(regs.cop0, FD(instr), fs - ft); SetCop1Reg<double>(regs.cop0, FD(instr), fs - ft);
} }
void Cop1::subw(Registers &regs, u32 instr) { void Cop1::subw(Registers &regs, u32 instr) {
@@ -298,10 +283,10 @@ void Cop1::subl(Registers &regs, u32 instr) {
} }
void Cop1::movs(Registers& regs, u32 instr) { void Cop1::movs(Registers& regs, u32 instr) {
SetCop1RegFloat( SetCop1Reg<float>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegFloat( GetCop1Reg<float>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -309,10 +294,10 @@ void Cop1::movs(Registers& regs, u32 instr) {
} }
void Cop1::movd(Registers& regs, u32 instr) { void Cop1::movd(Registers& regs, u32 instr) {
SetCop1RegDouble( SetCop1Reg<double>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
GetCop1RegDouble( GetCop1Reg<double>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -342,10 +327,10 @@ void Cop1::movl(Registers& regs, u32 instr) {
} }
void Cop1::negs(Registers &regs, u32 instr) { void Cop1::negs(Registers &regs, u32 instr) {
SetCop1RegFloat( SetCop1Reg<float>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
-GetCop1RegFloat( -GetCop1Reg<float>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -353,10 +338,10 @@ void Cop1::negs(Registers &regs, u32 instr) {
} }
void Cop1::negd(Registers &regs, u32 instr) { void Cop1::negd(Registers &regs, u32 instr) {
SetCop1RegDouble( SetCop1Reg<double>(
regs.cop0, regs.cop0,
FD(instr), FD(instr),
-GetCop1RegDouble( -GetCop1Reg<double>(
regs.cop0, regs.cop0,
FS(instr) FS(instr)
) )
@@ -364,101 +349,161 @@ void Cop1::negd(Registers &regs, u32 instr) {
} }
void Cop1::sqrts(Registers &regs, u32 instr) { void Cop1::sqrts(Registers &regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetCop1RegFloat(regs.cop0, FD(instr), sqrtf(fs)); SetCop1Reg<float>(regs.cop0, FD(instr), sqrtf(fs));
} }
void Cop1::sqrtd(Registers &regs, u32 instr) { void Cop1::sqrtd(Registers &regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetCop1RegDouble(regs.cop0, FD(instr), sqrt(fs)); SetCop1Reg<double>(regs.cop0, FD(instr), sqrt(fs));
} }
void Cop1::roundls(Registers& regs, u32 instr) { void Cop1::roundls(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s32)roundf(fs)); SetReg<u64>(regs.cop0, FD(instr), (s32)roundf(fs));
} }
void Cop1::roundld(Registers& regs, u32 instr) { void Cop1::roundld(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)round(fs)); SetReg<u64>(regs.cop0, FD(instr), (s64)round(fs));
} }
void Cop1::roundws(Registers& regs, u32 instr) { void Cop1::roundws(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s32)roundf(fs)); SetReg<u32>(regs.cop0, FD(instr), (s32)roundf(fs));
} }
void Cop1::roundwd(Registers& regs, u32 instr) { void Cop1::roundwd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s64)round(fs)); SetReg<u32>(regs.cop0, FD(instr), (s64)round(fs));
} }
void Cop1::floorls(Registers& regs, u32 instr) { void Cop1::floorls(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)floorf(fs)); SetReg<u64>(regs.cop0, FD(instr), (s64)floorf(fs));
} }
void Cop1::floorld(Registers& regs, u32 instr) { void Cop1::floorld(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u64>(regs.cop0, FD(instr), (s64)floor(fs)); SetReg<u64>(regs.cop0, FD(instr), (s64)floor(fs));
} }
void Cop1::floorws(Registers& regs, u32 instr) { void Cop1::floorws(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s64)floorf(fs)); SetReg<u32>(regs.cop0, FD(instr), (s64)floorf(fs));
} }
void Cop1::floorwd(Registers& regs, u32 instr) { void Cop1::floorwd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetReg<u32>(regs.cop0, FD(instr), (s64)floor(fs)); SetReg<u32>(regs.cop0, FD(instr), (s64)floor(fs));
} }
void Cop1::lwc1(Registers& regs, Mem& mem, u32 instr) { void Cop1::lwc1(Registers& regs, Mem& mem, u32 instr) {
u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; if(!regs.cop0.status.cu1) {
u32 data = mem.Read32(regs, addr, regs.oldPC); FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC);
SetReg<u32>(regs.cop0, FT(instr), data); 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<false>(regs, physical, regs.oldPC);
SetReg<u32>(regs.cop0, FT(instr), data);
}
} }
void Cop1::swc1(Registers& regs, Mem& mem, u32 instr) { void Cop1::swc1(Registers& regs, Mem& mem, u32 instr) {
u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; if(!regs.cop0.status.cu1) {
mem.Write32(regs, addr, GetReg<u32>(regs.cop0, FT(instr)), regs.oldPC); 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<false>(regs, physical, GetReg<u32>(regs.cop0, FT(instr)), regs.oldPC);
}
} }
void Cop1::ldc1(Registers& regs, Mem& mem, u32 instr) { void Cop1::ldc1(Registers& regs, Mem& mem, u32 instr) {
u32 addr = (s64)(s16)instr + regs.gpr[BASE(instr)]; if(!regs.cop0.status.cu1) {
u64 data = mem.Read64(regs, addr, regs.oldPC); FireException(regs, ExceptionCode::CoprocessorUnusable, 1, regs.oldPC);
SetReg<u64>(regs.cop0, FT(instr), data); 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<false>(regs, physical, regs.oldPC);
SetReg<u64>(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<false>(regs, physical, GetReg<u64>(regs.cop0, FT(instr)), regs.oldPC);
}
} }
void Cop1::truncws(Registers& regs, u32 instr) { void Cop1::truncws(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = (s32)truncf(fs); s32 result = (s32)truncf(fs);
SetReg<u32>(regs.cop0, FD(instr), result); SetReg<u32>(regs.cop0, FD(instr), result);
} }
void Cop1::truncwd(Registers& regs, u32 instr) { void Cop1::truncwd(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s32 result = (s32)trunc(fs); s32 result = (s32)trunc(fs);
SetReg<u32>(regs.cop0, FD(instr), result); SetReg<u32>(regs.cop0, FD(instr), result);
} }
void Cop1::truncls(Registers& regs, u32 instr) { void Cop1::truncls(Registers& regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr)); float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s64 result = (s64)truncf(fs); s64 result = (s64)truncf(fs);
SetReg<u64>(regs.cop0, FD(instr), result); SetReg<u64>(regs.cop0, FD(instr), result);
} }
void Cop1::truncld(Registers& regs, u32 instr) { void Cop1::truncld(Registers& regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr)); double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
s64 result = (s64)trunc(fs); s64 result = (s64)trunc(fs);
SetReg<u64>(regs.cop0, FD(instr), result); SetReg<u64>(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<u64>(regs.cop0, FT(instr)), regs.oldPC);
}
void Cop1::mfc1(Registers& regs, u32 instr) { void Cop1::mfc1(Registers& regs, u32 instr) {
regs.gpr[RT(instr)] = (s32)GetReg<u32>(regs.cop0, FS(instr)); regs.gpr[RT(instr)] = (s32)GetReg<u32>(regs.cop0, FS(instr));
} }

View File

@@ -226,18 +226,24 @@ void RSP::sqv(u32 instr) {
} }
void RSP::sllv(u32 instr) { void RSP::sllv(u32 instr) {
u8 sa = gpr[RS(instr)] & 0x1F; u8 sa = (gpr[RS(instr)]) & 0x1F;
gpr[RD(instr)] = (u32)gpr[RT(instr)] << sa; u32 rt = gpr[RT(instr)];
u32 result = rt << sa;
gpr[RD(instr)] = result;
} }
void RSP::srlv(u32 instr) { void RSP::srlv(u32 instr) {
u8 sa = gpr[RS(instr)] & 0x1F; u8 sa = (gpr[RS(instr)]) & 0x1F;
gpr[RD(instr)] = (u32)gpr[RT(instr)] >> sa; u32 rt = gpr[RT(instr)];
u32 result = rt >> sa;
gpr[RD(instr)] = result;
} }
void RSP::srav(u32 instr) { void RSP::srav(u32 instr) {
u8 sa = gpr[RS(instr)] & 0x1F; u8 sa = (gpr[RS(instr)]) & 0x1F;
gpr[RD(instr)] = gpr[RT(instr)] >> sa; s32 rt = gpr[RT(instr)];
s32 result = rt >> sa;
gpr[RD(instr)] = result;
} }
void RSP::sll(u32 instr) { void RSP::sll(u32 instr) {