Compare commits

...

10 Commits

25 changed files with 572 additions and 421 deletions

View File

@@ -13,6 +13,7 @@ if(APPLE)
enable_language(OBJC) enable_language(OBJC)
endif() endif()
set(VULKAN_VALIDATION FALSE)
set(SANITIZERS FALSE) set(SANITIZERS FALSE)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -102,7 +103,8 @@ if (HAS_SIMD)
add_compile_options(${SIMD_FLAG}) add_compile_options(${SIMD_FLAG})
endif () endif ()
if (${CMAKE_BUILD_TYPE} MATCHES Debug) if (${CMAKE_BUILD_TYPE} MATCHES Debug AND VULKAN_VALIDATION)
message("VALIDATION LAYERS: ON")
add_compile_definitions(VULKAN_DEBUG) add_compile_definitions(VULKAN_DEBUG)
endif () endif ()
@@ -152,6 +154,7 @@ target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::c
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
if (SANITIZERS) if (SANITIZERS)
message("UBSAN AND ASAN: ON")
target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address) target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address) target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
endif () endif ()

View File

@@ -6,14 +6,13 @@
#include <resources/frag.spv.h> #include <resources/frag.spv.h>
#include <KaizenGui.hpp> #include <KaizenGui.hpp>
#include <imgui_impl_vulkan.h> #include <imgui_impl_vulkan.h>
#include <mutex>
using namespace Vulkan; using namespace Vulkan;
using namespace RDP; using namespace RDP;
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; } bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
void ParallelRDP::SetFramerateUnlocked(bool unlocked) const { void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const {
if (unlocked) { if (unlocked) {
wsi->set_present_mode(PresentMode::UnlockedForceTearing); wsi->set_present_mode(PresentMode::UnlockedForceTearing);
} else { } else {
@@ -24,11 +23,11 @@ void ParallelRDP::SetFramerateUnlocked(bool unlocked) const {
Program *fullscreen_quad_program; Program *fullscreen_quad_program;
// Copied and modified from WSI::init_context_from_platform // Copied and modified from WSI::init_context_from_platform
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned num_thread_indices, Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
const Context::SystemHandles &system_handles) { const Context::SystemHandles &system_handles) {
VK_ASSERT(platform); VK_ASSERT(platform);
auto instance_ext = platform->get_instance_extensions(); const auto instance_ext = platform->get_instance_extensions();
auto device_ext = platform->get_device_extensions(); const auto device_ext = platform->get_device_extensions();
auto new_context = Util::make_handle<Context>(); auto new_context = Util::make_handle<Context>();
new_context->set_application_info(platform->get_application_info()); new_context->set_application_info(platform->get_application_info());
@@ -39,9 +38,9 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned nu
panic("Failed to create Vulkan instance.\n"); panic("Failed to create Vulkan instance.\n");
} }
VkSurfaceKHR tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE); const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(), const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT); CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
if (tmp_surface) { if (tmp_surface) {
@@ -61,9 +60,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platfo
wsi->set_backbuffer_srgb(false); wsi->set_backbuffer_srgb(false);
wsi->set_platform(wsi_platform.get()); wsi->set_platform(wsi_platform.get());
wsi->set_present_mode(PresentMode::SyncToVBlank); wsi->set_present_mode(PresentMode::SyncToVBlank);
Context::SystemHandles handles;
if (!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) { if (constexpr Context::SystemHandles handles;
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
panic("Failed to initialize WSI: init_from_existing_context() failed"); panic("Failed to initialize WSI: init_from_existing_context() failed");
} }
@@ -97,8 +96,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
fragLayout.sets[0].fp_mask = 1; fragLayout.sets[0].fp_mask = 1;
fragLayout.sets[0].array_size[0] = 1; fragLayout.sets[0].array_size[0] = 1;
auto sizeVert = sizeof(vertex_shader); constexpr auto sizeVert = sizeof(vertex_shader);
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 *>(fragment_shader), reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
@@ -108,8 +107,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
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) {
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;
} }
@@ -128,7 +127,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp); cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
cmd->set_program(fullscreen_quad_program); cmd->set_program(fullscreen_quad_program);
cmd->set_quad_state(); cmd->set_quad_state();
auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
data[0] = -1.0f; data[0] = -1.0f;
data[1] = -3.0f; data[1] = -3.0f;
data[2] = -1.0f; data[2] = -1.0f;
@@ -136,15 +135,15 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
data[4] = +3.0f; data[4] = +3.0f;
data[5] = +1.0f; data[5] = +1.0f;
auto windowSize = windowInfo->get_window_size(); const auto [x, y] = windowInfo->get_window_size();
float zoom = std::min(windowSize.x / wsi->get_platform().get_surface_width(), const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
windowSize.y / wsi->get_platform().get_surface_height()); y / static_cast<float>(wsi->get_platform().get_surface_height()));
float width = (wsi->get_platform().get_surface_width() / windowSize.x) * zoom; const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
float height = (wsi->get_platform().get_surface_height() / windowSize.y) * zoom; const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
float uniform_data[] = {// Size const float uniform_data[] = {// Size
width, height, width, height,
// Offset // Offset
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f}; (1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
@@ -190,10 +189,9 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
wsi->end_frame(); wsi->end_frame();
} }
void ParallelRDP::UpdateScreen(bool playing) const { template <>
if(playing) { void ParallelRDP::UpdateScreen<true>() const {
n64::Core& core = n64::Core::GetInstance(); const n64::VI& vi = n64::Core::GetMem().mmio.vi;
n64::VI& vi = core.GetMem().mmio.vi;
command_processor->set_vi_register(VIRegister::Control, vi.status.raw); command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
command_processor->set_vi_register(VIRegister::Origin, vi.origin); command_processor->set_vi_register(VIRegister::Origin, vi.origin);
command_processor->set_vi_register(VIRegister::Width, vi.width); command_processor->set_vi_register(VIRegister::Width, vi.width);
@@ -217,16 +215,17 @@ void ParallelRDP::UpdateScreen(bool playing) const {
opts.vi.gamma_dither = true; opts.vi.gamma_dither = true;
opts.downscale_steps = true; opts.downscale_steps = true;
opts.crop_overscan_pixels = true; opts.crop_overscan_pixels = true;
Util::IntrusivePtr<Image> image = command_processor->scanout(opts); const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
UpdateScreen(image); UpdateScreen(image);
command_processor->begin_frame_context(); command_processor->begin_frame_context();
return; }
}
template <>
void ParallelRDP::UpdateScreen<false>() const {
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr)); UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
} }
void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const { void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const {
command_processor->enqueue_command(command_length, buffer); command_processor->enqueue_command(command_length, buffer);
} }

View File

@@ -18,10 +18,11 @@ public:
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &, void Init(const std::shared_ptr<Vulkan::WSIPlatform> &,
const std::shared_ptr<WindowInfo> &, const u8 *); const std::shared_ptr<WindowInfo> &, const u8 *);
void UpdateScreen(bool = true) const; template <bool>
void UpdateScreen() const;
void EnqueueCommand(int, const u32 *) const; void EnqueueCommand(int, const u32 *) const;
void OnFullSync() const; void OnFullSync() const;
bool IsFramerateUnlocked() const; [[nodiscard]] bool IsFramerateUnlocked() const;
void SetFramerateUnlocked(bool) const; void SetFramerateUnlocked(bool) const;
std::shared_ptr<Vulkan::WSI> wsi; std::shared_ptr<Vulkan::WSI> wsi;

View File

