diff --git a/.gitignore b/.gitignore index d16eab8..3665593 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ saves/ .cache/ .vs/ .vscode/ +.zed/ out/ *.toml *.ini @@ -26,4 +27,5 @@ compile_commands.json *.diagsession tests/ .DS_Store -resources/version.hpp \ No newline at end of file +resources/version.hpp +__cmake_systeminformation/CMakeFiles/ diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index d674d57..c8a6003 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"); } @@ -66,6 +68,9 @@ u32 Core::StepCPU() { if (cpuType == Interpreted) return interpreter.Step() + regs.PopStalledCycles(); + if (cpuType == CachedInterpreter) + return interpreter.ExecuteCached() + regs.PopStalledCycles(); + #ifdef KAIZEN_JIT_ENABLED if (cpuType == DynamicRecompiler) return jit.Step() + regs.PopStalledCycles(); @@ -96,11 +101,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; @@ -117,12 +117,10 @@ void Core::Run(const float volumeL, const float volumeR) { for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { Scheduler::GetInstance().HandleEvents(); - const u32 taken = StepCPU(); cycles += taken; - - StepRSP(taken); frameCycles += taken; + StepRSP(taken); Scheduler::GetInstance().Tick(taken); } } diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 3777177..e4a0ea2 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(); @@ -19,7 +19,11 @@ struct Core { return instance; } - static void MaybeIdleSkip(); + static inline bool IsAddressError(u8 mask, u64 vaddr) { + auto regs = GetRegs(); + return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0; + } + static Registers &GetRegs() { return GetInstance().regs; } static Mem &GetMem() { return *GetInstance().mem; } diff --git a/src/backend/Scheduler.cpp b/src/backend/Scheduler.cpp index bb3db01..119e676 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -27,13 +27,9 @@ u64 Scheduler::Remove(const EventType eventType) const { return ret; } -void Scheduler::SkipToNext() { - ticks = events.top().time; -} +void Scheduler::SkipToNext() { ticks = events.top().time; } -void Scheduler::Tick(const u64 t) { - ticks += t; -} +void Scheduler::Tick(const u64 t) { ticks += t; } void Scheduler::HandleEvents() { n64::Mem &mem = n64::Core::GetMem(); diff --git a/src/backend/core/Cache.cpp b/src/backend/core/Cache.cpp index 94bad01..1f4367d 100644 --- a/src/backend/core/Cache.cpp +++ b/src/backend/core/Cache.cpp @@ -52,6 +52,7 @@ void DataCache::WriteBack(u64 vaddr, u32 paddr) { u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); u32 lineStart = GetDCacheLineStart(origPhysAddr); + Core::GetInstance().interpreter.EvictCachedBlock(vaddr); for (int i = 0; i < 16; i++) { mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); } @@ -86,6 +87,7 @@ void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { if (line.ptag == ptag && line.valid) { u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); u32 lineStart = GetICacheLineStart(origPhysAddr); + Core::GetInstance().interpreter.EvictCachedBlock(vaddr); for (int i = 0; i < 16; i++) { mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); } diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index 4bdcceb..4232ded 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) {} @@ -12,7 +14,7 @@ bool Interpreter::ShouldServiceInterrupt() const { return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; } -void Interpreter::CheckCompareInterrupt() const { +void Interpreter::UpdateCompareInterrupt() const { regs.cop0.count++; regs.cop0.count &= 0x1FFFFFFFF; if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { @@ -21,38 +23,159 @@ void Interpreter::CheckCompareInterrupt() const { } } -u32 Interpreter::Step() { - CheckCompareInterrupt(); +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(paddr); + return true; +} + +bool Interpreter::MaybeAdvance() { + UpdateCompareInterrupt(); regs.prevDelaySlot = regs.delaySlot; regs.delaySlot = false; - if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { + if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] { regs.cop0.HandleTLBException(regs.pc); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc); - return 1; + return false; } - 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); - return 1; - } - - const u32 instruction = mem.Read(paddr); - if (ShouldServiceInterrupt()) { regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc); - return 1; + return false; } regs.oldPC = regs.pc; regs.pc = regs.nextPC; regs.nextPC += 4; - Exec(instruction); + return true; +} + +bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) { + UpdateCompareInterrupt(); + + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + + if (Core::IsAddressError(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 Interpreter::CacheBlock(u32 addr) { + u32 blockAddr = addr; + + CachedLine line; + u32 i; + bool fetchDelaySlot = false; + + for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) { + Instruction instr; + if (!Fetch(instr, addr)) + return i + 1; + + addr += 4; + line.code[i] = instr; + + if (fetchDelaySlot) { + i++; + break; + } + + if (InstrEndsBlock(instr)) { + if (InstrHasDelaySlot(instr) && !fetchDelaySlot) { + fetchDelaySlot = true; + continue; + } + + if (i == 0) + i = 1; + + break; + } + } + + line.cycles = i; + line.len = i; + cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line); + + return ExecuteCached(); +} + +u32 Interpreter::ExecuteCached() { + u32 addr = regs.pc; + auto &blocks = cachedState.blocks; + + if (!blocks[CACHE_GET_BLOCK(addr)]) { + blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock(); + return CacheBlock(addr); + } + + const auto line = blocks[CACHE_GET_BLOCK(addr)]->lines[CACHE_GET_LINE(addr)]; + if (line) { + cachedState.exception = false; + // i copy the block cycles here in case the block evicts itself when executing which would set the cycles to + // 0, making so the emulator halts cause the outer loop won't advance + const auto blockCycles = line->cycles; + for (u32 i = 0; i < line->len; i++) { + addr += 4; + + if (!MaybeAdvance()) + return i + 1; + + Instruction instr = line->code[i]; + DecodeExecute(instr); + + if (cachedState.exception) + return i + 1; + + // Branch likely with false condition, it wasn't taken so don't execute the delay slot + if (IsBranchLikely(instr) && !regs.delaySlot) + break; + } + + if (blockCycles == 0) { + panic("Cycles are 0!"); + Scheduler::GetInstance().SkipToNext(); + } + + return blockCycles; + } + + return CacheBlock(addr); +} } // namespace n64 diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index a90dd10..0f02d15 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -1,36 +1,89 @@ #pragma once #include #include +#include namespace n64 { struct Core; +/* +static constexpr u32 MAX_INSTR_PER_BLOCK = 128; +static constexpr u32 MAX_LINES = 1 << 12; + +#define CACHE_GET_BLOCK(addr) (addr / MAX_LINES) +#define CACHE_GET_LINE(addr) ((addr & (MAX_LINES - 1)) >> 2) + +struct CachedLine { + std::array code = {}; + u32 len = 0; + u32 cycles = 0; +} __attribute__((__packed__)); + +struct CachedBlock { + CachedBlock() { lines.resize(MAX_LINES / 4); } + std::vector lines = {}; +}; + +struct CachedState { + std::vector blocks = {}; + bool exception = false; + + void Reset() { + for (auto block : blocks) { + if (block) + for (auto line : block->lines) + delete line; + + delete block; + } + blocks = {}; + blocks.resize(((u64)std::numeric_limits::max() + 1) / MAX_LINES); + } +}; +*/ struct Interpreter final { explicit Interpreter(Mem &, Registers &); ~Interpreter() = default; u32 Step(); + u32 ExecuteCached(); + bool FetchThenMaybeAdvance(Instruction &); + bool MaybeAdvance(); + u32 CacheBlock(u32 addr); - void Reset() { cop2Latch = {}; } + void SignalException(u32 addr) { cachedState.exception = true; } + void EvictCachedBlock(u32 addr) { cachedState.blocks[CACHE_GET_BLOCK(addr)] = {}; } + + void Reset() { + cop2Latch = {}; + cachedState.Reset(); + } + + CachedState<12, std::numeric_limits::max()> cachedState; private: + friend struct Cop1; + friend struct Mem; + + void MaybeIdleSkip(); + InstructionCache icache; DataCache dcache; Registers ®s; Mem &mem; u64 cop2Latch{}; - friend struct Cop1; + u32 rspSyncCount = 0; + + bool Fetch(Instruction &, u64); + void CacheTypeData(u8, u64, u32, u32); + void CacheTypeInstruction(u8, u64, u32, u32); - void cache_type_data(u8, u64, u32, u32); - void cache_type_instruction(u8, u64, u32, u32); -#define check_address_error(mask, vaddr) \ - (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) [[nodiscard]] bool ShouldServiceInterrupt() const; - void CheckCompareInterrupt() const; + void UpdateCompareInterrupt() const; void cop2Decode(Instruction); void special(Instruction); void regimm(Instruction); - void Exec(Instruction); + void DecodeExecute(Instruction); void add(Instruction); void addu(Instruction); void addi(Instruction); diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index f1d0b57..553321d 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -41,7 +41,7 @@ void JIT::InvalidateBlock(const u32 paddr) { std::optional JIT::FetchInstruction(s64 vaddr) { u32 paddr = 0; - if (check_address_error(0b11, vaddr)) [[unlikely]] { + if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC); return 1;*/ diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index b8c3c1d..83a546b 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -116,9 +116,6 @@ struct JIT final { void BranchAbsTaken(s64 addr); void BranchAbsTaken(const Xbyak::Reg64 &addr); -#define check_address_error(mask, vaddr) \ - (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) - [[nodiscard]] bool ShouldServiceInterrupt() const; void CheckCompareInterrupt() const; std::optional FetchInstruction(s64); diff --git a/src/backend/core/JITUtils.hpp b/src/backend/core/JITUtils.hpp new file mode 100644 index 0000000..fa51651 --- /dev/null +++ b/src/backend/core/JITUtils.hpp @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include + +namespace n64 { +static constexpr u32 MAX_INSTR_PER_BLOCK = 128; + +#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES)) +#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2) + +struct CachedLine { + std::array code = {}; + u32 len = 0; + u32 cycles = 0; +} __attribute__((__packed__)); + +template +struct CachedBlock { + CachedBlock() { lines.resize(lineAmount); } + std::vector lines = {}; +}; + +template +struct CachedState { + static constexpr u32 MAX_LINES = 1 << blockBits; + std::vector *> blocks = {}; + bool exception = false; + + void Reset() { + for (auto block : blocks) { + if (block) + for (auto line : block->lines) + delete line; + + delete block; + } + blocks = {}; + blocks.resize((addressSpace + 1) / MAX_LINES); + } +}; +} // namespace n64 diff --git a/src/backend/core/RDP.cpp b/src/backend/core/RDP.cpp index d503eb4..02a72a8 100644 --- a/src/backend/core/RDP.cpp +++ b/src/backend/core/RDP.cpp @@ -4,75 +4,75 @@ namespace n64 { RDP::RDP() { - rdram.resize(RDRAM_SIZE); - Reset(); + rdram.resize(RDRAM_SIZE); + Reset(); } void RDP::Reset() { - dpc = {}; - dpc.status.raw = 0x80; - std::ranges::fill(rdram, 0); - std::ranges::fill(cmd_buf, 0); + dpc = {}; + dpc.status.raw = 0x80; + std::ranges::fill(rdram, 0); + std::ranges::fill(cmd_buf, 0); } template <> void RDP::WriteRDRAM(const size_t idx, const u8 v) { - if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { - rdram[real] = v; - } + if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { + rdram[real] = v; + } } template <> void RDP::WriteRDRAM(const size_t idx, const u16 v) { - if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, real, v); - } + if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, real, v); + } } template <> void RDP::WriteRDRAM(const size_t idx, const u32 v) { - if (idx < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, idx, v); - } + if (idx < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, idx, v); + } } template <> void RDP::WriteRDRAM(const size_t idx, const u64 v) { - if (idx < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, idx, v); - } + if (idx < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, idx, v); + } } template <> u8 RDP::ReadRDRAM(const size_t idx) { - if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] - return rdram[real]; + if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] + return rdram[real]; - return 0; + return 0; } template <> u16 RDP::ReadRDRAM(const size_t idx) { - if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, real); + if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, real); - return 0; + return 0; } template <> u32 RDP::ReadRDRAM(const size_t idx) { - if (idx < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, idx); + if (idx < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, idx); - return 0; + return 0; } template <> u64 RDP::ReadRDRAM(const size_t idx) { - if (idx < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, idx); + if (idx < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, idx); - return 0; + return 0; } static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2, @@ -80,79 +80,78 @@ static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; auto RDP::Read(const u32 addr) const -> u32 { - switch (addr) { - case 0x04100000: - return dpc.start; - case 0x04100004: - return dpc.end; - case 0x04100008: - return dpc.current; - case 0x0410000C: - return dpc.status.raw; - case 0x04100010: - return dpc.clock; - case 0x04100014: - return dpc.status.cmdBusy; - case 0x04100018: - return dpc.status.pipeBusy; - case 0x0410001C: - return dpc.tmem; - default: - panic("Unhandled DP Command Registers read (addr: {:08X})", addr); - } + switch (addr) { + case 0x04100000: + return dpc.start; + case 0x04100004: + return dpc.end; + case 0x04100008: + return dpc.current; + case 0x0410000C: + return dpc.status.raw; + case 0x04100010: + return dpc.clock; + case 0x04100014: + return dpc.status.cmdBusy; + case 0x04100018: + return dpc.status.pipeBusy; + case 0x0410001C: + return dpc.tmem; + default: + panic("Unhandled DP Command Registers read (addr: {:08X})", addr); + } - return 0; + return 0; } void RDP::Write(const u32 addr, const u32 val) { - switch (addr) { - case 0x04100000: - WriteStart(val); - break; - case 0x04100004: - WriteEnd(val); - break; - case 0x0410000C: - WriteStatus(val); - break; - default: - panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val); - } + switch (addr) { + case 0x04100000: + WriteStart(val); + break; + case 0x04100004: + WriteEnd(val); + break; + case 0x0410000C: + WriteStatus(val); + break; + default: + panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val); + } } - void RDP::WriteStatus(const u32 val) { - DPCStatusWrite temp{}; - temp.raw = val; - bool unfrozen = false; + DPCStatusWrite temp{}; + temp.raw = val; + bool unfrozen = false; #define CLEAR_SET(val, clear, set) \ - do { \ - if ((clear)) \ - (val) = 0; \ - if ((set)) \ - (val) = 1; \ - } \ - while (0) + do { \ + if ((clear)) \ + (val) = 0; \ + if ((set)) \ + (val) = 1; \ + } \ + while (0) - CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma); - if (temp.clearFreeze) { - dpc.status.freeze = false; - unfrozen = true; - } + CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma); + if (temp.clearFreeze) { + dpc.status.freeze = false; + unfrozen = true; + } - if (temp.setFreeze) { - dpc.status.freeze = true; - } - CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush); - CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false); - CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false); - CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false); - CLEAR_SET(dpc.clock, temp.clearClock, false); + if (temp.setFreeze) { + dpc.status.freeze = true; + } + CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush); + CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false); + CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false); + CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false); + CLEAR_SET(dpc.clock, temp.clearClock, false); - if (!unfrozen) { - RunCommand(); - } + if (!unfrozen) { + RunCommand(); + } } /* FORCE_INLINE void logCommand(u8 cmd) { @@ -197,102 +196,102 @@ FORCE_INLINE void logCommand(u8 cmd) { */ void RDP::RunCommand() { - n64::Mem& mem = n64::Core::GetMem(); - ParallelRDP& parallel = n64::Core::GetInstance().parallel; - if (dpc.status.freeze) { - return; - } - dpc.status.pipeBusy = true; - dpc.status.startGclk = true; - if (dpc.end > dpc.current) { - dpc.status.freeze = true; - - static int remaining_cmds = 0; - - const u32 current = dpc.current & 0xFFFFF8; - const u32 end = dpc.end & 0xFFFFF8; - - const auto len = static_cast(end) - static_cast(current); - if (len <= 0) - return; - - if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) { - panic("Too many RDP commands"); - return; - } - - if (dpc.status.xbusDmemDma) { - for (int i = 0; i < len; i += 4) { - const u32 cmd = ircolib::ReadAccess(mem.mmio.rsp.dmem, current + i & 0xFFF); - cmd_buf[remaining_cmds + (i >> 2)] = cmd; - } - } else { - if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE) + n64::Mem &mem = n64::Core::GetMem(); + ParallelRDP ¶llel = n64::Core::GetInstance().parallel; + if (dpc.status.freeze) { return; - } - - for (int i = 0; i < len; i += 4) { - const u32 cmd = ircolib::ReadAccess(rdram, current + i); - cmd_buf[remaining_cmds + (i >> 2)] = cmd; - } } + dpc.status.pipeBusy = true; + dpc.status.startGclk = true; + if (dpc.end > dpc.current) { + dpc.status.freeze = true; - const int word_len = (len >> 2) + remaining_cmds; - int buf_index = 0; + static int remaining_cmds = 0; - bool processed_all = true; + const u32 current = dpc.current & 0xFFFFF8; + const u32 end = dpc.end & 0xFFFFF8; - while (buf_index < word_len) { - const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F; + const auto len = static_cast(end) - static_cast(current); + if (len <= 0) + return; - const int cmd_len = cmd_lens[cmd]; - if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) { - remaining_cmds = word_len - buf_index; - - u32 tmp[remaining_cmds]; - for (int i = 0; i < remaining_cmds; i++) { - tmp[i] = cmd_buf[buf_index + i]; + if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) { + panic("Too many RDP commands"); + return; } - for (int i = 0; i < remaining_cmds; i++) { - cmd_buf[i] = tmp[i]; + if (dpc.status.xbusDmemDma) { + for (int i = 0; i < len; i += 4) { + const u32 cmd = ircolib::ReadAccess(mem.mmio.rsp.dmem, current + i & 0xFFF); + cmd_buf[remaining_cmds + (i >> 2)] = cmd; + } + } else { + if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE) + return; + } + + for (int i = 0; i < len; i += 4) { + const u32 cmd = ircolib::ReadAccess(rdram, current + i); + cmd_buf[remaining_cmds + (i >> 2)] = cmd; + } } - processed_all = false; - break; - } + const int word_len = (len >> 2) + remaining_cmds; + int buf_index = 0; - if (cmd >= 8) { - parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]); - } + bool processed_all = true; - if (cmd == 0x29) { - OnFullSync(); - } + while (buf_index < word_len) { + const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F; - buf_index += cmd_len; + const int cmd_len = cmd_lens[cmd]; + if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) { + remaining_cmds = word_len - buf_index; + + u32 tmp[remaining_cmds]; + for (int i = 0; i < remaining_cmds; i++) { + tmp[i] = cmd_buf[buf_index + i]; + } + + for (int i = 0; i < remaining_cmds; i++) { + cmd_buf[i] = tmp[i]; + } + + processed_all = false; + break; + } + + if (cmd >= 8) { + parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]); + } + + if (cmd == 0x29) { + OnFullSync(); + } + + buf_index += cmd_len; + } + + if (processed_all) { + remaining_cmds = 0; + } + + dpc.current = end; + dpc.end = end; + dpc.status.freeze = false; } - - if (processed_all) { - remaining_cmds = 0; - } - - dpc.current = end; - dpc.end = end; - dpc.status.freeze = false; - } - dpc.status.cbufReady = true; + dpc.status.cbufReady = true; } void RDP::OnFullSync() { - n64::Mem& mem = n64::Core::GetMem(); - ParallelRDP& parallel = n64::Core::GetInstance().parallel; + n64::Mem &mem = n64::Core::GetMem(); + ParallelRDP ¶llel = n64::Core::GetInstance().parallel; - parallel.OnFullSync(); + parallel.OnFullSync(); - dpc.status.pipeBusy = false; - dpc.status.startGclk = false; - dpc.status.cbufReady = false; - mem.mmio.mi.InterruptRaise(MI::Interrupt::DP); + dpc.status.pipeBusy = false; + dpc.status.startGclk = false; + dpc.status.cbufReady = false; + mem.mmio.mi.InterruptRaise(MI::Interrupt::DP); } } // namespace n64 diff --git a/src/backend/core/RSP.cpp b/src/backend/core/RSP.cpp index b63bf50..ac0a3cc 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,170 @@ 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(); + + 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/interpreter/cop1instructions.cpp b/src/backend/core/interpreter/cop1instructions.cpp index 3f702ac..e4dc15d 100644 --- a/src/backend/core/interpreter/cop1instructions.cpp +++ b/src/backend/core/interpreter/cop1instructions.cpp @@ -1310,6 +1310,7 @@ void Cop1::swc1(const Instruction instr) { regs.cop0.HandleTLBException(addr); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + Core::GetInstance().interpreter.EvictCachedBlock(addr); mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); } } @@ -1337,6 +1338,7 @@ void Cop1::sdc1(const Instruction instr) { regs.cop0.HandleTLBException(addr); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + Core::GetInstance().interpreter.EvictCachedBlock(addr); mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); } } diff --git a/src/backend/core/interpreter/decode.cpp b/src/backend/core/interpreter/decode.cpp index abf8c52..ee5765b 100644 --- a/src/backend/core/interpreter/decode.cpp +++ b/src/backend/core/interpreter/decode.cpp @@ -249,7 +249,7 @@ void Interpreter::cop2Decode(const Instruction instr) { } } -void Interpreter::Exec(const Instruction instr) { +void Interpreter::DecodeExecute(const Instruction instr) { // 00rr_rccc switch (instr.opcode()) { case Instruction::SPECIAL: diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index 983b8a8..c7b0543 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); } @@ -202,7 +200,7 @@ void Interpreter::lb(const Instruction instr) { void Interpreter::lh(const Instruction instr) { const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { + if (Core::IsAddressError(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -220,7 +218,7 @@ void Interpreter::lh(const Instruction instr) { void Interpreter::lw(const Instruction instr) { const s16 offset = instr; const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -243,7 +241,7 @@ void Interpreter::ll(const Instruction instr) { regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { const s32 result = mem.Read(physical); - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; } @@ -287,7 +285,7 @@ void Interpreter::lwr(const Instruction instr) { void Interpreter::ld(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { + if (Core::IsAddressError(0b111, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -315,7 +313,7 @@ void Interpreter::lld(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { - if (check_address_error(0b111, address)) { + if (Core::IsAddressError(0b111, address)) { regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); } else { regs.Write(instr.rt(), mem.Read(paddr)); @@ -369,7 +367,7 @@ void Interpreter::lbu(const Instruction instr) { void Interpreter::lhu(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { + if (Core::IsAddressError(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -386,7 +384,7 @@ void Interpreter::lhu(const Instruction instr) { void Interpreter::lwu(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -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 { + EvictCachedBlock(address); mem.Write(paddr, regs.Read(instr.rt())); } } @@ -419,7 +418,7 @@ void Interpreter::sc(const Instruction instr) { if (regs.cop0.llbit) { regs.cop0.llbit = false; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { regs.Write(instr.rt(), 0); regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); @@ -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 { + EvictCachedBlock(address); mem.Write(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } @@ -451,7 +451,7 @@ void Interpreter::scd(const Instruction instr) { if (regs.cop0.llbit) { regs.cop0.llbit = false; - if (check_address_error(0b111, address)) { + if (Core::IsAddressError(0b111, address)) { regs.Write(instr.rt(), 0); regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); @@ -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 { + EvictCachedBlock(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 { + EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -487,7 +489,7 @@ void Interpreter::sh(const Instruction instr) { void Interpreter::sw(const Instruction instr) { const s16 offset = instr; const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; @@ -498,13 +500,14 @@ void Interpreter::sw(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { + EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } void Interpreter::sd(const Instruction instr) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { + if (Core::IsAddressError(0b111, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); return; @@ -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 { + EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -530,6 +534,7 @@ void Interpreter::sdl(const Instruction instr) { const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; const u64 data = mem.Read(paddr & ~7); const u64 rt = regs.Read(instr.rt()); + EvictCachedBlock(address); mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); } } @@ -545,6 +550,7 @@ void Interpreter::sdr(const Instruction instr) { const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; const u64 data = mem.Read(paddr & ~7); const u64 rt = regs.Read(instr.rt()); + EvictCachedBlock(address); mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); } } @@ -560,6 +566,7 @@ void Interpreter::swl(const Instruction instr) { const u32 mask = 0xFFFFFFFF >> shift; const u32 data = mem.Read(paddr & ~3); const u32 rt = regs.Read(instr.rt()); + EvictCachedBlock(address); mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); } } @@ -575,6 +582,7 @@ void Interpreter::swr(const Instruction instr) { const u32 mask = 0xFFFFFFFF << shift; const u32 data = mem.Read(paddr & ~3); const u32 rt = regs.Read(instr.rt()); + EvictCachedBlock(address); mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); } } @@ -869,12 +877,12 @@ void Interpreter::cache(const Instruction instr) { panic("Unknown cache type {}", type); if (type == 0) - return cache_type_instruction(op, vaddr, paddr, ptag); + return CacheTypeInstruction(op, vaddr, paddr, ptag); - return cache_type_data(op, vaddr, paddr, ptag); + return CacheTypeData(op, vaddr, paddr, ptag); } -void Interpreter::cache_type_instruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { +void Interpreter::CacheTypeInstruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { switch (op) { case 0: icache.InvalidateIndex(vaddr); @@ -899,7 +907,7 @@ void Interpreter::cache_type_instruction(const u8 op, const u64 vaddr, const u32 } } -void Interpreter::cache_type_data(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { +void Interpreter::CacheTypeData(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { switch (op) { case 0: dcache.WriteBack(vaddr, paddr); diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index 93d7b9f..98ce5dc 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -3,71 +3,114 @@ 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; + case Instruction::COP0: + switch (instr.cop_rs()) { + case 0x10 ... 0x1F: + switch (instr.cop_funct()) { + case 0x18: // eret + return true; + default: + return false; + } + default: + 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/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp index 77aa0da..e8cf89d 100644 --- a/src/backend/core/jit/instructions.cpp +++ b/src/backend/core/jit/instructions.cpp @@ -930,7 +930,7 @@ void JIT::lb(const Instruction instr) { void JIT::ld(const Instruction instr) { if (regs.IsRegConstant(instr.rs())) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { + if (Core::IsAddressError(0b111, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // return; @@ -1014,7 +1014,7 @@ void JIT::ldr(const Instruction instr) { void JIT::lh(const Instruction instr) { if (regs.IsRegConstant(instr.rs())) { const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { + if (Core::IsAddressError(0b1, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // return; @@ -1043,7 +1043,7 @@ void JIT::lhu(const Instruction instr) { u32 paddr; if (regs.IsRegConstant(instr.rs())) { const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { + if (Core::IsAddressError(0b1, address)) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); return; @@ -1080,7 +1080,7 @@ void JIT::lw(const Instruction instr) { u32 paddr = 0; if (regs.IsRegConstant(instr.rs())) { const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); // return; @@ -1344,7 +1344,7 @@ void JIT::sw(const Instruction instr) { if (regs.IsRegConstant(instr.rs(), instr.rt())) { const s16 offset = instr; const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); panic("[JIT]: Unhandled ADES exception in SW!"); @@ -1367,7 +1367,7 @@ void JIT::sw(const Instruction instr) { if (regs.IsRegConstant(instr.rs())) { const s16 offset = instr; const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { + if (Core::IsAddressError(0b11, address)) { // regs.cop0.HandleTLBException(address); // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); panic("[JIT]: Unhandled ADES exception in SW!"); 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..16f3ba4 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -363,13 +363,14 @@ bool Cop0::ProbeTLB(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) { Registers ®s = Core::GetRegs(); + Core::GetInstance().interpreter.SignalException(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; } } diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 3d9fb36..7bb8cc0 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -5,456 +5,484 @@ #include #include -KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { - gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); - SDL_InitSubSystem(SDL_INIT_GAMEPAD); +KaizenGui::KaizenGui() noexcept : + window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), + emuThread(fpsCounter, settingsWindow) { + gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); + SDL_InitSubSystem(SDL_INIT_GAMEPAD); - SDL_AddGamepadMapping(gamecontrollerdb_str); + SDL_AddGamepadMapping(gamecontrollerdb_str); } KaizenGui::~KaizenGui() { - gui::Cleanup(); - SDL_Quit(); + gui::Cleanup(); + SDL_Quit(); } void KaizenGui::QueryDevices(const SDL_Event &event) { - switch (event.type) { - case SDL_EVENT_GAMEPAD_ADDED: - if (!gamepad) { - const auto index = event.gdevice.which; + switch (event.type) { + case SDL_EVENT_GAMEPAD_ADDED: + if (!gamepad) { + const auto index = event.gdevice.which; - gamepad = SDL_OpenGamepad(index); - info("Found controller!"); - info("Name: {}", SDL_GetGamepadName(gamepad)); - info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + gamepad = SDL_OpenGamepad(index); + info("Found controller!"); + info("Name: {}", SDL_GetGamepadName(gamepad)); + info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + break; + case SDL_EVENT_GAMEPAD_REMOVED: + if (gamepad) + SDL_CloseGamepad(gamepad); + break; + default: + break; } - break; - case SDL_EVENT_GAMEPAD_REMOVED: - if (gamepad) - SDL_CloseGamepad(gamepad); - break; - default: break; - } } void KaizenGui::HandleInput(const SDL_Event &event) { - const n64::Core& core = n64::Core::GetInstance(); - n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; - switch(event.type) { + const n64::Core &core = n64::Core::GetInstance(); + n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; + switch (event.type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: - if(!gamepad) + if (!gamepad) + break; + { + pif.UpdateButton(0, n64::Controller::Key::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::Key::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); + pif.UpdateButton(0, n64::Controller::Key::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); + + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + } break; - { - pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); - - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast( xclamped)); - } - break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: - if(!gamepad) + if (!gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); break; - - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - { - const auto keys = SDL_GetKeyboardState(nullptr); - if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { - fileDialogOpen = true; + { + const auto keys = SDL_GetKeyboardState(nullptr); + if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { + fileDialogOpen = true; + } + + fastForward = keys[SDL_SCANCODE_SPACE]; + if (!unlockFramerate) + core.parallel.SetFramerateUnlocked(fastForward); + + if (core.romLoaded) { + if (keys[SDL_SCANCODE_P]) { + emuThread.TogglePause(); + } + + if (keys[SDL_SCANCODE_R]) { + emuThread.Reset(); + } + + if (keys[SDL_SCANCODE_Q]) { + emuThread.Stop(); + } + } + + if (gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); + pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); + pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); + pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); + pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); + pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); + pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); + pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); + pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); + pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); + pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); + pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); + pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); + pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); + + float x = 0, y = 0; + + if (keys[SDL_SCANCODE_UP]) + y = 86; + if (keys[SDL_SCANCODE_DOWN]) + y = -86; + if (keys[SDL_SCANCODE_LEFT]) + x = -86; + if (keys[SDL_SCANCODE_RIGHT]) + x = 86; + + pif.UpdateAxis(0, n64::Controller::Axis::X, x); + pif.UpdateAxis(0, n64::Controller::Axis::Y, y); } - - fastForward = keys[SDL_SCANCODE_SPACE]; - if(!unlockFramerate) - core.parallel.SetFramerateUnlocked(fastForward); - - if(core.romLoaded) { - if(keys[SDL_SCANCODE_P]) { - emuThread.TogglePause(); - } - - if(keys[SDL_SCANCODE_R]) { - emuThread.Reset(); - } - - if(keys[SDL_SCANCODE_Q]) { - emuThread.Stop(); - } - } - - if(gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); - pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); - pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); - pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); - pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); - pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); - pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); - pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); - pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); - pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); - pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); - pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); - pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); - pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - - float x = 0, y = 0; - - if (keys[SDL_SCANCODE_UP]) y = 86; - if (keys[SDL_SCANCODE_DOWN]) y = -86; - if (keys[SDL_SCANCODE_LEFT]) x = -86; - if (keys[SDL_SCANCODE_RIGHT]) x = 86; - - pif.UpdateAxis(0, n64::Controller::Axis::X, x); - pif.UpdateAxis(0, n64::Controller::Axis::Y, y); - } - break; - default: break; - } + break; + default: + break; + } } std::pair, std::optional> RenderErrorMessageDetails() { - auto lastPC = Util::Error::GetLastPC(); - if(lastPC.has_value()) { - ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); - } + auto lastPC = Util::Error::GetLastPC(); + if (lastPC.has_value()) { + ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); + } - auto memoryAccess = Util::Error::GetMemoryAccess(); - if(memoryAccess.has_value()) { - const auto [is_write, size, address, written_val] = memoryAccess.value(); - ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", - static_cast(size), address, - is_write ? std::format(" (value = 0x{:X})", written_val) : "") - .c_str()); - } + auto memoryAccess = Util::Error::GetMemoryAccess(); + if (memoryAccess.has_value()) { + const auto [is_write, size, address, written_val] = memoryAccess.value(); + ImGui::Text("%s", + std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast(size), + address, is_write ? std::format(" (value = 0x{:X})", written_val) : "") + .c_str()); + } - return {lastPC, memoryAccess}; + return {lastPC, memoryAccess}; } void KaizenGui::RenderUI() { - n64::Core& core = n64::Core::GetInstance(); - gui::StartFrame(); + n64::Core &core = n64::Core::GetInstance(); + gui::StartFrame(); - if(ImGui::BeginMainMenuBar()) { - if(ImGui::BeginMenu("File")) { - if(ImGui::MenuItem("Open", "Ctrl-O")) { - fileDialogOpen = true; - } - if(ImGui::MenuItem("Exit")) { - quit = true; - emuThread.Stop(); - } - ImGui::EndMenu(); + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open", "Ctrl-O")) { + fileDialogOpen = true; + } + if (ImGui::MenuItem("Exit")) { + quit = true; + emuThread.Stop(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Emulation")) { + ImGui::BeginDisabled(!core.romLoaded); + + if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { + emuThread.TogglePause(); + } + + if (ImGui::MenuItem("Reset", "R")) { + emuThread.Reset(); + } + + if (ImGui::MenuItem("Stop", "Q")) { + emuThread.Stop(); + core.romLoaded = false; + } + + if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { + core.parallel.SetFramerateUnlocked(unlockFramerate); + } + + if (ImGui::MenuItem("Open Debugger")) { + debugger.Open(); + } + + ImGui::EndDisabled(); + + if (ImGui::MenuItem("Options")) { + settingsWindow.isOpen = true; + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) { + if (ImGui::MenuItem("About")) { + aboutOpen = true; + } + + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } - if(ImGui::BeginMenu("Emulation")) { - ImGui::BeginDisabled(!core.romLoaded); - - if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { - emuThread.TogglePause(); - } - - if(ImGui::MenuItem("Reset", "R")) { - emuThread.Reset(); - } - - if(ImGui::MenuItem("Stop", "Q")) { - emuThread.Stop(); - core.romLoaded = false; - } - - if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { - core.parallel.SetFramerateUnlocked(unlockFramerate); - } - if(ImGui::MenuItem("Open Debugger")) { - debugger.Open(); - } - - ImGui::EndDisabled(); - - if(ImGui::MenuItem("Options")) { - settingsWindow.isOpen = true; - } - ImGui::EndMenu(); + if (!Util::Error::IsHandled()) { + ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); } - if(ImGui::BeginMenu("Help")) { - if(ImGui::MenuItem("About")) { - aboutOpen = true; - } - - ImGui::EndMenu(); + + if (settingsWindow.isOpen) { + ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); } - ImGui::EndMainMenuBar(); - } - if(!Util::Error::IsHandled()) { - ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); - } - - if(settingsWindow.isOpen) { - ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); - } - - if(aboutOpen) { - ImGui::OpenPopup("About Kaizen"); - } - - settingsWindow.render(); - debugger.render(); - - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); - ImGui::Text("to offer a friendly user experience and compatibility."); - ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); - ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); - ImGui::Separator(); - ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); - ImGui::Separator(); - if(ImGui::Button("OK")) { - aboutOpen = false; - ImGui::CloseCurrentPopup(); + if (aboutOpen) { + ImGui::OpenPopup("About Kaizen"); } - - ImGui::EndPopup(); - } - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + settingsWindow.render(); + debugger.render(); - if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - emuThread.TogglePause(); - switch(Util::Error::GetSeverity().as_enum) { - case Util::Error::Severity::WARN: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); - ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - if(ignore || stop || chooseAnother) { - Util::Error::SetHandled(); + if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); + ImGui::Text("to offer a friendly user experience and compatibility."); + ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); + ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); + ImGui::Separator(); + ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); + ImGui::Separator(); + if (ImGui::Button("OK")) { + aboutOpen = false; ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - break; } - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::UNRECOVERABLE: { - emuThread.Stop(); - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::NON_FATAL: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An error has occurred!"); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; - if(ignore || stop || chooseAnother || openInDebugger) { - Util::Error::SetHandled(); - ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - - if(openInDebugger) { - if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) - n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); - - debugger.Open(); - emuThread.Reset(); - } - } break; - default: break; + ImGui::EndPopup(); } - - ImGui::EndPopup(); - } - - if(ImGui::BeginMainStatusBar()) { - ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); - ImGui::EndMainStatusBar(); - } - if (shouldDisplaySpinner) { - ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + emuThread.TogglePause(); + switch (Util::Error::GetSeverity().as_enum) { + case Util::Error::Severity::WARN: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); + ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); - ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); - ImGui::SameLine(); + if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + if (ignore || stop || chooseAnother) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); - ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); - ImGui::PopFont(); + if (ignore) { + emuThread.TogglePause(); + } - ImGui::End(); + if (stop || chooseAnother) { + emuThread.Stop(); + } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - } + if (chooseAnother) { + fileDialogOpen = true; + } + break; + } - ImGui::Render(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::UNRECOVERABLE: + { + emuThread.Stop(); + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::NON_FATAL: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An error has occurred!"); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - if(fileDialogOpen) { - fileDialogOpen = false; - constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { - auto kaizen = static_cast(userdata); - - if (!filelist) { - panic("An error occured: {}", SDL_GetError()); - } + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool openInDebugger = + lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; + if (ignore || stop || chooseAnother || openInDebugger) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); + if (ignore) { + emuThread.TogglePause(); + } + + if (stop || chooseAnother) { + emuThread.Stop(); + } + + if (chooseAnother) { + fileDialogOpen = true; + } + + if (openInDebugger) { + if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) + n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); + + debugger.Open(); + emuThread.Reset(); + } + } + break; + default: + break; + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginMainStatusBar()) { + ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); + ImGui::EndMainStatusBar(); + } + + if (shouldDisplaySpinner) { + ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, + ImVec2(0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + + ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); + ImGui::SameLine(); + + ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); + ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); + ImGui::PopFont(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + + ImGui::Render(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + if (fileDialogOpen) { + fileDialogOpen = false; + constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, + {"Nintendo 64 executable", "n64;z64;v64"}, + {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; + SDL_ShowOpenFileDialog( + [](void *userdata, const char *const *filelist, int) { + auto kaizen = static_cast(userdata); + + if (!filelist) { + panic("An error occured: {}", SDL_GetError()); + } + + if (!*filelist) { + warn("The user did not select any file."); + warn("Most likely, the dialog was canceled."); + return; + } + + kaizen->fileToLoad = *filelist; + kaizen->shouldDisplaySpinner = true; + std::thread fileWorker(&KaizenGui::FileWorker, kaizen); + fileWorker.detach(); + }, + this, window.getHandle(), filters, 3, nullptr, false); + } + + if (minimized) return; - } - kaizen->fileToLoad = *filelist; - kaizen->shouldDisplaySpinner = true; + if (core.romLoaded) { + core.parallel.UpdateScreen(); + return; + } - std::thread fileWorker(&KaizenGui::FileWorker, kaizen); - fileWorker.detach(); - }, this, window.getHandle(), filters, 3, nullptr, false); - } - - if(minimized) - return; - - if(core.romLoaded) { - core.parallel.UpdateScreen(); - return; - } - - core.parallel.UpdateScreen(); + core.parallel.UpdateScreen(); } void KaizenGui::LoadROM(const std::string &path) noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.LoadROM(path); - const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; - SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); + n64::Core &core = n64::Core::GetInstance(); + core.LoadROM(path); + const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; + SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); } void KaizenGui::run() { - while(!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL3_ProcessEvent(&e); - switch(e.type) { - case SDL_EVENT_QUIT: - quit = true; - emuThread.Stop(); - break; - case SDL_EVENT_WINDOW_MINIMIZED: - minimized = true; - break; - case SDL_EVENT_WINDOW_RESTORED: - minimized = false; - break; - default: break; - } - QueryDevices(e); - HandleInput(e); + while (!quit) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + ImGui_ImplSDL3_ProcessEvent(&e); + switch (e.type) { + case SDL_EVENT_QUIT: + quit = true; + emuThread.Stop(); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + minimized = true; + break; + case SDL_EVENT_WINDOW_RESTORED: + minimized = false; + break; + default: + break; + } + QueryDevices(e); + HandleInput(e); + } + + SDL_GetWindowSize(window.getHandle(), &width, &height); + + emuThread.run(); + RenderUI(); } - - SDL_GetWindowSize(window.getHandle(), &width, &height); - - emuThread.run(); - RenderUI(); - } } -void KaizenGui::LoadTAS(const std::string &path) noexcept { - n64::Core::GetInstance().LoadTAS(fs::path(path)); -} +void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index d55a78d..9d0e9f1 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -10,7 +10,7 @@ class KaizenGui final { public: explicit KaizenGui() noexcept; ~KaizenGui(); - + double fpsCounter = -1.0; bool fastForward = false; bool unlockFramerate = false; @@ -22,7 +22,7 @@ public: Debugger debugger; SDL_Gamepad* gamepad = nullptr; - + void run(); static void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; @@ -37,12 +37,13 @@ private: void HandleInput(const SDL_Event &event); void QueryDevices(const SDL_Event &event); - [[noreturn]] void FileWorker() { + void FileWorker() { while (true) { if (!fileToLoad.empty()) { LoadROM(fileToLoad); shouldDisplaySpinner = false; fileToLoad = ""; + return; } } } diff --git a/src/frontend/Settings/CPUSettings.cpp b/src/frontend/Settings/CPUSettings.cpp index 1664013..12e4ed4 100644 --- a/src/frontend/Settings/CPUSettings.cpp +++ b/src/frontend/Settings/CPUSettings.cpp @@ -2,9 +2,13 @@ #include #include #include +#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 +16,7 @@ CPUSettings::CPUSettings() { } void CPUSettings::render() { - const char *items[] = {"Interpreter", + const char *items[] = {"Interpreter", "Cached Interpreter", #ifdef KAIZEN_JIT_ENABLED "Dynamic Recompiler" #endif @@ -37,8 +41,13 @@ void CPUSettings::render() { if (modified) { if (selectedCpuTypeIndex == 0) { Options::GetInstance().SetValue("cpu", "type", "interpreter"); + n64::Core::GetInstance().cpuType = n64::Core::Interpreted; + } else if (selectedCpuTypeIndex == 1) { + Options::GetInstance().SetValue("cpu", "type", "cached_interpreter"); + n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter; } else { Options::GetInstance().SetValue("cpu", "type", "jit"); + n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler; } } } diff --git a/src/utils/Instruction.hpp b/src/utils/Instruction.hpp index ef0c028..ef4fc30 100644 --- a/src/utils/Instruction.hpp +++ b/src/utils/Instruction.hpp @@ -3,7 +3,9 @@ #include namespace n64 { + struct Instruction { + Instruction() = default; Instruction(u32 v) { instr.raw = v; } void operator=(u32 v) { instr.raw = v; } operator u32() const { return instr.raw; }