- 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)
|
enable_language(OBJC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(USE_JIT FALSE)
|
||||||
set(VULKAN_VALIDATION FALSE)
|
set(VULKAN_VALIDATION FALSE)
|
||||||
set(SANITIZERS FALSE)
|
set(SANITIZERS FALSE)
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
@@ -115,6 +116,9 @@ endif()
|
|||||||
set(SIMD_FLAG NULL)
|
set(SIMD_FLAG NULL)
|
||||||
|
|
||||||
if(ARM64)
|
if(ARM64)
|
||||||
|
if(USE_JIT)
|
||||||
|
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
|
||||||
|
endif()
|
||||||
message("Defining USE_NEON...")
|
message("Defining USE_NEON...")
|
||||||
add_compile_definitions(USE_NEON)
|
add_compile_definitions(USE_NEON)
|
||||||
add_compile_definitions(SIMD_SUPPORT)
|
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_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)
|
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||||
|
if(USE_JIT)
|
||||||
|
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (SANITIZERS)
|
if (SANITIZERS)
|
||||||
message("UBSAN AND ASAN: ON")
|
message("UBSAN AND ASAN: ON")
|
||||||
|
|||||||
+6
-4
@@ -34,7 +34,8 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
|
|||||||
new_context->set_num_thread_indices(num_thread_indices);
|
new_context->set_num_thread_indices(num_thread_indices);
|
||||||
new_context->set_system_handles(system_handles);
|
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");
|
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 sizeVert = sizeof(vertex_shader);
|
||||||
constexpr auto sizeFrag = sizeof(fragment_shader);
|
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||||
|
|
||||||
fullscreen_quad_program = wsi->get_device().request_program(
|
fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
|
||||||
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
reinterpret_cast<const u32 *>(fragment_shader),
|
||||||
sizeFrag, &vertLayout, &fragLayout);
|
sizeFrag, &vertLayout, &fragLayout);
|
||||||
|
|
||||||
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
||||||
uintptr_t offset = 0;
|
uintptr_t offset = 0;
|
||||||
|
|
||||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
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;
|
offset = aligned_rdram & align - 1;
|
||||||
aligned_rdram -= offset;
|
aligned_rdram -= offset;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-9
@@ -4,18 +4,18 @@
|
|||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
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");
|
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||||
if (selectedCpu == "interpreter") {
|
if (selectedCpu == "interpreter") {
|
||||||
cpuType = Interpreted;
|
cpuType = Interpreted;
|
||||||
cpu = std::make_unique<Interpreter>(*mem, regs);
|
|
||||||
} else if (selectedCpu == "jit") {
|
} else if (selectedCpu == "jit") {
|
||||||
#ifndef __aarch64__
|
|
||||||
cpuType = DynamicRecompiler;
|
cpuType = DynamicRecompiler;
|
||||||
cpu = std::make_unique<JIT>(*mem, regs);
|
|
||||||
#else
|
|
||||||
panic("JIT currently unsupported on aarch64");
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
panic("Unimplemented CPU type");
|
panic("Unimplemented CPU type");
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ void Core::Stop() {
|
|||||||
void Core::Reset() {
|
void Core::Reset() {
|
||||||
regs.Reset();
|
regs.Reset();
|
||||||
mem->Reset();
|
mem->Reset();
|
||||||
cpu->Reset();
|
interpreter.Reset();
|
||||||
if (romLoaded)
|
if (romLoaded)
|
||||||
mem->mmio.si.pif.Execute();
|
mem->mmio.si.pif.Execute();
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,17 @@ void Core::LoadROM(const std::string &rom_) {
|
|||||||
romLoaded = true;
|
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) {
|
void Core::StepRSP(const u32 cpuCycles) {
|
||||||
MMIO &mmio = mem->mmio;
|
MMIO &mmio = mem->mmio;
|
||||||
|
|||||||
+13
-14
@@ -1,18 +1,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <ParallelRDPWrapper.hpp>
|
#include <ParallelRDPWrapper.hpp>
|
||||||
#include <backend/core/Interpreter.hpp>
|
#include <backend/core/Interpreter.hpp>
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
#include <backend/core/JIT.hpp>
|
#include <backend/core/JIT.hpp>
|
||||||
|
#endif
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <variant>
|
#include <Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core {
|
struct Core {
|
||||||
enum CPUType {
|
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted;
|
||||||
Interpreted,
|
|
||||||
DynamicRecompiler,
|
|
||||||
CachedInterpreter
|
|
||||||
} cpuType = Interpreted;
|
|
||||||
|
|
||||||
explicit Core();
|
explicit Core();
|
||||||
|
|
||||||
@@ -21,13 +19,9 @@ struct Core {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Registers& GetRegs() {
|
static Registers &GetRegs() { return GetInstance().regs; }
|
||||||
return GetInstance().regs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Mem& GetMem() {
|
static Mem &GetMem() { return *GetInstance().mem; }
|
||||||
return *GetInstance().mem;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 StepCPU();
|
u32 StepCPU();
|
||||||
void StepRSP(u32 cpuCycles);
|
void StepRSP(u32 cpuCycles);
|
||||||
@@ -53,10 +47,15 @@ struct Core {
|
|||||||
size_t memSize{}, cpuSize{}, verSize{};
|
size_t memSize{}, cpuSize{}, verSize{};
|
||||||
std::string rom;
|
std::string rom;
|
||||||
std::set<s64> breakpoints{};
|
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<Mem> mem = std::make_unique<Mem>();
|
||||||
std::unique_ptr<BaseCPU> cpu;
|
|
||||||
|
|
||||||
Registers regs;
|
Registers regs;
|
||||||
|
#endif
|
||||||
|
Interpreter interpreter;
|
||||||
ParallelRDP parallel;
|
ParallelRDP parallel;
|
||||||
};
|
};
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
|
|||||||
file(GLOB HEADERS *.hpp)
|
file(GLOB HEADERS *.hpp)
|
||||||
|
|
||||||
add_subdirectory(interpreter)
|
add_subdirectory(interpreter)
|
||||||
if(NOT ARM64)
|
if(USE_JIT)
|
||||||
add_subdirectory(jit)
|
add_subdirectory(jit)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(mem)
|
add_subdirectory(mem)
|
||||||
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
|
|||||||
|
|
||||||
add_library(core ${SOURCES} ${HEADERS})
|
add_library(core ${SOURCES} ${HEADERS})
|
||||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||||
if(NOT ARM64)
|
if(USE_JIT)
|
||||||
target_link_libraries(core PRIVATE jit)
|
target_link_libraries(core PRIVATE jit)
|
||||||
endif()
|
endif()
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Mem.hpp>
|
#include <common.hpp>
|
||||||
#include <Registers.hpp>
|
|
||||||
#include <Disassembler.hpp>
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct alignas(32) InstructionCache {
|
struct alignas(32) InstructionCache {
|
||||||
@@ -10,6 +8,7 @@ struct alignas(32) InstructionCache {
|
|||||||
u32 ptag;
|
u32 ptag;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct Interpreter;
|
||||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||||
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||||
};
|
};
|
||||||
@@ -21,13 +20,8 @@ struct alignas(32) DataCache {
|
|||||||
int index;
|
int index;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend struct Interpreter;
|
||||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||||
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BaseCPU {
|
|
||||||
virtual ~BaseCPU() = default;
|
|
||||||
virtual u32 Step() = 0;
|
|
||||||
virtual void Reset() = 0;
|
|
||||||
};
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
@@ -1,25 +1,27 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <BaseCPU.hpp>
|
#include <Cache.hpp>
|
||||||
#include <Mem.hpp>
|
#include <Mem.hpp>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|
||||||
struct Interpreter final : BaseCPU {
|
struct Interpreter final {
|
||||||
explicit Interpreter(Mem &, Registers &);
|
explicit Interpreter(Mem &, Registers &);
|
||||||
~Interpreter() override = default;
|
~Interpreter() = default;
|
||||||
u32 Step() override;
|
u32 Step();
|
||||||
|
|
||||||
void Reset() override {
|
void Reset() { cop2Latch = {}; }
|
||||||
cop2Latch = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
InstructionCache icache;
|
||||||
|
DataCache dcache;
|
||||||
Registers ®s;
|
Registers ®s;
|
||||||
Mem &mem;
|
Mem &mem;
|
||||||
u64 cop2Latch{};
|
u64 cop2Latch{};
|
||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
|
|
||||||
|
void cache_type_data(u8);
|
||||||
|
void cache_type_instruction(u8);
|
||||||
#define check_address_error(mask, vaddr) \
|
#define check_address_error(mask, vaddr) \
|
||||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||||
@@ -41,6 +43,7 @@ private:
|
|||||||
void blink(Instruction, bool);
|
void blink(Instruction, bool);
|
||||||
void bl(Instruction, bool);
|
void bl(Instruction, bool);
|
||||||
void bllink(Instruction, bool);
|
void bllink(Instruction, bool);
|
||||||
|
void cache(Instruction);
|
||||||
void dadd(Instruction);
|
void dadd(Instruction);
|
||||||
void daddu(Instruction);
|
void daddu(Instruction);
|
||||||
void daddi(Instruction);
|
void daddi(Instruction);
|
||||||
|
|||||||
+14
-17
@@ -1,11 +1,9 @@
|
|||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <jit/helpers.hpp>
|
#include <JIT.hpp>
|
||||||
|
#include <Disassembler.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
#ifndef __aarch64__
|
|
||||||
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
||||||
regs.SetJIT(this);
|
|
||||||
mem.SetJIT(this);
|
|
||||||
blockCache.resize(kUpperSize);
|
blockCache.resize(kUpperSize);
|
||||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
||||||
CS_ERR_OK) {
|
CS_ERR_OK) {
|
||||||
@@ -47,8 +45,8 @@ std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
|||||||
/*regs.cop0.HandleTLBException(blockPC);
|
/*regs.cop0.HandleTLBException(blockPC);
|
||||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
||||||
return 1;*/
|
return 1;*/
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
blockPC, {},
|
||||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -118,8 +116,8 @@ u32 JIT::Step() {
|
|||||||
/*regs.cop0.HandleTLBException(blockPC);
|
/*regs.cop0.HandleTLBException(blockPC);
|
||||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||||
return 1;*/
|
return 1;*/
|
||||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
Util::Error::GetInstance().Throw(
|
||||||
blockPC, {},
|
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -177,9 +175,9 @@ u32 JIT::Step() {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (InstrEndsBlock(delay_instruction.value())) {
|
if (InstrEndsBlock(delay_instruction.value())) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +204,8 @@ u32 JIT::Step() {
|
|||||||
code.L(runtime_branch_taken);
|
code.L(runtime_branch_taken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(branch_taken) branch_taken = false;
|
if (branch_taken)
|
||||||
|
branch_taken = false;
|
||||||
|
|
||||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||||
|
|
||||||
@@ -227,7 +226,8 @@ u32 JIT::Step() {
|
|||||||
blockInfoSize = code.getSize() - blockInfoSize;
|
blockInfoSize = code.getSize() - blockInfoSize;
|
||||||
|
|
||||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
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) {
|
if (count > 0) {
|
||||||
for (size_t j = 0; j < count; j++) {
|
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);
|
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();
|
return block();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JIT::DumpBlockCacheToDisk() const {
|
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
|
||||||
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+26
-38
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <BaseCPU.hpp>
|
#include <Cache.hpp>
|
||||||
|
#include <Registers.hpp>
|
||||||
#include <Mem.hpp>
|
#include <Mem.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <xbyak.h>
|
#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 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))
|
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
||||||
|
|
||||||
#ifdef __aarch64__
|
struct JIT final {
|
||||||
struct JIT : BaseCPU {};
|
|
||||||
#else
|
|
||||||
struct JIT final : BaseCPU {
|
|
||||||
explicit JIT(Mem &, Registers &);
|
explicit JIT(Mem &, Registers &);
|
||||||
~JIT() override = default;
|
~JIT() = default;
|
||||||
u32 Step() override;
|
u32 Step();
|
||||||
|
|
||||||
void Reset() override {
|
void Reset() {
|
||||||
code.reset();
|
code.reset();
|
||||||
blockCache = {};
|
blockCache = {};
|
||||||
blockCache.resize(kUpperSize);
|
blockCache.resize(kUpperSize);
|
||||||
@@ -47,6 +45,7 @@ struct JIT final : BaseCPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateBlock(u32);
|
void InvalidateBlock(u32);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct Cop1;
|
friend struct Cop1;
|
||||||
friend struct Registers;
|
friend struct Registers;
|
||||||
@@ -73,9 +72,9 @@ private:
|
|||||||
return code.qword[code.rbp + GPR_OFFSET(index)];
|
return code.qword[code.rbp + GPR_OFFSET(index)];
|
||||||
}
|
}
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||||
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
|
{Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
"[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
||||||
return Xbyak::Address{0};
|
return Xbyak::Address{0};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,54 +204,44 @@ private:
|
|||||||
void mtlo(Instruction);
|
void mtlo(Instruction);
|
||||||
void nor(Instruction);
|
void nor(Instruction);
|
||||||
void sb(const Instruction) {
|
void sb(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
||||||
}
|
}
|
||||||
void sc(const Instruction) {
|
void sc(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
||||||
}
|
}
|
||||||
void scd(const Instruction) {
|
void scd(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
||||||
}
|
}
|
||||||
void sd(const Instruction) {
|
void sd(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
||||||
}
|
}
|
||||||
void sdc1(const Instruction) {
|
void sdc1(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
||||||
}
|
}
|
||||||
void sdl(const Instruction) {
|
void sdl(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
||||||
}
|
}
|
||||||
void sdr(const Instruction) {
|
void sdr(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
||||||
}
|
}
|
||||||
void sh(const Instruction) {
|
void sh(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
||||||
}
|
}
|
||||||
void sw(Instruction);
|
void sw(Instruction);
|
||||||
void swl(const Instruction) {
|
void swl(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
||||||
}
|
}
|
||||||
void swr(const Instruction) {
|
void swr(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
|
||||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
||||||
}
|
}
|
||||||
void slti(Instruction);
|
void slti(Instruction);
|
||||||
@@ -264,23 +253,22 @@ private:
|
|||||||
void sub(Instruction);
|
void sub(Instruction);
|
||||||
void subu(Instruction);
|
void subu(Instruction);
|
||||||
void swc1(const Instruction) {
|
void swc1(const Instruction) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void sra(Instruction);
|
void sra(Instruction);
|
||||||
void srav(Instruction);
|
void srav(Instruction);
|
||||||
void srl(Instruction);
|
void srl(Instruction);
|
||||||
void srlv(Instruction);
|
void srlv(Instruction);
|
||||||
void trap(bool) {
|
void trap(bool) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
"[JIT]: Unhandled case of branch from delay slot!");
|
||||||
}
|
}
|
||||||
void or_(Instruction);
|
void or_(Instruction);
|
||||||
void ori(Instruction);
|
void ori(Instruction);
|
||||||
void xor_(Instruction);
|
void xor_(Instruction);
|
||||||
void xori(Instruction);
|
void xori(Instruction);
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
+132
-114
@@ -4,9 +4,14 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <Core.hpp>
|
#include <Core.hpp>
|
||||||
#include <Options.hpp>
|
#include <Options.hpp>
|
||||||
|
#include <Registers.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
|
Mem::Mem(JIT &jit) : flash(saveData), jit(jit) {
|
||||||
|
#else
|
||||||
Mem::Mem() : flash(saveData) {
|
Mem::Mem() : flash(saveData) {
|
||||||
|
#endif
|
||||||
rom.cart.resize(CART_SIZE);
|
rom.cart.resize(CART_SIZE);
|
||||||
std::ranges::fill(rom.cart, 0);
|
std::ranges::fill(rom.cart, 0);
|
||||||
isviewer_sink = std::ofstream("isviewer.log");
|
isviewer_sink = std::ofstream("isviewer.log");
|
||||||
@@ -19,9 +24,9 @@ void Mem::Reset() {
|
|||||||
std::error_code error;
|
std::error_code error;
|
||||||
saveData.sync(error);
|
saveData.sync(error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
|
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
||||||
{}, {}, "[Mem]: Could not sync save data!");
|
"[Mem]: Could not sync save data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -40,9 +45,9 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
|||||||
if (saveData.is_mapped()) {
|
if (saveData.is_mapped()) {
|
||||||
saveData.sync(error);
|
saveData.sync(error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA},
|
{Util::Error::Type::COULD_NOT_SYNC_SAVE_DATA}, {}, {},
|
||||||
{}, {}, R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
R"([Mem]: Could not sync save data stored @ "{}")", sramPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
saveData.unmap();
|
saveData.unmap();
|
||||||
@@ -56,16 +61,16 @@ void Mem::LoadSRAM(SaveType save_type, fs::path path) {
|
|||||||
|
|
||||||
if (sramVec.size() != SRAM_SIZE) {
|
if (sramVec.size() != SRAM_SIZE) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw(
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::SAVE_DATA_IS_CORRUPT_OR_INVALID_SIZE},
|
{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);
|
"[Mem]: Save data is corrupt or has unexpected size! (it's {} KiB)", sramVec.size() / 1024);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveData = mio::make_mmap_sink(sramPath, error);
|
saveData = mio::make_mmap_sink(sramPath, error);
|
||||||
if (error) {
|
if (error) {
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MMAP_MAKE_SINK_ERROR},
|
{Util::Error::Type::MMAP_MAKE_SINK_ERROR}, {}, {},
|
||||||
{}, {}, R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
R"([Mem]: Could not create file sink for save data @ "{}")", sramPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,13 +153,15 @@ u8 Mem::Read(const u32 paddr) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
|
return mmio.rdp.ReadRDRAM<u8>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return src[BYTE_ADDRESS(paddr & 0xfff)];
|
return src[BYTE_ADDRESS(paddr & 0xfff)];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u8, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
|
return mmio.pi.BusRead<u8, false>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) {
|
||||||
const u32 w = mmio.ai.Read(paddr & ~3);
|
const u32 w = mmio.ai.Read(paddr & ~3);
|
||||||
const int offs = 3 - (paddr & 3);
|
const int offs = 3 - (paddr & 3);
|
||||||
@@ -169,16 +176,20 @@ u8 Mem::Read(const u32 paddr) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return si.pif.ram[paddr - PIF_RAM_REGION_START];
|
return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START];
|
||||||
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return si.pif.ram[paddr - PIF_RAM_REGION_START];
|
||||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0},
|
||||||
|
"8-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,25 +198,32 @@ u16 Mem::Read(const u32 paddr) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
|
return mmio.rdp.ReadRDRAM<u16>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
|
return ircolib::ReadAccess<u16>(src, HALF_ADDRESS(paddr & 0xfff));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u16, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
|
return mmio.pi.BusRead<u16, false>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
|
return mmio.Read(paddr);
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
|
return ircolib::ReadAccess<u16>(si.pif.bootrom, HALF_ADDRESS(paddr) - PIF_ROM_REGION_START);
|
||||||
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u16>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, "16-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0},
|
||||||
|
"16-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,26 +232,33 @@ u32 Mem::Read(const u32 paddr) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
|
return mmio.rdp.ReadRDRAM<u32>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
|
return ircolib::ReadAccess<u32>(src, paddr & 0xfff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u32, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
|
return mmio.pi.BusRead<u32, false>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
return mmio.Read(paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
return ircolib::ReadAccess<u32>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
||||||
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, "32-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0},
|
||||||
|
"32-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,35 +267,49 @@ u64 Mem::Read(const u32 paddr) {
|
|||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
const SI &si = mmio.si;
|
const SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr);
|
if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END))
|
||||||
|
return mmio.rdp.ReadRDRAM<u64>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
const auto &src = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
|
return ircolib::ReadAccess<u64>(src, paddr & 0xfff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead<u64, false>(paddr);
|
if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2))
|
||||||
|
return mmio.pi.BusRead<u64, false>(paddr);
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr);
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
return mmio.Read(paddr);
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END))
|
||||||
if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
return ircolib::ReadAccess<u64>(si.pif.bootrom, paddr - PIF_ROM_REGION_START);
|
||||||
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END))
|
||||||
|
return std::byteswap(ircolib::ReadAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START));
|
||||||
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return 0;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
Util::Error::GetInstance().Throw(
|
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS},
|
||||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc,
|
regs.pc,
|
||||||
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0}, "64-bit read access in unhandled region");
|
Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::DWORD, paddr, 0},
|
||||||
|
"64-bit read access in unhandled region");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
void Mem::Write<u8>(u32 paddr, u32 val) {
|
||||||
n64::Registers ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; }
|
#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)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
val = val << (8 * (3 - (paddr & 3)));
|
val = val << (8 * (3 - (paddr & 3)));
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
@@ -286,7 +325,8 @@ void Mem::WriteInterpreter<u8>(u32 paddr, u32 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u8>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u8>!");
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
val = val << (8 * (3 - (paddr & 3)));
|
val = val << (8 * (3 - (paddr & 3)));
|
||||||
@@ -300,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_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteJIT<u8>(const u32 paddr, const u32 val) {
|
void Mem::Write<u16>(u32 paddr, 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 ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; }
|
#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)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
val = val << (16 * !(paddr & 2));
|
val = val << (16 * !(paddr & 2));
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
@@ -340,7 +374,8 @@ void Mem::WriteInterpreter<u16>(u32 paddr, u32 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u16>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u16>!");
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
val = val << (16 * !(paddr & 2));
|
val = val << (16 * !(paddr & 2));
|
||||||
@@ -354,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_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
|
||||||
template <>
|
template <>
|
||||||
void Mem::WriteJIT<u16>(const u32 paddr, const u32 val) {
|
void Mem::Write<u32>(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 ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; }
|
#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)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
ircolib::WriteAccess<u32>(dest, paddr & 0xfff, val);
|
||||||
@@ -392,7 +421,10 @@ void Mem::WriteInterpreter<u32>(const u32 paddr, const u32 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { mmio.Write(paddr, val); return; }
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) {
|
||||||
|
mmio.Write(paddr, val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
ircolib::WriteAccess<u32>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
||||||
@@ -404,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_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
void Mem::Write(const u32 paddr, u64 val) {
|
||||||
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 ®s = n64::Core::GetRegs();
|
n64::Registers ®s = n64::Core::GetRegs();
|
||||||
SI &si = mmio.si;
|
SI &si = mmio.si;
|
||||||
|
|
||||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; }
|
#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)) {
|
if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) {
|
||||||
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem;
|
||||||
val >>= 32;
|
val >>= 32;
|
||||||
@@ -452,7 +468,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) ||
|
||||||
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write<u64>!");
|
ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2))
|
||||||
|
panic("MMIO Write<u64>!");
|
||||||
|
|
||||||
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) {
|
||||||
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
ircolib::WriteAccess<u64>(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val));
|
||||||
@@ -464,7 +481,8 @@ void Mem::WriteInterpreter(const u32 paddr, u64 val) {
|
|||||||
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) ||
|
||||||
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) ||
|
||||||
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return;
|
ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4))
|
||||||
|
return;
|
||||||
|
|
||||||
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,15 +74,19 @@ struct Flash {
|
|||||||
T Read(u32 index) const;
|
T Read(u32 index) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
struct JIT;
|
struct JIT;
|
||||||
|
|
||||||
struct Mem {
|
struct Mem {
|
||||||
~Mem() = default;
|
Mem(JIT &jit);
|
||||||
|
JIT &jit;
|
||||||
|
#else
|
||||||
|
struct Mem {
|
||||||
Mem();
|
Mem();
|
||||||
|
~Mem() = default;
|
||||||
void Reset();
|
void Reset();
|
||||||
void LoadSRAM(SaveType, fs::path);
|
void LoadSRAM(SaveType, fs::path);
|
||||||
void LoadROM(bool, const std::string &);
|
void LoadROM(bool, const std::string &);
|
||||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
|
||||||
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
||||||
|
|
||||||
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
||||||
@@ -124,6 +128,7 @@ struct Mem {
|
|||||||
ROM rom;
|
ROM rom;
|
||||||
SaveType saveType = SAVE_NONE;
|
SaveType saveType = SAVE_NONE;
|
||||||
Flash flash;
|
Flash flash;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct SI;
|
friend struct SI;
|
||||||
friend struct PI;
|
friend struct PI;
|
||||||
@@ -132,17 +137,9 @@ private:
|
|||||||
friend struct JIT;
|
friend struct JIT;
|
||||||
friend struct Core;
|
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::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||||
std::ofstream isviewer_sink{};
|
std::ofstream isviewer_sink{};
|
||||||
int mmioSize{}, flashSize{};
|
int mmioSize{}, flashSize{};
|
||||||
JIT *jit = nullptr;
|
|
||||||
std::string sramPath{};
|
std::string sramPath{};
|
||||||
mio::mmap_sink saveData{};
|
mio::mmap_sink saveData{};
|
||||||
|
|
||||||
@@ -151,4 +148,5 @@ private:
|
|||||||
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
} // namespace n64
|
} // namespace n64
|
||||||
|
|||||||
@@ -164,8 +164,8 @@ void Interpreter::special(const Instruction instr) {
|
|||||||
dsra32(instr);
|
dsra32(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi, instr.instr.opcode.special_lo, instr.instr.raw,
|
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi,
|
||||||
static_cast<u64>(regs.oldPC));
|
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);
|
swr(instr);
|
||||||
break;
|
break;
|
||||||
case Instruction::CACHE:
|
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:
|
case Instruction::LL:
|
||||||
ll(instr);
|
ll(instr);
|
||||||
break;
|
break;
|
||||||
@@ -449,7 +453,8 @@ void Interpreter::Exec(const Instruction instr) {
|
|||||||
sd(instr);
|
sd(instr);
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
} // namespace n64
|
||||||
|
|||||||
@@ -583,7 +583,9 @@ void Interpreter::ori(const Instruction instr) {
|
|||||||
regs.Write(instr.rt(), result);
|
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) {
|
void Interpreter::nor(const Instruction instr) {
|
||||||
regs.Write(instr.rd(), ~(regs.Read<s64>(instr.rs()) | regs.Read<s64>(instr.rt())));
|
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);
|
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) {
|
void Interpreter::sltu(const Instruction instr) {
|
||||||
regs.Write(instr.rd(), regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
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::ctc2(const Instruction) {}
|
||||||
|
|
||||||
void Interpreter::cfc2(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
|
} // namespace n64
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
#include <core/JIT.hpp>
|
#include <core/JIT.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
|
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
||||||
|
#else
|
||||||
Registers::Registers() { Reset(); }
|
Registers::Registers() { Reset(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
void Registers::Reset() {
|
void Registers::Reset() {
|
||||||
hi = 0;
|
hi = 0;
|
||||||
@@ -72,85 +76,85 @@ s8 Registers::Read<s8>(size_t idx) {
|
|||||||
return static_cast<s8>(Read<u8>(idx));
|
return static_cast<s8>(Read<u8>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt64(), Read<u64>(idx));
|
jit.code.mov(reg.cvt64(), Read<u64>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt64(), Read<s64>(idx));
|
jit.code.mov(reg.cvt64(), Read<s64>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt32(), Read<u32>(idx));
|
jit.code.mov(reg.cvt32(), Read<u32>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx));
|
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt32(), Read<s32>(idx));
|
jit.code.mov(reg.cvt32(), Read<s32>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx));
|
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt16(), Read<u16>(idx));
|
jit.code.mov(reg.cvt16(), Read<u16>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt16(), Read<s16>(idx));
|
jit.code.mov(reg.cvt16(), Read<s16>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt8(), Read<u8>(idx));
|
jit.code.mov(reg.cvt8(), Read<u8>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx));
|
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
||||||
if (IsRegConstant(idx)) {
|
if (IsRegConstant(idx)) {
|
||||||
jit->code.mov(reg.cvt8(), Read<s8>(idx));
|
jit.code.mov(reg.cvt8(), Read<s8>(idx));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx));
|
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -159,7 +163,6 @@ void Registers::Write<bool>(size_t idx, bool v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
@@ -170,8 +173,8 @@ void Registers::Write<u64>(size_t idx, u64 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,8 +188,8 @@ void Registers::Write<u32>(size_t idx, u32 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,8 +199,8 @@ void Registers::Write<s32>(size_t idx, s32 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,8 +209,8 @@ void Registers::Write<u16>(size_t idx, u16 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,8 +220,8 @@ void Registers::Write<s16>(size_t idx, s16 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,8 +230,8 @@ void Registers::Write<u8>(size_t idx, u8 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,24 +241,21 @@ void Registers::Write<s8>(size_t idx, s8 v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jit) [[unlikely]]
|
|
||||||
regIsConstant |= (1 << idx);
|
regIsConstant |= (1 << idx);
|
||||||
|
|
||||||
gpr[idx] = v;
|
gpr[idx] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __aarch64__
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
template <>
|
template <>
|
||||||
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -263,13 +263,10 @@ void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -277,13 +274,10 @@ void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt8());
|
jit.code.movzx(v.cvt64(), v.cvt8());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -291,13 +285,10 @@ void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
|
|||||||
if (idx == 0)
|
if (idx == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!jit)
|
|
||||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
|
||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsx(v.cvt64(), v.cvt16());
|
jit.code.movsx(v.cvt64(), v.cvt16());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -310,8 +301,8 @@ void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt16());
|
jit.code.movzx(v.cvt64(), v.cvt16());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -324,8 +315,8 @@ void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movsxd(v.cvt64(), v.cvt32());
|
jit.code.movsxd(v.cvt64(), v.cvt32());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -338,8 +329,8 @@ void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.movzx(v.cvt64(), v.cvt32());
|
jit.code.movzx(v.cvt64(), v.cvt32());
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -352,7 +343,7 @@ void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
|
|||||||
|
|
||||||
regIsConstant &= ~(1 << idx);
|
regIsConstant &= ~(1 << idx);
|
||||||
|
|
||||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
@@ -4,13 +4,17 @@
|
|||||||
#include <backend/core/registers/Cop1.hpp>
|
#include <backend/core/registers/Cop1.hpp>
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
struct JIT;
|
struct JIT;
|
||||||
|
struct Registers {
|
||||||
|
JIT &jit;
|
||||||
|
Registers(JIT &jit);
|
||||||
|
#else
|
||||||
struct Registers {
|
struct Registers {
|
||||||
Registers();
|
Registers();
|
||||||
void Reset();
|
void Reset();
|
||||||
void SetPC64(s64);
|
void SetPC64(s64);
|
||||||
void SetPC32(s32);
|
void SetPC32(s32);
|
||||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
|
||||||
|
|
||||||
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||||
if (index == 0)
|
if (index == 0)
|
||||||
@@ -22,31 +26,17 @@ struct Registers {
|
|||||||
return IsRegConstant(index1) && IsRegConstant(index2);
|
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetLOConstant() {
|
bool GetLOConstant() { return regIsConstant & (1ull << 32); }
|
||||||
return regIsConstant & (1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetHIConstant() {
|
bool GetHIConstant() { return regIsConstant & (1ull << 33); }
|
||||||
return regIsConstant & (1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLOConstant() {
|
void SetLOConstant() { regIsConstant |= (1ull << 32); }
|
||||||
regIsConstant |= (1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetHIConstant() {
|
void SetHIConstant() { regIsConstant |= (1ull << 33); }
|
||||||
regIsConstant |= (1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnsetLOConstant() {
|
void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
|
||||||
regIsConstant &= ~(1ull << 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UnsetHIConstant() {
|
void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
|
||||||
regIsConstant &= ~(1ull << 33);
|
|
||||||
}
|
|
||||||
|
|
||||||
JIT *jit = nullptr;
|
|
||||||
|
|
||||||
uint64_t regIsConstant = 0;
|
uint64_t regIsConstant = 0;
|
||||||
|
|
||||||
@@ -76,5 +66,10 @@ struct Registers {
|
|||||||
void Write(size_t, Xbyak::Reg);
|
void Write(size_t, Xbyak::Reg);
|
||||||
|
|
||||||
std::array<s64, 32> gpr{};
|
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
|
} // namespace n64
|
||||||
|
|||||||
+11
-17
@@ -1,16 +1,6 @@
|
|||||||
#include <Debugger.hpp>
|
#include <Debugger.hpp>
|
||||||
#include <imgui.h>
|
#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 &) {
|
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
|
||||||
n64::Core &core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
@@ -70,7 +60,10 @@ void Debugger::RegisterView() {
|
|||||||
if (!ImGui::BeginTabItem("Registers"))
|
if (!ImGui::BeginTabItem("Registers"))
|
||||||
return;
|
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;
|
return;
|
||||||
|
|
||||||
ImGui::TableSetupColumn("Name");
|
ImGui::TableSetupColumn("Name");
|
||||||
@@ -126,7 +119,7 @@ void Debugger::RegisterView() {
|
|||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(0);
|
ImGui::TableSetColumnIndex(0);
|
||||||
ImGui::Text("%s", regNames[i]);
|
ImGui::Text("%s", n64::Registers::regNames[i]);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(1);
|
ImGui::TableSetColumnIndex(1);
|
||||||
auto value = regs.Read<u64>(i);
|
auto value = regs.Read<u64>(i);
|
||||||
@@ -134,7 +127,7 @@ void Debugger::RegisterView() {
|
|||||||
renderMemoryTable(value);
|
renderMemoryTable(value);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(2);
|
ImGui::TableSetColumnIndex(2);
|
||||||
ImGui::Text("%s", regNames[i+1]);
|
ImGui::Text("%s", n64::Registers::regNames[i + 1]);
|
||||||
|
|
||||||
ImGui::TableSetColumnIndex(3);
|
ImGui::TableSetColumnIndex(3);
|
||||||
value = regs.Read<u64>(i + 1);
|
value = regs.Read<u64>(i + 1);
|
||||||
@@ -163,7 +156,8 @@ bool Debugger::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::BeginDisabled(followPC);
|
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::EndDisabled();
|
||||||
|
|
||||||
ImGui::Text("Follow program counter:");
|
ImGui::Text("Follow program counter:");
|
||||||
@@ -196,8 +190,8 @@ bool Debugger::render() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
|
||||||
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
||||||
|
|
||||||
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||||
ImGui::EndTabBar();
|
ImGui::EndTabBar();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
|
#include <Disassembler.hpp>
|
||||||
|
|
||||||
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
|
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
|
||||||
void AddressFunc(s64, Disassembler::DisassemblyResult &);
|
void AddressFunc(s64, Disassembler::DisassemblyResult &);
|
||||||
@@ -19,10 +20,14 @@ class Debugger final {
|
|||||||
Column{"Address", &AddressFunc},
|
Column{"Address", &AddressFunc},
|
||||||
Column{"Instruction", &InstructionFunc},
|
Column{"Instruction", &InstructionFunc},
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void RegisterView();
|
static void RegisterView();
|
||||||
bool followPC = true;
|
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; }
|
void Close() { enabled = false; }
|
||||||
bool render();
|
bool render();
|
||||||
};
|
};
|
||||||
@@ -12,9 +12,10 @@ CPUSettings::CPUSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPUSettings::render() {
|
void CPUSettings::render() {
|
||||||
const char* items[] = {
|
const char *items[] = {"Interpreter",
|
||||||
"Interpreter",
|
#ifdef KAIZEN_JIT_ENABLED
|
||||||
"Dynamic Recompiler"
|
"Dynamic Recompiler"
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ struct Instruction {
|
|||||||
inline u8 vd() const { return fd(); }
|
inline u8 vd() const { return fd(); }
|
||||||
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
||||||
inline u8 e2() const { return rs() & 0x0f; }
|
inline u8 e2() const { return rs() & 0x0f; }
|
||||||
|
inline u8 op() const { return rt(); }
|
||||||
inline u16 imm() const { return instr.itype.imm; }
|
inline u16 imm() const { return instr.itype.imm; }
|
||||||
|
inline u16 offset() const { return imm(); }
|
||||||
inline u32 target() const { return instr.jtype.target; }
|
inline u32 target() const { return instr.jtype.target; }
|
||||||
inline u8 opcode() const { return instr.opcode.op; }
|
inline u8 opcode() const { return instr.opcode.op; }
|
||||||
inline u8 special() const { return instr.opcode.special; }
|
inline u8 special() const { return instr.opcode.special; }
|
||||||
@@ -221,4 +223,4 @@ struct Instruction {
|
|||||||
static constexpr u8 BLTZALL = 0b10010;
|
static constexpr u8 BLTZALL = 0b10010;
|
||||||
static constexpr u8 BGEZALL = 0b10011;
|
static constexpr u8 BGEZALL = 0b10011;
|
||||||
};
|
};
|
||||||
}
|
} // namespace n64
|
||||||
|
|||||||
Reference in New Issue
Block a user