From 3fb03c6aba5462b92e48f182082e223d573d8fc7 Mon Sep 17 00:00:00 2001 From: irisz64 Date: Sun, 3 Aug 2025 20:06:16 +0200 Subject: [PATCH] Sorta works --- CMakeLists.txt | 1 + src/backend/Core.cpp | 8 +- src/backend/Core.hpp | 2 +- src/backend/RomHelpers.hpp | 5 +- src/backend/Scheduler.cpp | 6 +- src/backend/core/Disassembler.cpp | 132 ------------------ src/backend/core/Disassembler.hpp | 26 +--- src/backend/core/JIT.cpp | 43 ++++-- src/backend/core/JIT.hpp | 77 +++++++++-- src/backend/core/Mem.cpp | 42 ++++-- src/backend/core/mmio/PIF/MupenMovie.cpp | 13 +- src/backend/core/mmio/PIF/MupenMovie.hpp | 2 +- src/backend/core/mmio/VI.cpp | 69 +++++----- src/frontend/Debugger.cpp | 126 +++++++++++------- src/frontend/Debugger.hpp | 8 +- src/frontend/ImGuiImpl/GUI.hpp | 10 +- src/frontend/KaizenGui.cpp | 123 ++++++++++++++++- src/utils/ErrorData.hpp | 163 +++++++++++++++-------- src/utils/File.cpp | 58 ++++++++ src/utils/File.hpp | 55 +------- 20 files changed, 563 insertions(+), 406 deletions(-) create mode 100644 src/utils/File.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b7c8d3a..441e6208 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ add_executable(kaizen src/frontend/AudioSettings.cpp src/frontend/NativeWindow.hpp src/utils/Options.cpp + src/utils/File.cpp src/frontend/Debugger.hpp src/frontend/Debugger.cpp) diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index b6d33425..65dac597 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -29,10 +29,11 @@ void Core::Reset() { regs.Reset(); mem->Reset(); cpu->Reset(); - mem->mmio.si.pif.Execute(); + if(romLoaded) + mem->mmio.si.pif.Execute(); } -bool Core::LoadTAS(const fs::path &path) const { return mem->mmio.si.pif.movie.Load(path); } +void Core::LoadTAS(const fs::path &path) const { mem->mmio.si.pif.movie.Load(path); } void Core::LoadROM(const std::string &rom_) { Stop(); @@ -75,6 +76,9 @@ void Core::Run(float volumeL, float volumeR) { u32 taken = cpu->Step(); taken += regs.PopStalledCycles(); + if(!Util::Error::IsHandled()) + return; + regs.steps += taken; if (mmio.rsp.spStatus.halt) { regs.steps = 0; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index c0a8f610..c11741e1 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -31,7 +31,7 @@ struct Core { void Stop(); void Reset(); void LoadROM(const std::string &); - [[nodiscard]] bool LoadTAS(const fs::path &) const; + void LoadTAS(const fs::path &) const; void Run(float volumeL, float volumeR); void TogglePause() { pause = !pause; } inline void ToggleBreakpoint(s64 addr) { diff --git a/src/backend/RomHelpers.hpp b/src/backend/RomHelpers.hpp index 8b29b3d2..15a2f2b3 100644 --- a/src/backend/RomHelpers.hpp +++ b/src/backend/RomHelpers.hpp @@ -13,7 +13,8 @@ FORCE_INLINE void SwapN64Rom(std::vector &rom, u32 endianness) { if (endianness >> 24 != 0x80) { if ((endianness & 0xFF) != 0x80) { if ((endianness >> 16 & 0xff) != 0x80) { - panic("TODO: Unrecognized rom endianness. Ideally, this should be more robust"); + Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); + return; } else { altByteShift = 12; } @@ -41,7 +42,7 @@ FORCE_INLINE void SwapN64Rom(std::vector &rom, u32 endianness) { SwapBuffer(rom); break; default: - panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!"); + Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "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 3486b78b..d031a0df 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -40,9 +40,11 @@ void Scheduler::Tick(const u64 t) { case NONE: break; case IMPOSSIBLE: - panic("Congratulations on keeping the emulator on for about 5 billion years, I guess, nerd."); + Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); + return; default: - panic("Unknown scheduler event type {}", static_cast(type)); + Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast(type)); + return; } events.pop(); } diff --git a/src/backend/core/Disassembler.cpp b/src/backend/core/Disassembler.cpp index 91101be8..c20129c1 100644 --- a/src/backend/core/Disassembler.cpp +++ b/src/backend/core/Disassembler.cpp @@ -2,119 +2,6 @@ #include #include -template <> -std::optional Disassembler::CapstoneToRegValue(mips_reg reg) const { - n64::Registers& regs = n64::Core::GetRegs(); - u64 val = 0; - switch(reg) { - case MIPS_REG_ZERO: case MIPS_REG_ZERO_64: case MIPS_REG_ZERO_NM: - val = 0; - break; - case MIPS_REG_AT: case MIPS_REG_AT_64: case MIPS_REG_AT_NM: - val = regs.Read(1); - break; - case MIPS_REG_V0: case MIPS_REG_V0_64: - val = regs.Read(2); - break; - case MIPS_REG_V1: case MIPS_REG_V1_64: - val = regs.Read(3); - break; - case MIPS_REG_A0: case MIPS_REG_A0_64: - val = regs.Read(4); - break; - case MIPS_REG_A1: case MIPS_REG_A1_64: - val = regs.Read(5); - break; - case MIPS_REG_A2: case MIPS_REG_A2_64: - val = regs.Read(6); - break; - case MIPS_REG_A3: case MIPS_REG_A3_64: - val = regs.Read(7); - break; - case MIPS_REG_T0: case MIPS_REG_T0_64: - val = regs.Read(8); - break; - case MIPS_REG_T1: case MIPS_REG_T1_64: - val = regs.Read(9); - break; - case MIPS_REG_T2: case MIPS_REG_T2_64: - val = regs.Read(10); - break; - case MIPS_REG_T3: case MIPS_REG_T3_64: - val = regs.Read(11); - break; - case MIPS_REG_T4: case MIPS_REG_T4_64: - val = regs.Read(12); - break; - case MIPS_REG_T5: case MIPS_REG_T5_64: - val = regs.Read(13); - break; - case MIPS_REG_T6: case MIPS_REG_T6_64: - val = regs.Read(14); - break; - case MIPS_REG_T7: case MIPS_REG_T7_64: - val = regs.Read(15); - break; - case MIPS_REG_S0: case MIPS_REG_S0_64: - val = regs.Read(16); - break; - case MIPS_REG_S1: case MIPS_REG_S1_64: - val = regs.Read(17); - break; - case MIPS_REG_S2: case MIPS_REG_S2_64: - val = regs.Read(18); - break; - case MIPS_REG_S3: case MIPS_REG_S3_64: - val = regs.Read(19); - break; - case MIPS_REG_S4: case MIPS_REG_S4_64: - val = regs.Read(20); - break; - case MIPS_REG_S5: case MIPS_REG_S5_64: - val = regs.Read(21); - break; - case MIPS_REG_S6: case MIPS_REG_S6_64: - val = regs.Read(22); - break; - case MIPS_REG_S7: case MIPS_REG_S7_64: - val = regs.Read(23); - break; - case MIPS_REG_T8: case MIPS_REG_T8_64: - val = regs.Read(24); - break; - case MIPS_REG_T9: case MIPS_REG_T9_64: - val = regs.Read(25); - break; - case MIPS_REG_K0: case MIPS_REG_K0_64: - val = regs.Read(26); - break; - case MIPS_REG_K1: case MIPS_REG_K1_64: - val = regs.Read(27); - break; - case MIPS_REG_GP: case MIPS_REG_GP_64: - val = regs.Read(28); - break; - case MIPS_REG_SP: case MIPS_REG_SP_64: - val = regs.Read(29); - break; - case MIPS_REG_RA: case MIPS_REG_RA_64: - val = regs.Read(31); - break; - default: - return {}; - } - - return val; -} - -template <> -std::string Disassembler::CapstoneToRegValue(mips_reg reg) const { - n64::Registers& regs = n64::Core::GetRegs(); - auto val = CapstoneToRegValue>(reg); - - return std::format("{}", val.has_value() ? std::format("{} (= 0x{:016X})", cs_reg_name(handle, reg), val.value()) : "! Unknown !"); -} - Disassembler::DisassemblyResult Disassembler::DisassembleSimple(const u32 address, const u32 instruction) const { cs_insn *insn; const auto bytes = Util::IntegralToBuffer(std::byteswap(instruction)); @@ -179,27 +66,8 @@ Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(const u32 addr } }; - auto formatComment = [&](const cs_mips_op &operand) -> CommentPart { - switch (operand.type) { - case MIPS_OP_IMM: - return {std::format("#{:X}, ", operand.is_unsigned ? operand.uimm : operand.imm)}; - case MIPS_OP_MEM: { - auto base = CapstoneToRegValue>(operand.mem.base); - return { - std::format("{}(0x{:X}), ", CapstoneToRegValue(operand.mem.base), operand.mem.disp), - base.has_value() ? base.value() + (s16)operand.mem.disp : 0xFFFF'FFFF'FFFF'FFFFull, - }; - } - case MIPS_OP_REG: - return {std::format("{}, ", CapstoneToRegValue(operand.reg))}; - default: - return {"! Unknown !"}; - } - }; - for (u8 i = 0; i < details->mips.op_count && i < 3; i++) { result.ops[i] = formatOperand(details->mips.operands[i]); - result.comment[i] = formatComment(details->mips.operands[i]); result.full += result.ops[i].str + "\t"; } diff --git a/src/backend/core/Disassembler.hpp b/src/backend/core/Disassembler.hpp index dfb45733..41f9a5d4 100644 --- a/src/backend/core/Disassembler.hpp +++ b/src/backend/core/Disassembler.hpp @@ -5,11 +5,6 @@ #include struct Disassembler { - struct CommentPart { - std::string str; - u64 resolvedAddr = 0xFFFF'FFFF'FFFF'FFFFull; - }; - struct DisassemblyResult { bool success = false; std::string full; @@ -20,23 +15,6 @@ struct Disassembler { std::string str; }; std::array ops{}; - std::array comment{}; - std::string GetFormattedComment() { - std::string res{}; - for(auto& [str, _] : comment) { - res += str; - } - return res; - } - - u64 GetResolvedAddressFromComment() { - auto it = std::ranges::find_if(comment, [](auto& part) { - return part.resolvedAddr != 0xFFFF'FFFF'FFFF'FFFFull; - }); - - if(it == comment.end()) return 0xFFFF'FFFF'FFFF'FFFFull; - return it->resolvedAddr; - } }; ~Disassembler() { cs_close(&handle); } @@ -50,8 +28,6 @@ struct Disassembler { [[nodiscard]] DisassemblyResult DisassembleDetailed(u32 address, u32 instruction) const; [[nodiscard]] DisassemblyResult DisassembleSimple(u32 address, u32 instruction) const; private: - template - T CapstoneToRegValue(mips_reg reg) const; 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) { @@ -59,7 +35,7 @@ private: } if (cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON) != CS_ERR_OK) { - warn("Could not enable disassembler's details!"); + Util::Error::GetInstance().Throw({Util::Error::Severity::WARN}, {Util::Error::Type::CAPSTONE_ERROR}, {}, {}, "Could not enable disassembler's details!"); details = false; } } diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index 9e5cb704..28e8488c 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -46,17 +46,21 @@ u32 JIT::FetchInstruction() { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); return 1;*/ - - panic("[JIT]: Unhandled exception ADL due to unaligned PC virtual value! (0x{:016X})", blockPC); + 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 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;*/ - panic( - "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address! (virtual: 0x{:016X})", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)), static_cast(blockPC)); + 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; } return Core::GetMem().Read(paddr); @@ -107,8 +111,11 @@ int JIT::Step() { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); return 1;*/ - panic("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address! (virtual: 0x{:016X})", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)), static_cast(blockPC)); + 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; } u32 upperIndex = paddr >> kUpperShift; @@ -149,17 +156,21 @@ int JIT::Step() { /*regs.cop0.HandleTLBException(blockPC); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); return 1;*/ - - panic("[JIT]: Unhandled exception ADL due to unaligned PC virtual value! (0x{:016X})", blockPC); + 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 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;*/ - panic( - "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address! (virtual: 0x{:016X})", - static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)), static_cast(blockPC)); + 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; } instruction = Core::GetMem().Read(paddr); @@ -185,8 +196,12 @@ int JIT::Step() { instructionsInBlock++; const u32 delay_instruction = FetchInstruction(); instrEndsBlock = InstrEndsBlock(delay_instruction); - if(instrEndsBlock) - panic("Branch in delay slot - YOU SHOULD KILL YOURSELF, NOW!!!"); + if(instrEndsBlock) { + 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; + } blockOldPC = blockPC; blockPC = blockNextPC; diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index 4ac2d9e6..91240f58 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -58,8 +58,9 @@ private: return code.qword[code.rbp + (reinterpret_cast(®s.gpr[index]) - reinterpret_cast(this))]; } - panic("[JIT]: Invalid register addressing"); - // never actually hit, but just to silence the warning "not all control paths return a value" + 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}; } @@ -188,17 +189,57 @@ private: void mthi(u32); void mtlo(u32); void nor(u32); - void sb(u32) { panic("[JIT] Implement sb!"); } - void sc(u32) { panic("[JIT] Implement sc!"); } - void scd(u32) { panic("[JIT] Implement scd!"); } - void sd(u32) { panic("[JIT] Implement sd!"); } - void sdc1(u32) { panic("[JIT] Implement sdc1!"); } - void sdl(u32) { panic("[JIT] Implement sdl!"); } - void sdr(u32) { panic("[JIT] Implement sdr!"); } - void sh(u32) { panic("[JIT] Implement sh!"); } + void sb(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sb'!"); + } + void sc(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sc'!"); + } + void scd(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'scd'!"); + } + void sd(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sd'!"); + } + void sdc1(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sdc1'!"); + } + void sdl(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sdl'!"); + } + void sdr(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sdr'!"); + } + void sh(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'sh'!"); + } void sw(u32); - void swl(u32) { panic("[JIT] Implement swl!"); } - void swr(u32) { panic("[JIT] Implement swr!"); } + void swl(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'swl'!"); + } + void swr(u32) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'swr'!"); + } void slti(u32); void sltiu(u32); void slt(u32); @@ -207,12 +248,20 @@ private: void sllv(u32); void sub(u32); void subu(u32); - void swc1(u32) { panic("[JIT] Implement swc1!"); } + void swc1(u32) { + 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(u32); void srav(u32); void srl(u32); void srlv(u32); - void trap(bool) { panic("[JIT] Implement trap!"); } + 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_(u32); void ori(u32); void xor_(u32); diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index a0c5e3c3..57928af7 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -18,7 +18,10 @@ void Mem::Reset() { std::error_code error; saveData.sync(error); if (error) { - panic("Could not sync save data"); + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, + {}, {}, "[Mem]: Could not sync save data!"); + return; } saveData.unmap(); } @@ -36,7 +39,10 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) { if (saveData.is_mapped()) { saveData.sync(error); if (error) { - panic("Could not sync {}", sramPath); + 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); + return; } saveData.unmap(); } @@ -48,12 +54,17 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) { } if (sramVec.size() != SRAM_SIZE) { - panic("Corrupt SRAM!"); + 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); + return; } saveData = mio::make_mmap_sink(sramPath, 0, mio::map_entire_file, error); if (error) { - panic("Could not mmap {}", sramPath); + 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); } } } @@ -150,7 +161,12 @@ u8 Mem::Read(const u32 paddr) { } if(Util::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - Util::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Read!"); + Util::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"); + return 0; + } if(Util::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START]; if(Util::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return si.pif.ram[paddr - PIF_RAM_REGION_START]; @@ -159,7 +175,9 @@ u8 Mem::Read(const u32 paddr) { Util::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || Util::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - panic("Unimplemented 8-bit read at address {:08X} (PC = {:016X})", paddr, (u64)regs.pc); + 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"); return 0; } @@ -184,7 +202,9 @@ u16 Mem::Read(const u32 paddr) { Util::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || Util::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - panic("Unimplemented 16-bit read at address {:08X} (PC = {:016X})", paddr, (u64)regs.pc); + 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"); return 0; } @@ -210,7 +230,9 @@ u32 Mem::Read(const u32 paddr) { Util::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || Util::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - panic("Unimplemented 32-bit read at address {:08X} (PC = {:016X})", paddr, (u64)regs.pc); + 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"); return 0; } @@ -236,7 +258,9 @@ u64 Mem::Read(const u32 paddr) { Util::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || Util::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; - panic("Unimplemented 64-bit read at address {:08X} (PC = {:016X})", paddr, (u64)regs.pc); + 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"); return 0; } diff --git a/src/backend/core/mmio/PIF/MupenMovie.cpp b/src/backend/core/mmio/PIF/MupenMovie.cpp index afe99b20..04cd8ea8 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.cpp +++ b/src/backend/core/mmio/PIF/MupenMovie.cpp @@ -29,12 +29,12 @@ union TASMovieControllerData { static_assert(sizeof(TASMovieControllerData) == 4); -bool MupenMovie::Load(const fs::path &path) { +void MupenMovie::Load(const fs::path &path) { filename = path.stem().string(); loadedTasMovie = Util::ReadFileBinary(path.string()); if (!IsLoaded()) { error("Error loading movie!"); - return false; + return; } std::memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), sizeof(TASMovieHeader)); @@ -42,18 +42,18 @@ bool MupenMovie::Load(const fs::path &path) { if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 || loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) { error("Failed to load movie: incorrect signature. Are you sure this is a valid movie?"); - return false; + return; } if (loadedTasMovieHeader.version != 3) { error("This movie is version {}: only version 3 is supported.", loadedTasMovieHeader.version); - return false; + return; } if (loadedTasMovieHeader.startType != 2) { error("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)", loadedTasMovieHeader.startType); - return false; + return; } info("Loaded movie '{}' ", loadedTasMovieHeader.movie_description); @@ -62,11 +62,10 @@ bool MupenMovie::Load(const fs::path &path) { if (loadedTasMovieHeader.numControllers != 1) { error("Currently, only movies with 1 controller connected are supported."); - return false; + return; } loadedTasMovieIndex = sizeof(TASMovieHeader) - 4; // skip header - return true; } MupenMovie::MupenMovie(const fs::path &path) { diff --git a/src/backend/core/mmio/PIF/MupenMovie.hpp b/src/backend/core/mmio/PIF/MupenMovie.hpp index f5fa86ba..efd3b4de 100644 --- a/src/backend/core/mmio/PIF/MupenMovie.hpp +++ b/src/backend/core/mmio/PIF/MupenMovie.hpp @@ -48,7 +48,7 @@ static_assert(sizeof(TASMovieHeader) == 1024); struct MupenMovie { MupenMovie() = default; explicit MupenMovie(const fs::path &); - bool Load(const fs::path &); + void Load(const fs::path &); void Reset(); n64::Controller NextInputs(); [[nodiscard]] bool IsLoaded() const { return !loadedTasMovie.empty(); } diff --git a/src/backend/core/mmio/VI.cpp b/src/backend/core/mmio/VI.cpp index d7e8e97a..f4d790f1 100644 --- a/src/backend/core/mmio/VI.cpp +++ b/src/backend/core/mmio/VI.cpp @@ -24,40 +24,41 @@ void VI::Reset() { u32 VI::Read(const u32 paddr) const { switch (paddr) { - case 0x04400000: - return status.raw; - case 0x04400004: - return origin; - case 0x04400008: - return width; - case 0x0440000C: - return intr; - case 0x04400010: - return current << 1; - case 0x04400014: - return burst.raw; - case 0x04400018: - return vsync; - case 0x0440001C: - return hsync; - case 0x04400020: - return hsyncLeap.raw; - case 0x04400024: - return hstart.raw; - case 0x04400028: - return vstart.raw; - case 0x0440002C: - return vburst; - case 0x04400030: - return xscale.raw; - case 0x04400034: - return yscale.raw; - case 0x04400038: - return 0; - case 0x0440003C: - return 0; - default: - panic("Unimplemented VI[{:08X}] read", paddr); + case 0x04400000: + return status.raw; + case 0x04400004: + return origin; + case 0x04400008: + return width; + case 0x0440000C: + return intr; + case 0x04400010: + return current << 1; + case 0x04400014: + return burst.raw; + case 0x04400018: + return vsync; + case 0x0440001C: + return hsync; + case 0x04400020: + return hsyncLeap.raw; + case 0x04400024: + return hstart.raw; + case 0x04400028: + return vstart.raw; + case 0x0440002C: + return vburst; + case 0x04400030: + 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; + } } } diff --git a/src/frontend/Debugger.cpp b/src/frontend/Debugger.cpp index 0f6f07a5..208f6e78 100644 --- a/src/frontend/Debugger.cpp +++ b/src/frontend/Debugger.cpp @@ -2,6 +2,17 @@ #include #include +char const* regNames[] = { + "zero", "at", "v0", "v1", + "a0", "a1", "a2", "a3", + "t0", "t1", "t2", "t3", + "t4", "t5", "t6", "t7", + "s0", "s1", "s2", "s3", + "s4", "s5", "s6", "s7", + "t8", "t9", "k0", "k1", + "gp", "sp", "s8", "ra", +}; + void BreakpointFunc(s64 addr, s64 startAddr, Disassembler::DisassemblyResult&) { n64::Core& core = n64::Core::GetInstance(); bool isBroken = core.breakpoints.contains(addr + 4); @@ -56,58 +67,80 @@ void InstructionFunc(s64, s64, Disassembler::DisassemblyResult& disasm) { } } -void CommentFunc(s64, s64, Disassembler::DisassemblyResult& disasm) { - n64::Core& core = n64::Core::GetInstance(); - - if(!disasm.success) { - ImGui::TextColored(ImColor(0xff71efe5), ""); - return; - } - - ImGui::TextColored(ImColor(0xff71efe5), "%s", std::format("{}", disasm.GetFormattedComment()).c_str()); - - u64 vaddr = disasm.GetResolvedAddressFromComment(); - - if(vaddr == 0xFFFF'FFFF'FFFF'FFFFull) +void Debugger::RegisterView() { + if(!ImGui::BeginTable("Registers", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody)) return; - if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip)) - return; - - if(!ImGui::BeginTooltip()) - return; + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); - ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str()); - if(!ImGui::BeginTable("##memoryContents", 16)) - return; - - for(u32 col = 0; col < 16; col++) - ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str()); - ImGui::TableHeadersRow(); - for(u32 row = 0; row < 16; row++) { - ImGui::TableNextRow(); - for(u32 col = 0; col < 16; col+=4) { - u32 paddr; - if(!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr+row*0x10+col, paddr)) - continue; - - u32 val = n64::Core::GetMem().Read(paddr); + auto renderMemoryTable = [&](u64 vaddr) { + if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip)) + return; - ImGui::TableSetColumnIndex(col+0); - ImGui::Text("%02X", (val >> 24) & 0xff); - ImGui::TableSetColumnIndex(col+1); - ImGui::Text("%02X", (val >> 16) & 0xff); - ImGui::TableSetColumnIndex(col+2); - ImGui::Text("%02X", (val >> 8) & 0xff); - ImGui::TableSetColumnIndex(col+3); - ImGui::Text("%02X", (val >> 0) & 0xff); - } - } + if(!ImGui::BeginTooltip()) + return; + ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str()); + if(!ImGui::BeginTable("##memoryContents", 16)) + return; + + for(u32 col = 0; col < 16; col++) + ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str()); + + ImGui::TableHeadersRow(); + + for(u32 row = 0; row < 16; row++) { + ImGui::TableNextRow(); + for(u32 col = 0; col < 16; col+=4) { + u32 paddr; + if(!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr+row*0x10+col, paddr)) + continue; + + u32 val = n64::Core::GetMem().Read(paddr); + + ImGui::TableSetColumnIndex(col+0); + ImGui::Text("%02X", (val >> 24) & 0xff); + ImGui::TableSetColumnIndex(col+1); + ImGui::Text("%02X", (val >> 16) & 0xff); + ImGui::TableSetColumnIndex(col+2); + ImGui::Text("%02X", (val >> 8) & 0xff); + ImGui::TableSetColumnIndex(col+3); + ImGui::Text("%02X", (val >> 0) & 0xff); + } + } + + ImGui::EndTable(); + ImGui::EndTooltip(); + }; + + n64::Registers& regs = n64::Core::GetRegs(); + + for(int i = 0; i < 32; i+=2) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", regNames[i]); + + ImGui::TableSetColumnIndex(1); + auto value = regs.Read(i); + ImGui::Text("%s", std::format("{:016X}", value).c_str()); + renderMemoryTable(value); + + ImGui::TableSetColumnIndex(2); + ImGui::Text("%s", regNames[i+1]); + + ImGui::TableSetColumnIndex(3); + value = regs.Read(i+1); + ImGui::Text("%s", std::format("{:016X}", value).c_str()); + renderMemoryTable(value); + } + ImGui::EndTable(); - ImGui::EndTooltip(); } bool Debugger::render() { @@ -119,7 +152,6 @@ bool Debugger::render() { static s64 startAddr = 0xFFFF'FFFF'8000'0000; int step = 4, stepFast = 256; - static bool followPC = true; if(!ImGui::Begin("Debugger", &enabled)) return false; @@ -144,7 +176,7 @@ bool Debugger::render() { core.ToggleBreakpoint(startAddr); } - if(!ImGui::BeginTable("Disassembly", columns.size(), ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody)) + if(!ImGui::BeginTable("Disassembly", columns.size(), ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody)) return false; for(int i = 0; i < columns.size(); i++) @@ -183,6 +215,8 @@ bool Debugger::render() { ImGui::EndTable(); + RegisterView(); + ImGui::End(); return true; diff --git a/src/frontend/Debugger.hpp b/src/frontend/Debugger.hpp index eb5c6705..fd7bf284 100644 --- a/src/frontend/Debugger.hpp +++ b/src/frontend/Debugger.hpp @@ -4,7 +4,6 @@ void BreakpointFunc(s64, s64, Disassembler::DisassemblyResult&); void AddressFunc(s64, s64, Disassembler::DisassemblyResult&); void InstructionFunc(s64, s64, Disassembler::DisassemblyResult&); -void CommentFunc(s64, s64, Disassembler::DisassemblyResult&); class Debugger final { bool enabled = false; @@ -15,14 +14,15 @@ class Debugger final { void (*func)(s64, s64, Disassembler::DisassemblyResult&) = nullptr; }; - std::array columns = { + std::array columns = { Column{"##BreakpointColumn", &BreakpointFunc}, Column{"Address", &AddressFunc}, Column{"Instruction", &InstructionFunc}, - Column{"Comment", &CommentFunc}, }; public: - void Open() { enabled = true; } + void RegisterView(); + bool followPC = true; + void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; } void Close() { enabled = false; } bool render(); }; \ No newline at end of file diff --git a/src/frontend/ImGuiImpl/GUI.hpp b/src/frontend/ImGuiImpl/GUI.hpp index 84e408c1..1fb0951e 100644 --- a/src/frontend/ImGuiImpl/GUI.hpp +++ b/src/frontend/ImGuiImpl/GUI.hpp @@ -22,11 +22,13 @@ namespace gui { inline std::shared_ptr g_Wsi; static void CheckVkResult(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; - error("[vulkan] VkResult = {}", (int) err); - if (err < 0) - abort(); + + if (err < VK_SUCCESS) + panic("[vulkan] VkResult = {}", (int) err); + + warn("[vulkan] VkResult = {}", (int) err); } inline void Initialize(const std::shared_ptr& wsi, SDL_Window* nativeWindow) { diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 5734de38..7c56581e 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -134,6 +134,24 @@ void KaizenGui::HandleInput(SDL_Event event) { } } +std::tuple, 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()) { + auto memoryAccessV = memoryAccess.value(); + ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", memoryAccessV.is_write ? "Writing" : "Reading", + (u8)memoryAccessV.size, memoryAccessV.address, + memoryAccessV.is_write ? std::format(" (value = 0x{:X})", memoryAccessV.written_val) : "") + .c_str()); + } + + return {lastPC, memoryAccess}; +} + void KaizenGui::RenderUI() { n64::Core& core = n64::Core::GetInstance(); gui::StartFrame(); @@ -189,6 +207,10 @@ void KaizenGui::RenderUI() { } ImGui::EndMainMenuBar(); } + + if(!Util::Error::GetInstance().IsHandled()) { + ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); + } if(settingsWindow.isOpen) { ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); @@ -216,6 +238,103 @@ void KaizenGui::RenderUI() { ImGui::EndPopup(); } + + 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) { + bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); + bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); + 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(); + + bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); + bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); + bool chooseAnother = ImGui::Button("Choose another ROM"); + 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(); + } + } break; + default: break; + } + + ImGui::EndPopup(); + } if(ImGui::BeginMainStatusBar()) { ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); @@ -292,7 +411,5 @@ void KaizenGui::run() { } void KaizenGui::LoadTAS(const std::string &path) const noexcept { - if (!n64::Core::GetInstance().LoadTAS(fs::path(path))) { - panic("Could not load TAS movie {}!", path); - } + n64::Core::GetInstance().LoadTAS(fs::path(path)); } \ No newline at end of file diff --git a/src/utils/ErrorData.hpp b/src/utils/ErrorData.hpp index 7af25a0a..465e7bd2 100644 --- a/src/utils/ErrorData.hpp +++ b/src/utils/ErrorData.hpp @@ -6,51 +6,103 @@ namespace Util { struct Error { - enum Severity { - NONE, - WARN, - ERROR, - FATAL, + struct Severity { + enum { + NONE, + WARN, + NON_FATAL, + UNRECOVERABLE, + } as_enum; + + const char* as_c_str() { + switch(as_enum) { + case NONE: return ""; + case WARN: return "Warning"; + case NON_FATAL: return "Error"; + case UNRECOVERABLE: return "Unrecoverable Error"; + } + } }; - union MemoryAccess { - struct { - unsigned : 3; - unsigned is_write : 1; - unsigned is_fatal : 1; - unsigned size : 3; - }; - - u8 raw; + struct MemoryAccess { + bool is_write; + enum Size { + BYTE = 8, SHORT = 16, WORD = 32, DWORD = 64 + } size; + u32 address; + u64 written_val; }; - enum Type { - 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_ERROR, - 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, + 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; + + + const char* as_c_str() { + 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 @@ -63,15 +115,7 @@ struct Error { this->lastPC = lastPC; this->memoryAccess = memoryAccess; this->type = type; - return std::format(fmt, std::forward(args)...); - } - - void SetHandled() { - severity = NONE; - } - - bool IsHandled() { - return severity == NONE; + err = std::format(fmt, std::forward(args)...); } static Error& GetInstance() { @@ -79,12 +123,25 @@ struct Error { return instance; } - static std::string Get() { - return GetInstance().err; + 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 = NONE; + Severity severity = {}; Type type = {}; std::optional lastPC = {}; std::optional memoryAccess = {}; diff --git a/src/utils/File.cpp b/src/utils/File.cpp new file mode 100644 index 00000000..552c6eae --- /dev/null +++ b/src/utils/File.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +namespace Util { +std::vector OpenROM(const std::string &filename, size_t &sizeAdjusted) { + auto buf = ReadFileBinary(filename); + sizeAdjusted = NextPow2(buf.size()); + return buf; +} + +std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted) { + const auto stream = ar_open_file(fs::path(path).string().c_str()); + + if (!stream) { + panic("Could not open archive! Are you sure it's an archive?"); + } + + ar_archive *archive = ar_open_zip_archive(stream, false); + + if (!archive) + archive = ar_open_rar_archive(stream); + if (!archive) + archive = ar_open_7z_archive(stream); + if (!archive) + archive = ar_open_tar_archive(stream); + + if (!archive) { + ar_close(stream); + panic("Could not open archive! Are you sure it's a supported archive? (7z, zip, rar and tar are supported)"); + } + + std::vector buf{}; + + std::vector rom_exts{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"}; + + while (ar_parse_entry(archive)) { + auto filename = ar_entry_get_name(archive); + auto extension = fs::path(filename).extension(); + + if (std::ranges::any_of(rom_exts, [&](const auto &x) { return extension == x; })) { + const auto size = ar_entry_get_size(archive); + sizeAdjusted = NextPow2(size); + buf.resize(sizeAdjusted); + ar_entry_uncompress(archive, buf.data(), size); + break; + } + + ar_close_archive(archive); + ar_close(stream); + panic("Could not find any rom image in the archive!"); + } + + ar_close_archive(archive); + ar_close(stream); + return buf; +} +} \ No newline at end of file diff --git a/src/utils/File.hpp b/src/utils/File.hpp index e730a51f..29160c93 100644 --- a/src/utils/File.hpp +++ b/src/utils/File.hpp @@ -2,7 +2,6 @@ #include #include #include -#include #include namespace fs = std::filesystem; @@ -42,56 +41,6 @@ FORCE_INLINE size_t NextPow2(size_t num) { return num + 1; } -FORCE_INLINE std::vector OpenROM(const std::string &filename, size_t &sizeAdjusted) { - auto buf = Util::ReadFileBinary(filename); - sizeAdjusted = Util::NextPow2(buf.size()); - return buf; -} - -FORCE_INLINE std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted) { - const auto stream = ar_open_file(fs::path(path).string().c_str()); - - if (!stream) { - panic("Could not open archive! Are you sure it's an archive?"); - } - - ar_archive *archive = ar_open_zip_archive(stream, false); - - if (!archive) - archive = ar_open_rar_archive(stream); - if (!archive) - archive = ar_open_7z_archive(stream); - if (!archive) - archive = ar_open_tar_archive(stream); - - if (!archive) { - ar_close(stream); - panic("Could not open archive! Are you sure it's a supported archive? (7z, zip, rar and tar are supported)"); - } - - std::vector buf{}; - - std::vector rom_exts{".n64", ".z64", ".v64", ".N64", ".Z64", ".V64"}; - - while (ar_parse_entry(archive)) { - auto filename = ar_entry_get_name(archive); - auto extension = fs::path(filename).extension(); - - if (std::ranges::any_of(rom_exts, [&](const auto &x) { return extension == x; })) { - const auto size = ar_entry_get_size(archive); - sizeAdjusted = Util::NextPow2(size); - buf.resize(sizeAdjusted); - ar_entry_uncompress(archive, buf.data(), size); - break; - } - - ar_close_archive(archive); - ar_close(stream); - panic("Could not find any rom image in the archive!"); - } - - ar_close_archive(archive); - ar_close(stream); - return buf; -} +std::vector OpenROM(const std::string &filename, size_t &sizeAdjusted); +std::vector OpenArchive(const std::string &path, size_t &sizeAdjusted); } // namespace Util