From 76475271d1d48dfbeed8266867837d558ad5e6cc Mon Sep 17 00:00:00 2001 From: iris Date: Mon, 25 May 2026 12:21:43 +0200 Subject: [PATCH] Does not quite fully work yet... --- src/backend/Core.cpp | 7 +- src/backend/Core.hpp | 1 - src/backend/core/Interpreter.cpp | 96 ++++++++++-- src/backend/core/Interpreter.hpp | 33 +++- src/backend/core/interpreter/instructions.cpp | 12 +- src/backend/core/jit/helpers.hpp | 143 +++++++++++------- src/backend/core/mmio/PI.cpp | 5 +- src/backend/core/registers/Cop0.cpp | 3 +- 8 files changed, 215 insertions(+), 85 deletions(-) diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index d674d57..e3123e4 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -64,7 +64,7 @@ void Core::LoadROM(const std::string &rom_) { u32 Core::StepCPU() { if (cpuType == Interpreted) - return interpreter.Step() + regs.PopStalledCycles(); + return interpreter.ExecuteCached() + regs.PopStalledCycles(); #ifdef KAIZEN_JIT_ENABLED if (cpuType == DynamicRecompiler) @@ -96,11 +96,6 @@ void Core::StepRSP(const u32 cpuCycles) { } } -void Core::MaybeIdleSkip() { - if (GetRegs().nextPC == GetRegs().pc) - Scheduler::GetInstance().SkipToNext(); -} - void Core::Run(const float volumeL, const float volumeR) { MMIO &mmio = mem->mmio; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 3777177..8d317d5 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -19,7 +19,6 @@ struct Core { return instance; } - static void MaybeIdleSkip(); static Registers &GetRegs() { return GetInstance().regs; } static Mem &GetMem() { return *GetInstance().mem; } diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index b9e9966..dbed406 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -1,4 +1,6 @@ #include +#include +#include "jit/helpers.hpp" namespace n64 { Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {} @@ -21,11 +23,11 @@ void Interpreter::CheckCompareInterrupt() const { } } -bool Interpreter::Fetch(Instruction &instr) { +bool Interpreter::Fetch(Instruction &instr, u64 vaddr) { 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); + 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; } @@ -69,7 +71,7 @@ bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) { return false; } - if (!Fetch(instr)) + if (!Fetch(instr, regs.pc)) return false; if (ShouldServiceInterrupt()) { @@ -94,18 +96,94 @@ u32 Interpreter::Step() { return 1; } -u32 DivideAddr(u32 addr, u32& offset) { - offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4; +u32 DivideAddr(u32 addr, u32 &offset) { + offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4; return addr / MAX_LINES_PER_BLOCK; } -CachedLine* GetLine(CachedState& cachedState, u32 addr) { +CachedLine *CachedState::GetLine(u64 addr) { u32 offset; u32 page = DivideAddr(addr, offset); - return cachedState.blocks[page][offset].lines[0]; + if (blocks[page] && blocks[page]->valid) + return &blocks[page]->lines[offset]; + + return nullptr; +} + +void CachedState::InsertLine(u64 addr, const CachedLine &line) { + u32 offset; + u32 page = DivideAddr(addr, offset); + + if (!blocks[page]) + blocks[page] = std::make_unique(); + + blocks[page]->valid = true; + blocks[page]->lines[offset] = line; +} + +void CachedState::EvictLine(u64 addr) { + u32 offset; + u32 page = DivideAddr(addr, offset); + + if (blocks[page] && blocks[page]->valid) + blocks[page]->valid = false; } 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); + if (IsBranchLikely(instr) && !regs.delaySlot) { + line->len -= 1; // Branch likely with false condition, it wasn't taken so don't execute the delay slot + // and remove it from the cache + if (line->len <= 0) + line->len = 1; + break; + } + } + + if (line->cycles == 0) + Scheduler::GetInstance().SkipToNext(); + + return line->cycles; + } + + std::array 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, {code, i, i}); + + return ExecuteCached(); } } // namespace n64 diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 9500e1b..a0408f2 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -9,21 +9,34 @@ static constexpr u32 MAX_INSTRUCTION_PER_LINE = 128; static constexpr u32 MAX_LINES_PER_BLOCK = 1 << 12; struct CachedLine { - std::array code; + std::array code = {}; u32 cycles = 0; u32 len = 0; }; struct CachedBlock { - std::array lines; + std::array lines = {}; + bool valid = false; }; -using CachedBlocks = std::array, (u64(std::numeric_limits::max()) + 1) / MAX_LINES_PER_BLOCK>; +using CachedBlocks = std::vector>; struct CachedState { - CachedBlocks blocks; - CachedLine* lastLine = nullptr; + CachedState() { blocks.resize((u64(std::numeric_limits::max()) + 1) / MAX_LINES_PER_BLOCK); } + CachedBlocks blocks = {}; bool exception = false; + + void Reset() { + for (auto &block : blocks) { + block.reset(); + } + + exception = false; + } + + CachedLine *GetLine(u64); + void InsertLine(u64, const CachedLine &); + void EvictLine(u64); }; struct Interpreter final { @@ -34,10 +47,16 @@ struct Interpreter final { bool FetchThenMaybeAdvance(Instruction &); bool MaybeAdvance(); - void Reset() { cop2Latch = {}; } + void Reset() { + cop2Latch = {}; + cachedState.Reset(); + } private: friend struct Cop1; + friend struct Mem; + + void MaybeIdleSkip(); CachedState cachedState; @@ -47,7 +66,7 @@ struct Interpreter final { Mem &mem; u64 cop2Latch{}; - bool Fetch(Instruction &); + bool Fetch(Instruction &, u64); void CacheTypeData(u8, u64, u32, u32); void CacheTypeInstruction(u8, u64, u32, u32); #define check_address_error(mask, vaddr) \ diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index 227bf14..55a6b34 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -140,7 +140,6 @@ void Interpreter::branch(const bool cond, const s64 address) { regs.delaySlot = true; if (cond) { regs.nextPC = address; - Core::MaybeIdleSkip(); } } @@ -148,7 +147,6 @@ void Interpreter::branch_likely(const bool cond, const s64 address) { if (cond) { regs.delaySlot = true; regs.nextPC = address; - Core::MaybeIdleSkip(); } else { regs.SetPC64(regs.nextPC); } @@ -409,6 +407,7 @@ void Interpreter::sb(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(paddr, regs.Read(instr.rt())); } } @@ -432,6 +431,7 @@ void Interpreter::sc(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } @@ -464,6 +464,7 @@ void Interpreter::scd(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } @@ -480,6 +481,7 @@ void Interpreter::sh(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -498,6 +500,7 @@ void Interpreter::sw(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -515,6 +518,7 @@ void Interpreter::sd(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + cachedState.EvictLine(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -531,6 +535,7 @@ void Interpreter::sdl(const Instruction instr) { const u64 data = mem.Read(paddr & ~7); const u64 rt = regs.Read(instr.rt()); mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); + cachedState.EvictLine(address); } } @@ -546,6 +551,7 @@ void Interpreter::sdr(const Instruction instr) { const u64 data = mem.Read(paddr & ~7); const u64 rt = regs.Read(instr.rt()); mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); + cachedState.EvictLine(address); } } @@ -561,6 +567,7 @@ void Interpreter::swl(const Instruction instr) { const u32 data = mem.Read(paddr & ~3); const u32 rt = regs.Read(instr.rt()); mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); + cachedState.EvictLine(address); } } @@ -576,6 +583,7 @@ void Interpreter::swr(const Instruction instr) { const u32 data = mem.Read(paddr & ~3); const u32 rt = regs.Read(instr.rt()); mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); + cachedState.EvictLine(address); } } diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index 93d7b9f..cd62a25 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -3,71 +3,102 @@ namespace n64 { static bool SpecialEndsBlock(const Instruction instr) { - switch (instr.special()) { - case Instruction::JR: - case Instruction::JALR: - case Instruction::SYSCALL: - case Instruction::BREAK: - case Instruction::TGE: - case Instruction::TGEU: - case Instruction::TLT: - case Instruction::TLTU: - case Instruction::TEQ: - case Instruction::TNE: - return true; - default: - return false; - } + switch (instr.special()) { + case Instruction::JR: + case Instruction::JALR: + case Instruction::SYSCALL: + case Instruction::BREAK: + case Instruction::TGE: + case Instruction::TGEU: + case Instruction::TLT: + case Instruction::TLTU: + case Instruction::TEQ: + case Instruction::TNE: + return true; + default: + return false; + } +} + +static bool InstrHasDelaySlot(const Instruction instr) { + switch (instr.opcode()) { + case Instruction::SPECIAL: + if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR) + return true; + return false; + case Instruction::REGIMM: + case Instruction::J: + case Instruction::JAL: + case Instruction::BEQ: + case Instruction::BNE: + case Instruction::BLEZ: + case Instruction::BGTZ: + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::COP1: + if (instr.cop_rs() == 8) + return true; + + return false; + default: + return false; + } } static bool InstrEndsBlock(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::SPECIAL: - return SpecialEndsBlock(instr); - case Instruction::REGIMM: - case Instruction::J: - case Instruction::JAL: - case Instruction::BEQ: - case Instruction::BNE: - case Instruction::BLEZ: - case Instruction::BGTZ: - return true; - default: - return false; - } + switch (instr.opcode()) { + case Instruction::SPECIAL: + return SpecialEndsBlock(instr); + case Instruction::REGIMM: + case Instruction::J: + case Instruction::JAL: + case Instruction::BEQ: + case Instruction::BNE: + case Instruction::BLEZ: + case Instruction::BGTZ: + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::COP1: + if (instr.cop_rs() == 8) + return true; + + return false; + default: + return false; + } } static bool IsBranchLikely(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::BEQL: - case Instruction::BNEL: - case Instruction::BLEZL: - case Instruction::BGTZL: - return true; - case Instruction::REGIMM: - switch (instr.regimm()) { - case Instruction::BLTZL: - case Instruction::BGEZL: - case Instruction::BLTZALL: - case Instruction::BGEZALL: - return true; - default: - return false; - } - case Instruction::COP1: - { - if (instr.cop_rs() == 0x08) { - if (instr.cop_rt() == 2 || instr.cop_rt() == 3) - return true; + switch (instr.opcode()) { + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::REGIMM: + switch (instr.regimm()) { + case Instruction::BLTZL: + case Instruction::BGEZL: + case Instruction::BLTZALL: + case Instruction::BGEZALL: + return true; + default: + return false; + } + case Instruction::COP1: + if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3)) + return true; return false; - } - - return false; + default: + return false; } - default: - return false; - } } #ifdef _WIN32 diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index c9eb2b8..98726bb 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -51,9 +51,10 @@ auto PI::BusRead(u32 addr) -> u8 { n64::Mem &mem = n64::Core::GetMem(); switch (addr) { case REGION_PI_UNKNOWN: + mem.DumpRDRAM(); panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " - "returning FF because it is not emulated", - addr); + "returning FF because it is not emulated (pc: 0x{:016X})", + addr, (u64)Core::GetRegs().oldPC); case REGION_PI_64DD_REG: panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " "returning FF because it is not emulated", diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index 6661aed..d919752 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -366,10 +366,9 @@ void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) { u16 vectorOffset = 0x0180; if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { if (!status.exl) { + vectorOffset = 0x0000; if (is64BitAddressing) vectorOffset = 0x0080; - else - vectorOffset = 0x0000; } }