#include namespace n64 { Interpreter::Interpreter(ParallelRDP& parallel, Mem& mem, Registers& regs) : mem(mem), regs(regs) {} bool Interpreter::ShouldServiceInterrupt() const { const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; const bool interrupts_enabled = regs.cop0.status.ie == 1; const bool currently_handling_exception = regs.cop0.status.exl == 1; const bool currently_handling_error = regs.cop0.status.erl == 1; return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; } void Interpreter::CheckCompareInterrupt() { regs.cop0.count++; regs.cop0.count &= 0x1FFFFFFFF; if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { regs.cop0.cause.ip7 = 1; mem.mmio.mi.UpdateInterrupt(); } } int Interpreter::Step() { CheckCompareInterrupt(); regs.prevDelaySlot = regs.delaySlot; regs.delaySlot = false; if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { regs.cop0.HandleTLBException(regs.pc); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc); return 1; } u32 paddr = 0; if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { regs.cop0.HandleTLBException(regs.pc); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); return 1; } const u32 instruction = mem.Read(paddr); if (ShouldServiceInterrupt()) { regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc); return 1; } regs.oldPC = regs.pc; regs.pc = regs.nextPC; regs.nextPC += 4; Exec(instruction); return 1; } } // namespace n64