Files
kaizen/src/backend/core/interpreter/instructions.cpp

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