Figure out why the program counter never stops increasing after a certain point

This commit is contained in:
2026-05-25 15:58:54 +02:00
parent 76475271d1
commit 3b7bdceabd
9 changed files with 218 additions and 177 deletions
+1
View File
@@ -4,6 +4,7 @@ saves/
.cache/ .cache/
.vs/ .vs/
.vscode/ .vscode/
.zed/
out/ out/
*.toml *.toml
*.ini *.ini
+9 -3
View File
@@ -16,6 +16,8 @@ Core::Core() :
cpuType = Interpreted; cpuType = Interpreted;
} else if (selectedCpu == "jit") { } else if (selectedCpu == "jit") {
cpuType = DynamicRecompiler; cpuType = DynamicRecompiler;
} else if (selectedCpu == "cached_interpreter") {
cpuType = CachedInterpreter;
} else { } else {
panic("Unimplemented CPU type"); panic("Unimplemented CPU type");
} }
@@ -63,7 +65,13 @@ void Core::LoadROM(const std::string &rom_) {
} }
u32 Core::StepCPU() { u32 Core::StepCPU() {
if (cpuType == Interpreted) if (cpuType == Interpreted) {
auto taken = interpreter.Step() + regs.PopStalledCycles();
StepRSP(taken);
return taken;
}
if (cpuType == CachedInterpreter)
return interpreter.ExecuteCached() + regs.PopStalledCycles(); return interpreter.ExecuteCached() + regs.PopStalledCycles();
#ifdef KAIZEN_JIT_ENABLED #ifdef KAIZEN_JIT_ENABLED
@@ -115,8 +123,6 @@ void Core::Run(const float volumeL, const float volumeR) {
const u32 taken = StepCPU(); const u32 taken = StepCPU();
cycles += taken; cycles += taken;
StepRSP(taken);
frameCycles += taken; frameCycles += taken;
Scheduler::GetInstance().Tick(taken); Scheduler::GetInstance().Tick(taken);
} }
+1 -1
View File
@@ -10,7 +10,7 @@
namespace n64 { namespace n64 {
struct Core { struct Core {
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted; enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
explicit Core(); explicit Core();
+24 -14
View File
@@ -52,6 +52,18 @@ bool Interpreter::MaybeAdvance() {
return false; return false;
} }
regs.push_to_stack_trace(regs.pc);
if ((u32)regs.pc == 0x4) {
auto &regs = 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.oldPC = regs.pc;
regs.pc = regs.nextPC; regs.pc = regs.nextPC;
regs.nextPC += 4; regs.nextPC += 4;
@@ -101,23 +113,22 @@ u32 DivideAddr(u32 addr, u32 &offset) {
return addr / MAX_LINES_PER_BLOCK; return addr / MAX_LINES_PER_BLOCK;
} }
CachedLine *CachedState::GetLine(u64 addr) { std::shared_ptr<CachedLine> CachedState::GetLine(u64 addr) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (blocks[page] && blocks[page]->valid) if (blocks[page])
return &blocks[page]->lines[offset]; return blocks[page]->lines[offset];
return nullptr; return nullptr;
} }
void CachedState::InsertLine(u64 addr, const CachedLine &line) { void CachedState::InsertLine(u64 addr, std::shared_ptr<CachedLine> line) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (!blocks[page]) if (!blocks[page])
blocks[page] = std::make_unique<CachedBlock>(); blocks[page] = std::make_unique<CachedBlock>();
blocks[page]->valid = true;
blocks[page]->lines[offset] = line; blocks[page]->lines[offset] = line;
} }
@@ -125,8 +136,8 @@ void CachedState::EvictLine(u64 addr) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (blocks[page] && blocks[page]->valid) if (blocks[page])
blocks[page]->valid = false; blocks[page] = nullptr;
} }
u32 Interpreter::ExecuteCached() { u32 Interpreter::ExecuteCached() {
@@ -141,13 +152,12 @@ u32 Interpreter::ExecuteCached() {
Instruction instr = line->code[i]; Instruction instr = line->code[i];
DecodeExecute(instr); 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 Core::GetInstance().StepRSP(1);
// and remove it from the cache
if (line->len <= 0) // Branch likely with false condition, it wasn't taken so don't execute the delay slot
line->len = 1; if (IsBranchLikely(instr) && !regs.delaySlot)
break; break;
}
} }
if (line->cycles == 0) if (line->cycles == 0)
@@ -182,7 +192,7 @@ u32 Interpreter::ExecuteCached() {
} }
} }
cachedState.InsertLine(block_addr, {code, i, i}); cachedState.InsertLine(block_addr, std::make_shared<CachedLine>(code, i, i));
return ExecuteCached(); return ExecuteCached();
} }
+3 -4
View File
@@ -15,8 +15,7 @@ struct CachedLine {
}; };
struct CachedBlock { struct CachedBlock {
std::array<CachedLine, MAX_LINES_PER_BLOCK / 4> lines = {}; std::array<std::shared_ptr<CachedLine>, MAX_LINES_PER_BLOCK / 4> lines = {};
bool valid = false;
}; };
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>; using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
@@ -34,8 +33,8 @@ struct CachedState {
exception = false; exception = false;
} }
CachedLine *GetLine(u64); std::shared_ptr<CachedLine> GetLine(u64);
void InsertLine(u64, const CachedLine &); void InsertLine(u64, std::shared_ptr<CachedLine>);
void EvictLine(u64); void EvictLine(u64);
}; };
+160 -152
View File
@@ -5,29 +5,29 @@ namespace n64 {
RSP::RSP() { Reset(); } RSP::RSP() { Reset(); }
void RSP::Reset() { void RSP::Reset() {
lastSuccessfulSPAddr.raw = 0; lastSuccessfulSPAddr.raw = 0;
lastSuccessfulDRAMAddr.raw = 0; lastSuccessfulDRAMAddr.raw = 0;
spStatus.raw = 0; spStatus.raw = 0;
spStatus.halt = true; spStatus.halt = true;
oldPC = 0; oldPC = 0;
pc = 0; pc = 0;
nextPC = 4; nextPC = 4;
spDMASPAddr.raw = 0; spDMASPAddr.raw = 0;
spDMADRAMAddr.raw = 0; spDMADRAMAddr.raw = 0;
spDMALen.raw = 0; spDMALen.raw = 0;
dmem = {}; dmem = {};
imem = {}; imem = {};
memset(vpr, 0, 32 * sizeof(VPR)); memset(vpr, 0, 32 * sizeof(VPR));
memset(gpr, 0, 32 * sizeof(u32)); memset(gpr, 0, 32 * sizeof(u32));
memset(&vce, 0, sizeof(VPR)); memset(&vce, 0, sizeof(VPR));
memset(&acc, 0, 3 * sizeof(VPR)); memset(&acc, 0, 3 * sizeof(VPR));
memset(&vcc, 0, 2 * sizeof(VPR)); memset(&vcc, 0, 2 * sizeof(VPR));
memset(&vco, 0, 2 * sizeof(VPR)); memset(&vco, 0, 2 * sizeof(VPR));
semaphore = false; semaphore = false;
divIn = 0; divIn = 0;
divOut = 0; divOut = 0;
divInLoaded = false; divInLoaded = false;
steps = 0; steps = 0;
} }
/* /*
@@ -64,165 +64,173 @@ FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) {
*/ */
auto RSP::Read(const u32 addr) -> u32 { auto RSP::Read(const u32 addr) -> u32 {
switch (addr) { switch (addr) {
case 0x04040000: case 0x04040000:
return lastSuccessfulSPAddr.raw & 0x1FF8; return lastSuccessfulSPAddr.raw & 0x1FF8;
case 0x04040004: case 0x04040004:
return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; return lastSuccessfulDRAMAddr.raw & 0xFFFFF8;
case 0x04040008: case 0x04040008:
case 0x0404000C: case 0x0404000C:
return spDMALen.raw; return spDMALen.raw;
case 0x04040010: case 0x04040010:
return spStatus.raw; return spStatus.raw;
case 0x04040014: case 0x04040014:
return spStatus.dmaFull; return spStatus.dmaFull;
case 0x04040018: case 0x04040018:
return 0; return 0;
case 0x0404001C: case 0x0404001C:
return AcquireSemaphore(); return AcquireSemaphore();
case 0x04080000: case 0x04080000:
return pc & 0xFFC; return pc & 0xFFC;
default: default:
panic("Unimplemented SP register read {:08X}", addr); {
} auto &regs = Core::GetRegs();
std::println("Stack trace:");
for (int i = 0; i < regs.stack_trace.size(); i++) {
std::println(" [{:016X}]", regs.stack_trace[i]);
}
panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr,
(u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]);
}
}
} }
void RSP::WriteStatus(const u32 value) { void RSP::WriteStatus(const u32 value) {
Mem& mem = Core::GetMem(); Mem &mem = Core::GetMem();
Registers& regs = Core::GetRegs(); Registers &regs = Core::GetRegs();
MI &mi = mem.mmio.mi; MI &mi = mem.mmio.mi;
const auto write = SPStatusWrite{.raw = value}; const auto write = SPStatusWrite{.raw = value};
if (write.clearHalt && !write.setHalt) { if (write.clearHalt && !write.setHalt) {
spStatus.halt = false; spStatus.halt = false;
} }
if (write.setHalt && !write.clearHalt) { if (write.setHalt && !write.clearHalt) {
regs.steps = 0; regs.steps = 0;
spStatus.halt = true; spStatus.halt = true;
} }
if (write.clearBroke) if (write.clearBroke)
spStatus.broke = false; spStatus.broke = false;
if (write.clearIntr && !write.setIntr) if (write.clearIntr && !write.setIntr)
mi.InterruptLower(MI::Interrupt::SP); mi.InterruptLower(MI::Interrupt::SP);
if (write.setIntr && !write.clearIntr) if (write.setIntr && !write.clearIntr)
mi.InterruptRaise(MI::Interrupt::SP); mi.InterruptRaise(MI::Interrupt::SP);
#define CLEAR_SET(val, clear, set) \ #define CLEAR_SET(val, clear, set) \
do { \ do { \
if ((clear) && !(set)) \ if ((clear) && !(set)) \
(val) = 0; \ (val) = 0; \
if ((set) && !(clear)) \ if ((set) && !(clear)) \
(val) = 1; \ (val) = 1; \
} \ } \
while (0) while (0)
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep); CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak); CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0); CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0);
CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1); CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1);
CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2); CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2);
CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3); CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3);
CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4); CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4);
CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5); CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5);
CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6); CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6);
CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7);
#undef CLEAR_SET #undef CLEAR_SET
} }
template <> template <>
void RSP::DMA<true>() { void RSP::DMA<true>() {
Mem& mem = Core::GetMem(); Mem &mem = Core::GetMem();
u32 length = spDMALen.len + 1; u32 length = spDMALen.len + 1;
length = (length + 0x7) & ~0x7; length = (length + 0x7) & ~0x7;
const auto &src = spDMASPAddr.bank ? imem : dmem; const auto &src = spDMASPAddr.bank ? imem : dmem;
u32 mem_address = spDMASPAddr.address & 0xFF8; u32 mem_address = spDMASPAddr.address & 0xFF8;
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address); trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address);
for (u32 i = 0; i < spDMALen.count + 1; i++) { for (u32 i = 0; i < spDMALen.count + 1; i++) {
for (u32 j = 0; j < length; j++) { for (u32 j = 0; j < length; j++) {
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
}
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
dram_address += (length + skip);
dram_address &= 0xFFFFF8;
mem_address += length;
mem_address &= 0xFF8;
} }
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
const int skip = i == spDMALen.count ? 0 : spDMALen.skip; lastSuccessfulSPAddr.address = mem_address;
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
dram_address += (length + skip); lastSuccessfulDRAMAddr.address = dram_address;
dram_address &= 0xFFFFF8; spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
mem_address += length;
mem_address &= 0xFF8;
}
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
lastSuccessfulSPAddr.address = mem_address;
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
lastSuccessfulDRAMAddr.address = dram_address;
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
} }
template <> template <>
void RSP::DMA<false>() { void RSP::DMA<false>() {
Mem& mem = Core::GetMem(); Mem &mem = Core::GetMem();
u32 length = spDMALen.len + 1; u32 length = spDMALen.len + 1;
length = (length + 0x7) & ~0x7; length = (length + 0x7) & ~0x7;
auto &dst = spDMASPAddr.bank ? imem : dmem; auto &dst = spDMASPAddr.bank ? imem : dmem;
u32 mem_address = spDMASPAddr.address & 0xFF8; u32 mem_address = spDMASPAddr.address & 0xFF8;
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address); trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
for (u32 i = 0; i < spDMALen.count + 1; i++) { for (u32 i = 0; i < spDMALen.count + 1; i++) {
for (u32 j = 0; j < length; j++) { for (u32 j = 0; j < length; j++) {
dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j)); dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j));
}
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
dram_address += (length + skip);
dram_address &= 0xFFFFF8;
mem_address += length;
mem_address &= 0xFF8;
} }
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
const int skip = i == spDMALen.count ? 0 : spDMALen.skip; lastSuccessfulSPAddr.address = mem_address;
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
dram_address += (length + skip); lastSuccessfulDRAMAddr.address = dram_address;
dram_address &= 0xFFFFF8; spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
mem_address += length;
mem_address &= 0xFF8;
}
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
lastSuccessfulSPAddr.address = mem_address;
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
lastSuccessfulDRAMAddr.address = dram_address;
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
} }
void RSP::Write(const u32 addr, const u32 val) { void RSP::Write(const u32 addr, const u32 val) {
switch (addr) { switch (addr) {
case 0x04040000: case 0x04040000:
spDMASPAddr.raw = val & 0x1FF8; spDMASPAddr.raw = val & 0x1FF8;
break; break;
case 0x04040004: case 0x04040004:
spDMADRAMAddr.raw = val & 0xFFFFF8; spDMADRAMAddr.raw = val & 0xFFFFF8;
break; break;
case 0x04040008: case 0x04040008:
spDMALen.raw = val; spDMALen.raw = val;
DMA<false>(); DMA<false>();
break; break;
case 0x0404000C: case 0x0404000C:
spDMALen.raw = val; spDMALen.raw = val;
DMA<true>(); DMA<true>();
break; break;
case 0x04040010: case 0x04040010:
WriteStatus(val); WriteStatus(val);
break; break;
case 0x0404001C: case 0x0404001C:
ReleaseSemaphore(); ReleaseSemaphore();
break; break;
case 0x04080000: case 0x04080000:
if (spStatus.halt) { if (spStatus.halt) {
SetPC(val); SetPC(val);
}
break;
default:
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
} }
break;
default:
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
}
} }
} // namespace n64 } // namespace n64
+5 -1
View File
@@ -6,7 +6,10 @@ namespace n64 {
#ifdef KAIZEN_JIT_ENABLED #ifdef KAIZEN_JIT_ENABLED
Registers::Registers(JIT &jit) : jit(jit) { Reset(); } Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
#else #else
Registers::Registers() { Reset(); } Registers::Registers() {
stack_trace.resize(0x10000000);
Reset();
}
#endif #endif
void Registers::Reset() { void Registers::Reset() {
@@ -14,6 +17,7 @@ void Registers::Reset() {
lo = 0; lo = 0;
delaySlot = false; delaySlot = false;
prevDelaySlot = false; prevDelaySlot = false;
std::fill(stack_trace.begin(), stack_trace.end(), 0);
gpr.fill(0); gpr.fill(0);
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
+8
View File
@@ -48,6 +48,14 @@ struct Registers {
Cop0 cop0; Cop0 cop0;
Cop1 cop1; Cop1 cop1;
std::vector<u64> stack_trace;
int stack_count = 0;
void push_to_stack_trace(s64 val) {
stack_trace[stack_count++] = val;
stack_count %= stack_trace.size();
}
void CpuStall(u32 cycles) { extraCycles += cycles; } void CpuStall(u32 cycles) { extraCycles += cycles; }
u32 PopStalledCycles() { u32 PopStalledCycles() {
+7 -2
View File
@@ -4,7 +4,10 @@
#include <imgui.h> #include <imgui.h>
CPUSettings::CPUSettings() { CPUSettings::CPUSettings() {
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") { auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
if (selectedCpuType == "jit") {
selectedCpuTypeIndex = 2;
} else if (selectedCpuType == "cached_interpreter") {
selectedCpuTypeIndex = 1; selectedCpuTypeIndex = 1;
} else { } else {
selectedCpuTypeIndex = 0; selectedCpuTypeIndex = 0;
@@ -12,7 +15,7 @@ CPUSettings::CPUSettings() {
} }
void CPUSettings::render() { void CPUSettings::render() {
const char *items[] = {"Interpreter", const char *items[] = {"Interpreter", "Cached Interpreter",
#ifdef KAIZEN_JIT_ENABLED #ifdef KAIZEN_JIT_ENABLED
"Dynamic Recompiler" "Dynamic Recompiler"
#endif #endif
@@ -37,6 +40,8 @@ void CPUSettings::render() {
if (modified) { if (modified) {
if (selectedCpuTypeIndex == 0) { if (selectedCpuTypeIndex == 0) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter"); Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
} else if (selectedCpuTypeIndex == 1) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
} else { } else {
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit"); Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
} }