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