Rework logs a little bit to allow choosing the log level from settings

This commit is contained in:
2026-04-07 15:45:30 +02:00
parent fda755f7d8
commit 2db12b8083
48 changed files with 536 additions and 1182 deletions
+3 -3
View File
@@ -167,11 +167,11 @@ add_executable(kaizen
src/frontend/Settings/CPUSettings.cpp src/frontend/Settings/CPUSettings.cpp
src/frontend/Settings/AudioSettings.hpp src/frontend/Settings/AudioSettings.hpp
src/frontend/Settings/AudioSettings.cpp src/frontend/Settings/AudioSettings.cpp
src/frontend/Settings/MiscSettings.hpp
src/frontend/Settings/MiscSettings.cpp
src/frontend/NativeWindow.hpp src/frontend/NativeWindow.hpp
src/utils/Options.cpp src/utils/Options.cpp
src/utils/File.cpp src/utils/File.cpp)
src/frontend/Debugger.hpp
src/frontend/Debugger.cpp)
if (WIN32) if (WIN32)
+1 -15
View File
@@ -62,9 +62,7 @@ void Core::LoadROM(const std::string &rom_) {
romLoaded = true; romLoaded = true;
} }
u32 Core::StepCPU() { u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
return cpu->Step() + regs.PopStalledCycles();
}
void Core::StepRSP(const u32 cpuCycles) { void Core::StepRSP(const u32 cpuCycles) {
MMIO &mmio = mem->mmio; MMIO &mmio = mem->mmio;
@@ -105,23 +103,14 @@ void Core::Run(const float volumeL, const float volumeR) {
const u32 taken = StepCPU(); const u32 taken = StepCPU();
cycles += taken; cycles += taken;
if((broken = breakpoints.contains(regs.nextPC)))
break;
StepRSP(taken); StepRSP(taken);
frameCycles += taken; frameCycles += taken;
Scheduler::GetInstance().Tick(taken); Scheduler::GetInstance().Tick(taken);
} }
if(broken)
break;
cycles -= mmio.vi.cyclesPerHalfline; cycles -= mmio.vi.cyclesPerHalfline;
} }
if(broken)
break;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
mmio.mi.InterruptRaise(MI::Interrupt::VI); mmio.mi.InterruptRaise(MI::Interrupt::VI);
} }
@@ -129,8 +118,5 @@ void Core::Run(const float volumeL, const float volumeR) {
mmio.ai.Step(frameCycles, volumeL, volumeR); mmio.ai.Step(frameCycles, volumeL, volumeR);
Scheduler::GetInstance().Tick(frameCycles); Scheduler::GetInstance().Tick(frameCycles);
} }
if(broken)
pause = true;
} }
} // namespace n64 } // namespace n64
+1 -1
View File
@@ -1,6 +1,6 @@
#include <Netplay.hpp> #include <Netplay.hpp>
#include <PIF.hpp> #include <PIF.hpp>
#include <array> #include <array>
#include <log.hpp> #include <Log.hpp>
namespace Netplay {} namespace Netplay {}
+3 -3
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <ircolib/mem_access.hpp> #include <ircolib/mem_access.hpp>
#include <log.hpp> #include <Log.hpp>
namespace Util { namespace Util {
#define Z64 0x80371200 #define Z64 0x80371200
@@ -13,7 +13,7 @@ FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
if (endianness >> 24 != 0x80) { if (endianness >> 24 != 0x80) {
if ((endianness & 0xFF) != 0x80) { if ((endianness & 0xFF) != 0x80) {
if ((endianness >> 16 & 0xff) != 0x80) { if ((endianness >> 16 & 0xff) != 0x80) {
Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); panic("Unrecognized rom endianness");
return; return;
} else { } else {
altByteShift = 12; altByteShift = 12;
@@ -42,7 +42,7 @@ FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
ircolib::SwapBuffer<u32>(rom); ircolib::SwapBuffer<u32>(rom);
break; break;
default: 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!"); panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
} }
} }
} // namespace Util } // namespace Util
+2 -2
View File
@@ -40,10 +40,10 @@ void Scheduler::Tick(const u64 t) {
case NONE: case NONE:
break; break;
case IMPOSSIBLE: case IMPOSSIBLE:
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); panic("Scheduler reached end-of-life. How?!");
return; return;
default: default:
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(type)); panic("Unknown scheduler event type {}", static_cast<int>(type));
return; return;
} }
events.pop(); events.pop();
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <log.hpp> #include <Log.hpp>
#include <queue> #include <queue>
enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE }; enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
-1
View File
@@ -1,7 +1,6 @@
#pragma once #pragma once
#include <Mem.hpp> #include <Mem.hpp>
#include <Registers.hpp> #include <Registers.hpp>
#include <Disassembler.hpp>
namespace n64 { namespace n64 {
struct BaseCPU { struct BaseCPU {
-77
View File
@@ -1,77 +0,0 @@
#include <Disassembler.hpp>
#include <Core.hpp>
#include <optional>
Disassembler::DisassemblyResult Disassembler::DisassembleSimple(const u32 address, const u32 instruction) const {
cs_insn *insn;
const auto bytes = ircolib::IntegralToBuffer(std::byteswap(instruction));
const auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
if (count <= 0)
return {};
DisassemblyResult result{true, std::format("0x{:016X}:\t{}\t{}", insn[0].address, insn[0].mnemonic, insn[0].op_str)};
cs_free(insn, count);
return result;
}
[[nodiscard]] Disassembler::DisassemblyResult Disassembler::Disassemble(const u32 address) const {
u32 paddr;
if(!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::TLBAccessType::LOAD, address, paddr))
return DisassemblyResult{false, ""};
u32 instruction = n64::Core::GetMem().Read<u32>(paddr);
return details ? DisassembleDetailed(address, instruction) : DisassembleSimple(address, instruction);
}
Disassembler::DisassemblyResult Disassembler::DisassembleDetailed(const u32 address, const u32 instruction) const {
n64::Core& core = n64::Core::GetInstance();
cs_insn *insn;
const auto bytes = ircolib::IntegralToBuffer(std::byteswap(instruction));
const auto count = cs_disasm(handle, bytes.data(), bytes.size(), address, 0, &insn);
if (count <= 0)
return {};
DisassemblyResult result{true};
result.address = insn[0].address;
result.mnemonic = insn[0].mnemonic;
result.full += std::format("0x{:016X}", result.address) + ":\t";
result.full += result.mnemonic + "\t";
const cs_detail *details = insn[0].detail;
auto formatOperand = [&](const cs_mips_op &operand) {
switch (operand.type) {
case MIPS_OP_IMM:
return DisassemblyResult::Operand{
0xffcbf1ae,
std::format("#{:X}", operand.is_unsigned ? operand.uimm : operand.imm)
};
case MIPS_OP_MEM:
return DisassemblyResult::Operand{
0xffaef1c3,
std::format("{}(0x{:X})", cs_reg_name(handle, operand.mem.base), operand.mem.disp)
};
case MIPS_OP_REG:
return DisassemblyResult::Operand{
0xffaef1eb,
std::format("{}", cs_reg_name(handle, operand.reg))
};
default:
return DisassemblyResult::Operand { 0xff808080, "" };
}
};
for (u8 i = 0; i < details->mips.op_count && i < 3; i++) {
result.ops[i] = formatOperand(details->mips.operands[i]);
result.full += result.ops[i].str + "\t";
}
cs_free(insn, count);
return result;
}
-46
View File
@@ -1,46 +0,0 @@
#pragma once
#include <capstone/capstone.h>
#include <utils/log.hpp>
#include <ircolib/mem_access.hpp>
#include <array>
struct Disassembler {
struct DisassemblyResult {
bool success = false;
std::string full;
u64 address;
std::string mnemonic;
struct Operand {
u32 color;
std::string str;
};
std::array<Operand, 3> ops{};
};
~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<cs_mode>((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) {
Util::Error::GetInstance().Throw({Util::Error::Severity::WARN}, {Util::Error::Type::CAPSTONE_ERROR}, {}, {}, "Could not enable disassembler's details!");
details = false;
}
}
bool rsp = false;
bool details = true;
csh handle{};
};
+4 -14
View File
@@ -47,9 +47,7 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw( error("[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return std::nullopt; return std::nullopt;
} }
@@ -57,17 +55,13 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw( error("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return std::nullopt; return std::nullopt;
} }
const u32 instr = Core::GetMem().Read<u32>(paddr); const u32 instr = Core::GetMem().Read<u32>(paddr);
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
return instr; return instr;
} }
@@ -118,9 +112,7 @@ u32 JIT::Step() {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, error("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0; return 0;
} }
@@ -177,9 +169,7 @@ u32 JIT::Step() {
return 0; return 0;
if(InstrEndsBlock(delay_instruction.value())) { if(InstrEndsBlock(delay_instruction.value())) {
Util::Error::GetInstance().Throw( error("[JIT]: Unhandled case of branch from delay slot!");
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
return 0; return 0;
} }
+14 -61
View File
@@ -47,6 +47,7 @@ struct JIT final : BaseCPU {
} }
void InvalidateBlock(u32); void InvalidateBlock(u32);
private: private:
friend struct Cop1; friend struct Cop1;
friend struct Registers; friend struct Registers;
@@ -73,9 +74,7 @@ private:
return code.qword[code.rbp + GPR_OFFSET(index)]; return code.qword[code.rbp + GPR_OFFSET(index)];
} }
Util::Error::GetInstance().Throw( panic("[JIT]: Invalid register addressing mode {}!", sizeof(T));
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
return Xbyak::Address{0}; return Xbyak::Address{0};
} }
@@ -204,57 +203,17 @@ private:
void mthi(Instruction); void mthi(Instruction);
void mtlo(Instruction); void mtlo(Instruction);
void nor(Instruction); void nor(Instruction);
void sb(const Instruction) { void sb(const Instruction) { panic("[JIT]: Unhandled 'sb' @ blockPC:{:016X}!", blockPC); }
Util::Error::GetInstance().Throw( void sc(const Instruction) { panic("[JIT]: Unhandled 'sc' @ blockPC:{:016X}!", blockPC); }
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void scd(const Instruction) { panic("[JIT]: Unhandled 'scd' @ blockPC:{:016X}!", blockPC); }
blockPC, {}, "[JIT]: Unhandled 'sb'!"); void sd(const Instruction) { panic("[JIT]: Unhandled 'sd' @ blockPC:{:016X}!", blockPC); }
} void sdc1(const Instruction) { panic("[JIT]: Unhandled 'sdc1' @ blockPC:{:016X}!", blockPC); }
void sc(const Instruction) { void sdl(const Instruction) { panic("[JIT]: Unhandled 'sdl' @ blockPC:{:016X}!", blockPC); }
Util::Error::GetInstance().Throw( void sdr(const Instruction) { panic("[JIT]: Unhandled 'sdr' @ blockPC:{:016X}!", blockPC); }
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void sh(const Instruction) { panic("[JIT]: Unhandled 'sh' @ blockPC:{:016X}!", blockPC); }
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 sw(Instruction);
void swl(const Instruction) { void swl(const Instruction) { panic("[JIT]: Unhandled 'swl' @ blockPC:{:016X}!", blockPC); }
Util::Error::GetInstance().Throw( void swr(const Instruction) { panic("[JIT]: Unhandled 'swr' @ blockPC:{:016X}!", blockPC); }
{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 slti(Instruction);
void sltiu(Instruction); void sltiu(Instruction);
void slt(Instruction); void slt(Instruction);
@@ -264,19 +223,13 @@ private:
void sub(Instruction); void sub(Instruction);
void subu(Instruction); void subu(Instruction);
void swc1(const Instruction) { void swc1(const Instruction) {
Util::Error::GetInstance().Throw( panic("[JIT]: Unhandled 'swc1' @ blockPC:{:016X}!", blockPC);
{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 sra(Instruction);
void srav(Instruction); void srav(Instruction);
void srl(Instruction); void srl(Instruction);
void srlv(Instruction); void srlv(Instruction);
void trap(bool) { void trap(bool) { panic("[JIT]: Unhandled 'trap' @ blockPC:{:016X}!", blockPC); }
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 or_(Instruction);
void ori(Instruction); void ori(Instruction);
void xor_(Instruction); void xor_(Instruction);
+95 -64
View File
@@ -19,10 +19,7 @@ void Mem::Reset() {
std::error_code error; std::error_code error;
saveData.sync(error); saveData.sync(error);
if (error) { if (error) {
Util::Error::GetInstance().Throw( warn("[Mem]: Could not sync save data!");
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
{}, {}, "[Mem]: Could not sync save data!");
return;
} }
saveData.unmap(); saveData.unmap();
} }
@@ -40,10 +37,7 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
if (saveData.is_mapped()) { if (saveData.is_mapped()) {
saveData.sync(error); saveData.sync(error);
if (error) { if (error) {
Util::Error::GetInstance().Throw( warn(R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
{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(); saveData.unmap();
} }
@@ -55,17 +49,13 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
} }
if (sramVec.size() != SRAM_SIZE) { if (sramVec.size() != SRAM_SIZE) {
Util::Error::GetInstance().Throw( panic("[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
{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; return;
} }
saveData = mio::make_mmap_sink(sramPath, error); saveData = mio::make_mmap_sink(sramPath, error);
if (error) { if (error) {
Util::Error::GetInstance().Throw( warn(R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MMAP_MAKE_SINK_ERROR},
{}, {}, R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
} }
} }
} }
@@ -148,13 +138,15 @@ u8 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si; const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr); if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u8>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return src[BYTE_ADDRESS(paddr & 0xfff)]; return src[BYTE_ADDRESS(paddr & 0xfff)];
} }
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u8, false>(paddr); if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u8, false>(paddr);
if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) { if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
const u32 w = mmio.ai.Read(paddr & ~3); const u32 w = mmio.ai.Read(paddr & ~3);
const int offs = 3 - (paddr & 3); const int offs = 3 - (paddr & 3);
@@ -163,22 +155,21 @@ u8 Mem::Read(const u32 paddr) {
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
Util::Error::GetInstance().Throw( warn("8-bit read access from MMIO @ {:08X}", paddr);
{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; return 0;
} }
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START]; if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return si.pif.ram[paddr - PIF_RAM_REGION_START]; return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return si.pif.ram[paddr - PIF_RAM_REGION_START];
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw( warn("8-bit read access in unhandled region @ {:08X}", paddr);
{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; return 0;
} }
@@ -187,25 +178,31 @@ u16 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si; const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr); if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u16>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff)); return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
} }
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u16, false>(paddr); if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u16, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START); return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START)); if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) {
warn("16-bit read access in unused region @ {:08X}", paddr);
return 0;
}
Util::Error::GetInstance().Throw( warn("16-bit read access in unhandled region @ {:08X}", paddr);
{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; return 0;
} }
@@ -214,26 +211,32 @@ u32 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si; const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr); if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u32>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u32>(src, paddr & 0xfff); return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
} }
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u32, false>(paddr); if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u32, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START); if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START)); return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) {
warn("32-bit read access in unused region @ {:08X}", paddr);
return 0;
}
Util::Error::GetInstance().Throw( warn("32-bit read access in unhandled region @ {:08X}", paddr);
{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; return 0;
} }
@@ -242,26 +245,32 @@ u64 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si; const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr); if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
return mmio.rdp.ReadRDRAM<u64>(paddr);
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
return ircolib::ReadAccess<u64>(src, paddr & 0xfff); return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
} }
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u64, false>(paddr); if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
return mmio.pi.BusRead<u64, false>(paddr);
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
return mmio.Read(paddr);
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START); if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START)); return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) {
warn("64-bit read access in unused region @ {:08X}", paddr);
return 0;
}
Util::Error::GetInstance().Throw( warn("64-bit read access in unhandled region @ {:08X}", paddr);
{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; return 0;
} }
@@ -270,7 +279,10 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si; SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; } if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u8>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
val = val << (8 * (3 - (paddr & 3))); val = val << (8 * (3 - (paddr & 3)));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
@@ -286,7 +298,8 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
} }
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u8>!"); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u8>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (8 * (3 - (paddr & 3))); val = val << (8 * (3 - (paddr & 3)));
@@ -300,7 +313,8 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc); panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
} }
@@ -324,7 +338,10 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si; SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; } if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u16>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
val = val << (16 * !(paddr & 2)); val = val << (16 * !(paddr & 2));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
@@ -340,7 +357,8 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
} }
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u16>!"); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u16>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (16 * !(paddr & 2)); val = val << (16 * !(paddr & 2));
@@ -354,7 +372,8 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc); panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
} }
@@ -378,7 +397,10 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si; SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; } if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u32>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val); ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
@@ -392,7 +414,10 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
} }
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { mmio.Write(paddr, val); return; } ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
mmio.Write(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -404,7 +429,8 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc); panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
} }
@@ -437,7 +463,10 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
n64::Registers &regs = n64::Core::GetRegs(); n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si; SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; } if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u64>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
val >>= 32; val >>= 32;
@@ -452,7 +481,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
} }
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u64>!"); ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u64>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -464,7 +494,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return;
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc); panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
} }
+2 -1
View File
@@ -4,7 +4,7 @@
#include <backend/MemoryRegions.hpp> #include <backend/MemoryRegions.hpp>
#include <backend/core/MMIO.hpp> #include <backend/core/MMIO.hpp>
#include <common.hpp> #include <common.hpp>
#include <log.hpp> #include <Log.hpp>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <ranges> #include <ranges>
@@ -124,6 +124,7 @@ struct Mem {
ROM rom; ROM rom;
SaveType saveType = SAVE_NONE; SaveType saveType = SAVE_NONE;
Flash flash; Flash flash;
private: private:
friend struct SI; friend struct SI;
friend struct PI; friend struct PI;
+1 -1
View File
@@ -1,4 +1,4 @@
#include <log.hpp> #include <Log.hpp>
#include <parallel-rdp/ParallelRDPWrapper.hpp> #include <parallel-rdp/ParallelRDPWrapper.hpp>
#include <Core.hpp> #include <Core.hpp>
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
RSP::RSP() { Reset(); } RSP::RSP() { Reset(); }
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
#include <ranges> #include <ranges>
namespace n64 { namespace n64 {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
#include <Instruction.hpp> #include <Instruction.hpp>
namespace n64 { namespace n64 {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
AI::AI() { Reset(); } AI::AI() { Reset(); }
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Audio.hpp> #include <Audio.hpp>
#include <log.hpp> #include <Log.hpp>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
namespace n64 { namespace n64 {
+1 -1
View File
@@ -1,6 +1,6 @@
#include <core/mmio/MI.hpp> #include <core/mmio/MI.hpp>
#include <core/registers/Registers.hpp> #include <core/registers/Registers.hpp>
#include <log.hpp> #include <Log.hpp>
#define MI_VERSION_REG 0x02020102 #define MI_VERSION_REG 0x02020102
+1 -1
View File
@@ -2,7 +2,7 @@
#include <Scheduler.hpp> #include <Scheduler.hpp>
#include <cmath> #include <cmath>
#include <core/mmio/PI.hpp> #include <core/mmio/PI.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
PI::PI() { Reset(); } PI::PI() { Reset(); }
+1 -1
View File
@@ -2,7 +2,7 @@
#include <cassert> #include <cassert>
#include <cic_nus_6105/n64_cic_nus_6105.hpp> #include <cic_nus_6105/n64_cic_nus_6105.hpp>
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
#include <Options.hpp> #include <Options.hpp>
#define MEMPAK_SIZE 32768 #define MEMPAK_SIZE 32768
+1 -1
View File
@@ -1,7 +1,7 @@
#include <Netplay.hpp> #include <Netplay.hpp>
#include <PIF.hpp> #include <PIF.hpp>
#include <PIF/MupenMovie.hpp> #include <PIF/MupenMovie.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
void PIF::InitDevices(SaveType saveType) { void PIF::InitDevices(SaveType saveType) {
+18 -16
View File
@@ -80,22 +80,24 @@ void MupenMovie::Reset() {
} }
FORCE_INLINE void LogController(const n64::Controller &controller) { FORCE_INLINE void LogController(const n64::Controller &controller) {
debug("c_right: {}", controller.cRight); trace("-- TAS CONTROLLER DATA --");
debug("c_left: {}", controller.cLeft); trace("c_right: {}", controller.cRight);
debug("c_down: {}", controller.cDown); trace("c_left: {}", controller.cLeft);
debug("c_up: {}", controller.cUp); trace("c_down: {}", controller.cDown);
debug("r: {}", controller.r); trace("c_up: {}", controller.cUp);
debug("l: {}", controller.l); trace("r: {}", controller.r);
debug("dp_right: {}", controller.dpRight); trace("l: {}", controller.l);
debug("dp_left: {}", controller.dpLeft); trace("dp_right: {}", controller.dpRight);
debug("dp_down: {}", controller.dpDown); trace("dp_left: {}", controller.dpLeft);
debug("dp_up: {}", controller.dpUp); trace("dp_down: {}", controller.dpDown);
debug("z: {}", controller.z); trace("dp_up: {}", controller.dpUp);
debug("b: {}", controller.b); trace("z: {}", controller.z);
debug("a: {}", controller.a); trace("b: {}", controller.b);
debug("start: {}", controller.start); trace("a: {}", controller.a);
debug("joy_x: {}", controller.joyX); trace("start: {}", controller.start);
debug("joy_y: {}", controller.joyY); trace("joy_x: {}", controller.joyX);
trace("joy_y: {}", controller.joyY);
trace("-- TAS CONTROLLER DATA --");
} }
n64::Controller MupenMovie::NextInputs() { n64::Controller MupenMovie::NextInputs() {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <core/mmio/RI.hpp> #include <core/mmio/RI.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
RI::RI() { Reset(); } RI::RI() { Reset(); }
+6 -6
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
VI::VI() { Reset(); } VI::VI() { Reset(); }
@@ -54,9 +54,7 @@ u32 VI::Read(const u32 paddr) const {
return yscale.raw; return yscale.raw;
default: { default: {
n64::Registers& regs = n64::Core::GetRegs(); n64::Registers& regs = n64::Core::GetRegs();
Util::Error::GetInstance().Throw( error("32-bit read access on unhandled VI register @ {:08X}, pc: {:016X}", paddr, (u64)regs.oldPC);
{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 0;
} }
} }
@@ -120,8 +118,10 @@ void VI::Write(const u32 paddr, const u32 val) {
break; break;
case 0x0440003C: case 0x0440003C:
break; break;
default: default: {
panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val); n64::Registers& regs = n64::Core::GetRegs();
error("32-bit write access on unhandled VI register @ {:08X}, pc: {:016X}, val: {:08X}", paddr, (u64)regs.oldPC, val);
}
} }
} }
} // namespace n64 } // namespace n64
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
Cop0::Cop0() { Reset(); } Cop0::Cop0() { Reset(); }
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <common.hpp> #include <common.hpp>
#include <log.hpp> #include <Log.hpp>
#include <unordered_map> #include <unordered_map>
#include <Instruction.hpp> #include <Instruction.hpp>
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
Cop1::Cop1() { Reset(); } Cop1::Cop1() { Reset(); }
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
void RSP::special(const Instruction instr) { void RSP::special(const Instruction instr) {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <Core.hpp> #include <Core.hpp>
#include <RCP.hpp> #include <RCP.hpp>
#include <RSQ.hpp> #include <RSQ.hpp>
#include <log.hpp> #include <Log.hpp>
namespace n64 { namespace n64 {
FORCE_INLINE bool AcquireSemaphore(RSP &rsp) { FORCE_INLINE bool AcquireSemaphore(RSP &rsp) {
-1
View File
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <types.hpp> #include <types.hpp>
#include <ErrorData.hpp>
#define FORCE_INLINE inline __attribute__((always_inline)) #define FORCE_INLINE inline __attribute__((always_inline))
-249
View File
@@ -1,249 +0,0 @@
#include <Debugger.hpp>
#include <imgui.h>
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, Disassembler::DisassemblyResult&) {
n64::Core& core = n64::Core::GetInstance();
bool isBroken = core.breakpoints.contains(addr);
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
if(ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
core.ToggleBreakpoint(addr);
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
if(!disasm.success) {
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
return;
}
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
}
void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
if(!disasm.success) {
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
return;
}
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
ImGui::SameLine(0, 0);
for(int i = 0; i < 3; i++) {
if(disasm.ops[i].str.empty())
continue;
if(i >= 2) {
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
ImGui::SameLine(0, 0);
continue;
}
std::string op_str = disasm.ops[i].str;
if(!disasm.ops[i+1].str.empty())
op_str += ", ";
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
ImGui::SameLine(0, 0);
}
}
void Debugger::RegisterView() {
if(!ImGui::BeginTabItem("Registers"))
return;
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
return;
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
ImGui::TableHeadersRow();
auto renderMemoryTable = [&](u64 vaddr) {
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
return;
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;
const u32 val = n64::Core::GetMem().Read<u32>(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<u64>(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<u64>(i+1);
ImGui::Text("%s", std::format("{:016X}", value).c_str());
renderMemoryTable(value);
}
ImGui::EndTable();
ImGui::EndTabItem();
}
bool Debugger::render() {
n64::Core &core = n64::Core::GetInstance();
const n64::Registers& regs = n64::Core::GetRegs();
if(!enabled)
return false;
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
constexpr int step = 4;
constexpr int stepFast = 256;
if(!ImGui::Begin("Debugger", &enabled)) {
ImGui::End();
return false;
}
ImGui::BeginDisabled(followPC);
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::EndDisabled();
ImGui::Text("Follow program counter:");
ImGui::SameLine(0,0);
ImGui::Checkbox("##followPC", &followPC);
ImGui::SameLine(0,0);
ImGui::Text("Add a breakpoint");
ImGui::SameLine(0,0);
if(followPC)
startAddr = regs.pc - 256; // TODO: arbitrary???
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
core.ToggleBreakpoint(startAddr);
}
if(!ImGui::BeginTabBar("##debuggerTabs")) {
ImGui::EndTabBar();
ImGui::End();
return false;
}
RegisterView();
if(!ImGui::BeginTabItem("MIPS R4300i code view")) {
ImGui::EndTabBar();
ImGui::End();
return false;
}
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
ImGui::EndTabBar();
ImGui::End();
return false;
}
for(auto &[name, _] : columns)
ImGui::TableSetupColumn(name);
ImGui::TableHeadersRow();
for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
auto disasm = Disassembler::GetInstance().Disassemble(addr);
const auto addrIsCurrent = addr == regs.nextPC;
const auto addrIsBreakpoint = core.breakpoints.contains(addr);
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
if(addrIsCurrent) {
colorChoice = 0x80e27fbc;
colorChoiceAlt = 0x80e27fbc;
}
if(addrIsBreakpoint) {
colorChoice = 0x800000ff;
colorChoiceAlt = 0x800000ff;
}
ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value);
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
ImGui::TableNextRow();
for(int i = 0; auto &[_, func] : columns) {
ImGui::TableSetColumnIndex(i++);
func(addr, disasm);
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
ImGui::EndTable();
ImGui::EndTabItem();
ImGui::EndTabBar();
ImGui::End();
return true;
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
#include <backend/Core.hpp>
void BreakpointFunc(s64, Disassembler::DisassemblyResult&);
void AddressFunc(s64, Disassembler::DisassemblyResult&);
void InstructionFunc(s64, Disassembler::DisassemblyResult&);
class Debugger final {
bool enabled = false;
static constexpr auto MAX_LINES_OF_DISASM = 150;
struct Column {
const char* name = nullptr;
void (*func)(s64, Disassembler::DisassemblyResult&) = nullptr;
};
std::array<Column, 3> columns = {
Column{"##BreakpointColumn", &BreakpointFunc},
Column{"Address", &AddressFunc},
Column{"Instruction", &InstructionFunc},
};
public:
static void RegisterView();
bool followPC = true;
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; }
void Close() { enabled = false; }
bool render();
};
+1 -1
View File
@@ -3,7 +3,7 @@
#include <imgui.h> #include <imgui.h>
#include <imgui_impl_sdl3.h> #include <imgui_impl_sdl3.h>
#include <imgui_impl_vulkan.h> #include <imgui_impl_vulkan.h>
#include <utils/log.hpp> #include <utils/Log.hpp>
#include <memory> #include <memory>
namespace gui { namespace gui {
+28 -140
View File
@@ -5,7 +5,9 @@
#include <ImGuiImpl/StatusBar.hpp> #include <ImGuiImpl/StatusBar.hpp>
#include <resources/gamecontrollerdb.h> #include <resources/gamecontrollerdb.h>
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { KaizenGui::KaizenGui() noexcept :
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
emuThread(fpsCounter, settingsWindow) {
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_InitSubSystem(SDL_INIT_GAMEPAD);
@@ -33,7 +35,8 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
if (gamepad) if (gamepad)
SDL_CloseGamepad(gamepad); SDL_CloseGamepad(gamepad);
break; break;
default: break; default:
break;
} }
} }
@@ -45,7 +48,8 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
if (!gamepad) if (!gamepad)
break; break;
{ {
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); pif.UpdateButton(0, n64::Controller::Key::Z,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
@@ -134,37 +138,24 @@ void KaizenGui::HandleInput(const SDL_Event &event) {
float x = 0, y = 0; float x = 0, y = 0;
if (keys[SDL_SCANCODE_UP]) y = 86; if (keys[SDL_SCANCODE_UP])
if (keys[SDL_SCANCODE_DOWN]) y = -86; y = 86;
if (keys[SDL_SCANCODE_LEFT]) x = -86; if (keys[SDL_SCANCODE_DOWN])
if (keys[SDL_SCANCODE_RIGHT]) x = 86; y = -86;
if (keys[SDL_SCANCODE_LEFT])
x = -86;
if (keys[SDL_SCANCODE_RIGHT])
x = 86;
pif.UpdateAxis(0, n64::Controller::Axis::X, x); pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y); pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
} }
break; break;
default: break; default:
break;
} }
} }
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> 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<u8>(size), address,
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
.c_str());
}
return {lastPC, memoryAccess};
}
void KaizenGui::RenderUI() { void KaizenGui::RenderUI() {
n64::Core &core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
gui::StartFrame(); gui::StartFrame();
@@ -200,10 +191,6 @@ void KaizenGui::RenderUI() {
core.parallel.SetFramerateUnlocked(unlockFramerate); core.parallel.SetFramerateUnlocked(unlockFramerate);
} }
if(ImGui::MenuItem("Open Debugger")) {
debugger.Open();
}
ImGui::EndDisabled(); ImGui::EndDisabled();
if (ImGui::MenuItem("Options")) { if (ImGui::MenuItem("Options")) {
@@ -221,10 +208,6 @@ void KaizenGui::RenderUI() {
ImGui::EndMainMenuBar(); ImGui::EndMainMenuBar();
} }
if(!Util::Error::IsHandled()) {
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
}
if (settingsWindow.isOpen) { if (settingsWindow.isOpen) {
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
} }
@@ -234,7 +217,6 @@ void KaizenGui::RenderUI() {
} }
settingsWindow.render(); settingsWindow.render();
debugger.render();
const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
@@ -255,111 +237,14 @@ void KaizenGui::RenderUI() {
ImGui::EndPopup(); 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) {
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()) { if (ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar(); ImGui::EndMainStatusBar();
} }
if (shouldDisplaySpinner) { if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@@ -386,8 +271,11 @@ void KaizenGui::RenderUI() {
if (fileDialogOpen) { if (fileDialogOpen) {
fileDialogOpen = false; fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { {"Nintendo 64 executable", "n64;z64;v64"},
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata); auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) { if (!filelist) {
@@ -405,7 +293,8 @@ void KaizenGui::RenderUI() {
std::thread fileWorker(&KaizenGui::FileWorker, kaizen); std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach(); fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false); },
this, window.getHandle(), filters, 3, nullptr, false);
} }
if (minimized) if (minimized)
@@ -455,6 +344,5 @@ void KaizenGui::run() {
} }
} }
void KaizenGui::LoadTAS(const std::string &path) noexcept { void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
n64::Core::GetInstance().LoadTAS(fs::path(path));
}
+2 -2
View File
@@ -1,12 +1,12 @@
#pragma once #pragma once
#include <RenderWidget.hpp> #include <RenderWidget.hpp>
#include <NativeWindow.hpp> #include <NativeWindow.hpp>
#include <Debugger.hpp>
#include <EmuThread.hpp> #include <EmuThread.hpp>
#include <SDL3/SDL_gamepad.h> #include <SDL3/SDL_gamepad.h>
class KaizenGui final { class KaizenGui final {
gui::NativeWindow window; gui::NativeWindow window;
public: public:
explicit KaizenGui() noexcept; explicit KaizenGui() noexcept;
~KaizenGui(); ~KaizenGui();
@@ -19,13 +19,13 @@ public:
SettingsWindow settingsWindow; SettingsWindow settingsWindow;
RenderWidget vulkanWidget; RenderWidget vulkanWidget;
EmuThread emuThread; EmuThread emuThread;
Debugger debugger;
SDL_Gamepad *gamepad = nullptr; SDL_Gamepad *gamepad = nullptr;
void run(); void run();
static void LoadTAS(const std::string &path) noexcept; static void LoadTAS(const std::string &path) noexcept;
void LoadROM(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept;
private: private:
int width{}, height{}; int width{}, height{};
bool aboutOpen = false; bool aboutOpen = false;
+1 -1
View File
@@ -3,7 +3,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <volk.h> #include <volk.h>
#include <utils/log.hpp> #include <utils/Log.hpp>
namespace gui { namespace gui {
struct NativeWindow { struct NativeWindow {
+1 -1
View File
@@ -1,6 +1,6 @@
#include <CPUSettings.hpp> #include <CPUSettings.hpp>
#include <Options.hpp> #include <Options.hpp>
#include <log.hpp> #include <Log.hpp>
#include <imgui.h> #include <imgui.h>
CPUSettings::CPUSettings() { CPUSettings::CPUSettings() {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <GeneralSettings.hpp> #include <GeneralSettings.hpp>
#include <Options.hpp> #include <Options.hpp>
#include <imgui.h> #include <imgui.h>
#include <log.hpp> #include <Log.hpp>
GeneralSettings::GeneralSettings(gui::NativeWindow& window) : window(window) { GeneralSettings::GeneralSettings(gui::NativeWindow& window) : window(window) {
savesPath = Options::GetInstance().GetValue<std::string>("general", "savePath"); savesPath = Options::GetInstance().GetValue<std::string>("general", "savePath");
+34
View File
@@ -0,0 +1,34 @@
#include <MiscSettings.hpp>
#include <Options.hpp>
#include <imgui.h>
#include <Log.hpp>
MiscSettings::MiscSettings() {
currLogLevel = static_cast<Util::LogLevel>(Options::GetInstance().GetValue<int>("misc", "logLevel"));
}
void MiscSettings::render() {
const char* items[] = {
"Trace", "Debug", "Warn", "Info", "Error", "Always", "Panic"
};
const char* combo_preview_value = items[selectedLogLevel];
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
const bool is_selected = (selectedLogLevel == n);
if (ImGui::Selectable(items[n], is_selected)) {
selectedLogLevel = n;
modified = true;
}
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
if(modified) {
Options::GetInstance().SetValue<int>("misc", "logLevel", static_cast<int>(selectedLogLevel));
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <SettingsTab.hpp>
#include <Log.hpp>
struct MiscSettings final : SettingsTab {
void render() override;
explicit MiscSettings();
private:
Util::LogLevel currLogLevel;
int selectedLogLevel = 4;
};
+4 -1
View File
@@ -2,6 +2,7 @@
#include <AudioSettings.hpp> #include <AudioSettings.hpp>
#include <CPUSettings.hpp> #include <CPUSettings.hpp>
#include <GeneralSettings.hpp> #include <GeneralSettings.hpp>
#include <MiscSettings.hpp>
#include <NativeWindow.hpp> #include <NativeWindow.hpp>
#include <vector> #include <vector>
@@ -10,17 +11,19 @@ class SettingsWindow final {
GeneralSettings generalSettings; GeneralSettings generalSettings;
CPUSettings cpuSettings; CPUSettings cpuSettings;
AudioSettings audioSettings; AudioSettings audioSettings;
MiscSettings miscSettings;
bool applyEnabled = false; bool applyEnabled = false;
std::vector<std::pair<std::string, SettingsTab*>> tabs = { std::vector<std::pair<std::string, SettingsTab*>> tabs = {
{ "General", &generalSettings }, { "General", &generalSettings },
{ "CPU", &cpuSettings }, { "CPU", &cpuSettings },
{ "Audio", &audioSettings }, { "Audio", &audioSettings },
{ "Misc", &miscSettings },
}; };
public: public:
bool isOpen = false; bool isOpen = false;
bool render(); bool render();
explicit SettingsWindow(gui::NativeWindow& window) : window(window), generalSettings(window) {} explicit SettingsWindow(gui::NativeWindow& window) : window(window), generalSettings(window), miscSettings() {}
[[nodiscard]] float getVolumeL() const { return audioSettings.volumeL / 100.f; } [[nodiscard]] float getVolumeL() const { return audioSettings.volumeL / 100.f; }
[[nodiscard]] float getVolumeR() const { return audioSettings.volumeR / 100.f; } [[nodiscard]] float getVolumeR() const { return audioSettings.volumeR / 100.f; }
}; };
-149
View File
@@ -1,149 +0,0 @@
#pragma once
#include <string>
#include <types.hpp>
#include <format>
#include <optional>
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 <class... Args>
void Throw (const Severity severity, const Type type, const std::optional<u64> lastPC,
const std::optional<MemoryAccess> memoryAccess,
const std::format_string<Args...> fmt, Args... args) {
this->severity = severity;
this->lastPC = lastPC;
this->memoryAccess = memoryAccess;
this->type = type;
err = std::format(fmt, std::forward<Args>(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<s64>& GetLastPC() { return GetInstance().lastPC; }
static std::optional<MemoryAccess>& 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<s64> lastPC = {};
std::optional<MemoryAccess> memoryAccess = {};
};
}
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <fstream> #include <fstream>
#include <log.hpp> #include <Log.hpp>
#include <vector> #include <vector>
#include <filesystem> #include <filesystem>
#include <ircolib/file.hpp> #include <ircolib/file.hpp>
+35
View File
@@ -1,4 +1,5 @@
#include <Options.hpp> #include <Options.hpp>
#include <Log.hpp>
template <> template <>
void Options::SetValue<std::string>(const std::string &key, const std::string &field, const std::string &value) { void Options::SetValue<std::string>(const std::string &key, const std::string &field, const std::string &value) {
@@ -10,6 +11,11 @@ void Options::SetValue<float>(const std::string &key, const std::string &field,
structure[key][field] = std::format("{:.2f}", value); structure[key][field] = std::format("{:.2f}", value);
} }
template <>
void Options::SetValue<int>(const std::string &key, const std::string &field, const int &value) {
structure[key][field] = std::format("{}", value);
}
template <> template <>
void Options::SetValue<bool>(const std::string &key, const std::string &field, const bool &value) { void Options::SetValue<bool>(const std::string &key, const std::string &field, const bool &value) {
structure[key][field] = value ? "true" : "false"; structure[key][field] = value ? "true" : "false";
@@ -25,7 +31,36 @@ float Options::GetValue<float>(const std::string &key, const std::string &field)
return std::stof(structure[key][field]); return std::stof(structure[key][field]);
} }
template<>
int Options::GetValue<int>(const std::string &key, const std::string &field) {
return std::stoi(structure[key][field]);
}
template<> template<>
bool Options::GetValue<bool>(const std::string &key, const std::string &field) { bool Options::GetValue<bool>(const std::string &key, const std::string &field) {
return structure[key][field] == "true" ? true : false; return structure[key][field] == "true" ? true : false;
} }
Options::Options() : file{"resources/options.ini"} {
auto fileExists = fs::exists("resources/options.ini");
if(fileExists) {
file.read(structure);
return;
}
structure["misc"]["logLevel"] = Util::LogLevel::Error;
structure["general"]["savePath"] = "saves";
fs::create_directory("saves");
structure["audio"]["volumeL"] = "0.5";
structure["audio"]["volumeR"] = "0.5";
structure["audio"]["lock"] = "true";
structure["cpu"]["type"] = "interpreter";
if(!file.generate(structure))
panic("Couldn't generate settings' INI!");
}
void Options::Apply() {
if(!file.write(structure))
panic("Could not modify options on disk!");
}
+2 -22
View File
@@ -3,28 +3,11 @@
#include <fstream> #include <fstream>
#include <common.hpp> #include <common.hpp>
#include <mini/ini.h> #include <mini/ini.h>
#include <log.hpp>
namespace fs = std::filesystem; namespace fs = std::filesystem;
struct Options { struct Options {
Options() : file{"resources/options.ini"} { Options();
auto fileExists = fs::exists("resources/options.ini");
if(fileExists) {
file.read(structure);
return;
}
structure["general"]["savePath"] = "saves";
fs::create_directory("saves");
structure["audio"]["volumeL"] = "0.5";
structure["audio"]["volumeR"] = "0.5";
structure["audio"]["lock"] = "true";
structure["cpu"]["type"] = "interpreter";
if(!file.generate(structure))
panic("Couldn't generate settings' INI!");
}
static Options &GetInstance() { static Options &GetInstance() {
static Options instance; static Options instance;
@@ -36,10 +19,7 @@ struct Options {
template <typename T> template <typename T>
T GetValue(const std::string &key, const std::string &field); T GetValue(const std::string &key, const std::string &field);
void Apply() { void Apply();
if(!file.write(structure))
panic("Could not modify options on disk!");
}
private: private:
mINI::INIFile file; mINI::INIFile file;
mINI::INIStructure structure; mINI::INIStructure structure;
+7 -16
View File
@@ -2,29 +2,20 @@
#include <common.hpp> #include <common.hpp>
#include <print> #include <print>
#include <string> #include <string>
#if !defined(NDEBUG) && !defined(_WIN32) #include <Options.hpp>
#include <dlfcn.h>
#endif
namespace Util { namespace Util {
enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always }; enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always, Panic};
#ifndef NDEBUG
static constexpr auto globalLogLevel = Debug;
#else
static constexpr auto globalLogLevel = Info;
#endif
template <LogLevel messageType = Info, class... Args> template <LogLevel messageType = Info, class... Args>
void print(const std::format_string<Args...> fmt, Args... args) { void print(const std::format_string<Args...> fmt, Args... args) {
if (messageType >= globalLogLevel) { if (messageType == Panic) {
if (messageType <= Debug) {
#ifndef NDEBUG
std::println(fmt, std::forward<Args>(args)...);
#endif
} else {
std::println(fmt, std::forward<Args>(args)...); std::println(fmt, std::forward<Args>(args)...);
exit(-1);
} }
if (messageType >= Options::GetInstance().GetValue<int>("misc", "logLevel")) {
std::println(fmt, std::forward<Args>(args)...);
} }
} }