Implement overflow and STORE and LOAD exceptions (add, addi, dadd, daddi, sub, dsub, lwl, ldl, lwr, ldr, swl, swr, sdl, sdr)

This commit is contained in:
CocoSimone
2022-09-23 15:31:54 +02:00
parent c1db93fd71
commit 19bf3e55e5

View File

@@ -3,14 +3,20 @@
#define se_imm(x) ((s16)((x) & 0xFFFF))
#define check_address_error(mask, addr) (((!regs.cop0.is_64bit_addressing) && (s32)(addr) != (addr)) || (((addr) & (mask)) != 0))
#define check_signed_overflow(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 {
void Cpu::add(u32 instr) {
s32 rs = (s32)regs.gpr[RS(instr)];
s32 rt = (s32)regs.gpr[RT(instr)];
s32 result = rs + rt;
regs.gpr[RD(instr)] = result;
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);
} else {
regs.gpr[RD(instr)] = s32(result);
}
}
void Cpu::addu(u32 instr) {
@@ -21,10 +27,14 @@ void Cpu::addu(u32 instr) {
}
void Cpu::addi(u32 instr) {
s32 rs = (s32)regs.gpr[RS(instr)];
s16 imm = (s16)(instr);
s32 result = rs + imm;
regs.gpr[RT(instr)] = result;
u32 rs = (s32)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);
} else {
regs.gpr[RD(instr)] = s32(result);
}
}
void Cpu::addiu(u32 instr) {
@@ -35,9 +45,14 @@ void Cpu::addiu(u32 instr) {
}
void Cpu::dadd(u32 instr) {
s64 rs = regs.gpr[RS(instr)];
s64 rt = regs.gpr[RT(instr)];
regs.gpr[RD(instr)] = rs + rt;
u64 rs = regs.gpr[RS(instr)];
u64 rt = regs.gpr[RT(instr)];
u64 result = rt + rs;
if(check_signed_overflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC);
} else {
regs.gpr[RD(instr)] = result;
}
}
void Cpu::daddu(u32 instr) {
@@ -47,9 +62,14 @@ void Cpu::daddu(u32 instr) {
}
void Cpu::daddi(u32 instr) {
s16 imm = (s16)(instr);
s64 rs = regs.gpr[RS(instr)];
regs.gpr[RT(instr)] = rs + imm;
u64 imm = s64(s16(instr));
u64 rs = regs.gpr[RS(instr)];
u64 result = imm + rs;
if(check_signed_overflow(rs, imm, result)) {
FireException(regs, Overflow, 0, regs.oldPC);
} else {
regs.gpr[RD(instr)] = result;
}
}
void Cpu::daddiu(u32 instr) {
@@ -214,24 +234,34 @@ void Cpu::ll(Mem& mem, u32 instr) {
}
void Cpu::lwl(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC);
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
s32 result = (s32)((rt & ~mask) | (data << shift));
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
s32 result = (s32) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
regs.gpr[RT(instr)] = result;
}
}
void Cpu::lwr(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if(!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, GetTLBExceptionCode(regs.cop0.tlbError, LOAD), 0, regs.oldPC);
} else {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
s32 result = (s32)((rt & ~mask) | (data >> shift));
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
s32 result = (s32) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
regs.gpr[RT(instr)] = result;
}
}
void Cpu::ld(Mem& mem, u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -246,37 +276,47 @@ void Cpu::ld(Mem& mem, u32 instr) {
void Cpu::lld(Mem& mem, u32 instr) {
s64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 physical;
if (!MapVAddr(regs, LOAD, address, physical)) {
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
} else {
regs.gpr[RT(instr)] = mem.Read64<false>(regs, physical, regs.oldPC);
regs.gpr[RT(instr)] = mem.Read64<false>(regs, paddr, regs.oldPC);
}
regs.cop0.llbit = true;
regs.cop0.LLAddr = physical >> 4;
regs.cop0.LLAddr = paddr >> 4;
}
void Cpu::ldl(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr = 0;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
} else {
s32 shift = 8 * ((address ^ 0) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
u64 data = mem.Read64(regs, address & ~7, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
s64 result = (s64)((rt & ~mask) | (data << shift));
u64 data = mem.Read64<false>(regs, paddr & ~7, regs.oldPC);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
regs.gpr[RT(instr)] = result;
}
}
void Cpu::ldr(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, LOAD, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
} else {
s32 shift = 8 * ((address ^ 7) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = mem.Read64(regs, address & ~7, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
s64 result = (s64)((rt & ~mask) | (data >> shift));
u64 data = mem.Read64<false>(regs, paddr & ~7, regs.oldPC);
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
regs.gpr[RT(instr)] = result;
}
}
void Cpu::lbu(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
@@ -372,39 +412,63 @@ void Cpu::sd(Mem& mem, u32 instr) {
}
void Cpu::sdl(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, regs.oldPC);
} else {
s32 shift = 8 * ((address ^ 0) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
u64 data = mem.Read64(regs, address & ~7, regs.oldPC);
u64 data = mem.Read64<false>(regs, paddr & ~7, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
mem.Write64(regs, address & ~7, (data & ~mask) | (rt >> shift), regs.oldPC);
mem.Write64<false>(regs, paddr & ~7, (data & ~mask) | (rt >> shift), regs.oldPC);
}
}
void Cpu::sdr(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, regs.oldPC);
} else {
s32 shift = 8 * ((address ^ 7) & 7);
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
u64 data = mem.Read64(regs, address & ~7, regs.oldPC);
u64 data = mem.Read64<false>(regs, paddr & ~7, regs.oldPC);
s64 rt = regs.gpr[RT(instr)];
mem.Write64(regs, address & ~7, (data & ~mask) | (rt << shift), regs.oldPC);
mem.Write64<false>(regs, paddr & ~7, (data & ~mask) | (rt << shift), regs.oldPC);
}
}
void Cpu::swl(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, regs.oldPC);
} else {
u32 shift = 8 * ((address ^ 0) & 3);
u32 mask = 0xFFFFFFFF >> shift;
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
u32 rt = regs.gpr[RT(instr)];
mem.Write32(regs, address & ~3, (data & ~mask) | (rt >> shift), regs.oldPC);
mem.Write32<false>(regs, paddr & ~3, (data & ~mask) | (rt >> shift), regs.oldPC);
}
}
void Cpu::swr(Mem& mem, u32 instr) {
u32 address = regs.gpr[RS(instr)] + (s16)instr;
u64 address = regs.gpr[RS(instr)] + (s16)instr;
u32 paddr;
if (!MapVAddr(regs, STORE, address, paddr)) {
HandleTLBException(regs, address);
FireException(regs, ExceptionCode::AddressErrorStore, 0, regs.oldPC);
} else {
u32 shift = 8 * ((address ^ 3) & 3);
u32 mask = 0xFFFFFFFF << shift;
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
u32 rt = regs.gpr[RT(instr)];
mem.Write32(regs, address & ~3, (data & ~mask) | (rt << shift), regs.oldPC);
mem.Write32<false>(regs, paddr & ~3, (data & ~mask) | (rt << shift), regs.oldPC);
}
}
void Cpu::ori(u32 instr) {
@@ -593,8 +657,12 @@ void Cpu::dsub(u32 instr) {
s64 rt = regs.gpr[RT(instr)];
s64 rs = regs.gpr[RS(instr)];
s64 result = rs - rt;
if(check_signed_underflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC);
} else {
regs.gpr[RD(instr)] = result;
}
}
void Cpu::dsubu(u32 instr) {
u64 rt = regs.gpr[RT(instr)];
@@ -607,7 +675,11 @@ void Cpu::sub(u32 instr) {
s32 rt = regs.gpr[RT(instr)];
s32 rs = regs.gpr[RS(instr)];
s32 result = rs - rt;
regs.gpr[RD(instr)] = (s64)result;
if(check_signed_underflow(rs, rt, result)) {
FireException(regs, Overflow, 0, regs.oldPC);
} else {
regs.gpr[RD(instr)] = result;
}
}
void Cpu::subu(u32 instr) {