@@ -5,14 +5,14 @@
namespace n64 { namespace n64 {
Core::Core() { Core::Core() {
auto cpuType = Options::GetInstance().GetValue<std::string>("cpu", "type"); const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
if (cpuType == "interpreter") { if (selectedCpu == "interpreter") {
cpuType = Interpreted; cpuType = Interpreted;
cpu = std::make_unique<Interpreter>(parallel, *mem, regs); cpu = std::make_unique<Interpreter>(*mem, regs);
} else if(cpuType == "jit") { } else if(selectedCpu == "jit") {
#ifndef __aarch64__ #ifndef __aarch64__
cpuType = DynamicRecompiler; cpuType = DynamicRecompiler;
cpu = std::make_unique<JIT>(parallel, *mem, regs); cpu = std::make_unique<JIT>(*mem, regs);
#else #else
panic("JIT currently unsupported on aarch64"); panic("JIT currently unsupported on aarch64");
#endif #endif
@@ -62,11 +62,11 @@ void Core::LoadROM(const std::string &rom_) {
romLoaded = true; romLoaded = true;
} }
int Core::StepCPU() { u32 Core::StepCPU() {
return cpu->Step() + regs.PopStalledCycles(); return cpu->Step() + regs.PopStalledCycles();
} }
void Core::StepRSP(int cpuCycles) { void Core::StepRSP(const u32 cpuCycles) {
MMIO &mmio = mem->mmio; MMIO &mmio = mem->mmio;
if (mmio.rsp.spStatus.halt) { if (mmio.rsp.spStatus.halt) {
@@ -75,10 +75,10 @@ void Core::StepRSP(int cpuCycles) {
return; return;
} }
static constexpr int cpuRatio = 3, rspRatio = 2; static constexpr u32 cpuRatio = 3, rspRatio = 2;
regs.steps += cpuCycles; regs.steps += cpuCycles;
int sets = regs.steps / cpuRatio; const auto sets = regs.steps / cpuRatio;
mmio.rsp.steps += sets * rspRatio; mmio.rsp.steps += sets * rspRatio;
regs.steps -= sets * cpuRatio; regs.steps -= sets * cpuRatio;
@@ -88,7 +88,7 @@ void Core::StepRSP(int cpuCycles) {
} }
} }
void Core::Run(float volumeL, float volumeR) { void Core::Run(const float volumeL, const float volumeR) {
MMIO &mmio = mem->mmio; MMIO &mmio = mem->mmio;
bool broken = false; bool broken = false;
@@ -102,7 +102,7 @@ void Core::Run(float volumeL, float volumeR) {
} }
while(cycles < mem->mmio.vi.cyclesPerHalfline) { while(cycles < mem->mmio.vi.cyclesPerHalfline) {
u32 taken = StepCPU(); const u32 taken = StepCPU();
cycles += taken; cycles += taken;
if((broken = breakpoints.contains(regs.nextPC))) if((broken = breakpoints.contains(regs.nextPC)))

View File

@@ -29,8 +29,8 @@ struct Core {
return *GetInstance().mem; return *GetInstance().mem;
} }
int StepCPU(); u32 StepCPU();
void StepRSP(int cpuCycles); void StepRSP(u32 cpuCycles);
void Stop(); void Stop();
void Reset(); void Reset();
void LoadROM(const std::string &); void LoadROM(const std::string &);

View File

@@ -6,7 +6,7 @@
namespace n64 { namespace n64 {
struct BaseCPU { struct BaseCPU {
virtual ~BaseCPU() = default; virtual ~BaseCPU() = default;
virtual int Step() = 0; virtual u32 Step() = 0;
virtual void Reset() = 0; virtual void Reset() = 0;
}; };
} // namespace n64 } // namespace n64

View File

@@ -1,7 +1,7 @@
#include <Core.hpp> #include <Core.hpp>
namespace n64 { namespace n64 {
Interpreter::Interpreter(ParallelRDP& parallel, Mem& mem, Registers& regs) : mem(mem), regs(regs) {} Interpreter::Interpreter(Mem& mem, Registers& regs) : regs(regs), mem(mem) {}
bool Interpreter::ShouldServiceInterrupt() const { bool Interpreter::ShouldServiceInterrupt() const {
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
@@ -12,7 +12,7 @@ bool Interpreter::ShouldServiceInterrupt() const {
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
} }
void Interpreter::CheckCompareInterrupt() { void Interpreter::CheckCompareInterrupt() const {
regs.cop0.count++; regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF; regs.cop0.count &= 0x1FFFFFFFF;
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) { if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
@@ -21,7 +21,7 @@ void Interpreter::CheckCompareInterrupt() {
} }
} }
int Interpreter::Step() { u32 Interpreter::Step() {
CheckCompareInterrupt(); CheckCompareInterrupt();
regs.prevDelaySlot = regs.delaySlot; regs.prevDelaySlot = regs.delaySlot;

View File

@@ -6,10 +6,10 @@
namespace n64 { namespace n64 {
struct Core; struct Core;
struct Interpreter : BaseCPU { struct Interpreter final : BaseCPU {
explicit Interpreter(ParallelRDP&, Mem&, Registers&); explicit Interpreter(Mem&, Registers&);
~Interpreter() override = default; ~Interpreter() override = default;
int Step() override; u32 Step() override;
void Reset() override { void Reset() override {
cop2Latch = {}; cop2Latch = {};
@@ -22,8 +22,8 @@ private:
friend struct Cop1; friend struct Cop1;
#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))
bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt(); void CheckCompareInterrupt() const;
void cop2Decode(Instruction); void cop2Decode(Instruction);
void special(Instruction); void special(Instruction);

View File

@@ -3,8 +3,9 @@
namespace n64 { namespace n64 {
#ifndef __aarch64__ #ifndef __aarch64__
JIT::JIT(ParallelRDP& parallel, Mem& mem, Registers& regs) : mem(mem), regs(regs) { JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) {
regs.SetJIT(this); 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) {
@@ -25,7 +26,7 @@ bool JIT::ShouldServiceInterrupt() const {
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error; return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
} }
void JIT::CheckCompareInterrupt() { void JIT::CheckCompareInterrupt() const {
regs.cop0.count++; regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF; regs.cop0.count &= 0x1FFFFFFFF;
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) { if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
@@ -39,20 +40,20 @@ void JIT::InvalidateBlock(const u32 paddr) {
blockCache[index] = {}; blockCache[index] = {};
} }
u32 JIT::FetchInstruction() { std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
u32 paddr = 0; u32 paddr = 0;
if (check_address_error(0b11, u64(blockPC))) [[unlikely]] { if (check_address_error(0b11, vaddr)) [[unlikely]] {
/*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}, blockPC, {}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!"); "[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return 0; return std::nullopt;
} }
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
@@ -60,48 +61,54 @@ u32 JIT::FetchInstruction() {
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, 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 std::nullopt;
} }
return Core::GetMem().Read<u32>(paddr); const u32 instr = Core::GetMem().Read<u32>(paddr);
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
return instr;
} }
void JIT::SetPC32(s32 val) { void JIT::SetPC32(const s32 val) {
code.mov(code.SCR1, REG(qword, pc)); code.mov(code.SCR1, code.ptr[PC_OFFSET()]);
code.mov(REG(qword, oldPC), code.SCR1); code.mov(code.ptr[OLD_PC_OFFSET()], code.SCR1);
code.mov(code.SCR1, s64(val)); code.mov(code.SCR1.cvt32(), val);
code.mov(REG(qword, pc), code.SCR1); code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
code.mov(code.SCR1, s64(val) + 4); code.mov(code.ptr[PC_OFFSET()], code.SCR1);
code.mov(REG(qword, nextPC), code.SCR1); code.mov(code.SCR1.cvt32(), val + 4);
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
code.mov(code.ptr[NEXT_PC_OFFSET()], code.SCR1);
} }
void JIT::SetPC64(s64 val) { void JIT::SetPC64(const s64 val) {
code.mov(code.SCR1, REG(qword, pc)); code.mov(code.SCR1, code.ptr[PC_OFFSET()]);
code.mov(REG(qword, oldPC), code.SCR1); code.mov(code.ptr[OLD_PC_OFFSET()], code.SCR1);
code.mov(code.SCR1, val); code.mov(code.SCR1, val);
code.mov(REG(qword, pc), code.SCR1); code.mov(code.ptr[PC_OFFSET()], code.SCR1);
code.mov(code.SCR1, val + 4); code.mov(code.SCR1, val + 4);
code.mov(REG(qword, nextPC), code.SCR1); code.mov(code.ptr[NEXT_PC_OFFSET()], code.SCR1);
} }
void JIT::SetPC32(const Xbyak::Reg32& val) { void JIT::SetPC32(const Xbyak::Reg32& val) {
code.mov(code.SCR1, REG(qword, pc)); code.mov(code.SCR1, code.ptr[PC_OFFSET()]);
code.mov(REG(qword, oldPC), code.SCR1); code.mov(code.ptr[OLD_PC_OFFSET()], code.SCR1);
code.movsxd(val.cvt64(), val); code.movsxd(val.cvt64(), val);
code.mov(REG(qword, pc), val); code.mov(code.ptr[PC_OFFSET()], val);
code.add(val, 4); code.add(val, 4);
code.mov(REG(qword, nextPC), val); code.mov(code.ptr[NEXT_PC_OFFSET()], val);
} }
void JIT::SetPC64(const Xbyak::Reg64& val) { void JIT::SetPC64(const Xbyak::Reg64& val) {
code.mov(code.SCR1, REG(qword, pc)); code.mov(code.SCR1, code.ptr[PC_OFFSET()]);
code.mov(REG(qword, oldPC), code.SCR1); code.mov(code.ptr[OLD_PC_OFFSET()], code.SCR1);
code.mov(REG(qword, pc), val); code.mov(code.ptr[PC_OFFSET()], val);
code.add(val, 4); code.add(val, 4);
code.mov(REG(qword, nextPC), val); code.mov(code.ptr[NEXT_PC_OFFSET()], val);
} }
int JIT::Step() { u32 JIT::Step() {
blockOldPC = regs.oldPC; blockOldPC = regs.oldPC;
blockPC = regs.pc; blockPC = regs.pc;
blockNextPC = regs.nextPC; blockNextPC = regs.nextPC;
@@ -111,15 +118,15 @@ int JIT::Step() {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
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 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;
} }
u32 upperIndex = paddr >> kUpperShift; const u32 upperIndex = paddr >> kUpperShift;
u32 lowerIndex = paddr & kLowerMask; const u32 lowerIndex = paddr & kLowerMask;
if (!blockCache[upperIndex].empty()) { if (!blockCache[upperIndex].empty()) {
if (blockCache[upperIndex][lowerIndex]) { if (blockCache[upperIndex][lowerIndex]) {
@@ -130,7 +137,7 @@ int JIT::Step() {
blockCache[upperIndex].resize(kLowerSize); blockCache[upperIndex].resize(kLowerSize);
} }
trace("[JIT]: Compiling block @ 0x{:016X}:", blockPC); info("[JIT]: Compiling block @ 0x{:016X}:", static_cast<u64>(blockPC));
const auto blockInfo = code.getCurr(); const auto blockInfo = code.getCurr();
const auto block = code.getCurr<BlockFn>(); const auto block = code.getCurr<BlockFn>();
blockCache[upperIndex][lowerIndex] = block; blockCache[upperIndex][lowerIndex] = block;
@@ -138,7 +145,6 @@ int JIT::Step() {
code.setProtectModeRW(); code.setProtectModeRW();
u32 instructionsInBlock = 0; u32 instructionsInBlock = 0;
u32 instruction = 0;
bool instrEndsBlock = false; bool instrEndsBlock = false;
@@ -146,78 +152,70 @@ int JIT::Step() {
code.push(code.rbp); code.push(code.rbp);
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
//cs_insn *insn; cs_insn *insn;
trace("\tMIPS code (guest PC = 0x{:016X}):", blockPC); info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
while (!instrEndsBlock) {
// CheckCompareInterrupt(); emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
while (true) {
paddr = 0; paddr = 0;
if (check_address_error(0b11, u64(blockPC))) [[unlikely]] { auto instruction = FetchInstruction(blockPC);
/*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, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return 0;
}
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { if(!instruction)
/*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, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0; return 0;
}
instruction = Core::GetMem().Read<u32>(paddr);
instructionsInBlock++; instructionsInBlock++;
blockOldPC = blockPC; blockOldPC = blockPC;
blockPC = blockNextPC; blockPC = blockNextPC;
blockNextPC += 4; blockNextPC += 4;
if((instrEndsBlock = InstrEndsBlock(instruction))) if(InstrEndsBlock(instruction.value())) {
continue; const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
if(!delay_instruction)
return 0;
trace("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instruction).full); if(InstrEndsBlock(delay_instruction.value())) {
/*if(ShouldServiceInterrupt()) {
regs.cop0.FireException(ExceptionCode::Interrupt, 0, blockPC);
return 1;
}*/
Emit(instruction);
}
instructionsInBlock++;
const u32 delay_instruction = FetchInstruction();
instrEndsBlock = InstrEndsBlock(delay_instruction);
if(instrEndsBlock) {
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
return 0; return 0;
} }
instructionsInBlock++;
blockOldPC = blockPC; blockOldPC = blockPC;
blockPC = blockNextPC; blockPC = blockNextPC;
blockNextPC += 4; blockNextPC += 4;
Emit(delay_instruction); Emit(delay_instruction.value());
Emit(instruction.value());
Emit(instruction); if(!branch_taken) {
Xbyak::Label runtime_branch_taken;
if(!regs.delaySlot) { code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET()]);
code.cmp(code.SCR1, 0);
code.jne(runtime_branch_taken);
code.mov(code.SCR1, blockOldPC); code.mov(code.SCR1, blockOldPC);
code.mov(REG(qword, oldPC), code.SCR1); code.mov(code.ptr[OLD_PC_OFFSET()], code.SCR1);
code.mov(code.SCR1, blockPC); code.mov(code.SCR1, blockPC);
code.mov(REG(qword, pc), code.SCR1); code.mov(code.ptr[PC_OFFSET()], code.SCR1);
code.mov(code.SCR1, blockNextPC); code.mov(code.SCR1, blockNextPC);
code.mov(REG(qword, nextPC), code.SCR1); code.mov(code.ptr[NEXT_PC_OFFSET()], code.SCR1);
code.L(runtime_branch_taken);
}
if(branch_taken) branch_taken = false;
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
break;
}
Emit(instruction.value());
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
} }
code.mov(code.rax, instructionsInBlock); code.mov(code.rax, instructionsInBlock);
@@ -225,22 +223,24 @@ int JIT::Step() {
code.add(code.rsp, 8); code.add(code.rsp, 8);
code.ret(); code.ret();
code.setProtectModeRE(); code.setProtectModeRE();
//static auto blockInfoSize = 0; static size_t blockInfoSize = 0;
//blockInfoSize = code.getSize() - blockInfoSize; blockInfoSize = code.getSize() - blockInfoSize;
//trace("\tX86 code (block address = 0x{:016X}):", (uintptr_t)block); info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
//auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, (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++) {
// trace("\t\t0x{:016X}:\t{}\t\t{}\n", 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);
// } }
//
// cs_free(insn, count); cs_free(insn, count);
//} }
// const auto dump = code.getCode();
// Util::WriteFileBinary(dump, code.getSize(), "jit.dump");
// panic(""); // panic("");
return block(); return block();
} }
#endif #endif
void JIT::DumpBlockCacheToDisk() const {
Util::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
}
} // namespace n64 } // namespace n64

View File

@@ -16,15 +16,21 @@ static constexpr u32 kUpperSize = kAddressSpaceSize >> kUpperShift; // 0x800000
static constexpr u32 kLowerSize = 0x100; // 0x80 static constexpr u32 kLowerSize = 0x100; // 0x80
static constexpr u32 kCodeCacheSize = 32_mb; static constexpr u32 kCodeCacheSize = 32_mb;
static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb; static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
#define REG(acc, x) code.acc[code.rbp + (reinterpret_cast<uintptr_t>(&regs.x) - (uintptr_t)this)] #define OLD_PC_OFFSET() (reinterpret_cast<uintptr_t>(&regs.oldPC))
#define PC_OFFSET() (reinterpret_cast<uintptr_t>(&regs.pc))
#define NEXT_PC_OFFSET() (reinterpret_cast<uintptr_t>(&regs.nextPC))
#define GPR_OFFSET(x) (reinterpret_cast<uintptr_t>(&regs.gpr[(x)]))
#define BRANCH_TAKEN_OFFSET() (reinterpret_cast<uintptr_t>(&branch_taken) - reinterpret_cast<uintptr_t>(this))
#define HI_OFFSET() (reinterpret_cast<uintptr_t>(&regs.hi))
#define LO_OFFSET() (reinterpret_cast<uintptr_t>(&regs.lo))
#ifdef __aarch64__ #ifdef __aarch64__
struct JIT : BaseCPU {}; struct JIT : BaseCPU {};
#else #else
struct JIT : BaseCPU { struct JIT final : BaseCPU {
explicit JIT(ParallelRDP&, Mem&, Registers&); explicit JIT(Mem&, Registers&);
~JIT() override = default; ~JIT() override = default;
int Step() override; u32 Step() override;
void Reset() override { void Reset() override {
code.reset(); code.reset();
@@ -32,30 +38,38 @@ struct JIT : BaseCPU {
blockCache.resize(kUpperSize); blockCache.resize(kUpperSize);
} }
void DumpBlockCacheToDisk() const;
void AdvanceDelaySlot() {
regs.prevDelaySlot = regs.delaySlot;
regs.delaySlot = false;
}
void InvalidateBlock(u32); void InvalidateBlock(u32);
private: private:
Registers& regs; Registers& regs;
Mem& mem; Mem& mem;
Xbyak::CodeGenerator code{kCodeCacheAllocSize}; Xbyak::CodeGenerator code{kCodeCacheAllocSize};
u64 cop2Latch{}; u64 cop2Latch{};
u64 blockOldPC = 0, blockPC = 0, blockNextPC = 0; s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
friend struct Cop1; friend struct Cop1;
friend struct Registers; friend struct Registers;
using BlockFn = int (*)(); using BlockFn = int (*)();
std::vector<std::vector<BlockFn>> blockCache; std::vector<std::vector<BlockFn>> blockCache;
Xbyak::Label branch_likely_not_taken; Xbyak::Label branch_likely_not_taken;
csh disassemblerMips, disassemblerX86; bool branch_taken;
csh disassemblerMips{}, disassemblerX86{};
template <typename T> template <typename T>
Xbyak::Address GPR(const size_t index) const { Xbyak::Address GPR(const size_t index) {
if constexpr (sizeof(T) == 1) { if constexpr (sizeof(T) == 1) {
return code.byte[code.rbp + (reinterpret_cast<uintptr_t>(&regs.gpr[index]) - reinterpret_cast<uintptr_t>(this))]; return code.byte[GPR_OFFSET(index)];
} else if constexpr (sizeof(T) == 2) { } else if constexpr (sizeof(T) == 2) {
return code.word[code.rbp + (reinterpret_cast<uintptr_t>(&regs.gpr[index]) - reinterpret_cast<uintptr_t>(this))]; return code.word[GPR_OFFSET(index)];
} else if constexpr (sizeof(T) == 4) { } else if constexpr (sizeof(T) == 4) {
return code.dword[code.rbp + (reinterpret_cast<uintptr_t>(&regs.gpr[index]) - reinterpret_cast<uintptr_t>(this))]; return code.dword[GPR_OFFSET(index)];
} else if constexpr (sizeof(T) == 8) { } else if constexpr (sizeof(T) == 8) {
return code.qword[code.rbp + (reinterpret_cast<uintptr_t>(&regs.gpr[index]) - reinterpret_cast<uintptr_t>(this))]; return code.ptr[GPR_OFFSET(index)];
} }
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw(
@@ -106,89 +120,89 @@ private:
(((!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;
void CheckCompareInterrupt(); void CheckCompareInterrupt() const;
u32 FetchInstruction(); std::optional<u32> FetchInstruction(s64);
void Emit(const Instruction); void Emit(Instruction);
void special(const Instruction); void special(Instruction);
void regimm(const Instruction); void regimm(Instruction);
void add(const Instruction); void add(Instruction);
void addu(const Instruction); void addu(Instruction);
void addi(const Instruction); void addi(Instruction);
void addiu(const Instruction); void addiu(Instruction);
void andi(const Instruction); void andi(Instruction);
void and_(const Instruction); void and_(Instruction);
void branch_constant(bool cond, s64 offset); void branch_constant(bool cond, s64 offset);
void branch_likely_constant(bool cond, s64 offset); void branch_likely_constant(bool cond, s64 offset);
void branch_abs_constant(bool cond, s64 address); void branch_abs_constant(bool cond, s64 address);
void bltz(const Instruction); void bltz(Instruction);
void bgez(const Instruction); void bgez(Instruction);
void bltzl(const Instruction); void bltzl(Instruction);
void bgezl(const Instruction); void bgezl(Instruction);
void bltzal(const Instruction); void bltzal(Instruction);
void bgezal(const Instruction); void bgezal(Instruction);
void bltzall(const Instruction); void bltzall(Instruction);
void bgezall(const Instruction); void bgezall(Instruction);
void beq(const Instruction); void beq(Instruction);
void beql(const Instruction); void beql(Instruction);
void bne(const Instruction); void bne(Instruction);
void bnel(const Instruction); void bnel(Instruction);
void blez(const Instruction); void blez(Instruction);
void blezl(const Instruction); void blezl(Instruction);
void bgtz(const Instruction); void bgtz(Instruction);
void bgtzl(const Instruction); void bgtzl(Instruction);
void bfc1(const Instruction); void bfc1(Instruction);
void blfc1(const Instruction); void blfc1(Instruction);
void bfc0(const Instruction); void bfc0(Instruction);
void blfc0(const Instruction); void blfc0(Instruction);
void dadd(const Instruction); void dadd(Instruction);
void daddu(const Instruction); void daddu(Instruction);
void daddi(const Instruction); void daddi(Instruction);
void daddiu(const Instruction); void daddiu(Instruction);
void ddiv(const Instruction); void ddiv(Instruction);
void ddivu(const Instruction); void ddivu(Instruction);
void div(const Instruction); void div(Instruction);
void divu(const Instruction); void divu(Instruction);
void dmult(const Instruction); void dmult(Instruction);
void dmultu(const Instruction); void dmultu(Instruction);
void dsll(const Instruction); void dsll(Instruction);
void dsllv(const Instruction); void dsllv(Instruction);
void dsll32(const Instruction); void dsll32(Instruction);
void dsra(const Instruction); void dsra(Instruction);
void dsrav(const Instruction); void dsrav(Instruction);
void dsra32(const Instruction); void dsra32(Instruction);
void dsrl(const Instruction); void dsrl(Instruction);
void dsrlv(const Instruction); void dsrlv(Instruction);
void dsrl32(const Instruction); void dsrl32(Instruction);
void dsub(const Instruction); void dsub(Instruction);
void dsubu(const Instruction); void dsubu(Instruction);
void j(const Instruction); void j(Instruction);
void jr(const Instruction); void jr(Instruction);
void jal(const Instruction); void jal(Instruction);
void jalr(const Instruction); void jalr(Instruction);
void lui(const Instruction); void lui(Instruction);
void lbu(const Instruction); void lbu(Instruction);
void lb(const Instruction); void lb(Instruction);
void ld(const Instruction); void ld(Instruction);
void ldc1(const Instruction); void ldc1(Instruction);
void ldl(const Instruction); void ldl(Instruction);
void ldr(const Instruction); void ldr(Instruction);
void lh(const Instruction); void lh(Instruction);
void lhu(const Instruction); void lhu(Instruction);
void ll(const Instruction); void ll(Instruction);
void lld(const Instruction); void lld(Instruction);
void lw(const Instruction); void lw(Instruction);
void lwc1(const Instruction); void lwc1(Instruction);
void lwl(const Instruction); void lwl(Instruction);
void lwu(const Instruction); void lwu(Instruction);
void lwr(const Instruction); void lwr(Instruction);
void mfhi(const Instruction); void mfhi(Instruction);
void mflo(const Instruction); void mflo(Instruction);
void mult(const Instruction); void mult(Instruction);
void multu(const Instruction); void multu(Instruction);
void mthi(const Instruction); void mthi(Instruction);
void mtlo(const Instruction); void mtlo(Instruction);
void nor(const 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},
@@ -229,7 +243,7 @@ private:
{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(const 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},
@@ -240,32 +254,32 @@ private:
{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(const Instruction); void slti(Instruction);
void sltiu(const Instruction); void sltiu(Instruction);
void slt(const Instruction); void slt(Instruction);
void sltu(const Instruction); void sltu(Instruction);
void sll(const Instruction); void sll(Instruction);
void sllv(const Instruction); void sllv(Instruction);
void sub(const Instruction); void sub(Instruction);
void subu(const 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::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
} }
void sra(const Instruction); void sra(Instruction);
void srav(const Instruction); void srav(Instruction);
void srl(const Instruction); void srl(Instruction);
void srlv(const 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::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
} }
void or_(const Instruction); void or_(Instruction);
void ori(const Instruction); void ori(Instruction);
void xor_(const Instruction); void xor_(Instruction);
void xori(const Instruction); void xori(Instruction);
}; };
#endif #endif
} // namespace n64 } // namespace n64

View File

@@ -6,7 +6,7 @@
#include <Options.hpp> #include <Options.hpp>
namespace n64 { namespace n64 {
Mem::Mem(JIT *jit) : flash(saveData), jit(jit) { Mem::Mem() : flash(saveData) {
rom.cart.resize(CART_SIZE); rom.cart.resize(CART_SIZE);
std::ranges::fill(rom.cart, 0); std::ranges::fill(rom.cart, 0);
} }

View File

@@ -78,10 +78,11 @@ struct JIT;
struct Mem { struct Mem {
~Mem() = default; ~Mem() = default;
Mem(JIT * = nullptr); Mem();
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; }

View File

@@ -135,7 +135,7 @@ struct RSP {
void SetVTE(const VPR &vt, u8 e); void SetVTE(const VPR &vt, u8 e);
auto Read(u32 addr) -> u32; auto Read(u32 addr) -> u32;
void Write(u32 addr, u32 val); void Write(u32 addr, u32 val);
void Exec(const Instruction instr); void Exec(Instruction instr);
SPStatus spStatus{}; SPStatus spStatus{};
u16 oldPC{}, pc{}, nextPC{}; u16 oldPC{}, pc{}, nextPC{};
SPDMASPAddr spDMASPAddr{}; SPDMASPAddr spDMASPAddr{};
@@ -151,7 +151,7 @@ struct RSP {
VPR vce{}; VPR vce{};
s16 divIn{}, divOut{}; s16 divIn{}, divOut{};
bool divInLoaded = false; bool divInLoaded = false;
int steps = 0; u32 steps = 0;
struct { struct {
VPR h{}, m{}, l{}; VPR h{}, m{}, l{};
@@ -257,12 +257,12 @@ struct RSP {
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; } FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
void special(const Instruction instr); void special(Instruction instr);
void regimm(const Instruction instr); void regimm(Instruction instr);
void lwc2(const Instruction instr); void lwc2(Instruction instr);
void swc2(const Instruction instr); void swc2(Instruction instr);
void cop2(const Instruction instr); void cop2(Instruction instr);
void cop0(const Instruction instr); void cop0(Instruction instr);
void add(Instruction instr); void add(Instruction instr);
void addi(Instruction instr); void addi(Instruction instr);

View File

@@ -270,7 +270,54 @@ void JIT::Emit(const Instruction instr) {
lui(instr); lui(instr);
break; break;
case Instruction::COP0: case Instruction::COP0:
panic("[JIT]: Unimplemented Cop0 decode"); switch (instr.cop_rs()) {
case 0x00:
code.mov(code.ARG2, instr);
emitMemberFunctionCall(&Cop0::mfc0, &regs.cop0);
break;
case 0x01:
code.mov(code.ARG2, instr);
emitMemberFunctionCall(&Cop0::dmfc0, &regs.cop0);
break;
case 0x04:
code.mov(code.ARG2, instr);
emitMemberFunctionCall(&Cop0::mtc0, &regs.cop0);
break;
case 0x05:
code.mov(code.ARG2, instr);
emitMemberFunctionCall(&Cop0::dmtc0, &regs.cop0);
break;
case 0x10 ... 0x1F:
switch (instr.cop_funct()) {
case 0x01:
emitMemberFunctionCall(&Cop0::tlbr, &regs.cop0);
break;
case 0x02:
code.mov(code.ARG2, COP0_REG_INDEX);
emitMemberFunctionCall(&Cop0::GetReg32, &regs.cop0);
code.mov(code.ARG2, code.rax);
code.and_(code.ARG2, 0x3F);
emitMemberFunctionCall(&Cop0::tlbw, &regs.cop0);
break;
case 0x06:
emitMemberFunctionCall(&Cop0::GetRandom, &regs.cop0);
code.mov(code.ARG2, code.rax);
emitMemberFunctionCall(&Cop0::tlbw, &regs.cop0);
break;
case 0x08:
emitMemberFunctionCall(&Cop0::tlbp, &regs.cop0);
break;
case 0x18:
emitMemberFunctionCall(&Cop0::eret, &regs.cop0);
break;
default:
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
regs.oldPC);
}
break;
default:
panic("Unimplemented COP0 instruction {}", instr.cop_rs());
}
break; break;
case Instruction::COP1: case Instruction::COP1:
{ {
@@ -411,6 +458,7 @@ void JIT::Emit(const Instruction instr) {
sd(instr); sd(instr);
break; break;
default: default:
DumpBlockCacheToDisk();
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast<u64>(regs.oldPC)); panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast<u64>(regs.oldPC));
} }
} }

View File

@@ -159,7 +159,7 @@ void JIT::bfc0(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = u64((s64)imm) << 2;
const s64 address = blockPC + offset; const s64 address = blockPC + offset;
// code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
// code.test(code.al, code.al); // code.test(code.al, code.al);
// branch(address, z); // branch(address, z);
} }
@@ -168,7 +168,7 @@ void JIT::blfc0(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = u64((s64)imm) << 2;
const s64 address = blockPC + offset; const s64 address = blockPC + offset;
// code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
// code.test(code.al, code.al); // code.test(code.al, code.al);
// branch_likely(address, z); // branch_likely(address, z);
} }
@@ -177,7 +177,7 @@ void JIT::bfc1(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = u64((s64)imm) << 2;
const s64 address = blockPC + offset; const s64 address = blockPC + offset;
// code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
// code.test(code.al, code.al); // code.test(code.al, code.al);
// branch(address, nz); // branch(address, nz);
} }
@@ -186,87 +186,95 @@ void JIT::blfc1(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = u64((s64)imm) << 2;
const s64 address = blockPC + offset; const s64 address = blockPC + offset;
// code.mov(code.al, REG(byte, cop1.fcr31.compare)); // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare));
// code.test(code.al, code.al); // code.test(code.al, code.al);
// branch_likely(address, nz); // branch_likely(address, nz);
} }
void JIT::BranchNotTaken() {} void JIT::BranchNotTaken() {}
void JIT::BranchTaken(s64 offs) {
code.mov(code.SCR1, REG(qword, pc)); void JIT::BranchTaken(const s64 offs) {
code.add(code.SCR1, offs); code.mov(code.SCR2, code.qword[PC_OFFSET()]);
SetPC64(offs); code.add(code.SCR2, offs);
SetPC64(code.SCR2);
} }
void JIT::BranchTaken(const Xbyak::Reg64 &offs) { void JIT::BranchTaken(const Xbyak::Reg64 &offs) {
code.mov(code.SCR1, REG(qword, pc)); code.mov(code.SCR2, code.qword[PC_OFFSET()]);
code.add(code.SCR1, offs); code.add(code.SCR2, offs);
SetPC64(offs); SetPC64(code.SCR2);
} }
void JIT::BranchAbsTaken(s64 addr) { void JIT::BranchAbsTaken(const s64 addr) {
code.add(code.SCR1, addr); SetPC64(addr);
code.mov(REG(qword, nextPC), code.SCR1);
} }
void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) {
code.mov(REG(qword, nextPC), addr); SetPC64(addr);
} }
void JIT::branch_constant(const bool cond, s64 offset) { void JIT::branch_constant(const bool cond, const s64 offset) {
if(cond) { if(cond) {
regs.delaySlot = true; regs.delaySlot = true;
BranchTaken(offset); BranchTaken(offset);
branch_taken = true;
} }
} }
void JIT::branch_likely_constant(bool cond, s64 offset) { void JIT::branch_abs_constant(const bool cond, const s64 address) {
if(cond) {
regs.delaySlot = true;
BranchAbsTaken(address);
branch_taken = true;
}
}
void JIT::branch_likely_constant(const bool cond, const s64 offset) {
if(cond) { if(cond) {
regs.delaySlot = true; regs.delaySlot = true;
BranchTaken(offset); BranchTaken(offset);
branch_taken = true;
} else { } else {
SetPC64(blockNextPC); SetPC64(blockNextPC);
} }
} }
void JIT::branch_abs_constant(bool cond, s64 address) {
if(cond) {
regs.delaySlot = true;
BranchAbsTaken(address);
}
}
#define branch(offs, cond) do { \ #define branch(offs, cond) do { \
Xbyak::Label taken, not_taken; \ Xbyak::Label taken, not_taken; \
code.j##cond(taken); \ code.j## cond(taken); \
code.jmp(not_taken); \ code.jmp(not_taken); \
code.L(taken); \ code.L(taken); \
BranchTaken(offs); \ BranchTaken(offs); \
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET()], 1); \
code.L(not_taken); \ code.L(not_taken); \
} while(0) } while(0)
#define branch_abs(addr, cond) do { \ #define branch_abs(addr, cond) do { \
Xbyak::Label taken, not_taken; \ Xbyak::Label taken, not_taken; \
code.j##cond(taken); \ code.j## cond(taken); \
code.jmp(not_taken); \ code.jmp(not_taken); \
code.L(taken); \ code.L(taken); \
BranchAbsTaken(addr); \ BranchAbsTaken(addr); \
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET()], 1); \
code.L(not_taken); \ code.L(not_taken); \
} while(0) } while(0)
#define branch_likely(offs, cond) do { \ #define branch_likely(offs, cond) do { \
Xbyak::Label taken, not_taken; \ Xbyak::Label taken, not_taken, end; \
code.j##cond(taken); \ code.j## cond(taken); \
code.jmp(not_taken); \ code.jmp(not_taken); \
code.L(taken); \ code.L(taken); \
BranchTaken(offs); \ BranchTaken(offs); \
code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET()], 1); \
code.jmp(end); \
code.L(not_taken); \ code.L(not_taken); \
SetPC64(blockNextPC); \ SetPC64(blockNextPC); \
code.L(end); \
} while(0) } while(0)
void JIT::bltz(const Instruction instr) { void JIT::bltz(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = static_cast<s64>(imm) << 2;
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
branch_constant(regs.Read<s64>(instr.rs()) < 0, offset); branch_constant(regs.Read<s64>(instr.rs()) < 0, offset);
return; return;
@@ -435,7 +443,7 @@ void JIT::beql(const Instruction instr) {
void JIT::bne(const Instruction instr) { void JIT::bne(const Instruction instr) {
const s16 imm = instr; const s16 imm = instr;
const s64 offset = u64((s64)imm) << 2; const s64 offset = static_cast<s64>(imm) << 2;
if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) {
branch_constant(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()), offset); branch_constant(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()), offset);
return; return;
@@ -857,13 +865,13 @@ void JIT::dsubu(const Instruction instr) {
void JIT::j(const Instruction instr) { void JIT::j(const Instruction instr) {
const s32 target = (instr & 0x3ffffff) << 2; const s32 target = (instr & 0x3ffffff) << 2;
const s64 address = (blockOldPC & ~0xfffffff) | target; const s64 address = blockOldPC & ~0xfffffff | target;
branch_abs_constant(true, address); branch_abs_constant(true, address);
} }
void JIT::jr(const Instruction instr) { void JIT::jr(const Instruction instr) {
if (regs.IsRegConstant(instr.rs())) { if (regs.IsRegConstant(instr.rs())) {
const u64 address = regs.Read<s64>(instr.rs()); const auto address = regs.Read<s64>(instr.rs());
branch_abs_constant(true, address); branch_abs_constant(true, address);
return; return;
} }
@@ -891,9 +899,7 @@ void JIT::lbu(const Instruction instr) {
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
panic("[JIT]: Unhandled TLBL exception in LBU!"); panic("[JIT]: Unhandled TLBL exception in LBU!");
} else { } else {
code.mov(code.ARG2, code.mov(code.ARG2, paddr);
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u8>, &mem); emitMemberFunctionCall(&Mem::Read<u8>, &mem);
regs.Write<u8>(instr.rt(), code.rax); regs.Write<u8>(instr.rt(), code.rax);
} }
@@ -908,11 +914,9 @@ void JIT::lb(const Instruction instr) {
if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
// regs.cop0.HandleTLBException(address); // regs.cop0.HandleTLBException(address);
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", blockPC); panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast<u64>(blockPC));
} else { } else {
code.mov(code.ARG2, code.mov(code.ARG2, paddr);
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u8>, &mem); emitMemberFunctionCall(&Mem::Read<u8>, &mem);
regs.Write<s8>(instr.rt(), code.rax); regs.Write<s8>(instr.rt(), code.rax);
} }
@@ -937,9 +941,7 @@ void JIT::ld(const Instruction instr) {
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
panic("[JIT]: Unhandled TLBL exception in LD!"); panic("[JIT]: Unhandled TLBL exception in LD!");
} else { } else {
code.mov(code.ARG2, code.mov(code.ARG2, paddr);
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u64>, &mem); emitMemberFunctionCall(&Mem::Read<u64>, &mem);
regs.Write<u64>(instr.rt(), code.rax); regs.Write<u64>(instr.rt(), code.rax);
} }
@@ -1026,8 +1028,7 @@ void JIT::lh(const Instruction instr) {
return; return;
} }
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, paddr);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u16>, &mem); emitMemberFunctionCall(&Mem::Read<u16>, &mem);
regs.Write<s16>(instr.rt(), code.rax); regs.Write<s16>(instr.rt(), code.rax);
return; return;
@@ -1052,8 +1053,7 @@ void JIT::lhu(const Instruction instr) {
return; return;
} }
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, paddr);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u16>, &mem); emitMemberFunctionCall(&Mem::Read<u16>, &mem);
regs.Write<u16>(instr.rt(), code.rax); regs.Write<u16>(instr.rt(), code.rax);
} }
@@ -1064,8 +1064,7 @@ void JIT::lhu(const Instruction instr) {
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr)); code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0); emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0);
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, code.ptr[code.ARG4]);
code.mov(code.ARG3, code.qword[code.ARG4]);
emitMemberFunctionCall(&Mem::Read<u16>, &mem); emitMemberFunctionCall(&Mem::Read<u16>, &mem);
regs.Write<u16>(instr.rt(), code.rax); regs.Write<u16>(instr.rt(), code.rax);
} }
@@ -1094,8 +1093,7 @@ void JIT::lw(const Instruction instr) {
return; return;
} }
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, paddr);
code.mov(code.ARG3, paddr);
emitMemberFunctionCall(&Mem::Read<u32>, &mem); emitMemberFunctionCall(&Mem::Read<u32>, &mem);
regs.Write<s32>(instr.rt(), code.rax); regs.Write<s32>(instr.rt(), code.rax);
return; return;
@@ -1109,8 +1107,7 @@ void JIT::lw(const Instruction instr) {
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr)); code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&paddr));
emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0); emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0);
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, code.ptr[code.ARG4]);
code.mov(code.ARG3, code.qword[code.ARG4]);
emitMemberFunctionCall(&Mem::Read<u32>, &mem); emitMemberFunctionCall(&Mem::Read<u32>, &mem);
regs.Write<s32>(instr.rt(), code.rax); regs.Write<s32>(instr.rt(), code.rax);
} }
@@ -1127,7 +1124,7 @@ void JIT::mfhi(const Instruction instr) {
if (regs.GetHIConstant()) { if (regs.GetHIConstant()) {
regs.Write(instr.rd(), regs.hi); regs.Write(instr.rd(), regs.hi);
} else { } else {
code.mov(code.SCR1, REG(qword, hi)); code.mov(code.SCR1, code.ptr[HI_OFFSET()]);
regs.Write<s64>(instr.rd(), code.SCR1); regs.Write<s64>(instr.rd(), code.SCR1);
} }
} }
@@ -1136,7 +1133,7 @@ void JIT::mflo(const Instruction instr) {
if (regs.GetLOConstant()) { if (regs.GetLOConstant()) {
regs.Write(instr.rd(), regs.lo); regs.Write(instr.rd(), regs.lo);
} else { } else {
code.mov(code.SCR1, REG(qword, lo)); code.mov(code.SCR1, code.ptr[LO_OFFSET()]);
regs.Write<s64>(instr.rd(), code.SCR1); regs.Write<s64>(instr.rd(), code.SCR1);
} }
} }
@@ -1175,7 +1172,7 @@ void JIT::mthi(const Instruction instr) {
regs.SetHIConstant(); regs.SetHIConstant();
} else { } else {
regs.Read<s64>(instr.rs(), code.SCR1); regs.Read<s64>(instr.rs(), code.SCR1);
code.mov(REG(qword, hi), code.SCR1); code.mov(code.ptr[HI_OFFSET()], code.SCR1);
regs.UnsetHIConstant(); regs.UnsetHIConstant();
} }
} }
@@ -1186,7 +1183,7 @@ void JIT::mtlo(const Instruction instr) {
regs.SetLOConstant(); regs.SetLOConstant();
} else { } else {
regs.Read<s64>(instr.rs(), code.SCR1); regs.Read<s64>(instr.rs(), code.SCR1);
code.mov(REG(qword, lo), code.SCR1); code.mov(code.ptr[LO_OFFSET()], code.SCR1);
regs.UnsetLOConstant(); regs.UnsetLOConstant();
} }
} }
@@ -1357,10 +1354,8 @@ void JIT::sw(const Instruction instr) {
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
panic("[JIT]: Unhandled TLBS exception in SW!"); panic("[JIT]: Unhandled TLBS exception in SW!");
} else { } else {
code.lea(code.ARG2, code.mov(code.ARG2, physical);
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); regs.Read<s64>(instr.rt(), code.ARG3);
code.mov(code.ARG3, physical);
regs.Read<s64>(instr.rt(), code.ARG4);
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem); emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
} }
@@ -1382,10 +1377,8 @@ void JIT::sw(const Instruction instr) {
// regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
panic("[JIT]: Unhandled TLBS exception in SW!"); panic("[JIT]: Unhandled TLBS exception in SW!");
} else { } else {
code.mov(code.ARG2, code.mov(code.ARG2, physical);
code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); regs.Read<s64>(instr.rt(), code.ARG3);
code.mov(code.ARG3, physical);
regs.Read<s64>(instr.rt(), code.ARG4);
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem); emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
} }
@@ -1402,9 +1395,8 @@ void JIT::sw(const Instruction instr) {
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical)); code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0); emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0);
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, code.ptr[code.ARG4]);
code.mov(code.ARG3, code.qword[code.ARG4]); regs.Read<s64>(instr.rt(), code.ARG3);
regs.Read<s64>(instr.rt(), code.ARG4);
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem); emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
return; return;
@@ -1419,9 +1411,8 @@ void JIT::sw(const Instruction instr) {
code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical)); code.mov(code.ARG4, reinterpret_cast<uintptr_t>(&physical));
emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0); emitMemberFunctionCall(&Cop0::MapVAddr, &regs.cop0);
code.mov(code.ARG2, code.ptr[code.rbp + (reinterpret_cast<uintptr_t>(&regs) - reinterpret_cast<uintptr_t>(this))]); code.mov(code.ARG2, code.ptr[code.ARG4]);
code.mov(code.ARG3, code.qword[code.ARG4]); regs.Read<s64>(instr.rt(), code.ARG3);
regs.Read<s64>(instr.rt(), code.ARG4);
emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem); emitMemberFunctionCall(&Mem::WriteJIT<u32>, &mem);
} }

