Files
kaizen/src/backend/core/interpreter/instructions.cpp
2025-07-29 11:08:05 +02:00

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