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:
@@ -3,14 +3,20 @@
|
|||||||
|
|
||||||
#define se_imm(x) ((s16)((x) & 0xFFFF))
|
#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_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 {
|
namespace n64 {
|
||||||
|
|
||||||
void Cpu::add(u32 instr) {
|
void Cpu::add(u32 instr) {
|
||||||
s32 rs = (s32)regs.gpr[RS(instr)];
|
u32 rs = (s32)regs.gpr[RS(instr)];
|
||||||
s32 rt = (s32)regs.gpr[RT(instr)];
|
u32 rt = (s32)regs.gpr[RT(instr)];
|
||||||
s32 result = rs + rt;
|
u32 result = rs + rt;
|
||||||
regs.gpr[RD(instr)] = result;
|
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) {
|
void Cpu::addu(u32 instr) {
|
||||||
@@ -21,10 +27,14 @@ void Cpu::addu(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::addi(u32 instr) {
|
void Cpu::addi(u32 instr) {
|
||||||
s32 rs = (s32)regs.gpr[RS(instr)];
|
u32 rs = (s32)regs.gpr[RS(instr)];
|
||||||
s16 imm = (s16)(instr);
|
u32 imm = s32(s16(instr));
|
||||||
s32 result = rs + imm;
|
u32 result = rs + imm;
|
||||||
regs.gpr[RT(instr)] = result;
|
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) {
|
void Cpu::addiu(u32 instr) {
|
||||||
@@ -35,9 +45,14 @@ void Cpu::addiu(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::dadd(u32 instr) {
|
void Cpu::dadd(u32 instr) {
|
||||||
s64 rs = regs.gpr[RS(instr)];
|
u64 rs = regs.gpr[RS(instr)];
|
||||||
s64 rt = regs.gpr[RT(instr)];
|
u64 rt = regs.gpr[RT(instr)];
|
||||||
regs.gpr[RD(instr)] = rs + rt;
|
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) {
|
void Cpu::daddu(u32 instr) {
|
||||||
@@ -47,9 +62,14 @@ void Cpu::daddu(u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::daddi(u32 instr) {
|
void Cpu::daddi(u32 instr) {
|
||||||
s16 imm = (s16)(instr);
|
u64 imm = s64(s16(instr));
|
||||||
s64 rs = regs.gpr[RS(instr)];
|
u64 rs = regs.gpr[RS(instr)];
|
||||||
regs.gpr[RT(instr)] = rs + imm;
|
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) {
|
void Cpu::daddiu(u32 instr) {
|
||||||
@@ -214,24 +234,34 @@ void Cpu::ll(Mem& mem, u32 instr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::lwl(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 shift = 8 * ((address ^ 0) & 3);
|
||||||
u32 mask = 0xFFFFFFFF << shift;
|
u32 mask = 0xFFFFFFFF << shift;
|
||||||
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
|
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
|
||||||
s64 rt = regs.gpr[RT(instr)];
|
s32 result = (s32) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
|
||||||
s32 result = (s32)((rt & ~mask) | (data << shift));
|
|
||||||
regs.gpr[RT(instr)] = result;
|
regs.gpr[RT(instr)] = result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::lwr(Mem& mem, u32 instr) {
|
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 shift = 8 * ((address ^ 3) & 3);
|
||||||
u32 mask = 0xFFFFFFFF >> shift;
|
u32 mask = 0xFFFFFFFF >> shift;
|
||||||
u32 data = mem.Read32(regs, address & ~3, regs.oldPC);
|
u32 data = mem.Read32<false>(regs, paddr & ~3, regs.oldPC);
|
||||||
s64 rt = regs.gpr[RT(instr)];
|
s32 result = (s32) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
|
||||||
s32 result = (s32)((rt & ~mask) | (data >> shift));
|
|
||||||
regs.gpr[RT(instr)] = result;
|
regs.gpr[RT(instr)] = result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::ld(Mem& mem, u32 instr) {
|
void Cpu::ld(Mem& mem, u32 instr) {
|
||||||
s64 address = regs.gpr[RS(instr)] + (s16)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) {
|
void Cpu::lld(Mem& mem, u32 instr) {
|
||||||
s64 address = regs.gpr[RS(instr)] + (s16)instr;
|
s64 address = regs.gpr[RS(instr)] + (s16)instr;
|
||||||
u32 physical;
|
u32 paddr;
|
||||||
if (!MapVAddr(regs, LOAD, address, physical)) {
|
if (!MapVAddr(regs, LOAD, address, paddr)) {
|
||||||
HandleTLBException(regs, address);
|
HandleTLBException(regs, address);
|
||||||
FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
FireException(regs, ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||||
} else {
|
} 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.llbit = true;
|
||||||
regs.cop0.LLAddr = physical >> 4;
|
regs.cop0.LLAddr = paddr >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cpu::ldl(Mem& mem, u32 instr) {
|
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);
|
s32 shift = 8 * ((address ^ 0) & 7);
|
||||||
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
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)];
|
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data << shift));
|
||||||
s64 result = (s64)((rt & ~mask) | (data << shift));
|
|
||||||
regs.gpr[RT(instr)] = result;
|
regs.gpr[RT(instr)] = result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::ldr(Mem& mem, u32 instr) {
|
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);
|
s32 shift = 8 * ((address ^ 7) & 7);
|
||||||
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
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)];
|
s64 result = (s64) ((regs.gpr[RT(instr)] & ~mask) | (data >> shift));
|
||||||
s64 result = (s64)((rt & ~mask) | (data >> shift));
|
|
||||||
regs.gpr[RT(instr)] = result;
|
regs.gpr[RT(instr)] = result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::lbu(Mem& mem, u32 instr) {
|
void Cpu::lbu(Mem& mem, u32 instr) {
|
||||||
u32 address = regs.gpr[RS(instr)] + (s16)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) {
|
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);
|
s32 shift = 8 * ((address ^ 0) & 7);
|
||||||
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
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)];
|
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) {
|
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);
|
s32 shift = 8 * ((address ^ 7) & 7);
|
||||||
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
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)];
|
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) {
|
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 shift = 8 * ((address ^ 0) & 3);
|
||||||
u32 mask = 0xFFFFFFFF >> shift;
|
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)];
|
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) {
|
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 shift = 8 * ((address ^ 3) & 3);
|
||||||
u32 mask = 0xFFFFFFFF << shift;
|
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)];
|
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) {
|
void Cpu::ori(u32 instr) {
|
||||||
@@ -593,8 +657,12 @@ void Cpu::dsub(u32 instr) {
|
|||||||
s64 rt = regs.gpr[RT(instr)];
|
s64 rt = regs.gpr[RT(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)) {
|
||||||
|
FireException(regs, Overflow, 0, regs.oldPC);
|
||||||
|
} else {
|
||||||
regs.gpr[RD(instr)] = result;
|
regs.gpr[RD(instr)] = result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Cpu::dsubu(u32 instr) {
|
void Cpu::dsubu(u32 instr) {
|
||||||
u64 rt = regs.gpr[RT(instr)];
|
u64 rt = regs.gpr[RT(instr)];
|
||||||
@@ -607,7 +675,11 @@ void Cpu::sub(u32 instr) {
|
|||||||
s32 rt = regs.gpr[RT(instr)];
|
s32 rt = regs.gpr[RT(instr)];
|
||||||
s32 rs = regs.gpr[RS(instr)];
|
s32 rs = regs.gpr[RS(instr)];
|
||||||
s32 result = rs - rt;
|
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) {
|
void Cpu::subu(u32 instr) {
|
||||||
|
|||||||
Reference in New Issue
Block a user