View File

@@ -264,6 +264,8 @@ struct Cop0 {
void decode(const Instruction); void decode(const Instruction);
private: private:
friend struct JIT;
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }

View File

@@ -18,8 +18,8 @@ struct Registers {
return regIsConstant & (1 << index); return regIsConstant & (1 << index);
} }
[[nodiscard]] bool IsRegConstant(const u32 first, const u32 second) const { [[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const {
return IsRegConstant(first) && IsRegConstant(second); return IsRegConstant(index1) && IsRegConstant(index2);
} }
bool GetLOConstant() { bool GetLOConstant() {

View File

@@ -0,0 +1,43 @@
#pragma once
#include <imgui.h>
#include <imgui_internal.h>
namespace ImGui {
inline bool Spinner(const char* label, const float radius, const int thickness, const ImU32& color) {
ImGuiWindow* window = GetCurrentWindow();
if (window->SkipItems)
return false;
const ImGuiContext & g = *GImGui;
const ImGuiStyle& style = g.Style;
const ImGuiID id = window->GetID(label);
const ImVec2 pos = window->DC.CursorPos;
const ImVec2 size(radius*2, (radius + style.FramePadding.y)*2);
const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y));
ItemSize(bb, style.FramePadding.y);
if (!ItemAdd(bb, id))
return false;
// Render
window->DrawList->PathClear();
constexpr int num_segments = 30;
const int start = abs(ImSin(g.Time*1.8f)*(num_segments-5));
const float a_min = IM_PI * 2.0f * static_cast<float>(start) / static_cast<float>(num_segments);
constexpr float a_max = IM_PI*2.0f * (static_cast<float>(num_segments) -3) / static_cast<float>(num_segments);
const auto centre = ImVec2(pos.x+radius, pos.y+radius+style.FramePadding.y);
for (int i = 0; i < num_segments; i++) {
const float a = a_min + static_cast<float>(i) / static_cast<float>(num_segments) * (a_max - a_min);
window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a+g.Time*8) * radius,
centre.y + ImSin(a+g.Time*8) * radius));
}
window->DrawList->PathStroke(color, false, thickness);
return true;
}
}

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>
#include <functional>
namespace ImGui { namespace ImGui {
inline bool BeginMainStatusBar() inline bool BeginMainStatusBar()

View File

@@ -1,6 +1,7 @@
#include <KaizenGui.hpp> #include <KaizenGui.hpp>
#include <backend/Core.hpp> #include <backend/Core.hpp>
#include <ImGuiImpl/GUI.hpp> #include <ImGuiImpl/GUI.hpp>
#include <ImGuiImpl/ProgressIndicators.hpp>
#include <ImGuiImpl/StatusBar.hpp> #include <ImGuiImpl/StatusBar.hpp>
#include <resources/gamecontrollerdb.h> #include <resources/gamecontrollerdb.h>
@@ -16,7 +17,7 @@ KaizenGui::~KaizenGui() {
SDL_Quit(); SDL_Quit();
} }
void KaizenGui::QueryDevices(SDL_Event event) { void KaizenGui::QueryDevices(const SDL_Event &event) {
switch (event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
if (!gamepad) { if (!gamepad) {
@@ -36,8 +37,8 @@ void KaizenGui::QueryDevices(SDL_Event event) {
} }
} }
void KaizenGui::HandleInput(SDL_Event event) { void KaizenGui::HandleInput(const SDL_Event &event) {
n64::Core& core = n64::Core::GetInstance(); const n64::Core& core = n64::Core::GetInstance();
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
switch(event.type) { switch(event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION: case SDL_EVENT_GAMEPAD_AXIS_MOTION:
@@ -52,7 +53,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) { if (xclamped < 0) {
xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MAX)); xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else { } else {
xclamped /= SDL_JOYSTICK_AXIS_MAX; xclamped /= SDL_JOYSTICK_AXIS_MAX;
} }
@@ -61,15 +62,15 @@ void KaizenGui::HandleInput(SDL_Event event) {
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) { if (yclamped < 0) {
yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN)); yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else { } else {
yclamped /= SDL_JOYSTICK_AXIS_MAX; yclamped /= SDL_JOYSTICK_AXIS_MAX;
} }
yclamped *= 86; yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, -yclamped); pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, xclamped); pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
} }
break; break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
@@ -90,7 +91,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
{ {
auto keys = SDL_GetKeyboardState(nullptr); const auto keys = SDL_GetKeyboardState(nullptr);
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true; fileDialogOpen = true;
} }
@@ -130,15 +131,25 @@ void KaizenGui::HandleInput(SDL_Event event) {
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
pif.UpdateAxis(0, n64::Controller::Axis::Y, keys[SDL_SCANCODE_UP] ? 86 : keys[SDL_SCANCODE_DOWN] ? -86 : 0);
pif.UpdateAxis(0, n64::Controller::Axis::X, keys[SDL_SCANCODE_LEFT] ? -86 : keys[SDL_SCANCODE_RIGHT] ? 86 : 0); if (keys[SDL_SCANCODE_UP]) pif.UpdateAxis(0, n64::Controller::Axis::Y, 86);
else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0);
if (keys[SDL_SCANCODE_DOWN]) pif.UpdateAxis(0, n64::Controller::Axis::Y, -86);
else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0);
if (keys[SDL_SCANCODE_LEFT]) pif.UpdateAxis(0, n64::Controller::Axis::X, -86);
else pif.UpdateAxis(0, n64::Controller::Axis::X, 0);
if (keys[SDL_SCANCODE_RIGHT]) pif.UpdateAxis(0, n64::Controller::Axis::X, 86);
else pif.UpdateAxis(0, n64::Controller::Axis::X, 0);
} }
break; break;
default: break; default: break;
} }
} }
std::tuple<std::optional<u64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() { std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
auto lastPC = Util::Error::GetLastPC(); auto lastPC = Util::Error::GetLastPC();
if(lastPC.has_value()) { if(lastPC.has_value()) {
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
@@ -146,10 +157,10 @@ std::tuple<std::optional<u64>, std::optional<Util::Error::MemoryAccess>> RenderE
auto memoryAccess = Util::Error::GetMemoryAccess(); auto memoryAccess = Util::Error::GetMemoryAccess();
if(memoryAccess.has_value()) { if(memoryAccess.has_value()) {
auto memoryAccessV = memoryAccess.value(); const auto [is_write, size, address, written_val] = memoryAccess.value();
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", memoryAccessV.is_write ? "Writing" : "Reading", ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
(u8)memoryAccessV.size, memoryAccessV.address, static_cast<u8>(size), address,
memoryAccessV.is_write ? std::format(" (value = 0x{:X})", memoryAccessV.written_val) : "") is_write ? std::format(" (value = 0x{:X})", written_val) : "")
.c_str()); .c_str());
} }
@@ -227,7 +238,7 @@ void KaizenGui::RenderUI() {
settingsWindow.render(); settingsWindow.render();
debugger.render(); debugger.render();
ImVec2 center = ImGui::GetMainViewport()->GetCenter(); const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
@@ -258,9 +269,9 @@ void KaizenGui::RenderUI() {
RenderErrorMessageDetails(); RenderErrorMessageDetails();
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
if(ignore || stop || chooseAnother) { if(ignore || stop || chooseAnother) {
Util::Error::SetHandled(); Util::Error::SetHandled();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
@@ -306,10 +317,10 @@ void KaizenGui::RenderUI() {
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
bool chooseAnother = ImGui::Button("Choose another ROM"); const bool chooseAnother = ImGui::Button("Choose another ROM");
bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
if(ignore || stop || chooseAnother || openInDebugger) { if(ignore || stop || chooseAnother || openInDebugger) {
Util::Error::SetHandled(); Util::Error::SetHandled();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
@@ -346,6 +357,26 @@ void KaizenGui::RenderUI() {
ImGui::EndMainStatusBar(); ImGui::EndMainStatusBar();
} }
if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
ImGui::SameLine();
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
ImGui::PopFont();
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::Render(); ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows(); ImGui::UpdatePlatformWindows();
@@ -354,20 +385,25 @@ void KaizenGui::RenderUI() {
if(fileDialogOpen) { if(fileDialogOpen) {
fileDialogOpen = false; fileDialogOpen = false;
const SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int filter) { SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
auto kaizen = (KaizenGui*)userdata; auto kaizen = static_cast<KaizenGui*>(userdata);
if (!filelist) { if (!filelist) {
panic("An error occured: {}", SDL_GetError()); panic("An error occured: {}", SDL_GetError());
return; }
} else if (!*filelist) {
if (!*filelist) {
warn("The user did not select any file."); warn("The user did not select any file.");
warn("Most likely, the dialog was canceled."); warn("Most likely, the dialog was canceled.");
return; return;
} }
kaizen->LoadROM(*filelist); kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false); }, this, window.getHandle(), filters, 3, nullptr, false);
} }
@@ -375,17 +411,17 @@ void KaizenGui::RenderUI() {
return; return;
if(core.romLoaded) { if(core.romLoaded) {
core.parallel.UpdateScreen(); core.parallel.UpdateScreen<true>();
return; return;
} }
core.parallel.UpdateScreen(false); core.parallel.UpdateScreen<false>();
} }
void KaizenGui::LoadROM(const std::string &path) noexcept { void KaizenGui::LoadROM(const std::string &path) noexcept {
n64::Core& core = n64::Core::GetInstance(); n64::Core& core = n64::Core::GetInstance();
core.LoadROM(path); core.LoadROM(path);
const auto gameNameDB = core.GetMem().rom.gameNameDB; const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str()); SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str());
} }
@@ -405,11 +441,14 @@ void KaizenGui::run() {
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
minimized = false; minimized = false;
break; break;
default:
} }
QueryDevices(e); QueryDevices(e);
HandleInput(e); HandleInput(e);
} }
SDL_GetWindowSize(window.getHandle(), &width, &height);
emuThread.run(); emuThread.run();
RenderUI(); RenderUI();
} }

