From e140a6d124bf7da2fd9ee68577059bb3cc320e13 Mon Sep 17 00:00:00 2001 From: iris Date: Tue, 28 Apr 2026 18:01:43 +0200 Subject: [PATCH] - 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 --- CMakeLists.txt | 7 + external/parallel-rdp/ParallelRDPWrapper.cpp | 300 ++--- src/backend/Core.cpp | 28 +- src/backend/Core.hpp | 93 +- src/backend/core/CMakeLists.txt | 4 +- src/backend/core/{BaseCPU.hpp => Cache.hpp} | 12 +- src/backend/core/Interpreter.hpp | 227 ++-- src/backend/core/JIT.cpp | 393 +++--- src/backend/core/JIT.hpp | 474 ++++--- src/backend/core/Mem.cpp | 920 ++++++------- src/backend/core/Mem.hpp | 228 ++-- src/backend/core/interpreter/decode.cpp | 869 ++++++------- src/backend/core/interpreter/instructions.cpp | 1136 +++++++++-------- src/backend/core/registers/Registers.cpp | 311 +++-- src/backend/core/registers/Registers.hpp | 105 +- src/frontend/Debugger.cpp | 420 +++--- src/frontend/Debugger.hpp | 47 +- src/frontend/Settings/CPUSettings.cpp | 61 +- src/utils/Instruction.hpp | 68 +- 19 files changed, 2868 insertions(+), 2835 deletions(-) rename src/backend/core/{BaseCPU.hpp => Cache.hpp} (73%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f659f5b..6e25eb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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") diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index 3b4bff5..8a698d7 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -13,11 +13,11 @@ using namespace RDP; bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; } void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const { - if (unlocked) { - wsi->set_present_mode(PresentMode::UnlockedForceTearing); - } else { - wsi->set_present_mode(PresentMode::SyncToVBlank); - } + if (unlocked) { + wsi->set_present_mode(PresentMode::UnlockedForceTearing); + } else { + wsi->set_present_mode(PresentMode::SyncToVBlank); + } } Program *fullscreen_quad_program; @@ -25,208 +25,210 @@ Program *fullscreen_quad_program; // Copied and modified from WSI::init_context_from_platform Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices, const Context::SystemHandles &system_handles) { - VK_ASSERT(platform); - const auto instance_ext = platform->get_instance_extensions(); - const auto device_ext = platform->get_device_extensions(); - auto new_context = Util::make_handle(); + VK_ASSERT(platform); + const auto instance_ext = platform->get_instance_extensions(); + const auto device_ext = platform->get_device_extensions(); + auto new_context = Util::make_handle(); - new_context->set_application_info(platform->get_application_info()); - new_context->set_num_thread_indices(num_thread_indices); - new_context->set_system_handles(system_handles); + new_context->set_application_info(platform->get_application_info()); + 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)) { - panic("Failed to create Vulkan instance.\n"); - } + if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), + CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) { + panic("Failed to create Vulkan instance.\n"); + } - const auto 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); - const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(), - CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT); + const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(), + CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT); - if (tmp_surface) { - platform->destroy_surface(new_context->get_instance(), tmp_surface); - } + if (tmp_surface) { + platform->destroy_surface(new_context->get_instance(), tmp_surface); + } - if (!ret) { - panic("Failed to create Vulkan device.\n"); - } + if (!ret) { + panic("Failed to create Vulkan device.\n"); + } - return new_context; + return new_context; } void ParallelRDP::LoadWSIPlatform(const std::shared_ptr &wsi_platform, const std::shared_ptr &newWindowInfo) { - wsi = std::make_shared(); - wsi->set_backbuffer_srgb(false); - wsi->set_platform(wsi_platform.get()); - wsi->set_present_mode(PresentMode::SyncToVBlank); + wsi = std::make_shared(); + wsi->set_backbuffer_srgb(false); + wsi->set_platform(wsi_platform.get()); + wsi->set_present_mode(PresentMode::SyncToVBlank); - 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"); - } + 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"); + } - if (!wsi->init_device()) { - panic("Failed to initialize WSI: init_device() failed"); - } + if (!wsi->init_device()) { + panic("Failed to initialize WSI: init_device() failed"); + } - if (!wsi->init_surface_swapchain()) { - panic("Failed to initialize WSI: init_surface_swapchain() failed"); - } + if (!wsi->init_surface_swapchain()) { + panic("Failed to initialize WSI: init_surface_swapchain() failed"); + } - windowInfo = newWindowInfo; + windowInfo = newWindowInfo; } void ParallelRDP::Init(const std::shared_ptr &wsiPlatform, const std::shared_ptr &newWindowInfo, const u8 *rdram) { - LoadWSIPlatform(wsiPlatform, newWindowInfo); + LoadWSIPlatform(wsiPlatform, newWindowInfo); - ResourceLayout vertLayout; - ResourceLayout fragLayout; + ResourceLayout vertLayout; + ResourceLayout fragLayout; - vertLayout.input_mask = 1; - vertLayout.output_mask = 1; + vertLayout.input_mask = 1; + vertLayout.output_mask = 1; - fragLayout.input_mask = 1; - fragLayout.output_mask = 1; - fragLayout.spec_constant_mask = 1; - fragLayout.push_constant_size = 4 * sizeof(float); + fragLayout.input_mask = 1; + fragLayout.output_mask = 1; + fragLayout.spec_constant_mask = 1; + fragLayout.push_constant_size = 4 * sizeof(float); - fragLayout.sets[0].sampled_image_mask = 1; - fragLayout.sets[0].fp_mask = 1; - fragLayout.sets[0].array_size[0] = 1; + fragLayout.sets[0].sampled_image_mask = 1; + fragLayout.sets[0].fp_mask = 1; + fragLayout.sets[0].array_size[0] = 1; - constexpr auto sizeVert = sizeof(vertex_shader); - constexpr auto sizeFrag = sizeof(fragment_shader); + constexpr auto sizeVert = sizeof(vertex_shader); + constexpr auto sizeFrag = sizeof(fragment_shader); - fullscreen_quad_program = wsi->get_device().request_program( - reinterpret_cast(vertex_shader), sizeVert, reinterpret_cast(fragment_shader), - sizeFrag, &vertLayout, &fragLayout); + fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast(vertex_shader), sizeVert, + reinterpret_cast(fragment_shader), + sizeFrag, &vertLayout, &fragLayout); - auto aligned_rdram = reinterpret_cast(rdram); - uintptr_t offset = 0; + auto aligned_rdram = reinterpret_cast(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; - offset = aligned_rdram & align - 1; - aligned_rdram -= offset; - } + 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; + offset = aligned_rdram & align - 1; + aligned_rdram -= offset; + } - CommandProcessorFlags flags = 0; + CommandProcessorFlags flags = 0; - command_processor = std::make_shared(wsi->get_device(), reinterpret_cast(aligned_rdram), - offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); + command_processor = std::make_shared(wsi->get_device(), reinterpret_cast(aligned_rdram), + offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); - if (!command_processor->device_is_supported()) { - panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!"); - } + if (!command_processor->device_is_supported()) { + panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!"); + } } void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr image, Util::IntrusivePtr cmd) const { - cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp); - cmd->set_program(fullscreen_quad_program); - cmd->set_quad_state(); - const auto data = static_cast(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); - data[0] = -1.0f; - data[1] = -3.0f; - data[2] = -1.0f; - data[3] = +1.0f; - data[4] = +3.0f; - data[5] = +1.0f; + cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp); + cmd->set_program(fullscreen_quad_program); + cmd->set_quad_state(); + const auto data = static_cast(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); + data[0] = -1.0f; + data[1] = -3.0f; + data[2] = -1.0f; + data[3] = +1.0f; + data[4] = +3.0f; + data[5] = +1.0f; - const auto [x, y] = windowInfo->get_window_size(); + const auto [x, y] = windowInfo->get_window_size(); - const float zoom = std::min(x / static_cast(wsi->get_platform().get_surface_width()), - y / static_cast(wsi->get_platform().get_surface_height())); + const float zoom = std::min(x / static_cast(wsi->get_platform().get_surface_width()), + y / static_cast(wsi->get_platform().get_surface_height())); - const float width = static_cast(wsi->get_platform().get_surface_width()) / x * zoom; - const float height = static_cast(wsi->get_platform().get_surface_height()) / y * zoom; + const float width = static_cast(wsi->get_platform().get_surface_width()) / x * zoom; + const float height = static_cast(wsi->get_platform().get_surface_height()) / y * zoom; - const float uniform_data[] = {// Size - width, height, - // Offset - (1.0f - width) * 0.5f, (1.0f - height) * 0.5f}; + const float uniform_data[] = {// Size + width, height, + // Offset + (1.0f - width) * 0.5f, (1.0f - height) * 0.5f}; - cmd->push_constants(uniform_data, 0, sizeof(uniform_data)); + cmd->push_constants(uniform_data, 0, sizeof(uniform_data)); - cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0); - cmd->set_depth_test(false, false); - cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS); - cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); - cmd->draw(3, 1); + cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0); + cmd->set_depth_test(false, false); + cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS); + cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + cmd->draw(3, 1); } void ParallelRDP::UpdateScreen(Util::IntrusivePtr image) const { - wsi->begin_frame(); + wsi->begin_frame(); - if (!image) { - auto info = ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM); - info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT; - info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; - image = wsi->get_device().create_image(info); + if (!image) { + auto info = ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM); + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT; + info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; + image = wsi->get_device().create_image(info); - auto cmd = wsi->get_device().request_command_buffer(); + auto cmd = wsi->get_device().request_command_buffer(); - cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_ACCESS_TRANSFER_WRITE_BIT); - cmd->clear_image(*image, {}); - cmd->image_barrier(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); + cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT); + cmd->clear_image(*image, {}); + cmd->image_barrier(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); + wsi->get_device().submit(cmd); + } + + Util::IntrusivePtr cmd = wsi->get_device().request_command_buffer(); + + cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly)); + DrawFullscreenTexturedQuad(image, cmd); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->get_command_buffer()); + cmd->end_render_pass(); wsi->get_device().submit(cmd); - } - - Util::IntrusivePtr cmd = wsi->get_device().request_command_buffer(); - - cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly)); - DrawFullscreenTexturedQuad(image, cmd); - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->get_command_buffer()); - cmd->end_render_pass(); - wsi->get_device().submit(cmd); - wsi->end_frame(); + wsi->end_frame(); } template <> void ParallelRDP::UpdateScreen() const { - const n64::VI& vi = n64::Core::GetMem().mmio.vi; - command_processor->set_vi_register(VIRegister::Control, vi.status.raw); - command_processor->set_vi_register(VIRegister::Origin, vi.origin); - command_processor->set_vi_register(VIRegister::Width, vi.width); - command_processor->set_vi_register(VIRegister::Intr, vi.intr); - command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); - command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); - command_processor->set_vi_register(VIRegister::VSync, vi.vsync); - command_processor->set_vi_register(VIRegister::HSync, vi.hsync); - command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); - command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); - command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); - command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); - command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); - command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); - ScanoutOptions opts; - opts.persist_frame_on_invalid_input = true; - opts.vi.aa = true; - opts.vi.scale = true; - opts.vi.dither_filter = true; - opts.vi.divot_filter = true; - opts.vi.gamma_dither = true; - opts.downscale_steps = true; - opts.crop_overscan_pixels = true; - const Util::IntrusivePtr image = command_processor->scanout(opts); - UpdateScreen(image); - command_processor->begin_frame_context(); + const n64::VI &vi = n64::Core::GetMem().mmio.vi; + command_processor->set_vi_register(VIRegister::Control, vi.status.raw); + command_processor->set_vi_register(VIRegister::Origin, vi.origin); + command_processor->set_vi_register(VIRegister::Width, vi.width); + command_processor->set_vi_register(VIRegister::Intr, vi.intr); + command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); + command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); + command_processor->set_vi_register(VIRegister::VSync, vi.vsync); + command_processor->set_vi_register(VIRegister::HSync, vi.hsync); + command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); + command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); + command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); + command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); + command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); + command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); + ScanoutOptions opts; + opts.persist_frame_on_invalid_input = true; + opts.vi.aa = true; + opts.vi.scale = true; + opts.vi.dither_filter = true; + opts.vi.divot_filter = true; + opts.vi.gamma_dither = true; + opts.downscale_steps = true; + opts.crop_overscan_pixels = true; + const Util::IntrusivePtr image = command_processor->scanout(opts); + UpdateScreen(image); + command_processor->begin_frame_context(); } template <> void ParallelRDP::UpdateScreen() const { - UpdateScreen(static_cast>(nullptr)); + UpdateScreen(static_cast>(nullptr)); } 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); } void ParallelRDP::OnFullSync() const { command_processor->wait_for_timeline(command_processor->signal_timeline()); } diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index de5e4e2..34bacf3 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -4,18 +4,18 @@ #include namespace n64 { -Core::Core() { +Core::Core() : + interpreter(*mem, regs) +#ifdef KAIZEN_JIT_ENABLED + , + jit(*mem, regs) +#endif +{ const auto selectedCpu = Options::GetInstance().GetValue("cpu", "type"); if (selectedCpu == "interpreter") { cpuType = Interpreted; - cpu = std::make_unique(*mem, regs); } else if (selectedCpu == "jit") { -#ifndef __aarch64__ cpuType = DynamicRecompiler; - cpu = std::make_unique(*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; diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 79113c2..32dccad 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -1,62 +1,61 @@ #pragma once #include #include +#ifdef KAIZEN_JIT_ENABLED #include +#endif #include #include -#include +#include namespace n64 { struct Core { - enum CPUType { - Interpreted, - DynamicRecompiler, - CachedInterpreter - } cpuType = Interpreted; - - explicit Core(); - - static Core& GetInstance() { - static Core instance; - return instance; - } - - static Registers& GetRegs() { - return GetInstance().regs; - } - - static Mem& GetMem() { - return *GetInstance().mem; - } + enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted; - u32 StepCPU(); - void StepRSP(u32 cpuCycles); - void Stop(); - void Reset(); - void LoadROM(const std::string &); - void LoadTAS(const fs::path &) const; - void Run(float volumeL, float volumeR); - void TogglePause() { pause = !pause; } - inline void ToggleBreakpoint(s64 addr) { - if(breakpoints.contains(addr)) { - breakpoints.erase(addr); - return; + explicit Core(); + + static Core &GetInstance() { + static Core instance; + return instance; } - - breakpoints.insert(addr); - } - bool pause = true; - bool romLoaded = false; - int slot = 0; - u32 cycles = 0; - size_t memSize{}, cpuSize{}, verSize{}; - std::string rom; - std::set breakpoints{}; - std::unique_ptr mem = std::make_unique(); - std::unique_ptr cpu; + static Registers &GetRegs() { return GetInstance().regs; } - Registers regs; - ParallelRDP parallel; + static Mem &GetMem() { return *GetInstance().mem; } + + u32 StepCPU(); + void StepRSP(u32 cpuCycles); + void Stop(); + void Reset(); + void LoadROM(const std::string &); + void LoadTAS(const fs::path &) const; + void Run(float volumeL, float volumeR); + void TogglePause() { pause = !pause; } + inline void ToggleBreakpoint(s64 addr) { + if (breakpoints.contains(addr)) { + breakpoints.erase(addr); + return; + } + + breakpoints.insert(addr); + } + + bool pause = true; + bool romLoaded = false; + int slot = 0; + u32 cycles = 0; + size_t memSize{}, cpuSize{}, verSize{}; + std::string rom; + std::set breakpoints{}; +#ifdef KAIZEN_JIT_ENABLED + JIT jit; + std::unique_ptr mem = std::make_unique(jit); + Registers regs(jit); +#else + std::unique_ptr mem = std::make_unique(); + Registers regs; +#endif + Interpreter interpreter; + ParallelRDP parallel; }; } // namespace n64 diff --git a/src/backend/core/CMakeLists.txt b/src/backend/core/CMakeLists.txt index e658ad0..7c5d618 100644 --- a/src/backend/core/CMakeLists.txt +++ b/src/backend/core/CMakeLists.txt @@ -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() \ No newline at end of file diff --git a/src/backend/core/BaseCPU.hpp b/src/backend/core/Cache.hpp similarity index 73% rename from src/backend/core/BaseCPU.hpp rename to src/backend/core/Cache.hpp index c69448f..31e7b5a 100644 --- a/src/backend/core/BaseCPU.hpp +++ b/src/backend/core/Cache.hpp @@ -1,7 +1,5 @@ #pragma once -#include -#include -#include +#include 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 diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 1bac7a4..03e4b65 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -1,125 +1,128 @@ #pragma once -#include +#include #include -#include namespace n64 { struct Core; -struct Interpreter final : BaseCPU { - explicit Interpreter(Mem&, Registers&); - ~Interpreter() override = default; - u32 Step() override; +struct Interpreter final { + explicit Interpreter(Mem &, Registers &); + ~Interpreter() = default; + u32 Step(); - void Reset() override { - cop2Latch = {}; - } + void Reset() { cop2Latch = {}; } -private: - Registers& regs; - Mem& mem; - u64 cop2Latch{}; - friend struct Cop1; + 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; - void CheckCompareInterrupt() const; + (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) + [[nodiscard]] bool ShouldServiceInterrupt() const; + void CheckCompareInterrupt() const; - void cop2Decode(Instruction); - void special(Instruction); - void regimm(Instruction); - void Exec(Instruction); - void add(Instruction); - void addu(Instruction); - void addi(Instruction); - void addiu(Instruction); - void andi(Instruction); - void and_(Instruction); - void branch(bool, s64); - void branch_likely(bool, s64); - void b(Instruction, bool); - void blink(Instruction, bool); - void bl(Instruction, bool); - void bllink(Instruction, bool); - void dadd(Instruction); - void daddu(Instruction); - void daddi(Instruction); - void daddiu(Instruction); - void ddiv(Instruction); - void ddivu(Instruction); - void div(Instruction); - void divu(Instruction); - void dmult(Instruction); - void dmultu(Instruction); - void dsll(Instruction); - void dsllv(Instruction); - void dsll32(Instruction); - void dsra(Instruction); - void dsrav(Instruction); - void dsra32(Instruction); - void dsrl(Instruction); - void dsrlv(Instruction); - void dsrl32(Instruction); - void dsub(Instruction); - void dsubu(Instruction); - void j(Instruction); - void jr(Instruction); - void jal(Instruction); - void jalr(Instruction); - void lui(Instruction); - void lbu(Instruction); - void lb(Instruction); - void ld(Instruction); - void ldl(Instruction); - void ldr(Instruction); - void lh(Instruction); - void lhu(Instruction); - void ll(Instruction); - void lld(Instruction); - void lw(Instruction); - void lwl(Instruction); - void lwu(Instruction); - void lwr(Instruction); - void mfhi(Instruction); - void mflo(Instruction); - void mult(Instruction); - void multu(Instruction); - void mthi(Instruction); - void mtlo(Instruction); - void nor(Instruction); - void sb(Instruction); - void sc(Instruction); - void scd(Instruction); - void sd(Instruction); - void sdl(Instruction); - void sdr(Instruction); - void sh(Instruction); - void sw(Instruction); - void swl(Instruction); - void swr(Instruction); - void slti(Instruction); - void sltiu(Instruction); - void slt(Instruction); - void sltu(Instruction); - void sll(Instruction); - void sllv(Instruction); - void sub(Instruction); - void subu(Instruction); - void sra(Instruction); - void srav(Instruction); - void srl(Instruction); - void srlv(Instruction); - void trap(bool) const; - void or_(Instruction); - void ori(Instruction); - void xor_(Instruction); - void xori(Instruction); + void cop2Decode(Instruction); + void special(Instruction); + void regimm(Instruction); + void Exec(Instruction); + void add(Instruction); + void addu(Instruction); + void addi(Instruction); + void addiu(Instruction); + void andi(Instruction); + void and_(Instruction); + void branch(bool, s64); + void branch_likely(bool, s64); + void b(Instruction, bool); + void blink(Instruction, bool); + void bl(Instruction, bool); + void bllink(Instruction, bool); + void cache(Instruction); + void dadd(Instruction); + void daddu(Instruction); + void daddi(Instruction); + void daddiu(Instruction); + void ddiv(Instruction); + void ddivu(Instruction); + void div(Instruction); + void divu(Instruction); + void dmult(Instruction); + void dmultu(Instruction); + void dsll(Instruction); + void dsllv(Instruction); + void dsll32(Instruction); + void dsra(Instruction); + void dsrav(Instruction); + void dsra32(Instruction); + void dsrl(Instruction); + void dsrlv(Instruction); + void dsrl32(Instruction); + void dsub(Instruction); + void dsubu(Instruction); + void j(Instruction); + void jr(Instruction); + void jal(Instruction); + void jalr(Instruction); + void lui(Instruction); + void lbu(Instruction); + void lb(Instruction); + void ld(Instruction); + void ldl(Instruction); + void ldr(Instruction); + void lh(Instruction); + void lhu(Instruction); + void ll(Instruction); + void lld(Instruction); + void lw(Instruction); + void lwl(Instruction); + void lwu(Instruction); + void lwr(Instruction); + void mfhi(Instruction); + void mflo(Instruction); + void mult(Instruction); + void multu(Instruction); + void mthi(Instruction); + void mtlo(Instruction); + void nor(Instruction); + void sb(Instruction); + void sc(Instruction); + void scd(Instruction); + void sd(Instruction); + void sdl(Instruction); + void sdr(Instruction); + void sh(Instruction); + void sw(Instruction); + void swl(Instruction); + void swr(Instruction); + void slti(Instruction); + void sltiu(Instruction); + void slt(Instruction); + void sltu(Instruction); + void sll(Instruction); + void sllv(Instruction); + void sub(Instruction); + void subu(Instruction); + void sra(Instruction); + void srav(Instruction); + void srl(Instruction); + void srlv(Instruction); + void trap(bool) const; + void or_(Instruction); + void ori(Instruction); + void xor_(Instruction); + void xori(Instruction); - void mtc2(Instruction); - void mfc2(Instruction); - void dmtc2(Instruction); - void dmfc2(Instruction); - void ctc2(Instruction); - void cfc2(Instruction); + void mtc2(Instruction); + void mfc2(Instruction); + void dmtc2(Instruction); + void dmfc2(Instruction); + void ctc2(Instruction); + void cfc2(Instruction); }; } // namespace n64 diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index e2b6dff..8122ecc 100644 --- a/src/backend/core/JIT.cpp +++ b/src/backend/core/JIT.cpp @@ -1,246 +1,243 @@ #include -#include +#include +#include namespace n64 { -#ifndef __aarch64__ -JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) { - regs.SetJIT(this); - mem.SetJIT(this); - blockCache.resize(kUpperSize); - if (cs_open(CS_ARCH_MIPS, static_cast(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) != - CS_ERR_OK) { - panic("Failed to initialize MIPS disassembler"); - } +JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) { + blockCache.resize(kUpperSize); + if (cs_open(CS_ARCH_MIPS, static_cast(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) != + CS_ERR_OK) { + panic("Failed to initialize MIPS disassembler"); + } - if (cs_open(CS_ARCH_X86, static_cast(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) { - panic("Failed to initialize x86 disassembler"); - } + if (cs_open(CS_ARCH_X86, static_cast(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) { + panic("Failed to initialize x86 disassembler"); + } } bool JIT::ShouldServiceInterrupt() const { - const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; - const bool interrupts_enabled = regs.cop0.status.ie == 1; - const bool currently_handling_exception = regs.cop0.status.exl == 1; - const bool currently_handling_error = regs.cop0.status.erl == 1; + const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; + const bool interrupts_enabled = regs.cop0.status.ie == 1; + const bool currently_handling_exception = regs.cop0.status.exl == 1; + const bool currently_handling_error = regs.cop0.status.erl == 1; - 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() const { - regs.cop0.count++; - regs.cop0.count &= 0x1FFFFFFFF; - if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { - regs.cop0.cause.ip7 = 1; - Core::GetMem().mmio.mi.UpdateInterrupt(); - } + regs.cop0.count++; + regs.cop0.count &= 0x1FFFFFFFF; + if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { + regs.cop0.cause.ip7 = 1; + Core::GetMem().mmio.mi.UpdateInterrupt(); + } } void JIT::InvalidateBlock(const u32 paddr) { - if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty()) - blockCache[index] = {}; + if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty()) + blockCache[index] = {}; } std::optional JIT::FetchInstruction(s64 vaddr) { - u32 paddr = 0; + u32 paddr = 0; - if (check_address_error(0b11, vaddr)) [[unlikely]] { - /*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 std::nullopt; - } + if (check_address_error(0b11, vaddr)) [[unlikely]] { + /*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 std::nullopt; + } - if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { - /*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(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); - return std::nullopt; - } + if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { + /*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(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); + return std::nullopt; + } - const u32 instr = Core::GetMem().Read(paddr); + const u32 instr = Core::GetMem().Read(paddr); - info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full); + info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full); - return instr; + return instr; } void JIT::SetPC32(const s32 val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1.cvt32(), val); - code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1.cvt32(), val + 4); - code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); + code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); + code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); + code.mov(code.SCR1.cvt32(), val); + code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); + code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); + code.mov(code.SCR1.cvt32(), val + 4); + code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); + code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); } void JIT::SetPC64(const s64 val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1, val); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1, val + 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); + code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); + code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); + code.mov(code.SCR1, val); + code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); + code.mov(code.SCR1, val + 4); + code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); } -void JIT::SetPC32(const Xbyak::Reg32& val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.movsxd(val.cvt64(), val); - code.mov(code.qword[code.rbp + PC_OFFSET], val); - code.add(val, 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); +void JIT::SetPC32(const Xbyak::Reg32 &val) { + code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); + code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); + code.movsxd(val.cvt64(), val); + code.mov(code.qword[code.rbp + PC_OFFSET], val); + code.add(val, 4); + code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); } -void JIT::SetPC64(const Xbyak::Reg64& val) { - code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); - code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.qword[code.rbp + PC_OFFSET], val); - code.add(val, 4); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); +void JIT::SetPC64(const Xbyak::Reg64 &val) { + code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); + code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); + code.mov(code.qword[code.rbp + PC_OFFSET], val); + code.add(val, 4); + code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); } u32 JIT::Step() { - blockOldPC = regs.oldPC; - blockPC = regs.pc; - blockNextPC = regs.nextPC; - u32 paddr = 0; + blockOldPC = regs.oldPC; + blockPC = regs.pc; + blockNextPC = regs.nextPC; + u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { - /*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(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); - return 0; - } - - const u32 upperIndex = paddr >> kUpperShift; - const u32 lowerIndex = paddr & kLowerMask; - - if (!blockCache[upperIndex].empty()) { - if (blockCache[upperIndex][lowerIndex]) { - // trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC); - return blockCache[upperIndex][lowerIndex](); - } - } else { - blockCache[upperIndex].resize(kLowerSize); - } - - info("[JIT]: Compiling block @ 0x{:016X}:", static_cast(blockPC)); - const auto blockInfo = code.getCurr(); - const auto block = code.getCurr(); - blockCache[upperIndex][lowerIndex] = block; - - code.setProtectModeRW(); - - u32 instructionsInBlock = 0; - - bool instrEndsBlock = false; - - code.sub(code.rsp, 8); - code.push(code.rbp); - code.mov(code.rbp, reinterpret_cast(this)); // Load context pointer - - cs_insn *insn; - info("\tMIPS code (guest PC = 0x{:016X}):", static_cast(blockPC)); - - emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - - while (true) { - paddr = 0; - - auto instruction = FetchInstruction(blockPC); - - if(!instruction) - return 0; - - instructionsInBlock++; - - blockOldPC = blockPC; - blockPC = blockNextPC; - blockNextPC += 4; - - if(InstrEndsBlock(instruction.value())) { - const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot - if(!delay_instruction) - return 0; - - if(InstrEndsBlock(delay_instruction.value())) { + if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { + /*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::JIT_BRANCH_INSIDE_DELAY_SLOT}, - blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, + "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", + static_cast(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); return 0; - } - - instructionsInBlock++; - - blockOldPC = blockPC; - blockPC = blockNextPC; - blockNextPC += 4; - - Emit(delay_instruction.value()); - Emit(instruction.value()); - - if(!branch_taken) { - Xbyak::Label runtime_branch_taken; - 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.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); - code.mov(code.SCR1, blockPC); - code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); - code.mov(code.SCR1, blockNextPC); - code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); - code.L(runtime_branch_taken); - } - - if(branch_taken) branch_taken = false; - - emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - - break; } - Emit(instruction.value()); + const u32 upperIndex = paddr >> kUpperShift; + const u32 lowerIndex = paddr & kLowerMask; + + if (!blockCache[upperIndex].empty()) { + if (blockCache[upperIndex][lowerIndex]) { + // trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC); + return blockCache[upperIndex][lowerIndex](); + } + } else { + blockCache[upperIndex].resize(kLowerSize); + } + + info("[JIT]: Compiling block @ 0x{:016X}:", static_cast(blockPC)); + const auto blockInfo = code.getCurr(); + const auto block = code.getCurr(); + blockCache[upperIndex][lowerIndex] = block; + + code.setProtectModeRW(); + + u32 instructionsInBlock = 0; + + bool instrEndsBlock = false; + + code.sub(code.rsp, 8); + code.push(code.rbp); + code.mov(code.rbp, reinterpret_cast(this)); // Load context pointer + + cs_insn *insn; + info("\tMIPS code (guest PC = 0x{:016X}):", static_cast(blockPC)); emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); - } - code.mov(code.rax, instructionsInBlock); - code.pop(code.rbp); - code.add(code.rsp, 8); - code.ret(); - code.setProtectModeRE(); - static size_t blockInfoSize = 0; - blockInfoSize = code.getSize() - blockInfoSize; + while (true) { + paddr = 0; - info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast(block)); - const auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast(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); + auto instruction = FetchInstruction(blockPC); + + if (!instruction) + return 0; + + instructionsInBlock++; + + blockOldPC = blockPC; + blockPC = blockNextPC; + blockNextPC += 4; + + if (InstrEndsBlock(instruction.value())) { + const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot + if (!delay_instruction) + return 0; + + if (InstrEndsBlock(delay_instruction.value())) { + Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, + {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {}, + "[JIT]: Unhandled case of branch from delay slot!"); + return 0; + } + + instructionsInBlock++; + + blockOldPC = blockPC; + blockPC = blockNextPC; + blockNextPC += 4; + + Emit(delay_instruction.value()); + Emit(instruction.value()); + + if (!branch_taken) { + Xbyak::Label runtime_branch_taken; + 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.qword[code.rbp + OLD_PC_OFFSET], code.SCR1); + code.mov(code.SCR1, blockPC); + code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); + code.mov(code.SCR1, blockNextPC); + code.mov(code.qword[code.rbp + 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); } - - cs_free(insn, count); - } - // panic(""); - return block(); + + code.mov(code.rax, instructionsInBlock); + code.pop(code.rbp); + code.add(code.rsp, 8); + code.ret(); + code.setProtectModeRE(); + static size_t blockInfoSize = 0; + blockInfoSize = code.getSize() - blockInfoSize; + + info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast(block)); + const auto count = + cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast(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); + } + + cs_free(insn, count); + } + // panic(""); + return block(); } -void JIT::DumpBlockCacheToDisk() const { - ircolib::WriteFileBinary(code.getCode(), code.getSize(), "jit.dump"); -} -#endif +void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode(), code.getSize(), "jit.dump"); } } // namespace n64 diff --git a/src/backend/core/JIT.hpp b/src/backend/core/JIT.hpp index 2948d01..b8c3c1d 100644 --- a/src/backend/core/JIT.hpp +++ b/src/backend/core/JIT.hpp @@ -1,5 +1,6 @@ #pragma once -#include +#include +#include #include #include #include @@ -25,262 +26,249 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb; #define HI_OFFSET (reinterpret_cast(®s.hi) - reinterpret_cast(this)) #define LO_OFFSET (reinterpret_cast(®s.lo) - reinterpret_cast(this)) -#ifdef __aarch64__ -struct JIT : BaseCPU {}; -#else -struct JIT final : BaseCPU { - explicit JIT(Mem&, Registers&); - ~JIT() override = default; - u32 Step() override; +struct JIT final { + explicit JIT(Mem &, Registers &); + ~JIT() = default; + u32 Step(); - void Reset() override { - code.reset(); - blockCache = {}; - blockCache.resize(kUpperSize); - } - - void DumpBlockCacheToDisk() const; - - void AdvanceDelaySlot() { - regs.prevDelaySlot = regs.delaySlot; - regs.delaySlot = false; - } - - void InvalidateBlock(u32); -private: - friend struct Cop1; - friend struct Registers; - using BlockFn = int (*)(); - - bool branch_taken; - Registers& regs; - Mem& mem; - u64 cop2Latch{}; - s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0; - Xbyak::CodeGenerator code{kCodeCacheAllocSize}; - csh disassemblerMips{}, disassemblerX86{}; - std::vector> blockCache; - - template - Xbyak::Address GPR(const size_t index) { - if constexpr (sizeof(T) == 1) { - return code.byte[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 2) { - return code.word[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 4) { - return code.dword[code.rbp + GPR_OFFSET(index)]; - } else if constexpr (sizeof(T) == 8) { - return code.qword[code.rbp + GPR_OFFSET(index)]; + void Reset() { + code.reset(); + blockCache = {}; + blockCache.resize(kUpperSize); } - 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}; - } + void DumpBlockCacheToDisk() const; + + void AdvanceDelaySlot() { + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + } + + void InvalidateBlock(u32); + + private: + friend struct Cop1; + friend struct Registers; + using BlockFn = int (*)(); + + bool branch_taken; + Registers ®s; + Mem &mem; + u64 cop2Latch{}; + s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0; + Xbyak::CodeGenerator code{kCodeCacheAllocSize}; + csh disassemblerMips{}, disassemblerX86{}; + std::vector> blockCache; + + template + Xbyak::Address GPR(const size_t index) { + if constexpr (sizeof(T) == 1) { + return code.byte[code.rbp + GPR_OFFSET(index)]; + } else if constexpr (sizeof(T) == 2) { + return code.word[code.rbp + GPR_OFFSET(index)]; + } else if constexpr (sizeof(T) == 4) { + return code.dword[code.rbp + GPR_OFFSET(index)]; + } else if constexpr (sizeof(T) == 8) { + 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)); + return Xbyak::Address{0}; + } - // Thanks to https://github.com/grumpycoders/pcsx-redux - // Load a pointer to the JIT object in "reg" - template - void emitMemberFunctionCall(T func, void *thisObject) { - uintptr_t functionPtr; - auto thisPtr = reinterpret_cast(thisObject); + // Thanks to https://github.com/grumpycoders/pcsx-redux + // Load a pointer to the JIT object in "reg" + template + void emitMemberFunctionCall(T func, void *thisObject) { + uintptr_t functionPtr; + auto thisPtr = reinterpret_cast(thisObject); #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) - static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer"); - std::memcpy(&functionPtr, &func, sizeof(T)); + static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer"); + std::memcpy(&functionPtr, &func, sizeof(T)); #else - static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer"); - uintptr_t arr[2]; - std::memcpy(arr, &func, sizeof(T)); - // First 8 bytes correspond to the actual pointer to the function - functionPtr = reinterpret_cast(reinterpret_cast(arr[0])); - // Next 8 bytes correspond to the "this" pointer adjustment - thisPtr += arr[1]; + static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer"); + uintptr_t arr[2]; + std::memcpy(arr, &func, sizeof(T)); + // First 8 bytes correspond to the actual pointer to the function + functionPtr = reinterpret_cast(reinterpret_cast(arr[0])); + // Next 8 bytes correspond to the "this" pointer adjustment + thisPtr += arr[1]; #endif - code.mov(code.ARG1, thisPtr); - code.mov(code.rax, functionPtr); - code.sub(code.rsp, 8); - code.call(code.rax); - code.add(code.rsp, 8); - } + code.mov(code.ARG1, thisPtr); + code.mov(code.rax, functionPtr); + code.sub(code.rsp, 8); + code.call(code.rax); + code.add(code.rsp, 8); + } - void SetPC32(s32 val); - void SetPC64(s64 val); - void SetPC32(const Xbyak::Reg32& val); - void SetPC64(const Xbyak::Reg64& val); - void BranchNotTaken(); - void BranchTaken(s64 offs); - void BranchTaken(const Xbyak::Reg64 &offs); - void BranchAbsTaken(s64 addr); - void BranchAbsTaken(const Xbyak::Reg64 &addr); + void SetPC32(s32 val); + void SetPC64(s64 val); + void SetPC32(const Xbyak::Reg32 &val); + void SetPC64(const Xbyak::Reg64 &val); + void BranchNotTaken(); + void BranchTaken(s64 offs); + void BranchTaken(const Xbyak::Reg64 &offs); + void BranchAbsTaken(s64 addr); + void BranchAbsTaken(const Xbyak::Reg64 &addr); #define check_address_error(mask, vaddr) \ - (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) + (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) - [[nodiscard]] bool ShouldServiceInterrupt() const; - void CheckCompareInterrupt() const; - std::optional FetchInstruction(s64); + [[nodiscard]] bool ShouldServiceInterrupt() const; + void CheckCompareInterrupt() const; + std::optional FetchInstruction(s64); - void Emit(Instruction); - void special(Instruction); - void regimm(Instruction); - void add(Instruction); - void addu(Instruction); - void addi(Instruction); - void addiu(Instruction); - void andi(Instruction); - void and_(Instruction); - void branch_constant(bool cond, s64 offset); - void branch_likely_constant(bool cond, s64 offset); - void branch_abs_constant(bool cond, s64 address); - void bltz(Instruction); - void bgez(Instruction); - void bltzl(Instruction); - void bgezl(Instruction); - void bltzal(Instruction); - void bgezal(Instruction); - void bltzall(Instruction); - void bgezall(Instruction); - void beq(Instruction); - void beql(Instruction); - void bne(Instruction); - void bnel(Instruction); - void blez(Instruction); - void blezl(Instruction); - void bgtz(Instruction); - void bgtzl(Instruction); - void bfc1(Instruction); - void blfc1(Instruction); - void bfc0(Instruction); - void blfc0(Instruction); - void dadd(Instruction); - void daddu(Instruction); - void daddi(Instruction); - void daddiu(Instruction); - void ddiv(Instruction); - void ddivu(Instruction); - void div(Instruction); - void divu(Instruction); - void dmult(Instruction); - void dmultu(Instruction); - void dsll(Instruction); - void dsllv(Instruction); - void dsll32(Instruction); - void dsra(Instruction); - void dsrav(Instruction); - void dsra32(Instruction); - void dsrl(Instruction); - void dsrlv(Instruction); - void dsrl32(Instruction); - void dsub(Instruction); - void dsubu(Instruction); - void j(Instruction); - void jr(Instruction); - void jal(Instruction); - void jalr(Instruction); - void lui(Instruction); - void lbu(Instruction); - void lb(Instruction); - void ld(Instruction); - void ldc1(Instruction); - void ldl(Instruction); - void ldr(Instruction); - void lh(Instruction); - void lhu(Instruction); - void ll(Instruction); - void lld(Instruction); - void lw(Instruction); - void lwc1(Instruction); - void lwl(Instruction); - void lwu(Instruction); - void lwr(Instruction); - void mfhi(Instruction); - void mflo(Instruction); - void mult(Instruction); - void multu(Instruction); - void mthi(Instruction); - void mtlo(Instruction); - void nor(Instruction); - void sb(const 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}, - blockPC, {}, "[JIT]: Unhandled 'sc'!"); - } - void scd(const 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}, - blockPC, {}, "[JIT]: Unhandled 'sd'!"); - } - void sdc1(const 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}, - blockPC, {}, "[JIT]: Unhandled 'sdl'!"); - } - void sdr(const 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}, - 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}, - blockPC, {}, "[JIT]: Unhandled 'swl'!"); - } - void swr(const Instruction) { - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, - blockPC, {}, "[JIT]: Unhandled 'swr'!"); - } - void slti(Instruction); - void sltiu(Instruction); - void slt(Instruction); - void sltu(Instruction); - void sll(Instruction); - void sllv(Instruction); - 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!"); - } - 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!"); - } - void or_(Instruction); - void ori(Instruction); - void xor_(Instruction); - void xori(Instruction); + void Emit(Instruction); + void special(Instruction); + void regimm(Instruction); + void add(Instruction); + void addu(Instruction); + void addi(Instruction); + void addiu(Instruction); + void andi(Instruction); + void and_(Instruction); + void branch_constant(bool cond, s64 offset); + void branch_likely_constant(bool cond, s64 offset); + void branch_abs_constant(bool cond, s64 address); + void bltz(Instruction); + void bgez(Instruction); + void bltzl(Instruction); + void bgezl(Instruction); + void bltzal(Instruction); + void bgezal(Instruction); + void bltzall(Instruction); + void bgezall(Instruction); + void beq(Instruction); + void beql(Instruction); + void bne(Instruction); + void bnel(Instruction); + void blez(Instruction); + void blezl(Instruction); + void bgtz(Instruction); + void bgtzl(Instruction); + void bfc1(Instruction); + void blfc1(Instruction); + void bfc0(Instruction); + void blfc0(Instruction); + void dadd(Instruction); + void daddu(Instruction); + void daddi(Instruction); + void daddiu(Instruction); + void ddiv(Instruction); + void ddivu(Instruction); + void div(Instruction); + void divu(Instruction); + void dmult(Instruction); + void dmultu(Instruction); + void dsll(Instruction); + void dsllv(Instruction); + void dsll32(Instruction); + void dsra(Instruction); + void dsrav(Instruction); + void dsra32(Instruction); + void dsrl(Instruction); + void dsrlv(Instruction); + void dsrl32(Instruction); + void dsub(Instruction); + void dsubu(Instruction); + void j(Instruction); + void jr(Instruction); + void jal(Instruction); + void jalr(Instruction); + void lui(Instruction); + void lbu(Instruction); + void lb(Instruction); + void ld(Instruction); + void ldc1(Instruction); + void ldl(Instruction); + void ldr(Instruction); + void lh(Instruction); + void lhu(Instruction); + void ll(Instruction); + void lld(Instruction); + void lw(Instruction); + void lwc1(Instruction); + void lwl(Instruction); + void lwu(Instruction); + void lwr(Instruction); + void mfhi(Instruction); + void mflo(Instruction); + void mult(Instruction); + void multu(Instruction); + void mthi(Instruction); + void mtlo(Instruction); + void nor(Instruction); + void sb(const 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}, + blockPC, {}, "[JIT]: Unhandled 'sc'!"); + } + void scd(const 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}, + blockPC, {}, "[JIT]: Unhandled 'sd'!"); + } + void sdc1(const 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}, + blockPC, {}, "[JIT]: Unhandled 'sdl'!"); + } + void sdr(const 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}, + 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}, + blockPC, {}, "[JIT]: Unhandled 'swl'!"); + } + void swr(const Instruction) { + Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, + blockPC, {}, "[JIT]: Unhandled 'swr'!"); + } + void slti(Instruction); + void sltiu(Instruction); + void slt(Instruction); + void sltu(Instruction); + void sll(Instruction); + void sllv(Instruction); + 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!"); + } + 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!"); + } + void or_(Instruction); + void ori(Instruction); + void xor_(Instruction); + void xori(Instruction); }; -#endif } // namespace n64 diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index eadd4a8..bd53f81 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -4,553 +4,571 @@ #include #include #include +#include namespace n64 { +#ifdef KAIZEN_JIT_ENABLED +Mem::Mem(JIT &jit) : flash(saveData), jit(jit) { +#else Mem::Mem() : flash(saveData) { - rom.cart.resize(CART_SIZE); - std::ranges::fill(rom.cart, 0); - isviewer_sink = std::ofstream("isviewer.log"); +#endif + rom.cart.resize(CART_SIZE); + std::ranges::fill(rom.cart, 0); + isviewer_sink = std::ofstream("isviewer.log"); } void Mem::Reset() { - std::ranges::fill(isviewer, 0); - flash.Reset(); - if (saveData.is_mapped()) { - 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!"); - return; + std::ranges::fill(isviewer, 0); + flash.Reset(); + if (saveData.is_mapped()) { + 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!"); + return; + } + saveData.unmap(); } - saveData.unmap(); - } - mmio.Reset(); + mmio.Reset(); } void Mem::LoadSRAM(SaveType save_type, fs::path path) { - if (save_type == SAVE_SRAM_256k) { - std::error_code error; - std::string savePath = Options::GetInstance().GetValue("general", "savePath"); - if (!savePath.empty()) { - path = savePath / path.filename(); - } - sramPath = path.replace_extension(".sram").string(); - 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); - return; - } - saveData.unmap(); - } + if (save_type == SAVE_SRAM_256k) { + std::error_code error; + std::string savePath = Options::GetInstance().GetValue("general", "savePath"); + if (!savePath.empty()) { + path = savePath / path.filename(); + } + sramPath = path.replace_extension(".sram").string(); + 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); + return; + } + saveData.unmap(); + } - auto sramVec = ircolib::ReadFileBinary(sramPath); - if (sramVec.empty()) { - ircolib::WriteFileBinary(std::array{}, sramPath); - sramVec = ircolib::ReadFileBinary(sramPath); - } + auto sramVec = ircolib::ReadFileBinary(sramPath); + if (sramVec.empty()) { + ircolib::WriteFileBinary(std::array{}, sramPath); + sramVec = ircolib::ReadFileBinary(sramPath); + } - 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); - return; - } + 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); + 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); + 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); + } } - } } FORCE_INLINE void SetROMCIC(u32 checksum, ROM &rom) { - switch (checksum) { - case 0xEC8B1325: - rom.cicType = CIC_NUS_7102; - break; // 7102 - case 0x1DEB51A9: - rom.cicType = CIC_NUS_6101; - break; // 6101 - case 0xC08E5BD6: - rom.cicType = CIC_NUS_6102_7101; - break; - case 0x03B8376A: - rom.cicType = CIC_NUS_6103_7103; - break; - case 0xCF7F41DC: - rom.cicType = CIC_NUS_6105_7105; - break; - case 0xD1059C6A: - rom.cicType = CIC_NUS_6106_7106; - break; - default: - warn("Could not determine CIC TYPE! Checksum: 0x{:08X} is unknown!", checksum); - rom.cicType = UNKNOWN_CIC_TYPE; - break; - } + switch (checksum) { + case 0xEC8B1325: + rom.cicType = CIC_NUS_7102; + break; // 7102 + case 0x1DEB51A9: + rom.cicType = CIC_NUS_6101; + break; // 6101 + case 0xC08E5BD6: + rom.cicType = CIC_NUS_6102_7101; + break; + case 0x03B8376A: + rom.cicType = CIC_NUS_6103_7103; + break; + case 0xCF7F41DC: + rom.cicType = CIC_NUS_6105_7105; + break; + case 0xD1059C6A: + rom.cicType = CIC_NUS_6106_7106; + break; + default: + warn("Could not determine CIC TYPE! Checksum: 0x{:08X} is unknown!", checksum); + rom.cicType = UNKNOWN_CIC_TYPE; + break; + } } void Mem::LoadROM(const bool isArchive, const std::string &filename) { - u32 endianness; - { - size_t sizeAdjusted; - std::vector buf{}; - if (isArchive) { - buf = Util::OpenArchive(filename, sizeAdjusted); - } else { - buf = Util::OpenROM(filename, sizeAdjusted); + u32 endianness; + { + size_t sizeAdjusted; + std::vector buf{}; + if (isArchive) { + buf = Util::OpenArchive(filename, sizeAdjusted); + } else { + buf = Util::OpenROM(filename, sizeAdjusted); + } + + endianness = std::byteswap(ircolib::ReadAccess(buf, 0)); + Util::SwapN64Rom(buf, endianness); + + std::ranges::copy(buf, rom.cart.begin()); + rom.mask = sizeAdjusted - 1; + memcpy(&rom.header, buf.data(), sizeof(ROMHeader)); + } + memcpy(rom.gameNameCart, rom.header.imageName, sizeof(rom.header.imageName)); + + rom.header.clockRate = std::byteswap(rom.header.clockRate); + rom.header.programCounter = std::byteswap(rom.header.programCounter); + rom.header.release = std::byteswap(rom.header.release); + rom.header.crc1 = std::byteswap(rom.header.crc1); + rom.header.crc2 = std::byteswap(rom.header.crc2); + rom.header.unknown = std::byteswap(rom.header.unknown); + rom.header.unknown2 = std::byteswap(rom.header.unknown2); + rom.header.manufacturerId = std::byteswap(rom.header.manufacturerId); + rom.header.cartridgeId = std::byteswap(rom.header.cartridgeId); + + rom.code[0] = rom.header.manufacturerId & 0xFF; + rom.code[1] = (rom.header.cartridgeId >> 8) & 0xFF; + rom.code[2] = rom.header.cartridgeId & 0xFF; + rom.code[3] = '\0'; + + for (int i = sizeof(rom.header.imageName) - 1; rom.gameNameCart[i] == ' '; i--) { + rom.gameNameCart[i] = '\0'; } - endianness = std::byteswap(ircolib::ReadAccess(buf, 0)); - Util::SwapN64Rom(buf, endianness); - - std::ranges::copy(buf, rom.cart.begin()); - rom.mask = sizeAdjusted - 1; - memcpy(&rom.header, buf.data(), sizeof(ROMHeader)); - } - memcpy(rom.gameNameCart, rom.header.imageName, sizeof(rom.header.imageName)); - - rom.header.clockRate = std::byteswap(rom.header.clockRate); - rom.header.programCounter = std::byteswap(rom.header.programCounter); - rom.header.release = std::byteswap(rom.header.release); - rom.header.crc1 = std::byteswap(rom.header.crc1); - rom.header.crc2 = std::byteswap(rom.header.crc2); - rom.header.unknown = std::byteswap(rom.header.unknown); - rom.header.unknown2 = std::byteswap(rom.header.unknown2); - rom.header.manufacturerId = std::byteswap(rom.header.manufacturerId); - rom.header.cartridgeId = std::byteswap(rom.header.cartridgeId); - - rom.code[0] = rom.header.manufacturerId & 0xFF; - rom.code[1] = (rom.header.cartridgeId >> 8) & 0xFF; - rom.code[2] = rom.header.cartridgeId & 0xFF; - rom.code[3] = '\0'; - - for (int i = sizeof(rom.header.imageName) - 1; rom.gameNameCart[i] == ' '; i--) { - rom.gameNameCart[i] = '\0'; - } - - const u32 checksum = SDL_crc32(0, &rom.cart[0x40], 0x9C0); - SetROMCIC(checksum, rom); - endianness = std::byteswap(ircolib::ReadAccess(rom.cart, 0)); - Util::SwapN64Rom(rom.cart, endianness); - rom.pal = IsROMPAL(); + const u32 checksum = SDL_crc32(0, &rom.cart[0x40], 0x9C0); + SetROMCIC(checksum, rom); + endianness = std::byteswap(ircolib::ReadAccess(rom.cart, 0)); + Util::SwapN64Rom(rom.cart, endianness); + rom.pal = IsROMPAL(); } template <> u8 Mem::Read(const u32 paddr) { - n64::Registers& regs = n64::Core::GetRegs(); - const SI &si = mmio.si; + 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(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, RDRAM_REGION_START, RDRAM_REGION_END)) + return mmio.rdp.ReadRDRAM(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(paddr); - if(ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) { - const u32 w = mmio.ai.Read(paddr & ~3); - const int offs = 3 - (paddr & 3); - return w >> offs * 8 & 0xff; - } + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) + return mmio.pi.BusRead(paddr); + if (ircolib::IsInsideRange(paddr, AI_REGION_START, AI_REGION_END)) { + const u32 w = mmio.ai.Read(paddr & ~3); + const int offs = 3 - (paddr & 3); + return w >> offs * 8 & 0xff; + } - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access from MMIO"); + if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || + ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { + Util::Error::GetInstance().Throw( + {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_INVALID_ACCESS}, regs.pc, + Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access from MMIO"); + return 0; + } + + if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) + return si.pif.bootrom[BYTE_ADDRESS(paddr) - PIF_ROM_REGION_START]; + if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) + return si.pif.ram[paddr - PIF_RAM_REGION_START]; + if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused + 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; + + 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; - } - - 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; - - Util::Error::GetInstance().Throw( - {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, regs.pc, - Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::BYTE, paddr, 0}, "8-bit read access in unhandled region"); - return 0; } template <> u16 Mem::Read(const u32 paddr) { - n64::Registers& regs = n64::Core::GetRegs(); - const SI &si = mmio.si; + 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(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(src, HALF_ADDRESS(paddr & 0xfff)); - } + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) + return mmio.rdp.ReadRDRAM(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(src, HALF_ADDRESS(paddr & 0xfff)); + } - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead(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(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(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; + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) + return mmio.pi.BusRead(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(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(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; - 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; + Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, + regs.pc, + Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::SHORT, paddr, 0}, + "16-bit read access in unhandled region"); + return 0; } template <> u32 Mem::Read(const u32 paddr) { - n64::Registers& regs = n64::Core::GetRegs(); - const SI &si = mmio.si; + 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(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(src, paddr & 0xfff); - } + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) + return mmio.rdp.ReadRDRAM(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(src, paddr & 0xfff); + } - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead(paddr); - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr); + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) + return mmio.pi.BusRead(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(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(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; + if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) + return ircolib::ReadAccess(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(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; - 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; + Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::MEM_UNHANDLED_ACCESS}, + regs.pc, + Util::Error::MemoryAccess{false, Util::Error::MemoryAccess::WORD, paddr, 0}, + "32-bit read access in unhandled region"); + return 0; } template <> u64 Mem::Read(const u32 paddr) { - n64::Registers& regs = n64::Core::GetRegs(); - const SI &si = mmio.si; + 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(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(src, paddr & 0xfff); - } + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) + return mmio.rdp.ReadRDRAM(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(src, paddr & 0xfff); + } - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) return mmio.pi.BusRead(paddr); - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) return mmio.Read(paddr); + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) + return mmio.pi.BusRead(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(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(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; + if (ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END)) + return ircolib::ReadAccess(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(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; - 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; + 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(u32 paddr, u32 val) { - n64::Registers& regs = n64::Core::GetRegs(); - SI &si = mmio.si; +void Mem::Write(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(paddr, val); return; } - if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { - val = val << (8 * (3 - (paddr & 3))); - auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - paddr = (paddr & 0xFFF) & ~3; - ircolib::WriteAccess(dest, paddr, val); - return; - } - - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { - trace("BusWrite @ {:08X} = {:02X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - return; - } - - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write!"); - - if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { - val = val << (8 * (3 - (paddr & 3))); - paddr = (paddr - PIF_RAM_REGION_START) & ~3; - ircolib::WriteAccess(si.pif.ram, paddr, std::byteswap(val)); - si.pif.ProcessCommands(); - return; - } - - if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused - ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || - ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || - ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || - ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; - - panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc); -} - -#ifndef __aarch64__ -template <> -void Mem::WriteJIT(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); - if (jit) - jit->InvalidateBlock(paddr); -} +#ifdef KAIZEN_JIT_ENABLED + jit.InvalidateBlock(paddr); #endif -template <> -void Mem::Write(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { + mmio.rdp.WriteRDRAM(paddr, val); + return; + } + if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { + val = val << (8 * (3 - (paddr & 3))); + auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + paddr = (paddr & 0xFFF) & ~3; + ircolib::WriteAccess(dest, paddr, val); + return; + } + + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { + trace("BusWrite @ {:08X} = {:02X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + return; + } + + if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || + ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) + panic("MMIO Write!"); + + if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { + val = val << (8 * (3 - (paddr & 3))); + paddr = (paddr - PIF_RAM_REGION_START) & ~3; + ircolib::WriteAccess(si.pif.ram, paddr, std::byteswap(val)); + si.pif.ProcessCommands(); + return; + } + + if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused + ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || + ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || + ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || + ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) + return; + + panic("Unimplemented 8-bit write at address {:08X} with value {:02X} (PC = {:016X})", paddr, val, (u64)regs.pc); } template <> -void Mem::WriteInterpreter(u32 paddr, u32 val) { - n64::Registers& regs = n64::Core::GetRegs(); - SI &si = mmio.si; +void Mem::Write(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(paddr, val); return; } - if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { - val = val << (16 * !(paddr & 2)); - auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - paddr = (paddr & 0xFFF) & ~3; - ircolib::WriteAccess(dest, paddr, val); - return; - } - - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { - trace("BusWrite @ {:08X} = {:04X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - return; - } - - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write!"); - - if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { - val = val << (16 * !(paddr & 2)); - paddr &= ~3; - ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); - si.pif.ProcessCommands(); - return; - } - - if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused - 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; - - panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc); -} - -#ifndef __aarch64__ -template <> -void Mem::WriteJIT(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); - if (jit) - jit->InvalidateBlock(paddr); -} +#ifdef KAIZEN_JIT_ENABLED + jit.InvalidateBlock(paddr); #endif -template <> -void Mem::Write(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { + mmio.rdp.WriteRDRAM(paddr, val); + return; + } + if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { + val = val << (16 * !(paddr & 2)); + auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + paddr = (paddr & 0xFFF) & ~3; + ircolib::WriteAccess(dest, paddr, val); + return; + } + + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { + trace("BusWrite @ {:08X} = {:04X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + return; + } + + if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || + ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) + panic("MMIO Write!"); + + if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { + val = val << (16 * !(paddr & 2)); + paddr &= ~3; + ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); + si.pif.ProcessCommands(); + return; + } + + if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused + 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; + + panic("Unimplemented 16-bit write at address {:08X} with value {:04X} (PC = {:016X})", paddr, val, (u64)regs.pc); } -template <> -void Mem::WriteInterpreter(const u32 paddr, const u32 val) { - n64::Registers& regs = n64::Core::GetRegs(); - SI &si = mmio.si; - - if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(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(dest, paddr & 0xfff, val); - return; - } - - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { - trace("BusWrite @ {:08X} = {:08X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - return; - } - - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { mmio.Write(paddr, val); return; } - - if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { - ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); - si.pif.ProcessCommands(); - return; - } - - if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused - 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; - - panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc); -} - -#ifndef __aarch64__ -template <> -void Mem::WriteJIT(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); - if (jit) - jit->InvalidateBlock(paddr); -} -#endif - template <> void Mem::Write(const u32 paddr, const u32 val) { - WriteInterpreter(paddr, val); -} + n64::Registers ®s = n64::Core::GetRegs(); + SI &si = mmio.si; -#ifndef __aarch64__ -void Mem::WriteJIT(const u32 paddr, const u64 val) { - WriteInterpreter(paddr, val); - if (jit) - jit->InvalidateBlock(paddr); -} +#ifdef KAIZEN_JIT_ENABLED + jit.InvalidateBlock(paddr); #endif -void Mem::Write(const u32 paddr, const u64 val) { WriteInterpreter(paddr, val); } + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { + mmio.rdp.WriteRDRAM(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(dest, paddr & 0xfff, val); + return; + } -void Mem::WriteInterpreter(const u32 paddr, u64 val) { - n64::Registers& regs = n64::Core::GetRegs(); - SI &si = mmio.si; + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { + trace("BusWrite @ {:08X} = {:08X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + return; + } - if(ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { mmio.rdp.WriteRDRAM(paddr, val); return; } - if(ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { - auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; - val >>= 32; - ircolib::WriteAccess(dest, paddr & 0xfff, val); - return; - } + if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || + ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) { + mmio.Write(paddr, val); + return; + } - if(ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { - trace("BusWrite @ {:08X} = {:016X}", paddr, val); - mmio.pi.BusWrite(paddr, val); - return; - } + if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { + ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); + si.pif.ProcessCommands(); + return; + } - if(ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || - ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) panic("MMIO Write!"); + if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused + ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || + ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || + ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || + ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) + return; - if(ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { - ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); - si.pif.ProcessCommands(); - return; - } + panic("Unimplemented 32-bit write at address {:08X} with value {:08X} (PC = {:016X})", paddr, val, (u64)regs.pc); +} - if(ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused - ircolib::IsInsideRange(paddr, UNUSED_START_2, UNUSED_END_2) || - ircolib::IsInsideRange(paddr, UNUSED_START_3, UNUSED_END_3) || - ircolib::IsInsideRange(paddr, PIF_ROM_REGION_START, PIF_ROM_REGION_END) || - ircolib::IsInsideRange(paddr, UNUSED_START_4, UNUSED_END_4)) return; +void Mem::Write(const u32 paddr, u64 val) { + n64::Registers ®s = n64::Core::GetRegs(); + SI &si = mmio.si; - panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc); +#ifdef KAIZEN_JIT_ENABLED + jit.InvalidateBlock(paddr); +#endif + + if (ircolib::IsInsideRange(paddr, RDRAM_REGION_START, RDRAM_REGION_END)) { + mmio.rdp.WriteRDRAM(paddr, val); + return; + } + if (ircolib::IsInsideRange(paddr, DMEM_REGION_START, RSP_MEM_REGION_END)) { + auto &dest = paddr & 0x1000 ? mmio.rsp.imem : mmio.rsp.dmem; + val >>= 32; + ircolib::WriteAccess(dest, paddr & 0xfff, val); + return; + } + + if (ircolib::IsInsideRange(paddr, CART_REGION_START_2_1, CART_REGION_END_1_2)) { + trace("BusWrite @ {:08X} = {:016X}", paddr, val); + mmio.pi.BusWrite(paddr, val); + return; + } + + if (ircolib::IsInsideRange(paddr, MMIO_REGION_START_1, MMIO_REGION_END_1) || + ircolib::IsInsideRange(paddr, MMIO_REGION_START_2, MMIO_REGION_END_2)) + panic("MMIO Write!"); + + if (ircolib::IsInsideRange(paddr, PIF_RAM_REGION_START, PIF_RAM_REGION_END)) { + ircolib::WriteAccess(si.pif.ram, paddr - PIF_RAM_REGION_START, std::byteswap(val)); + si.pif.ProcessCommands(); + return; + } + + if (ircolib::IsInsideRange(paddr, UNUSED_START_1, UNUSED_END_1) || // unused + 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; + + panic("Unimplemented 64-bit write at address {:08X} with value {:016X} (PC = {:016X})", paddr, val, (u64)regs.pc); } template <> u32 Mem::BackupRead(const u32 addr) { - switch (saveType) { - case SAVE_NONE: - return 0; - case SAVE_EEPROM_4k: - case SAVE_EEPROM_16k: - warn("Accessing cartridge backup type SAVE_EEPROM, returning 0 for word read"); - return 0; - case SAVE_FLASH_1m: - return flash.Read(addr); - case SAVE_SRAM_256k: - return 0xFFFFFFFF; - default: - panic("Backup read word with unknown save type"); - } + switch (saveType) { + case SAVE_NONE: + return 0; + case SAVE_EEPROM_4k: + case SAVE_EEPROM_16k: + warn("Accessing cartridge backup type SAVE_EEPROM, returning 0 for word read"); + return 0; + case SAVE_FLASH_1m: + return flash.Read(addr); + case SAVE_SRAM_256k: + return 0xFFFFFFFF; + default: + panic("Backup read word with unknown save type"); + } } template <> u8 Mem::BackupRead(const u32 addr) { - switch (saveType) { - case SAVE_NONE: - return 0; - case SAVE_EEPROM_4k: - case SAVE_EEPROM_16k: - warn("Accessing cartridge backup type SAVE_EEPROM, returning 0 for word read"); - return 0; - case SAVE_FLASH_1m: - return flash.Read(addr); - case SAVE_SRAM_256k: - if (saveData.is_mapped()) { - assert(addr < saveData.size()); - return saveData[addr]; - } else { - panic("Invalid backup Read if save data is not initialized"); + switch (saveType) { + case SAVE_NONE: + return 0; + case SAVE_EEPROM_4k: + case SAVE_EEPROM_16k: + warn("Accessing cartridge backup type SAVE_EEPROM, returning 0 for word read"); + return 0; + case SAVE_FLASH_1m: + return flash.Read(addr); + case SAVE_SRAM_256k: + if (saveData.is_mapped()) { + assert(addr < saveData.size()); + return saveData[addr]; + } else { + panic("Invalid backup Read if save data is not initialized"); + } + default: + panic("Backup read word with unknown save type"); } - default: - panic("Backup read word with unknown save type"); - } } template <> void Mem::BackupWrite(const u32 addr, const u32 val) { - switch (saveType) { - case SAVE_NONE: - warn("Accessing cartridge with save type SAVE_NONE in write word"); - break; - case SAVE_EEPROM_4k: - case SAVE_EEPROM_16k: - panic("Accessing cartridge with save type SAVE_EEPROM in write word"); - case SAVE_FLASH_1m: - flash.Write(addr, val); - break; - case SAVE_SRAM_256k: - break; - default: - panic("Backup read word with unknown save type"); - } + switch (saveType) { + case SAVE_NONE: + warn("Accessing cartridge with save type SAVE_NONE in write word"); + break; + case SAVE_EEPROM_4k: + case SAVE_EEPROM_16k: + panic("Accessing cartridge with save type SAVE_EEPROM in write word"); + case SAVE_FLASH_1m: + flash.Write(addr, val); + break; + case SAVE_SRAM_256k: + break; + default: + panic("Backup read word with unknown save type"); + } } template <> void Mem::BackupWrite(const u32 addr, const u8 val) { - switch (saveType) { - case SAVE_NONE: - warn("Accessing cartridge with save type SAVE_NONE in write word"); - break; - case SAVE_EEPROM_4k: - case SAVE_EEPROM_16k: - panic("Accessing cartridge with save type SAVE_EEPROM in write word"); - case SAVE_FLASH_1m: - flash.Write(addr, val); - break; - case SAVE_SRAM_256k: - if (saveData.is_mapped()) { - assert(addr < saveData.size()); - saveData[addr] = val; - } else { - panic("Invalid backup Write if save data is not initialized"); + switch (saveType) { + case SAVE_NONE: + warn("Accessing cartridge with save type SAVE_NONE in write word"); + break; + case SAVE_EEPROM_4k: + case SAVE_EEPROM_16k: + panic("Accessing cartridge with save type SAVE_EEPROM in write word"); + case SAVE_FLASH_1m: + flash.Write(addr, val); + break; + case SAVE_SRAM_256k: + if (saveData.is_mapped()) { + assert(addr < saveData.size()); + saveData[addr] = val; + } else { + panic("Invalid backup Write if save data is not initialized"); + } + break; + default: + panic("Backup read word with unknown save type"); } - break; - default: - panic("Backup read word with unknown save type"); - } } } // namespace n64 diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 8c5d5e1..09eaa10 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -11,144 +11,142 @@ namespace n64 { struct ROMHeader { - u8 initialValues[4]; - char imageName[20]; - char countryCode[2]; - u16 cartridgeId; - u32 clockRate; - u32 programCounter; - u32 release; - u32 crc1; - u32 crc2; - u32 unknown2; - u32 manufacturerId; - u64 unknown; + u8 initialValues[4]; + char imageName[20]; + char countryCode[2]; + u16 cartridgeId; + u32 clockRate; + u32 programCounter; + u32 release; + u32 crc1; + u32 crc2; + u32 unknown2; + u32 manufacturerId; + u64 unknown; }; struct ROM { - bool pal; - char gameNameCart[20]; - char code[4]; - ROMHeader header; - size_t mask; - CICType cicType; - std::vector cart; - std::string gameNameDB; + bool pal; + char gameNameCart[20]; + char code[4]; + ROMHeader header; + size_t mask; + CICType cicType; + std::vector cart; + std::string gameNameDB; }; enum class FlashState : u8 { Idle, Erase, Write, Read, Status }; struct Flash { - explicit Flash(mio::mmap_sink &); - ~Flash() = default; - void Reset(); - void Load(SaveType, const std::string &); - std::array writeBuf{}; - FlashState state{}; - u64 status{}; - size_t eraseOffs{}; - size_t writeOffs{}; - std::string flashPath{}; - mio::mmap_sink &saveData; + explicit Flash(mio::mmap_sink &); + ~Flash() = default; + void Reset(); + void Load(SaveType, const std::string &); + std::array writeBuf{}; + FlashState state{}; + u64 status{}; + size_t eraseOffs{}; + size_t writeOffs{}; + std::string flashPath{}; + mio::mmap_sink &saveData; - enum FlashCommands : u8 { - FLASH_COMMAND_EXECUTE = 0xD2, - FLASH_COMMAND_STATUS = 0xE1, - FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B, - FLASH_COMMAND_ERASE = 0x78, - FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5, - FLASH_COMMAND_WRITE = 0xB4, - FLASH_COMMAND_READ = 0xF0, - }; + enum FlashCommands : u8 { + FLASH_COMMAND_EXECUTE = 0xD2, + FLASH_COMMAND_STATUS = 0xE1, + FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B, + FLASH_COMMAND_ERASE = 0x78, + FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5, + FLASH_COMMAND_WRITE = 0xB4, + FLASH_COMMAND_READ = 0xF0, + }; - void CommandExecute() const; - void CommandStatus(); - void CommandSetEraseOffs(u32); - void CommandErase(); - void CommandSetWriteOffs(u32); - void CommandWrite(); - void CommandRead(); - template - void Write(u32 index, T val); - template - T Read(u32 index) const; + void CommandExecute() const; + void CommandStatus(); + void CommandSetEraseOffs(u32); + void CommandErase(); + void CommandSetWriteOffs(u32); + void CommandWrite(); + void CommandRead(); + template + void Write(u32 index, T val); + template + T Read(u32 index) const; }; +#ifdef KAIZEN_JIT_ENABLED struct JIT; - struct Mem { - ~Mem() = default; - Mem(); - 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(); } + Mem(JIT &jit); + JIT &jit; +#else +struct Mem { + Mem(); + ~Mem() = default; + void Reset(); + void LoadSRAM(SaveType, fs::path); + void LoadROM(bool, const std::string &); - [[nodiscard]] auto GetRDRAM() -> std::vector & { return mmio.rdp.rdram; } + [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } - template - T Read(u32); - template - void Write(u32, u32); - void Write(u32, u64); + [[nodiscard]] auto GetRDRAM() -> std::vector & { return mmio.rdp.rdram; } - template - T BackupRead(u32); - template - void BackupWrite(u32, T); + template + T Read(u32); + template + void Write(u32, u32); + void Write(u32, u64); - FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const { - std::vector temp{}; - temp.resize(size); - std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin()); - ircolib::SwapBuffer(temp); - ircolib::WriteFileBinary(temp, "rdram.bin"); - } + template + T BackupRead(u32); + template + void BackupWrite(u32, T); - FORCE_INLINE void DumpIMEM() const { - std::array temp{}; - std::ranges::copy(mmio.rsp.imem, temp.begin()); - ircolib::SwapBuffer(temp); - ircolib::WriteFileBinary(temp, "imem.bin"); - } + FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const { + std::vector temp{}; + temp.resize(size); + std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin()); + ircolib::SwapBuffer(temp); + ircolib::WriteFileBinary(temp, "rdram.bin"); + } - FORCE_INLINE void DumpDMEM() const { - std::array temp{}; - std::ranges::copy(mmio.rsp.dmem, temp.begin()); - ircolib::SwapBuffer(temp); - ircolib::WriteFileBinary(temp, "dmem.bin"); - } + FORCE_INLINE void DumpIMEM() const { + std::array temp{}; + std::ranges::copy(mmio.rsp.imem, temp.begin()); + ircolib::SwapBuffer(temp); + ircolib::WriteFileBinary(temp, "imem.bin"); + } - MMIO mmio; - ROM rom; - SaveType saveType = SAVE_NONE; - Flash flash; -private: - friend struct SI; - friend struct PI; - friend struct AI; - friend struct RSP; - friend struct JIT; - friend struct Core; + FORCE_INLINE void DumpDMEM() const { + std::array temp{}; + std::ranges::copy(mmio.rsp.dmem, temp.begin()); + ircolib::SwapBuffer(temp); + ircolib::WriteFileBinary(temp, "dmem.bin"); + } - template - void WriteInterpreter(u32, u32); - void WriteInterpreter(u32, u64); - template - void WriteJIT(u32, u32); - void WriteJIT(u32, u64); + MMIO mmio; + ROM rom; + SaveType saveType = SAVE_NONE; + Flash flash; - std::array isviewer{}; - std::ofstream isviewer_sink{}; - int mmioSize{}, flashSize{}; - JIT *jit = nullptr; - std::string sramPath{}; - mio::mmap_sink saveData{}; + private: + friend struct SI; + friend struct PI; + friend struct AI; + friend struct RSP; + friend struct JIT; + friend struct Core; - [[nodiscard]] FORCE_INLINE bool IsROMPAL() const { - static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'}; - return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; }); - } + std::array isviewer{}; + std::ofstream isviewer_sink{}; + int mmioSize{}, flashSize{}; + std::string sramPath{}; + mio::mmap_sink saveData{}; + + [[nodiscard]] FORCE_INLINE bool IsROMPAL() const { + static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'}; + return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; }); + } }; +#endif } // namespace n64 diff --git a/src/backend/core/interpreter/decode.cpp b/src/backend/core/interpreter/decode.cpp index 1bfe6c0..35ca5d0 100644 --- a/src/backend/core/interpreter/decode.cpp +++ b/src/backend/core/interpreter/decode.cpp @@ -4,452 +4,457 @@ namespace n64 { void Interpreter::special(const Instruction instr) { - // 00rr_rccc - switch (instr.special()) { - case Instruction::SLL: - if (instr.instr.raw != 0) { - sll(instr); + // 00rr_rccc + switch (instr.special()) { + case Instruction::SLL: + if (instr.instr.raw != 0) { + sll(instr); + } + break; + case Instruction::SRL: + srl(instr); + break; + case Instruction::SRA: + sra(instr); + break; + case Instruction::SLLV: + sllv(instr); + break; + case Instruction::SRLV: + srlv(instr); + break; + case Instruction::SRAV: + srav(instr); + break; + case Instruction::JR: + jr(instr); + break; + case Instruction::JALR: + jalr(instr); + break; + case Instruction::SYSCALL: + regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); + break; + case Instruction::BREAK: + regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); + break; + case Instruction::SYNC: + break; // SYNC + case Instruction::MFHI: + mfhi(instr); + break; + case Instruction::MTHI: + mthi(instr); + break; + case Instruction::MFLO: + mflo(instr); + break; + case Instruction::MTLO: + mtlo(instr); + break; + case Instruction::DSLLV: + dsllv(instr); + break; + case Instruction::DSRLV: + dsrlv(instr); + break; + case Instruction::DSRAV: + dsrav(instr); + break; + case Instruction::MULT: + mult(instr); + break; + case Instruction::MULTU: + multu(instr); + break; + case Instruction::DIV: + div(instr); + break; + case Instruction::DIVU: + divu(instr); + break; + case Instruction::DMULT: + dmult(instr); + break; + case Instruction::DMULTU: + dmultu(instr); + break; + case Instruction::DDIV: + ddiv(instr); + break; + case Instruction::DDIVU: + ddivu(instr); + break; + case Instruction::ADD: + add(instr); + break; + case Instruction::ADDU: + addu(instr); + break; + case Instruction::SUB: + sub(instr); + break; + case Instruction::SUBU: + subu(instr); + break; + case Instruction::AND: + and_(instr); + break; + case Instruction::OR: + or_(instr); + break; + case Instruction::XOR: + xor_(instr); + break; + case Instruction::NOR: + nor(instr); + break; + case Instruction::SLT: + slt(instr); + break; + case Instruction::SLTU: + sltu(instr); + break; + case Instruction::DADD: + dadd(instr); + break; + case Instruction::DADDU: + daddu(instr); + break; + case Instruction::DSUB: + dsub(instr); + break; + case Instruction::DSUBU: + dsubu(instr); + break; + case Instruction::TGE: + trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); + break; + case Instruction::TGEU: + trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); + break; + case Instruction::TLT: + trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); + break; + case Instruction::TLTU: + trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); + break; + case Instruction::TEQ: + trap(regs.Read(instr.rs()) == regs.Read(instr.rt())); + break; + case Instruction::TNE: + trap(regs.Read(instr.rs()) != regs.Read(instr.rt())); + break; + case Instruction::DSLL: + dsll(instr); + break; + case Instruction::DSRL: + dsrl(instr); + break; + case Instruction::DSRA: + dsra(instr); + break; + case Instruction::DSLL32: + dsll32(instr); + break; + case Instruction::DSRL32: + dsrl32(instr); + break; + case Instruction::DSRA32: + 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(regs.oldPC)); } - break; - case Instruction::SRL: - srl(instr); - break; - case Instruction::SRA: - sra(instr); - break; - case Instruction::SLLV: - sllv(instr); - break; - case Instruction::SRLV: - srlv(instr); - break; - case Instruction::SRAV: - srav(instr); - break; - case Instruction::JR: - jr(instr); - break; - case Instruction::JALR: - jalr(instr); - break; - case Instruction::SYSCALL: - regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); - break; - case Instruction::BREAK: - regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); - break; - case Instruction::SYNC: - break; // SYNC - case Instruction::MFHI: - mfhi(instr); - break; - case Instruction::MTHI: - mthi(instr); - break; - case Instruction::MFLO: - mflo(instr); - break; - case Instruction::MTLO: - mtlo(instr); - break; - case Instruction::DSLLV: - dsllv(instr); - break; - case Instruction::DSRLV: - dsrlv(instr); - break; - case Instruction::DSRAV: - dsrav(instr); - break; - case Instruction::MULT: - mult(instr); - break; - case Instruction::MULTU: - multu(instr); - break; - case Instruction::DIV: - div(instr); - break; - case Instruction::DIVU: - divu(instr); - break; - case Instruction::DMULT: - dmult(instr); - break; - case Instruction::DMULTU: - dmultu(instr); - break; - case Instruction::DDIV: - ddiv(instr); - break; - case Instruction::DDIVU: - ddivu(instr); - break; - case Instruction::ADD: - add(instr); - break; - case Instruction::ADDU: - addu(instr); - break; - case Instruction::SUB: - sub(instr); - break; - case Instruction::SUBU: - subu(instr); - break; - case Instruction::AND: - and_(instr); - break; - case Instruction::OR: - or_(instr); - break; - case Instruction::XOR: - xor_(instr); - break; - case Instruction::NOR: - nor(instr); - break; - case Instruction::SLT: - slt(instr); - break; - case Instruction::SLTU: - sltu(instr); - break; - case Instruction::DADD: - dadd(instr); - break; - case Instruction::DADDU: - daddu(instr); - break; - case Instruction::DSUB: - dsub(instr); - break; - case Instruction::DSUBU: - dsubu(instr); - break; - case Instruction::TGE: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TGEU: - trap(regs.Read(instr.rs()) >= regs.Read(instr.rt())); - break; - case Instruction::TLT: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TLTU: - trap(regs.Read(instr.rs()) < regs.Read(instr.rt())); - break; - case Instruction::TEQ: - trap(regs.Read(instr.rs()) == regs.Read(instr.rt())); - break; - case Instruction::TNE: - trap(regs.Read(instr.rs()) != regs.Read(instr.rt())); - break; - case Instruction::DSLL: - dsll(instr); - break; - case Instruction::DSRL: - dsrl(instr); - break; - case Instruction::DSRA: - dsra(instr); - break; - case Instruction::DSLL32: - dsll32(instr); - break; - case Instruction::DSRL32: - dsrl32(instr); - break; - case Instruction::DSRA32: - 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(regs.oldPC)); - } } void Interpreter::regimm(const Instruction instr) { - // 000r_rccc - switch (instr.regimm()) { - case Instruction::BLTZ: - b(instr, regs.Read(instr.rs()) < 0); - break; - case Instruction::BGEZ: - b(instr, regs.Read(instr.rs()) >= 0); - break; - case Instruction::BLTZL: - bl(instr, regs.Read(instr.rs()) < 0); - break; - case Instruction::BGEZL: - bl(instr, regs.Read(instr.rs()) >= 0); - break; - case Instruction::TGEI: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr.instr.itype.imm))); - break; - case Instruction::TGEIU: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr.instr.itype.imm)))); - break; - case Instruction::TLTI: - trap(regs.Read(instr.rs()) < static_cast(static_cast(instr.instr.itype.imm))); - break; - case Instruction::TLTIU: - trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr.instr.itype.imm)))); - break; - case Instruction::TEQI: - trap(regs.Read(instr.rs()) == static_cast(static_cast(instr.instr.itype.imm))); - break; - case Instruction::TNEI: - trap(regs.Read(instr.rs()) != static_cast(static_cast(instr.instr.itype.imm))); - break; - case Instruction::BLTZAL: - blink(instr, regs.Read(instr.rs()) < 0); - break; - case Instruction::BGEZAL: - blink(instr, regs.Read(instr.rs()) >= 0); - break; - case Instruction::BLTZALL: - bllink(instr, regs.Read(instr.rs()) < 0); - break; - case Instruction::BGEZALL: - bllink(instr, regs.Read(instr.rs()) >= 0); - break; - default: - panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi, - instr.instr.opcode.regimm_lo, u32(instr), static_cast(regs.oldPC)); - } + // 000r_rccc + switch (instr.regimm()) { + case Instruction::BLTZ: + b(instr, regs.Read(instr.rs()) < 0); + break; + case Instruction::BGEZ: + b(instr, regs.Read(instr.rs()) >= 0); + break; + case Instruction::BLTZL: + bl(instr, regs.Read(instr.rs()) < 0); + break; + case Instruction::BGEZL: + bl(instr, regs.Read(instr.rs()) >= 0); + break; + case Instruction::TGEI: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr.instr.itype.imm))); + break; + case Instruction::TGEIU: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr.instr.itype.imm)))); + break; + case Instruction::TLTI: + trap(regs.Read(instr.rs()) < static_cast(static_cast(instr.instr.itype.imm))); + break; + case Instruction::TLTIU: + trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr.instr.itype.imm)))); + break; + case Instruction::TEQI: + trap(regs.Read(instr.rs()) == static_cast(static_cast(instr.instr.itype.imm))); + break; + case Instruction::TNEI: + trap(regs.Read(instr.rs()) != static_cast(static_cast(instr.instr.itype.imm))); + break; + case Instruction::BLTZAL: + blink(instr, regs.Read(instr.rs()) < 0); + break; + case Instruction::BGEZAL: + blink(instr, regs.Read(instr.rs()) >= 0); + break; + case Instruction::BLTZALL: + bllink(instr, regs.Read(instr.rs()) < 0); + break; + case Instruction::BGEZALL: + bllink(instr, regs.Read(instr.rs()) >= 0); + break; + default: + panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi, + instr.instr.opcode.regimm_lo, u32(instr), static_cast(regs.oldPC)); + } } void Interpreter::cop2Decode(const Instruction instr) { - if (!regs.cop0.status.cu2) { - regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); - return; - } - switch (instr.rs()) { - case 0x00: - mfc2(instr); - break; - case 0x01: - dmfc2(instr); - break; - case 0x02: - cfc2(instr); - break; - case 0x04: - mtc2(instr); - break; - case 0x05: - dmtc2(instr); - break; - case 0x06: - ctc2(instr); - break; - default: - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC); - } + if (!regs.cop0.status.cu2) { + regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); + return; + } + switch (instr.rs()) { + case 0x00: + mfc2(instr); + break; + case 0x01: + dmfc2(instr); + break; + case 0x02: + cfc2(instr); + break; + case 0x04: + mtc2(instr); + break; + case 0x05: + dmtc2(instr); + break; + case 0x06: + ctc2(instr); + break; + default: + regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC); + } } void Interpreter::Exec(const Instruction instr) { - // 00rr_rccc - switch (instr.opcode()) { - case Instruction::SPECIAL: - special(instr); - break; - case Instruction::REGIMM: - regimm(instr); - break; - case Instruction::J: - j(instr); - break; - case Instruction::JAL: - jal(instr); - break; - case Instruction::BEQ: - b(instr, regs.Read(instr.rs()) == regs.Read(instr.rt())); - break; - case Instruction::BNE: - b(instr, regs.Read(instr.rs()) != regs.Read(instr.rt())); - break; - case Instruction::BLEZ: - b(instr, regs.Read(instr.rs()) <= 0); - break; - case Instruction::BGTZ: - b(instr, regs.Read(instr.rs()) > 0); - break; - case Instruction::ADDI: - addi(instr); - break; - case Instruction::ADDIU: - addiu(instr); - break; - case Instruction::SLTI: - slti(instr); - break; - case Instruction::SLTIU: - sltiu(instr); - break; - case Instruction::ANDI: - andi(instr); - break; - case Instruction::ORI: - ori(instr); - break; - case Instruction::XORI: - xori(instr); - break; - case Instruction::LUI: - lui(instr); - break; - case Instruction::COP0: - regs.cop0.decode(instr); - break; - case Instruction::COP1: - if(instr.cop_rs() == 0x08) { - switch (instr.cop_rt()) { - case 0: - if (!regs.cop1.CheckFPUUsable()) + // 00rr_rccc + switch (instr.opcode()) { + case Instruction::SPECIAL: + special(instr); + break; + case Instruction::REGIMM: + regimm(instr); + break; + case Instruction::J: + j(instr); + break; + case Instruction::JAL: + jal(instr); + break; + case Instruction::BEQ: + b(instr, regs.Read(instr.rs()) == regs.Read(instr.rt())); + break; + case Instruction::BNE: + b(instr, regs.Read(instr.rs()) != regs.Read(instr.rt())); + break; + case Instruction::BLEZ: + b(instr, regs.Read(instr.rs()) <= 0); + break; + case Instruction::BGTZ: + b(instr, regs.Read(instr.rs()) > 0); + break; + case Instruction::ADDI: + addi(instr); + break; + case Instruction::ADDIU: + addiu(instr); + break; + case Instruction::SLTI: + slti(instr); + break; + case Instruction::SLTIU: + sltiu(instr); + break; + case Instruction::ANDI: + andi(instr); + break; + case Instruction::ORI: + ori(instr); + break; + case Instruction::XORI: + xori(instr); + break; + case Instruction::LUI: + lui(instr); + break; + case Instruction::COP0: + regs.cop0.decode(instr); + break; + case Instruction::COP1: + if (instr.cop_rs() == 0x08) { + switch (instr.cop_rt()) { + case 0: + if (!regs.cop1.CheckFPUUsable()) + return; + b(instr, !regs.cop1.fcr31.compare); + break; + case 1: + if (!regs.cop1.CheckFPUUsable()) + return; + b(instr, regs.cop1.fcr31.compare); + break; + case 2: + if (!regs.cop1.CheckFPUUsable()) + return; + bl(instr, !regs.cop1.fcr31.compare); + break; + case 3: + if (!regs.cop1.CheckFPUUsable()) + return; + bl(instr, regs.cop1.fcr31.compare); + break; + default: + panic("Undefined BC COP1 {:02X}", instr.cop_rt()); + } return; - b(instr, !regs.cop1.fcr31.compare); - break; - case 1: - if (!regs.cop1.CheckFPUUsable()) + } + regs.cop1.decode(instr); + break; + case Instruction::COP2: + cop2Decode(instr); + break; + case Instruction::BEQL: + bl(instr, regs.Read(instr.rs()) == regs.Read(instr.rt())); + break; + case Instruction::BNEL: + bl(instr, regs.Read(instr.rs()) != regs.Read(instr.rt())); + break; + case Instruction::BLEZL: + bl(instr, regs.Read(instr.rs()) <= 0); + break; + case Instruction::BGTZL: + bl(instr, regs.Read(instr.rs()) > 0); + break; + case Instruction::DADDI: + daddi(instr); + break; + case Instruction::DADDIU: + daddiu(instr); + break; + case Instruction::LDL: + ldl(instr); + break; + case Instruction::LDR: + ldr(instr); + break; + case 0x1F: + regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + break; + case Instruction::LB: + lb(instr); + break; + case Instruction::LH: + lh(instr); + break; + case Instruction::LWL: + lwl(instr); + break; + case Instruction::LW: + lw(instr); + break; + case Instruction::LBU: + lbu(instr); + break; + case Instruction::LHU: + lhu(instr); + break; + case Instruction::LWR: + lwr(instr); + break; + case Instruction::LWU: + lwu(instr); + break; + case Instruction::SB: + sb(instr); + break; + case Instruction::SH: + sh(instr); + break; + case Instruction::SWL: + swl(instr); + break; + case Instruction::SW: + sw(instr); + break; + case Instruction::SDL: + sdl(instr); + break; + case Instruction::SDR: + sdr(instr); + break; + case Instruction::SWR: + swr(instr); + break; + case Instruction::CACHE: + { + panic("CACHE 0b{:05b}, 0x{:04X}({}/r{} = 0x{:08X})", instr.op(), instr.offset(), + Registers::regNames[instr.base()], instr.base(), regs.Read(instr.base())); + } + break; + case Instruction::LL: + ll(instr); + break; + case Instruction::LWC1: + if (!regs.cop1.CheckFPUUsable()) return; - b(instr, regs.cop1.fcr31.compare); - break; - case 2: - if (!regs.cop1.CheckFPUUsable()) + regs.cop1.lwc1(instr); + break; + case Instruction::LLD: + lld(instr); + break; + case Instruction::LDC1: + if (!regs.cop1.CheckFPUUsable()) return; - bl(instr, !regs.cop1.fcr31.compare); - break; - case 3: - if (!regs.cop1.CheckFPUUsable()) + regs.cop1.ldc1(instr); + break; + case Instruction::LD: + ld(instr); + break; + case Instruction::SC: + sc(instr); + break; + case Instruction::SWC1: + if (!regs.cop1.CheckFPUUsable()) return; - bl(instr, regs.cop1.fcr31.compare); - break; - default: - panic("Undefined BC COP1 {:02X}", instr.cop_rt()); - } - return; + regs.cop1.swc1(instr); + break; + case Instruction::SCD: + scd(instr); + break; + case Instruction::SDC1: + if (!regs.cop1.CheckFPUUsable()) + return; + regs.cop1.sdc1(instr); + break; + case Instruction::SD: + sd(instr); + break; + default: + panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), + static_cast(regs.oldPC)); } - regs.cop1.decode(instr); - break; - case Instruction::COP2: - cop2Decode(instr); - break; - case Instruction::BEQL: - bl(instr, regs.Read(instr.rs()) == regs.Read(instr.rt())); - break; - case Instruction::BNEL: - bl(instr, regs.Read(instr.rs()) != regs.Read(instr.rt())); - break; - case Instruction::BLEZL: - bl(instr, regs.Read(instr.rs()) <= 0); - break; - case Instruction::BGTZL: - bl(instr, regs.Read(instr.rs()) > 0); - break; - case Instruction::DADDI: - daddi(instr); - break; - case Instruction::DADDIU: - daddiu(instr); - break; - case Instruction::LDL: - ldl(instr); - break; - case Instruction::LDR: - ldr(instr); - break; - case 0x1F: - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); - break; - case Instruction::LB: - lb(instr); - break; - case Instruction::LH: - lh(instr); - break; - case Instruction::LWL: - lwl(instr); - break; - case Instruction::LW: - lw(instr); - break; - case Instruction::LBU: - lbu(instr); - break; - case Instruction::LHU: - lhu(instr); - break; - case Instruction::LWR: - lwr(instr); - break; - case Instruction::LWU: - lwu(instr); - break; - case Instruction::SB: - sb(instr); - break; - case Instruction::SH: - sh(instr); - break; - case Instruction::SWL: - swl(instr); - break; - case Instruction::SW: - sw(instr); - break; - case Instruction::SDL: - sdl(instr); - break; - case Instruction::SDR: - sdr(instr); - break; - case Instruction::SWR: - swr(instr); - break; - case Instruction::CACHE: - break; // CACHE - case Instruction::LL: - ll(instr); - break; - case Instruction::LWC1: - if (!regs.cop1.CheckFPUUsable()) - return; - regs.cop1.lwc1(instr); - break; - case Instruction::LLD: - lld(instr); - break; - case Instruction::LDC1: - if (!regs.cop1.CheckFPUUsable()) - return; - regs.cop1.ldc1(instr); - break; - case Instruction::LD: - ld(instr); - break; - case Instruction::SC: - sc(instr); - break; - case Instruction::SWC1: - if (!regs.cop1.CheckFPUUsable()) - return; - regs.cop1.swc1(instr); - break; - case Instruction::SCD: - scd(instr); - break; - case Instruction::SDC1: - if (!regs.cop1.CheckFPUUsable()) - return; - regs.cop1.sdc1(instr); - break; - case Instruction::SD: - sd(instr); - break; - default: - panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), static_cast(regs.oldPC)); - } } } // namespace n64 diff --git a/src/backend/core/interpreter/instructions.cpp b/src/backend/core/interpreter/instructions.cpp index 7f3e9d3..d2cf017 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -5,815 +5,819 @@ namespace n64 { void Interpreter::add(const Instruction instr) { - const u32 rs = regs.Read(instr.rs()); - const u32 rt = regs.Read(instr.rt()); - if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rd(), static_cast(result)); - } + const u32 rs = regs.Read(instr.rs()); + const u32 rt = regs.Read(instr.rt()); + if (const u32 result = rs + rt; check_signed_overflow(rs, rt, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), static_cast(result)); + } } void Interpreter::addu(const Instruction instr) { - const s32 rs = regs.Read(instr.rs()); - const s32 rt = regs.Read(instr.rt()); - const s32 result = rs + rt; - regs.Write(instr.rd(), result); + const s32 rs = regs.Read(instr.rs()); + const s32 rt = regs.Read(instr.rt()); + const s32 result = rs + rt; + regs.Write(instr.rd(), result); } void Interpreter::addi(const Instruction instr) { - const u32 rs = regs.Read(instr.rs()); - const u32 imm = static_cast(static_cast(instr)); - if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rt(), static_cast(result)); - } + const u32 rs = regs.Read(instr.rs()); + const u32 imm = static_cast(static_cast(instr)); + if (const u32 result = rs + imm; check_signed_overflow(rs, imm, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rt(), static_cast(result)); + } } void Interpreter::addiu(const Instruction instr) { - const s32 rs = regs.Read(instr.rs()); - const s16 imm = static_cast(instr); - const s32 result = rs + imm; - regs.Write(instr.rt(), result); + const s32 rs = regs.Read(instr.rs()); + const s16 imm = static_cast(instr); + const s32 result = rs + imm; + regs.Write(instr.rt(), result); } void Interpreter::dadd(const Instruction instr) { - const u64 rs = regs.Read(instr.rs()); - const u64 rt = regs.Read(instr.rt()); - if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rd(), result); - } + const u64 rs = regs.Read(instr.rs()); + const u64 rt = regs.Read(instr.rt()); + if (const u64 result = rt + rs; check_signed_overflow(rs, rt, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), result); + } } void Interpreter::daddu(const Instruction instr) { - const s64 rs = regs.Read(instr.rs()); - const s64 rt = regs.Read(instr.rt()); - regs.Write(instr.rd(), rs + rt); + const s64 rs = regs.Read(instr.rs()); + const s64 rt = regs.Read(instr.rt()); + regs.Write(instr.rd(), rs + rt); } void Interpreter::daddi(const Instruction instr) { - const u64 imm = s64(s16(instr)); - const u64 rs = regs.Read(instr.rs()); - if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rt(), result); - } + const u64 imm = s64(s16(instr)); + const u64 rs = regs.Read(instr.rs()); + if (const u64 result = imm + rs; check_signed_overflow(rs, imm, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rt(), result); + } } void Interpreter::daddiu(const Instruction instr) { - const s16 imm = static_cast(instr); - const s64 rs = regs.Read(instr.rs()); - regs.Write(instr.rt(), rs + imm); + const s16 imm = static_cast(instr); + const s64 rs = regs.Read(instr.rs()); + regs.Write(instr.rt(), rs + imm); } void Interpreter::div(const Instruction instr) { - const s64 dividend = regs.Read(instr.rs()); + const s64 dividend = regs.Read(instr.rs()); - if (const s64 divisor = regs.Read(instr.rt()); divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = static_cast(-1); + if (const s64 divisor = regs.Read(instr.rt()); divisor == 0) { + regs.hi = dividend; + if (dividend >= 0) { + regs.lo = static_cast(-1); + } else { + regs.lo = static_cast(1); + } } else { - regs.lo = static_cast(1); + const s32 quotient = dividend / divisor; + const s32 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; } - } else { - const s32 quotient = dividend / divisor; - const s32 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } } void Interpreter::divu(const Instruction instr) { - const u32 dividend = regs.Read(instr.rs()); - if (const u32 divisor = regs.Read(instr.rt()); divisor == 0) { - regs.lo = -1; - regs.hi = (s32)dividend; - } else { - const s32 quotient = (s32)(dividend / divisor); - const s32 remainder = (s32)(dividend % divisor); - regs.lo = quotient; - regs.hi = remainder; - } + const u32 dividend = regs.Read(instr.rs()); + if (const u32 divisor = regs.Read(instr.rt()); divisor == 0) { + regs.lo = -1; + regs.hi = (s32)dividend; + } else { + const s32 quotient = (s32)(dividend / divisor); + const s32 remainder = (s32)(dividend % divisor); + regs.lo = quotient; + regs.hi = remainder; + } } void Interpreter::ddiv(const Instruction instr) { - const s64 dividend = regs.Read(instr.rs()); - const s64 divisor = regs.Read(instr.rt()); - if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { - regs.lo = dividend; - regs.hi = 0; - } else if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = -1; + const s64 dividend = regs.Read(instr.rs()); + const s64 divisor = regs.Read(instr.rt()); + if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) { + regs.lo = dividend; + regs.hi = 0; + } else if (divisor == 0) { + regs.hi = dividend; + if (dividend >= 0) { + regs.lo = -1; + } else { + regs.lo = 1; + } } else { - regs.lo = 1; + const s64 quotient = dividend / divisor; + const s64 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; } - } else { - const s64 quotient = dividend / divisor; - const s64 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } } void Interpreter::ddivu(const Instruction instr) { - const u64 dividend = regs.Read(instr.rs()); - const u64 divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s64)dividend; - } else { - const u64 quotient = dividend / divisor; - const u64 remainder = dividend % divisor; - regs.lo = (s64)quotient; - regs.hi = (s64)remainder; - } + const u64 dividend = regs.Read(instr.rs()); + const u64 divisor = regs.Read(instr.rt()); + if (divisor == 0) { + regs.lo = -1; + regs.hi = (s64)dividend; + } else { + const u64 quotient = dividend / divisor; + const u64 remainder = dividend % divisor; + regs.lo = (s64)quotient; + regs.hi = (s64)remainder; + } } void Interpreter::branch(const bool cond, const s64 address) { - regs.delaySlot = true; - if (cond) { - regs.nextPC = address; - } + regs.delaySlot = true; + if (cond) { + regs.nextPC = address; + } } void Interpreter::branch_likely(const bool cond, const s64 address) { - if (cond) { - regs.delaySlot = true; - regs.nextPC = address; - } else { - regs.SetPC64(regs.nextPC); - } + if (cond) { + regs.delaySlot = true; + regs.nextPC = address; + } else { + regs.SetPC64(regs.nextPC); + } } void Interpreter::b(const Instruction instr, const bool cond) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = regs.pc + offset; - branch(cond, address); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = regs.pc + offset; + branch(cond, address); } void Interpreter::blink(const Instruction instr, const bool cond) { - regs.Write(31, regs.nextPC); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = regs.pc + offset; - branch(cond, address); + regs.Write(31, regs.nextPC); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = regs.pc + offset; + branch(cond, address); } void Interpreter::bl(const Instruction instr, const bool cond) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = regs.pc + offset; - branch_likely(cond, address); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = regs.pc + offset; + branch_likely(cond, address); } void Interpreter::bllink(const Instruction instr, const bool cond) { - regs.Write(31, regs.nextPC); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = regs.pc + offset; - branch_likely(cond, address); + regs.Write(31, regs.nextPC); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = regs.pc + offset; + branch_likely(cond, address); } void Interpreter::lui(const Instruction instr) { - u64 val = s64((s16)instr); - val <<= 16; - regs.Write(instr.rt(), val); + u64 val = s64((s16)instr); + val <<= 16; + regs.Write(instr.rt(), val); } void Interpreter::lb(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - regs.Write(instr.rt(), (s8)mem.Read(paddr)); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + if (u32 paddr = 0; !regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + regs.Write(instr.rt(), (s8)mem.Read(paddr)); + } } void Interpreter::lh(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - regs.Write(instr.rt(), (s16)mem.Read(paddr)); - } + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + regs.Write(instr.rt(), (s16)mem.Read(paddr)); + } } void Interpreter::lw(const Instruction instr) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (check_address_error(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } - u32 physical = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - regs.Write(instr.rt(), (s32)mem.Read(physical)); - } + u32 physical = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + regs.Write(instr.rt(), (s32)mem.Read(physical)); + } } void Interpreter::ll(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 physical; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const s32 result = mem.Read(physical); - if (check_address_error(0b11, address)) { - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 physical; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, physical)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const s32 result = mem.Read(physical); + if (check_address_error(0b11, address)) { + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } + + regs.Write(instr.rt(), result); + + regs.cop0.llbit = true; + regs.cop0.LLAddr = physical >> 4; } - - regs.Write(instr.rt(), result); - - regs.cop0.llbit = true; - regs.cop0.LLAddr = physical >> 4; - } } void Interpreter::lwl(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u32 shift = 8 * ((address ^ 0) & 3); - const u32 mask = 0xFFFFFFFF << shift; - const u32 data = mem.Read(paddr & ~3); - const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data << shift)); - regs.Write(instr.rt(), result); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u32 shift = 8 * ((address ^ 0) & 3); + const u32 mask = 0xFFFFFFFF << shift; + const u32 data = mem.Read(paddr & ~3); + const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data << shift)); + regs.Write(instr.rt(), result); + } } void Interpreter::lwr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u32 shift = 8 * ((address ^ 3) & 3); - const u32 mask = 0xFFFFFFFF >> shift; - const u32 data = mem.Read(paddr & ~3); - const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data >> shift)); - regs.Write(instr.rt(), result); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u32 shift = 8 * ((address ^ 3) & 3); + const u32 mask = 0xFFFFFFFF >> shift; + const u32 data = mem.Read(paddr & ~3); + const s32 result = s32((regs.Read(instr.rt()) & ~mask) | (data >> shift)); + regs.Write(instr.rt(), result); + } } void Interpreter::ld(const Instruction instr) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b111, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const s64 value = mem.Read(paddr); - regs.Write(instr.rt(), value); - } + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const s64 value = mem.Read(paddr); + regs.Write(instr.rt(), value); + } } void Interpreter::lld(const Instruction instr) { - if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); - return; - } + if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { + regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + return; + } - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - if (check_address_error(0b111, address)) { - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); } else { - regs.Write(instr.rt(), mem.Read(paddr)); - regs.cop0.llbit = true; - regs.cop0.LLAddr = paddr >> 4; + if (check_address_error(0b111, address)) { + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + } else { + regs.Write(instr.rt(), mem.Read(paddr)); + regs.cop0.llbit = true; + regs.cop0.LLAddr = paddr >> 4; + } } - } } void Interpreter::ldl(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const s32 shift = 8 * ((address ^ 0) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); - regs.Write(instr.rt(), result); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const s32 shift = 8 * ((address ^ 0) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; + const u64 data = mem.Read(paddr & ~7); + const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data << shift)); + regs.Write(instr.rt(), result); + } } void Interpreter::ldr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const s32 shift = 8 * ((address ^ 7) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; - const u64 data = mem.Read(paddr & ~7); - const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); - regs.Write(instr.rt(), result); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const s32 shift = 8 * ((address ^ 7) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; + const u64 data = mem.Read(paddr & ~7); + const s64 result = (s64)((regs.Read(instr.rt()) & ~mask) | (data >> shift)); + regs.Write(instr.rt(), result); + } } void Interpreter::lbu(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u8 value = mem.Read(paddr); - regs.Write(instr.rt(), value); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u8 value = mem.Read(paddr); + regs.Write(instr.rt(), value); + } } void Interpreter::lhu(const Instruction instr) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b1, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u16 value = mem.Read(paddr); - regs.Write(instr.rt(), value); - } + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u16 value = mem.Read(paddr); + regs.Write(instr.rt(), value); + } } void Interpreter::lwu(const Instruction instr) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b11, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); - return; - } + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u32 value = mem.Read(paddr); - regs.Write(instr.rt(), value); - } + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u32 value = mem.Read(paddr); + regs.Write(instr.rt(), value); + } } void Interpreter::sb(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(paddr, regs.Read(instr.rt())); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(paddr, regs.Read(instr.rt())); + } } void Interpreter::sc(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; + const u64 address = regs.Read(instr.rs()) + (s16)instr; - if (regs.cop0.llbit) { - regs.cop0.llbit = false; + if (regs.cop0.llbit) { + regs.cop0.llbit = false; - if (check_address_error(0b11, address)) { - regs.Write(instr.rt(), 0); - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; - } + if (check_address_error(0b11, address)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.Write(instr.rt(), 0); - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(paddr, regs.Read(instr.rt())); + regs.Write(instr.rt(), 1); + } } else { - mem.Write(paddr, regs.Read(instr.rt())); - regs.Write(instr.rt(), 1); + regs.Write(instr.rt(), 0); } - } else { - regs.Write(instr.rt(), 0); - } } void Interpreter::scd(const Instruction instr) { - if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { - regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); - return; - } - - const s64 address = regs.Read(instr.rs()) + (s16)instr; - - if (regs.cop0.llbit) { - regs.cop0.llbit = false; - - if (check_address_error(0b111, address)) { - regs.Write(instr.rt(), 0); - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; + if (!regs.cop0.is64BitAddressing && !regs.cop0.kernelMode) { + regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); + return; } - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.Write(instr.rt(), 0); - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + const s64 address = regs.Read(instr.rs()) + (s16)instr; + + if (regs.cop0.llbit) { + regs.cop0.llbit = false; + + if (check_address_error(0b111, address)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } + + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(paddr, regs.Read(instr.rt())); + regs.Write(instr.rt(), 1); + } } else { - mem.Write(paddr, regs.Read(instr.rt())); - regs.Write(instr.rt(), 1); + regs.Write(instr.rt(), 0); } - } else { - regs.Write(instr.rt(), 0); - } } void Interpreter::sh(const Instruction instr) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; + const s64 address = regs.Read(instr.rs()) + (s16)instr; - u32 physical; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, regs.Read(instr.rt())); - } + u32 physical; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(physical, regs.Read(instr.rt())); + } } void Interpreter::sw(const Instruction instr) { - const s16 offset = instr; - const u64 address = regs.Read(instr.rs()) + offset; - if (check_address_error(0b11, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; - } + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (check_address_error(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } - u32 physical; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, regs.Read(instr.rt())); - } + u32 physical; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(physical, regs.Read(instr.rt())); + } } void Interpreter::sd(const Instruction instr) { - const s64 address = regs.Read(instr.rs()) + (s16)instr; - if (check_address_error(0b111, address)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); - return; - } + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (check_address_error(0b111, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(ExceptionCode::AddressErrorStore, 0, regs.oldPC); + return; + } - u32 physical; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, regs.Read(instr.rt())); - } + u32 physical; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + mem.Write(physical, regs.Read(instr.rt())); + } } void Interpreter::sdl(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - const s32 shift = 8 * ((address ^ 0) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; - const u64 data = mem.Read(paddr & ~7); - const u64 rt = regs.Read(instr.rt()); - mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + const s32 shift = 8 * ((address ^ 0) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF >> shift; + const u64 data = mem.Read(paddr & ~7); + const u64 rt = regs.Read(instr.rt()); + mem.Write(paddr & ~7, (data & ~mask) | (rt >> shift)); + } } void Interpreter::sdr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - const s32 shift = 8 * ((address ^ 7) & 7); - const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; - const u64 data = mem.Read(paddr & ~7); - const u64 rt = regs.Read(instr.rt()); - mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + const s32 shift = 8 * ((address ^ 7) & 7); + const u64 mask = 0xFFFFFFFFFFFFFFFF << shift; + const u64 data = mem.Read(paddr & ~7); + const u64 rt = regs.Read(instr.rt()); + mem.Write(paddr & ~7, (data & ~mask) | (rt << shift)); + } } void Interpreter::swl(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - const u32 shift = 8 * ((address ^ 0) & 3); - const u32 mask = 0xFFFFFFFF >> shift; - const u32 data = mem.Read(paddr & ~3); - const u32 rt = regs.Read(instr.rt()); - mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + const u32 shift = 8 * ((address ^ 0) & 3); + const u32 mask = 0xFFFFFFFF >> shift; + const u32 data = mem.Read(paddr & ~3); + const u32 rt = regs.Read(instr.rt()); + mem.Write(paddr & ~3, (data & ~mask) | (rt >> shift)); + } } void Interpreter::swr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()) + (s16)instr; - u32 paddr; - if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { - regs.cop0.HandleTLBException(address); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - const u32 shift = 8 * ((address ^ 3) & 3); - const u32 mask = 0xFFFFFFFF << shift; - const u32 data = mem.Read(paddr & ~3); - const u32 rt = regs.Read(instr.rt()); - mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); - } + const u64 address = regs.Read(instr.rs()) + (s16)instr; + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + const u32 shift = 8 * ((address ^ 3) & 3); + const u32 mask = 0xFFFFFFFF << shift; + const u32 data = mem.Read(paddr & ~3); + const u32 rt = regs.Read(instr.rt()); + mem.Write(paddr & ~3, (data & ~mask) | (rt << shift)); + } } void Interpreter::ori(const Instruction instr) { - const s64 imm = (u16)instr; - const s64 result = imm | regs.Read(instr.rs()); - regs.Write(instr.rt(), result); + const s64 imm = (u16)instr; + const s64 result = imm | regs.Read(instr.rs()); + regs.Write(instr.rt(), result); } -void Interpreter::or_(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); } +void Interpreter::or_(const Instruction instr) { + regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); +} void Interpreter::nor(const Instruction instr) { - regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); + regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); } void Interpreter::j(const Instruction instr) { - const s32 target = (instr & 0x3ffffff) << 2; - const s64 address = (regs.oldPC & ~0xfffffff) | target; + const s32 target = (instr & 0x3ffffff) << 2; + const s64 address = (regs.oldPC & ~0xfffffff) | target; - branch(true, address); + branch(true, address); } void Interpreter::jal(const Instruction instr) { - regs.Write(31, regs.nextPC); - j(instr); + regs.Write(31, regs.nextPC); + j(instr); } void Interpreter::jalr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()); - regs.Write(instr.rd(), regs.nextPC); - branch(true, address); + const u64 address = regs.Read(instr.rs()); + regs.Write(instr.rd(), regs.nextPC); + branch(true, address); } void Interpreter::jr(const Instruction instr) { - const u64 address = regs.Read(instr.rs()); - branch(true, address); + const u64 address = regs.Read(instr.rs()); + branch(true, address); } void Interpreter::slti(const Instruction instr) { - const s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + const s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); } void Interpreter::sltiu(const Instruction instr) { - const s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + const s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); } -void Interpreter::slt(const Instruction instr) { regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); } +void Interpreter::slt(const Instruction instr) { + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); +} void Interpreter::sltu(const Instruction instr) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); } void Interpreter::xori(const Instruction instr) { - const s64 imm = (u16)instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); + const s64 imm = (u16)instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); } void Interpreter::xor_(const Instruction instr) { - regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); + regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); } void Interpreter::andi(const Instruction instr) { - const s64 imm = (u16)instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); + const s64 imm = (u16)instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); } void Interpreter::and_(const Instruction instr) { - regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); + regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); } void Interpreter::sll(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - const s32 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), (s64)result); + const u8 sa = ((instr >> 6) & 0x1f); + const s32 result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), (s64)result); } void Interpreter::sllv(const Instruction instr) { - const u8 sa = (regs.Read(instr.rs())) & 0x1F; - const u32 rt = regs.Read(instr.rt()); - const s32 result = rt << sa; - regs.Write(instr.rd(), (s64)result); + const u8 sa = (regs.Read(instr.rs())) & 0x1F; + const u32 rt = regs.Read(instr.rt()); + const s32 result = rt << sa; + regs.Write(instr.rd(), (s64)result); } void Interpreter::dsll32(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - const s64 result = regs.Read(instr.rt()) << (sa + 32); - regs.Write(instr.rd(), result); + const u8 sa = ((instr >> 6) & 0x1f); + const s64 result = regs.Read(instr.rt()) << (sa + 32); + regs.Write(instr.rd(), result); } void Interpreter::dsll(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - const s64 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); + const u8 sa = ((instr >> 6) & 0x1f); + const s64 result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); } void Interpreter::dsllv(const Instruction instr) { - const s64 sa = regs.Read(instr.rs()) & 63; - const s64 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); + const s64 sa = regs.Read(instr.rs()) & 63; + const s64 result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); } void Interpreter::srl(const Instruction instr) { - const u32 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const u32 result = rt >> sa; - regs.Write(instr.rd(), (s32)result); + const u32 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const u32 result = rt >> sa; + regs.Write(instr.rd(), (s32)result); } void Interpreter::srlv(const Instruction instr) { - const u8 sa = (regs.Read(instr.rs()) & 0x1F); - const u32 rt = regs.Read(instr.rt()); - const s32 result = rt >> sa; - regs.Write(instr.rd(), (s64)result); + const u8 sa = (regs.Read(instr.rs()) & 0x1F); + const u32 rt = regs.Read(instr.rt()); + const s32 result = rt >> sa; + regs.Write(instr.rd(), (s64)result); } void Interpreter::dsrl(const Instruction instr) { - const u64 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const u64 result = rt >> sa; - regs.Write(instr.rd(), s64(result)); + const u64 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const u64 result = rt >> sa; + regs.Write(instr.rd(), s64(result)); } void Interpreter::dsrlv(const Instruction instr) { - const u8 amount = (regs.Read(instr.rs()) & 63); - const u64 rt = regs.Read(instr.rt()); - const u64 result = rt >> amount; - regs.Write(instr.rd(), s64(result)); + const u8 amount = (regs.Read(instr.rs()) & 63); + const u64 rt = regs.Read(instr.rt()); + const u64 result = rt >> amount; + regs.Write(instr.rd(), s64(result)); } void Interpreter::dsrl32(const Instruction instr) { - const u64 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const u64 result = rt >> (sa + 32); - regs.Write(instr.rd(), s64(result)); + const u64 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const u64 result = rt >> (sa + 32); + regs.Write(instr.rd(), s64(result)); } void Interpreter::sra(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const s32 result = rt >> sa; - regs.Write(instr.rd(), result); + const s64 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const s32 result = rt >> sa; + regs.Write(instr.rd(), result); } void Interpreter::srav(const Instruction instr) { - const s64 rs = regs.Read(instr.rs()); - const s64 rt = regs.Read(instr.rt()); - const u8 sa = rs & 0x1f; - const s32 result = rt >> sa; - regs.Write(instr.rd(), result); + const s64 rs = regs.Read(instr.rs()); + const s64 rt = regs.Read(instr.rt()); + const u8 sa = rs & 0x1f; + const s32 result = rt >> sa; + regs.Write(instr.rd(), result); } void Interpreter::dsra(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const s64 result = rt >> sa; - regs.Write(instr.rd(), result); + const s64 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const s64 result = rt >> sa; + regs.Write(instr.rd(), result); } void Interpreter::dsrav(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const s64 rs = regs.Read(instr.rs()); - const s64 sa = rs & 63; - const s64 result = rt >> sa; - regs.Write(instr.rd(), result); + const s64 rt = regs.Read(instr.rt()); + const s64 rs = regs.Read(instr.rs()); + const s64 sa = rs & 63; + const s64 result = rt >> sa; + regs.Write(instr.rd(), result); } void Interpreter::dsra32(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const u8 sa = ((instr >> 6) & 0x1f); - const s64 result = rt >> (sa + 32); - regs.Write(instr.rd(), result); + const s64 rt = regs.Read(instr.rt()); + const u8 sa = ((instr >> 6) & 0x1f); + const s64 result = rt >> (sa + 32); + regs.Write(instr.rd(), result); } void Interpreter::dsub(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const s64 rs = regs.Read(instr.rs()); - if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rd(), result); - } + const s64 rt = regs.Read(instr.rt()); + const s64 rs = regs.Read(instr.rs()); + if (const s64 result = rs - rt; check_signed_underflow(rs, rt, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), result); + } } void Interpreter::dsubu(const Instruction instr) { - const u64 rt = regs.Read(instr.rt()); - const u64 rs = regs.Read(instr.rs()); - const u64 result = rs - rt; - regs.Write(instr.rd(), s64(result)); + const u64 rt = regs.Read(instr.rt()); + const u64 rs = regs.Read(instr.rs()); + const u64 result = rs - rt; + regs.Write(instr.rd(), s64(result)); } void Interpreter::sub(const Instruction instr) { - const s32 rt = regs.Read(instr.rt()); - const s32 rs = regs.Read(instr.rs()); - const s32 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - } else { - regs.Write(instr.rd(), result); - } + const s32 rt = regs.Read(instr.rt()); + const s32 rs = regs.Read(instr.rs()); + const s32 result = rs - rt; + if (check_signed_underflow(rs, rt, result)) { + regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), result); + } } void Interpreter::subu(const Instruction instr) { - const u32 rt = regs.Read(instr.rt()); - const u32 rs = regs.Read(instr.rs()); - const u32 result = rs - rt; - regs.Write(instr.rd(), (s64)((s32)result)); + const u32 rt = regs.Read(instr.rt()); + const u32 rs = regs.Read(instr.rs()); + const u32 result = rs - rt; + regs.Write(instr.rd(), (s64)((s32)result)); } void Interpreter::dmultu(const Instruction instr) { - const u64 rt = regs.Read(instr.rt()); - const u64 rs = regs.Read(instr.rs()); - const u128 result = (u128)rt * (u128)rs; - regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF); - regs.hi = (s64)(result >> 64); + const u64 rt = regs.Read(instr.rt()); + const u64 rs = regs.Read(instr.rs()); + const u128 result = (u128)rt * (u128)rs; + regs.lo = (s64)(result & 0xFFFFFFFFFFFFFFFF); + regs.hi = (s64)(result >> 64); } void Interpreter::dmult(const Instruction instr) { - const s64 rt = regs.Read(instr.rt()); - const s64 rs = regs.Read(instr.rs()); - const s128 result = (s128)rt * (s128)rs; - regs.lo = result & 0xFFFFFFFFFFFFFFFF; - regs.hi = result >> 64; + const s64 rt = regs.Read(instr.rt()); + const s64 rs = regs.Read(instr.rs()); + const s128 result = (s128)rt * (s128)rs; + regs.lo = result & 0xFFFFFFFFFFFFFFFF; + regs.hi = result >> 64; } void Interpreter::multu(const Instruction instr) { - const u32 rt = regs.Read(instr.rt()); - const u32 rs = regs.Read(instr.rs()); - const u64 result = (u64)rt * (u64)rs; - regs.lo = (s64)((s32)result); - regs.hi = (s64)((s32)(result >> 32)); + const u32 rt = regs.Read(instr.rt()); + const u32 rs = regs.Read(instr.rs()); + const u64 result = (u64)rt * (u64)rs; + regs.lo = (s64)((s32)result); + regs.hi = (s64)((s32)(result >> 32)); } void Interpreter::mult(const Instruction instr) { - const s32 rt = regs.Read(instr.rt()); - const s32 rs = regs.Read(instr.rs()); - const s64 result = (s64)rt * (s64)rs; - regs.lo = (s64)((s32)result); - regs.hi = (s64)((s32)(result >> 32)); + const s32 rt = regs.Read(instr.rt()); + const s32 rs = regs.Read(instr.rs()); + const s64 result = (s64)rt * (s64)rs; + regs.lo = (s64)((s32)result); + regs.hi = (s64)((s32)(result >> 32)); } void Interpreter::mflo(const Instruction instr) { regs.Write(instr.rd(), regs.lo); } @@ -825,17 +829,17 @@ void Interpreter::mtlo(const Instruction instr) { regs.lo = regs.Read(instr void Interpreter::mthi(const Instruction instr) { regs.hi = regs.Read(instr.rs()); } void Interpreter::trap(const bool cond) const { - Cop0& cop0 = Core::GetRegs().cop0; - if (cond) { - cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC); - } + Cop0 &cop0 = Core::GetRegs().cop0; + if (cond) { + cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC); + } } void Interpreter::mtc2(const Instruction instr) { cop2Latch = regs.Read(instr.rt()); } void Interpreter::mfc2(const Instruction instr) { - const s32 value = cop2Latch; - regs.Write(instr.rt(), value); + const s32 value = cop2Latch; + regs.Write(instr.rt(), value); } void Interpreter::dmtc2(const Instruction instr) { cop2Latch = regs.Read(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 diff --git a/src/backend/core/registers/Registers.cpp b/src/backend/core/registers/Registers.cpp index 00b8670..e5f6a21 100644 --- a/src/backend/core/registers/Registers.cpp +++ b/src/backend/core/registers/Registers.cpp @@ -3,361 +3,352 @@ #include namespace n64 { +#ifdef KAIZEN_JIT_ENABLED +Registers::Registers(JIT &jit) : jit(jit) { Reset(); } +#else Registers::Registers() { Reset(); } +#endif void Registers::Reset() { - hi = 0; - lo = 0; - delaySlot = false; - prevDelaySlot = false; - gpr.fill(0); - regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always + hi = 0; + lo = 0; + delaySlot = false; + prevDelaySlot = false; + gpr.fill(0); + regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always - cop0.Reset(); - cop1.Reset(); + cop0.Reset(); + cop1.Reset(); - steps = 0; - extraCycles = 0; + steps = 0; + extraCycles = 0; } void Registers::SetPC64(s64 val) { - oldPC = pc; - pc = val; - nextPC = pc + 4; + oldPC = pc; + pc = val; + nextPC = pc + 4; } void Registers::SetPC32(s32 val) { - oldPC = pc; - pc = s64(val); - nextPC = pc + 4; + oldPC = pc; + pc = s64(val); + nextPC = pc + 4; } template <> u64 Registers::Read(size_t idx) { - return gpr[idx]; + return gpr[idx]; } template <> s64 Registers::Read(const size_t idx) { - return static_cast(Read(idx)); + return static_cast(Read(idx)); } template <> u32 Registers::Read(size_t idx) { - return gpr[idx]; + return gpr[idx]; } template <> s32 Registers::Read(size_t idx) { - return static_cast(Read(idx)); + return static_cast(Read(idx)); } template <> u16 Registers::Read(size_t idx) { - return gpr[idx]; + return gpr[idx]; } template <> s16 Registers::Read(size_t idx) { - return static_cast(Read(idx)); + return static_cast(Read(idx)); } template <> u8 Registers::Read(size_t idx) { - return gpr[idx]; + return gpr[idx]; } template <> s8 Registers::Read(size_t idx) { - return static_cast(Read(idx)); + return static_cast(Read(idx)); } -#ifndef __aarch64__ +#ifdef KAIZEN_JIT_ENABLED template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt64(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt64(), Read(idx)); + return; + } - jit->code.mov(reg.cvt64(), jit->GPR(idx)); + jit.code.mov(reg.cvt64(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt64(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt64(), Read(idx)); + return; + } - jit->code.mov(reg.cvt64(), jit->GPR(idx)); + jit.code.mov(reg.cvt64(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt32(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt32(), Read(idx)); + return; + } - jit->code.mov(reg.cvt32(), jit->GPR(idx)); + jit.code.mov(reg.cvt32(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt32(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt32(), Read(idx)); + return; + } - jit->code.mov(reg.cvt32(), jit->GPR(idx)); + jit.code.mov(reg.cvt32(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt16(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt16(), Read(idx)); + return; + } - jit->code.mov(reg.cvt16(), jit->GPR(idx)); + jit.code.mov(reg.cvt16(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt16(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt16(), Read(idx)); + return; + } - jit->code.mov(reg.cvt16(), jit->GPR(idx)); + jit.code.mov(reg.cvt16(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt8(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt8(), Read(idx)); + return; + } - jit->code.mov(reg.cvt8(), jit->GPR(idx)); + jit.code.mov(reg.cvt8(), jit.GPR(idx)); } template <> void Registers::Read(size_t idx, Xbyak::Reg reg) { - if(IsRegConstant(idx)) { - jit->code.mov(reg.cvt8(), Read(idx)); - return; - } + if (IsRegConstant(idx)) { + jit.code.mov(reg.cvt8(), Read(idx)); + return; + } - jit->code.mov(reg.cvt8(), jit->GPR(idx)); + jit.code.mov(reg.cvt8(), jit.GPR(idx)); } #endif template <> void Registers::Write(size_t idx, bool v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + gpr[idx] = v; } template <> void Registers::Write(size_t idx, u64 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, s64 v) { - Write(idx, v); + Write(idx, v); } template <> void Registers::Write(size_t idx, u32 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, s32 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, u16 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, s16 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, u8 v) { - if (idx == 0) - return; - - if (jit) [[unlikely]] + if (idx == 0) + return; + regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } template <> void Registers::Write(size_t idx, s8 v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (jit) [[unlikely]] regIsConstant |= (1 << idx); - gpr[idx] = v; + + gpr[idx] = v; } -#ifndef __aarch64__ +#ifdef KAIZEN_JIT_ENABLED template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - - regIsConstant &= ~(1 << idx); + regIsConstant &= ~(1 << idx); - jit->code.movsx(v.cvt64(), v.cvt8()); - jit->code.mov(jit->GPR(idx), v); + jit.code.movsx(v.cvt64(), v.cvt8()); + jit.code.mov(jit.GPR(idx), v); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + regIsConstant &= ~(1 << idx); - regIsConstant &= ~(1 << idx); - - jit->code.movsx(v.cvt64(), v.cvt8()); - jit->code.mov(jit->GPR(idx), v); + jit.code.movsx(v.cvt64(), v.cvt8()); + jit.code.mov(jit.GPR(idx), v); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + regIsConstant &= ~(1 << idx); - regIsConstant &= ~(1 << idx); - - jit->code.movzx(v.cvt64(), v.cvt8()); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.movzx(v.cvt64(), v.cvt8()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + regIsConstant &= ~(1 << idx); - regIsConstant &= ~(1 << idx); - - jit->code.movsx(v.cvt64(), v.cvt16()); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.movsx(v.cvt64(), v.cvt16()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + if (!jit) + panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - regIsConstant &= ~(1 << idx); + regIsConstant &= ~(1 << idx); - jit->code.movzx(v.cvt64(), v.cvt16()); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.movzx(v.cvt64(), v.cvt16()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + if (!jit) + panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - regIsConstant &= ~(1 << idx); + regIsConstant &= ~(1 << idx); - jit->code.movsxd(v.cvt64(), v.cvt32()); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.movsxd(v.cvt64(), v.cvt32()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + if (!jit) + panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - regIsConstant &= ~(1 << idx); + regIsConstant &= ~(1 << idx); - jit->code.movzx(v.cvt64(), v.cvt32()); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.movzx(v.cvt64(), v.cvt32()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - if (idx == 0) - return; + if (idx == 0) + return; - if (!jit) - panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); + if (!jit) + panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); - regIsConstant &= ~(1 << idx); + regIsConstant &= ~(1 << idx); - jit->code.mov(jit->GPR(idx), v.cvt64()); + jit.code.mov(jit.GPR(idx), v.cvt64()); } template <> void Registers::Write(size_t idx, Xbyak::Reg v) { - Write(idx, v); + Write(idx, v); } #endif } // namespace n64 diff --git a/src/backend/core/registers/Registers.hpp b/src/backend/core/registers/Registers.hpp index c4f17fa..f137b34 100644 --- a/src/backend/core/registers/Registers.hpp +++ b/src/backend/core/registers/Registers.hpp @@ -4,77 +4,72 @@ #include namespace n64 { +#ifdef KAIZEN_JIT_ENABLED struct JIT; struct Registers { - Registers(); - void Reset(); - void SetPC64(s64); - void SetPC32(s32); - void SetJIT(JIT* jit) { this->jit = jit; } + JIT &jit; + Registers(JIT &jit); +#else +struct Registers { + Registers(); + void Reset(); + void SetPC64(s64); + void SetPC32(s32); - [[nodiscard]] bool IsRegConstant(const u32 index) const { - if (index == 0) - return true; - return regIsConstant & (1 << index); - } + [[nodiscard]] bool IsRegConstant(const u32 index) const { + if (index == 0) + return true; + return regIsConstant & (1 << index); + } - [[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const { - return IsRegConstant(index1) && IsRegConstant(index2); - } + [[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const { + 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); - } + void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); } - JIT *jit = nullptr; + uint64_t regIsConstant = 0; - uint64_t regIsConstant = 0; + bool prevDelaySlot{}, delaySlot{}; + u32 steps = 0; + u32 extraCycles = 0; + s64 oldPC{}, pc{}, nextPC{}; + s64 hi{}, lo{}; + Cop0 cop0; + Cop1 cop1; - bool prevDelaySlot{}, delaySlot{}; - u32 steps = 0; - u32 extraCycles = 0; - s64 oldPC{}, pc{}, nextPC{}; - s64 hi{}, lo{}; - Cop0 cop0; - Cop1 cop1; + void CpuStall(u32 cycles) { extraCycles += cycles; } - void CpuStall(u32 cycles) { extraCycles += cycles; } + u32 PopStalledCycles() { + u32 ret = extraCycles; + extraCycles = 0; + return ret; + } - u32 PopStalledCycles() { - u32 ret = extraCycles; - extraCycles = 0; - return ret; - } + template + T Read(size_t); + template + void Read(size_t, Xbyak::Reg); + template + void Write(size_t, T); + template + void Write(size_t, Xbyak::Reg); - template - T Read(size_t); - template - void Read(size_t, Xbyak::Reg); - template - void Write(size_t, T); - template - void Write(size_t, Xbyak::Reg); + std::array gpr{}; - std::array 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 diff --git a/src/frontend/Debugger.cpp b/src/frontend/Debugger.cpp index 8fd9888..f883f37 100644 --- a/src/frontend/Debugger.cpp +++ b/src/frontend/Debugger.cpp @@ -1,249 +1,243 @@ #include #include +#include -char const* regNames[] = { - "zero", "at", "v0", "v1", - "a0", "a1", "a2", "a3", - "t0", "t1", "t2", "t3", - "t4", "t5", "t6", "t7", - "s0", "s1", "s2", "s3", - "s4", "s5", "s6", "s7", - "t8", "t9", "k0", "k1", - "gp", "sp", "s8", "ra", -}; - -void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) { - n64::Core& core = n64::Core::GetInstance(); - bool isBroken = core.breakpoints.contains(addr); - ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff); - ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff); - ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f); - if(ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) { - core.ToggleBreakpoint(addr); - } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); +void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) { + n64::Core &core = n64::Core::GetInstance(); + bool isBroken = core.breakpoints.contains(addr); + ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff); + ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff); + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f); + if (ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) { + core.ToggleBreakpoint(addr); + } + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); } -void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) { - if(!disasm.success) { - ImGui::TextColored(ImColor(0xffeaefb6), "????????????????"); - return; - } - - ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str()); -} - -void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) { - if(!disasm.success) { - ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful..."); - return; - } - - ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str()); - ImGui::SameLine(0, 0); - for(int i = 0; i < 3; i++) { - if(disasm.ops[i].str.empty()) - continue; - - if(i >= 2) { - ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str()); - ImGui::SameLine(0, 0); - continue; +void AddressFunc(s64, Disassembler::DisassemblyResult &disasm) { + if (!disasm.success) { + ImGui::TextColored(ImColor(0xffeaefb6), "????????????????"); + return; } - std::string op_str = disasm.ops[i].str; - if(!disasm.ops[i+1].str.empty()) - op_str += ", "; - - ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str()); + ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str()); +} + +void InstructionFunc(s64, Disassembler::DisassemblyResult &disasm) { + if (!disasm.success) { + ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful..."); + return; + } + + ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str()); ImGui::SameLine(0, 0); - } + for (int i = 0; i < 3; i++) { + if (disasm.ops[i].str.empty()) + continue; + + if (i >= 2) { + ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str()); + ImGui::SameLine(0, 0); + continue; + } + + std::string op_str = disasm.ops[i].str; + if (!disasm.ops[i + 1].str.empty()) + op_str += ", "; + + ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str()); + ImGui::SameLine(0, 0); + } } void Debugger::RegisterView() { - if(!ImGui::BeginTabItem("Registers")) - return; - - if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody)) - return; + if (!ImGui::BeginTabItem("Registers")) + return; - ImGui::TableSetupColumn("Name"); - ImGui::TableSetupColumn("Value"); - ImGui::TableSetupColumn("Name"); - ImGui::TableSetupColumn("Value"); - - ImGui::TableHeadersRow(); + if (!ImGui::BeginTable("##regs", 4, + ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | + ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | + ImGuiTableFlags_ContextMenuInBody)) + return; - auto renderMemoryTable = [&](u64 vaddr) { - if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip)) - return; - - if(!ImGui::BeginTooltip()) - return; - - ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str()); - if(!ImGui::BeginTable("##memoryContents", 16)) - return; - - for(u32 col = 0; col < 16; col++) - ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str()); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Value"); ImGui::TableHeadersRow(); - for(u32 row = 0; row < 16; row++) { - ImGui::TableNextRow(); - for(u32 col = 0; col < 16; col+=4) { - u32 paddr; - if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr)) - continue; + auto renderMemoryTable = [&](u64 vaddr) { + if (!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip)) + return; - const u32 val = n64::Core::GetMem().Read(paddr); + if (!ImGui::BeginTooltip()) + return; - ImGui::TableSetColumnIndex(col+0); - ImGui::Text("%02X", (val >> 24) & 0xff); - ImGui::TableSetColumnIndex(col+1); - ImGui::Text("%02X", (val >> 16) & 0xff); - ImGui::TableSetColumnIndex(col+2); - ImGui::Text("%02X", (val >> 8) & 0xff); - ImGui::TableSetColumnIndex(col+3); - ImGui::Text("%02X", (val >> 0) & 0xff); - } + ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str()); + if (!ImGui::BeginTable("##memoryContents", 16)) + return; + + for (u32 col = 0; col < 16; col++) + ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str()); + + ImGui::TableHeadersRow(); + + for (u32 row = 0; row < 16; row++) { + ImGui::TableNextRow(); + for (u32 col = 0; col < 16; col += 4) { + u32 paddr; + if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr)) + continue; + + const u32 val = n64::Core::GetMem().Read(paddr); + + ImGui::TableSetColumnIndex(col + 0); + ImGui::Text("%02X", (val >> 24) & 0xff); + ImGui::TableSetColumnIndex(col + 1); + ImGui::Text("%02X", (val >> 16) & 0xff); + ImGui::TableSetColumnIndex(col + 2); + ImGui::Text("%02X", (val >> 8) & 0xff); + ImGui::TableSetColumnIndex(col + 3); + ImGui::Text("%02X", (val >> 0) & 0xff); + } + } + + ImGui::EndTable(); + ImGui::EndTooltip(); + }; + + n64::Registers ®s = n64::Core::GetRegs(); + + for (int i = 0; i < 32; i += 2) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", n64::Registers::regNames[i]); + + ImGui::TableSetColumnIndex(1); + auto value = regs.Read(i); + ImGui::Text("%s", std::format("{:016X}", value).c_str()); + renderMemoryTable(value); + + ImGui::TableSetColumnIndex(2); + ImGui::Text("%s", n64::Registers::regNames[i + 1]); + + ImGui::TableSetColumnIndex(3); + value = regs.Read(i + 1); + ImGui::Text("%s", std::format("{:016X}", value).c_str()); + renderMemoryTable(value); } - + ImGui::EndTable(); - ImGui::EndTooltip(); - }; - - n64::Registers& regs = n64::Core::GetRegs(); - - for(int i = 0; i < 32; i+=2) { - ImGui::TableNextRow(); - - ImGui::TableSetColumnIndex(0); - ImGui::Text("%s", regNames[i]); - - ImGui::TableSetColumnIndex(1); - auto value = regs.Read(i); - ImGui::Text("%s", std::format("{:016X}", value).c_str()); - renderMemoryTable(value); - - ImGui::TableSetColumnIndex(2); - ImGui::Text("%s", regNames[i+1]); - - ImGui::TableSetColumnIndex(3); - value = regs.Read(i+1); - ImGui::Text("%s", std::format("{:016X}", value).c_str()); - renderMemoryTable(value); - } - - ImGui::EndTable(); - ImGui::EndTabItem(); + ImGui::EndTabItem(); } bool Debugger::render() { - n64::Core &core = n64::Core::GetInstance(); - const n64::Registers& regs = n64::Core::GetRegs(); + n64::Core &core = n64::Core::GetInstance(); + const n64::Registers ®s = n64::Core::GetRegs(); - if(!enabled) - return false; + if (!enabled) + return false; - static s64 startAddr = 0xFFFF'FFFF'8000'0000; - constexpr int step = 4; - constexpr int stepFast = 256; - - if(!ImGui::Begin("Debugger", &enabled)) { - ImGui::End(); - return false; - } + static s64 startAddr = 0xFFFF'FFFF'8000'0000; + constexpr int step = 4; + constexpr int stepFast = 256; - ImGui::BeginDisabled(followPC); - ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::EndDisabled(); - - ImGui::Text("Follow program counter:"); - ImGui::SameLine(0,0); - - ImGui::Checkbox("##followPC", &followPC); - ImGui::SameLine(0,0); - - ImGui::Text("Add a breakpoint"); - ImGui::SameLine(0,0); - - if(followPC) - startAddr = regs.pc - 256; // TODO: arbitrary??? - - if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) { - core.ToggleBreakpoint(startAddr); - } - - if(!ImGui::BeginTabBar("##debuggerTabs")) { - ImGui::EndTabBar(); - ImGui::End(); - return false; - } - - RegisterView(); - - if(!ImGui::BeginTabItem("MIPS R4300i code view")) { - ImGui::EndTabBar(); - ImGui::End(); - return false; - } - - constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | - ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - - if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) { - ImGui::EndTabBar(); - ImGui::End(); - return false; - } - - for(auto &[name, _] : columns) - ImGui::TableSetupColumn(name); - - ImGui::TableHeadersRow(); - - for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) { - auto disasm = Disassembler::GetInstance().Disassemble(addr); - const auto addrIsCurrent = addr == regs.nextPC; - const auto addrIsBreakpoint = core.breakpoints.contains(addr); - ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg]; - ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt]; - if(addrIsCurrent) { - colorChoice = 0x80e27fbc; - colorChoiceAlt = 0x80e27fbc; + if (!ImGui::Begin("Debugger", &enabled)) { + ImGui::End(); + return false; } - if(addrIsBreakpoint) { - colorChoice = 0x800000ff; - colorChoiceAlt = 0x800000ff; + ImGui::BeginDisabled(followPC); + ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", + ImGuiInputTextFlags_CharsHexadecimal); + ImGui::EndDisabled(); + + ImGui::Text("Follow program counter:"); + ImGui::SameLine(0, 0); + + ImGui::Checkbox("##followPC", &followPC); + ImGui::SameLine(0, 0); + + ImGui::Text("Add a breakpoint"); + ImGui::SameLine(0, 0); + + if (followPC) + startAddr = regs.pc - 256; // TODO: arbitrary??? + + if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) { + core.ToggleBreakpoint(startAddr); } - ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value); - ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value); - - ImGui::TableNextRow(); - for(int i = 0; auto &[_, func] : columns) { - ImGui::TableSetColumnIndex(i++); - func(addr, disasm); + if (!ImGui::BeginTabBar("##debuggerTabs")) { + ImGui::EndTabBar(); + ImGui::End(); + return false; } - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - } + RegisterView(); - ImGui::EndTable(); + if (!ImGui::BeginTabItem("MIPS R4300i code view")) { + ImGui::EndTabBar(); + ImGui::End(); + return false; + } - ImGui::EndTabItem(); - ImGui::EndTabBar(); + constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | + ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; - ImGui::End(); - return true; + if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) { + ImGui::EndTabBar(); + ImGui::End(); + return false; + } + + for (auto &[name, _] : columns) + ImGui::TableSetupColumn(name); + + ImGui::TableHeadersRow(); + + for (auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) { + auto disasm = Disassembler::GetInstance().Disassemble(addr); + const auto addrIsCurrent = addr == regs.nextPC; + const auto addrIsBreakpoint = core.breakpoints.contains(addr); + ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg]; + ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt]; + if (addrIsCurrent) { + colorChoice = 0x80e27fbc; + colorChoiceAlt = 0x80e27fbc; + } + + if (addrIsBreakpoint) { + colorChoice = 0x800000ff; + colorChoiceAlt = 0x800000ff; + } + + ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value); + ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value); + + ImGui::TableNextRow(); + for (int i = 0; auto &[_, func] : columns) { + ImGui::TableSetColumnIndex(i++); + func(addr, disasm); + } + + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + } + + ImGui::EndTable(); + + ImGui::EndTabItem(); + ImGui::EndTabBar(); + + ImGui::End(); + return true; } diff --git a/src/frontend/Debugger.hpp b/src/frontend/Debugger.hpp index 659e2b7..3f05ac1 100644 --- a/src/frontend/Debugger.hpp +++ b/src/frontend/Debugger.hpp @@ -1,28 +1,33 @@ #pragma once #include +#include -void BreakpointFunc(s64, Disassembler::DisassemblyResult&); -void AddressFunc(s64, Disassembler::DisassemblyResult&); -void InstructionFunc(s64, Disassembler::DisassemblyResult&); +void BreakpointFunc(s64, Disassembler::DisassemblyResult &); +void AddressFunc(s64, Disassembler::DisassemblyResult &); +void InstructionFunc(s64, Disassembler::DisassemblyResult &); class Debugger final { - bool enabled = false; - static constexpr auto MAX_LINES_OF_DISASM = 150; + bool enabled = false; + static constexpr auto MAX_LINES_OF_DISASM = 150; - struct Column { - const char* name = nullptr; - void (*func)(s64, Disassembler::DisassemblyResult&) = nullptr; - }; + struct Column { + const char *name = nullptr; + void (*func)(s64, Disassembler::DisassemblyResult &) = nullptr; + }; - std::array columns = { - Column{"##BreakpointColumn", &BreakpointFunc}, - Column{"Address", &AddressFunc}, - Column{"Instruction", &InstructionFunc}, - }; -public: - static void RegisterView(); - bool followPC = true; - void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; } - void Close() { enabled = false; } - bool render(); -}; \ No newline at end of file + std::array columns = { + Column{"##BreakpointColumn", &BreakpointFunc}, + Column{"Address", &AddressFunc}, + Column{"Instruction", &InstructionFunc}, + }; + + public: + static void RegisterView(); + bool followPC = true; + void Open(bool wantFollowPC = true) { + enabled = true; + followPC = wantFollowPC; + } + void Close() { enabled = false; } + bool render(); +}; diff --git a/src/frontend/Settings/CPUSettings.cpp b/src/frontend/Settings/CPUSettings.cpp index 0b7ee07..1664013 100644 --- a/src/frontend/Settings/CPUSettings.cpp +++ b/src/frontend/Settings/CPUSettings.cpp @@ -4,40 +4,41 @@ #include CPUSettings::CPUSettings() { - if (Options::GetInstance().GetValue("cpu", "type") == "jit") { - selectedCpuTypeIndex = 1; - } else { - selectedCpuTypeIndex = 0; - } + if (Options::GetInstance().GetValue("cpu", "type") == "jit") { + selectedCpuTypeIndex = 1; + } else { + selectedCpuTypeIndex = 0; + } } void CPUSettings::render() { - const char* items[] = { - "Interpreter", - "Dynamic Recompiler" - }; + const char *items[] = {"Interpreter", +#ifdef KAIZEN_JIT_ENABLED + "Dynamic Recompiler" +#endif + }; - const char* combo_preview_value = items[selectedCpuTypeIndex]; - if (ImGui::BeginCombo("CPU Type", combo_preview_value)) { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) { - const bool is_selected = (selectedCpuTypeIndex == n); - if (ImGui::Selectable(items[n], is_selected)) { - selectedCpuTypeIndex = n; - modified = true; - } - - // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) - if (is_selected) - ImGui::SetItemDefaultFocus(); - } - ImGui::EndCombo(); - } + const char *combo_preview_value = items[selectedCpuTypeIndex]; + if (ImGui::BeginCombo("CPU Type", combo_preview_value)) { + for (int n = 0; n < IM_ARRAYSIZE(items); n++) { + const bool is_selected = (selectedCpuTypeIndex == n); + if (ImGui::Selectable(items[n], is_selected)) { + selectedCpuTypeIndex = n; + modified = true; + } - if(modified) { - if(selectedCpuTypeIndex == 0) { - Options::GetInstance().SetValue("cpu", "type", "interpreter"); - } else { - Options::GetInstance().SetValue("cpu", "type", "jit"); + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + + if (modified) { + if (selectedCpuTypeIndex == 0) { + Options::GetInstance().SetValue("cpu", "type", "interpreter"); + } else { + Options::GetInstance().SetValue("cpu", "type", "jit"); + } } - } } diff --git a/src/utils/Instruction.hpp b/src/utils/Instruction.hpp index acc53f6..ef0c028 100644 --- a/src/utils/Instruction.hpp +++ b/src/utils/Instruction.hpp @@ -7,7 +7,7 @@ struct Instruction { Instruction(u32 v) { instr.raw = v; } void operator=(u32 v) { instr.raw = v; } operator u32() const { return instr.raw; } - + inline u8 rs() const { return instr.rtype.rs; } inline u8 rt() const { return instr.rtype.rt; } inline u8 rd() const { return instr.rtype.rd; } @@ -21,7 +21,9 @@ struct Instruction { inline u8 vd() const { return fd(); } inline u8 e1() const { return (instr.raw >> 7) & 0x0f; } inline u8 e2() const { return rs() & 0x0f; } + inline u8 op() const { return rt(); } inline u16 imm() const { return instr.itype.imm; } + inline u16 offset() const { return imm(); } inline u32 target() const { return instr.jtype.target; } inline u8 opcode() const { return instr.opcode.op; } inline u8 special() const { return instr.opcode.special; } @@ -32,61 +34,61 @@ struct Instruction { union { struct { - unsigned imm:16; - unsigned rt:5; - unsigned rs:5; - unsigned op:6; + unsigned imm : 16; + unsigned rt : 5; + unsigned rs : 5; + unsigned op : 6; } itype; struct { - unsigned target:26; - unsigned op:6; + unsigned target : 26; + unsigned op : 6; } jtype; struct { - unsigned funct:6; - unsigned sa:5; - unsigned rd:5; - unsigned rt:5; - unsigned rs:5; - unsigned op:6; + unsigned funct : 6; + unsigned sa : 5; + unsigned rd : 5; + unsigned rt : 5; + unsigned rs : 5; + unsigned op : 6; } rtype; union { struct { - unsigned special_lo:3; - unsigned special_hi:3; - unsigned:26; + unsigned special_lo : 3; + unsigned special_hi : 3; + unsigned : 26; }; struct { - unsigned special:6; - unsigned:26; + unsigned special : 6; + unsigned : 26; }; struct { - unsigned:16; - unsigned regimm_lo:3; - unsigned regimm_hi:2; - unsigned:11; + unsigned : 16; + unsigned regimm_lo : 3; + unsigned regimm_hi : 2; + unsigned : 11; }; struct { - unsigned:16; - unsigned regimm:5; - unsigned:11; + unsigned : 16; + unsigned regimm : 5; + unsigned : 11; }; struct { - unsigned:26; - unsigned op:6; + unsigned : 26; + unsigned op : 6; }; struct { - unsigned funct:6; - unsigned:10; - unsigned cop_rt:5; - unsigned cop_rs:5; - unsigned:6; + unsigned funct : 6; + unsigned : 10; + unsigned cop_rt : 5; + unsigned cop_rs : 5; + unsigned : 6; }; u32 raw; @@ -221,4 +223,4 @@ struct Instruction { static constexpr u8 BLTZALL = 0b10010; static constexpr u8 BGEZALL = 0b10011; }; -} \ No newline at end of file +} // namespace n64