- 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:
2026-04-28 18:01:43 +02:00
parent 68e613057e
commit e140a6d124
19 changed files with 2868 additions and 2835 deletions
+7
View File
@@ -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")
+6 -4
View File
@@ -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;
}
+19 -9
View File
@@ -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;
+13 -14
View File
@@ -1,18 +1,16 @@
#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();
@@ -21,13 +19,9 @@ struct Core {
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);
@@ -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 -2
View File
@@ -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
+11 -8
View File
@@ -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 {
struct Interpreter final {
explicit Interpreter(Mem &, Registers &);
~Interpreter() override = default;
u32 Step() override;
~Interpreter() = default;
u32 Step();
void Reset() override {
cop2Latch = {};
}
void Reset() { cop2Latch = {}; }
private:
InstructionCache icache;
DataCache dcache;
Registers &regs;
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);
+14 -17
View File
@@ -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);
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;
}
@@ -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;
@@ -177,9 +175,9 @@ u32 JIT::Step() {
return 0;
if (InstrEndsBlock(delay_instruction.value())) {
Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
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;
}
@@ -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
+26 -38
View File
@@ -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>(&regs.hi) - reinterpret_cast<uintptr_t>(this))
#define LO_OFFSET (reinterpret_cast<uintptr_t>(&regs.lo) - reinterpret_cast<uintptr_t>(this))
#ifdef __aarch64__
struct JIT : BaseCPU {};
#else
struct JIT final : BaseCPU {
struct JIT final {
explicit JIT(Mem &, Registers &);
~JIT() override = default;
u32 Step() override;
~JIT() = default;
u32 Step();
void Reset() override {
void Reset() {
code.reset();
blockCache = {};
blockCache.resize(kUpperSize);
@@ -47,6 +45,7 @@ struct JIT final : BaseCPU {
}
void InvalidateBlock(u32);
private:
friend struct Cop1;
friend struct Registers;
@@ -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};
}
@@ -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
+132 -114
View File
@@ -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);
}
}
}
@@ -148,13 +153,15 @@ u8 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr);
if (ircolib::IsInsideRange(paddr, 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, 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);
@@ -169,16 +176,20 @@ 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, 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;
}
@@ -187,25 +198,32 @@ u16 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr);
if (ircolib::IsInsideRange(paddr, 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, 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));
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;
}
@@ -214,26 +232,33 @@ u32 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr);
if (ircolib::IsInsideRange(paddr, 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, 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);
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, 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;
}
@@ -242,35 +267,49 @@ u64 Mem::Read(const u32 paddr) {
n64::Registers &regs = n64::Core::GetRegs();
const SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr);
if (ircolib::IsInsideRange(paddr, 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, 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);
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, 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) {
void Mem::Write<u8>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; }
#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;
@@ -286,7 +325,8 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u8>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u8>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (8 * (3 - (paddr & 3)));
@@ -300,31 +340,25 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
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) {
void Mem::Write<u16>(u32 paddr, u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; }
#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;
@@ -340,7 +374,8 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u16>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u16>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
val = val << (16 * !(paddr & 2));
@@ -354,31 +389,25 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
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) {
void Mem::Write<u32>(const u32 paddr, const u32 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; }
#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);
@@ -392,7 +421,10 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { mmio.Write(paddr, val); return; }
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
mmio.Write(paddr, val);
return;
}
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -404,40 +436,24 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
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) {
void Mem::Write(const u32 paddr, u64 val) {
n64::Registers &regs = n64::Core::GetRegs();
SI &si = mmio.si;
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; }
#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;
@@ -452,7 +468,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
}
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u64>!");
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
panic("MMIO Write<u64>!");
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
@@ -464,7 +481,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
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);
}
+9 -11
View File
@@ -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,6 +128,7 @@ struct Mem {
ROM rom;
SaveType saveType = SAVE_NONE;
Flash flash;
private:
friend struct SI;
friend struct PI;
@@ -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
+9 -4
View File
@@ -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));
}
}
@@ -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
+26 -2
View File
@@ -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()));
@@ -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
+44 -53
View File
@@ -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));
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));
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));
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));
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));
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));
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));
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));
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 <>
+16 -21
View File
@@ -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
+11 -17
View File
@@ -1,16 +1,6 @@
#include <Debugger.hpp>
#include <imgui.h>
char const* regNames[] = {
"zero", "at", "v0", "v1",
"a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3",
"t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3",
"s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1",
"gp", "sp", "s8", "ra",
};
#include <Registers.hpp>
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
n64::Core &core = n64::Core::GetInstance();
@@ -70,7 +60,10 @@ void Debugger::RegisterView() {
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");
@@ -126,7 +119,7 @@ void Debugger::RegisterView() {
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,7 +127,7 @@ 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);
@@ -163,7 +156,8 @@ bool Debugger::render() {
}
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:");
@@ -196,8 +190,8 @@ bool Debugger::render() {
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)) {
ImGui::EndTabBar();
+6 -1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <backend/Core.hpp>
#include <Disassembler.hpp>
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
void AddressFunc(s64, Disassembler::DisassemblyResult &);
@@ -19,10 +20,14 @@ class Debugger final {
Column{"Address", &AddressFunc},
Column{"Instruction", &InstructionFunc},
};
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();
};
+3 -2
View File
@@ -12,9 +12,10 @@ 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];
+3 -1
View File
@@ -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; }
@@ -221,4 +223,4 @@ struct Instruction {
static constexpr u8 BLTZALL = 0b10010;
static constexpr u8 BGEZALL = 0b10011;
};
}
} // namespace n64