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":{
"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)"

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) {
if(ShouldServiceInterrupt(regs)) {
FireException(regs, ExceptionCode::Interrupt, 0, regs.pc);

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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<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) {
u64 vpn2 = (vaddr >> 13) & 0x7FFFF;
u64 xvpn2 = (vaddr >> 13) & 0x7FFFFFF;

View File

@@ -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);

View File

@@ -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<float>(regs, instr, F); break;
case 0x31: ccond<float>(regs, instr, UN); break;
case 0x32: ccond<float>(regs, instr, EQ); break;
case 0x33: ccond<float>(regs, instr, UEQ); break;
case 0x34: ccond<float>(regs, instr, OLT); break;
case 0x35: ccond<float>(regs, instr, ULT); break;
case 0x36: ccond<float>(regs, instr, OLE); break;
case 0x37: ccond<float>(regs, instr, ULE); break;
case 0x38: ccond<float>(regs, instr, SF); break;
case 0x39: ccond<float>(regs, instr, NGLE); break;
case 0x3A: ccond<float>(regs, instr, SEQ); break;
case 0x3B: ccond<float>(regs, instr, NGL); break;
case 0x3C: ccond<float>(regs, instr, LT); break;
case 0x3D: ccond<float>(regs, instr, NGE); break;
case 0x3E: ccond<float>(regs, instr, LE); 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);
}
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<double>(regs, instr, F); break;
case 0x31: ccond<double>(regs, instr, UN); break;
case 0x32: ccond<double>(regs, instr, EQ); break;
case 0x33: ccond<double>(regs, instr, UEQ); break;
case 0x34: ccond<double>(regs, instr, OLT); break;
case 0x35: ccond<double>(regs, instr, ULT); break;
case 0x36: ccond<double>(regs, instr, OLE); break;
case 0x37: ccond<double>(regs, instr, ULE); break;
case 0x38: ccond<double>(regs, instr, SF); break;
case 0x39: ccond<double>(regs, instr, NGLE); break;
case 0x3A: ccond<double>(regs, instr, SEQ); break;
case 0x3B: ccond<double>(regs, instr, NGL); break;
case 0x3C: ccond<double>(regs, instr, LT); break;
case 0x3D: ccond<double>(regs, instr, NGE); break;
case 0x3E: ccond<double>(regs, instr, LE); 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);
}
break;

View File

