- Stop using inheritance for CPU, instead use composition.
- Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work
This commit is contained in:
@@ -13,6 +13,7 @@ if(APPLE)
|
||||
enable_language(OBJC)
|
||||
endif()
|
||||
|
||||
set(USE_JIT FALSE)
|
||||
set(VULKAN_VALIDATION FALSE)
|
||||
set(SANITIZERS FALSE)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -115,6 +116,9 @@ endif()
|
||||
set(SIMD_FLAG NULL)
|
||||
|
||||
if(ARM64)
|
||||
if(USE_JIT)
|
||||
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
|
||||
endif()
|
||||
message("Defining USE_NEON...")
|
||||
add_compile_definitions(USE_NEON)
|
||||
add_compile_definitions(SIMD_SUPPORT)
|
||||
@@ -182,6 +186,9 @@ endif()
|
||||
|
||||
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
|
||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||
if(USE_JIT)
|
||||
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
|
||||
endif()
|
||||
|
||||
if (SANITIZERS)
|
||||
message("UBSAN AND ASAN: ON")
|
||||
|
||||
+7
-5
@@ -34,7 +34,8 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
|
||||
new_context->set_num_thread_indices(num_thread_indices);
|
||||
new_context->set_system_handles(system_handles);
|
||||
|
||||
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
||||
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
|
||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
||||
panic("Failed to create Vulkan instance.\n");
|
||||
}
|
||||
|
||||
@@ -99,15 +100,16 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
||||
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||
|
||||
fullscreen_quad_program = wsi->get_device().request_program(
|
||||
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
||||
fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
|
||||
reinterpret_cast<const u32 *>(fragment_shader),
|
||||
sizeFrag, &vertLayout, &fragLayout);
|
||||
|
||||
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
||||
uintptr_t offset = 0;
|
||||
|
||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
||||
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||
const size_t align =
|
||||
wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||
offset = aligned_rdram & align - 1;
|
||||
aligned_rdram -= offset;
|
||||
}
|
||||
@@ -191,7 +193,7 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
||||
|
||||
template <>
|
||||
void ParallelRDP::UpdateScreen<true>() const {
|
||||
const n64::VI& vi = n64::Core::GetMem().mmio.vi;
|
||||
const n64::VI &vi = n64::Core::GetMem().mmio.vi;
|
||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||
|
||||
+19
-9
@@ -4,18 +4,18 @@
|
||||
#include <Options.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Core::Core() {
|
||||
Core::Core() :
|
||||
interpreter(*mem, regs)
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
,
|
||||
jit(*mem, regs)
|
||||
#endif
|
||||
{
|
||||
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||
if (selectedCpu == "interpreter") {
|
||||
cpuType = Interpreted;
|
||||
cpu = std::make_unique<Interpreter>(*mem, regs);
|
||||
} else if (selectedCpu == "jit") {
|
||||
#ifndef __aarch64__
|
||||
cpuType = DynamicRecompiler;
|
||||
cpu = std::make_unique<JIT>(*mem, regs);
|
||||
#else
|
||||
panic("JIT currently unsupported on aarch64");
|
||||
#endif
|
||||
} else {
|
||||
panic("Unimplemented CPU type");
|
||||
}
|
||||
@@ -30,7 +30,7 @@ void Core::Stop() {
|
||||
void Core::Reset() {
|
||||
regs.Reset();
|
||||
mem->Reset();
|
||||
cpu->Reset();
|
||||
interpreter.Reset();
|
||||
if (romLoaded)
|
||||
mem->mmio.si.pif.Execute();
|
||||
}
|
||||
@@ -62,7 +62,17 @@ void Core::LoadROM(const std::string &rom_) {
|
||||
romLoaded = true;
|
||||
}
|
||||
|
||||
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
|
||||
u32 Core::StepCPU() {
|
||||
if (cpuType == Interpreted)
|
||||
return interpreter.Step() + regs.PopStalledCycles();
|
||||
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
if (cpuType == DynamicRecompiler)
|
||||
return jit.Step() + regs.PopStalledCycles();
|
||||
#endif
|
||||
|
||||
panic("Invalid CPU type?");
|
||||
}
|
||||
|
||||
void Core::StepRSP(const u32 cpuCycles) {
|
||||
MMIO &mmio = mem->mmio;
|
||||
|
||||
+15
-16
@@ -1,33 +1,27 @@
|
||||
#pragma once
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <backend/core/Interpreter.hpp>
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
#include <backend/core/JIT.hpp>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
#include <Registers.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct Core {
|
||||
enum CPUType {
|
||||
Interpreted,
|
||||
DynamicRecompiler,
|
||||
CachedInterpreter
|
||||
} cpuType = Interpreted;
|
||||
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted;
|
||||
|
||||
explicit Core();
|
||||
|
||||
static Core& GetInstance() {
|
||||
static Core &GetInstance() {
|
||||
static Core instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static Registers& GetRegs() {
|
||||
return GetInstance().regs;
|
||||
}
|
||||
static Registers &GetRegs() { return GetInstance().regs; }
|
||||
|
||||
static Mem& GetMem() {
|
||||
return *GetInstance().mem;
|
||||
}
|
||||
static Mem &GetMem() { return *GetInstance().mem; }
|
||||
|
||||
u32 StepCPU();
|
||||
void StepRSP(u32 cpuCycles);
|
||||
@@ -38,7 +32,7 @@ struct Core {
|
||||
void Run(float volumeL, float volumeR);
|
||||
void TogglePause() { pause = !pause; }
|
||||
inline void ToggleBreakpoint(s64 addr) {
|
||||
if(breakpoints.contains(addr)) {
|
||||
if (breakpoints.contains(addr)) {
|
||||
breakpoints.erase(addr);
|
||||
return;
|
||||
}
|
||||
@@ -53,10 +47,15 @@ struct Core {
|
||||
size_t memSize{}, cpuSize{}, verSize{};
|
||||
std::string rom;
|
||||
std::set<s64> breakpoints{};
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
JIT jit;
|
||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>(jit);
|
||||
Registers regs(jit);
|
||||
#else
|
||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
||||
std::unique_ptr<BaseCPU> cpu;
|
||||
|
||||
Registers regs;
|
||||
#endif
|
||||
Interpreter interpreter;
|
||||
ParallelRDP parallel;
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
add_subdirectory(interpreter)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
add_subdirectory(jit)
|
||||
endif()
|
||||
add_subdirectory(mem)
|
||||
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
|
||||
|
||||
add_library(core ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
target_link_libraries(core PRIVATE jit)
|
||||
endif()
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
#include <Mem.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
#include <common.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct alignas(32) InstructionCache {
|
||||
@@ -10,6 +8,7 @@ struct alignas(32) InstructionCache {
|
||||
u32 ptag;
|
||||
|
||||
private:
|
||||
friend struct Interpreter;
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||
};
|
||||
@@ -21,13 +20,8 @@ struct alignas(32) DataCache {
|
||||
int index;
|
||||
|
||||
private:
|
||||
friend struct Interpreter;
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||
};
|
||||
|
||||
struct BaseCPU {
|
||||
virtual ~BaseCPU() = default;
|
||||
virtual u32 Step() = 0;
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
} // namespace n64
|
||||
@@ -1,25 +1,27 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace n64 {
|
||||
struct Core;
|
||||
|
||||
struct Interpreter final : BaseCPU {
|
||||
explicit Interpreter(Mem&, Registers&);
|
||||
~Interpreter() override = default;
|
||||
u32 Step() override;
|
||||
struct Interpreter final {
|
||||
explicit Interpreter(Mem &, Registers &);
|
||||
~Interpreter() = default;
|
||||
u32 Step();
|
||||
|
||||
void Reset() override {
|
||||
cop2Latch = {};
|
||||
}
|
||||
void Reset() { cop2Latch = {}; }
|
||||
|
||||
private:
|
||||
Registers& regs;
|
||||
Mem& mem;
|
||||
private:
|
||||
InstructionCache icache;
|
||||
DataCache dcache;
|
||||
Registers ®s;
|
||||
Mem &mem;
|
||||
u64 cop2Latch{};
|
||||
friend struct Cop1;
|
||||
|
||||
void cache_type_data(u8);
|
||||
void cache_type_instruction(u8);
|
||||
#define check_address_error(mask, vaddr) \
|
||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
@@ -41,6 +43,7 @@ private:
|
||||
void blink(Instruction, bool);
|
||||
void bl(Instruction, bool);
|
||||
void bllink(Instruction, bool);
|
||||
void cache(Instruction);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
|
||||
+22
-25
@@ -1,11 +1,9 @@
|
||||
#include <Core.hpp>
|
||||
#include <jit/helpers.hpp>
|
||||
#include <JIT.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifndef __aarch64__
|
||||
JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) {
|
||||
regs.SetJIT(this);
|
||||
mem.SetJIT(this);
|
||||
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
||||
blockCache.resize(kUpperSize);
|
||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
||||
CS_ERR_OK) {
|
||||
@@ -47,8 +45,8 @@ 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, {},
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
||||
blockPC, {},
|
||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -91,7 +89,7 @@ void JIT::SetPC64(const s64 val) {
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
}
|
||||
|
||||
void JIT::SetPC32(const Xbyak::Reg32& val) {
|
||||
void JIT::SetPC32(const Xbyak::Reg32 &val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.movsxd(val.cvt64(), val);
|
||||
@@ -100,7 +98,7 @@ void JIT::SetPC32(const Xbyak::Reg32& val) {
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||
}
|
||||
|
||||
void JIT::SetPC64(const Xbyak::Reg64& val) {
|
||||
void JIT::SetPC64(const Xbyak::Reg64 &val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||
@@ -118,8 +116,8 @@ 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, {},
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||
return 0;
|
||||
@@ -162,7 +160,7 @@ u32 JIT::Step() {
|
||||
|
||||
auto instruction = FetchInstruction(blockPC);
|
||||
|
||||
if(!instruction)
|
||||
if (!instruction)
|
||||
return 0;
|
||||
|
||||
instructionsInBlock++;
|
||||
@@ -171,15 +169,15 @@ u32 JIT::Step() {
|
||||
blockPC = blockNextPC;
|
||||
blockNextPC += 4;
|
||||
|
||||
if(InstrEndsBlock(instruction.value())) {
|
||||
if (InstrEndsBlock(instruction.value())) {
|
||||
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||
if(!delay_instruction)
|
||||
if (!delay_instruction)
|
||||
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!");
|
||||
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!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -192,7 +190,7 @@ u32 JIT::Step() {
|
||||
Emit(delay_instruction.value());
|
||||
Emit(instruction.value());
|
||||
|
||||
if(!branch_taken) {
|
||||
if (!branch_taken) {
|
||||
Xbyak::Label runtime_branch_taken;
|
||||
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
||||
code.cmp(code.SCR1, 0);
|
||||
@@ -206,7 +204,8 @@ u32 JIT::Step() {
|
||||
code.L(runtime_branch_taken);
|
||||
}
|
||||
|
||||
if(branch_taken) branch_taken = false;
|
||||
if (branch_taken)
|
||||
branch_taken = false;
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
|
||||
@@ -227,7 +226,8 @@ u32 JIT::Step() {
|
||||
blockInfoSize = code.getSize() - blockInfoSize;
|
||||
|
||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
||||
const auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
||||
const auto count =
|
||||
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
||||
if (count > 0) {
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
|
||||
@@ -239,8 +239,5 @@ u32 JIT::Step() {
|
||||
return block();
|
||||
}
|
||||
|
||||
void JIT::DumpBlockCacheToDisk() const {
|
||||
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
|
||||
}
|
||||
#endif
|
||||
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
|
||||
} // namespace n64
|
||||
|
||||
+32
-44
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
#include <xbyak.h>
|
||||
@@ -25,15 +26,12 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
||||
#define HI_OFFSET (reinterpret_cast<uintptr_t>(®s.hi) - reinterpret_cast<uintptr_t>(this))
|
||||
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
||||
|
||||
#ifdef __aarch64__
|
||||
struct JIT : BaseCPU {};
|
||||
#else
|
||||
struct JIT final : BaseCPU {
|
||||
explicit JIT(Mem&, Registers&);
|
||||
~JIT() override = default;
|
||||
u32 Step() override;
|
||||
struct JIT final {
|
||||
explicit JIT(Mem &, Registers &);
|
||||
~JIT() = default;
|
||||
u32 Step();
|
||||
|
||||
void Reset() override {
|
||||
void Reset() {
|
||||
code.reset();
|
||||
blockCache = {};
|
||||
blockCache.resize(kUpperSize);
|
||||
@@ -47,14 +45,15 @@ struct JIT final : BaseCPU {
|
||||
}
|
||||
|
||||
void InvalidateBlock(u32);
|
||||
private:
|
||||
|
||||
private:
|
||||
friend struct Cop1;
|
||||
friend struct Registers;
|
||||
using BlockFn = int (*)();
|
||||
|
||||
bool branch_taken;
|
||||
Registers& regs;
|
||||
Mem& mem;
|
||||
Registers ®s;
|
||||
Mem &mem;
|
||||
u64 cop2Latch{};
|
||||
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
||||
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
||||
@@ -73,9 +72,9 @@ 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));
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||
{Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {},
|
||||
"[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
||||
return Xbyak::Address{0};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -205,54 +204,44 @@ private:
|
||||
void mtlo(Instruction);
|
||||
void nor(Instruction);
|
||||
void sb(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_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},
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
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},
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
||||
}
|
||||
void sw(Instruction);
|
||||
void swl(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
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},
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
||||
}
|
||||
void slti(Instruction);
|
||||
@@ -264,23 +253,22 @@ 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!");
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||
"[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void sra(Instruction);
|
||||
void srav(Instruction);
|
||||
void srl(Instruction);
|
||||
void srlv(Instruction);
|
||||
void trap(bool) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||
"[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void or_(Instruction);
|
||||
void ori(Instruction);
|
||||
void xor_(Instruction);
|
||||
void xori(Instruction);
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
+173
-155
@@ -4,9 +4,14 @@
|
||||
#include <cassert>
|
||||
#include <Core.hpp>
|
||||
#include <Options.hpp>
|
||||
#include <Registers.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
Mem::Mem(JIT &jit) : flash(saveData), jit(jit) {
|
||||
#else
|
||||
Mem::Mem() : flash(saveData) {
|
||||
#endif
|
||||
rom.cart.resize(CART_SIZE);
|
||||
std::ranges::fill(rom.cart, 0);
|
||||
isviewer_sink = std::ofstream("isviewer.log");
|
||||
@@ -19,9 +24,9 @@ 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!");
|
||||
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();
|
||||
@@ -40,9 +45,9 @@ 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);
|
||||
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();
|
||||
@@ -56,16 +61,16 @@ 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);
|
||||
{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, 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,23 +150,25 @@ 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 ®s = 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) ||
|
||||
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,
|
||||
@@ -169,109 +176,141 @@ u8 Mem::Read(const u32 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
|
||||
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;
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
template <>
|
||||
u16 Mem::Read(const u32 paddr) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
n64::Registers ®s = 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
|
||||
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;
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||
return 0;
|
||||
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, "16-bit read access in unhandled region");
|
||||
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;
|
||||
}
|
||||
|
||||
template <>
|
||||
u32 Mem::Read(const u32 paddr) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
n64::Registers ®s = 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
|
||||
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;
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||
return 0;
|
||||
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access in unhandled region");
|
||||
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;
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 Mem::Read(const u32 paddr) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
n64::Registers ®s = 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
|
||||
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;
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||
return 0;
|
||||
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0}, "64-bit read access in unhandled region");
|
||||
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;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
void Mem::Write<u8>(u32 paddr, u32 val) {
|
||||
n64::Registers ®s = 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)) {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
jit.InvalidateBlock(paddr);
|
||||
#endif
|
||||
|
||||
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 +318,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,36 +336,30 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_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;
|
||||
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);
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
template <>
|
||||
void Mem::WriteJIT<u8>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u8>(paddr, val);
|
||||
if (jit)
|
||||
jit->InvalidateBlock(paddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void Mem::Write<u8>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u8>(paddr, val);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
void Mem::Write<u16>(u32 paddr, u32 val) {
|
||||
n64::Registers ®s = 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)) {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
jit.InvalidateBlock(paddr);
|
||||
#endif
|
||||
|
||||
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 +367,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,121 +385,104 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||
ircolib::IsInsideRange(paddr, UNUSED_START_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;
|
||||
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);
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
template <>
|
||||
void Mem::WriteJIT<u16>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u16>(paddr, val);
|
||||
if (jit)
|
||||
jit->InvalidateBlock(paddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void Mem::Write<u16>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u16>(paddr, val);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
void Mem::Write<u32>(const u32 paddr, const u32 val) {
|
||||
n64::Registers ®s = 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)) {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
jit.InvalidateBlock(paddr);
|
||||
#endif
|
||||
|
||||
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
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
template <>
|
||||
void Mem::WriteJIT<u32>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u32>(paddr, val);
|
||||
if (jit)
|
||||
jit->InvalidateBlock(paddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void Mem::Write<u32>(const u32 paddr, const u32 val) {
|
||||
WriteInterpreter<u32>(paddr, val);
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
void Mem::WriteJIT(const u32 paddr, const u64 val) {
|
||||
WriteInterpreter(paddr, val);
|
||||
if (jit)
|
||||
jit->InvalidateBlock(paddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
void Mem::Write(const u32 paddr, u64 val) {
|
||||
n64::Registers ®s = 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)) {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
jit.InvalidateBlock(paddr);
|
||||
#endif
|
||||
|
||||
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
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
+10
-12
@@ -74,15 +74,19 @@ struct Flash {
|
||||
T Read(u32 index) const;
|
||||
};
|
||||
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
struct JIT;
|
||||
|
||||
struct Mem {
|
||||
~Mem() = default;
|
||||
Mem(JIT &jit);
|
||||
JIT &jit;
|
||||
#else
|
||||
struct Mem {
|
||||
Mem();
|
||||
~Mem() = default;
|
||||
void Reset();
|
||||
void LoadSRAM(SaveType, fs::path);
|
||||
void LoadROM(bool, const std::string &);
|
||||
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,7 +128,8 @@ struct Mem {
|
||||
ROM rom;
|
||||
SaveType saveType = SAVE_NONE;
|
||||
Flash flash;
|
||||
private:
|
||||
|
||||
private:
|
||||
friend struct SI;
|
||||
friend struct PI;
|
||||
friend struct AI;
|
||||
@@ -132,17 +137,9 @@ private:
|
||||
friend struct JIT;
|
||||
friend struct Core;
|
||||
|
||||
template <typename T>
|
||||
void WriteInterpreter(u32, u32);
|
||||
void WriteInterpreter(u32, u64);
|
||||
template <typename T>
|
||||
void WriteJIT(u32, u32);
|
||||
void WriteJIT(u32, u64);
|
||||
|
||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||
std::ofstream isviewer_sink{};
|
||||
int mmioSize{}, flashSize{};
|
||||
JIT *jit = nullptr;
|
||||
std::string sramPath{};
|
||||
mio::mmap_sink saveData{};
|
||||
|
||||
@@ -151,4 +148,5 @@ private:
|
||||
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
@@ -164,8 +164,8 @@ void Interpreter::special(const Instruction instr) {
|
||||
dsra32(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi, instr.instr.opcode.special_lo, instr.instr.raw,
|
||||
static_cast<u64>(regs.oldPC));
|
||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi,
|
||||
instr.instr.opcode.special_lo, instr.instr.raw, static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ void Interpreter::Exec(const Instruction instr) {
|
||||
regs.cop0.decode(instr);
|
||||
break;
|
||||
case Instruction::COP1:
|
||||
if(instr.cop_rs() == 0x08) {
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
switch (instr.cop_rt()) {
|
||||
case 0:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
@@ -409,7 +409,11 @@ void Interpreter::Exec(const Instruction instr) {
|
||||
swr(instr);
|
||||
break;
|
||||
case Instruction::CACHE:
|
||||
break; // CACHE
|
||||
{
|
||||
panic("CACHE 0b{:05b}, 0x{:04X}({}/r{} = 0x{:08X})", instr.op(), instr.offset(),
|
||||
Registers::regNames[instr.base()], instr.base(), regs.Read<u64>(instr.base()));
|
||||
}
|
||||
break;
|
||||
case Instruction::LL:
|
||||
ll(instr);
|
||||
break;
|
||||
@@ -449,7 +453,8 @@ void Interpreter::Exec(const Instruction instr) {
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), static_cast<u64>(regs.oldPC));
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -583,7 +583,9 @@ void Interpreter::ori(const Instruction instr) {
|
||||
regs.Write(instr.rt(), result);
|
||||
}
|
||||
|
||||
void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())); }
|
||||
void Interpreter::or_(const Instruction instr) {
|
||||
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt()));
|
||||
}
|
||||
|
||||
void Interpreter::nor(const Instruction instr) {
|
||||
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
|
||||
@@ -622,7 +624,9 @@ void Interpreter::sltiu(const Instruction instr) {
|
||||
regs.Write(instr.rt(), regs.Read<u64>(instr.rs()) < imm);
|
||||
}
|
||||
|
||||
void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt())); }
|
||||
void Interpreter::slt(const Instruction instr) {
|
||||
regs.Write(instr.rd(), regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
|
||||
}
|
||||
|
||||
void Interpreter::sltu(const Instruction instr) {
|
||||
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
||||
@@ -825,7 +829,7 @@ void Interpreter::mtlo(const Instruction instr) { regs.lo = regs.Read<s64>(instr
|
||||
void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read<s64>(instr.rs()); }
|
||||
|
||||
void Interpreter::trap(const bool cond) const {
|
||||
Cop0& cop0 = Core::GetRegs().cop0;
|
||||
Cop0 &cop0 = Core::GetRegs().cop0;
|
||||
if (cond) {
|
||||
cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC);
|
||||
}
|
||||
@@ -845,4 +849,24 @@ void Interpreter::dmfc2(const Instruction instr) { regs.Write(instr.rt(), cop2La
|
||||
void Interpreter::ctc2(const Instruction) {}
|
||||
|
||||
void Interpreter::cfc2(const Instruction) {}
|
||||
|
||||
void Interpreter::cache(const Instruction instr) {
|
||||
u8 type = instr.op() & 3;
|
||||
u8 op = (instr.op() >> 2) & 7;
|
||||
if (type > 1)
|
||||
panic("Unknown cache type {}", type);
|
||||
|
||||
if (type == 0)
|
||||
return cache_type_instruction(op);
|
||||
|
||||
if (type == 1)
|
||||
return cache_type_data(op);
|
||||
}
|
||||
|
||||
void Interpreter::cache_type_instruction(const u8 op) {
|
||||
switch (op) {
|
||||
case 0:
|
||||
icache.
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
#include <core/JIT.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
||||
#else
|
||||
Registers::Registers() { Reset(); }
|
||||
#endif
|
||||
|
||||
void Registers::Reset() {
|
||||
hi = 0;
|
||||
@@ -72,85 +76,85 @@ s8 Registers::Read<s8>(size_t idx) {
|
||||
return static_cast<s8>(Read<u8>(idx));
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
template <>
|
||||
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt64(), Read<u64>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt64(), Read<u64>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt64(), Read<s64>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt64(), Read<s64>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt32(), Read<u32>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt32(), Read<u32>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx));
|
||||
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt32(), Read<s32>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt32(), Read<s32>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx));
|
||||
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt16(), Read<u16>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt16(), Read<u16>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt16(), Read<s16>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt16(), Read<s16>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt8(), Read<u8>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt8(), Read<u8>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx));
|
||||
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt8(), Read<s8>(idx));
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt8(), Read<s8>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx));
|
||||
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -159,7 +163,6 @@ void Registers::Write<bool>(size_t idx, bool v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
@@ -170,8 +173,8 @@ void Registers::Write<u64>(size_t idx, u64 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -185,8 +188,8 @@ void Registers::Write<u32>(size_t idx, u32 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -196,8 +199,8 @@ void Registers::Write<s32>(size_t idx, s32 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -206,8 +209,8 @@ void Registers::Write<u16>(size_t idx, u16 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -217,8 +220,8 @@ void Registers::Write<s16>(size_t idx, s16 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -227,8 +230,8 @@ void Registers::Write<u8>(size_t idx, u8 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
@@ -238,24 +241,21 @@ void Registers::Write<s8>(size_t idx, s8 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
template <>
|
||||
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -263,13 +263,10 @@ void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -277,13 +274,10 @@ void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -291,13 +285,10 @@ void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt16());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movsx(v.cvt64(), v.cvt16());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -310,8 +301,8 @@ void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt16());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt16());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -324,8 +315,8 @@ void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsxd(v.cvt64(), v.cvt32());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movsxd(v.cvt64(), v.cvt32());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -338,8 +329,8 @@ void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt32());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt32());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -352,7 +343,7 @@ void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
@@ -4,13 +4,17 @@
|
||||
#include <backend/core/registers/Cop1.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
struct JIT;
|
||||
struct Registers {
|
||||
JIT &jit;
|
||||
Registers(JIT &jit);
|
||||
#else
|
||||
struct Registers {
|
||||
Registers();
|
||||
void Reset();
|
||||
void SetPC64(s64);
|
||||
void SetPC32(s32);
|
||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
||||
|
||||
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||
if (index == 0)
|
||||
@@ -22,31 +26,17 @@ struct Registers {
|
||||
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||
}
|
||||
|
||||
bool GetLOConstant() {
|
||||
return regIsConstant & (1ull << 32);
|
||||
}
|
||||
bool GetLOConstant() { return regIsConstant & (1ull << 32); }
|
||||
|
||||
bool GetHIConstant() {
|
||||
return regIsConstant & (1ull << 33);
|
||||
}
|
||||
bool GetHIConstant() { return regIsConstant & (1ull << 33); }
|
||||
|
||||
void SetLOConstant() {
|
||||
regIsConstant |= (1ull << 32);
|
||||
}
|
||||
void SetLOConstant() { regIsConstant |= (1ull << 32); }
|
||||
|
||||
void SetHIConstant() {
|
||||
regIsConstant |= (1ull << 33);
|
||||
}
|
||||
void SetHIConstant() { regIsConstant |= (1ull << 33); }
|
||||
|
||||
void UnsetLOConstant() {
|
||||
regIsConstant &= ~(1ull << 32);
|
||||
}
|
||||
void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
|
||||
|
||||
void UnsetHIConstant() {
|
||||
regIsConstant &= ~(1ull << 33);
|
||||
}
|
||||
|
||||
JIT *jit = nullptr;
|
||||
void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
|
||||
|
||||
uint64_t regIsConstant = 0;
|
||||
|
||||
@@ -76,5 +66,10 @@ struct Registers {
|
||||
void Write(size_t, Xbyak::Reg);
|
||||
|
||||
std::array<s64, 32> gpr{};
|
||||
|
||||
static inline const char *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"};
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
+51
-57
@@ -1,26 +1,16 @@
|
||||
#include <Debugger.hpp>
|
||||
#include <imgui.h>
|
||||
#include <Registers.hpp>
|
||||
|
||||
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();
|
||||
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)) {
|
||||
if (ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
|
||||
core.ToggleBreakpoint(addr);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
@@ -30,8 +20,8 @@ void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult &disasm) {
|
||||
if (!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||
return;
|
||||
}
|
||||
@@ -39,26 +29,26 @@ void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||
}
|
||||
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
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())
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (disasm.ops[i].str.empty())
|
||||
continue;
|
||||
|
||||
if(i >= 2) {
|
||||
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())
|
||||
if (!disasm.ops[i + 1].str.empty())
|
||||
op_str += ", ";
|
||||
|
||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
||||
@@ -67,10 +57,13 @@ void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
}
|
||||
|
||||
void Debugger::RegisterView() {
|
||||
if(!ImGui::BeginTabItem("Registers"))
|
||||
if (!ImGui::BeginTabItem("Registers"))
|
||||
return;
|
||||
|
||||
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
|
||||
if (!ImGui::BeginTable("##regs", 4,
|
||||
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_ContextMenuInBody))
|
||||
return;
|
||||
|
||||
ImGui::TableSetupColumn("Name");
|
||||
@@ -81,37 +74,37 @@ void Debugger::RegisterView() {
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto renderMemoryTable = [&](u64 vaddr) {
|
||||
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
||||
if (!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
||||
return;
|
||||
|
||||
if(!ImGui::BeginTooltip())
|
||||
if (!ImGui::BeginTooltip())
|
||||
return;
|
||||
|
||||
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
||||
if(!ImGui::BeginTable("##memoryContents", 16))
|
||||
if (!ImGui::BeginTable("##memoryContents", 16))
|
||||
return;
|
||||
|
||||
for(u32 col = 0; col < 16; col++)
|
||||
for (u32 col = 0; col < 16; col++)
|
||||
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for(u32 row = 0; row < 16; row++) {
|
||||
for (u32 row = 0; row < 16; row++) {
|
||||
ImGui::TableNextRow();
|
||||
for(u32 col = 0; col < 16; col+=4) {
|
||||
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::TableSetColumnIndex(col + 0);
|
||||
ImGui::Text("%02X", (val >> 24) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+1);
|
||||
ImGui::TableSetColumnIndex(col + 1);
|
||||
ImGui::Text("%02X", (val >> 16) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+2);
|
||||
ImGui::TableSetColumnIndex(col + 2);
|
||||
ImGui::Text("%02X", (val >> 8) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+3);
|
||||
ImGui::TableSetColumnIndex(col + 3);
|
||||
ImGui::Text("%02X", (val >> 0) & 0xff);
|
||||
}
|
||||
}
|
||||
@@ -120,13 +113,13 @@ void Debugger::RegisterView() {
|
||||
ImGui::EndTooltip();
|
||||
};
|
||||
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
n64::Registers ®s = n64::Core::GetRegs();
|
||||
|
||||
for(int i = 0; i < 32; i+=2) {
|
||||
for (int i = 0; i < 32; i += 2) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("%s", regNames[i]);
|
||||
ImGui::Text("%s", n64::Registers::regNames[i]);
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
auto value = regs.Read<u64>(i);
|
||||
@@ -134,10 +127,10 @@ void Debugger::RegisterView() {
|
||||
renderMemoryTable(value);
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%s", regNames[i+1]);
|
||||
ImGui::Text("%s", n64::Registers::regNames[i + 1]);
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
value = regs.Read<u64>(i+1);
|
||||
value = regs.Read<u64>(i + 1);
|
||||
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
||||
renderMemoryTable(value);
|
||||
}
|
||||
@@ -148,41 +141,42 @@ void Debugger::RegisterView() {
|
||||
|
||||
bool Debugger::render() {
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
const n64::Registers& regs = n64::Core::GetRegs();
|
||||
const n64::Registers ®s = n64::Core::GetRegs();
|
||||
|
||||
if(!enabled)
|
||||
if (!enabled)
|
||||
return false;
|
||||
|
||||
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
||||
constexpr int step = 4;
|
||||
constexpr int stepFast = 256;
|
||||
|
||||
if(!ImGui::Begin("Debugger", &enabled)) {
|
||||
if (!ImGui::Begin("Debugger", &enabled)) {
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(followPC);
|
||||
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX",
|
||||
ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text("Follow program counter:");
|
||||
ImGui::SameLine(0,0);
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::Checkbox("##followPC", &followPC);
|
||||
ImGui::SameLine(0,0);
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::Text("Add a breakpoint");
|
||||
ImGui::SameLine(0,0);
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
if(followPC)
|
||||
if (followPC)
|
||||
startAddr = regs.pc - 256; // TODO: arbitrary???
|
||||
|
||||
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
||||
core.ToggleBreakpoint(startAddr);
|
||||
}
|
||||
|
||||
if(!ImGui::BeginTabBar("##debuggerTabs")) {
|
||||
if (!ImGui::BeginTabBar("##debuggerTabs")) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
@@ -190,38 +184,38 @@ bool Debugger::render() {
|
||||
|
||||
RegisterView();
|
||||
|
||||
if(!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
||||
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;
|
||||
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
||||
|
||||
if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto &[name, _] : columns)
|
||||
for (auto &[name, _] : columns)
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
||||
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) {
|
||||
if (addrIsCurrent) {
|
||||
colorChoice = 0x80e27fbc;
|
||||
colorChoiceAlt = 0x80e27fbc;
|
||||
}
|
||||
|
||||
if(addrIsBreakpoint) {
|
||||
if (addrIsBreakpoint) {
|
||||
colorChoice = 0x800000ff;
|
||||
colorChoiceAlt = 0x800000ff;
|
||||
}
|
||||
@@ -230,7 +224,7 @@ bool Debugger::render() {
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
for(int i = 0; auto &[_, func] : columns) {
|
||||
for (int i = 0; auto &[_, func] : columns) {
|
||||
ImGui::TableSetColumnIndex(i++);
|
||||
func(addr, disasm);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
#pragma once
|
||||
#include <backend/Core.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
|
||||
void BreakpointFunc(s64, Disassembler::DisassemblyResult&);
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult&);
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult&);
|
||||
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;
|
||||
const char *name = nullptr;
|
||||
void (*func)(s64, Disassembler::DisassemblyResult &) = nullptr;
|
||||
};
|
||||
|
||||
std::array<Column, 3> columns = {
|
||||
@@ -19,10 +20,14 @@ class Debugger final {
|
||||
Column{"Address", &AddressFunc},
|
||||
Column{"Instruction", &InstructionFunc},
|
||||
};
|
||||
public:
|
||||
|
||||
public:
|
||||
static void RegisterView();
|
||||
bool followPC = true;
|
||||
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; }
|
||||
void Open(bool wantFollowPC = true) {
|
||||
enabled = true;
|
||||
followPC = wantFollowPC;
|
||||
}
|
||||
void Close() { enabled = false; }
|
||||
bool render();
|
||||
};
|
||||
@@ -12,12 +12,13 @@ CPUSettings::CPUSettings() {
|
||||
}
|
||||
|
||||
void CPUSettings::render() {
|
||||
const char* items[] = {
|
||||
"Interpreter",
|
||||
const char *items[] = {"Interpreter",
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
"Dynamic Recompiler"
|
||||
#endif
|
||||
};
|
||||
|
||||
const char* combo_preview_value = items[selectedCpuTypeIndex];
|
||||
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
||||
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
|
||||
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
|
||||
const bool is_selected = (selectedCpuTypeIndex == n);
|
||||
@@ -33,8 +34,8 @@ void CPUSettings::render() {
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if(modified) {
|
||||
if(selectedCpuTypeIndex == 0) {
|
||||
if (modified) {
|
||||
if (selectedCpuTypeIndex == 0) {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
|
||||
} else {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||
|
||||
+34
-32
@@ -21,7 +21,9 @@ struct Instruction {
|
||||
inline u8 vd() const { return fd(); }
|
||||
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
||||
inline u8 e2() const { return rs() & 0x0f; }
|
||||
inline u8 op() const { return rt(); }
|
||||
inline u16 imm() const { return instr.itype.imm; }
|
||||
inline u16 offset() const { return imm(); }
|
||||
inline u32 target() const { return instr.jtype.target; }
|
||||
inline u8 opcode() const { return instr.opcode.op; }
|
||||
inline u8 special() const { return instr.opcode.special; }
|
||||
@@ -32,61 +34,61 @@ struct Instruction {
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned imm:16;
|
||||
unsigned rt:5;
|
||||
unsigned rs:5;
|
||||
unsigned op:6;
|
||||
unsigned imm : 16;
|
||||
unsigned rt : 5;
|
||||
unsigned rs : 5;
|
||||
unsigned op : 6;
|
||||
} itype;
|
||||
|
||||
struct {
|
||||
unsigned target:26;
|
||||
unsigned op:6;
|
||||
unsigned target : 26;
|
||||
unsigned op : 6;
|
||||
} jtype;
|
||||
|
||||
struct {
|
||||
unsigned funct:6;
|
||||
unsigned sa:5;
|
||||
unsigned rd:5;
|
||||
unsigned rt:5;
|
||||
unsigned rs:5;
|
||||
unsigned op:6;
|
||||
unsigned funct : 6;
|
||||
unsigned sa : 5;
|
||||
unsigned rd : 5;
|
||||
unsigned rt : 5;
|
||||
unsigned rs : 5;
|
||||
unsigned op : 6;
|
||||
} rtype;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned special_lo:3;
|
||||
unsigned special_hi:3;
|
||||
unsigned:26;
|
||||
unsigned special_lo : 3;
|
||||
unsigned special_hi : 3;
|
||||
unsigned : 26;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned special:6;
|
||||
unsigned:26;
|
||||
unsigned special : 6;
|
||||
unsigned : 26;
|
||||
};
|
||||
struct {
|
||||
unsigned:16;
|
||||
unsigned regimm_lo:3;
|
||||
unsigned regimm_hi:2;
|
||||
unsigned:11;
|
||||
unsigned : 16;
|
||||
unsigned regimm_lo : 3;
|
||||
unsigned regimm_hi : 2;
|
||||
unsigned : 11;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned:16;
|
||||
unsigned regimm:5;
|
||||
unsigned:11;
|
||||
unsigned : 16;
|
||||
unsigned regimm : 5;
|
||||
unsigned : 11;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned:26;
|
||||
unsigned op:6;
|
||||
unsigned : 26;
|
||||
unsigned op : 6;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned funct:6;
|
||||
unsigned:10;
|
||||
unsigned cop_rt:5;
|
||||
unsigned cop_rs:5;
|
||||
unsigned:6;
|
||||
unsigned funct : 6;
|
||||
unsigned : 10;
|
||||
unsigned cop_rt : 5;
|
||||
unsigned cop_rs : 5;
|
||||
unsigned : 6;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
@@ -221,4 +223,4 @@ struct Instruction {
|
||||
static constexpr u8 BLTZALL = 0b10010;
|
||||
static constexpr u8 BGEZALL = 0b10011;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
Reference in New Issue
Block a user