200 lines
5.1 KiB
C++
200 lines
5.1 KiB
C++
#include <Core.hpp>
|
|
#include <Scheduler.hpp>
|
|
#include "jit/helpers.hpp"
|
|
|
|
namespace n64 {
|
|
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
|
|
|
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() const {
|
|
regs.cop0.count++;
|
|
regs.cop0.count &= 0x1FFFFFFFF;
|
|
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
|
regs.cop0.cause.ip7 = 1;
|
|
mem.mmio.mi.UpdateInterrupt();
|
|
}
|
|
}
|
|
|
|
bool Interpreter::Fetch(Instruction &instr, u64 vaddr) {
|
|
u32 paddr = 0;
|
|
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
|
regs.cop0.HandleTLBException(vaddr);
|
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, vaddr);
|
|
return false;
|
|
}
|
|
|
|
instr = mem.Read<u32>(paddr);
|
|
return true;
|
|
}
|
|
|
|
bool Interpreter::MaybeAdvance() {
|
|
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(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
|
return false;
|
|
}
|
|
|
|
if (ShouldServiceInterrupt()) {
|
|
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
|
return false;
|
|
}
|
|
|
|
regs.push_to_stack_trace(regs.pc);
|
|
if ((u32)regs.pc == 0x4) {
|
|
auto ®s = Core::GetRegs();
|
|
std::sort(regs.stack_trace.begin(), regs.stack_trace.end());
|
|
|
|
std::println("Stack trace:");
|
|
for (int i = 0; i < regs.stack_trace.size(); i++) {
|
|
std::println(" [{:016X}]", regs.stack_trace[i]);
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
regs.oldPC = regs.pc;
|
|
regs.pc = regs.nextPC;
|
|
regs.nextPC += 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) {
|
|
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(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
|
return false;
|
|
}
|
|
|
|
if (!Fetch(instr, regs.pc))
|
|
return false;
|
|
|
|
if (ShouldServiceInterrupt()) {
|
|
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
|
return false;
|
|
}
|
|
|
|
regs.oldPC = regs.pc;
|
|
regs.pc = regs.nextPC;
|
|
regs.nextPC += 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
u32 Interpreter::Step() {
|
|
Instruction instr;
|
|
if (!FetchThenMaybeAdvance(instr))
|
|
return 1;
|
|
|
|
DecodeExecute(instr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
u32 DivideAddr(u32 addr, u32 &offset) {
|
|
offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4;
|
|
return addr / MAX_LINES_PER_BLOCK;
|
|
}
|
|
|
|
std::shared_ptr<CachedLine> CachedState::GetLine(u64 addr) {
|
|
u32 offset;
|
|
u32 page = DivideAddr(addr, offset);
|
|
if (blocks[page])
|
|
return blocks[page]->lines[offset];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void CachedState::InsertLine(u64 addr, std::shared_ptr<CachedLine> line) {
|
|
u32 offset;
|
|
u32 page = DivideAddr(addr, offset);
|
|
|
|
if (!blocks[page])
|
|
blocks[page] = std::make_unique<CachedBlock>();
|
|
|
|
blocks[page]->lines[offset] = line;
|
|
}
|
|
|
|
void CachedState::EvictLine(u64 addr) {
|
|
u32 offset;
|
|
u32 page = DivideAddr(addr, offset);
|
|
|
|
if (blocks[page])
|
|
blocks[page] = nullptr;
|
|
}
|
|
|
|
u32 Interpreter::ExecuteCached() {
|
|
auto addr = regs.pc;
|
|
auto block_addr = addr;
|
|
auto line = cachedState.GetLine(addr);
|
|
|
|
if (line) {
|
|
for (u32 i = 0; i < line->len; i++) {
|
|
if (!MaybeAdvance())
|
|
return i + 1;
|
|
|
|
Instruction instr = line->code[i];
|
|
DecodeExecute(instr);
|
|
|
|
Core::GetInstance().StepRSP(1);
|
|
|
|
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
|
if (IsBranchLikely(instr) && !regs.delaySlot)
|
|
break;
|
|
}
|
|
|
|
if (line->cycles == 0)
|
|
Scheduler::GetInstance().SkipToNext();
|
|
|
|
return line->cycles;
|
|
}
|
|
|
|
std::array<Instruction, MAX_INSTRUCTION_PER_LINE> code;
|
|
|
|
u32 i;
|
|
bool fetchDelaySlot = false;
|
|
for (i = 0; i < MAX_INSTRUCTION_PER_LINE; i++) {
|
|
Instruction instr;
|
|
if (!Fetch(instr, addr))
|
|
return i + 1;
|
|
|
|
addr += 4;
|
|
code[i] = instr;
|
|
|
|
if (fetchDelaySlot) {
|
|
i++;
|
|
break;
|
|
}
|
|
|
|
if (InstrEndsBlock(instr)) {
|
|
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) {
|
|
fetchDelaySlot = true;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
cachedState.InsertLine(block_addr, std::make_shared<CachedLine>(code, i, i));
|
|
|
|
return ExecuteCached();
|
|
}
|
|
} // namespace n64
|