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
+16 -16
View File
@@ -27,26 +27,26 @@ endif()
find_program(GIT_EXECUTABLE git)
if (GIT_EXECUTABLE)
execute_process(
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE _git_hash
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (NOT "${KAIZEN_GIT_COMMIT_HASH}" STREQUAL "${_git_hash}")
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
set(KAIZEN_GIT_COMMIT_HASH "${_git_hash}" CACHE STRING "" FORCE)
endif()
if (NOT "${KAIZEN_GIT_COMMIT_HASH}" STREQUAL "${_git_hash}")
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
set(KAIZEN_GIT_COMMIT_HASH "${_git_hash}" CACHE STRING "" FORCE)
endif()
else()
# Otherwise, ensure KAIZEN_GIT_COMMIT_HASH is defined
if (DEFINED KAIZEN_GIT_COMMIT_HASH)
set(_git_hash "${KAIZEN_GIT_COMMIT_HASH}" CACHE STRING "" FORCE)
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
message("Git not found, but git commit hash was defined as ${KAIZEN_GIT_COMMIT_HASH}")
else()
message(FATAL_ERROR "Git not found, please define KAIZEN_GIT_COMMIT_HASH manually.")
endif()
# Otherwise, ensure KAIZEN_GIT_COMMIT_HASH is defined
if (DEFINED KAIZEN_GIT_COMMIT_HASH)
set(_git_hash "${KAIZEN_GIT_COMMIT_HASH}" CACHE STRING "" FORCE)
file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
message("Git not found, but git commit hash was defined as ${KAIZEN_GIT_COMMIT_HASH}")
else()
message(FATAL_ERROR "Git not found, please define KAIZEN_GIT_COMMIT_HASH manually.")
endif()
endif()
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
@@ -167,11 +167,11 @@ add_executable(kaizen
src/frontend/Settings/CPUSettings.cpp
src/frontend/Settings/AudioSettings.hpp
src/frontend/Settings/AudioSettings.cpp
src/frontend/Settings/MiscSettings.hpp
src/frontend/Settings/MiscSettings.cpp
src/frontend/NativeWindow.hpp
src/utils/Options.cpp
src/utils/File.cpp
src/frontend/Debugger.hpp
src/frontend/Debugger.cpp)
src/utils/File.cpp)
if (WIN32)
+7 -21
View File
@@ -9,13 +9,13 @@ Core::Core() {
if (selectedCpu == "interpreter") {
cpuType = Interpreted;
cpu = std::make_unique<Interpreter>(*mem, regs);
} else if(selectedCpu == "jit") {
#ifndef __aarch64__
} else if (selectedCpu == "jit") {
#ifndef __aarch64__
cpuType = DynamicRecompiler;
cpu = std::make_unique<JIT>(*mem, regs);
#else
#else
panic("JIT currently unsupported on aarch64");
#endif
#endif
} else {
panic("Unimplemented CPU type");
}
@@ -31,7 +31,7 @@ void Core::Reset() {
regs.Reset();
mem->Reset();
cpu->Reset();
if(romLoaded)
if (romLoaded)
mem->mmio.si.pif.Execute();
}
@@ -62,9 +62,7 @@ void Core::LoadROM(const std::string &rom_) {
romLoaded = true;
}
u32 Core::StepCPU() {
return cpu->Step() + regs.PopStalledCycles();
}
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
void Core::StepRSP(const u32 cpuCycles) {
MMIO &mmio = mem->mmio;
@@ -101,27 +99,18 @@ void Core::Run(const float volumeL, const float volumeR) {
mmio.mi.InterruptRaise(MI::Interrupt::VI);
}
while(cycles < mem->mmio.vi.cyclesPerHalfline) {
while (cycles < mem->mmio.vi.cyclesPerHalfline) {
const u32 taken = StepCPU();
cycles += taken;
if((broken = breakpoints.contains(regs.nextPC)))
break;
StepRSP(taken);
frameCycles += taken;
Scheduler::GetInstance().Tick(taken);
}
if(broken)
break;
cycles -= mmio.vi.cyclesPerHalfline;
}
if(broken)
break;
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
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);
Scheduler::GetInstance().Tick(frameCycles);
}
if(broken)
pause = true;
}
} // namespace n64
+1 -1
View File
@@ -1,6 +1,6 @@
#include <Netplay.hpp>
#include <PIF.hpp>
#include <array>
#include <log.hpp>
#include <Log.hpp>
namespace Netplay {}
+3 -3
View File
@@ -1,6 +1,6 @@
#pragma once
#include <ircolib/mem_access.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace Util {
#define Z64 0x80371200
@@ -13,7 +13,7 @@ FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
if (endianness >> 24 != 0x80) {
if ((endianness & 0xFF) != 0x80) {
if ((endianness >> 16 & 0xff) != 0x80) {
Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
panic("Unrecognized rom endianness");
return;
} else {
altByteShift = 12;
@@ -42,7 +42,7 @@ FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
ircolib::SwapBuffer<u32>(rom);
break;
default:
Error::GetInstance().Throw({Error::Severity::UNRECOVERABLE}, {Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
}
}
} // namespace Util
+2 -2
View File
@@ -40,10 +40,10 @@ void Scheduler::Tick(const u64 t) {
case NONE:
break;
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;
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;
}
events.pop();
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include <functional>
#include <log.hpp>
#include <Log.hpp>
#include <queue>
enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
-1
View File
@@ -1,7 +1,6 @@
#pragma once
#include <Mem.hpp>
#include <Registers.hpp>
#include <Disassembler.hpp>
namespace n64 {
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.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
return 1;*/
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
error("[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return std::nullopt;
}
@@ -57,17 +55,13 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
/*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
error("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return std::nullopt;
}
const u32 instr = Core::GetMem().Read<u32>(paddr);
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
return instr;
}
@@ -118,9 +112,7 @@ u32 JIT::Step() {
/*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
error("[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0;
}
@@ -177,9 +169,7 @@ u32 JIT::Step() {
return 0;
if(InstrEndsBlock(delay_instruction.value())) {
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!");
error("[JIT]: Unhandled case of branch from delay slot!");
return 0;
}
+19 -66
View File
@@ -29,7 +29,7 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
struct JIT : BaseCPU {};
#else
struct JIT final : BaseCPU {
explicit JIT(Mem&, Registers&);
explicit JIT(Mem &, Registers &);
~JIT() override = default;
u32 Step() override;
@@ -47,14 +47,15 @@ struct JIT final : BaseCPU {
}
void InvalidateBlock(u32);
private:
friend struct Cop1;
friend struct Registers;
using BlockFn = int (*)();
bool branch_taken;
Registers& regs;
Mem& mem;
Registers &regs;
Mem &mem;
u64 cop2Latch{};
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
@@ -73,9 +74,7 @@ private:
return code.qword[code.rbp + GPR_OFFSET(index)];
}
Util::Error::GetInstance().Throw(
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
panic("[JIT]: Invalid register addressing mode {}!", sizeof(T));
return Xbyak::Address{0};
}
@@ -109,8 +108,8 @@ private:
void SetPC32(s32 val);
void SetPC64(s64 val);
void SetPC32(const Xbyak::Reg32& val);
void SetPC64(const Xbyak::Reg64& val);
void SetPC32(const Xbyak::Reg32 &val);
void SetPC64(const Xbyak::Reg64 &val);
void BranchNotTaken();
void BranchTaken(s64 offs);
void BranchTaken(const Xbyak::Reg64 &offs);
@@ -204,57 +203,17 @@ private:
void mthi(Instruction);
void mtlo(Instruction);
void nor(Instruction);
void sb(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sb'!");
}
void sc(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sc'!");
}
void scd(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'scd'!");
}
void sd(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sd'!");
}
void sdc1(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
}
void sdl(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
}
void sdr(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
}
void sh(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sh'!");
}
void sb(const Instruction) { panic("[JIT]: Unhandled 'sb' @ blockPC:{:016X}!", blockPC); }
void sc(const Instruction) { panic("[JIT]: Unhandled 'sc' @ blockPC:{:016X}!", blockPC); }
void scd(const Instruction) { panic("[JIT]: Unhandled 'scd' @ blockPC:{:016X}!", blockPC); }
void sd(const Instruction) { panic("[JIT]: Unhandled 'sd' @ blockPC:{:016X}!", blockPC); }
void sdc1(const Instruction) { panic("[JIT]: Unhandled 'sdc1' @ blockPC:{:016X}!", blockPC); }
void sdl(const Instruction) { panic("[JIT]: Unhandled 'sdl' @ blockPC:{:016X}!", blockPC); }
void sdr(const Instruction) { panic("[JIT]: Unhandled 'sdr' @ blockPC:{:016X}!", blockPC); }
void sh(const Instruction) { panic("[JIT]: Unhandled 'sh' @ blockPC:{:016X}!", blockPC); }
void sw(Instruction);
void swl(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'swl'!");
}
void swr(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'swr'!");
}
void swl(const Instruction) { panic("[JIT]: Unhandled 'swl' @ blockPC:{:016X}!", blockPC); }
void swr(const Instruction) { panic("[JIT]: Unhandled 'swr' @ blockPC:{:016X}!", blockPC); }
void slti(Instruction);
void sltiu(Instruction);
void slt(Instruction);
@@ -264,19 +223,13 @@ private:
void sub(Instruction);
void subu(Instruction);
void swc1(const Instruction) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
panic("[JIT]: Unhandled 'swc1' @ blockPC:{:016X}!", blockPC);
}
void sra(Instruction);
void srav(Instruction);
void srl(Instruction);
void srlv(Instruction);
void trap(bool) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
}
void trap(bool) { panic("[JIT]: Unhandled 'trap' @ blockPC:{:016X}!", blockPC); }
void or_(Instruction);
void ori(Instruction);
void xor_(Instruction);
+158 -127
View File
@@ -19,10 +19,7 @@ void Mem::Reset() {
std::error_code error;
saveData.sync(error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
{}, {}, "[Mem]: Could not sync save data!");
return;
warn("[Mem]: Could not sync save data!");
}
saveData.unmap();
}
@@ -40,10 +37,7 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
if (saveData.is_mapped()) {
saveData.sync(error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
{}, {}, R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
return;
warn(R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
}
saveData.unmap();
}
@@ -55,17 +49,13 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
}
if (sramVec.size() != SRAM_SIZE) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE},
{}, {}, "[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
return;
panic("[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
return;
}
saveData = mio::make_mmap_sink(sramPath, error);
if (error) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MMAP_MAKE_SINK_ERROR},
{}, {}, R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
warn(R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
}
}
}
@@ -145,133 +135,155 @@ void Mem::LoadROM(const bool isArchive, const std::string &filename) {
template <>
u8 Mem::Read(const u32 paddr) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
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, AI_REGION_START, AI_REGION_END)) {
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)) {
const u32 w = mmio.ai.Read(paddr & ~3);
const int offs = 3 - (paddr & 3);
return w >> offs * 8 & 0xff;
}
if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access from MMIO");
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
warn("8-bit read access from MMIO @ {:08X}", paddr);
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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) 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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
return 0;
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access in unhandled region");
warn("8-bit read access in unhandled region @ {:08X}", paddr);
return 0;
}
template <>
u16 Mem::Read(const u32 paddr) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
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, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
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<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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
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) ||
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<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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
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(
{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");
warn("16-bit read access in unhandled region @ {:08X}", paddr);
return 0;
}
template <>
u32 Mem::Read(const u32 paddr) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
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, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(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) ||
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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
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(
{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");
warn("32-bit read access in unhandled region @ {:08X}", paddr);
return 0;
}
template <>
u64 Mem::Read(const u32 paddr) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
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, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(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) ||
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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
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_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
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
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(
{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");
warn("64-bit read access in unhandled region @ {:08X}", paddr);
return 0;
}
template <>
void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
val = val << (8 * (3 - (paddr & 3)));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
paddr = (paddr & 0xFFF) & ~3;
@@ -279,16 +291,17 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
return;
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
trace("BusWrite<u8> @ {:08X} = {:02X}", paddr, val);
mmio.pi.BusWrite<u8, false>(paddr, val);
return;
}
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>!");
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>!");
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)));
paddr = (paddr - PIF_RAM_REGION_START) & ~3;
ircolib::WriteAccess<u32>(si.pif.ram, paddr, std::byteswap(val));
@@ -296,11 +309,12 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
return;
}
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
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);
}
@@ -321,11 +335,14 @@ void Mem::Write<u8>(const u32 paddr, const u32 val) {
template <>
void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
val = val << (16 * !(paddr & 2));
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
paddr = (paddr & 0xFFF) & ~3;
@@ -333,16 +350,17 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
return;
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
trace("BusWrite<u8> @ {:08X} = {:04X}", paddr, val);
mmio.pi.BusWrite<u16, false>(paddr, val);
return;
}
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>!");
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>!");
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));
paddr &= ~3;
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -350,11 +368,12 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
return;
}
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
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);
}
@@ -375,36 +394,43 @@ void Mem::Write<u16>(const u32 paddr, const u32 val) {
template <>
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;
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, RDRAM_REGION_START, RDRAM_REGION_END)) {
mmio.rdp.WriteRDRAM<u32>(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
return;
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
trace("BusWrite<u8> @ {:08X} = {:08X}", paddr, val);
mmio.pi.BusWrite<u32, false>(paddr, val);
return;
}
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; }
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;
}
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));
si.pif.ProcessCommands();
return;
}
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
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);
}
@@ -434,37 +460,42 @@ void Mem::WriteJIT(const u32 paddr, const u64 val) {
void Mem::Write(const u32 paddr, const u64 val) { WriteInterpreter(paddr, val); }
void Mem::WriteInterpreter(const u32 paddr, u64 val) {
n64::Registers& regs = n64::Core::GetRegs();
n64::Registers &regs = n64::Core::GetRegs();
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, DMEM_REGION_START, RSP_MEM_REGION_END)) {
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)) {
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
val >>= 32;
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
return;
}
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) {
trace("BusWrite<u64> @ {:08X} = {:016X}", paddr, val);
mmio.pi.BusWrite<false>(paddr, val);
return;
}
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>!");
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>!");
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));
si.pif.ProcessCommands();
return;
}
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
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_3, UNUSED_END_3) ||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
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);
}
+3 -2
View File
@@ -4,7 +4,7 @@
#include <backend/MemoryRegions.hpp>
#include <backend/core/MMIO.hpp>
#include <common.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <vector>
#include <algorithm>
#include <ranges>
@@ -82,7 +82,7 @@ struct Mem {
void Reset();
void LoadSRAM(SaveType, fs::path);
void LoadROM(bool, const std::string &);
void SetJIT(JIT* jit) { this->jit = jit; }
void SetJIT(JIT *jit) { this->jit = jit; }
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
@@ -124,6 +124,7 @@ struct Mem {
ROM rom;
SaveType saveType = SAVE_NONE;
Flash flash;
private:
friend struct SI;
friend struct PI;
+1 -1
View File
@@ -1,4 +1,4 @@
#include <log.hpp>
#include <Log.hpp>
#include <parallel-rdp/ParallelRDPWrapper.hpp>
#include <Core.hpp>
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
RSP::RSP() { Reset(); }
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <ranges>
namespace n64 {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <Instruction.hpp>
namespace n64 {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
AI::AI() { Reset(); }
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Audio.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <SDL3/SDL.h>
namespace n64 {
+1 -1
View File
@@ -1,6 +1,6 @@
#include <core/mmio/MI.hpp>
#include <core/registers/Registers.hpp>
#include <log.hpp>
#include <Log.hpp>
#define MI_VERSION_REG 0x02020102
+1 -1
View File
@@ -2,7 +2,7 @@
#include <Scheduler.hpp>
#include <cmath>
#include <core/mmio/PI.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
PI::PI() { Reset(); }
+1 -1
View File
@@ -2,7 +2,7 @@
#include <cassert>
#include <cic_nus_6105/n64_cic_nus_6105.hpp>
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <Options.hpp>
#define MEMPAK_SIZE 32768
+1 -1
View File
@@ -1,7 +1,7 @@
#include <Netplay.hpp>
#include <PIF.hpp>
#include <PIF/MupenMovie.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
void PIF::InitDevices(SaveType saveType) {
+18 -16
View File
@@ -80,22 +80,24 @@ void MupenMovie::Reset() {
}
FORCE_INLINE void LogController(const n64::Controller &controller) {
debug("c_right: {}", controller.cRight);
debug("c_left: {}", controller.cLeft);
debug("c_down: {}", controller.cDown);
debug("c_up: {}", controller.cUp);
debug("r: {}", controller.r);
debug("l: {}", controller.l);
debug("dp_right: {}", controller.dpRight);
debug("dp_left: {}", controller.dpLeft);
debug("dp_down: {}", controller.dpDown);
debug("dp_up: {}", controller.dpUp);
debug("z: {}", controller.z);
debug("b: {}", controller.b);
debug("a: {}", controller.a);
debug("start: {}", controller.start);
debug("joy_x: {}", controller.joyX);
debug("joy_y: {}", controller.joyY);
trace("-- TAS CONTROLLER DATA --");
trace("c_right: {}", controller.cRight);
trace("c_left: {}", controller.cLeft);
trace("c_down: {}", controller.cDown);
trace("c_up: {}", controller.cUp);
trace("r: {}", controller.r);
trace("l: {}", controller.l);
trace("dp_right: {}", controller.dpRight);
trace("dp_left: {}", controller.dpLeft);
trace("dp_down: {}", controller.dpDown);
trace("dp_up: {}", controller.dpUp);
trace("z: {}", controller.z);
trace("b: {}", controller.b);
trace("a: {}", controller.a);
trace("start: {}", controller.start);
trace("joy_x: {}", controller.joyX);
trace("joy_y: {}", controller.joyY);
trace("-- TAS CONTROLLER DATA --");
}
n64::Controller MupenMovie::NextInputs() {
+1 -1
View File
@@ -1,5 +1,5 @@
#include <core/mmio/RI.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
RI::RI() { Reset(); }
+6 -6
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
VI::VI() { Reset(); }
@@ -54,9 +54,7 @@ u32 VI::Read(const u32 paddr) const {
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");
error("32-bit read access on unhandled VI register @ {:08X}, pc: {:016X}", paddr, (u64)regs.oldPC);
return 0;
}
}
@@ -120,8 +118,10 @@ void VI::Write(const u32 paddr, const u32 val) {
break;
case 0x0440003C:
break;
default:
panic("Unimplemented VI[{:08X}] write ({:08X})", paddr, val);
default: {
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
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
Cop0::Cop0() { Reset(); }
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include <common.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <unordered_map>
#include <Instruction.hpp>
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
Cop1::Cop1() { Reset(); }
+1 -1
View File
@@ -1,5 +1,5 @@
#include <Core.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
void RSP::special(const Instruction instr) {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <Core.hpp>
#include <RCP.hpp>
#include <RSQ.hpp>
#include <log.hpp>
#include <Log.hpp>
namespace n64 {
FORCE_INLINE bool AcquireSemaphore(RSP &rsp) {
-1
View File
@@ -1,6 +1,5 @@
#pragma once
#include <types.hpp>
#include <ErrorData.hpp>
#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_impl_sdl3.h>
#include <imgui_impl_vulkan.h>
#include <utils/log.hpp>
#include <utils/Log.hpp>
#include <memory>
namespace gui {
+168 -280
View File
@@ -5,7 +5,9 @@
#include <ImGuiImpl/StatusBar.hpp>
#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());
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
@@ -33,186 +35,171 @@ void KaizenGui::QueryDevices(const SDL_Event &event) {
if (gamepad)
SDL_CloseGamepad(gamepad);
break;
default: break;
default:
break;
}
}
void KaizenGui::HandleInput(const SDL_Event &event) {
const n64::Core& core = n64::Core::GetInstance();
const n64::Core &core = n64::Core::GetInstance();
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
switch(event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if(!gamepad)
break;
{
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else {
xclamped /= SDL_JOYSTICK_AXIS_MAX;
}
xclamped *= 86;
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) {
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
}
switch (event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if (!gamepad)
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if(!gamepad)
{
pif.UpdateButton(0, n64::Controller::Key::Z,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else {
xclamped /= SDL_JOYSTICK_AXIS_MAX;
}
xclamped *= 86;
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) {
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
}
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if (!gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
{
const auto keys = SDL_GetKeyboardState(nullptr);
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true;
}
fastForward = keys[SDL_SCANCODE_SPACE];
if (!unlockFramerate)
core.parallel.SetFramerateUnlocked(fastForward);
if (core.romLoaded) {
if (keys[SDL_SCANCODE_P]) {
emuThread.TogglePause();
}
if (keys[SDL_SCANCODE_R]) {
emuThread.Reset();
}
if (keys[SDL_SCANCODE_Q]) {
emuThread.Stop();
}
}
if (gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
{
const auto keys = SDL_GetKeyboardState(nullptr);
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true;
}
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
fastForward = keys[SDL_SCANCODE_SPACE];
if(!unlockFramerate)
core.parallel.SetFramerateUnlocked(fastForward);
float x = 0, y = 0;
if(core.romLoaded) {
if(keys[SDL_SCANCODE_P]) {
emuThread.TogglePause();
}
if (keys[SDL_SCANCODE_UP])
y = 86;
if (keys[SDL_SCANCODE_DOWN])
y = -86;
if (keys[SDL_SCANCODE_LEFT])
x = -86;
if (keys[SDL_SCANCODE_RIGHT])
x = 86;
if(keys[SDL_SCANCODE_R]) {
emuThread.Reset();
}
if(keys[SDL_SCANCODE_Q]) {
emuThread.Stop();
}
}
if(gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
float x = 0, y = 0;
if (keys[SDL_SCANCODE_UP]) y = 86;
if (keys[SDL_SCANCODE_DOWN]) y = -86;
if (keys[SDL_SCANCODE_LEFT]) x = -86;
if (keys[SDL_SCANCODE_RIGHT]) x = 86;
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
}
break;
default: break;
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
}
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() {
n64::Core& core = n64::Core::GetInstance();
n64::Core &core = n64::Core::GetInstance();
gui::StartFrame();
if(ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Open", "Ctrl-O")) {
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl-O")) {
fileDialogOpen = true;
}
if(ImGui::MenuItem("Exit")) {
if (ImGui::MenuItem("Exit")) {
quit = true;
emuThread.Stop();
}
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Emulation")) {
if (ImGui::BeginMenu("Emulation")) {
ImGui::BeginDisabled(!core.romLoaded);
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
emuThread.TogglePause();
}
if(ImGui::MenuItem("Reset", "R")) {
if (ImGui::MenuItem("Reset", "R")) {
emuThread.Reset();
}
if(ImGui::MenuItem("Stop", "Q")) {
if (ImGui::MenuItem("Stop", "Q")) {
emuThread.Stop();
core.romLoaded = false;
}
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
core.parallel.SetFramerateUnlocked(unlockFramerate);
}
if(ImGui::MenuItem("Open Debugger")) {
debugger.Open();
}
ImGui::EndDisabled();
if(ImGui::MenuItem("Options")) {
if (ImGui::MenuItem("Options")) {
settingsWindow.isOpen = true;
}
ImGui::EndMenu();
}
if(ImGui::BeginMenu("Help")) {
if(ImGui::MenuItem("About")) {
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About")) {
aboutOpen = true;
}
@@ -221,20 +208,15 @@ void KaizenGui::RenderUI() {
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);
}
if(aboutOpen) {
if (aboutOpen) {
ImGui::OpenPopup("About Kaizen");
}
settingsWindow.render();
debugger.render();
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
@@ -247,7 +229,7 @@ void KaizenGui::RenderUI() {
ImGui::Separator();
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
ImGui::Separator();
if(ImGui::Button("OK")) {
if (ImGui::Button("OK")) {
aboutOpen = false;
ImGui::CloseCurrentPopup();
}
@@ -255,111 +237,14 @@ 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) {
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::EndMainStatusBar();
}
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::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
@@ -384,34 +269,38 @@ void KaizenGui::RenderUI() {
ImGui::RenderPlatformWindowsDefault();
}
if(fileDialogOpen) {
if (fileDialogOpen) {
fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
auto kaizen = static_cast<KaizenGui*>(userdata);
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
{"Nintendo 64 executable", "n64;z64;v64"},
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) {
panic("An error occured: {}", SDL_GetError());
}
if (!filelist) {
panic("An error occured: {}", SDL_GetError());
}
if (!*filelist) {
warn("The user did not select any file.");
warn("Most likely, the dialog was canceled.");
return;
}
if (!*filelist) {
warn("The user did not select any file.");
warn("Most likely, the dialog was canceled.");
return;
}
kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false);
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
},
this, window.getHandle(), filters, 3, nullptr, false);
}
if(minimized)
if (minimized)
return;
if(core.romLoaded) {
if (core.romLoaded) {
core.parallel.UpdateScreen<true>();
return;
}
@@ -420,29 +309,29 @@ void KaizenGui::RenderUI() {
}
void KaizenGui::LoadROM(const std::string &path) noexcept {
n64::Core& core = n64::Core::GetInstance();
n64::Core &core = n64::Core::GetInstance();
core.LoadROM(path);
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
}
void KaizenGui::run() {
while(!quit) {
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e);
switch(e.type) {
case SDL_EVENT_QUIT:
quit = true;
emuThread.Stop();
break;
case SDL_EVENT_WINDOW_MINIMIZED:
minimized = true;
break;
case SDL_EVENT_WINDOW_RESTORED:
minimized = false;
break;
default:
switch (e.type) {
case SDL_EVENT_QUIT:
quit = true;
emuThread.Stop();
break;
case SDL_EVENT_WINDOW_MINIMIZED:
minimized = true;
break;
case SDL_EVENT_WINDOW_RESTORED:
minimized = false;
break;
default:
}
QueryDevices(e);
HandleInput(e);
@@ -455,6 +344,5 @@ void KaizenGui::run() {
}
}
void KaizenGui::LoadTAS(const std::string &path) noexcept {
n64::Core::GetInstance().LoadTAS(fs::path(path));
}
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
+3 -3
View File
@@ -1,12 +1,12 @@
#pragma once
#include <RenderWidget.hpp>
#include <NativeWindow.hpp>
#include <Debugger.hpp>
#include <EmuThread.hpp>
#include <SDL3/SDL_gamepad.h>
class KaizenGui final {
gui::NativeWindow window;
public:
explicit KaizenGui() noexcept;
~KaizenGui();
@@ -19,13 +19,13 @@ public:
SettingsWindow settingsWindow;
RenderWidget vulkanWidget;
EmuThread emuThread;
Debugger debugger;
SDL_Gamepad* gamepad = nullptr;
SDL_Gamepad *gamepad = nullptr;
void run();
static void LoadTAS(const std::string &path) noexcept;
void LoadROM(const std::string &path) noexcept;
private:
int width{}, height{};
bool aboutOpen = false;
+1 -1
View File
@@ -3,7 +3,7 @@
#include <string>
#include <memory>
#include <volk.h>
#include <utils/log.hpp>
#include <utils/Log.hpp>
namespace gui {
struct NativeWindow {
+1 -1
View File
@@ -1,6 +1,6 @@
#include <CPUSettings.hpp>
#include <Options.hpp>
#include <log.hpp>
#include <Log.hpp>
#include <imgui.h>
CPUSettings::CPUSettings() {
+1 -1
View File
@@ -1,7 +1,7 @@
#include <GeneralSettings.hpp>
#include <Options.hpp>
#include <imgui.h>
#include <log.hpp>
#include <Log.hpp>
GeneralSettings::GeneralSettings(gui::NativeWindow& window) : window(window) {
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 <CPUSettings.hpp>
#include <GeneralSettings.hpp>
#include <MiscSettings.hpp>
#include <NativeWindow.hpp>
#include <vector>
@@ -10,17 +11,19 @@ class SettingsWindow final {
GeneralSettings generalSettings;
CPUSettings cpuSettings;
AudioSettings audioSettings;
MiscSettings miscSettings;
bool applyEnabled = false;
std::vector<std::pair<std::string, SettingsTab*>> tabs = {
{ "General", &generalSettings },
{ "CPU", &cpuSettings },
{ "Audio", &audioSettings },
{ "Misc", &miscSettings },
};
public:
bool isOpen = false;
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 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
#include <fstream>
#include <log.hpp>
#include <Log.hpp>
#include <vector>
#include <filesystem>
#include <ircolib/file.hpp>
+35
View File
@@ -1,4 +1,5 @@
#include <Options.hpp>
#include <Log.hpp>
template <>
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);
}
template <>
void Options::SetValue<int>(const std::string &key, const std::string &field, const int &value) {
structure[key][field] = std::format("{}", value);
}
template <>
void Options::SetValue<bool>(const std::string &key, const std::string &field, const bool &value) {
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]);
}
template<>
int Options::GetValue<int>(const std::string &key, const std::string &field) {
return std::stoi(structure[key][field]);
}
template<>
bool Options::GetValue<bool>(const std::string &key, const std::string &field) {
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 <common.hpp>
#include <mini/ini.h>
#include <log.hpp>
namespace fs = std::filesystem;
struct Options {
Options() : file{"resources/options.ini"} {
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!");
}
Options();
static Options &GetInstance() {
static Options instance;
@@ -36,10 +19,7 @@ struct Options {
template <typename T>
T GetValue(const std::string &key, const std::string &field);
void Apply() {
if(!file.write(structure))
panic("Could not modify options on disk!");
}
void Apply();
private:
mINI::INIFile file;
mINI::INIStructure structure;
+9 -18
View File
@@ -2,30 +2,21 @@
#include <common.hpp>
#include <print>
#include <string>
#if !defined(NDEBUG) && !defined(_WIN32)
#include <dlfcn.h>
#endif
#include <Options.hpp>
namespace Util {
enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always };
#ifndef NDEBUG
static constexpr auto globalLogLevel = Debug;
#else
static constexpr auto globalLogLevel = Info;
#endif
enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always, Panic};
template <LogLevel messageType = Info, class... Args>
void print(const std::format_string<Args...> fmt, Args... args) {
if (messageType >= globalLogLevel) {
if (messageType <= Debug) {
#ifndef NDEBUG
std::println(fmt, std::forward<Args>(args)...);
#endif
} else {
std::println(fmt, std::forward<Args>(args)...);
if (messageType == Panic) {
std::println(fmt, std::forward<Args>(args)...);
exit(-1);
}
if (messageType >= Options::GetInstance().GetValue<int>("misc", "logLevel")) {
std::println(fmt, std::forward<Args>(args)...);
}
}
}
#define panic(fmt, ...) do { Util::print<Util::Error>("[FATAL] " fmt __VA_OPT__(,) __VA_ARGS__); exit(-1); } while(0)