@@ -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,42 +107,31 @@ private:
}
}
inline void SetCop1RegDouble(Cop0& cop0, u8 index, double value) {
template <typename T>
inline void SetCop1Reg(Cop0& cop0, u8 index, T value) {
if constexpr (sizeof(T) == 4) {
u32 raw;
memcpy(&raw, &value, sizeof(T));
SetReg<u32>(cop0, index, raw);
} else if constexpr (sizeof(T) == 8) {
u64 raw;
memcpy(&raw, &value, sizeof(double));
memcpy(&raw, &value, sizeof(T));
SetReg<u64>(cop0, index, raw);
}
inline double GetCop1RegDouble(Cop0& cop0, u8 index) {
double doublevalue;
u64 raw = GetReg<u64>(cop0, index);
memcpy(&doublevalue, &raw, sizeof(double));
return doublevalue;
}
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) {
template <typename T>
inline T GetCop1Reg(Cop0& cop0, u8 index) {
T value;
if constexpr (sizeof(T) == 4) {
u32 raw = GetReg<u32>(cop0, index);
float floatvalue;
memcpy(&floatvalue, &raw, sizeof(float));
return floatvalue;
memcpy(&value, &raw, sizeof(T));
} else if constexpr (sizeof(T) == 8) {
u64 raw = GetReg<u64>(cop0, index);
memcpy(&value, &raw, sizeof(T));
}
return value;
}
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);
@@ -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 <typename T>
void ccond(Registers&, u32 instr, CompConds);
void divs(Registers&, u32 instr);
void divd(Registers&, u32 instr);
void muls(Registers&, u32 instr);

View File

@@ -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<double>(regs.cop0, FS(instr));
SetCop1Reg<double>(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<float>(regs.cop0, FS(instr));
SetCop1Reg<float>(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<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
float result = fs + ft;
SetCop1RegFloat(regs.cop0, FD(instr), result);
SetCop1Reg<float>(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<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
double result = fs + ft;
SetCop1RegDouble(regs.cop0, FD(instr), result);
SetCop1Reg<double>(regs.cop0, FD(instr), result);
}
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);
SetReg<u64>(regs.cop0, FD(instr), result);
}
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);
SetReg<u32>(regs.cop0, FD(instr), result);
}
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);
SetReg<u64>(regs.cop0, FD(instr), result);
}
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);
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);
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<double>(
regs.cop0,
FD(instr),
GetCop1RegFloat(
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
@@ -99,10 +99,10 @@ void Cop1::cvtds(Registers& regs, u32 instr) {
}
void Cop1::cvtsd(Registers& regs, u32 instr) {
SetCop1RegFloat(
SetCop1Reg<float>(
regs.cop0,
FD(instr),
GetCop1RegDouble(
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
@@ -113,7 +113,7 @@ void Cop1::cvtwd(Registers& regs, u32 instr) {
SetReg<u32>(
regs.cop0,
FD(instr),
GetCop1RegDouble(
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
@@ -124,7 +124,7 @@ void Cop1::cvtws(Registers& regs, u32 instr) {
SetReg<u32>(
regs.cop0,
FD(instr),
GetCop1RegFloat(
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
@@ -135,7 +135,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) {
SetReg<u64>(
regs.cop0,
FD(instr),
GetCop1RegFloat(
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
@@ -143,7 +143,7 @@ void Cop1::cvtls(Registers& regs, u32 instr) {
}
void Cop1::cvtsl(Registers& regs, u32 instr) {
SetCop1RegFloat(
SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s64)GetReg<u64>(
@@ -154,7 +154,7 @@ void Cop1::cvtsl(Registers& regs, u32 instr) {
}
void Cop1::cvtdw(Registers& regs, u32 instr) {
SetCop1RegDouble(
SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s32)GetReg<u32>(
@@ -165,7 +165,7 @@ void Cop1::cvtdw(Registers& regs, u32 instr) {
}
void Cop1::cvtsw(Registers& regs, u32 instr) {
SetCop1RegFloat(
SetCop1Reg<float>(
regs.cop0,
FD(instr),
(s32)GetReg<u32>(
@@ -176,7 +176,7 @@ void Cop1::cvtsw(Registers& regs, u32 instr) {
}
void Cop1::cvtdl(Registers& regs, u32 instr) {
SetCop1RegDouble(
SetCop1Reg<double>(
regs.cop0,
FD(instr),
(s64)GetReg<u64>(
@@ -190,75 +190,60 @@ void Cop1::cvtld(Registers& regs, u32 instr) {
SetReg<u64>(
regs.cop0,
FD(instr),
GetCop1RegDouble(
GetCop1Reg<double>(
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 <typename T>
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);
template <typename T>
void Cop1::ccond(Registers& regs, u32 instr, CompConds cond) {
T fs = GetCop1Reg<T>(regs.cop0, FS(instr));
T ft = GetCop1Reg<T>(regs.cop0, FT(instr));
fcr31.compare = condition;
fcr31.compare = CalculateCondition(fs, ft, cond);
}
void Cop1::cconds(Registers& regs, u32 instr, CompConds cond) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(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;
}
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) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fs / ft);
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs / ft);
}
void Cop1::divd(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<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs / ft);
}
void Cop1::muls(Registers &regs, 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<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(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<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs * ft);
}
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) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr));
float ft = GetCop1RegFloat(regs.cop0, FT(instr));
SetCop1RegFloat(regs.cop0, FD(instr), fs - ft);
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
float ft = GetCop1Reg<float>(regs.cop0, FT(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), fs - ft);
}
void Cop1::subd(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<double>(regs.cop0, FS(instr));
double ft = GetCop1Reg<double>(regs.cop0, FT(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), fs - ft);
}
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) {
SetCop1RegFloat(
SetCop1Reg<float>(
regs.cop0,
FD(instr),
GetCop1RegFloat(
GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
@@ -309,10 +294,10 @@ void Cop1::movs(Registers& regs, u32 instr) {
}
void Cop1::movd(Registers& regs, u32 instr) {
SetCop1RegDouble(
SetCop1Reg<double>(
regs.cop0,
FD(instr),
GetCop1RegDouble(
GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
@@ -342,10 +327,10 @@ void Cop1::movl(Registers& regs, u32 instr) {
}
void Cop1::negs(Registers &regs, u32 instr) {
SetCop1RegFloat(
SetCop1Reg<float>(
regs.cop0,
FD(instr),
-GetCop1RegFloat(
-GetCop1Reg<float>(
regs.cop0,
FS(instr)
)
@@ -353,10 +338,10 @@ void Cop1::negs(Registers &regs, u32 instr) {
}
void Cop1::negd(Registers &regs, u32 instr) {
SetCop1RegDouble(
SetCop1Reg<double>(
regs.cop0,
FD(instr),
-GetCop1RegDouble(
-GetCop1Reg<double>(
regs.cop0,
FS(instr)
)
@@ -364,101 +349,161 @@ void Cop1::negd(Registers &regs, u32 instr) {
}
void Cop1::sqrts(Registers &regs, u32 instr) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr));
SetCop1RegFloat(regs.cop0, FD(instr), sqrtf(fs));
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
SetCop1Reg<float>(regs.cop0, FD(instr), sqrtf(fs));
}
void Cop1::sqrtd(Registers &regs, u32 instr) {
double fs = GetCop1RegDouble(regs.cop0, FS(instr));
SetCop1RegDouble(regs.cop0, FD(instr), sqrt(fs));
double fs = GetCop1Reg<double>(regs.cop0, FS(instr));
SetCop1Reg<double>(regs.cop0, FD(instr), sqrt(fs));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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);
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<false>(regs, physical, regs.oldPC);
SetReg<u32>(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<u32>(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<false>(regs, physical, GetReg<u32>(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);
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<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) {
float fs = GetCop1RegFloat(regs.cop0, FS(instr));
float fs = GetCop1Reg<float>(regs.cop0, FS(instr));
s32 result = (s32)truncf(fs);
SetReg<u32>(regs.cop0, FD(instr), result);
}
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);
SetReg<u32>(regs.cop0, FD(instr), result);
}
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);
SetReg<u64>(regs.cop0, FD(instr), result);
}
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);
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) {
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) {
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) {