From 12e81e73e8234b29cad18dc7b3ac2f147c76f809 Mon Sep 17 00:00:00 2001 From: iris Date: Wed, 3 Jun 2026 16:03:24 +0200 Subject: [PATCH] need to figure out why n64-systemtest loops indefinitely at some address that appears to be valid (i think it's me not invalidating the cache properly) --- CMakeLists.txt | 3 - src/backend/Core.cpp | 19 +- src/backend/Core.hpp | 12 +- src/backend/RomHelpers.hpp | 62 +- src/backend/Scheduler.cpp | 8 +- src/backend/core/CMakeLists.txt | 8 +- src/backend/core/Cache.cpp | 4 +- .../core/{JITUtils.hpp => CodeCache.hpp} | 14 +- src/backend/core/Disassembler.hpp | 65 +- src/backend/core/Interpreter.cpp | 77 +- src/backend/core/Interpreter.hpp | 2 +- src/backend/core/JIT.cpp | 243 --- src/backend/core/JIT.hpp | 271 --- src/backend/core/Mem.cpp | 80 +- src/backend/core/Mem.hpp | 9 - src/backend/core/RSP.cpp | 2 +- src/backend/core/RSP.hpp | 1 - .../core/interpreter/cop1instructions.cpp | 2 - src/backend/core/interpreter/instructions.cpp | 10 - src/backend/core/jit/CMakeLists.txt | 4 - src/backend/core/jit/decode.cpp | 465 ------ src/backend/core/jit/helpers.hpp | 34 - src/backend/core/jit/instructions.cpp | 1469 ----------------- src/backend/core/mmio/PI.cpp | 1 + src/backend/core/mmio/SI.cpp | 1 + src/backend/core/mmio/VI.cpp | 191 ++- src/backend/core/registers/Cop0.hpp | 2 - src/backend/core/registers/Cop1.hpp | 1 - src/backend/core/registers/Registers.cpp | 194 --- src/backend/core/registers/Registers.hpp | 12 - src/common.hpp | 1 - src/frontend/KaizenGui.cpp | 130 -- src/frontend/KaizenGui.hpp | 64 +- src/frontend/Settings/CPUSettings.cpp | 31 +- src/frontend/Settings/CPUSettings.hpp | 7 +- src/utils/ErrorData.hpp | 149 -- src/utils/Options.hpp | 12 +- 37 files changed, 305 insertions(+), 3355 deletions(-) rename src/backend/core/{JITUtils.hpp => CodeCache.hpp} (69%) delete mode 100644 src/backend/core/JIT.cpp delete mode 100644 src/backend/core/JIT.hpp delete mode 100644 src/backend/core/jit/CMakeLists.txt delete mode 100644 src/backend/core/jit/decode.cpp delete mode 100644 src/backend/core/jit/helpers.hpp delete mode 100644 src/backend/core/jit/instructions.cpp delete mode 100644 src/utils/ErrorData.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e25eb1..9ec5f34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,9 +186,6 @@ endif() target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend) target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) -if(USE_JIT) - target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED) -endif() if (SANITIZERS) message("UBSAN AND ASAN: ON") diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index c8a6003..e316d4a 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -4,18 +4,10 @@ #include namespace n64 { -Core::Core() : - interpreter(*mem, regs) -#ifdef KAIZEN_JIT_ENABLED - , - jit(*mem, regs) -#endif -{ +Core::Core() : interpreter(*mem, regs) { const auto selectedCpu = Options::GetInstance().GetValue("cpu", "type"); if (selectedCpu == "interpreter") { - cpuType = Interpreted; - } else if (selectedCpu == "jit") { - cpuType = DynamicRecompiler; + cpuType = PlainInterpreter; } else if (selectedCpu == "cached_interpreter") { cpuType = CachedInterpreter; } else { @@ -65,17 +57,12 @@ void Core::LoadROM(const std::string &rom_) { } u32 Core::StepCPU() { - if (cpuType == Interpreted) + if (cpuType == PlainInterpreter) 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(); -#endif - panic("Invalid CPU type?"); } diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 1dfa342..709f999 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -1,17 +1,15 @@ #pragma once #include #include -#ifdef KAIZEN_JIT_ENABLED -#include -#endif #include #include #include namespace n64 { struct Core { - enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter; + enum CPUType { PlainInterpreter, CachedInterpreter } cpuType = CachedInterpreter; + bool idleSkip = true; explicit Core(); static Core &GetInstance() { @@ -51,14 +49,8 @@ struct Core { size_t memSize{}, cpuSize{}, verSize{}; std::string rom; std::set breakpoints{}; -#ifdef KAIZEN_JIT_ENABLED - JIT jit; - std::unique_ptr mem = std::make_unique(jit); - Registers regs(jit); -#else std::unique_ptr mem = std::make_unique(); Registers regs; -#endif Interpreter interpreter; ParallelRDP parallel; }; diff --git a/src/backend/RomHelpers.hpp b/src/backend/RomHelpers.hpp index b69b9ae..f0d372c 100644 --- a/src/backend/RomHelpers.hpp +++ b/src/backend/RomHelpers.hpp @@ -9,40 +9,40 @@ namespace Util { template FORCE_INLINE void SwapN64Rom(std::vector &rom, u32 endianness) { - u8 altByteShift = 0; - if (endianness >> 24 != 0x80) { - if ((endianness & 0xFF) != 0x80) { - if ((endianness >> 16 & 0xff) != 0x80) { - Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); - return; - } else { - altByteShift = 12; - } + u8 altByteShift = 0; + if (endianness >> 24 != 0x80) { + if ((endianness & 0xFF) != 0x80) { + if ((endianness >> 16 & 0xff) != 0x80) { + panic("Unrecognized rom endianness"); + return; + } else { + altByteShift = 12; + } + } else { + altByteShift = 24; + } } else { - altByteShift = 24; + altByteShift = 0; } - } else { - altByteShift = 0; - } - endianness &= ~(0xFF << altByteShift); + endianness &= ~(0xFF << altByteShift); - switch (endianness) { - case V64: - ircolib::SwapBuffer(rom); - if constexpr (!toBE) - ircolib::SwapBuffer(rom); - break; - case N64: - if constexpr (toBE) - ircolib::SwapBuffer(rom); - break; - case Z64: - if constexpr (!toBE) - ircolib::SwapBuffer(rom); - break; - default: - Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!"); - } + switch (endianness) { + case V64: + ircolib::SwapBuffer(rom); + if constexpr (!toBE) + ircolib::SwapBuffer(rom); + break; + case N64: + if constexpr (toBE) + ircolib::SwapBuffer(rom); + break; + case Z64: + if constexpr (!toBE) + ircolib::SwapBuffer(rom); + break; + default: + panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!"); + } } } // namespace Util diff --git a/src/backend/Scheduler.cpp b/src/backend/Scheduler.cpp index 119e676..82f02b0 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -52,14 +52,10 @@ void Scheduler::HandleEvents() { case NONE: break; case IMPOSSIBLE: - Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, - {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, - "Unrecognized rom endianness"); + panic("Impossible scheduler event happened"); return; default: - Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, - {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, - "Unknown scheduler event type {}", static_cast(type)); + panic("Unknown scheduler event type {}", static_cast(type)); return; } events.pop(); diff --git a/src/backend/core/CMakeLists.txt b/src/backend/core/CMakeLists.txt index 7c5d618..7723a5a 100644 --- a/src/backend/core/CMakeLists.txt +++ b/src/backend/core/CMakeLists.txt @@ -2,16 +2,10 @@ file(GLOB SOURCES *.cpp) file(GLOB HEADERS *.hpp) add_subdirectory(interpreter) -if(USE_JIT) - add_subdirectory(jit) -endif() add_subdirectory(mem) add_subdirectory(mmio) add_subdirectory(registers) add_subdirectory(rsp) add_library(core ${SOURCES} ${HEADERS}) -target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp) -if(USE_JIT) - target_link_libraries(core PRIVATE jit) -endif() \ No newline at end of file +target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp) \ No newline at end of file diff --git a/src/backend/core/Cache.cpp b/src/backend/core/Cache.cpp index 6614d09..d994242 100644 --- a/src/backend/core/Cache.cpp +++ b/src/backend/core/Cache.cpp @@ -52,7 +52,7 @@ void DataCache::WriteBack(u64 vaddr, u32 paddr) { u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); u32 lineStart = GetDCacheLineStart(origPhysAddr); - Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr); + for (int i = 0; i < 16; i++) { mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); } @@ -87,7 +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.cachedState.EvictCachedBlock(vaddr); + for (int i = 0; i < 16; i++) { mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); } diff --git a/src/backend/core/JITUtils.hpp b/src/backend/core/CodeCache.hpp similarity index 69% rename from src/backend/core/JITUtils.hpp rename to src/backend/core/CodeCache.hpp index 46afced..dd874b9 100644 --- a/src/backend/core/JITUtils.hpp +++ b/src/backend/core/CodeCache.hpp @@ -9,25 +9,31 @@ 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) +template struct CachedLine { bool idleSkip = false; - std::array code = {}; + std::array code = {}; u32 len = 0; u32 cycles = 0; }; +template struct CachedBlock { CachedBlock(u32 lineAmount) { lines.resize(lineAmount); } - std::vector lines = {}; + std::vector *> lines = {}; }; -template +template struct CachedState { static constexpr u32 MAX_LINES = 1 << blockBits; - std::vector blocks = {}; + std::vector *> blocks = {}; bool exception = false; void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; } + void EvictCachedBlocksRange(u32 start, u32 end) { + for (u32 i = start; i < end; i += 4) + blocks[i / MAX_LINES] = {}; + } void Reset() { for (auto block : blocks) { diff --git a/src/backend/core/Disassembler.hpp b/src/backend/core/Disassembler.hpp index 6f634aa..256e7e3 100644 --- a/src/backend/core/Disassembler.hpp +++ b/src/backend/core/Disassembler.hpp @@ -5,42 +5,43 @@ #include struct Disassembler { - struct DisassemblyResult { - bool success = false; - std::string full; - u64 address; - std::string mnemonic; - struct Operand { - u32 color; - std::string str; + struct DisassemblyResult { + bool success = false; + std::string full; + u64 address; + std::string mnemonic; + struct Operand { + u32 color; + std::string str; + }; + std::array ops{}; }; - std::array ops{}; - }; - ~Disassembler() { cs_close(&handle); } + ~Disassembler() { cs_close(&handle); } - static Disassembler &GetInstance(bool rsp = false) { - static Disassembler ret(rsp); - return ret; - } - - [[nodiscard]] DisassemblyResult Disassemble(const u32 address) const; - [[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const; - [[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const; -private: - explicit Disassembler(const bool rsp) : rsp(rsp) { - if (cs_open(CS_ARCH_MIPS, static_cast((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), &handle) != - CS_ERR_OK) { - panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU"); + static Disassembler &GetInstance(bool rsp = false) { + static Disassembler ret(rsp); + return ret; } - if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) { - Util::Error::GetInstance().Throw({Util::Error::Severity::WARN}, {Util::Error::Type::CAPSTONE_ERROR}, {}, {}, "Could not enable disassembler's details!"); - details = false; - } - } + [[nodiscard]] DisassemblyResult Disassemble(const u32 address) const; + [[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const; + [[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const; - bool rsp = false; - bool details = true; - csh handle{}; + private: + explicit Disassembler(const bool rsp) : rsp(rsp) { + if (cs_open(CS_ARCH_MIPS, static_cast((rsp ? CS_MODE_32 : CS_MODE_64) | CS_MODE_BIG_ENDIAN), + &handle) != CS_ERR_OK) { + panic("Could not initialize {} disassembler!", rsp ? "RSP" : "CPU"); + } + + if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) { + warn("Could not enable disassembler's details!"); + details = false; + } + } + + bool rsp = false; + bool details = true; + csh handle{}; }; diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index b5b597f..2bf674c 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -1,6 +1,5 @@ #include #include -#include "jit/helpers.hpp" namespace n64 { Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {} @@ -88,6 +87,7 @@ bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) { u32 Interpreter::Step() { Instruction instr; + if (!FetchThenMaybeAdvance(instr)) return 1; @@ -99,14 +99,12 @@ u32 Interpreter::Step() { u32 Interpreter::CacheBlock(u32 addr) { u32 blockAddr = addr; - CachedLine line; + 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; + Instruction instr = mem.Read(addr); addr += 4; line.code[i] = instr; @@ -129,43 +127,44 @@ u32 Interpreter::CacheBlock(u32 addr) { } } - auto delay = line.code[i - 1]; + if (Core::GetInstance().idleSkip) { + auto delay = line.code[i - 1]; - if (i == 2) { - // branch to itself - // _nop + if (i == 2) { + // branch to itself + // _nop - auto branch = line.code[i - 2]; - line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0; - } + auto branch = line.code[i - 2]; + line.idleSkip = branch.IsBranch() && branch.offset() == -4 && delay.instr.raw == 0; + } - if (i == 3) { - // load reg1, [some location] - // bcond reg2 - // _andi reg2, reg1, immediate + if (i == 3) { + // load reg1, [some location] + // bcond reg2 + // _andi reg2, reg1, immediate - auto branch = line.code[i - 2]; - auto load = line.code[i - 3]; // load + auto branch = line.code[i - 2]; + auto load = line.code[i - 3]; // load - line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) && - delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 && - load.rt() == delay.rs() && delay.rt() == branch.rs(); - } + line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) && + delay.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -8 && + load.rt() == delay.rs() && delay.rt() == branch.rs(); + } - if (i == 5) { - // lui reg1 - // load reg2, [reg1(offset)] - // andi reg3, reg2, immediate - // bcond reg3 - // _nop + if (i == 4) { + // load reg2, [reg1(offset)] + // andi reg3, reg2, immediate + // bcond reg3, reg4 + // _nop - auto branch = line.code[i - 2]; - auto andi = line.code[i - 3]; // andi - auto load = line.code[i - 4]; // load + auto branch = line.code[i - 2]; + auto andi = line.code[i - 3]; // andi + auto load = line.code[i - 4]; // load - line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) && - andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -16 && - load.rt() == andi.rs() && andi.rt() == branch.rs(); + line.idleSkip = (load.opcode() == Instruction::LW || load.opcode() == Instruction::LWU) && + andi.opcode() == Instruction::ANDI && branch.IsBranch() && branch.offset() == -12 && + load.rt() == andi.rs() && andi.rt() == branch.rs(); + } } line.cycles = i; @@ -176,11 +175,17 @@ u32 Interpreter::CacheBlock(u32 addr) { } u32 Interpreter::ExecuteCached() { - u32 addr = regs.pc; + u32 addr; auto &blocks = cachedState.blocks; + if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, addr)) { + regs.cop0.HandleTLBException(regs.pc); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); + return 1; + } + if (!blocks[CACHE_GET_BLOCK(addr)]) { - blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock(cachedState.MAX_LINES / 4); + blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock(cachedState.MAX_LINES / 4); return CacheBlock(addr); } diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index d7a5665..0ac2f88 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include namespace n64 { struct Core; diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp deleted file mode 100644 index 97cbc3b..0000000 --- a/src/backend/core/JIT.cpp +++ /dev/null @@ -1,243 +0,0 @@ -#include -#include -#include - -namespace n64 { -JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) { - blockCache.resize(kUpperSize); - if (cs_open(CS_ARCH_MIPS, static_cast(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) != - CS_ERR_OK) { - panic("Failed to initialize MIPS disassembler"); - } - - if (cs_open(CS_ARCH_X86, static_cast(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) { - panic("Failed to initialize x86 disassembler"); - } -} - -bool JIT::ShouldServiceInterrupt() const { - const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; - const bool interrupts_enabled = regs.cop0.status.ie == 1; - const bool currently_handling_exception = regs.cop0.status.exl == 1; - const bool currently_handling_error = regs.cop0.status.erl == 1; - - return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; -} - -void JIT::CheckCompareInterrupt() const { - regs.cop0.count++; - regs.cop0.count &= 0x1FFFFFFFF; - if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { - regs.cop0.cause.ip7 = 1; - Core::GetMem().mmio.mi.UpdateInterrupt(); - } -} - -void JIT::InvalidateBlock(const u32 paddr) { - if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty()) - blockCache[index] = {}; -} - -std::optional JIT::FetchInstruction(s64 vaddr) { - u32 paddr = 0; - - if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] { - /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC); - return 1;*/ - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, - blockPC, {}, - "[JIT]: Unhandled exception ADL due to unaligned PC virtual value!"); - return std::nullopt; - } - - if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { - /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); - return 1;*/ - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, - "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); - return std::nullopt; - } - - const Instruction instr = Core::GetMem().Read(paddr); - - info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full); - - return instr; -} - -void JIT::SetPC32(const s32 val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1.cvt32(), val); - code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1.cvt32(), val + 4); - code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); -} - -void JIT::SetPC64(const s64 val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1, val); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1, val + 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); -} - -void JIT::SetPC32(const Xbyak::Reg32 &val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.movsxd(val.cvt64(), val); - code.mov(code.qword[code.rbp + PC_OFFSET], val); - code.add(val, 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); -} - -void JIT::SetPC64(const Xbyak::Reg64 &val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.qword[code.rbp + PC_OFFSET], val); - code.add(val, 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); -} - -u32 JIT::Step() { - blockOldPC = regs.oldPC; - blockPC = regs.pc; - blockNextPC = regs.nextPC; - u32 paddr = 0; - - if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { - /*regs.cop0.HandleTLBException(blockPC); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); - return 1;*/ - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, - "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); - return 0; - } - - const u32 upperIndex = paddr >> kUpperShift; - const u32 lowerIndex = paddr & kLowerMask; - - if (!blockCache[upperIndex].empty()) { - if (blockCache[upperIndex][lowerIndex]) { - // trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC); - return blockCache[upperIndex][lowerIndex](); - } - } else { - blockCache[upperIndex].resize(kLowerSize); - } - - info("[JIT]: Compiling block @ 0x{:016X}:", static_cast(blockPC)); - const auto blockInfo = code.getCurr(); - const auto block = code.getCurr(); - blockCache[upperIndex][lowerIndex] = block; - - code.setProtectModeRW(); - - u32 instructionsInBlock = 0; - - bool instrEndsBlock = false; - - code.sub(code.rsp, 8); - code.push(code.rbp); - code.mov(code.rbp, reinterpret_cast(this)); // Load context pointer - - cs_insn *insn; - info("\tMIPS code (guest PC = 0x{:016X}):", static_cast(blockPC)); - - emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - - while (true) { - paddr = 0; - - auto instruction = FetchInstruction(blockPC); - - if (!instruction) - return 0; - - instructionsInBlock++; - - blockOldPC = blockPC; - blockPC = blockNextPC; - blockNextPC += 4; - - if (instruction.value().EndsBlock()) { - auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot - if (!delay_instruction) - return 0; - - if (delay_instruction.value().EndsBlock()) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {}, - "[JIT]: Unhandled case of branch from delay slot!"); - return 0; - } - - instructionsInBlock++; - - blockOldPC = blockPC; - blockPC = blockNextPC; - blockNextPC += 4; - - Emit(delay_instruction.value()); - Emit(instruction.value()); - - if (!branch_taken) { - Xbyak::Label runtime_branch_taken; - code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]); - code.cmp(code.SCR1, 0); - code.jne(runtime_branch_taken); - code.mov(code.SCR1, blockOldPC); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1, blockPC); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1, blockNextPC); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); - code.L(runtime_branch_taken); - } - - if (branch_taken) - branch_taken = false; - - emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - - break; - } - - Emit(instruction.value()); - - emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - } - - code.mov(code.rax, instructionsInBlock); - code.pop(code.rbp); - code.add(code.rsp, 8); - code.ret(); - code.setProtectModeRE(); - static size_t blockInfoSize = 0; - blockInfoSize = code.getSize() - blockInfoSize; - - info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast(block)); - const auto count = - cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast(block), 0, &insn); - if (count > 0) { - for (size_t j = 0; j < count; j++) { - info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str); - } - - cs_free(insn, count); - } - // panic(""); - return block(); -} - -void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode(), code.getSize(), "jit.dump"); } -} // namespace n64 diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp deleted file mode 100644 index e6b0a67..0000000 --- a/src/backend/core/JIT.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - -namespace n64 { -struct Core; - -static constexpr u64 kAddressSpaceSize = 0x8000'0000; -static constexpr u8 kUpperShift = 8; -static constexpr u8 kLowerMask = 0xff; -static constexpr u32 kUpperSize = kAddressSpaceSize >> kUpperShift; // 0x800000 -static constexpr u32 kLowerSize = 0x100; // 0x80 -static constexpr u32 kCodeCacheSize = 32_mb; -static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb; - -#define OLD_PC_OFFSET (reinterpret_cast(®s.oldPC) - reinterpret_cast(this)) -#define PC_OFFSET (reinterpret_cast(®s.pc) - reinterpret_cast(this)) -#define NEXT_PC_OFFSET (reinterpret_cast(®s.nextPC) - reinterpret_cast(this)) -#define GPR_OFFSET(x) (reinterpret_cast(®s.gpr[(x)]) - reinterpret_cast(this)) -#define BRANCH_TAKEN_OFFSET (reinterpret_cast(&branch_taken) - reinterpret_cast(this)) -#define HI_OFFSET (reinterpret_cast(®s.hi) - reinterpret_cast(this)) -#define LO_OFFSET (reinterpret_cast(®s.lo) - reinterpret_cast(this)) - -struct JIT final { - explicit JIT(Mem &, Registers &); - ~JIT() = default; - u32 Step(); - - void Reset() { - code.reset(); - blockCache = {}; - blockCache.resize(kUpperSize); - } - - void DumpBlockCacheToDisk() const; - - void AdvanceDelaySlot() { - regs.prevDelaySlot = regs.delaySlot; - regs.delaySlot = false; - } - - void InvalidateBlock(u32); - - private: - friend struct Cop1; - friend struct Registers; - using BlockFn = int (*)(); - - bool branch_taken; - Registers ®s; - Mem &mem; - u64 cop2Latch{}; - s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0; - Xbyak::CodeGenerator code{kCodeCacheAllocSize}; - csh disassemblerMips{}, disassemblerX86{}; - std::vector> blockCache; - - template - Xbyak::Address GPR(const size_t index) { - if constexpr (sizeof(T) == 1) { - return code.byte[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 2) { - return code.word[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 4) { - return code.dword[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 8) { - return code.qword[code.rbp + GPR_OFFSET(index)]; - } - - Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, - {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {}, - "[JIT]: Invalid register addressing mode {}!", sizeof(T)); - return Xbyak::Address{0}; - } - - - // Thanks to https://github.com/grumpycoders/pcsx-redux - // Load a pointer to the JIT object in "reg" - template - void emitMemberFunctionCall(T func, void *thisObject) { - uintptr_t functionPtr; - auto thisPtr = reinterpret_cast(thisObject); - -#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) - static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer"); - std::memcpy(&functionPtr, &func, sizeof(T)); -#else - static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer"); - uintptr_t arr[2]; - std::memcpy(arr, &func, sizeof(T)); - // First 8 bytes correspond to the actual pointer to the function - functionPtr = reinterpret_cast(reinterpret_cast(arr[0])); - // Next 8 bytes correspond to the "this" pointer adjustment - thisPtr += arr[1]; -#endif - - code.mov(code.ARG1, thisPtr); - code.mov(code.rax, functionPtr); - code.sub(code.rsp, 8); - code.call(code.rax); - code.add(code.rsp, 8); - } - - void SetPC32(s32 val); - void SetPC64(s64 val); - void SetPC32(const Xbyak::Reg32 &val); - void SetPC64(const Xbyak::Reg64 &val); - void BranchNotTaken(); - void BranchTaken(s64 offs); - void BranchTaken(const Xbyak::Reg64 &offs); - void BranchAbsTaken(s64 addr); - void BranchAbsTaken(const Xbyak::Reg64 &addr); - - [[nodiscard]] bool ShouldServiceInterrupt() const; - void CheckCompareInterrupt() const; - std::optional FetchInstruction(s64); - - void Emit(Instruction); - void special(Instruction); - void regimm(Instruction); - void add(Instruction); - void addu(Instruction); - void addi(Instruction); - void addiu(Instruction); - void andi(Instruction); - void and_(Instruction); - void branch_constant(bool cond, s64 offset); - void branch_likely_constant(bool cond, s64 offset); - void branch_abs_constant(bool cond, s64 address); - void bltz(Instruction); - void bgez(Instruction); - void bltzl(Instruction); - void bgezl(Instruction); - void bltzal(Instruction); - void bgezal(Instruction); - void bltzall(Instruction); - void bgezall(Instruction); - void beq(Instruction); - void beql(Instruction); - void bne(Instruction); - void bnel(Instruction); - void blez(Instruction); - void blezl(Instruction); - void bgtz(Instruction); - void bgtzl(Instruction); - void bfc1(Instruction); - void blfc1(Instruction); - void bfc0(Instruction); - void blfc0(Instruction); - void dadd(Instruction); - void daddu(Instruction); - void daddi(Instruction); - void daddiu(Instruction); - void ddiv(Instruction); - void ddivu(Instruction); - void div(Instruction); - void divu(Instruction); - void dmult(Instruction); - void dmultu(Instruction); - void dsll(Instruction); - void dsllv(Instruction); - void dsll32(Instruction); - void dsra(Instruction); - void dsrav(Instruction); - void dsra32(Instruction); - void dsrl(Instruction); - void dsrlv(Instruction); - void dsrl32(Instruction); - void dsub(Instruction); - void dsubu(Instruction); - void j(Instruction); - void jr(Instruction); - void jal(Instruction); - void jalr(Instruction); - void lui(Instruction); - void lbu(Instruction); - void lb(Instruction); - void ld(Instruction); - void ldc1(Instruction); - void ldl(Instruction); - void ldr(Instruction); - void lh(Instruction); - void lhu(Instruction); - void ll(Instruction); - void lld(Instruction); - void lw(Instruction); - void lwc1(Instruction); - void lwl(Instruction); - void lwu(Instruction); - void lwr(Instruction); - void mfhi(Instruction); - void mflo(Instruction); - void mult(Instruction); - void multu(Instruction); - void mthi(Instruction); - void mtlo(Instruction); - void nor(Instruction); - void sb(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sb'!"); - } - void sc(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sc'!"); - } - void scd(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'scd'!"); - } - void sd(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sd'!"); - } - void sdc1(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sdc1'!"); - } - void sdl(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sdl'!"); - } - void sdr(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sdr'!"); - } - void sh(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'sh'!"); - } - void sw(Instruction); - void swl(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'swl'!"); - } - void swr(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'swr'!"); - } - void slti(Instruction); - void sltiu(Instruction); - void slt(Instruction); - void sltu(Instruction); - void sll(Instruction); - void sllv(Instruction); - void sub(Instruction); - void subu(Instruction); - void swc1(const Instruction) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {}, - "[JIT]: Unhandled case of branch from delay slot!"); - } - void sra(Instruction); - void srav(Instruction); - void srl(Instruction); - void srlv(Instruction); - void trap(bool) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {}, - "[JIT]: Unhandled case of branch from delay slot!"); - } - void or_(Instruction); - void ori(Instruction); - void xor_(Instruction); - void xori(Instruction); -}; -} // namespace n64 diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 6eb887d..40a9296 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -7,11 +7,7 @@ #include namespace n64 { -#ifdef KAIZEN_JIT_ENABLED -Mem::Mem(JIT &jit) : flash(saveData), jit(jit) { -#else Mem::Mem() : flash(saveData) { -#endif rom.cart.resize(CART_SIZE); std::ranges::fill(rom.cart, 0); isviewer_sink = std::ofstream("isviewer.log"); @@ -21,12 +17,10 @@ void Mem::Reset() { std::ranges::fill(isviewer, 0); flash.Reset(); if (saveData.is_mapped()) { - std::error_code error; - saveData.sync(error); - if (error) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {}, - "[Mem]: Could not sync save data!"); + std::error_code err; + saveData.sync(err); + if (err) { + error("[Mem]: Could not sync save data!"); return; } saveData.unmap(); @@ -36,18 +30,16 @@ void Mem::Reset() { void Mem::LoadSRAM(SaveType save_type, fs::path path) { if (save_type == SAVE_SRAM_256k) { - std::error_code error; + std::error_code err; std::string savePath = Options::GetInstance().GetValue("general", "savePath"); if (!savePath.empty()) { path = savePath / path.filename(); } sramPath = path.replace_extension(".sram").string(); if (saveData.is_mapped()) { - saveData.sync(error); - if (error) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {}, - R"([Mem]: Could not sync save data stored @ "{}")", sramPath); + saveData.sync(err); + if (err) { + error(R"([Mem]: Could not sync save data stored @ "{}")", sramPath); return; } saveData.unmap(); @@ -60,17 +52,13 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) { } if (sramVec.size() != SRAM_SIZE) { - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE}, {}, {}, - "[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024); + error("[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024); return; } - saveData = mio::make_mmap_sink(sramPath, error); - if (error) { - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, - {Util::Error::Type::MMAP_MAKE_SINK_ERROR}, {}, {}, - R"([Mem]: Could not create file sink for save data @ "{}")", sramPath); + saveData = mio::make_mmap_sink(sramPath, err); + if (err) { + error(R"([Mem]: Could not create file sink for save data @ "{}")", sramPath); } } } @@ -170,9 +158,7 @@ u8 Mem::Read(const u32 paddr) { if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access from MMIO"); + panic("8-bit read access from MMIO addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc); return 0; } @@ -186,10 +172,7 @@ u8 Mem::Read(const u32 paddr) { ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, - regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, - "8-bit read access in unhandled region"); + panic("8-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc); return 0; } @@ -220,10 +203,7 @@ u16 Mem::Read(const u32 paddr) { ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, - regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, - "16-bit read access in unhandled region"); + panic("16-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc); return 0; } @@ -255,10 +235,7 @@ u32 Mem::Read(const u32 paddr) { ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, - regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, - "32-bit read access in unhandled region"); + panic("32-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc); return 0; } @@ -290,10 +267,7 @@ u64 Mem::Read(const u32 paddr) { ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, - regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0}, - "64-bit read access in unhandled region"); + panic("64-bit read access in unhandled addr 0x{:08X} @ pc 0x{:016X}", paddr, (u64)regs.pc); return 0; } @@ -302,12 +276,9 @@ void Mem::Write(u32 paddr, u32 val) { n64::Registers ®s = n64::Core::GetRegs(); SI &si = mmio.si; -#ifdef KAIZEN_JIT_ENABLED - jit.InvalidateBlock(paddr); -#endif - if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(paddr, val); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr); return; } if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { @@ -352,12 +323,9 @@ void Mem::Write(u32 paddr, u32 val) { n64::Registers ®s = n64::Core::GetRegs(); SI &si = mmio.si; -#ifdef KAIZEN_JIT_ENABLED - jit.InvalidateBlock(paddr); -#endif - if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(paddr, val); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr); return; } if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { @@ -402,12 +370,9 @@ void Mem::Write(const u32 paddr, const u32 val) { n64::Registers ®s = n64::Core::GetRegs(); SI &si = mmio.si; -#ifdef KAIZEN_JIT_ENABLED - jit.InvalidateBlock(paddr); -#endif - if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(paddr, val); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr); return; } if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { @@ -449,12 +414,9 @@ void Mem::Write(const u32 paddr, u64 val) { n64::Registers ®s = n64::Core::GetRegs(); SI &si = mmio.si; -#ifdef KAIZEN_JIT_ENABLED - jit.InvalidateBlock(paddr); -#endif - if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(paddr, val); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(paddr); return; } if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 09eaa10..8279868 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -7,7 +7,6 @@ #include #include #include -#include namespace n64 { struct ROMHeader { @@ -74,12 +73,6 @@ struct Flash { T Read(u32 index) const; }; -#ifdef KAIZEN_JIT_ENABLED -struct JIT; -struct Mem { - Mem(JIT &jit); - JIT &jit; -#else struct Mem { Mem(); ~Mem() = default; @@ -134,7 +127,6 @@ struct Mem { friend struct PI; friend struct AI; friend struct RSP; - friend struct JIT; friend struct Core; std::array isviewer{}; @@ -148,5 +140,4 @@ struct Mem { return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; }); } }; -#endif } // namespace n64 diff --git a/src/backend/core/RSP.cpp b/src/backend/core/RSP.cpp index 160ebcf..9d8113c 100644 --- a/src/backend/core/RSP.cpp +++ b/src/backend/core/RSP.cpp @@ -1,6 +1,5 @@ #include #include -#include namespace n64 { RSP::RSP() { Reset(); } @@ -118,6 +117,7 @@ void RSP::DMA() { for (u32 j = 0; j < length; j++) { mem.mmio.rdp.WriteRDRAM(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); } + Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dram_address, dram_address + length); const int skip = i == spDMALen.count ? 0 : spDMALen.skip; diff --git a/src/backend/core/RSP.hpp b/src/backend/core/RSP.hpp index ba303d7..85142ef 100644 --- a/src/backend/core/RSP.hpp +++ b/src/backend/core/RSP.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF]) #define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1)) diff --git a/src/backend/core/interpreter/cop1instructions.cpp b/src/backend/core/interpreter/cop1instructions.cpp index 5e7aac9..3f702ac 100644 --- a/src/backend/core/interpreter/cop1instructions.cpp +++ b/src/backend/core/interpreter/cop1instructions.cpp @@ -1310,7 +1310,6 @@ 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.cachedState.EvictCachedBlock(addr); mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); } } @@ -1338,7 +1337,6 @@ 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.cachedState.EvictCachedBlock(addr); mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); } } diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index c56e1f6..984ad63 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -409,7 +409,6 @@ void Interpreter::sb(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(paddr, regs.Read(instr.rt())); } } @@ -433,7 +432,6 @@ void Interpreter::sc(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } @@ -466,7 +464,6 @@ void Interpreter::scd(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(paddr, regs.Read(instr.rt())); regs.Write(instr.rt(), 1); } @@ -483,7 +480,6 @@ void Interpreter::sh(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -502,7 +498,6 @@ void Interpreter::sw(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -520,7 +515,6 @@ void Interpreter::sd(const Instruction instr) { regs.cop0.HandleTLBException(address); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); } else { - cachedState.EvictCachedBlock(address); mem.Write(physical, regs.Read(instr.rt())); } } @@ -536,7 +530,6 @@ 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()); - cachedState.EvictCachedBlock(address); mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); } } @@ -552,7 +545,6 @@ 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()); - cachedState.EvictCachedBlock(address); mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); } } @@ -568,7 +560,6 @@ 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()); - cachedState.EvictCachedBlock(address); mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); } } @@ -584,7 +575,6 @@ 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()); - cachedState.EvictCachedBlock(address); mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); } } diff --git a/src/backend/core/jit/CMakeLists.txt b/src/backend/core/jit/CMakeLists.txt deleted file mode 100644 index 9042a39..0000000 --- a/src/backend/core/jit/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -file(GLOB_RECURSE SOURCES *.cpp) -file(GLOB_RECURSE HEADERS *.hpp) - -add_library(jit ${SOURCES} ${HEADERS}) \ No newline at end of file diff --git a/src/backend/core/jit/decode.cpp b/src/backend/core/jit/decode.cpp deleted file mode 100644 index 27c025f..0000000 --- a/src/backend/core/jit/decode.cpp +++ /dev/null @@ -1,465 +0,0 @@ -#include -#include - -namespace n64 { -void JIT::special(const Instruction instr) { - // 00rr_rccc - switch (instr.special()) { - case Instruction::SLL: - if (instr != 0) { - sll(instr); - } - break; - case Instruction::SRL: - srl(instr); - break; - case Instruction::SRA: - sra(instr); - break; - case Instruction::SLLV: - sllv(instr); - break; - case Instruction::SRLV: - srlv(instr); - break; - case Instruction::SRAV: - srav(instr); - break; - case Instruction::JR: - jr(instr); - break; - case Instruction::JALR: - jalr(instr); - break; - case Instruction::SYSCALL: - regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC); - break; - case Instruction::BREAK: - regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC); - break; - case Instruction::SYNC: - break; // SYNC - case Instruction::MFHI: - mfhi(instr); - break; - case Instruction::MTHI: - mthi(instr); - break; - case Instruction::MFLO: - mflo(instr); - break; - case Instruction::MTLO: - mtlo(instr); - break; - case Instruction::DSLLV: - dsllv(instr); - break; - case Instruction::DSRLV: - dsrlv(instr); - break; - case Instruction::DSRAV: - dsrav(instr); - break; - case Instruction::MULT: - mult(instr); - break; - case Instruction::MULTU: - multu(instr); - break; - case Instruction::DIV: - div(instr); - break; - case Instruction::DIVU: - divu(instr); - break; - case Instruction::DMULT: - dmult(instr); - break; - case Instruction::DMULTU: - dmultu(instr); - break; - case Instruction::DDIV: - ddiv(instr); - break; - case Instruction::DDIVU: - ddivu(instr); - break; - case Instruction::ADD: - add(instr); - break; - case Instruction::ADDU: - addu(instr); - break; - case Instruction::SUB: - sub(instr); - break; - case Instruction::SUBU: - subu(instr); - break; - case Instruction::AND: - and_(instr); - break; - case Instruction::OR: - or_(instr); - break; - case Instruction::XOR: - xor_(instr); - break; - case Instruction::NOR: - nor(instr); - break; - case Instruction::SLT: - slt(instr); - break; - case Instruction::SLTU: - sltu(instr); - break; - case Instruction::DADD: - dadd(instr); - break; - case Instruction::DADDU: - daddu(instr); - break; - case Instruction::DSUB: - dsub(instr); - break; - case Instruction::DSUBU: - dsubu(instr); - break; - case Instruction::TGE: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TGEU: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TLT: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TLTU: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TEQ: - trap(regs.Read(instr.rs()) == regs.Read(instr.rt())); - break; - case Instruction::TNE: - trap(regs.Read(instr.rs()) != regs.Read(instr.rt())); - break; - case Instruction::DSLL: - dsll(instr); - break; - case Instruction::DSRL: - dsrl(instr); - break; - case Instruction::DSRA: - dsra(instr); - break; - case Instruction::DSLL32: - dsll32(instr); - break; - case Instruction::DSRL32: - dsrl32(instr); - break; - case Instruction::DSRA32: - dsra32(instr); - break; - default: - panic("Unimplemented special {} ({:08X}) (pc: {:016X})", instr.special(), u32(instr), - static_cast(regs.oldPC)); - } -} - -void JIT::regimm(const Instruction instr) { - // 000r_rccc - switch (instr.regimm()) { - case Instruction::BLTZ: - bltz(instr); - break; - case Instruction::BGEZ: - bgez(instr); - break; - case Instruction::BLTZL: - bltzl(instr); - break; - case Instruction::BGEZL: - bgezl(instr); - break; - case Instruction::TGEI: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr))); - break; - case Instruction::TGEIU: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TLTI: - trap(regs.Read(instr.rs()) < static_cast(static_cast(instr))); - break; - case Instruction::TLTIU: - trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TEQI: - trap(regs.Read(instr.rs()) == static_cast(static_cast(instr))); - break; - case Instruction::TNEI: - trap(regs.Read(instr.rs()) != static_cast(static_cast(instr))); - break; - case Instruction::BLTZAL: - bltzal(instr); - break; - case Instruction::BGEZAL: - bgezal(instr); - break; - case Instruction::BLTZALL: - bltzall(instr); - break; - case Instruction::BGEZALL: - bgezall(instr); - break; - default: - panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr), - static_cast(regs.oldPC)); - } -} - -void JIT::Emit(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::SPECIAL: - special(instr); - break; - case Instruction::REGIMM: - regimm(instr); - break; - case Instruction::J: - j(instr); - break; - case Instruction::JAL: - jal(instr); - break; - case Instruction::BEQ: - beq(instr); - break; - case Instruction::BNE: - bne(instr); - break; - case Instruction::BLEZ: - blez(instr); - break; - case Instruction::BGTZ: - bgtz(instr); - break; - case Instruction::ADDI: - addi(instr); - break; - case Instruction::ADDIU: - addiu(instr); - break; - case Instruction::SLTI: - slti(instr); - break; - case Instruction::SLTIU: - sltiu(instr); - break; - case Instruction::ANDI: - andi(instr); - break; - case Instruction::ORI: - ori(instr); - break; - case Instruction::XORI: - xori(instr); - break; - case Instruction::LUI: - lui(instr); - break; - case Instruction::COP0: - switch (instr.cop_rs()) { - case 0x00: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0); - break; - case 0x01: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0); - break; - case 0x04: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0); - break; - case 0x05: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0); - break; - case 0x10 ... 0x1F: - switch (instr.cop_funct()) { - case 0x01: - emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0); - break; - case 0x02: - code.mov(code.ARG2, COP0_REG_INDEX); - emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0); - code.mov(code.ARG2, code.rax); - code.and_(code.ARG2, 0x3F); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x06: - emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0); - code.mov(code.ARG2, code.rax); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x08: - emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0); - break; - case 0x18: - emitMemberFunctionCall(&Cop0::eret, ®s.cop0); - break; - default: - panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC); - } - break; - default: - panic("Unimplemented COP0 instruction {}", instr.cop_rs()); - } - break; - case Instruction::COP1: - { - if (instr.cop_rs() == 0x08) { - switch (instr.cop_rt()) { - case 0: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc0(instr); - break; - case 1: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc1(instr); - break; - case 2: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc0(instr); - break; - case 3: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc1(instr); - break; - default: - panic("Undefined BC COP1 {:02X}", instr.cop_rt()); - } - break; - } - regs.cop1.decode(instr); - } - break; - case Instruction::COP2: - break; - case Instruction::BEQL: - beql(instr); - break; - case Instruction::BNEL: - bnel(instr); - break; - case Instruction::BLEZL: - blezl(instr); - break; - case Instruction::BGTZL: - bgtzl(instr); - break; - case Instruction::DADDI: - daddi(instr); - break; - case Instruction::DADDIU: - daddiu(instr); - break; - case Instruction::LDL: - ldl(instr); - break; - case Instruction::LDR: - ldr(instr); - break; - case 0x1F: - regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC); - break; - case Instruction::LB: - lb(instr); - break; - case Instruction::LH: - lh(instr); - break; - case Instruction::LWL: - lwl(instr); - break; - case Instruction::LW: - lw(instr); - break; - case Instruction::LBU: - lbu(instr); - break; - case Instruction::LHU: - lhu(instr); - break; - case Instruction::LWR: - lwr(instr); - break; - case Instruction::LWU: - lwu(instr); - break; - case Instruction::SB: - sb(instr); - break; - case Instruction::SH: - sh(instr); - break; - case Instruction::SWL: - swl(instr); - break; - case Instruction::SW: - sw(instr); - break; - case Instruction::SDL: - sdl(instr); - break; - case Instruction::SDR: - sdr(instr); - break; - case Instruction::SWR: - swr(instr); - break; - case Instruction::CACHE: - break; // CACHE - case Instruction::LL: - ll(instr); - break; - case Instruction::LWC1: - lwc1(instr); - break; - case Instruction::LLD: - lld(instr); - break; - case Instruction::LDC1: - ldc1(instr); - break; - case Instruction::LD: - ld(instr); - break; - case Instruction::SC: - sc(instr); - break; - case Instruction::SWC1: - swc1(instr); - break; - case Instruction::SCD: - scd(instr); - break; - case Instruction::SDC1: - sdc1(instr); - break; - case Instruction::SD: - sd(instr); - break; - default: - DumpBlockCacheToDisk(); - panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), - static_cast(regs.oldPC)); - } -} -} // namespace n64 diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp deleted file mode 100644 index 030a91b..0000000 --- a/src/backend/core/jit/helpers.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include - -namespace n64 { -#ifdef _WIN32 -#define ARG1 rcx -#define ARG2 rdx -#define ARG3 r8 -#define ARG4 r9 -#define SCR1 rax -#define SCR2 rcx -#define SCR3 rdx -#define SCR4 r8 -#define SCR5 r9 -#define SCR6 r10 -#define SCR7 r11 -#else -#define ARG1 rdi -#define ARG2 rsi -#define ARG3 rdx -#define ARG4 rcx -#define ARG5 r8 -#define ARG6 r9 -#define SCR1 rax -#define SCR2 rdi -#define SCR3 rsi -#define SCR4 rdx -#define SCR5 rcx -#define SCR6 r8 -#define SCR7 r9 -#define SCR8 r10 -#define SCR9 r11 -#endif -} // namespace n64 diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp deleted file mode 100644 index e8cf89d..0000000 --- a/src/backend/core/jit/instructions.cpp +++ /dev/null @@ -1,1469 +0,0 @@ -#include - -#define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) -#define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) - -namespace n64 { -using namespace Xbyak::util; - -void JIT::lui(const Instruction instr) { - u64 val = static_cast(static_cast(instr)); - val <<= 16; - regs.Write(instr.rt(), val); -} - -void JIT::add(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u32 rs = regs.Read(instr.rs()); - const u32 rt = regs.Read(instr.rt()); - const u32 result = rs + rt; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in ADD!"); - } - - regs.Write(instr.rd(), result); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - const u32 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); - regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const u32 rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rt); - regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - regs.Read(instr.rt(), code.SCR1.cvt32()); - regs.Read(instr.rs(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); -} - -void JIT::addu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const s32 rs = regs.Read(instr.rs()); - const s32 rt = regs.Read(instr.rt()); - const s32 result = rs + rt; - - regs.Write(instr.rd(), result); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - const s32 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); - regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const s32 rs = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); - regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - regs.Read(instr.rt(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); -} - -void JIT::addi(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - if (check_signed_overflow(rs, imm, result)) { - panic("[JIT]: Unhandled Overflow exception in ADDI!"); - } - - regs.Write(instr.rt(), static_cast(result)); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.eax, SCR1.cvt32()); - regs.Write(instr.rt(), code.SCR1.cvt32()); -} - -void JIT::addiu(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - regs.Write(instr.rt(), s32(result)); - - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), imm); - regs.Write(instr.rt(), code.SCR1.cvt32()); -} - -void JIT::andi(const Instruction instr) { - const s64 imm = static_cast(instr); - if (regs.IsRegConstant(instr.rs())) { - regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); - return; - } - - regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); -} - -void JIT::and_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - const auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.and_(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const auto rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, rt); - regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.and_(code.SCR2, code.SCR1); - regs.Write(instr.rd(), code.SCR2); -} - -void JIT::bfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, z); -} - -void JIT::blfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, z); -} - -void JIT::bfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, nz); -} - -void JIT::blfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, nz); -} - -void JIT::BranchNotTaken() {} - -void JIT::BranchTaken(const s64 offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); -} - -void JIT::BranchTaken(const Xbyak::Reg64 &offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); -} - -void JIT::BranchAbsTaken(const s64 addr) { SetPC64(addr); } - -void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { SetPC64(addr); } - -void JIT::branch_constant(const bool cond, const s64 offset) { - if (cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } -} - -void JIT::branch_abs_constant(const bool cond, const s64 address) { - if (cond) { - regs.delaySlot = true; - BranchAbsTaken(address); - branch_taken = true; - } -} - -void JIT::branch_likely_constant(const bool cond, const s64 offset) { - if (cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } else { - SetPC64(blockNextPC); - } -} - -#define branch(offs, cond) \ - do { \ - Xbyak::Label taken, not_taken; \ - code.j##cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ - } \ - while (0) - -#define branch_abs(addr, cond) \ - do { \ - Xbyak::Label taken, not_taken; \ - code.j##cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchAbsTaken(addr); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ - } \ - while (0) - -#define branch_likely(offs, cond) \ - do { \ - Xbyak::Label taken, not_taken, end; \ - code.j##cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.jmp(end); \ - code.L(not_taken); \ - SetPC64(blockNextPC); \ - code.L(end); \ - } \ - while (0) - -void JIT::bltz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); -} - -void JIT::bgez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); -} - -void JIT::bltzl(const Instruction instr) { - panic("Implement branch likely < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); -} - -void JIT::bgezl(const Instruction instr) { - panic("Implement branch likely >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); -} - -void JIT::bltzal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); -} - -void JIT::bgezal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); -} - -void JIT::bltzall(const Instruction instr) { - panic("Implement branch likely and link < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); -} - -void JIT::bgezall(const Instruction instr) { - panic("Implement branch likely and link >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); -} - -void JIT::beq(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, e); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); - branch(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, e); -} - -void JIT::beql(const Instruction instr) { - panic("Implement branch likely =="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, e); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); - branch_likely(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, e); -} - -void JIT::bne(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, ne); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); - branch(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, ne); -} - -void JIT::bnel(const Instruction instr) { - panic("Implement branch likely !="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, ne); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); - branch_likely(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, ne); -} - -void JIT::blez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, le); -} - -void JIT::blezl(const Instruction instr) { - panic("Implement branch likely <= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, le); -} - -void JIT::bgtz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) > 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, g); -} - -void JIT::bgtzl(const Instruction instr) { - panic("Implement branch likely > 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) > 0, offset); - return; - } - - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, g); -} - -void JIT::dadd(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto rs = regs.Read(instr.rs()); - auto rt = regs.Read(instr.rt()); - u64 result = rt + rs; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADD!"); - } - regs.Write(instr.rd(), result); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.add(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, rt); - regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.add(code.SCR1, code.SCR2); - regs.Write(instr.rd(), code.SCR1); -} - -void JIT::daddu(const Instruction instr) { - // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY - dadd(instr); -} - -void JIT::daddi(const Instruction instr) { - u64 imm = s64(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u64 result = imm + rs; - if (check_signed_overflow(rs, imm, result)) { - // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADDI!"); - } - regs.Write(instr.rt(), result); - return; - } - - regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); -} - -void JIT::daddiu(const Instruction instr) { - // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY - daddi(instr); -} - -void JIT::ddiv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { - regs.lo = dividend; - regs.hi = 0; - } else if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = -1; - } else { - regs.lo = 1; - } - } else { - s64 quotient = dividend / divisor; - s64 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIV!"); - } -} - -void JIT::ddivu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s64)dividend; - } else { - u64 quotient = dividend / divisor; - u64 remainder = dividend % divisor; - regs.lo = (s64)quotient; - regs.hi = (s64)remainder; - } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIVU!"); - } -} - -void JIT::div(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 dividend = regs.Read(instr.rs()); - s64 divisor = regs.Read(instr.rt()); - - if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = s64(-1); - } else { - regs.lo = s64(1); - } - } else { - s32 quotient = dividend / divisor; - s32 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIV!"); - } -} - -void JIT::divu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s32)dividend; - } else { - s32 quotient = (s32)(dividend / divisor); - s32 remainder = (s32)(dividend % divisor); - regs.lo = quotient; - regs.hi = remainder; - } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIVU!"); - } -} - -void JIT::dmult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s128 result = (s128)rt * (s128)rs; - regs.lo = result & 0xFFFFFFFFFFFFFFFF; - regs.hi = result >> 64; - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } -} - -void JIT::dmultu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u128 result = (u128)rt * (u128)rs; - regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); - regs.hi = s64(result >> 64); - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } -} - -void JIT::dsll(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL!"); - } -} - -void JIT::dsllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto sa = regs.Read(instr.rs()) & 63; - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLLV!"); - } -} - -void JIT::dsll32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL32!"); - } -} - -void JIT::dsra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA!"); - } -} - -void JIT::dsrav(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 sa = rs & 63; - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRAV!"); - } -} - -void JIT::dsra32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA32!"); - } -} - -void JIT::dsrl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> sa; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL!"); - } -} - -void JIT::dsrlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - u8 amount = regs.Read(instr.rs()) & 63; - auto rt = regs.Read(instr.rt()); - u64 result = rt >> amount; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRLV!"); - } -} - -void JIT::dsrl32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> (sa + 32); - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL32!"); - } -} - -void JIT::dsub(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DSUB!"); - } else { - regs.Write(instr.rd(), result); - } - } else { - panic("[JIT]: Implement non constant DSUB!"); - } -} - -void JIT::dsubu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSUBU!"); - } -} - -void JIT::j(const Instruction instr) { - const s32 target = (instr & 0x3ffffff) << 2; - const s64 address = blockOldPC & ~0xfffffff | target; - branch_abs_constant(true, address); -} - -void JIT::jr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const auto address = regs.Read(instr.rs()); - branch_abs_constant(true, address); - return; - } - - regs.Read(instr.rs(), code.rax); - branch_abs(code.rax, mp); -} - -void JIT::jal(const Instruction instr) { - regs.Write(31, blockNextPC); - j(instr); -} - -void JIT::jalr(const Instruction instr) { - regs.Write(instr.rd(), blockNextPC); - jr(instr); -} - -void JIT::lbu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LBU!"); - } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - } - } else { - panic("[JIT]: Implement non constant LBU!"); - } -} - -void JIT::lb(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); - } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - } - } else { - panic("[JIT]: Implement non constant LB!"); - } -} - -void JIT::ld(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (Core::IsAddressError(0b111, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LD!"); - } - - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LD!"); - } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - } - } else { - panic("[JIT]: Implement non constant LD!"); - } -} - -void JIT::ldc1(const Instruction instr) { - if (regs.IsRegConstant(instr.base())) { - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - // regs.cop0.HandleTLBException(addr); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LD1!"); - } else { - const u64 data = mem.Read(physical); - regs.cop1.FGR_T(regs.cop0.status, instr.ft()) = data; - regs.cop1.fgrIsConstant[instr.ft()] = true; - } - } else { - panic("[JIT]: Implement non constant LD1!"); - } -} - -void JIT::ldl(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LDL!"); - } else { - panic("[JIT]: Implement constant LDL!"); - const s32 shift = 8 * ((address ^ 0) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); - regs.Write(instr.rt(), result); - } - } else { - panic("[JIT]: Implement non constant LDL!"); - } -} - -void JIT::ldr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LDR!"); - } else { - const s32 shift = 8 * ((address ^ 7) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); - regs.Write(instr.rt(), result); - } - } else { - panic("[JIT]: Implement non constant LDR!"); - } -} - -void JIT::lh(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (Core::IsAddressError(0b1, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LH!"); - return; - } - - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LH!"); - return; - } - - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - return; - } - - panic("[JIT]: Implement non constant LH!"); -} - -void JIT::lhu(const Instruction instr) { - u32 paddr; - if (regs.IsRegConstant(instr.rs())) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (Core::IsAddressError(0b1, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } - - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - return; - } - - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - } - - code.mov(code.ARG2, Cop0::LOAD); - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, s16(instr)); - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); -} - -void JIT::ll(const Instruction) { panic("[JIT]: Implement constant LL!"); } - -void JIT::lld(const Instruction) { panic("[JIT]: Implement constant LLD!"); } - -void JIT::lw(const Instruction instr) { - const s16 offset = instr; - u32 paddr = 0; - if (regs.IsRegConstant(instr.rs())) { - const u64 address = regs.Read(instr.rs()) + offset; - if (Core::IsAddressError(0b11, address)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - // return; - panic("[JIT]: Unhandled ADEL exception in LW!"); - return; - } - - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LW!"); - return; - } - - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - return; - } - - code.mov(code.ARG2, Cop0::LOAD); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); -} - -void JIT::lwc1(const Instruction) { panic("[JIT]: Implement constant LWC1!"); } - -void JIT::lwl(const Instruction) { panic("[JIT]: Implement constant LWL!"); } - -void JIT::lwu(const Instruction) { panic("[JIT]: Implement constant LWU!"); } - -void JIT::lwr(const Instruction) { panic("[JIT]: Implement constant LWR!"); } - -void JIT::mfhi(const Instruction instr) { - if (regs.GetHIConstant()) { - regs.Write(instr.rd(), regs.hi); - } else { - code.mov(code.SCR1, code.qword[HI_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } -} - -void JIT::mflo(const Instruction instr) { - if (regs.GetLOConstant()) { - regs.Write(instr.rd(), regs.lo); - } else { - code.mov(code.SCR1, code.qword[LO_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } -} - -void JIT::mult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = (s64)rt * (s64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULT!"); - } -} - -void JIT::multu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u64 result = (u64)rt * (u64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULTU!"); - } -} - -void JIT::mthi(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.hi = regs.Read(instr.rs()); - regs.SetHIConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[HI_OFFSET], code.SCR1); - regs.UnsetHIConstant(); - } -} - -void JIT::mtlo(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.lo = regs.Read(instr.rs()); - regs.SetLOConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[LO_OFFSET], code.SCR1); - regs.UnsetLOConstant(); - } -} - -void JIT::nor(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); - } else { - panic("[JIT]: Implement non constant NOR!"); - } -} - -void JIT::slti(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - return; - } - - panic("[JIT]: Implement non constant SLTI!"); -} - -void JIT::sltiu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - } else { - panic("[JIT]: Implement non constant SLTIU!"); - } -} - -void JIT::slt(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - return; - } - - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1); - code.cmp(code.SCR1, rt); - code.setl(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - s64 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.cmp(code.SCR1, rs); - code.setge(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.cmp(code.SCR1, code.SCR2); - code.setl(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); -} - -void JIT::sltu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant SLT!"); - } -} - -void JIT::sll(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - if (regs.IsRegConstant(instr.rt())) { - const s32 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), (s64)result); - } else { - regs.Read(instr.rt(), code.SCR1); - code.sal(code.SCR1, sa); - regs.Write(instr.rd(), code.SCR1.cvt32()); - } -} - -void JIT::sllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs())) & 0x1F; - u32 rt = regs.Read(instr.rt()); - s32 result = rt << sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SLLV!"); - } -} - -void JIT::sub(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s32 rt = regs.Read(instr.rt()); - s32 rs = regs.Read(instr.rs()); - s32 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rd(), result); - } - } else { - panic("[JIT]: Implement non constant SUB!"); - } -} - -void JIT::subu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u32 rs = regs.Read(instr.rs()); - u32 result = rs - rt; - regs.Write(instr.rd(), (s64)((s32)result)); - } else { - panic("[JIT]: Implement non constant SUBU!"); - } -} - -void JIT::sra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRA!"); - } -} - -void JIT::srav(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 rs = regs.Read(instr.rs()); - s64 rt = regs.Read(instr.rt()); - u8 sa = rs & 0x1f; - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRAV!"); - } -} - -void JIT::srl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u32 result = rt >> sa; - regs.Write(instr.rd(), (s32)result); - } else { - panic("[JIT]: Implement non constant SRL!"); - } -} - -void JIT::sw(const Instruction instr) { - u32 physical; - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - 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!"); - return; - } - - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); - } - - return; - } - - if (regs.IsRegConstant(instr.rs())) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - 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!"); - return; - } - - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - // regs.cop0.HandleTLBException(address); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); - } - - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const s16 offset = instr; - code.mov(code.ARG2, Cop0::STORE); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&physical)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); - - return; - } - - const s16 offset = instr; - code.mov(code.ARG2, Cop0::STORE); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&physical)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); -} - -void JIT::srlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs()) & 0x1F); - u32 rt = regs.Read(instr.rt()); - s32 result = rt >> sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SRLV!"); - } -} - -void JIT::or_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant OR!"); - } -} - -void JIT::ori(const Instruction instr) { - s64 imm = (u16)instr; - if (regs.IsRegConstant(instr.rs())) { - s64 result = imm | regs.Read(instr.rs()); - regs.Write(instr.rt(), result); - return; - } - - regs.Read(instr.rs(), code.SCR1); - code.or_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); -} - -void JIT::xori(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s64 imm = (u16)instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); - } else { - panic("[JIT]: Implement non constant XORI!"); - } -} - -void JIT::xor_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); - } else { - panic("[JIT]: Implement non constant XOR!"); - } -} -} // namespace n64 diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index 98726bb..68712c5 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -539,6 +539,7 @@ void PI::DMA() { for (u32 i = 0; i < len; i++) { mem.mmio.rdp.WriteRDRAM(dramAddr + i, BusRead(cartAddr + i)); } + Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dramAddr, dramAddr + len); dramAddr += len; dramAddr = (dramAddr + 7) & ~7; cartAddr += len; diff --git a/src/backend/core/mmio/SI.cpp b/src/backend/core/mmio/SI.cpp index 9a35327..539c249 100644 --- a/src/backend/core/mmio/SI.cpp +++ b/src/backend/core/mmio/SI.cpp @@ -78,6 +78,7 @@ void SI::Write(u32 addr, u32 val) { status.dmaBusy = true; toDram = true; Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA); + Core::GetInstance().interpreter.cachedState.EvictCachedBlocksRange(dramAddr, dramAddr + 64); break; case 0x04800010: pifAddr = val & 0x1FFFFFFF; diff --git a/src/backend/core/mmio/VI.cpp b/src/backend/core/mmio/VI.cpp index f4d790f..fa478e6 100644 --- a/src/backend/core/mmio/VI.cpp +++ b/src/backend/core/mmio/VI.cpp @@ -5,123 +5,122 @@ namespace n64 { VI::VI() { Reset(); } void VI::Reset() { - status.raw = 0xF; - intr = 256; - origin = 0; - width = 320; - current = 0; - vsync = 0; - hsync = 0; - numHalflines = 262; - numFields = 1; - cyclesPerHalfline = 1000; - xscale = {}, yscale = {}; - hsyncLeap = {}, burst = {}, vburst = {}; - hstart = {}, vstart = {}; - isPal = false; - swaps = {}; + status.raw = 0xF; + intr = 256; + origin = 0; + width = 320; + current = 0; + vsync = 0; + hsync = 0; + numHalflines = 262; + numFields = 1; + cyclesPerHalfline = 1000; + xscale = {}, yscale = {}; + hsyncLeap = {}, burst = {}, vburst = {}; + hstart = {}, vstart = {}; + isPal = false; + swaps = {}; } u32 VI::Read(const u32 paddr) const { - switch (paddr) { + switch (paddr) { case 0x04400000: - return status.raw; + return status.raw; case 0x04400004: - return origin; + return origin; case 0x04400008: - return width; + return width; case 0x0440000C: - return intr; + return intr; case 0x04400010: - return current << 1; + return current << 1; case 0x04400014: - return burst.raw; + return burst.raw; case 0x04400018: - return vsync; + return vsync; case 0x0440001C: - return hsync; + return hsync; case 0x04400020: - return hsyncLeap.raw; + return hsyncLeap.raw; case 0x04400024: - return hstart.raw; + return hstart.raw; case 0x04400028: - return vstart.raw; + return vstart.raw; case 0x0440002C: - return vburst; + return vburst; case 0x04400030: - return xscale.raw; + return xscale.raw; case 0x04400034: - return yscale.raw; - default: { - n64::Registers& regs = n64::Core::GetRegs(); - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access on unhandled VI register"); - return 0; + return yscale.raw; + default: + { + n64::Registers ®s = n64::Core::GetRegs(); + panic("32-bit read access on unhandled VI register @ pc 0x{:016X}", u64(regs.oldPC)); + return 0; + } } - } } void VI::Write(const u32 paddr, const u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - switch (paddr) { - case 0x04400000: - status.raw = val; - numFields = status.serrate ? 2 : 1; - break; - case 0x04400004: - { - const u32 masked = val & 0xFFFFFF; - if (origin != masked) { - swaps++; - } - origin = masked; + n64::Mem &mem = n64::Core::GetMem(); + switch (paddr) { + case 0x04400000: + status.raw = val; + numFields = status.serrate ? 2 : 1; + break; + case 0x04400004: + { + const u32 masked = val & 0xFFFFFF; + if (origin != masked) { + swaps++; + } + origin = masked; + } + break; + case 0x04400008: + width = val & 0x7FF; + break; + case 0x0440000C: + intr = val & 0x3FF; + break; + case 0x04400010: + mem.mmio.mi.InterruptLower(MI::Interrupt::VI); + break; + case 0x04400014: + burst.raw = val; + break; + case 0x04400018: + vsync = val & 0x3FF; + numHalflines = vsync >> 1; + cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines; + break; + case 0x0440001C: + hsync = val & 0x3FF; + break; + case 0x04400020: + hsyncLeap.raw = val; + break; + case 0x04400024: + hstart.raw = val; + break; + case 0x04400028: + vstart.raw = val; + break; + case 0x0440002C: + vburst = val; + break; + case 0x04400030: + xscale.raw = val; + break; + case 0x04400034: + yscale.raw = val; + break; + case 0x04400038: + break; + case 0x0440003C: + break; + default: + panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val); } - break; - case 0x04400008: - width = val & 0x7FF; - break; - case 0x0440000C: - intr = val & 0x3FF; - break; - case 0x04400010: - mem.mmio.mi.InterruptLower(MI::Interrupt::VI); - break; - case 0x04400014: - burst.raw = val; - break; - case 0x04400018: - vsync = val & 0x3FF; - numHalflines = vsync >> 1; - cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines; - break; - case 0x0440001C: - hsync = val & 0x3FF; - break; - case 0x04400020: - hsyncLeap.raw = val; - break; - case 0x04400024: - hstart.raw = val; - break; - case 0x04400028: - vstart.raw = val; - break; - case 0x0440002C: - vburst = val; - break; - case 0x04400030: - xscale.raw = val; - break; - case 0x04400034: - yscale.raw = val; - break; - case 0x04400038: - break; - case 0x0440003C: - break; - default: - panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val); - } } } // namespace n64 diff --git a/src/backend/core/registers/Cop0.hpp b/src/backend/core/registers/Cop0.hpp index 02b9db3..ac0d21c 100644 --- a/src/backend/core/registers/Cop0.hpp +++ b/src/backend/core/registers/Cop0.hpp @@ -275,8 +275,6 @@ struct Cop0 { } private: - friend struct JIT; - [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index a79d4a7..ebb5af5 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -120,7 +120,6 @@ struct Cop1 { void Reset(); void decode(const Instruction); friend struct Interpreter; - friend struct JIT; template bool CheckFPUUsable(); diff --git a/src/backend/core/registers/Registers.cpp b/src/backend/core/registers/Registers.cpp index e5f6a21..320c413 100644 --- a/src/backend/core/registers/Registers.cpp +++ b/src/backend/core/registers/Registers.cpp @@ -1,13 +1,7 @@ -#include #include -#include namespace n64 { -#ifdef KAIZEN_JIT_ENABLED -Registers::Registers(JIT &jit) : jit(jit) { Reset(); } -#else Registers::Registers() { Reset(); } -#endif void Registers::Reset() { hi = 0; @@ -76,88 +70,6 @@ s8 Registers::Read(size_t idx) { return static_cast(Read(idx)); } -#ifdef KAIZEN_JIT_ENABLED -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt64(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt64(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt64(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt64(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt32(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt32(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt32(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt32(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt16(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt16(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt16(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt16(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt8(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt8(), jit.GPR(idx)); -} - -template <> -void Registers::Read(size_t idx, Xbyak::Reg reg) { - if (IsRegConstant(idx)) { - jit.code.mov(reg.cvt8(), Read(idx)); - return; - } - - jit.code.mov(reg.cvt8(), jit.GPR(idx)); -} -#endif - template <> void Registers::Write(size_t idx, bool v) { if (idx == 0) @@ -245,110 +157,4 @@ void Registers::Write(size_t idx, s8 v) { gpr[idx] = v; } - -#ifdef KAIZEN_JIT_ENABLED -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - regIsConstant &= ~(1 << idx); - - jit.code.movsx(v.cvt64(), v.cvt8()); - jit.code.mov(jit.GPR(idx), v); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - regIsConstant &= ~(1 << idx); - - jit.code.movsx(v.cvt64(), v.cvt8()); - jit.code.mov(jit.GPR(idx), v); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - regIsConstant &= ~(1 << idx); - - jit.code.movzx(v.cvt64(), v.cvt8()); - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - regIsConstant &= ~(1 << idx); - - jit.code.movsx(v.cvt64(), v.cvt16()); - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - - regIsConstant &= ~(1 << idx); - - jit.code.movzx(v.cvt64(), v.cvt16()); - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - - regIsConstant &= ~(1 << idx); - - jit.code.movsxd(v.cvt64(), v.cvt32()); - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - - regIsConstant &= ~(1 << idx); - - jit.code.movzx(v.cvt64(), v.cvt32()); - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; - - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - - regIsConstant &= ~(1 << idx); - - jit.code.mov(jit.GPR(idx), v.cvt64()); -} - -template <> -void Registers::Write(size_t idx, Xbyak::Reg v) { - Write(idx, v); -} -#endif } // namespace n64 diff --git a/src/backend/core/registers/Registers.hpp b/src/backend/core/registers/Registers.hpp index f137b34..45bad81 100644 --- a/src/backend/core/registers/Registers.hpp +++ b/src/backend/core/registers/Registers.hpp @@ -1,15 +1,8 @@ #pragma once #include -#include #include namespace n64 { -#ifdef KAIZEN_JIT_ENABLED -struct JIT; -struct Registers { - JIT &jit; - Registers(JIT &jit); -#else struct Registers { Registers(); void Reset(); @@ -59,11 +52,7 @@ struct Registers { template T Read(size_t); template - void Read(size_t, Xbyak::Reg); - template void Write(size_t, T); - template - void Write(size_t, Xbyak::Reg); std::array gpr{}; @@ -71,5 +60,4 @@ struct Registers { "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"}; }; -#endif } // namespace n64 diff --git a/src/common.hpp b/src/common.hpp index 8286d5a..618ec38 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include #define FORCE_INLINE inline __attribute__((always_inline)) diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 7bb8cc0..dd6700e 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -160,24 +160,6 @@ void KaizenGui::HandleInput(const SDL_Event &event) { } } -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 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}; -} - void KaizenGui::RenderUI() { n64::Core &core = n64::Core::GetInstance(); gui::StartFrame(); @@ -234,10 +216,6 @@ void KaizenGui::RenderUI() { ImGui::EndMainMenuBar(); } - if (!Util::Error::IsHandled()) { - ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); - } - if (settingsWindow.isOpen) { ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); } @@ -270,114 +248,6 @@ void KaizenGui::RenderUI() { ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - 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(); - - 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(); - } - - 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(); - } - if (ImGui::BeginMainStatusBar()) { ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); ImGui::EndMainStatusBar(); diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index 9d0e9f1..d2d9f6e 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -6,45 +6,45 @@ #include class KaizenGui final { - gui::NativeWindow window; -public: - explicit KaizenGui() noexcept; - ~KaizenGui(); + gui::NativeWindow window; - double fpsCounter = -1.0; - bool fastForward = false; - bool unlockFramerate = false; - bool minimized = false; + public: + explicit KaizenGui() noexcept; + ~KaizenGui(); - SettingsWindow settingsWindow; - RenderWidget vulkanWidget; - EmuThread emuThread; - Debugger debugger; + double fpsCounter = -1.0; + bool fastForward = false; + bool unlockFramerate = false; + bool minimized = false; - SDL_Gamepad* gamepad = nullptr; + SettingsWindow settingsWindow; + RenderWidget vulkanWidget; + EmuThread emuThread; + Debugger debugger; - void run(); - static void LoadTAS(const std::string &path) noexcept; - void LoadROM(const std::string &path) noexcept; -private: - int width{}, height{}; - bool aboutOpen = false; - bool fileDialogOpen = false; - bool quit = false; - bool shouldDisplaySpinner = false; - std::string fileToLoad = ""; - void RenderUI(); - void HandleInput(const SDL_Event &event); - void QueryDevices(const SDL_Event &event); + SDL_Gamepad *gamepad = nullptr; + + void run(); + static void LoadTAS(const std::string &path) noexcept; + void LoadROM(const std::string &path) noexcept; + + private: + int width{}, height{}; + bool aboutOpen = false; + bool fileDialogOpen = false; + bool quit = false; + bool shouldDisplaySpinner = false; + std::string fileToLoad = ""; + void RenderUI(); + void HandleInput(const SDL_Event &event); + void QueryDevices(const SDL_Event &event); + + void FileWorker() { + if (fileToLoad.empty()) + return; - 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 12e4ed4..1a563ce 100644 --- a/src/frontend/Settings/CPUSettings.cpp +++ b/src/frontend/Settings/CPUSettings.cpp @@ -6,21 +6,17 @@ CPUSettings::CPUSettings() { auto selectedCpuType = Options::GetInstance().GetValue("cpu", "type"); - if (selectedCpuType == "jit") { - selectedCpuTypeIndex = 2; - } else if (selectedCpuType == "cached_interpreter") { + if (selectedCpuType == "cached_interpreter") { selectedCpuTypeIndex = 1; } else { selectedCpuTypeIndex = 0; } + + idleSkip = Options::GetInstance().GetValue("cpu", "idleSkip"); } void CPUSettings::render() { - const char *items[] = {"Interpreter", "Cached Interpreter", -#ifdef KAIZEN_JIT_ENABLED - "Dynamic Recompiler" -#endif - }; + const char *items[] = {"Interpreter", "Cached Interpreter"}; const char *combo_preview_value = items[selectedCpuTypeIndex]; if (ImGui::BeginCombo("CPU Type", combo_preview_value)) { @@ -38,16 +34,23 @@ void CPUSettings::render() { ImGui::EndCombo(); } + bool showIdleSkipping = std::string(items[selectedCpuTypeIndex]) == "Cached Interpreter"; + if (showIdleSkipping) + if (ImGui::Checkbox("Idle skipping", &idleSkip)) + modified = true; + 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; + idleSkip = false; + Options::GetInstance().SetValue("cpu", "idleSkip", idleSkip); + n64::Core::GetInstance().cpuType = n64::Core::PlainInterpreter; + n64::Core::GetInstance().idleSkip = idleSkip; } else { - Options::GetInstance().SetValue("cpu", "type", "jit"); - n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler; + Options::GetInstance().SetValue("cpu", "type", "cached_interpreter"); + Options::GetInstance().SetValue("cpu", "idleSkip", idleSkip); + n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter; + n64::Core::GetInstance().idleSkip = idleSkip; } } } diff --git a/src/frontend/Settings/CPUSettings.hpp b/src/frontend/Settings/CPUSettings.hpp index 11fd921..3a8c8a2 100644 --- a/src/frontend/Settings/CPUSettings.hpp +++ b/src/frontend/Settings/CPUSettings.hpp @@ -2,7 +2,8 @@ #include struct CPUSettings final : SettingsTab { - int selectedCpuTypeIndex = 0; - void render() override; - explicit CPUSettings(); + int selectedCpuTypeIndex = 0; + bool idleSkip = false; + void render() override; + explicit CPUSettings(); }; diff --git a/src/utils/ErrorData.hpp b/src/utils/ErrorData.hpp deleted file mode 100644 index 43de503..0000000 --- a/src/utils/ErrorData.hpp +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace Util { -struct Error { - struct Severity { - enum { - NONE, - WARN, - NON_FATAL, - UNRECOVERABLE, - } as_enum; - - [[nodiscard]] const char* as_c_str() const { - switch(as_enum) { - case NONE: return ""; - case WARN: return "Warning"; - case NON_FATAL: return "Error"; - case UNRECOVERABLE: return "Unrecoverable Error"; - } - - return "Unknown"; - } - }; - - struct MemoryAccess { - bool is_write; - enum Size { - BYTE = 8, SHORT = 16, WORD = 32, DWORD = 64 - } size; - u32 address; - u64 written_val; - }; - - struct Type { - enum { - SCHEDULER_EOL, - SCHEDULER_UNKNOWN, - UNHANDLED_EXCEPTION, - UNHANDLED_INSTRUCTION, - INVALID_INSTRUCTION_FORMAT, - TLB_LIMIT_EXCEEDED, - TLB_INVALID_ERROR, - TLB_UNHANDLED_ERROR, - TLB_UNHANDLED_MAPPING, - JIT_BRANCH_INSIDE_DELAY_SLOT, - JIT_INVALID_X86_REG_ADDRESSING, - COULD_NOT_SYNC_SAVE_DATA, - SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE, - MMAP_MAKE_SINK_ERROR, - MEM_INVALID_ACCESS, - MEM_UNHANDLED_ACCESS, - RDP_LIMIT_EXCEEDED, - FLASH_EXECUTE_COMMAND, - PIF_UNHANDLED_CHANNEL, - UNHANDLED_COP0_STATUS_BIT, - COP0_INVALID_ACCESS, - COP0_UNHANDLED_ACCESS, - SYSTEM_DIALOG_ERROR, - TAS_LOAD_ERROR, - ROM_LOAD_ERROR, - SAVE_OPTIONS_ERROR, - DIALOG_CANCELED, - UNKNOWN_CIC_TYPE, - GAME_DB_NOT_MATCHED, - CAPSTONE_ERROR, - } as_enum; - - - [[nodiscard]] const char* as_c_str() const { - switch(as_enum) { - case SCHEDULER_EOL: return "SCHEDULER_EOL"; - case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN"; - case UNHANDLED_EXCEPTION: return "UNHANDLED_EXCEPTION"; - case UNHANDLED_INSTRUCTION: return "UNHANDLED_INSTRUCTION"; - case INVALID_INSTRUCTION_FORMAT: return "INVALID_INSTRUCTION_FORMAT"; - case TLB_LIMIT_EXCEEDED: return "TLB_LIMIT_EXCEEDED"; - case TLB_INVALID_ERROR: return "TLB_INVALID_ERROR"; - case TLB_UNHANDLED_ERROR: return "TLB_UNHANDLED_ERROR"; - case TLB_UNHANDLED_MAPPING: return "TLB_UNHANDLED_MAPPING"; - case JIT_BRANCH_INSIDE_DELAY_SLOT: return "JIT_BRANCH_INSIDE_DELAY_SLOT"; - case JIT_INVALID_X86_REG_ADDRESSING: return "JIT_INVALID_X86_REG_ADDRESSING"; - case COULD_NOT_SYNC_SAVE_DATA: return "COULD_NOT_SYNC_SAVE_DATA"; - case SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE: return "SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE"; - case MMAP_MAKE_SINK_ERROR: return "MMAP_MAKE_SINK_ERROR"; - case MEM_INVALID_ACCESS: return "MEM_INVALID_ACCESS"; - case MEM_UNHANDLED_ACCESS: return "MEM_UNHANDLED_ACCESS"; - case RDP_LIMIT_EXCEEDED: return "RDP_LIMIT_EXCEEDED"; - case FLASH_EXECUTE_COMMAND: return "FLASH_EXECUTE_COMMAND"; - case PIF_UNHANDLED_CHANNEL: return "PIF_UNHANDLED_CHANNEL"; - case UNHANDLED_COP0_STATUS_BIT: return "UNHANDLED_COP0_STATUS_BIT"; - case COP0_INVALID_ACCESS: return "COP0_INVALID_ACCESS"; - case COP0_UNHANDLED_ACCESS: return "COP0_UNHANDLED_ACCESS"; - case SYSTEM_DIALOG_ERROR: return "SYSTEM_DIALOG_ERROR"; - case TAS_LOAD_ERROR: return "TAS_LOAD_ERROR"; - case ROM_LOAD_ERROR: return "ROM_LOAD_ERROR"; - case SAVE_OPTIONS_ERROR: return "SAVE_OPTIONS_ERROR"; - case DIALOG_CANCELED: return "DIALOG_CANCELED"; - case UNKNOWN_CIC_TYPE: return "UNKNOWN_CIC_TYPE"; - case GAME_DB_NOT_MATCHED: return "GAME_DB_NOT_MATCHED"; - case CAPSTONE_ERROR: return "CAPSTONE_ERROR"; - default: return "Unknown"; - } - } - }; - - template - void Throw (const Severity severity, const Type type, const std::optional lastPC, - const std::optional memoryAccess, - const std::format_string fmt, Args... args) { - this->severity = severity; - this->lastPC = lastPC; - this->memoryAccess = memoryAccess; - this->type = type; - err = std::format(fmt, std::forward(args)...); - } - - static Error& GetInstance() { - static Error instance; - return instance; - } - - static std::string& GetError() { return GetInstance().err; } - static Severity& GetSeverity() { return GetInstance().severity; } - static Type& GetType() { return GetInstance().type; } - static std::optional& GetLastPC() { return GetInstance().lastPC; } - static std::optional& GetMemoryAccess() { return GetInstance().memoryAccess; } - static bool IsHandled() { - return GetSeverity().as_enum == Severity::NONE; - } - - static void SetHandled() { - GetSeverity() = {}; - GetError() = ""; - GetType() = {}; - GetLastPC() = {}; - GetMemoryAccess() = {}; - } -private: - std::string err; - Severity severity = {}; - Type type = {}; - std::optional lastPC = {}; - std::optional memoryAccess = {}; -}; -} \ No newline at end of file diff --git a/src/utils/Options.hpp b/src/utils/Options.hpp index 8450a2b..ae9e3bb 100644 --- a/src/utils/Options.hpp +++ b/src/utils/Options.hpp @@ -10,7 +10,7 @@ namespace fs = std::filesystem; struct Options { Options() : file{"resources/options.ini"} { auto fileExists = fs::exists("resources/options.ini"); - if(fileExists) { + if (fileExists) { file.read(structure); return; } @@ -21,8 +21,9 @@ struct Options { structure["audio"]["volumeR"] = "0.5"; structure["audio"]["lock"] = "true"; structure["cpu"]["type"] = "interpreter"; + structure["cpu"]["idleSkip"] = "false"; - if(!file.generate(structure)) + if (!file.generate(structure)) panic("Couldn't generate settings' INI!"); } @@ -37,10 +38,11 @@ struct Options { T GetValue(const std::string &key, const std::string &field); void Apply() { - if(!file.write(structure)) + if (!file.write(structure)) panic("Could not modify options on disk!"); } -private: + + private: mINI::INIFile file; mINI::INIStructure structure; -}; \ No newline at end of file +};