View File

@@ -27,10 +27,23 @@ public:
static void LoadTAS(const std::string &path) noexcept; static void LoadTAS(const std::string &path) noexcept;
void LoadROM(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept;
private: private:
int width{}, height{};
bool aboutOpen = false; bool aboutOpen = false;
bool fileDialogOpen = false; bool fileDialogOpen = false;
bool quit = false; bool quit = false;
bool shouldDisplaySpinner = false;
std::string fileToLoad = "";
void RenderUI(); void RenderUI();
void HandleInput(SDL_Event event); void HandleInput(const SDL_Event &event);
void QueryDevices(SDL_Event event); void QueryDevices(const SDL_Event &event);
[[noreturn]] void FileWorker() {
while (true) {
if (!fileToLoad.empty()) {
LoadROM(fileToLoad);
shouldDisplaySpinner = false;
fileToLoad = "";
}
}
}
}; };

View File

@@ -23,7 +23,7 @@ void GeneralSettings::render() {
return; return;
} }
general->savesPath = *filelist; general->savesPath = fs::absolute(*filelist).string();
Options::GetInstance().SetValue<std::string>("general", "savePath", general->savesPath); Options::GetInstance().SetValue<std::string>("general", "savePath", general->savesPath);
general->modified = true; general->modified = true;
}, this, window.getHandle(), nullptr, false); }, this, window.getHandle(), nullptr, false);

