Does not quite fully work yet...

This commit is contained in:
2026-05-25 12:21:43 +02:00
parent 72253d9d6a
commit 76475271d1
8 changed files with 215 additions and 85 deletions
+1 -6
View File
@@ -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;
-1
View File
@@ -19,7 +19,6 @@ struct Core {
return instance;
}
static void MaybeIdleSkip();
static Registers &GetRegs() { return GetInstance().regs; }
static Mem &GetMem() { return *GetInstance().mem; }
+86 -8
View File
@@ -1,4 +1,6 @@
#include <Core.hpp>
#include <Scheduler.hpp>
#include "jit/helpers.hpp"
namespace n64 {
Interpreter::Interpreter(Mem &mem, Registers &regs) : 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) {
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<CachedBlock>();
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<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, {code, i, i});
return ExecuteCached();
}
} // namespace n64
+26 -7
View File
@@ -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<u32, MAX_INSTRUCTION_PER_LINE> code;
std::array<Instruction, MAX_INSTRUCTION_PER_LINE> code = {};
u32 cycles = 0;
u32 len = 0;
};
struct CachedBlock {
std::array<CachedLine*, MAX_LINES_PER_BLOCK / 4> lines;
std::array<CachedLine, MAX_LINES_PER_BLOCK / 4> lines = {};
bool valid = false;
};
using CachedBlocks = std::array<std::vector<CachedBlock>, (u64(std::numeric_limits<u32>::max()) + 1) / MAX_LINES_PER_BLOCK>;
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
struct CachedState {
CachedBlocks blocks;
CachedLine* lastLine = nullptr;
CachedState() { blocks.resize((u64(std::numeric_limits<u32>::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) \
+10 -2
View File
@@ -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<u8>(paddr, regs.Read<s64>(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<u32>(paddr, regs.Read<s64>(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<u32>(paddr, regs.Read<s64>(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<u16>(physical, regs.Read<s64>(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<u32>(physical, regs.Read<s64>(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<s64>(instr.rt()));
}
}
@@ -531,6 +535,7 @@ void Interpreter::sdl(const Instruction instr) {
const u64 data = mem.Read<u64>(paddr & ~7);
const u64 rt = regs.Read<s64>(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<u64>(paddr & ~7);
const u64 rt = regs.Read<s64>(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<u32>(paddr & ~3);
const u32 rt = regs.Read<s64>(instr.rt());
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt >> shift));
cachedState.EvictLine(address);
}
}
@@ -576,6 +583,7 @@ void Interpreter::swr(const Instruction instr) {
const u32 data = mem.Read<u32>(paddr & ~3);
const u32 rt = regs.Read<s64>(instr.rt());
mem.Write<u32>(paddr & ~3, (data & ~mask) | (rt << shift));
cachedState.EvictLine(address);
}
}
+38 -7
View File
@@ -20,6 +20,34 @@ static bool SpecialEndsBlock(const Instruction instr) {
}
}
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:
@@ -31,7 +59,16 @@ static bool InstrEndsBlock(const Instruction instr) {
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;
}
@@ -55,16 +92,10 @@ static bool IsBranchLikely(const Instruction instr) {
return false;
}
case Instruction::COP1:
{
if (instr.cop_rs() == 0x08) {
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
return true;
return false;
}
return false;
}
default:
return false;
}
+3 -2
View File
@@ -51,9 +51,10 @@ auto PI::BusRead<u8, true>(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",
+1 -2
View File
@@ -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;
}
}