848 lines
27 KiB
C++
848 lines
27 KiB
C++
#include <Core.hpp>
|
|
|
|
#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 Interpreter::add(const Instruction instr) {
|
|
const u32 rs = regs.Read<s32>(instr.rs());
|
|
const u32 rt = regs.Read<s32>(instr.rt());
|
|
if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rd(), static_cast<s32>(result));
|
|
}
|
|
}
|
|
|
|
void Interpreter::addu(const Instruction instr) {
|
|
const s32 rs = regs.Read<s32>(instr.rs());
|
|
const s32 rt = regs.Read<s32>(instr.rt());
|
|
const s32 result = rs + rt;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::addi(const Instruction instr) {
|
|
const u32 rs = regs.Read<s64>(instr.rs());
|
|
const u32 imm = static_cast<s32>(static_cast<s16>(instr));
|
|
if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), static_cast<s32>(result));
|
|
}
|
|
}
|
|
|
|
void Interpreter::addiu(const Instruction instr) {
|
|
const s32 rs = regs.Read<s32>(instr.rs());
|
|
const s16 imm = static_cast<s16>(instr);
|
|
const s32 result = rs + imm;
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
|
|
void Interpreter::dadd(const Instruction instr) {
|
|
const u64 rs = regs.Read<s64>(instr.rs());
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::daddu(const Instruction instr) {
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
regs.Write(instr.rd(), rs + rt);
|
|
}
|
|
|
|
void Interpreter::daddi(const Instruction instr) {
|
|
const u64 imm = s64(s16(instr));
|
|
const u64 rs = regs.Read<s64>(instr.rs());
|
|
if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::daddiu(const Instruction instr) {
|
|
const s16 imm = static_cast<s16>(instr);
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
regs.Write(instr.rt(), rs + imm);
|
|
}
|
|
|
|
void Interpreter::div(const Instruction instr) {
|
|
const s64 dividend = regs.Read<s32>(instr.rs());
|
|
|
|
if (const s64 divisor = regs.Read<s32>(instr.rt()); divisor == 0) {
|
|
regs.hi = dividend;
|
|
if (dividend >= 0) {
|
|
regs.lo = static_cast<s64>(-1);
|
|
} else {
|
|
regs.lo = static_cast<s64>(1);
|
|
}
|
|
} else {
|
|
const s32 quotient = dividend / divisor;
|
|
const s32 remainder = dividend % divisor;
|
|
regs.lo = quotient;
|
|
regs.hi = remainder;
|
|
}
|
|
}
|
|
|
|
void Interpreter::divu(const Instruction instr) {
|
|
const u32 dividend = regs.Read<s64>(instr.rs());
|
|
if (const u32 divisor = regs.Read<s64>(instr.rt()); divisor == 0) {
|
|
regs.lo = -1;
|
|
regs.hi = (s32)dividend;
|
|
} else {
|
|
const s32 quotient = (s32)(dividend / divisor);
|
|
const s32 remainder = (s32)(dividend % divisor);
|
|
regs.lo = quotient;
|
|
regs.hi = remainder;
|
|
}
|
|
}
|
|
|
|
void Interpreter::ddiv(const Instruction instr) {
|
|
const s64 dividend = regs.Read<s64>(instr.rs());
|
|
const s64 divisor = regs.Read<s64>(instr.rt());
|
|
if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) {
|
|
regs.lo = dividend;
|
|
regs.hi = 0;
|
|
} else if (divisor == 0) {
|
|
regs.hi = dividend;
|
|
if (dividend >= 0) {
|
|
regs.lo = -1;
|
|
} else {
|
|
regs.lo = 1;
|
|
}
|
|
} else {
|
|
const s64 quotient = dividend / divisor;
|
|
const s64 remainder = dividend % divisor;
|
|
regs.lo = quotient;
|
|
regs.hi = remainder;
|
|
}
|
|
}
|
|
|
|
void Interpreter::ddivu(const Instruction instr) {
|
|
const u64 dividend = regs.Read<s64>(instr.rs());
|
|
const u64 divisor = regs.Read<s64>(instr.rt());
|
|
if (divisor == 0) {
|
|
regs.lo = -1;
|
|
regs.hi = (s64)dividend;
|
|
} else {
|
|
const u64 quotient = dividend / divisor;
|
|
const u64 remainder = dividend % divisor;
|
|
regs.lo = (s64)quotient;
|
|
regs.hi = (s64)remainder;
|
|
}
|
|
}
|
|
|
|
void Interpreter::branch(const bool cond, const s64 address) {
|
|
regs.delaySlot = true;
|
|
if (cond) {
|
|
regs.nextPC = address;
|
|
}
|
|
}
|
|
|
|
void Interpreter::branch_likely(const bool cond, const s64 address) {
|
|
if (cond) {
|
|
regs.delaySlot = true;
|
|
regs.nextPC = address;
|
|
} else {
|
|
regs.SetPC64(regs.nextPC);
|
|
}
|
|
}
|
|
|
|
void Interpreter::b(const Instruction instr, const bool cond) {
|
|
const s16 imm = instr;
|
|
const s64 offset = u64((s64)imm) << 2;
|
|
const s64 address = regs.pc + offset;
|
|
branch(cond, address);
|
|
}
|
|
|
|
void Interpreter::blink(const Instruction instr, const bool cond) {
|
|
regs.Write(31, regs.nextPC);
|
|
const s16 imm = instr;
|
|
const s64 offset = u64((s64)imm) << 2;
|
|
const s64 address = regs.pc + offset;
|
|
branch(cond, address);
|
|
}
|
|
|
|
void Interpreter::bl(const Instruction instr, const bool cond) {
|
|
const s16 imm = instr;
|
|
const s64 offset = u64((s64)imm) << 2;
|
|
const s64 address = regs.pc + offset;
|
|
branch_likely(cond, address);
|
|
}
|
|
|
|
void Interpreter::bllink(const Instruction instr, const bool cond) {
|
|
regs.Write(31, regs.nextPC);
|
|
const s16 imm = instr;
|
|
const s64 offset = u64((s64)imm) << 2;
|
|
const s64 address = regs.pc + offset;
|
|
branch_likely(cond, address);
|
|
}
|
|
|
|
void Interpreter::lui(const Instruction instr) {
|
|
u64 val = s64((s16)instr);
|
|
val <<= 16;
|
|
regs.Write(instr.rt(), val);
|
|
}
|
|
|
|
void Interpreter::lb(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), (s8)mem.Read<u8>(paddr));
|
|
}
|
|
}
|
|
|
|
void Interpreter::lh(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (check_address_error(0b1, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), (s16)mem.Read<u16>(paddr));
|
|
}
|
|
}
|
|
|
|
void Interpreter::lw(const Instruction instr) {
|
|
const s16 offset = instr;
|
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
|
if (check_address_error(0b11, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 physical = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), (s32)mem.Read<u32>(physical));
|
|
}
|
|
}
|
|
|
|
void Interpreter::ll(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 physical;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const s32 result = mem.Read<u32>(physical);
|
|
if (check_address_error(0b11, address)) {
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
regs.Write(instr.rt(), result);
|
|
|
|
regs.cop0.llbit = true;
|
|
regs.cop0.LLAddr = physical >> 4;
|
|
}
|
|
}
|
|
|
|
void Interpreter::lwl(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const u32 shift = 8 * ((address ^ 0) & 3);
|
|
const u32 mask = 0xFFFFFFFF << shift;
|
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
|
const s32 result = s32((regs.Read<s64>(instr.rt()) & ~mask) | (data << shift));
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::lwr(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const u32 shift = 8 * ((address ^ 3) & 3);
|
|
const u32 mask = 0xFFFFFFFF >> shift;
|
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
|
const s32 result = s32((regs.Read<s64>(instr.rt()) & ~mask) | (data >> shift));
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::ld(const Instruction instr) {
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (check_address_error(0b111, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const s64 value = mem.Read<u64>(paddr);
|
|
regs.Write(instr.rt(), value);
|
|
}
|
|
}
|
|
|
|
void Interpreter::lld(const Instruction instr) {
|
|
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
|
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
if (check_address_error(0b111, address)) {
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rt(), mem.Read<u64>(paddr));
|
|
regs.cop0.llbit = true;
|
|
regs.cop0.LLAddr = paddr >> 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Interpreter::ldl(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const s32 shift = 8 * ((address ^ 0) & 7);
|
|
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
|
const s64 result = (s64)((regs.Read<s64>(instr.rt()) & ~mask) | (data << shift));
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::ldr(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const s32 shift = 8 * ((address ^ 7) & 7);
|
|
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
|
const s64 result = (s64)((regs.Read<s64>(instr.rt()) & ~mask) | (data >> shift));
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::lbu(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const u8 value = mem.Read<u8>(paddr);
|
|
regs.Write(instr.rt(), value);
|
|
}
|
|
}
|
|
|
|
void Interpreter::lhu(const Instruction instr) {
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (check_address_error(0b1, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const u16 value = mem.Read<u16>(paddr);
|
|
regs.Write(instr.rt(), value);
|
|
}
|
|
}
|
|
|
|
void Interpreter::lwu(const Instruction instr) {
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (check_address_error(0b11, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
|
} else {
|
|
const u32 value = mem.Read<u32>(paddr);
|
|
regs.Write(instr.rt(), value);
|
|
}
|
|
}
|
|
|
|
void Interpreter::sb(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write<u8>(paddr, regs.Read<s64>(instr.rt()));
|
|
}
|
|
}
|
|
|
|
void Interpreter::sc(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
|
|
if (regs.cop0.llbit) {
|
|
regs.cop0.llbit = false;
|
|
|
|
if (check_address_error(0b11, address)) {
|
|
regs.Write(instr.rt(), 0);
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.Write(instr.rt(), 0);
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
|
regs.Write(instr.rt(), 1);
|
|
}
|
|
} else {
|
|
regs.Write(instr.rt(), 0);
|
|
}
|
|
}
|
|
|
|
void Interpreter::scd(const Instruction instr) {
|
|
if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) {
|
|
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
|
|
if (regs.cop0.llbit) {
|
|
regs.cop0.llbit = false;
|
|
|
|
if (check_address_error(0b111, address)) {
|
|
regs.Write(instr.rt(), 0);
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.Write(instr.rt(), 0);
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write<u32>(paddr, regs.Read<s64>(instr.rt()));
|
|
regs.Write(instr.rt(), 1);
|
|
}
|
|
} else {
|
|
regs.Write(instr.rt(), 0);
|
|
}
|
|
}
|
|
|
|
void Interpreter::sh(const Instruction instr) {
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
|
|
u32 physical;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write<u16>(physical, regs.Read<s64>(instr.rt()));
|
|
}
|
|
}
|
|
|
|
void Interpreter::sw(const Instruction instr) {
|
|
const s16 offset = instr;
|
|
const u64 address = regs.Read<s64>(instr.rs()) + offset;
|
|
if (check_address_error(0b11, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 physical;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write<u32>(physical, regs.Read<s64>(instr.rt()));
|
|
}
|
|
}
|
|
|
|
void Interpreter::sd(const Instruction instr) {
|
|
const s64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
if (check_address_error(0b111, address)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC);
|
|
return;
|
|
}
|
|
|
|
u32 physical;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
mem.Write(physical, regs.Read<s64>(instr.rt()));
|
|
}
|
|
}
|
|
|
|
void Interpreter::sdl(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
const s32 shift = 8 * ((address ^ 0) & 7);
|
|
const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift));
|
|
}
|
|
}
|
|
|
|
void Interpreter::sdr(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
const s32 shift = 8 * ((address ^ 7) & 7);
|
|
const u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
|
const u64 data = mem.Read<u64>(paddr & ~7);
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
mem.Write(paddr & ~7, (data & ~mask) | (rt << shift));
|
|
}
|
|
}
|
|
|
|
void Interpreter::swl(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
const u32 shift = 8 * ((address ^ 0) & 3);
|
|
const u32 mask = 0xFFFFFFFF >> shift;
|
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
|
|
}
|
|
}
|
|
|
|
void Interpreter::swr(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs()) + (s16)instr;
|
|
u32 paddr;
|
|
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
|
regs.cop0.HandleTLBException(address);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
|
} else {
|
|
const u32 shift = 8 * ((address ^ 3) & 3);
|
|
const u32 mask = 0xFFFFFFFF << shift;
|
|
const u32 data = mem.Read<u32>(paddr & ~3);
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
|
|
}
|
|
}
|
|
|
|
void Interpreter::ori(const Instruction instr) {
|
|
const s64 imm = (u16)instr;
|
|
const s64 result = imm | regs.Read<s64>(instr.rs());
|
|
regs.Write(instr.rt(), result);
|
|
}
|
|
|
|
void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())); }
|
|
|
|
void Interpreter::nor(const Instruction instr) {
|
|
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
|
|
}
|
|
|
|
void Interpreter::j(const Instruction instr) {
|
|
const s32 target = (instr & 0x3ffffff) << 2;
|
|
const s64 address = (regs.oldPC & ~0xfffffff) | target;
|
|
|
|
branch(true, address);
|
|
}
|
|
|
|
void Interpreter::jal(const Instruction instr) {
|
|
regs.Write(31, regs.nextPC);
|
|
j(instr);
|
|
}
|
|
|
|
void Interpreter::jalr(const Instruction instr) {
|
|
regs.Write(instr.rd(), regs.nextPC);
|
|
jr(instr);
|
|
}
|
|
|
|
void Interpreter::jr(const Instruction instr) {
|
|
const u64 address = regs.Read<s64>(instr.rs());
|
|
branch(true, address);
|
|
}
|
|
|
|
void Interpreter::slti(const Instruction instr) {
|
|
const s16 imm = instr;
|
|
regs.Write(instr.rt(), regs.Read<s64>(instr.rs()) < imm);
|
|
}
|
|
|
|
void Interpreter::sltiu(const Instruction instr) {
|
|
const s16 imm = instr;
|
|
regs.Write(instr.rt(), regs.Read<u64>(instr.rs()) < imm);
|
|
}
|
|
|
|
void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt())); }
|
|
|
|
void Interpreter::sltu(const Instruction instr) {
|
|
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
|
}
|
|
|
|
void Interpreter::xori(const Instruction instr) {
|
|
const s64 imm = (u16)instr;
|
|
regs.Write(instr.rt(), regs.Read<s64>(instr.rs()) ^ imm);
|
|
}
|
|
|
|
void Interpreter::xor_(const Instruction instr) {
|
|
regs.Write(instr.rd(), regs.Read<s64>(instr.rt()) ^ regs.Read<s64>(instr.rs()));
|
|
}
|
|
|
|
void Interpreter::andi(const Instruction instr) {
|
|
const s64 imm = (u16)instr;
|
|
regs.Write(instr.rt(), regs.Read<s64>(instr.rs()) & imm);
|
|
}
|
|
|
|
void Interpreter::and_(const Instruction instr) {
|
|
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) & regs.Read<s64>(instr.rt()));
|
|
}
|
|
|
|
void Interpreter::sll(const Instruction instr) {
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s32 result = regs.Read<s64>(instr.rt()) << sa;
|
|
regs.Write(instr.rd(), (s64)result);
|
|
}
|
|
|
|
void Interpreter::sllv(const Instruction instr) {
|
|
const u8 sa = (regs.Read<s64>(instr.rs())) & 0x1F;
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
const s32 result = rt << sa;
|
|
regs.Write(instr.rd(), (s64)result);
|
|
}
|
|
|
|
void Interpreter::dsll32(const Instruction instr) {
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s64 result = regs.Read<s64>(instr.rt()) << (sa + 32);
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsll(const Instruction instr) {
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s64 result = regs.Read<s64>(instr.rt()) << sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsllv(const Instruction instr) {
|
|
const s64 sa = regs.Read<s64>(instr.rs()) & 63;
|
|
const s64 result = regs.Read<s64>(instr.rt()) << sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::srl(const Instruction instr) {
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const u32 result = rt >> sa;
|
|
regs.Write(instr.rd(), (s32)result);
|
|
}
|
|
|
|
void Interpreter::srlv(const Instruction instr) {
|
|
const u8 sa = (regs.Read<s64>(instr.rs()) & 0x1F);
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
const s32 result = rt >> sa;
|
|
regs.Write(instr.rd(), (s64)result);
|
|
}
|
|
|
|
void Interpreter::dsrl(const Instruction instr) {
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const u64 result = rt >> sa;
|
|
regs.Write(instr.rd(), s64(result));
|
|
}
|
|
|
|
void Interpreter::dsrlv(const Instruction instr) {
|
|
const u8 amount = (regs.Read<s64>(instr.rs()) & 63);
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
const u64 result = rt >> amount;
|
|
regs.Write(instr.rd(), s64(result));
|
|
}
|
|
|
|
void Interpreter::dsrl32(const Instruction instr) {
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const u64 result = rt >> (sa + 32);
|
|
regs.Write(instr.rd(), s64(result));
|
|
}
|
|
|
|
void Interpreter::sra(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s32 result = rt >> sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::srav(const Instruction instr) {
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = rs & 0x1f;
|
|
const s32 result = rt >> sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsra(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s64 result = rt >> sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsrav(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
const s64 sa = rs & 63;
|
|
const s64 result = rt >> sa;
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsra32(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const u8 sa = ((instr >> 6) & 0x1f);
|
|
const s64 result = rt >> (sa + 32);
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
|
|
void Interpreter::dsub(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::dsubu(const Instruction instr) {
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
const u64 rs = regs.Read<s64>(instr.rs());
|
|
const u64 result = rs - rt;
|
|
regs.Write(instr.rd(), s64(result));
|
|
}
|
|
|
|
void Interpreter::sub(const Instruction instr) {
|
|
const s32 rt = regs.Read<s64>(instr.rt());
|
|
const s32 rs = regs.Read<s64>(instr.rs());
|
|
const s32 result = rs - rt;
|
|
if (check_signed_underflow(rs, rt, result)) {
|
|
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
|
} else {
|
|
regs.Write(instr.rd(), result);
|
|
}
|
|
}
|
|
|
|
void Interpreter::subu(const Instruction instr) {
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
const u32 rs = regs.Read<s64>(instr.rs());
|
|
const u32 result = rs - rt;
|
|
regs.Write(instr.rd(), (s64)((s32)result));
|
|
}
|
|
|
|
void Interpreter::dmultu(const Instruction instr) {
|
|
const u64 rt = regs.Read<s64>(instr.rt());
|
|
const u64 rs = regs.Read<s64>(instr.rs());
|
|
const u128 result = (u128)rt * (u128)rs;
|
|
regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF);
|
|
regs.hi = (s64)(result >> 64);
|
|
}
|
|
|
|
void Interpreter::dmult(const Instruction instr) {
|
|
const s64 rt = regs.Read<s64>(instr.rt());
|
|
const s64 rs = regs.Read<s64>(instr.rs());
|
|
const s128 result = (s128)rt * (s128)rs;
|
|
regs.lo = result & 0xFFFFFFFFFFFFFFFF;
|
|
regs.hi = result >> 64;
|
|
}
|
|
|
|
void Interpreter::multu(const Instruction instr) {
|
|
const u32 rt = regs.Read<s64>(instr.rt());
|
|
const u32 rs = regs.Read<s64>(instr.rs());
|
|
const u64 result = (u64)rt * (u64)rs;
|
|
regs.lo = (s64)((s32)result);
|
|
regs.hi = (s64)((s32)(result >> 32));
|
|
}
|
|
|
|
void Interpreter::mult(const Instruction instr) {
|
|
const s32 rt = regs.Read<s64>(instr.rt());
|
|
const s32 rs = regs.Read<s64>(instr.rs());
|
|
const s64 result = (s64)rt * (s64)rs;
|
|
regs.lo = (s64)((s32)result);
|
|
regs.hi = (s64)((s32)(result >> 32));
|
|
}
|
|
|
|
void Interpreter::mflo(const Instruction instr) { regs.Write(instr.rd(), regs.lo); }
|
|
|
|
void Interpreter::mfhi(const Instruction instr) { regs.Write(instr.rd(), regs.hi); }
|
|
|
|
void Interpreter::mtlo(const Instruction instr) { regs.lo = regs.Read<s64>(instr.rs()); }
|
|
|
|
void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read<s64>(instr.rs()); }
|
|
|
|
void Interpreter::trap(const bool cond) const {
|
|
Cop0& cop0 = Core::GetRegs().cop0;
|
|
if (cond) {
|
|
cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC);
|
|
}
|
|
}
|
|
|
|
void Interpreter::mtc2(const Instruction instr) { cop2Latch = regs.Read<s64>(instr.rt()); }
|
|
|
|
void Interpreter::mfc2(const Instruction instr) {
|
|
const s32 value = cop2Latch;
|
|
regs.Write(instr.rt(), value);
|
|
}
|
|
|
|
void Interpreter::dmtc2(const Instruction instr) { cop2Latch = regs.Read<s64>(instr.rt()); }
|
|
|
|
void Interpreter::dmfc2(const Instruction instr) { regs.Write(instr.rt(), cop2Latch); }
|
|
|
|
void Interpreter::ctc2(const Instruction) {}
|
|
|
|
void Interpreter::cfc2(const Instruction) {}
|
|
} // namespace n64
|