View File

@@ -7,7 +7,7 @@ bool SettingsWindow::render() {
const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if(!ImGui::BeginPopupModal("Settings", &isOpen)) if(!ImGui::BeginPopupModal("Settings", &isOpen, ImGuiWindowFlags_AlwaysAutoResize))
return false; return false;
if(!ImGui::BeginTabBar("SettingsTabBar")) if(!ImGui::BeginTabBar("SettingsTabBar"))

View File

@@ -1,7 +1,7 @@
#include <KaizenGui.hpp> #include <KaizenGui.hpp>
#include <cflags.hpp> #include <cflags.hpp>
int main(int argc, char **argv) { int main(const int argc, char **argv) {
KaizenGui kaizenGui; KaizenGui kaizenGui;
cflags::cflags flags; cflags::cflags flags;
flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line"); flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line");

View File

@@ -14,7 +14,7 @@ struct Error {
UNRECOVERABLE, UNRECOVERABLE,
} as_enum; } as_enum;
const char* as_c_str() { [[nodiscard]] const char* as_c_str() const {
switch(as_enum) { switch(as_enum) {
case NONE: return ""; case NONE: return "";
case WARN: return "Warning"; case WARN: return "Warning";
@@ -70,7 +70,7 @@ struct Error {
} as_enum; } as_enum;
const char* as_c_str() { [[nodiscard]] const char* as_c_str() const {
switch(as_enum) { switch(as_enum) {
case SCHEDULER_EOL: return "SCHEDULER_EOL"; case SCHEDULER_EOL: return "SCHEDULER_EOL";
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN"; case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
@@ -108,10 +108,8 @@ struct Error {
}; };
template <class... Args> template <class... Args>
void Throw (Severity severity, void Throw (const Severity severity, const Type type, const std::optional<u64> lastPC,
Type type, const std::optional<MemoryAccess> memoryAccess,
std::optional<u64> lastPC,
std::optional<MemoryAccess> memoryAccess,
const std::format_string<Args...> fmt, Args... args) { const std::format_string<Args...> fmt, Args... args) {
this->severity = severity; this->severity = severity;
this->lastPC = lastPC; this->lastPC = lastPC;
@@ -128,7 +126,7 @@ struct Error {
static std::string& GetError() { return GetInstance().err; } static std::string& GetError() { return GetInstance().err; }
static Severity& GetSeverity() { return GetInstance().severity; } static Severity& GetSeverity() { return GetInstance().severity; }
static Type& GetType() { return GetInstance().type; } static Type& GetType() { return GetInstance().type; }
static std::optional<u64>& GetLastPC() { return GetInstance().lastPC; } static std::optional<s64>& GetLastPC() { return GetInstance().lastPC; }
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; } static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
static bool IsHandled() { static bool IsHandled() {
return GetSeverity().as_enum == Severity::NONE; return GetSeverity().as_enum == Severity::NONE;
@@ -145,7 +143,7 @@ private:
std::string err; std::string err;
Severity severity = {}; Severity severity = {};
Type type = {}; Type type = {};
std::optional<u64> lastPC = {}; std::optional<s64> lastPC = {};
std::optional<MemoryAccess> memoryAccess = {}; std::optional<MemoryAccess> memoryAccess = {};
}; };
} }