#include #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(instr.rs()); const u32 rt = regs.Read(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(result)); } } void Interpreter::addu(const Instruction instr) { const s32 rs = regs.Read(instr.rs()); const s32 rt = regs.Read(instr.rt()); const s32 result = rs + rt; regs.Write(instr.rd(), result); } void Interpreter::addi(const Instruction instr) { const u32 rs = regs.Read(instr.rs()); const u32 imm = static_cast(static_cast(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(result)); } } void Interpreter::addiu(const Instruction instr) { const s32 rs = regs.Read(instr.rs()); const s16 imm = static_cast(instr); const s32 result = rs + imm; regs.Write(instr.rt(), result); } void Interpreter::dadd(const Instruction instr) { const u64 rs = regs.Read(instr.rs()); const u64 rt = regs.Read(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(instr.rs()); const s64 rt = regs.Read(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(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(instr); const s64 rs = regs.Read(instr.rs()); regs.Write(instr.rt(), rs + imm); } void Interpreter::div(const Instruction instr) { const s64 dividend = regs.Read(instr.rs()); if (const s64 divisor = regs.Read(instr.rt()); divisor == 0) { regs.hi = dividend; if (dividend >= 0) { regs.lo = static_cast(-1); } else { regs.lo = static_cast(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(instr.rs()); if (const u32 divisor = regs.Read(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(instr.rs()); const s64 divisor = regs.Read(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(instr.rs()); const u64 divisor = regs.Read(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(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(paddr)); } } void Interpreter::lh(const Instruction instr) { const u64 address = regs.Read(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(paddr)); } } void Interpreter::lw(const Instruction instr) { const s16 offset = instr; const u64 address = regs.Read(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(physical)); } } void Interpreter::ll(const Instruction instr) { const u64 address = regs.Read(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(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(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(paddr & ~3); const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data << shift)); regs.Write(instr.rt(), result); } } void Interpreter::lwr(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~3); const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data >> shift)); regs.Write(instr.rt(), result); } } void Interpreter::ld(const Instruction instr) { const s64 address = regs.Read(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(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(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(paddr)); regs.cop0.llbit = true; regs.cop0.LLAddr = paddr >> 4; } } } void Interpreter::ldl(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~7); const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); regs.Write(instr.rt(), result); } } void Interpreter::ldr(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~7); const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); regs.Write(instr.rt(), result); } } void Interpreter::lbu(const Instruction instr) { const u64 address = regs.Read(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(paddr); regs.Write(instr.rt(), value); } } void Interpreter::lhu(const Instruction instr) { const s64 address = regs.Read(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(paddr); regs.Write(instr.rt(), value); } } void Interpreter::lwu(const Instruction instr) { const s64 address = regs.Read(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(paddr); regs.Write(instr.rt(), value); } } void Interpreter::sb(const Instruction instr) { const u64 address = regs.Read(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(paddr, regs.Read(instr.rt())); } } void Interpreter::sc(const Instruction instr) { const u64 address = regs.Read(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(paddr, regs.Read(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(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(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } } else { regs.Write(instr.rt(), 0); } } void Interpreter::sh(const Instruction instr) { const s64 address = regs.Read(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(physical, regs.Read(instr.rt())); } } void Interpreter::sw(const Instruction instr) { const s16 offset = instr; const u64 address = regs.Read(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(physical, regs.Read(instr.rt())); } } void Interpreter::sd(const Instruction instr) { const s64 address = regs.Read(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(instr.rt())); } } void Interpreter::sdl(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~7); const u64 rt = regs.Read(instr.rt()); mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); } } void Interpreter::sdr(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~7); const u64 rt = regs.Read(instr.rt()); mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); } } void Interpreter::swl(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~3); const u32 rt = regs.Read(instr.rt()); mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); } } void Interpreter::swr(const Instruction instr) { const u64 address = regs.Read(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(paddr & ~3); const u32 rt = regs.Read(instr.rt()); mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); } } void Interpreter::ori(const Instruction instr) { const s64 imm = (u16)instr; const s64 result = imm | regs.Read(instr.rs()); regs.Write(instr.rt(), result); } void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); } void Interpreter::nor(const Instruction instr) { regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(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(instr.rs()); branch(true, address); } void Interpreter::slti(const Instruction instr) { const s16 imm = instr; regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); } void Interpreter::sltiu(const Instruction instr) { const s16 imm = instr; regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); } void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); } void Interpreter::sltu(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); } void Interpreter::xori(const Instruction instr) { const s64 imm = (u16)instr; regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); } void Interpreter::xor_(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); } void Interpreter::andi(const Instruction instr) { const s64 imm = (u16)instr; regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); } void Interpreter::and_(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); } void Interpreter::sll(const Instruction instr) { const u8 sa = ((instr >> 6) & 0x1f); const s32 result = regs.Read(instr.rt()) << sa; regs.Write(instr.rd(), (s64)result); } void Interpreter::sllv(const Instruction instr) { const u8 sa = (regs.Read(instr.rs())) & 0x1F; const u32 rt = regs.Read(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(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(instr.rt()) << sa; regs.Write(instr.rd(), result); } void Interpreter::dsllv(const Instruction instr) { const s64 sa = regs.Read(instr.rs()) & 63; const s64 result = regs.Read(instr.rt()) << sa; regs.Write(instr.rd(), result); } void Interpreter::srl(const Instruction instr) { const u32 rt = regs.Read(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(instr.rs()) & 0x1F); const u32 rt = regs.Read(instr.rt()); const s32 result = rt >> sa; regs.Write(instr.rd(), (s64)result); } void Interpreter::dsrl(const Instruction instr) { const u64 rt = regs.Read(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(instr.rs()) & 63); const u64 rt = regs.Read(instr.rt()); const u64 result = rt >> amount; regs.Write(instr.rd(), s64(result)); } void Interpreter::dsrl32(const Instruction instr) { const u64 rt = regs.Read(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(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(instr.rs()); const s64 rt = regs.Read(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(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(instr.rt()); const s64 rs = regs.Read(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(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(instr.rt()); const s64 rs = regs.Read(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(instr.rt()); const u64 rs = regs.Read(instr.rs()); const u64 result = rs - rt; regs.Write(instr.rd(), s64(result)); } void Interpreter::sub(const Instruction instr) { const s32 rt = regs.Read(instr.rt()); const s32 rs = regs.Read(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(instr.rt()); const u32 rs = regs.Read(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(instr.rt()); const u64 rs = regs.Read(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(instr.rt()); const s64 rs = regs.Read(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(instr.rt()); const u32 rs = regs.Read(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(instr.rt()); const s32 rs = regs.Read(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(instr.rs()); } void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read(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(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(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