diff --git a/.gitignore b/.gitignore index d16eab8..c72e622 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ saves/ .cache/ .vs/ .vscode/ +.zed/ out/ *.toml *.ini diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index e3123e4..38a62de 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -16,6 +16,8 @@ Core::Core() : cpuType = Interpreted; } else if (selectedCpu == "jit") { cpuType = DynamicRecompiler; + } else if (selectedCpu == "cached_interpreter") { + cpuType = CachedInterpreter; } else { panic("Unimplemented CPU type"); } @@ -63,7 +65,13 @@ void Core::LoadROM(const std::string &rom_) { } 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(); #ifdef KAIZEN_JIT_ENABLED @@ -115,8 +123,6 @@ void Core::Run(const float volumeL, const float volumeR) { const u32 taken = StepCPU(); cycles += taken; - - StepRSP(taken); frameCycles += taken; Scheduler::GetInstance().Tick(taken); } diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 8d317d5..a4d7165 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -10,7 +10,7 @@ namespace n64 { struct Core { - enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted; + enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter; explicit Core(); diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index dbed406..0e5573c 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -52,6 +52,18 @@ bool Interpreter::MaybeAdvance() { 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; @@ -101,23 +113,22 @@ u32 DivideAddr(u32 addr, u32 &offset) { return addr / MAX_LINES_PER_BLOCK; } -CachedLine *CachedState::GetLine(u64 addr) { +std::shared_ptr CachedState::GetLine(u64 addr) { u32 offset; u32 page = DivideAddr(addr, offset); - if (blocks[page] && blocks[page]->valid) - return &blocks[page]->lines[offset]; + if (blocks[page]) + return blocks[page]->lines[offset]; return nullptr; } -void CachedState::InsertLine(u64 addr, const CachedLine &line) { +void CachedState::InsertLine(u64 addr, std::shared_ptr 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; } @@ -125,8 +136,8 @@ void CachedState::EvictLine(u64 addr) { u32 offset; u32 page = DivideAddr(addr, offset); - if (blocks[page] && blocks[page]->valid) - blocks[page]->valid = false; + if (blocks[page]) + blocks[page] = nullptr; } u32 Interpreter::ExecuteCached() { @@ -141,13 +152,12 @@ u32 Interpreter::ExecuteCached() { 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; + + 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) @@ -182,7 +192,7 @@ u32 Interpreter::ExecuteCached() { } } - cachedState.InsertLine(block_addr, {code, i, i}); + cachedState.InsertLine(block_addr, std::make_shared(code, i, i)); return ExecuteCached(); } diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index a0408f2..716a672 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -15,8 +15,7 @@ struct CachedLine { }; struct CachedBlock { - std::array lines = {}; - bool valid = false; + std::array, MAX_LINES_PER_BLOCK / 4> lines = {}; }; using CachedBlocks = std::vector>; @@ -34,8 +33,8 @@ struct CachedState { exception = false; } - CachedLine *GetLine(u64); - void InsertLine(u64, const CachedLine &); + std::shared_ptr GetLine(u64); + void InsertLine(u64, std::shared_ptr); void EvictLine(u64); }; diff --git a/src/backend/core/RSP.cpp b/src/backend/core/RSP.cpp index b63bf50..d7fe1c6 100644 --- a/src/backend/core/RSP.cpp +++ b/src/backend/core/RSP.cpp @@ -5,29 +5,29 @@ namespace n64 { RSP::RSP() { Reset(); } void RSP::Reset() { - lastSuccessfulSPAddr.raw = 0; - lastSuccessfulDRAMAddr.raw = 0; - spStatus.raw = 0; - spStatus.halt = true; - oldPC = 0; - pc = 0; - nextPC = 4; - spDMASPAddr.raw = 0; - spDMADRAMAddr.raw = 0; - spDMALen.raw = 0; - dmem = {}; - imem = {}; - memset(vpr, 0, 32 * sizeof(VPR)); - memset(gpr, 0, 32 * sizeof(u32)); - memset(&vce, 0, sizeof(VPR)); - memset(&acc, 0, 3 * sizeof(VPR)); - memset(&vcc, 0, 2 * sizeof(VPR)); - memset(&vco, 0, 2 * sizeof(VPR)); - semaphore = false; - divIn = 0; - divOut = 0; - divInLoaded = false; - steps = 0; + lastSuccessfulSPAddr.raw = 0; + lastSuccessfulDRAMAddr.raw = 0; + spStatus.raw = 0; + spStatus.halt = true; + oldPC = 0; + pc = 0; + nextPC = 4; + spDMASPAddr.raw = 0; + spDMADRAMAddr.raw = 0; + spDMALen.raw = 0; + dmem = {}; + imem = {}; + memset(vpr, 0, 32 * sizeof(VPR)); + memset(gpr, 0, 32 * sizeof(u32)); + memset(&vce, 0, sizeof(VPR)); + memset(&acc, 0, 3 * sizeof(VPR)); + memset(&vcc, 0, 2 * sizeof(VPR)); + memset(&vco, 0, 2 * sizeof(VPR)); + semaphore = false; + divIn = 0; + divOut = 0; + divInLoaded = false; + steps = 0; } /* @@ -64,165 +64,173 @@ FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) { */ auto RSP::Read(const u32 addr) -> u32 { - switch (addr) { - case 0x04040000: - return lastSuccessfulSPAddr.raw & 0x1FF8; - case 0x04040004: - return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; - case 0x04040008: - case 0x0404000C: - return spDMALen.raw; - case 0x04040010: - return spStatus.raw; - case 0x04040014: - return spStatus.dmaFull; - case 0x04040018: - return 0; - case 0x0404001C: - return AcquireSemaphore(); - case 0x04080000: - return pc & 0xFFC; - default: - panic("Unimplemented SP register read {:08X}", addr); - } + switch (addr) { + case 0x04040000: + return lastSuccessfulSPAddr.raw & 0x1FF8; + case 0x04040004: + return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; + case 0x04040008: + case 0x0404000C: + return spDMALen.raw; + case 0x04040010: + return spStatus.raw; + case 0x04040014: + return spStatus.dmaFull; + case 0x04040018: + return 0; + case 0x0404001C: + return AcquireSemaphore(); + case 0x04080000: + return pc & 0xFFC; + default: + { + auto ®s = 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) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - MI &mi = mem.mmio.mi; - const auto write = SPStatusWrite{.raw = value}; - if (write.clearHalt && !write.setHalt) { - spStatus.halt = false; - } - if (write.setHalt && !write.clearHalt) { - regs.steps = 0; - spStatus.halt = true; - } - if (write.clearBroke) - spStatus.broke = false; - if (write.clearIntr && !write.setIntr) - mi.InterruptLower(MI::Interrupt::SP); - if (write.setIntr && !write.clearIntr) - mi.InterruptRaise(MI::Interrupt::SP); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + MI &mi = mem.mmio.mi; + const auto write = SPStatusWrite{.raw = value}; + if (write.clearHalt && !write.setHalt) { + spStatus.halt = false; + } + if (write.setHalt && !write.clearHalt) { + regs.steps = 0; + spStatus.halt = true; + } + if (write.clearBroke) + spStatus.broke = false; + if (write.clearIntr && !write.setIntr) + mi.InterruptLower(MI::Interrupt::SP); + if (write.setIntr && !write.clearIntr) + mi.InterruptRaise(MI::Interrupt::SP); #define CLEAR_SET(val, clear, set) \ - do { \ - if ((clear) && !(set)) \ - (val) = 0; \ - if ((set) && !(clear)) \ - (val) = 1; \ - } \ - while (0) + do { \ + if ((clear) && !(set)) \ + (val) = 0; \ + if ((set) && !(clear)) \ + (val) = 1; \ + } \ + while (0) - CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep); - CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak); - CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0); - CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1); - CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2); - CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3); - CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4); - CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5); - CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6); - CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); + CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep); + CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak); + CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0); + CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1); + CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2); + CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3); + CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4); + CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5); + CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6); + CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); #undef CLEAR_SET } template <> void RSP::DMA() { - Mem& mem = Core::GetMem(); - u32 length = spDMALen.len + 1; + Mem &mem = Core::GetMem(); + 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 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address); + u32 mem_address = spDMASPAddr.address & 0xFF8; + u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; + 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 j = 0; j < length; j++) { - mem.mmio.rdp.WriteRDRAM(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for (u32 j = 0; j < length; j++) { + mem.mmio.rdp.WriteRDRAM(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; - - 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); - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); + lastSuccessfulSPAddr.address = mem_address; + lastSuccessfulSPAddr.bank = spDMASPAddr.bank; + lastSuccessfulDRAMAddr.address = dram_address; + spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); } template <> void RSP::DMA() { - Mem& mem = Core::GetMem(); - u32 length = spDMALen.len + 1; + Mem &mem = Core::GetMem(); + 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 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address); + u32 mem_address = spDMASPAddr.address & 0xFF8; + u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; + 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 j = 0; j < length; j++) { - dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM(BYTE_ADDRESS(dram_address + j)); + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for (u32 j = 0; j < length; j++) { + dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM(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; - - 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); - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); + 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) { - switch (addr) { - case 0x04040000: - spDMASPAddr.raw = val & 0x1FF8; - break; - case 0x04040004: - spDMADRAMAddr.raw = val & 0xFFFFF8; - break; - case 0x04040008: - spDMALen.raw = val; - DMA(); - break; - case 0x0404000C: - spDMALen.raw = val; - DMA(); - break; - case 0x04040010: - WriteStatus(val); - break; - case 0x0404001C: - ReleaseSemaphore(); - break; - case 0x04080000: - if (spStatus.halt) { - SetPC(val); + switch (addr) { + case 0x04040000: + spDMASPAddr.raw = val & 0x1FF8; + break; + case 0x04040004: + spDMADRAMAddr.raw = val & 0xFFFFF8; + break; + case 0x04040008: + spDMALen.raw = val; + DMA(); + break; + case 0x0404000C: + spDMALen.raw = val; + DMA(); + break; + case 0x04040010: + WriteStatus(val); + break; + case 0x0404001C: + ReleaseSemaphore(); + break; + case 0x04080000: + if (spStatus.halt) { + 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 diff --git a/src/backend/core/registers/Registers.cpp b/src/backend/core/registers/Registers.cpp index e5f6a21..7543628 100644 --- a/src/backend/core/registers/Registers.cpp +++ b/src/backend/core/registers/Registers.cpp @@ -6,7 +6,10 @@ namespace n64 { #ifdef KAIZEN_JIT_ENABLED Registers::Registers(JIT &jit) : jit(jit) { Reset(); } #else -Registers::Registers() { Reset(); } +Registers::Registers() { + stack_trace.resize(0x10000000); + Reset(); +} #endif void Registers::Reset() { @@ -14,6 +17,7 @@ void Registers::Reset() { lo = 0; delaySlot = false; prevDelaySlot = false; + std::fill(stack_trace.begin(), stack_trace.end(), 0); gpr.fill(0); regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always diff --git a/src/backend/core/registers/Registers.hpp b/src/backend/core/registers/Registers.hpp index f137b34..1c28569 100644 --- a/src/backend/core/registers/Registers.hpp +++ b/src/backend/core/registers/Registers.hpp @@ -48,6 +48,14 @@ struct Registers { Cop0 cop0; Cop1 cop1; + std::vector 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; } u32 PopStalledCycles() { diff --git a/src/frontend/Settings/CPUSettings.cpp b/src/frontend/Settings/CPUSettings.cpp index 1664013..de3918e 100644 --- a/src/frontend/Settings/CPUSettings.cpp +++ b/src/frontend/Settings/CPUSettings.cpp @@ -4,7 +4,10 @@ #include CPUSettings::CPUSettings() { - if (Options::GetInstance().GetValue("cpu", "type") == "jit") { + auto selectedCpuType = Options::GetInstance().GetValue("cpu", "type"); + if (selectedCpuType == "jit") { + selectedCpuTypeIndex = 2; + } else if (selectedCpuType == "cached_interpreter") { selectedCpuTypeIndex = 1; } else { selectedCpuTypeIndex = 0; @@ -12,7 +15,7 @@ CPUSettings::CPUSettings() { } void CPUSettings::render() { - const char *items[] = {"Interpreter", + const char *items[] = {"Interpreter", "Cached Interpreter", #ifdef KAIZEN_JIT_ENABLED "Dynamic Recompiler" #endif @@ -37,6 +40,8 @@ void CPUSettings::render() { if (modified) { if (selectedCpuTypeIndex == 0) { Options::GetInstance().SetValue("cpu", "type", "interpreter"); + } else if (selectedCpuTypeIndex == 1) { + Options::GetInstance().SetValue("cpu", "type", "cached_interpreter"); } else { Options::GetInstance().SetValue("cpu", "type", "jit"); }