- Stop using inheritance for CPU, instead use composition.
- Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like. - More cache work
This commit is contained in:
@@ -13,6 +13,7 @@ if(APPLE)
|
||||
enable_language(OBJC)
|
||||
endif()
|
||||
|
||||
set(USE_JIT FALSE)
|
||||
set(VULKAN_VALIDATION FALSE)
|
||||
set(SANITIZERS FALSE)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -115,6 +116,9 @@ endif()
|
||||
set(SIMD_FLAG NULL)
|
||||
|
||||
if(ARM64)
|
||||
if(USE_JIT)
|
||||
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
|
||||
endif()
|
||||
message("Defining USE_NEON...")
|
||||
add_compile_definitions(USE_NEON)
|
||||
add_compile_definitions(SIMD_SUPPORT)
|
||||
@@ -182,6 +186,9 @@ endif()
|
||||
|
||||
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
|
||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||
if(USE_JIT)
|
||||
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
|
||||
endif()
|
||||
|
||||
if (SANITIZERS)
|
||||
message("UBSAN AND ASAN: ON")
|
||||
|
||||
+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_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
@@ -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
@@ -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,7 +2,7 @@ file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
add_subdirectory(interpreter)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
add_subdirectory(jit)
|
||||
endif()
|
||||
add_subdirectory(mem)
|
||||
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
|
||||
|
||||
add_library(core ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
target_link_libraries(core PRIVATE jit)
|
||||
endif()
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
#include <Mem.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
#include <common.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct alignas(32) InstructionCache {
|
||||
@@ -10,6 +8,7 @@ struct alignas(32) InstructionCache {
|
||||
u32 ptag;
|
||||
|
||||
private:
|
||||
friend struct Interpreter;
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||
};
|
||||
@@ -21,13 +20,8 @@ struct alignas(32) DataCache {
|
||||
int index;
|
||||
|
||||
private:
|
||||
friend struct Interpreter;
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||
};
|
||||
|
||||
struct BaseCPU {
|
||||
virtual ~BaseCPU() = default;
|
||||
virtual u32 Step() = 0;
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
} // namespace n64
|
||||
@@ -1,25 +1,27 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace n64 {
|
||||
struct Core;
|
||||
|
||||
struct Interpreter final : BaseCPU {
|
||||
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 ®s;
|
||||
Mem &mem;
|
||||
u64 cop2Latch{};
|
||||
friend struct Cop1;
|
||||
|
||||
void cache_type_data(u8);
|
||||
void cache_type_instruction(u8);
|
||||
#define check_address_error(mask, vaddr) \
|
||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
@@ -41,6 +43,7 @@ private:
|
||||
void blink(Instruction, bool);
|
||||
void bl(Instruction, bool);
|
||||
void bllink(Instruction, bool);
|
||||
void cache(Instruction);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
|
||||
+14
-17
@@ -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 ®s) : 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
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
#include <xbyak.h>
|
||||
@@ -25,15 +26,12 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
||||
#define HI_OFFSET (reinterpret_cast<uintptr_t>(®s.hi) - reinterpret_cast<uintptr_t>(this))
|
||||
#define LO_OFFSET (reinterpret_cast<uintptr_t>(®s.lo) - reinterpret_cast<uintptr_t>(this))
|
||||
|
||||
#ifdef __aarch64__
|
||||
struct JIT : BaseCPU {};
|
||||
#else
|
||||
struct JIT final : BaseCPU {
|
||||
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
@@ -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 ®s = n64::Core::GetRegs();
|
||||
const SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u8>(paddr);
|
||||
if (ircolib::IsInsideRange(paddr, 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 ®s = n64::Core::GetRegs();
|
||||
const SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u16>(paddr);
|
||||
if (ircolib::IsInsideRange(paddr, 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 ®s = n64::Core::GetRegs();
|
||||
const SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u32>(paddr);
|
||||
if (ircolib::IsInsideRange(paddr, 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 ®s = n64::Core::GetRegs();
|
||||
const SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) return mmio.rdp.ReadRDRAM<u64>(paddr);
|
||||
if (ircolib::IsInsideRange(paddr, 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 ®s = n64::Core::GetRegs();
|
||||
SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u8>(paddr, val); return; }
|
||||
#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 ®s = n64::Core::GetRegs();
|
||||
SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u16>(paddr, val); return; }
|
||||
#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 ®s = n64::Core::GetRegs();
|
||||
SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u32>(paddr, val); return; }
|
||||
#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 ®s = n64::Core::GetRegs();
|
||||
SI &si = mmio.si;
|
||||
|
||||
if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM<u64>(paddr, val); return; }
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <>
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user