diff --git a/.gitignore b/.gitignore index d4651cd..3665593 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ saves/ .cache/ .vs/ .vscode/ +.zed/ out/ *.toml *.ini @@ -20,9 +21,11 @@ vgcore.* *.data disasm.txt *log*.txt +*.log CMakeSettings.json compile_commands.json *.diagsession tests/ .DS_Store -resources/version.hpp \ No newline at end of file +resources/version.hpp +__cmake_systeminformation/CMakeFiles/ 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..c8a6003 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -4,18 +4,20 @@ #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 if (selectedCpu == "cached_interpreter") { + cpuType = CachedInterpreter; } else { panic("Unimplemented CPU type"); } @@ -30,7 +32,7 @@ void Core::Stop() { void Core::Reset() { regs.Reset(); mem->Reset(); - cpu->Reset(); + interpreter.Reset(); if (romLoaded) mem->mmio.si.pif.Execute(); } @@ -62,7 +64,20 @@ 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(); + + if (cpuType == CachedInterpreter) + return interpreter.ExecuteCached() + 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; @@ -91,35 +106,25 @@ void Core::Run(const float volumeL, const float volumeR) { bool broken = false; for (int field = 0; field < mmio.vi.numFields; field++) { + Scheduler::GetInstance().HandleEvents(); u32 frameCycles = 0; - for (int i = 0; i < mmio.vi.numHalflines; i++) { - mmio.vi.current = (i << 1) + field; + for (int halfline = 0; halfline < mmio.vi.numHalflines; halfline++) { + mmio.vi.current = (halfline << 1) + field; if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { mmio.mi.InterruptRaise(MI::Interrupt::VI); } - while (cycles < mem->mmio.vi.cyclesPerHalfline) { + for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { + Scheduler::GetInstance().HandleEvents(); const u32 taken = StepCPU(); cycles += taken; - - if ((broken = breakpoints.contains(regs.nextPC))) - break; - - StepRSP(taken); frameCycles += taken; + StepRSP(taken); Scheduler::GetInstance().Tick(taken); } - - if (broken) - break; - - cycles -= mmio.vi.cyclesPerHalfline; } - if (broken) - break; - if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { mmio.mi.InterruptRaise(MI::Interrupt::VI); } @@ -127,8 +132,5 @@ void Core::Run(const float volumeL, const float volumeR) { mmio.ai.Step(frameCycles, volumeL, volumeR); Scheduler::GetInstance().Tick(frameCycles); } - - if (broken) - pause = true; } } // namespace n64 diff --git a/src/backend/Core.hpp b/src/backend/Core.hpp index 79113c2..e4a0ea2 100644 --- a/src/backend/Core.hpp +++ b/src/backend/Core.hpp @@ -1,62 +1,65 @@ #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 = CachedInterpreter; - 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 inline bool IsAddressError(u8 mask, u64 vaddr) { + auto regs = GetRegs(); + return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0; + } - Registers regs; - ParallelRDP parallel; + static Registers &GetRegs() { return GetInstance().regs; } + + 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; + 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/Scheduler.cpp b/src/backend/Scheduler.cpp index d031a0d..119e676 100644 --- a/src/backend/Scheduler.cpp +++ b/src/backend/Scheduler.cpp @@ -5,47 +5,63 @@ void Scheduler::EnqueueRelative(const u64 t, const EventType type) { EnqueueAbso void Scheduler::EnqueueAbsolute(const u64 t, const EventType type) { events.push({t, type}); } +Event *Scheduler::Find(const EventType eventType) const { + for (auto &event : events) { + if (event.type == eventType) { + const u64 ret = event.time - ticks; + return &event; + } + } + + return nullptr; +} + u64 Scheduler::Remove(const EventType eventType) const { - for (auto &[time, type] : events) { - if (type == eventType) { - const u64 ret = time - ticks; - type = NONE; - time = ticks; - return ret; - } - } + auto event = Find(eventType); + if (!event) + return 0; - return 0; + const u64 ret = event->time - ticks; + event->type = NONE; + event->time = ticks; + return ret; } -void Scheduler::Tick(const u64 t) { - n64::Mem& mem = n64::Core::GetMem(); - ticks += t; - n64::MI &mi = mem.mmio.mi; - n64::SI &si = mem.mmio.si; - n64::PI &pi = mem.mmio.pi; +void Scheduler::SkipToNext() { ticks = events.top().time; } - while (ticks >= events.top().time) { - switch (const auto type = events.top().type) { - case SI_DMA: - si.DMA(); - break; - case PI_DMA_COMPLETE: - mi.InterruptRaise(n64::MI::Interrupt::PI); - pi.dmaBusy = false; - break; - case PI_BUS_WRITE_COMPLETE: - pi.ioBusy = false; - break; - case NONE: - break; - case IMPOSSIBLE: - Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness"); - return; - default: - Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast(type)); - return; +void Scheduler::Tick(const u64 t) { ticks += t; } + +void Scheduler::HandleEvents() { + n64::Mem &mem = n64::Core::GetMem(); + n64::MI &mi = mem.mmio.mi; + n64::SI &si = mem.mmio.si; + n64::PI &pi = mem.mmio.pi; + + while (ticks >= events.top().time) { + switch (const auto type = events.top().type) { + case SI_DMA: + si.DMA(); + break; + case PI_DMA_COMPLETE: + mi.InterruptRaise(n64::MI::Interrupt::PI); + pi.dmaBusy = false; + break; + case PI_BUS_WRITE_COMPLETE: + pi.ioBusy = false; + break; + case NONE: + break; + case IMPOSSIBLE: + Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, + {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, + "Unrecognized rom endianness"); + return; + default: + Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, + {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, + "Unknown scheduler event type {}", static_cast(type)); + return; + } + events.pop(); } - events.pop(); - } } diff --git a/src/backend/Scheduler.hpp b/src/backend/Scheduler.hpp index 5a37a52..51e43d5 100644 --- a/src/backend/Scheduler.hpp +++ b/src/backend/Scheduler.hpp @@ -6,41 +6,44 @@ enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE }; struct Event { - u64 time; - EventType type; + u64 time; + EventType type; - friend bool operator<(const Event &rhs, const Event &lhs) { return rhs.time < lhs.time; } + friend bool operator<(const Event &rhs, const Event &lhs) { return rhs.time < lhs.time; } - friend bool operator>(const Event &rhs, const Event &lhs) { return rhs.time > lhs.time; } + friend bool operator>(const Event &rhs, const Event &lhs) { return rhs.time > lhs.time; } - friend bool operator>=(const Event &rhs, const Event &lhs) { return rhs.time >= lhs.time; } + friend bool operator>=(const Event &rhs, const Event &lhs) { return rhs.time >= lhs.time; } }; struct IterableEvents { - std::priority_queue, std::greater<>> events; + std::priority_queue, std::greater<>> events; - explicit IterableEvents() = default; - [[nodiscard]] auto top() const { return events.top(); } - auto pop() { events.pop(); } - [[nodiscard]] auto begin() const { return const_cast(&events.top()); } - [[nodiscard]] auto end() const { return begin() + events.size(); } - auto push(const Event e) { events.push(e); } + explicit IterableEvents() = default; + [[nodiscard]] auto top() const { return events.top(); } + auto pop() { events.pop(); } + [[nodiscard]] auto begin() const { return const_cast(&events.top()); } + [[nodiscard]] auto end() const { return begin() + events.size(); } + auto push(const Event e) { events.push(e); } }; struct Scheduler { - Scheduler() { EnqueueAbsolute(std::numeric_limits::max(), IMPOSSIBLE); } + Scheduler() { EnqueueAbsolute(std::numeric_limits::max(), IMPOSSIBLE); } - static Scheduler &GetInstance() { - static Scheduler instance; - return instance; - } + static Scheduler &GetInstance() { + static Scheduler instance; + return instance; + } - void EnqueueRelative(u64, EventType); - void EnqueueAbsolute(u64, EventType); - [[nodiscard]] u64 Remove(EventType) const; - void Tick(u64 t); + void HandleEvents(); + void EnqueueRelative(u64, EventType); + void EnqueueAbsolute(u64, EventType); + [[nodiscard]] u64 Remove(EventType) const; + [[nodiscard]] Event *Find(EventType) const; + void Tick(u64 t); + void SkipToNext(); - u8 index = 0; - u64 ticks = 0; - IterableEvents events{}; + u8 index = 0; + u64 ticks = 0; + IterableEvents events{}; }; diff --git a/src/backend/core/BaseCPU.hpp b/src/backend/core/BaseCPU.hpp deleted file mode 100644 index c69448f..0000000 --- a/src/backend/core/BaseCPU.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include -#include - -namespace n64 { -struct alignas(32) InstructionCache { - bool valid; - u32 data[8]; - u32 ptag; - - private: - int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; } - u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; } -}; - -struct alignas(32) DataCache { - bool valid, dirty; - u8 data[16]; - u32 ptag; - int index; - - private: - 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/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/Cache.cpp b/src/backend/core/Cache.cpp new file mode 100644 index 0000000..6614d09 --- /dev/null +++ b/src/backend/core/Cache.cpp @@ -0,0 +1,96 @@ +#include + +namespace n64 { +void InstructionCache::FillLine(u64 vaddr, u32 paddr) { + int index = GetICacheLineIndex(vaddr); + auto &line = lines[index]; + line.valid = true; + line.ptag = paddr & ~0x00000fff; + auto &mmio = Core::GetMem().mmio; + + for (int i = 0; i < 8; i++) { + line.data[i] = mmio.rdp.ReadRDRAM(line.ptag | index); + } +} + +void InstructionCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) { + auto &line = lines[GetICacheLineIndex(vaddr)]; + + line.valid = (cop0.tagLo.pstate >> 1) & 1; + line.ptag = ptag; +} + +void DataCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) { + auto &line = lines[GetDCacheLineIndex(vaddr)]; + + line.valid = (cop0.tagLo.pstate >> 1) & 1; + line.dirty = (cop0.tagLo.pstate >> 0) & 1; + line.ptag = ptag; +} + +void InstructionCache::LoadTag(u64 vaddr) { + auto &cop0 = Core::GetRegs().cop0; + auto &line = lines[GetICacheLineIndex(vaddr)]; + cop0.tagLo.pstate = line.valid << 1; + cop0.tagLo.ptaglo = line.ptag; +} + +void DataCache::LoadTag(u64 vaddr) { + auto &cop0 = Core::GetRegs().cop0; + auto &line = lines[GetDCacheLineIndex(vaddr)]; + cop0.tagLo.pstate = (line.valid << 1) | line.dirty; + cop0.tagLo.ptaglo = line.ptag; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr) { + auto &mmio = Core::GetMem().mmio; + + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (!line.valid) + return; + + u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); + u32 lineStart = GetDCacheLineStart(origPhysAddr); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr); + for (int i = 0; i < 16; i++) { + mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); + } + + line.dirty = false; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr) { + WriteBack(vaddr, paddr); + lines[GetDCacheLineIndex(vaddr)].valid = false; +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (line.ptag == ptag) + WriteBack(vaddr, paddr); +} + +template <> +void DataCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + DCacheLine &line = lines[GetDCacheLineIndex(vaddr)]; + if (line.ptag == ptag) + WriteBack(vaddr, paddr); +} + +void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) { + auto &mmio = Core::GetMem().mmio; + ICacheLine &line = lines[GetICacheLineIndex(vaddr)]; + + if (line.ptag == ptag && line.valid) { + u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff); + u32 lineStart = GetICacheLineStart(origPhysAddr); + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr); + for (int i = 0; i < 16; i++) { + mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]); + } + } +} +} // namespace n64 diff --git a/src/backend/core/Cache.hpp b/src/backend/core/Cache.hpp new file mode 100644 index 0000000..49da308 --- /dev/null +++ b/src/backend/core/Cache.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include + +namespace n64 { +struct alignas(32) ICacheLine { + bool valid; + u32 data[8]; + u32 ptag; +}; + +struct alignas(32) DCacheLine { + bool valid, dirty; + u8 data[16]; + u32 ptag; + int index; +}; + +struct Cop0; + +inline const u32 GetPhysicalAddressPTag(const u32 paddr) { return paddr >> 12; } +inline const int GetICacheLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; } +inline const u32 GetICacheLineStart(u64 paddr) { return paddr & ~0x1F; } +inline const int GetDCacheLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; } +inline const u32 GetDCacheLineStart(u64 paddr) { return paddr & ~0xF; } + +struct InstructionCache { + std::array lines; + + void FillLine(u64, u32); + void InvalidateIndex(u64 vaddr) { lines[GetICacheLineIndex(vaddr)].valid = false; } + void StoreTag(u64, u32, Cop0 &); + void LoadTag(u64 vaddr); + + void WriteBack(u64 vaddr, u32 paddr, u32 ptag); + void InvalidateIndex(u64 vaddr, u32 ptag) { + int lineIndex = GetICacheLineIndex(vaddr); + if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag) + lines[lineIndex].valid = false; + } + + private: +}; + +struct DataCache { + std::array lines; + + void StoreTag(u64, u32, Cop0 &); + void LoadTag(u64 vaddr); + template + void WriteBack(u64 vaddr, u32 paddr); + template + void WriteBack(u64 vaddr, u32 paddr, u32 ptag); + void InvalidateIndex(u64 vaddr) { lines[GetDCacheLineIndex(vaddr)].valid = false; } + void InvalidateIndex(u64 vaddr, u32 ptag) { + int lineIndex = GetDCacheLineIndex(vaddr); + if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag) + lines[lineIndex].valid = false; + } +}; +} // namespace n64 diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index a389dd1..54ccaf6 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -1,58 +1,181 @@ #include +#include +#include "jit/helpers.hpp" namespace n64 { -Interpreter::Interpreter(Mem& mem, Registers& regs) : regs(regs), mem(mem) {} +Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {} bool Interpreter::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 Interpreter::CheckCompareInterrupt() const { - regs.cop0.count++; - regs.cop0.count &= 0x1FFFFFFFF; - if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { - regs.cop0.cause.ip7 = 1; - mem.mmio.mi.UpdateInterrupt(); - } +void Interpreter::UpdateCompareInterrupt() const { + regs.cop0.count++; + regs.cop0.count &= 0x1FFFFFFFF; + if (regs.cop0.count == static_cast(regs.cop0.compare) << 1) { + regs.cop0.cause.ip7 = 1; + mem.mmio.mi.UpdateInterrupt(); + } +} + +bool Interpreter::Fetch(Instruction &instr, u64 vaddr) { + u32 paddr = 0; + if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { + regs.cop0.HandleTLBException(vaddr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, vaddr); + return false; + } + + instr = mem.Read(paddr); + return true; +} + +bool Interpreter::MaybeAdvance() { + UpdateCompareInterrupt(); + + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + + if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] { + regs.cop0.HandleTLBException(regs.pc); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc); + return false; + } + + if (ShouldServiceInterrupt()) { + regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc); + return false; + } + + regs.oldPC = regs.pc; + regs.pc = regs.nextPC; + regs.nextPC += 4; + + return true; +} + +bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) { + UpdateCompareInterrupt(); + + regs.prevDelaySlot = regs.delaySlot; + regs.delaySlot = false; + + if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] { + regs.cop0.HandleTLBException(regs.pc); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc); + return false; + } + + if (!Fetch(instr, regs.pc)) + return false; + + if (ShouldServiceInterrupt()) { + regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc); + return false; + } + + regs.oldPC = regs.pc; + regs.pc = regs.nextPC; + regs.nextPC += 4; + + return true; } u32 Interpreter::Step() { - CheckCompareInterrupt(); + Instruction instr; + if (!FetchThenMaybeAdvance(instr)) + return 1; - regs.prevDelaySlot = regs.delaySlot; - regs.delaySlot = false; + DecodeExecute(instr); - if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc); return 1; - } +} - u32 paddr = 0; - if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) { - regs.cop0.HandleTLBException(regs.pc); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc); - return 1; - } +u32 Interpreter::CacheBlock(u32 addr) { + u32 blockAddr = addr; - const u32 instruction = mem.Read(paddr); + CachedLine line; + u32 i; + bool fetchDelaySlot = false; - if (ShouldServiceInterrupt()) { - regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc); - return 1; - } + for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) { + Instruction instr; + if (!Fetch(instr, addr)) + return i + 1; - regs.oldPC = regs.pc; - regs.pc = regs.nextPC; - regs.nextPC += 4; + addr += 4; + line.code[i] = instr; - Exec(instruction); + if (fetchDelaySlot) { + i++; + break; + } - return 1; + if (InstrEndsBlock(instr)) { + if (InstrHasDelaySlot(instr) && !fetchDelaySlot) { + fetchDelaySlot = true; + continue; + } + + if (i == 0) + i = 1; + + break; + } + } + + line.cycles = i; + line.len = i; + cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line); + + return ExecuteCached(); +} + +u32 Interpreter::ExecuteCached() { + u32 addr = regs.pc; + auto &blocks = cachedState.blocks; + + if (!blocks[CACHE_GET_BLOCK(addr)]) { + blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock(); + return CacheBlock(addr); + } + + const auto line = blocks[CACHE_GET_BLOCK(addr)]->lines[CACHE_GET_LINE(addr)]; + if (line) { + cachedState.exception = false; + // i copy the block cycles here in case the block evicts itself when executing which would set the cycles to + // 0, making so the emulator halts cause the outer loop won't advance + const auto blockCycles = line->cycles; + for (u32 i = 0; i < line->len; i++) { + if (!MaybeAdvance()) + return i + 1; + + Instruction instr = line->code[i]; + DecodeExecute(instr); + + if (cachedState.exception) + return i + 1; + + // Branch likely with false condition, it wasn't taken so don't execute the delay slot + if (IsBranchLikely(instr) && !regs.delaySlot) + break; + } + + if (line->idleSkip) { + Scheduler::GetInstance().SkipToNext(); + } + + if (blockCycles == 0) + panic("Cycles are 0"); + + return blockCycles; + } + + return CacheBlock(addr); } } // namespace n64 diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 1bac7a4..79657b7 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -1,125 +1,145 @@ #pragma once -#include +#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(); + u32 ExecuteCached(); + bool FetchThenMaybeAdvance(Instruction &); + bool MaybeAdvance(); + u32 CacheBlock(u32 addr); - void Reset() override { - cop2Latch = {}; - } + void SignalException(u32 addr) { cachedState.exception = true; } -private: - Registers& regs; - Mem& mem; - u64 cop2Latch{}; - friend struct Cop1; -#define check_address_error(mask, vaddr) \ - (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) - [[nodiscard]] bool ShouldServiceInterrupt() const; - void CheckCompareInterrupt() const; + void Reset() { + cop2Latch = {}; + cachedState.Reset(); + } - 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); + CachedState<12, std::numeric_limits::max()> cachedState; - void mtc2(Instruction); - void mfc2(Instruction); - void dmtc2(Instruction); - void dmfc2(Instruction); - void ctc2(Instruction); - void cfc2(Instruction); + private: + friend struct Cop1; + friend struct Mem; + + void MaybeIdleSkip(); + + InstructionCache icache; + DataCache dcache; + Registers ®s; + Mem &mem; + u64 cop2Latch{}; + u32 rspSyncCount = 0; + + bool Fetch(Instruction &, u64); + void CacheTypeData(u8, u64, u32, u32); + void CacheTypeInstruction(u8, u64, u32, u32); + + [[nodiscard]] bool ShouldServiceInterrupt() const; + void UpdateCompareInterrupt() const; + + void cop2Decode(Instruction); + void special(Instruction); + void regimm(Instruction); + void DecodeExecute(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); }; } // namespace n64 diff --git a/src/backend/core/JIT.cpp b/src/backend/core/JIT.cpp index e2b6dff..553321d 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 (Core::IsAddressError(0b11, vaddr)) [[unlikely]] { + /*regs.cop0.HandleTLBException(blockPC); + regs.cop0.FireException(Cop0::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..83a546b 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,246 @@ 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)) + [[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/JITUtils.hpp b/src/backend/core/JITUtils.hpp new file mode 100644 index 0000000..e63977a --- /dev/null +++ b/src/backend/core/JITUtils.hpp @@ -0,0 +1,45 @@ +#pragma once +#include +#include +#include + +namespace n64 { +static constexpr u32 MAX_INSTR_PER_BLOCK = 128; + +#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES)) +#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2) + +struct CachedLine { + bool idleSkip = false; + std::array code = {}; + u32 len = 0; + u32 cycles = 0; +}; + +template +struct CachedBlock { + CachedBlock() { lines.resize(lineAmount); } + std::vector lines = {}; +}; + +template +struct CachedState { + static constexpr u32 MAX_LINES = 1 << blockBits; + std::vector *> blocks = {}; + bool exception = false; + + void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; } + + void Reset() { + for (auto block : blocks) { + if (block) + for (auto line : block->lines) + delete line; + + delete block; + } + blocks = {}; + blocks.resize((addressSpace + 1) / MAX_LINES); + } +}; +} // namespace n64 diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index eadd4a8..6eb887d 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -4,553 +4,575 @@ #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))); + bool is_imem = paddr & 0x1000; + auto &dest = is_imem ? 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)); + bool is_imem = paddr & 0x1000; + auto &dest = is_imem ? 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)) { + bool is_imem = paddr & 0x1000; + auto &dest = is_imem ? 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)) { + bool is_imem = paddr & 0x1000; + auto &dest = is_imem ? 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/RDP.cpp b/src/backend/core/RDP.cpp index d503eb4..02a72a8 100644 --- a/src/backend/core/RDP.cpp +++ b/src/backend/core/RDP.cpp @@ -4,75 +4,75 @@ namespace n64 { RDP::RDP() { - rdram.resize(RDRAM_SIZE); - Reset(); + rdram.resize(RDRAM_SIZE); + Reset(); } void RDP::Reset() { - dpc = {}; - dpc.status.raw = 0x80; - std::ranges::fill(rdram, 0); - std::ranges::fill(cmd_buf, 0); + dpc = {}; + dpc.status.raw = 0x80; + std::ranges::fill(rdram, 0); + std::ranges::fill(cmd_buf, 0); } template <> void RDP::WriteRDRAM(const size_t idx, const u8 v) { - if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { - rdram[real] = v; - } + if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { + rdram[real] = v; + } } template <> void RDP::WriteRDRAM(const size_t idx, const u16 v) { - if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, real, v); - } + if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, real, v); + } } template <> void RDP::WriteRDRAM(const size_t idx, const u32 v) { - if (idx < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, idx, v); - } + if (idx < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, idx, v); + } } template <> void RDP::WriteRDRAM(const size_t idx, const u64 v) { - if (idx < RDRAM_SIZE) [[likely]] { - ircolib::WriteAccess(rdram, idx, v); - } + if (idx < RDRAM_SIZE) [[likely]] { + ircolib::WriteAccess(rdram, idx, v); + } } template <> u8 RDP::ReadRDRAM(const size_t idx) { - if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] - return rdram[real]; + if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] + return rdram[real]; - return 0; + return 0; } template <> u16 RDP::ReadRDRAM(const size_t idx) { - if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, real); + if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, real); - return 0; + return 0; } template <> u32 RDP::ReadRDRAM(const size_t idx) { - if (idx < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, idx); + if (idx < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, idx); - return 0; + return 0; } template <> u64 RDP::ReadRDRAM(const size_t idx) { - if (idx < RDRAM_SIZE) [[likely]] - return ircolib::ReadAccess(rdram, idx); + if (idx < RDRAM_SIZE) [[likely]] + return ircolib::ReadAccess(rdram, idx); - return 0; + return 0; } static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2, @@ -80,79 +80,78 @@ static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; auto RDP::Read(const u32 addr) const -> u32 { - switch (addr) { - case 0x04100000: - return dpc.start; - case 0x04100004: - return dpc.end; - case 0x04100008: - return dpc.current; - case 0x0410000C: - return dpc.status.raw; - case 0x04100010: - return dpc.clock; - case 0x04100014: - return dpc.status.cmdBusy; - case 0x04100018: - return dpc.status.pipeBusy; - case 0x0410001C: - return dpc.tmem; - default: - panic("Unhandled DP Command Registers read (addr: {:08X})", addr); - } + switch (addr) { + case 0x04100000: + return dpc.start; + case 0x04100004: + return dpc.end; + case 0x04100008: + return dpc.current; + case 0x0410000C: + return dpc.status.raw; + case 0x04100010: + return dpc.clock; + case 0x04100014: + return dpc.status.cmdBusy; + case 0x04100018: + return dpc.status.pipeBusy; + case 0x0410001C: + return dpc.tmem; + default: + panic("Unhandled DP Command Registers read (addr: {:08X})", addr); + } - return 0; + return 0; } void RDP::Write(const u32 addr, const u32 val) { - switch (addr) { - case 0x04100000: - WriteStart(val); - break; - case 0x04100004: - WriteEnd(val); - break; - case 0x0410000C: - WriteStatus(val); - break; - default: - panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val); - } + switch (addr) { + case 0x04100000: + WriteStart(val); + break; + case 0x04100004: + WriteEnd(val); + break; + case 0x0410000C: + WriteStatus(val); + break; + default: + panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val); + } } - void RDP::WriteStatus(const u32 val) { - DPCStatusWrite temp{}; - temp.raw = val; - bool unfrozen = false; + DPCStatusWrite temp{}; + temp.raw = val; + bool unfrozen = false; #define CLEAR_SET(val, clear, set) \ - do { \ - if ((clear)) \ - (val) = 0; \ - if ((set)) \ - (val) = 1; \ - } \ - while (0) + do { \ + if ((clear)) \ + (val) = 0; \ + if ((set)) \ + (val) = 1; \ + } \ + while (0) - CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma); - if (temp.clearFreeze) { - dpc.status.freeze = false; - unfrozen = true; - } + CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma); + if (temp.clearFreeze) { + dpc.status.freeze = false; + unfrozen = true; + } - if (temp.setFreeze) { - dpc.status.freeze = true; - } - CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush); - CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false); - CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false); - CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false); - CLEAR_SET(dpc.clock, temp.clearClock, false); + if (temp.setFreeze) { + dpc.status.freeze = true; + } + CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush); + CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false); + CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false); + CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false); + CLEAR_SET(dpc.clock, temp.clearClock, false); - if (!unfrozen) { - RunCommand(); - } + if (!unfrozen) { + RunCommand(); + } } /* FORCE_INLINE void logCommand(u8 cmd) { @@ -197,102 +196,102 @@ FORCE_INLINE void logCommand(u8 cmd) { */ void RDP::RunCommand() { - n64::Mem& mem = n64::Core::GetMem(); - ParallelRDP& parallel = n64::Core::GetInstance().parallel; - if (dpc.status.freeze) { - return; - } - dpc.status.pipeBusy = true; - dpc.status.startGclk = true; - if (dpc.end > dpc.current) { - dpc.status.freeze = true; - - static int remaining_cmds = 0; - - const u32 current = dpc.current & 0xFFFFF8; - const u32 end = dpc.end & 0xFFFFF8; - - const auto len = static_cast(end) - static_cast(current); - if (len <= 0) - return; - - if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) { - panic("Too many RDP commands"); - return; - } - - if (dpc.status.xbusDmemDma) { - for (int i = 0; i < len; i += 4) { - const u32 cmd = ircolib::ReadAccess(mem.mmio.rsp.dmem, current + i & 0xFFF); - cmd_buf[remaining_cmds + (i >> 2)] = cmd; - } - } else { - if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE) + n64::Mem &mem = n64::Core::GetMem(); + ParallelRDP ¶llel = n64::Core::GetInstance().parallel; + if (dpc.status.freeze) { return; - } - - for (int i = 0; i < len; i += 4) { - const u32 cmd = ircolib::ReadAccess(rdram, current + i); - cmd_buf[remaining_cmds + (i >> 2)] = cmd; - } } + dpc.status.pipeBusy = true; + dpc.status.startGclk = true; + if (dpc.end > dpc.current) { + dpc.status.freeze = true; - const int word_len = (len >> 2) + remaining_cmds; - int buf_index = 0; + static int remaining_cmds = 0; - bool processed_all = true; + const u32 current = dpc.current & 0xFFFFF8; + const u32 end = dpc.end & 0xFFFFF8; - while (buf_index < word_len) { - const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F; + const auto len = static_cast(end) - static_cast(current); + if (len <= 0) + return; - const int cmd_len = cmd_lens[cmd]; - if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) { - remaining_cmds = word_len - buf_index; - - u32 tmp[remaining_cmds]; - for (int i = 0; i < remaining_cmds; i++) { - tmp[i] = cmd_buf[buf_index + i]; + if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) { + panic("Too many RDP commands"); + return; } - for (int i = 0; i < remaining_cmds; i++) { - cmd_buf[i] = tmp[i]; + if (dpc.status.xbusDmemDma) { + for (int i = 0; i < len; i += 4) { + const u32 cmd = ircolib::ReadAccess(mem.mmio.rsp.dmem, current + i & 0xFFF); + cmd_buf[remaining_cmds + (i >> 2)] = cmd; + } + } else { + if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE) + return; + } + + for (int i = 0; i < len; i += 4) { + const u32 cmd = ircolib::ReadAccess(rdram, current + i); + cmd_buf[remaining_cmds + (i >> 2)] = cmd; + } } - processed_all = false; - break; - } + const int word_len = (len >> 2) + remaining_cmds; + int buf_index = 0; - if (cmd >= 8) { - parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]); - } + bool processed_all = true; - if (cmd == 0x29) { - OnFullSync(); - } + while (buf_index < word_len) { + const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F; - buf_index += cmd_len; + const int cmd_len = cmd_lens[cmd]; + if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) { + remaining_cmds = word_len - buf_index; + + u32 tmp[remaining_cmds]; + for (int i = 0; i < remaining_cmds; i++) { + tmp[i] = cmd_buf[buf_index + i]; + } + + for (int i = 0; i < remaining_cmds; i++) { + cmd_buf[i] = tmp[i]; + } + + processed_all = false; + break; + } + + if (cmd >= 8) { + parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]); + } + + if (cmd == 0x29) { + OnFullSync(); + } + + buf_index += cmd_len; + } + + if (processed_all) { + remaining_cmds = 0; + } + + dpc.current = end; + dpc.end = end; + dpc.status.freeze = false; } - - if (processed_all) { - remaining_cmds = 0; - } - - dpc.current = end; - dpc.end = end; - dpc.status.freeze = false; - } - dpc.status.cbufReady = true; + dpc.status.cbufReady = true; } void RDP::OnFullSync() { - n64::Mem& mem = n64::Core::GetMem(); - ParallelRDP& parallel = n64::Core::GetInstance().parallel; + n64::Mem &mem = n64::Core::GetMem(); + ParallelRDP ¶llel = n64::Core::GetInstance().parallel; - parallel.OnFullSync(); + parallel.OnFullSync(); - dpc.status.pipeBusy = false; - dpc.status.startGclk = false; - dpc.status.cbufReady = false; - mem.mmio.mi.InterruptRaise(MI::Interrupt::DP); + dpc.status.pipeBusy = false; + dpc.status.startGclk = false; + dpc.status.cbufReady = false; + mem.mmio.mi.InterruptRaise(MI::Interrupt::DP); } } // namespace n64 diff --git a/src/backend/core/RSP.cpp b/src/backend/core/RSP.cpp index b63bf50..160ebcf 100644 --- a/src/backend/core/RSP.cpp +++ b/src/backend/core/RSP.cpp @@ -1,228 +1,202 @@ #include #include +#include namespace n64 { RSP::RSP() { Reset(); } void RSP::Reset() { - lastSuccessfulSPAddr.raw = 0; - lastSuccessfulDRAMAddr.raw = 0; - spStatus.raw = 0; - spStatus.halt = true; - oldPC = 0; - pc = 0; - nextPC = 4; - spDMASPAddr.raw = 0; - spDMADRAMAddr.raw = 0; - spDMALen.raw = 0; - dmem = {}; - imem = {}; - memset(vpr, 0, 32 * sizeof(VPR)); - memset(gpr, 0, 32 * sizeof(u32)); - memset(&vce, 0, sizeof(VPR)); - memset(&acc, 0, 3 * sizeof(VPR)); - memset(&vcc, 0, 2 * sizeof(VPR)); - memset(&vco, 0, 2 * sizeof(VPR)); - semaphore = false; - divIn = 0; - divOut = 0; - divInLoaded = false; - steps = 0; + lastSuccessfulSPAddr.raw = 0; + lastSuccessfulDRAMAddr.raw = 0; + spStatus.raw = 0; + spStatus.halt = true; + oldPC = 0; + pc = 0; + nextPC = 4; + spDMASPAddr.raw = 0; + spDMADRAMAddr.raw = 0; + spDMALen.raw = 0; + dmem = {}; + imem = {}; + memset(vpr, 0, 32 * sizeof(VPR)); + memset(gpr, 0, 32 * sizeof(u32)); + memset(&vce, 0, sizeof(VPR)); + memset(&acc, 0, 3 * sizeof(VPR)); + memset(&vcc, 0, 2 * sizeof(VPR)); + memset(&vco, 0, 2 * sizeof(VPR)); + semaphore = false; + divIn = 0; + divOut = 0; + divInLoaded = false; + steps = 0; } -/* -FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) { - debug("{:04X} {:08X} ", rsp.oldPC, instr); - for (auto gpr : rsp.gpr) { - debug("{:08X} ", gpr); - } - - for (auto vpr : rsp.vpr) { - for (int i = 0; i < 8; i++) { - debug("{:04X}", vpr.element[i]); - } - debug(" "); - } - - for (int i = 0; i < 8; i++) { - debug("{:04X}", rsp.acc.h.element[i]); - } - debug(" "); - - for (int i = 0; i < 8; i++) { - debug("{:04X}", rsp.acc.m.element[i]); - } - debug(" "); - - for (int i = 0; i < 8; i++) { - debug("{:04X}", rsp.acc.l.element[i]); - } - - debug(" {:04X} {:04X} {:02X}", rsp.GetVCC(), rsp.GetVCO(), rsp.GetVCE()); - debug("DMEM: {:02X}{:02X}", rsp.dmem[0x3c4], rsp.dmem[0x3c5]); -} -*/ - auto RSP::Read(const u32 addr) -> u32 { - switch (addr) { - case 0x04040000: - return lastSuccessfulSPAddr.raw & 0x1FF8; - case 0x04040004: - return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; - case 0x04040008: - case 0x0404000C: - return spDMALen.raw; - case 0x04040010: - return spStatus.raw; - case 0x04040014: - return spStatus.dmaFull; - case 0x04040018: - return 0; - case 0x0404001C: - return AcquireSemaphore(); - case 0x04080000: - return pc & 0xFFC; - default: - panic("Unimplemented SP register read {:08X}", addr); - } + switch (addr) { + case 0x04040000: + return lastSuccessfulSPAddr.raw & 0x1FF8; + case 0x04040004: + return lastSuccessfulDRAMAddr.raw & 0xFFFFF8; + case 0x04040008: + case 0x0404000C: + return spDMALen.raw; + case 0x04040010: + return spStatus.raw; + case 0x04040014: + return spStatus.dmaFull; + case 0x04040018: + return 0; + case 0x0404001C: + return AcquireSemaphore(); + case 0x04080000: + return pc & 0xFFC; + default: + { + auto ®s = Core::GetRegs(); + + panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr, + (u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]); + } + } } void RSP::WriteStatus(const u32 value) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - MI &mi = mem.mmio.mi; - const auto write = SPStatusWrite{.raw = value}; - if (write.clearHalt && !write.setHalt) { - spStatus.halt = false; - } - if (write.setHalt && !write.clearHalt) { - regs.steps = 0; - spStatus.halt = true; - } - if (write.clearBroke) - spStatus.broke = false; - if (write.clearIntr && !write.setIntr) - mi.InterruptLower(MI::Interrupt::SP); - if (write.setIntr && !write.clearIntr) - mi.InterruptRaise(MI::Interrupt::SP); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + MI &mi = mem.mmio.mi; + const auto write = SPStatusWrite{.raw = value}; + if (write.clearHalt && !write.setHalt) { + spStatus.halt = false; + } + if (write.setHalt && !write.clearHalt) { + regs.steps = 0; + spStatus.halt = true; + } + if (write.clearBroke) + spStatus.broke = false; + if (write.clearIntr && !write.setIntr) + mi.InterruptLower(MI::Interrupt::SP); + if (write.setIntr && !write.clearIntr) + mi.InterruptRaise(MI::Interrupt::SP); #define CLEAR_SET(val, clear, set) \ - do { \ - if ((clear) && !(set)) \ - (val) = 0; \ - if ((set) && !(clear)) \ - (val) = 1; \ - } \ - while (0) + do { \ + if ((clear) && !(set)) \ + (val) = 0; \ + if ((set) && !(clear)) \ + (val) = 1; \ + } \ + while (0) - CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep); - CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak); - CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0); - CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1); - CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2); - CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3); - CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4); - CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5); - CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6); - CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); + CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep); + CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak); + CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0); + CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1); + CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2); + CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3); + CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4); + CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5); + CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6); + CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7); #undef CLEAR_SET } template <> void RSP::DMA() { - Mem& mem = Core::GetMem(); - u32 length = spDMALen.len + 1; + Mem &mem = Core::GetMem(); + u32 length = spDMALen.len + 1; - length = (length + 0x7) & ~0x7; + length = (length + 0x7) & ~0x7; - const auto &src = spDMASPAddr.bank ? imem : dmem; + const auto &src = spDMASPAddr.bank ? imem : dmem; - u32 mem_address = spDMASPAddr.address & 0xFF8; - u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address); + u32 mem_address = spDMASPAddr.address & 0xFF8; + u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; + trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address); - for (u32 i = 0; i < spDMALen.count + 1; i++) { - for (u32 j = 0; j < length; j++) { - mem.mmio.rdp.WriteRDRAM(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for (u32 j = 0; j < length; j++) { + mem.mmio.rdp.WriteRDRAM(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]); + } + + const int skip = i == spDMALen.count ? 0 : spDMALen.skip; + + dram_address += (length + skip); + dram_address &= 0xFFFFF8; + mem_address += length; + mem_address &= 0xFF8; } + trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); - const int skip = i == spDMALen.count ? 0 : spDMALen.skip; - - dram_address += (length + skip); - dram_address &= 0xFFFFF8; - mem_address += length; - mem_address &= 0xFF8; - } - trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); + lastSuccessfulSPAddr.address = mem_address; + lastSuccessfulSPAddr.bank = spDMASPAddr.bank; + lastSuccessfulDRAMAddr.address = dram_address; + spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); } template <> void RSP::DMA() { - Mem& mem = Core::GetMem(); - u32 length = spDMALen.len + 1; + Mem &mem = Core::GetMem(); + u32 length = spDMALen.len + 1; - length = (length + 0x7) & ~0x7; + length = (length + 0x7) & ~0x7; - auto &dst = spDMASPAddr.bank ? imem : dmem; + auto &dst = spDMASPAddr.bank ? imem : dmem; - u32 mem_address = spDMASPAddr.address & 0xFF8; - u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; - trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address); + u32 mem_address = spDMASPAddr.address & 0xFF8; - for (u32 i = 0; i < spDMALen.count + 1; i++) { - for (u32 j = 0; j < length; j++) { - dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM(BYTE_ADDRESS(dram_address + j)); + u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8; + trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address); + + for (u32 i = 0; i < spDMALen.count + 1; i++) { + for (u32 j = 0; j < length; j++) { + dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM(BYTE_ADDRESS(dram_address + j)); + } + + const int skip = i == spDMALen.count ? 0 : spDMALen.skip; + + dram_address += (length + skip); + dram_address &= 0xFFFFF8; + mem_address += length; + mem_address &= 0xFF8; } + trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); - const int skip = i == spDMALen.count ? 0 : spDMALen.skip; - - dram_address += (length + skip); - dram_address &= 0xFFFFF8; - mem_address += length; - mem_address &= 0xFF8; - } - trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address); - - lastSuccessfulSPAddr.address = mem_address; - lastSuccessfulSPAddr.bank = spDMASPAddr.bank; - lastSuccessfulDRAMAddr.address = dram_address; - spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); + lastSuccessfulSPAddr.address = mem_address; + lastSuccessfulSPAddr.bank = spDMASPAddr.bank; + lastSuccessfulDRAMAddr.address = dram_address; + spDMALen.raw = 0xFF8 | (spDMALen.skip << 20); } void RSP::Write(const u32 addr, const u32 val) { - switch (addr) { - case 0x04040000: - spDMASPAddr.raw = val & 0x1FF8; - break; - case 0x04040004: - spDMADRAMAddr.raw = val & 0xFFFFF8; - break; - case 0x04040008: - spDMALen.raw = val; - DMA(); - break; - case 0x0404000C: - spDMALen.raw = val; - DMA(); - break; - case 0x04040010: - WriteStatus(val); - break; - case 0x0404001C: - ReleaseSemaphore(); - break; - case 0x04080000: - if (spStatus.halt) { - SetPC(val); + switch (addr) { + case 0x04040000: + spDMASPAddr.raw = val & 0x1FF8; + break; + case 0x04040004: + spDMADRAMAddr.raw = val & 0xFFFFF8; + break; + case 0x04040008: + spDMALen.raw = val; + DMA(); + break; + case 0x0404000C: + spDMALen.raw = val; + DMA(); + break; + case 0x04040010: + WriteStatus(val); + break; + case 0x0404001C: + ReleaseSemaphore(); + break; + case 0x04080000: + if (spStatus.halt) { + SetPC(val); + } + break; + default: + panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val); } - break; - default: - panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val); - } } } // namespace n64 diff --git a/src/backend/core/RSP.hpp b/src/backend/core/RSP.hpp index 7849406..ba303d7 100644 --- a/src/backend/core/RSP.hpp +++ b/src/backend/core/RSP.hpp @@ -5,110 +5,111 @@ #include #include #include +#include #define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF]) #define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1)) #define SET_RSP_HALF(addr, value) \ - do { \ - RSP_BYTE(addr) = ((value) >> 8) & 0xFF; \ - RSP_BYTE((addr) + 1) = (value) & 0xFF; \ - } \ - while (0) + do { \ + RSP_BYTE(addr) = ((value) >> 8) & 0xFF; \ + RSP_BYTE((addr) + 1) = (value) & 0xFF; \ + } \ + while (0) #define GET_RSP_WORD(addr) ((GET_RSP_HALF(addr) << 16) | GET_RSP_HALF((addr) + 2)) #define SET_RSP_WORD(addr, value) \ - do { \ - SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); \ - SET_RSP_HALF((addr) + 2, (value) & 0xFFFF); \ - } \ - while (0) + do { \ + SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); \ + SET_RSP_HALF((addr) + 2, (value) & 0xFFFF); \ + } \ + while (0) namespace n64 { union SPStatus { - u32 raw; - struct { - unsigned halt : 1; - unsigned broke : 1; - unsigned dmaBusy : 1; - unsigned dmaFull : 1; - unsigned ioFull : 1; - unsigned singleStep : 1; - unsigned interruptOnBreak : 1; - unsigned signal0 : 1; - unsigned signal1 : 1; - unsigned signal2 : 1; - unsigned signal3 : 1; - unsigned signal4 : 1; - unsigned signal5 : 1; - unsigned signal6 : 1; - unsigned signal7 : 1; - unsigned : 17; - }; + u32 raw; + struct { + unsigned halt : 1; + unsigned broke : 1; + unsigned dmaBusy : 1; + unsigned dmaFull : 1; + unsigned ioFull : 1; + unsigned singleStep : 1; + unsigned interruptOnBreak : 1; + unsigned signal0 : 1; + unsigned signal1 : 1; + unsigned signal2 : 1; + unsigned signal3 : 1; + unsigned signal4 : 1; + unsigned signal5 : 1; + unsigned signal6 : 1; + unsigned signal7 : 1; + unsigned : 17; + }; }; union SPStatusWrite { - u32 raw; - struct { - unsigned clearHalt : 1; - unsigned setHalt : 1; - unsigned clearBroke : 1; - unsigned clearIntr : 1; - unsigned setIntr : 1; - unsigned clearSstep : 1; - unsigned setSstep : 1; - unsigned clearIntrOnBreak : 1; - unsigned setIntrOnBreak : 1; - unsigned clearSignal0 : 1; - unsigned setSignal0 : 1; - unsigned clearSignal1 : 1; - unsigned setSignal1 : 1; - unsigned clearSignal2 : 1; - unsigned setSignal2 : 1; - unsigned clearSignal3 : 1; - unsigned setSignal3 : 1; - unsigned clearSignal4 : 1; - unsigned setSignal4 : 1; - unsigned clearSignal5 : 1; - unsigned setSignal5 : 1; - unsigned clearSignal6 : 1; - unsigned setSignal6 : 1; - unsigned clearSignal7 : 1; - unsigned setSignal7 : 1; - unsigned : 7; - }; + u32 raw; + struct { + unsigned clearHalt : 1; + unsigned setHalt : 1; + unsigned clearBroke : 1; + unsigned clearIntr : 1; + unsigned setIntr : 1; + unsigned clearSstep : 1; + unsigned setSstep : 1; + unsigned clearIntrOnBreak : 1; + unsigned setIntrOnBreak : 1; + unsigned clearSignal0 : 1; + unsigned setSignal0 : 1; + unsigned clearSignal1 : 1; + unsigned setSignal1 : 1; + unsigned clearSignal2 : 1; + unsigned setSignal2 : 1; + unsigned clearSignal3 : 1; + unsigned setSignal3 : 1; + unsigned clearSignal4 : 1; + unsigned setSignal4 : 1; + unsigned clearSignal5 : 1; + unsigned setSignal5 : 1; + unsigned clearSignal6 : 1; + unsigned setSignal6 : 1; + unsigned clearSignal7 : 1; + unsigned setSignal7 : 1; + unsigned : 7; + }; }; union SPDMALen { - struct { - unsigned len : 12; - unsigned count : 8; - unsigned skip : 12; - }; - u32 raw; + struct { + unsigned len : 12; + unsigned count : 8; + unsigned skip : 12; + }; + u32 raw; }; union SPDMASPAddr { - struct { - unsigned address : 12; - unsigned bank : 1; - unsigned : 19; - }; - u32 raw; + struct { + unsigned address : 12; + unsigned bank : 1; + unsigned : 19; + }; + u32 raw; }; union SPDMADRAMAddr { - struct { - unsigned address : 24; - unsigned : 8; - }; - u32 raw; + struct { + unsigned address : 24; + unsigned : 8; + }; + u32 raw; }; union VPR { - s16 selement[8]; - u16 element[8]; - u8 byte[16]; - u32 word[4]; - m128i single; + s16 selement[8]; + u16 element[8]; + u8 byte[16]; + u32 word[4]; + m128i single; } __attribute__((packed)); static_assert(sizeof(VPR) == 16); @@ -119,277 +120,268 @@ struct Registers; #define DE(x) (((x) >> 11) & 0x1F) struct RSP { - bool divInLoaded = false; - bool semaphore = false; - std::array dmem{}; - std::array imem{}; - u16 oldPC{}, pc{}, nextPC{}; - s16 divIn{}, divOut{}; - u32 steps = 0; - SPStatus spStatus{}; - SPDMASPAddr spDMASPAddr{}; - SPDMADRAMAddr spDMADRAMAddr{}; - SPDMASPAddr lastSuccessfulSPAddr{}; - SPDMADRAMAddr lastSuccessfulDRAMAddr{}; - SPDMALen spDMALen{}; - s32 gpr[32]{}; - VPR vpr[32]{}; - VPR vte{}; - VPR vce{}; + bool divInLoaded = false; + bool semaphore = false; + std::array dmem{}; + std::array imem{}; + u16 oldPC{}, pc{}, nextPC{}; + s16 divIn{}, divOut{}; + u32 steps = 0; + SPStatus spStatus{}; + SPDMASPAddr spDMASPAddr{}; + SPDMADRAMAddr spDMADRAMAddr{}; + SPDMASPAddr lastSuccessfulSPAddr{}; + SPDMADRAMAddr lastSuccessfulDRAMAddr{}; + SPDMALen spDMALen{}; + s32 gpr[32]{}; + VPR vpr[32]{}; + VPR vte{}; + VPR vce{}; - struct { - VPR h{}, m{}, l{}; - } acc; + struct { + VPR h{}, m{}, l{}; + } acc; - struct { - VPR l{}, h{}; - } vcc, vco; + struct { + VPR l{}, h{}; + } vcc, vco; - RSP(); - void Reset(); + RSP(); + void Reset(); - FORCE_INLINE void Step() { - gpr[0] = 0; - const u32 instr = ircolib::ReadAccess(imem, pc & IMEM_DSIZE); - oldPC = pc & 0xFFC; - pc = nextPC & 0xFFC; - nextPC += 4; + FORCE_INLINE void Step() { + gpr[0] = 0; + const u32 instr = ircolib::ReadAccess(imem, pc & IMEM_DSIZE); + oldPC = pc & 0xFFC; + pc = nextPC & 0xFFC; + nextPC += 4; - Exec(instr); - } - - void SetVTE(const VPR &vt, u8 e); - auto Read(u32 addr) -> u32; - void Write(u32 addr, u32 val); - void Exec(Instruction instr); - - FORCE_INLINE void SetPC(const u16 val) { - oldPC = pc & 0xFFC; - pc = val & 0xFFC; - nextPC = pc + 4; - } - - [[nodiscard]] FORCE_INLINE s64 GetACC(const int e) const { - s64 val = u64(acc.h.element[e]) << 32; - val |= u64(acc.m.element[e]) << 16; - val |= u64(acc.l.element[e]) << 00; - if ((val & 0x0000800000000000) != 0) { - val |= 0xFFFF000000000000; + Exec(instr); } - return val; - } - FORCE_INLINE void SetACC(const int e, const s64 val) { - acc.h.element[e] = val >> 32; - acc.m.element[e] = val >> 16; - acc.l.element[e] = val; - } + void SetVTE(const VPR &vt, u8 e); + auto Read(u32 addr) -> u32; + void Write(u32 addr, u32 val); + void Exec(Instruction instr); - [[nodiscard]] FORCE_INLINE u16 GetVCO() const { - u16 value = 0; - for (int i = 0; i < 8; i++) { - const bool h = vco.h.element[7 - i] != 0; - const bool l = vco.l.element[7 - i] != 0; - const u32 mask = (l << i) | (h << (i + 8)); - value |= mask; + FORCE_INLINE void SetPC(const u16 val) { + oldPC = pc & 0xFFC; + pc = val & 0xFFC; + nextPC = pc + 4; } - return value; - } - [[nodiscard]] FORCE_INLINE u16 GetVCC() const { - u16 value = 0; - for (int i = 0; i < 8; i++) { - const bool h = vcc.h.element[7 - i] != 0; - const bool l = vcc.l.element[7 - i] != 0; - const u32 mask = (l << i) | (h << (i + 8)); - value |= mask; + [[nodiscard]] FORCE_INLINE s64 GetACC(const int e) const { + s64 val = u64(acc.h.element[e]) << 32; + val |= u64(acc.m.element[e]) << 16; + val |= u64(acc.l.element[e]) << 00; + if ((val & 0x0000800000000000) != 0) { + val |= 0xFFFF000000000000; + } + return val; } - return value; - } - [[nodiscard]] FORCE_INLINE u8 GetVCE() const { - u8 value = 0; - for (int i = 0; i < 8; i++) { - const bool l = vce.element[ELEMENT_INDEX(i)] != 0; - value |= (l << i); + FORCE_INLINE void SetACC(const int e, const s64 val) { + acc.h.element[e] = val >> 32; + acc.m.element[e] = val >> 16; + acc.l.element[e] = val; } - return value; - } - [[nodiscard]] FORCE_INLINE u32 ReadWord(u32 addr) const { - addr &= 0xfff; - return GET_RSP_WORD(addr); - } - - FORCE_INLINE void WriteWord(u32 addr, const u32 val) { - addr &= 0xfff; - SET_RSP_WORD(addr, val); - } - - [[nodiscard]] FORCE_INLINE u16 ReadHalf(u32 addr) const { - addr &= 0xfff; - return GET_RSP_HALF(addr); - } - - FORCE_INLINE void WriteHalf(u32 addr, const u16 val) { - addr &= 0xfff; - SET_RSP_HALF(addr, val); - } - - [[nodiscard]] FORCE_INLINE u8 ReadByte(u32 addr) const { - addr &= 0xfff; - return RSP_BYTE(addr); - } - - FORCE_INLINE void WriteByte(u32 addr, const u8 val) { - addr &= 0xfff; - RSP_BYTE(addr) = val; - } - - FORCE_INLINE bool AcquireSemaphore() { - if (semaphore) { - return true; - } else { - semaphore = true; - return false; + [[nodiscard]] FORCE_INLINE u16 GetVCO() const { + u16 value = 0; + for (int i = 0; i < 8; i++) { + const bool h = vco.h.element[7 - i] != 0; + const bool l = vco.l.element[7 - i] != 0; + const u32 mask = (l << i) | (h << (i + 8)); + value |= mask; + } + return value; } - } - FORCE_INLINE void ReleaseSemaphore() { semaphore = false; } - - void special(Instruction instr); - void regimm(Instruction instr); - void lwc2(Instruction instr); - void swc2(Instruction instr); - void cop2(Instruction instr); - void cop0(Instruction instr); - - void add(Instruction instr); - void addi(Instruction instr); - void and_(Instruction instr); - void andi(Instruction instr); - void b(Instruction instr, bool cond); - void blink(Instruction instr, bool cond); - void cfc2(Instruction instr); - void ctc2(Instruction instr); - void lb(Instruction instr); - void lh(Instruction instr); - void lw(Instruction instr); - void lbu(Instruction instr); - void lhu(Instruction instr); - void lui(Instruction instr); - void luv(Instruction instr); - void lbv(Instruction instr); - void ldv(Instruction instr); - void lsv(Instruction instr); - void llv(Instruction instr); - void lrv(Instruction instr); - void lqv(Instruction instr); - void lfv(Instruction instr); - void lhv(Instruction instr); - void ltv(Instruction instr); - void lpv(Instruction instr); - void j(Instruction instr); - void jal(Instruction instr); - void jr(Instruction instr); - void jalr(Instruction instr); - void nor(Instruction instr); - void or_(Instruction instr); - void ori(Instruction instr); - void xor_(Instruction instr); - void xori(Instruction instr); - void sb(Instruction instr); - void sh(Instruction instr); - void sw(Instruction instr); - void swv(Instruction instr); - void sub(Instruction instr); - void sbv(Instruction instr); - void sdv(Instruction instr); - void stv(Instruction instr); - void sqv(Instruction instr); - void ssv(Instruction instr); - void suv(Instruction instr); - void slv(Instruction instr); - void shv(Instruction instr); - void sfv(Instruction instr); - void srv(Instruction instr); - void spv(Instruction instr); - void sllv(Instruction instr); - void srlv(Instruction instr); - void srav(Instruction instr); - void sll(Instruction instr); - void srl(Instruction instr); - void sra(Instruction instr); - void slt(Instruction instr); - void sltu(Instruction instr); - void slti(Instruction instr); - void sltiu(Instruction instr); - void vabs(Instruction instr); - void vadd(Instruction instr); - void vaddc(Instruction instr); - void vand(Instruction instr); - void vnand(Instruction instr); - void vch(Instruction instr); - void vcr(Instruction instr); - void vcl(Instruction instr); - void vmacf(Instruction instr); - void vmacu(Instruction instr); - void vmacq(Instruction instr); - void vmadh(Instruction instr); - void vmadl(Instruction instr); - void vmadm(Instruction instr); - void vmadn(Instruction instr); - void vmov(Instruction instr); - void vmulf(Instruction instr); - void vmulu(Instruction instr); - void vmulq(Instruction instr); - void vmudl(Instruction instr); - void vmudh(Instruction instr); - void vmudm(Instruction instr); - void vmudn(Instruction instr); - void vmrg(Instruction instr); - void vlt(Instruction instr); - void veq(Instruction instr); - void vne(Instruction instr); - void vge(Instruction instr); - void vrcp(Instruction instr); - void vrsq(Instruction instr); - void vrcpl(Instruction instr); - void vrsql(Instruction instr); - void vrndp(Instruction instr); - void vrndn(Instruction instr); - void vrcph(Instruction instr); - void vsar(Instruction instr); - void vsub(Instruction instr); - void vsubc(Instruction instr); - void vxor(Instruction instr); - void vnxor(Instruction instr); - void vor(Instruction instr); - void vnor(Instruction instr); - void vzero(Instruction instr); - void mfc0(const RDP &rdp, Instruction instr); - void mtc0(Instruction instr) const; - void mfc2(Instruction instr); - void mtc2(Instruction instr); - - template - void DMA(); - void WriteStatus(u32 value); - -private: - FORCE_INLINE void branch(const u16 address, const bool cond) { - if (cond) { - nextPC = address & 0xFFC; + [[nodiscard]] FORCE_INLINE u16 GetVCC() const { + u16 value = 0; + for (int i = 0; i < 8; i++) { + const bool h = vcc.h.element[7 - i] != 0; + const bool l = vcc.l.element[7 - i] != 0; + const u32 mask = (l << i) | (h << (i + 8)); + value |= mask; + } + return value; } - } - FORCE_INLINE void branch_likely(const u16 address, const bool cond) { - if (cond) { - nextPC = address & 0xFFC; - } else { - pc = nextPC & 0xFFC; - nextPC = pc + 4; + [[nodiscard]] FORCE_INLINE u8 GetVCE() const { + u8 value = 0; + for (int i = 0; i < 8; i++) { + const bool l = vce.element[ELEMENT_INDEX(i)] != 0; + value |= (l << i); + } + return value; + } + + [[nodiscard]] FORCE_INLINE u32 ReadWord(u32 addr) const { + addr &= 0xfff; + return GET_RSP_WORD(addr); + } + + FORCE_INLINE void WriteWord(u32 addr, const u32 val) { + addr &= 0xfff; + SET_RSP_WORD(addr, val); + } + + [[nodiscard]] FORCE_INLINE u16 ReadHalf(u32 addr) const { + addr &= 0xfff; + return GET_RSP_HALF(addr); + } + + FORCE_INLINE void WriteHalf(u32 addr, const u16 val) { + addr &= 0xfff; + SET_RSP_HALF(addr, val); + } + + [[nodiscard]] FORCE_INLINE u8 ReadByte(u32 addr) const { + addr &= 0xfff; + return RSP_BYTE(addr); + } + + FORCE_INLINE void WriteByte(u32 addr, const u8 val) { + addr &= 0xfff; + RSP_BYTE(addr) = val; + } + + FORCE_INLINE bool AcquireSemaphore() { + if (semaphore) { + return true; + } else { + semaphore = true; + return false; + } + } + + FORCE_INLINE void ReleaseSemaphore() { semaphore = false; } + + void special(Instruction instr); + void regimm(Instruction instr); + void lwc2(Instruction instr); + void swc2(Instruction instr); + void cop2(Instruction instr); + void cop0(Instruction instr); + + void add(Instruction instr); + void addi(Instruction instr); + void and_(Instruction instr); + void andi(Instruction instr); + void b(Instruction instr, bool cond); + void blink(Instruction instr, bool cond); + void cfc2(Instruction instr); + void ctc2(Instruction instr); + void lb(Instruction instr); + void lh(Instruction instr); + void lw(Instruction instr); + void lbu(Instruction instr); + void lhu(Instruction instr); + void lui(Instruction instr); + void luv(Instruction instr); + void lbv(Instruction instr); + void ldv(Instruction instr); + void lsv(Instruction instr); + void llv(Instruction instr); + void lrv(Instruction instr); + void lqv(Instruction instr); + void lfv(Instruction instr); + void lhv(Instruction instr); + void ltv(Instruction instr); + void lpv(Instruction instr); + void j(Instruction instr); + void jal(Instruction instr); + void jr(Instruction instr); + void jalr(Instruction instr); + void nor(Instruction instr); + void or_(Instruction instr); + void ori(Instruction instr); + void xor_(Instruction instr); + void xori(Instruction instr); + void sb(Instruction instr); + void sh(Instruction instr); + void sw(Instruction instr); + void swv(Instruction instr); + void sub(Instruction instr); + void sbv(Instruction instr); + void sdv(Instruction instr); + void stv(Instruction instr); + void sqv(Instruction instr); + void ssv(Instruction instr); + void suv(Instruction instr); + void slv(Instruction instr); + void shv(Instruction instr); + void sfv(Instruction instr); + void srv(Instruction instr); + void spv(Instruction instr); + void sllv(Instruction instr); + void srlv(Instruction instr); + void srav(Instruction instr); + void sll(Instruction instr); + void srl(Instruction instr); + void sra(Instruction instr); + void slt(Instruction instr); + void sltu(Instruction instr); + void slti(Instruction instr); + void sltiu(Instruction instr); + void vabs(Instruction instr); + void vadd(Instruction instr); + void vaddc(Instruction instr); + void vand(Instruction instr); + void vnand(Instruction instr); + void vch(Instruction instr); + void vcr(Instruction instr); + void vcl(Instruction instr); + void vmacf(Instruction instr); + void vmacu(Instruction instr); + void vmacq(Instruction instr); + void vmadh(Instruction instr); + void vmadl(Instruction instr); + void vmadm(Instruction instr); + void vmadn(Instruction instr); + void vmov(Instruction instr); + void vmulf(Instruction instr); + void vmulu(Instruction instr); + void vmulq(Instruction instr); + void vmudl(Instruction instr); + void vmudh(Instruction instr); + void vmudm(Instruction instr); + void vmudn(Instruction instr); + void vmrg(Instruction instr); + void vlt(Instruction instr); + void veq(Instruction instr); + void vne(Instruction instr); + void vge(Instruction instr); + void vrcp(Instruction instr); + void vrsq(Instruction instr); + void vrcpl(Instruction instr); + void vrsql(Instruction instr); + void vrndp(Instruction instr); + void vrndn(Instruction instr); + void vrcph(Instruction instr); + void vsar(Instruction instr); + void vsub(Instruction instr); + void vsubc(Instruction instr); + void vxor(Instruction instr); + void vnxor(Instruction instr); + void vor(Instruction instr); + void vnor(Instruction instr); + void vzero(Instruction instr); + void mfc0(const RDP &rdp, Instruction instr); + void mtc0(Instruction instr) const; + void mfc2(Instruction instr); + void mtc2(Instruction instr); + + template + void DMA(); + void WriteStatus(u32 value); + + private: + FORCE_INLINE void branch(const u16 address, const bool cond) { + if (cond) { + nextPC = address & 0xFFC; + } } - } }; } // namespace n64 diff --git a/src/backend/core/interpreter/cop0instructions.cpp b/src/backend/core/interpreter/cop0instructions.cpp index 484316f..5c2f5ce 100644 --- a/src/backend/core/interpreter/cop0instructions.cpp +++ b/src/backend/core/interpreter/cop0instructions.cpp @@ -4,90 +4,90 @@ namespace n64 { void Cop0::mtc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - SetReg32(instr.rd(), regs.Read(instr.rt())); + Registers ®s = Core::GetRegs(); + SetReg32(instr.rd(), regs.Read(instr.rt())); } -void Cop0::dmtc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - SetReg64(instr.rd(), regs.Read(instr.rt())); +void Cop0::dmtc0(const Instruction instr) { + Registers ®s = Core::GetRegs(); + SetReg64(instr.rd(), regs.Read(instr.rt())); } -void Cop0::mfc0(const Instruction instr) { - Registers& regs = Core::GetRegs(); - regs.Write(instr.rt(), s32(GetReg32(instr.rd()))); +void Cop0::mfc0(const Instruction instr) { + Registers ®s = Core::GetRegs(); + regs.Write(instr.rt(), s32(GetReg32(instr.rd()))); } -void Cop0::dmfc0(const Instruction instr) const { - Registers& regs = Core::GetRegs(); - regs.Write(instr.rt(), s64(GetReg64(instr.rd()))); +void Cop0::dmfc0(const Instruction instr) const { + Registers ®s = Core::GetRegs(); + regs.Write(instr.rt(), s64(GetReg64(instr.rd()))); } void Cop0::eret() { - Registers& regs = Core::GetRegs(); - if (!regs.cop0.kernelMode) { - FireException(ExceptionCode::CoprocessorUnusable, 0, regs.oldPC); - return; - } - if (status.erl) { - regs.SetPC64(ErrorEPC); - status.erl = false; - } else { - regs.SetPC64(EPC); - status.exl = false; - } - regs.cop0.Update(); - llbit = false; + Registers ®s = Core::GetRegs(); + if (!regs.cop0.kernelMode) { + FireException(Cop0::ExceptionCode::CoprocessorUnusable, 0, regs.oldPC); + return; + } + if (status.erl) { + regs.SetPC64(ErrorEPC); + status.erl = false; + } else { + regs.SetPC64(EPC); + status.exl = false; + } + regs.cop0.Update(); + llbit = false; } void Cop0::tlbr() { - if (index.i >= 32) { - panic("TLBR with TLB index {}", index.i); - } + if (index.i >= 32) { + panic("TLBR with TLB index {}", index.i); + } - const TLBEntry entry = tlb[index.i]; + const TLBEntry entry = tlb[index.i]; - entryHi.raw = entry.entryHi.raw; - entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF; - entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF; + entryHi.raw = entry.entryHi.raw; + entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF; + entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF; - entryLo0.g = entry.global; - entryLo1.g = entry.global; - pageMask.raw = entry.pageMask.raw; + entryLo0.g = entry.global; + entryLo1.g = entry.global; + pageMask.raw = entry.pageMask.raw; } void Cop0::tlbw(const int index_) { - PageMask page_mask{}; - page_mask = pageMask; - const u32 top = page_mask.mask & 0xAAA; - page_mask.mask = top | (top >> 1); + PageMask page_mask{}; + page_mask = pageMask; + const u32 top = page_mask.mask & 0xAAA; + page_mask.mask = top | (top >> 1); - if (index_ >= 32) { - panic("TLBWI with TLB index {}", index_); - } + if (index_ >= 32) { + panic("TLBWI with TLB index {}", index_); + } - tlb[index_].entryHi.raw = entryHi.raw; - tlb[index_].entryHi.vpn2 &= ~page_mask.mask; + tlb[index_].entryHi.raw = entryHi.raw; + tlb[index_].entryHi.vpn2 &= ~page_mask.mask; - tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE; - tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE; - tlb[index_].pageMask.raw = page_mask.raw; + tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE; + tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE; + tlb[index_].pageMask.raw = page_mask.raw; - tlb[index_].global = entryLo0.g && entryLo1.g; - tlb[index_].initialized = true; + tlb[index_].global = entryLo0.g && entryLo1.g; + tlb[index_].initialized = true; } void Cop0::tlbp() { - int match = -1; - const TLBEntry *entry = TLBTryMatch(entryHi.raw, match); - if (match >= 0) { - index.raw = match; - return; - } + int match = -1; + const TLBEntry *entry = TLBTryMatch(entryHi.raw, match); + if (match >= 0) { + index.raw = match; + return; + } - index.raw = 0; - index.p = 1; + index.raw = 0; + index.p = 1; } } // namespace n64 diff --git a/src/backend/core/interpreter/cop1instructions.cpp b/src/backend/core/interpreter/cop1instructions.cpp index 530f2ed..5e7aac9 100644 --- a/src/backend/core/interpreter/cop1instructions.cpp +++ b/src/backend/core/interpreter/cop1instructions.cpp @@ -5,377 +5,378 @@ namespace n64 { template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> s32 & { - if (status.fr) { - return fgr[index].int32; - } +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> s32 & { + if (status.fr) { + return fgr[index].int32; + } - if (index & 1) { - return fgr[index & ~1].int32h; - } + if (index & 1) { + return fgr[index & ~1].int32h; + } - return fgr[index & ~1].int32; + return fgr[index & ~1].int32; } template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_T(status, index)); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_T(status, index)); } template <> -auto Cop1::FGR_T(const Cop0Status &, const u32 index) -> float & { - return fgr[index].float32; -} - -template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> s64 & { - if (status.fr) { - return fgr[index].int64; - } - - return fgr[index & ~1].int64; -} - -template <> -auto Cop1::FGR_T(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_T(status, index)); -} - -template <> -auto Cop1::FGR_T(const Cop0Status &, const u32 index) -> double & { - return fgr[index].float64; -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> s32 & { - if (status.fr) { - return fgr[index].int32; - } - - return fgr[index & ~1].int32; -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_S(status, index)); -} - -template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> float & { - if (status.fr) { +auto Cop1::FGR_T(const Cop0::Status &, const u32 index) -> float & { return fgr[index].float32; - } - - return fgr[index & ~1].float32; } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> s64 & { - return FGR_T(status, index); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> s64 & { + if (status.fr) { + return fgr[index].int64; + } + + return fgr[index & ~1].int64; } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_S(status, index)); +auto Cop1::FGR_T(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_T(status, index)); } template <> -auto Cop1::FGR_S(const Cop0Status &status, const u32 index) -> double & { - if (status.fr) { +auto Cop1::FGR_T(const Cop0::Status &, const u32 index) -> double & { return fgr[index].float64; - } - - return fgr[index & ~1].float64; } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> s32 & { - fgr[index].int32h = 0; - return fgr[index].int32; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> s32 & { + if (status.fr) { + return fgr[index].int32; + } + + return fgr[index & ~1].int32; } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> u32 & { - return reinterpret_cast(FGR_D(status, index)); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_S(status, index)); } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> float & { - fgr[index].float32h = 0; - return fgr[index].float32; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> float & { + if (status.fr) { + return fgr[index].float32; + } + + return fgr[index & ~1].float32; } template <> -auto Cop1::FGR_D(const Cop0Status &, const u32 index) -> s64 & { - return fgr[index].int64; +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> s64 & { + return FGR_T(status, index); } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> u64 & { - return reinterpret_cast(FGR_D(status, index)); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_S(status, index)); } template <> -auto Cop1::FGR_D(const Cop0Status &status, const u32 index) -> double & { - return FGR_T(status, index); +auto Cop1::FGR_S(const Cop0::Status &status, const u32 index) -> double & { + if (status.fr) { + return fgr[index].float64; + } + + return fgr[index & ~1].float64; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> s32 & { + fgr[index].int32h = 0; + return fgr[index].int32; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> u32 & { + return reinterpret_cast(FGR_D(status, index)); +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> float & { + fgr[index].float32h = 0; + return fgr[index].float32; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &, const u32 index) -> s64 & { + return fgr[index].int64; +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> u64 & { + return reinterpret_cast(FGR_D(status, index)); +} + +template <> +auto Cop1::FGR_D(const Cop0::Status &status, const u32 index) -> double & { + return FGR_T(status, index); } template <> bool Cop1::isqnan(const float f) { - return std::bit_cast(f) >> 22 & 1; + return std::bit_cast(f) >> 22 & 1; } template <> bool Cop1::isqnan(const double f) { - return std::bit_cast(f) >> 51 & 1; + return std::bit_cast(f) >> 51 & 1; } template <> bool Cop1::CheckCVTArg(const float f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if (f >= 0x1p+31f || f < -0x1p+31f) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if (f >= 0x1p+31f || f < -0x1p+31f) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const double f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+31 || f < -0x1p+31)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+31 || f < -0x1p+31)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const float f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+53f || f <= -0x1p+53f)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+53f || f <= -0x1p+53f)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template <> bool Cop1::CheckCVTArg(const double f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - case FP_INFINITE: - case FP_NAN: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + case FP_INFINITE: + case FP_NAN: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - if ((f >= 0x1p+53 || f <= -0x1p+53)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } + if ((f >= 0x1p+53 || f <= -0x1p+53)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } - return true; + return true; } template bool Cop1::CheckArg(const T f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - case FP_NAN: - if (isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + case FP_NAN: + if (isqnan(f) ? SetCauseInvalid() : (SetCauseUnimplemented(), true)) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + return true; } return true; - } - return true; } template bool Cop1::CheckArgs(const T f1, const T f2) { - Registers& regs = Core::GetRegs(); - auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2); - if ((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - - if (class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - - if ((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) { - if (SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + auto class1 = std::fpclassify(f1), class2 = std::fpclassify(f2); + if ((class1 == FP_NAN && !isqnan(f1)) || (class2 == FP_NAN && !isqnan(f2))) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; } - } - return true; + if (class1 == FP_SUBNORMAL || class2 == FP_SUBNORMAL) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + + if ((class1 == FP_NAN && isqnan(f1)) || (class2 == FP_NAN && isqnan(f2))) { + if (SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + } + + return true; } -template<> +template <> bool Cop1::CheckFPUUsable() { - Registers& regs = Core::GetRegs(); - if (!regs.cop0.status.cu1) { - regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); - return false; - } + Registers ®s = Core::GetRegs(); + if (!regs.cop0.status.cu1) { + regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 1, regs.oldPC); + return false; + } - return true; + return true; } -template<> +template <> bool Cop1::CheckFPUUsable() { - if (!CheckFPUUsable()) return false; - fcr31.cause = {}; - return true; + if (!CheckFPUUsable()) + return false; + fcr31.cause = {}; + return true; } template FORCE_INLINE T FlushResult(T f, const u32 round) { - switch (round) { - case FE_TONEAREST: - case FE_TOWARDZERO: - return std::copysign(T(), f); - case FE_UPWARD: - return std::signbit(f) ? -T() : std::numeric_limits::min(); - case FE_DOWNWARD: - return std::signbit(f) ? -std::numeric_limits::min() : T(); - default: - __builtin_unreachable(); - } + switch (round) { + case FE_TONEAREST: + case FE_TOWARDZERO: + return std::copysign(T(), f); + case FE_UPWARD: + return std::signbit(f) ? -T() : std::numeric_limits::min(); + case FE_DOWNWARD: + return std::signbit(f) ? -std::numeric_limits::min() : T(); + default: + __builtin_unreachable(); + } } template <> bool Cop1::CheckResult(float &f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, std::fegetround()); + return true; + case FP_NAN: + { + f = std::bit_cast(0x7fbf'ffff); + return true; + } } - SetCauseUnderflow(); - SetCauseInexact(); - f = FlushResult(f, std::fegetround()); return true; - case FP_NAN: - { - f = std::bit_cast(0x7fbf'ffff); - return true; - } - } - return true; } template <> bool Cop1::CheckResult(double &f) { - Registers& regs = Core::GetRegs(); - switch (std::fpclassify(f)) { - case FP_SUBNORMAL: - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + switch (std::fpclassify(f)) { + case FP_SUBNORMAL: + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + SetCauseUnderflow(); + SetCauseInexact(); + f = FlushResult(f, fegetround()); + return true; + case FP_NAN: + { + f = std::bit_cast(0x7ff7'ffff'ffff'ffff); + return true; + } } - SetCauseUnderflow(); - SetCauseInexact(); - f = FlushResult(f, fegetround()); return true; - case FP_NAN: - { - f = std::bit_cast(0x7ff7'ffff'ffff'ffff); - return true; - } - } - return true; } template bool Cop1::TestExceptions() { - Registers& regs = Core::GetRegs(); - const u32 exc = std::fetestexcept(FE_ALL_EXCEPT); + Registers ®s = Core::GetRegs(); + const u32 exc = std::fetestexcept(FE_ALL_EXCEPT); - if (!exc) - return false; + if (!exc) + return false; - if constexpr (cvt) { - if (exc & FE_INVALID) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return true; + if constexpr (cvt) { + if (exc & FE_INVALID) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } } - } - if (exc & FE_UNDERFLOW) { - if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return true; + if (exc & FE_UNDERFLOW) { + if (!fcr31.fs || fcr31.enable.underflow || fcr31.enable.inexact_operation) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return true; + } } - } - bool raise = false; - if (exc & FE_DIVBYZERO) - raise |= SetCauseDivisionByZero(); - if (exc & FE_INEXACT) { - raise |= SetCauseInexact(); - } - if (exc & FE_UNDERFLOW) - raise |= SetCauseUnderflow(); - if (exc & FE_OVERFLOW) - raise |= SetCauseOverflow(); - if (exc & FE_INVALID) - raise |= SetCauseInvalid(); - if (raise) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return raise; + bool raise = false; + if (exc & FE_DIVBYZERO) + raise |= SetCauseDivisionByZero(); + if (exc & FE_INEXACT) { + raise |= SetCauseInexact(); + } + if (exc & FE_UNDERFLOW) + raise |= SetCauseUnderflow(); + if (exc & FE_OVERFLOW) + raise |= SetCauseOverflow(); + if (exc & FE_INVALID) + raise |= SetCauseInvalid(); + if (raise) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return raise; } template bool Cop1::TestExceptions(); @@ -384,58 +385,58 @@ template bool Cop1::TestExceptions(); void Cop1::SetCauseUnimplemented() { fcr31.cause.unimplemented_operation = true; } bool Cop1::SetCauseUnderflow() { - fcr31.cause.underflow = true; - if (fcr31.enable.underflow) - return true; - fcr31.flag.underflow = true; - return false; + fcr31.cause.underflow = true; + if (fcr31.enable.underflow) + return true; + fcr31.flag.underflow = true; + return false; } bool Cop1::SetCauseInexact() { - fcr31.cause.inexact_operation = true; - if (fcr31.enable.inexact_operation) - return true; - fcr31.flag.inexact_operation = true; - return false; + fcr31.cause.inexact_operation = true; + if (fcr31.enable.inexact_operation) + return true; + fcr31.flag.inexact_operation = true; + return false; } bool Cop1::SetCauseDivisionByZero() { - fcr31.cause.division_by_zero = true; - if (fcr31.enable.division_by_zero) - return true; - fcr31.flag.division_by_zero = true; - return false; + fcr31.cause.division_by_zero = true; + if (fcr31.enable.division_by_zero) + return true; + fcr31.flag.division_by_zero = true; + return false; } bool Cop1::SetCauseOverflow() { - fcr31.cause.overflow = true; - if (fcr31.enable.overflow) - return true; - fcr31.flag.overflow = true; - return false; + fcr31.cause.overflow = true; + if (fcr31.enable.overflow) + return true; + fcr31.flag.overflow = true; + return false; } bool Cop1::SetCauseInvalid() { - fcr31.cause.invalid_operation = true; - if (fcr31.enable.invalid_operation) - return true; - fcr31.flag.invalid_operation = true; - return false; + fcr31.cause.invalid_operation = true; + if (fcr31.enable.invalid_operation) + return true; + fcr31.flag.invalid_operation = true; + return false; } #define CHECK_FPE_IMPL(type, res, operation, convert) \ - feclearexcept(FE_ALL_EXCEPT); \ - volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ - if (TestExceptions()) \ - return; \ - type res = v##res; + feclearexcept(FE_ALL_EXCEPT); \ + volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ + if (TestExceptions()) \ + return; \ + type res = v##res; #define CHECK_FPE_IMPL_CONST(type, res, operation, convert) \ - feclearexcept(FE_ALL_EXCEPT); \ - volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ - if (TestExceptions()) \ - return; \ - const type res = v##res; + feclearexcept(FE_ALL_EXCEPT); \ + volatile type v##res = [&]() __attribute__((noinline)) -> type { return operation; }(); \ + if (TestExceptions()) \ + return; \ + const type res = v##res; #define CHECK_FPE(type, res, operation) CHECK_FPE_IMPL(type, res, operation, false) #define CHECK_FPE_CONST(type, res, operation) CHECK_FPE_IMPL_CONST(type, res, operation, false) @@ -443,312 +444,313 @@ bool Cop1::SetCauseInvalid() { #define CHECK_FPE_CONV_CONST(type, res, operation) CHECK_FPE_IMPL_CONST(type, res, operation, true) void Cop1::absd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - auto fd = std::abs(fs); - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + auto fd = std::abs(fs); + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::abss(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - auto fd = std::abs(fs); - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + auto fd = std::abs(fs); + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::adds(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs + ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs + ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::addd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs + ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs + ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceills(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)); - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)); + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::ceilwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCeil(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const u8 fd = instr.rd(); - s32 val = 0; - switch (fd) { - case 0: - val = fcr0; - break; - case 31: - val = fcr31.read(); - break; - default: - panic("Undefined CFC1 with rd != 0 or 31"); - } - regs.Write(instr.rt(), val); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const u8 fd = instr.rd(); + s32 val = 0; + switch (fd) { + case 0: + val = fcr0; + break; + case 31: + val = fcr31.read(); + break; + default: + panic("Undefined CFC1 with rd != 0 or 31"); + } + regs.Write(instr.rt(), val); } void Cop1::ctc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const u8 fs = instr.rd(); - const u32 val = regs.Read(instr.rt()); - switch (fs) { - case 0: - break; - case 31: - { - const u32 prevRound = fcr31.rounding_mode; - fcr31.write(val); - if (prevRound != fcr31.rounding_mode) { - switch (fcr31.rounding_mode) { - case 0: - fesetround(FE_TONEAREST); - break; - case 1: - fesetround(FE_TOWARDZERO); - break; - case 2: - fesetround(FE_UPWARD); - break; - case 3: - fesetround(FE_DOWNWARD); - break; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const u8 fs = instr.rd(); + const u32 val = regs.Read(instr.rt()); + switch (fs) { + case 0: + break; + case 31: + { + const u32 prevRound = fcr31.rounding_mode; + fcr31.write(val); + if (prevRound != fcr31.rounding_mode) { + switch (fcr31.rounding_mode) { + case 0: + fesetround(FE_TONEAREST); + break; + case 1: + fesetround(FE_TOWARDZERO); + break; + case 2: + fesetround(FE_UPWARD); + break; + case 3: + fesetround(FE_DOWNWARD); + break; + } + } + if (fcr31.cause.inexact_operation && fcr31.enable.inexact_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.underflow && fcr31.enable.underflow) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.overflow && fcr31.enable.overflow) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.division_by_zero && fcr31.enable.division_by_zero) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.invalid_operation && fcr31.enable.invalid_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + if (fcr31.cause.unimplemented_operation) + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); } - } - if (fcr31.cause.inexact_operation && fcr31.enable.inexact_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.underflow && fcr31.enable.underflow) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.overflow && fcr31.enable.overflow) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.division_by_zero && fcr31.enable.division_by_zero) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.invalid_operation && fcr31.enable.invalid_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - if (fcr31.cause.unimplemented_operation) - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + break; + default: + panic("Undefined CTC1 with rd != 0 or 31"); } - break; - default: - panic("Undefined CTC1 with rd != 0 or 31"); - } } void Cop1::cvtds(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, static_cast(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, static_cast(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsw(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - CHECK_FPE(float, fd, fs) - if (!CheckResult(fd)) return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + CHECK_FPE(float, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtsl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - CHECK_FPE(float, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + CHECK_FPE(float, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtdw(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtdl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - CHECK_FPE(double, fd, fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + if (fs >= 0x0080000000000000 || fs < static_cast(0xff80000000000000)) { + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + CHECK_FPE(double, fd, fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::cvtld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundCurrent(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } template bool Cop1::XORDERED(const T fs, const T ft) { - Registers& regs = Core::GetRegs(); - if (std::isnan(fs) || std::isnan(ft)) { - if (std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; + Registers ®s = Core::GetRegs(); + if (std::isnan(fs) || std::isnan(ft)) { + if (std::isnan(fs) && (!quiet || isqnan(fs)) && SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + if (std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return false; + } + fcr31.compare = cf; + return false; } - if (std::isnan(ft) && (!quiet || isqnan(ft)) && SetCauseInvalid()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return false; - } - fcr31.compare = cf; - return false; - } - return true; + return true; } #define ORDERED(type, cf) XORDERED @@ -756,191 +758,191 @@ bool Cop1::XORDERED(const T fs, const T ft) { template void Cop1::cf(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = 0; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cun(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::ceq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::cueq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::colt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cult(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cole(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cule(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!UNORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!UNORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::csf(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cngle(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = 0; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + if (const T ft = FGR_T(regs.cop0.status, instr.ft()); !ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = 0; } template void Cop1::cseq(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::cngl(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs == ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs == ft; } template void Cop1::clt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cnge(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs < ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs < ft; } template void Cop1::cle(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 0)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 0)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cngt(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const T fs = FGR_S(regs.cop0.status, instr.fs()); - const T ft = FGR_T(regs.cop0.status, instr.ft()); - if (!ORDERED(T, 1)(fs, ft)) - return; - fcr31.compare = fs <= ft; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const T fs = FGR_S(regs.cop0.status, instr.fs()); + const T ft = FGR_T(regs.cop0.status, instr.ft()); + if (!ORDERED(T, 1)(fs, ft)) + return; + fcr31.compare = fs <= ft; } template void Cop1::cf(Instruction instr); @@ -977,401 +979,403 @@ template void Cop1::cle(Instruction instr); template void Cop1::cngt(Instruction instr); void Cop1::divs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs / ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs / ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::divd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs / ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs / ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::muls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs *ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs *ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::muld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs *ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs *ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::subs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(float, fd, fs - ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(float, fd, fs - ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::subd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - const auto ft = FGR_T(regs.cop0.status, instr.ft()); - if (!CheckArgs(fs, ft)) - return; - CHECK_FPE(double, fd, fs - ft) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + const auto ft = FGR_T(regs.cop0.status, instr.ft()); + if (!CheckArgs(fs, ft)) + return; + CHECK_FPE(double, fd, fs - ft) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::movs(const Instruction instr) { movd(instr); } void Cop1::movd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_D(regs.cop0.status, instr.fd()) = FGR_S(regs.cop0.status, instr.fs()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_D(regs.cop0.status, instr.fd()) = FGR_S(regs.cop0.status, instr.fs()); } void Cop1::negs(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, -fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, -fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::negd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, -fs) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, -fs) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::sqrts(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(float, fd, sqrtf(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(float, fd, sqrtf(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::sqrtd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckArg(fs)) - return; - CHECK_FPE(double, fd, sqrt(fs)) - if (!CheckResult(fd)) - return; - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckArg(fs)) + return; + CHECK_FPE(double, fd, sqrt(fs)) + if (!CheckResult(fd)) + return; + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::roundwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) - if (fd != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundNearest(fs)) + if (fd != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::floorwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundFloor(fs)) + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncws(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncwd(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONV_CONST(s32, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncls(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::truncld(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - const auto fs = FGR_S(regs.cop0.status, instr.fs()); - if (!CheckCVTArg(fs)) - return; - CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) - if (static_cast(fd) != fs && SetCauseInexact()) { - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); - return; - } - FGR_D(regs.cop0.status, instr.fd()) = fd; + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + const auto fs = FGR_S(regs.cop0.status, instr.fs()); + if (!CheckCVTArg(fs)) + return; + CHECK_FPE_CONST(s64, fd, ircolib::roundTrunc(fs)) + if (static_cast(fd) != fs && SetCauseInexact()) { + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); + return; + } + FGR_D(regs.cop0.status, instr.fd()) = fd; } void Cop1::lwc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u32 data = mem.Read(physical); - FGR_T(regs.cop0.status, instr.ft()) = data; - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u32 data = mem.Read(physical); + FGR_T(regs.cop0.status, instr.ft()) = data; + } } void Cop1::swc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(addr); + mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); + } } void Cop1::ldc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - } else { - const u64 data = mem.Read(physical); - FGR_T(regs.cop0.status, instr.ft()) = data; - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + } else { + const u64 data = mem.Read(physical); + FGR_T(regs.cop0.status, instr.ft()) = data; + } } void Cop1::sdc1(const Instruction instr) { - Mem& mem = Core::GetMem(); - Registers& regs = Core::GetRegs(); - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + Mem &mem = Core::GetMem(); + Registers ®s = Core::GetRegs(); + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { - regs.cop0.HandleTLBException(addr); - regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); - } else { - mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); - } + if (u32 physical; !regs.cop0.MapVAddr(Cop0::STORE, addr, physical)) { + regs.cop0.HandleTLBException(addr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC); + } else { + Core::GetInstance().interpreter.cachedState.EvictCachedBlock(addr); + mem.Write(physical, FGR_T(regs.cop0.status, instr.ft())); + } } void Cop1::unimplemented() { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - SetCauseUnimplemented(); - regs.cop0.FireException(ExceptionCode::FloatingPointError, 0, regs.oldPC); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + SetCauseUnimplemented(); + regs.cop0.FireException(Cop0::ExceptionCode::FloatingPointError, 0, regs.oldPC); } void Cop1::mfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - regs.Write(instr.rt(), FGR_T(regs.cop0.status, instr.fs())); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + regs.Write(instr.rt(), FGR_T(regs.cop0.status, instr.fs())); } void Cop1::dmfc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - regs.Write(instr.rt(), FGR_S(regs.cop0.status, instr.fs())); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + regs.Write(instr.rt(), FGR_S(regs.cop0.status, instr.fs())); } void Cop1::mtc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_T(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_T(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); } void Cop1::dmtc1(const Instruction instr) { - Registers& regs = Core::GetRegs(); - if (!CheckFPUUsable()) - return; - FGR_S(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); + Registers ®s = Core::GetRegs(); + if (!CheckFPUUsable()) + return; + FGR_S(regs.cop0.status, instr.fs()) = regs.Read(instr.rt()); } } // namespace n64 diff --git a/src/backend/core/interpreter/decode.cpp b/src/backend/core/interpreter/decode.cpp index 1bfe6c0..ee5765b 100644 --- a/src/backend/core/interpreter/decode.cpp +++ b/src/backend/core/interpreter/decode.cpp @@ -4,452 +4,454 @@ 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(Cop0::ExceptionCode::Syscall, 0, regs.oldPC); + break; + case Instruction::BREAK: + regs.cop0.FireException(Cop0::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(Cop0::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(Cop0::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()) +void Interpreter::DecodeExecute(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()) + 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(Cop0::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: + // cache(instr); + 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..4259029 100644 --- a/src/backend/core/interpreter/instructions.cpp +++ b/src/backend/core/interpreter/instructions.cpp @@ -1,819 +1,835 @@ #include +#include #define check_signed_overflow(op1, op2, res) (((~((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) #define check_signed_underflow(op1, op2, res) (((((op1) ^ (op2)) & ((op1) ^ (res))) >> ((sizeof(res) * 8) - 1)) & 1) 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(Cop0::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(Cop0::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(Cop0::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(Cop0::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) { + if (!cond) { + regs.SetPC64(regs.nextPC); + return; + } + 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 (Core::IsAddressError(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 (Core::IsAddressError(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 (Core::IsAddressError(0b11, address)) { + regs.cop0.FireException(Cop0::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 (Core::IsAddressError(0b111, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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(Cop0::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 (Core::IsAddressError(0b111, address)) { + regs.cop0.FireException(Cop0::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 (Core::IsAddressError(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 (Core::IsAddressError(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 { + cachedState.EvictCachedBlock(address); + 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 (Core::IsAddressError(0b11, address)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 { + cachedState.EvictCachedBlock(address); + 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(Cop0::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 (Core::IsAddressError(0b111, address)) { + regs.Write(instr.rt(), 0); + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 { + cachedState.EvictCachedBlock(address); + 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 { + cachedState.EvictCachedBlock(address); + 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 (Core::IsAddressError(0b11, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 { + cachedState.EvictCachedBlock(address); + 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 (Core::IsAddressError(0b111, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::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 { + cachedState.EvictCachedBlock(address); + 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()); + cachedState.EvictCachedBlock(address); + 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()); + cachedState.EvictCachedBlock(address); + 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()); + cachedState.EvictCachedBlock(address); + 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()); + cachedState.EvictCachedBlock(address); + 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(Cop0::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(Cop0::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 +841,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(Cop0::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 +861,87 @@ 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; + const u8 op = (instr.op() >> 2) & 7; + u64 vaddr = regs.Read(instr.rs()) + (s16)instr.offset(); + u32 paddr; + if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { + regs.cop0.HandleTLBException(vaddr); + regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + return; + } + + u32 ptag = GetPhysicalAddressPTag(paddr); + + if (type > 1) + panic("Unknown cache type {}", type); + + if (type == 0) + return CacheTypeInstruction(op, vaddr, paddr, ptag); + + return CacheTypeData(op, vaddr, paddr, ptag); +} + +void Interpreter::CacheTypeInstruction(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { + switch (op) { + case 0: + icache.InvalidateIndex(vaddr); + break; + case 1: + icache.LoadTag(vaddr); + break; + case 2: + icache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0); + break; + case 4: + icache.InvalidateIndex(vaddr, ptag); + break; + case 5: + icache.FillLine(vaddr, paddr); + break; + case 6: + icache.WriteBack(vaddr, paddr, ptag); + break; + default: + panic("Unimplemented icache op 0b{:03b}", op); + } +} + +void Interpreter::CacheTypeData(const u8 op, const u64 vaddr, const u32 paddr, const u32 ptag) { + switch (op) { + case 0: + dcache.WriteBack(vaddr, paddr); + break; + case 1: + dcache.LoadTag(vaddr); + break; + case 2: + dcache.StoreTag(vaddr, regs.cop0.tagLo.ptaglo, regs.cop0); + break; + case 3: + { + auto &line = dcache.lines[GetDCacheLineIndex(vaddr)]; + if ((line.ptag != ptag || !line.valid) && line.dirty) + dcache.WriteBack(vaddr, paddr); + + line.ptag = ptag; + line.valid = true; + line.dirty = true; + } + break; + case 4: + dcache.InvalidateIndex(vaddr, ptag); + break; + case 5: + dcache.WriteBack(vaddr, paddr, ptag); + break; + case 6: + dcache.WriteBack(vaddr, paddr, ptag); + break; + default: + panic("Unimplemented dcache op 0b{:03b}", op); + } +} } // namespace n64 diff --git a/src/backend/core/jit/decode.cpp b/src/backend/core/jit/decode.cpp index 2b14735..27c025f 100644 --- a/src/backend/core/jit/decode.cpp +++ b/src/backend/core/jit/decode.cpp @@ -3,463 +3,463 @@ namespace n64 { void JIT::special(const Instruction instr) { - // 00rr_rccc - switch (instr.special()) { - case Instruction::SLL: - if (instr != 0) { - sll(instr); + // 00rr_rccc + switch (instr.special()) { + case Instruction::SLL: + if (instr != 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(Cop0::ExceptionCode::Syscall, 0, regs.oldPC); + break; + case Instruction::BREAK: + regs.cop0.FireException(Cop0::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.special(), u32(instr), + 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.special(), u32(instr), - static_cast(regs.oldPC)); - } } void JIT::regimm(const Instruction instr) { - // 000r_rccc - switch (instr.regimm()) { - case Instruction::BLTZ: - bltz(instr); - break; - case Instruction::BGEZ: - bgez(instr); - break; - case Instruction::BLTZL: - bltzl(instr); - break; - case Instruction::BGEZL: - bgezl(instr); - break; - case Instruction::TGEI: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr))); - break; - case Instruction::TGEIU: - trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TLTI: - trap(regs.Read(instr.rs()) < static_cast(static_cast(instr))); - break; - case Instruction::TLTIU: - trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr)))); - break; - case Instruction::TEQI: - trap(regs.Read(instr.rs()) == static_cast(static_cast(instr))); - break; - case Instruction::TNEI: - trap(regs.Read(instr.rs()) != static_cast(static_cast(instr))); - break; - case Instruction::BLTZAL: - bltzal(instr); - break; - case Instruction::BGEZAL: - bgezal(instr); - break; - case Instruction::BLTZALL: - bltzall(instr); - break; - case Instruction::BGEZALL: - bgezall(instr); - break; - default: - panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr), - static_cast(regs.oldPC)); - } + // 000r_rccc + switch (instr.regimm()) { + case Instruction::BLTZ: + bltz(instr); + break; + case Instruction::BGEZ: + bgez(instr); + break; + case Instruction::BLTZL: + bltzl(instr); + break; + case Instruction::BGEZL: + bgezl(instr); + break; + case Instruction::TGEI: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(instr))); + break; + case Instruction::TGEIU: + trap(regs.Read(instr.rs()) >= static_cast(static_cast(static_cast(instr)))); + break; + case Instruction::TLTI: + trap(regs.Read(instr.rs()) < static_cast(static_cast(instr))); + break; + case Instruction::TLTIU: + trap(regs.Read(instr.rs()) < static_cast(static_cast(static_cast(instr)))); + break; + case Instruction::TEQI: + trap(regs.Read(instr.rs()) == static_cast(static_cast(instr))); + break; + case Instruction::TNEI: + trap(regs.Read(instr.rs()) != static_cast(static_cast(instr))); + break; + case Instruction::BLTZAL: + bltzal(instr); + break; + case Instruction::BGEZAL: + bgezal(instr); + break; + case Instruction::BLTZALL: + bltzall(instr); + break; + case Instruction::BGEZALL: + bgezall(instr); + break; + default: + panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr), + static_cast(regs.oldPC)); + } } void JIT::Emit(const Instruction instr) { - 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: - beq(instr); - break; - case Instruction::BNE: - bne(instr); - break; - case Instruction::BLEZ: - blez(instr); - break; - case Instruction::BGTZ: - bgtz(instr); - 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: - switch (instr.cop_rs()) { - case 0x00: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0); + switch (instr.opcode()) { + case Instruction::SPECIAL: + special(instr); break; - case 0x01: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0); + case Instruction::REGIMM: + regimm(instr); break; - case 0x04: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0); + case Instruction::J: + j(instr); break; - case 0x05: - code.mov(code.ARG2, instr); - emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0); + case Instruction::JAL: + jal(instr); break; - case 0x10 ... 0x1F: - switch (instr.cop_funct()) { - case 0x01: - emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0); - break; - case 0x02: - code.mov(code.ARG2, COP0_REG_INDEX); - emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0); - code.mov(code.ARG2, code.rax); - code.and_(code.ARG2, 0x3F); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x06: - emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0); - code.mov(code.ARG2, code.rax); - emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); - break; - case 0x08: - emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0); - break; - case 0x18: - emitMemberFunctionCall(&Cop0::eret, ®s.cop0); - break; - default: - panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), - regs.oldPC); - } + case Instruction::BEQ: + beq(instr); break; - default: - panic("Unimplemented COP0 instruction {}", instr.cop_rs()); - } - break; - case Instruction::COP1: - { - if (instr.cop_rs() == 0x08) { - switch (instr.cop_rt()) { - case 0: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc0(instr); - break; - case 1: - // if (!regs.cop1.CheckFPUUsable()) - // return; - bfc1(instr); - break; - case 2: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc0(instr); - break; - case 3: - // if (!regs.cop1.CheckFPUUsable()) - // return; - blfc1(instr); - break; + case Instruction::BNE: + bne(instr); + break; + case Instruction::BLEZ: + blez(instr); + break; + case Instruction::BGTZ: + bgtz(instr); + 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: + switch (instr.cop_rs()) { + case 0x00: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0); + break; + case 0x01: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0); + break; + case 0x04: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0); + break; + case 0x05: + code.mov(code.ARG2, instr); + emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0); + break; + case 0x10 ... 0x1F: + switch (instr.cop_funct()) { + case 0x01: + emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0); + break; + case 0x02: + code.mov(code.ARG2, COP0_REG_INDEX); + emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0); + code.mov(code.ARG2, code.rax); + code.and_(code.ARG2, 0x3F); + emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); + break; + case 0x06: + emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0); + code.mov(code.ARG2, code.rax); + emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0); + break; + case 0x08: + emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0); + break; + case 0x18: + emitMemberFunctionCall(&Cop0::eret, ®s.cop0); + break; + default: + panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC); + } + break; default: - panic("Undefined BC COP1 {:02X}", instr.cop_rt()); + panic("Unimplemented COP0 instruction {}", instr.cop_rs()); } break; - } - regs.cop1.decode(instr); + case Instruction::COP1: + { + if (instr.cop_rs() == 0x08) { + switch (instr.cop_rt()) { + case 0: + // if (!regs.cop1.CheckFPUUsable()) + // return; + bfc0(instr); + break; + case 1: + // if (!regs.cop1.CheckFPUUsable()) + // return; + bfc1(instr); + break; + case 2: + // if (!regs.cop1.CheckFPUUsable()) + // return; + blfc0(instr); + break; + case 3: + // if (!regs.cop1.CheckFPUUsable()) + // return; + blfc1(instr); + break; + default: + panic("Undefined BC COP1 {:02X}", instr.cop_rt()); + } + break; + } + regs.cop1.decode(instr); + } + break; + case Instruction::COP2: + break; + case Instruction::BEQL: + beql(instr); + break; + case Instruction::BNEL: + bnel(instr); + break; + case Instruction::BLEZL: + blezl(instr); + break; + case Instruction::BGTZL: + bgtzl(instr); + 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(Cop0::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: + lwc1(instr); + break; + case Instruction::LLD: + lld(instr); + break; + case Instruction::LDC1: + ldc1(instr); + break; + case Instruction::LD: + ld(instr); + break; + case Instruction::SC: + sc(instr); + break; + case Instruction::SWC1: + swc1(instr); + break; + case Instruction::SCD: + scd(instr); + break; + case Instruction::SDC1: + sdc1(instr); + break; + case Instruction::SD: + sd(instr); + break; + default: + DumpBlockCacheToDisk(); + panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), + static_cast(regs.oldPC)); } - break; - case Instruction::COP2: - break; - case Instruction::BEQL: - beql(instr); - break; - case Instruction::BNEL: - bnel(instr); - break; - case Instruction::BLEZL: - blezl(instr); - break; - case Instruction::BGTZL: - bgtzl(instr); - 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: - lwc1(instr); - break; - case Instruction::LLD: - lld(instr); - break; - case Instruction::LDC1: - ldc1(instr); - break; - case Instruction::LD: - ld(instr); - break; - case Instruction::SC: - sc(instr); - break; - case Instruction::SWC1: - swc1(instr); - break; - case Instruction::SCD: - scd(instr); - break; - case Instruction::SDC1: - sdc1(instr); - break; - case Instruction::SD: - sd(instr); - break; - default: - DumpBlockCacheToDisk(); - panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast(regs.oldPC)); - } } } // namespace n64 diff --git a/src/backend/core/jit/helpers.hpp b/src/backend/core/jit/helpers.hpp index 93d7b9f..39806fe 100644 --- a/src/backend/core/jit/helpers.hpp +++ b/src/backend/core/jit/helpers.hpp @@ -3,71 +3,141 @@ namespace n64 { static bool SpecialEndsBlock(const Instruction instr) { - switch (instr.special()) { - case Instruction::JR: - case Instruction::JALR: - case Instruction::SYSCALL: - case Instruction::BREAK: - case Instruction::TGE: - case Instruction::TGEU: - case Instruction::TLT: - case Instruction::TLTU: - case Instruction::TEQ: - case Instruction::TNE: - return true; - default: - return false; - } + switch (instr.special()) { + case Instruction::JR: + case Instruction::JALR: + case Instruction::SYSCALL: + case Instruction::BREAK: + case Instruction::TGE: + case Instruction::TGEU: + case Instruction::TLT: + case Instruction::TLTU: + case Instruction::TEQ: + case Instruction::TNE: + return true; + default: + return false; + } +} + +static bool InstrHasDelaySlot(const Instruction instr) { + switch (instr.opcode()) { + case Instruction::SPECIAL: + if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR) + return true; + return false; + case Instruction::REGIMM: + case Instruction::J: + case Instruction::JAL: + case Instruction::BEQ: + case Instruction::BNE: + case Instruction::BLEZ: + case Instruction::BGTZ: + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::COP1: + if (instr.cop_rs() == 8) + return true; + + return false; + default: + return false; + } } static bool InstrEndsBlock(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::SPECIAL: - return SpecialEndsBlock(instr); - case Instruction::REGIMM: - case Instruction::J: - case Instruction::JAL: - case Instruction::BEQ: - case Instruction::BNE: - case Instruction::BLEZ: - case Instruction::BGTZ: - return true; - default: - return false; - } + switch (instr.opcode()) { + case Instruction::SPECIAL: + return SpecialEndsBlock(instr); + case Instruction::REGIMM: + case Instruction::J: + case Instruction::JAL: + case Instruction::BEQ: + case Instruction::BNE: + case Instruction::BLEZ: + case Instruction::BGTZ: + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::COP1: + if (instr.cop_rs() == 8) + return true; + + return false; + case Instruction::COP0: + switch (instr.cop_rs()) { + case 0x10 ... 0x1F: + switch (instr.cop_funct()) { + case 0x18: // eret + return true; + default: + return false; + } + default: + return false; + } + default: + return false; + } } static bool IsBranchLikely(const Instruction instr) { - switch (instr.opcode()) { - case Instruction::BEQL: - case Instruction::BNEL: - case Instruction::BLEZL: - case Instruction::BGTZL: - return true; - case Instruction::REGIMM: - switch (instr.regimm()) { - case Instruction::BLTZL: - case Instruction::BGEZL: - case Instruction::BLTZALL: - case Instruction::BGEZALL: - return true; - default: - return false; - } - case Instruction::COP1: - { - if (instr.cop_rs() == 0x08) { - if (instr.cop_rt() == 2 || instr.cop_rt() == 3) - return true; + switch (instr.opcode()) { + case Instruction::BEQL: + case Instruction::BNEL: + case Instruction::BLEZL: + case Instruction::BGTZL: + return true; + case Instruction::REGIMM: + switch (instr.regimm()) { + case Instruction::BLTZL: + case Instruction::BGEZL: + case Instruction::BLTZALL: + case Instruction::BGEZALL: + return true; + default: + return false; + } + case Instruction::COP1: + if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3)) + return true; return false; - } + default: + return false; + } +} - return false; +static bool IsBranch(const Instruction instr) { + switch (instr.opcode()) { + case Instruction::BEQ: + case Instruction::BNE: + case Instruction::BLEZ: + case Instruction::BGTZ: + return true; + case Instruction::REGIMM: + switch (instr.regimm()) { + case Instruction::BLTZ: + case Instruction::BGEZ: + case Instruction::BLTZAL: + case Instruction::BGEZAL: + return true; + default: + return false; + } + case Instruction::COP1: + if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1)) + return true; + + return false; + default: + return false; } - default: - return false; - } } #ifdef _WIN32 diff --git a/src/backend/core/jit/instructions.cpp b/src/backend/core/jit/instructions.cpp index 1d2b04f..e8cf89d 100644 --- a/src/backend/core/jit/instructions.cpp +++ b/src/backend/core/jit/instructions.cpp @@ -7,1066 +7,1068 @@ namespace n64 { using namespace Xbyak::util; void JIT::lui(const Instruction instr) { - u64 val = static_cast(static_cast(instr)); - val <<= 16; - regs.Write(instr.rt(), val); + u64 val = static_cast(static_cast(instr)); + val <<= 16; + regs.Write(instr.rt(), val); } void JIT::add(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const u32 rs = regs.Read(instr.rs()); - const u32 rt = regs.Read(instr.rt()); - const u32 result = rs + rt; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in ADD!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const u32 rs = regs.Read(instr.rs()); + const u32 rt = regs.Read(instr.rt()); + const u32 result = rs + rt; + if (check_signed_overflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in ADD!"); + } + + regs.Write(instr.rd(), result); + return; } - regs.Write(instr.rd(), result); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const u32 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const u32 rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rt); + regs.Write(instr.rd(), code.SCR1.cvt32()); + + return; + } - if (regs.IsRegConstant(instr.rs())) { - const u32 rs = regs.Read(instr.rs()); regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); + regs.Read(instr.rs(), code.SCR2.cvt32()); + code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - if (regs.IsRegConstant(instr.rt())) { - const u32 rt = regs.Read(instr.rt()); - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rt); - regs.Write(instr.rd(), code.SCR1.cvt32()); - - return; - } - - regs.Read(instr.rt(), code.SCR1.cvt32()); - regs.Read(instr.rs(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); } void JIT::addu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - const s32 rs = regs.Read(instr.rs()); - const s32 rt = regs.Read(instr.rt()); - const s32 result = rs + rt; + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const s32 rs = regs.Read(instr.rs()); + const s32 rt = regs.Read(instr.rt()); + const s32 result = rs + rt; - regs.Write(instr.rd(), result); - return; - } + regs.Write(instr.rd(), result); + return; + } - if (regs.IsRegConstant(instr.rs())) { - const s32 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); - regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const s32 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const s32 rs = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), rs); + regs.Write(instr.rd(), code.SCR1.cvt32()); + return; + } - if (regs.IsRegConstant(instr.rt())) { - const s32 rs = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), rs); + regs.Read(instr.rt(), code.SCR2.cvt32()); + code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); regs.Write(instr.rd(), code.SCR1.cvt32()); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - regs.Read(instr.rt(), code.SCR2.cvt32()); - code.add(code.SCR1.cvt32(), code.SCR2.cvt32()); - regs.Write(instr.rd(), code.SCR1.cvt32()); } void JIT::addi(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - if (check_signed_overflow(rs, imm, result)) { - panic("[JIT]: Unhandled Overflow exception in ADDI!"); + u32 imm = s32(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u32 result = rs + imm; + if (check_signed_overflow(rs, imm, result)) { + panic("[JIT]: Unhandled Overflow exception in ADDI!"); + } + + regs.Write(instr.rt(), static_cast(result)); + return; } - regs.Write(instr.rt(), static_cast(result)); - return; - } - - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.eax, SCR1.cvt32()); - regs.Write(instr.rt(), code.SCR1.cvt32()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.eax, SCR1.cvt32()); + regs.Write(instr.rt(), code.SCR1.cvt32()); } void JIT::addiu(const Instruction instr) { - u32 imm = s32(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u32 result = rs + imm; - regs.Write(instr.rt(), s32(result)); + u32 imm = s32(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u32 result = rs + imm; + regs.Write(instr.rt(), s32(result)); - return; - } + return; + } - regs.Read(instr.rs(), code.SCR1.cvt32()); - code.add(code.SCR1.cvt32(), imm); - regs.Write(instr.rt(), code.SCR1.cvt32()); + regs.Read(instr.rs(), code.SCR1.cvt32()); + code.add(code.SCR1.cvt32(), imm); + regs.Write(instr.rt(), code.SCR1.cvt32()); } void JIT::andi(const Instruction instr) { - const s64 imm = static_cast(instr); - if (regs.IsRegConstant(instr.rs())) { - regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); - return; - } + const s64 imm = static_cast(instr); + if (regs.IsRegConstant(instr.rs())) { + regs.Write(instr.rt(), regs.Read(instr.rs()) & imm); + return; + } - regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.and_(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::and_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); - return; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) & regs.Read(instr.rt())); + return; + } - if (regs.IsRegConstant(instr.rs())) { - const auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.and_(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const auto rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.and_(code.SCR1, rs); + regs.Write(instr.rd(), code.SCR1); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + const auto rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.and_(code.SCR1, rt); + regs.Write(instr.rd(), code.SCR1); + return; + } - if (regs.IsRegConstant(instr.rt())) { - const auto rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.and_(code.SCR1, rt); - regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.and_(code.SCR2, code.SCR1); - regs.Write(instr.rd(), code.SCR2); + regs.Read(instr.rt(), code.SCR2); + code.and_(code.SCR2, code.SCR1); + regs.Write(instr.rd(), code.SCR2); } void JIT::bfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, z); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch(address, z); } void JIT::blfc0(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, z); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch_likely(address, z); } void JIT::bfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch(address, nz); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch(address, nz); } void JIT::blfc1(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - const s64 address = blockPC + offset; - // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); - // code.test(code.al, code.al); - // branch_likely(address, nz); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + const s64 address = blockPC + offset; + // code.mov(code.al, code.byte[code.rbp + REG_OFFSET(cop1.fcr31.compare)); + // code.test(code.al, code.al); + // branch_likely(address, nz); } void JIT::BranchNotTaken() {} void JIT::BranchTaken(const s64 offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); + code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } void JIT::BranchTaken(const Xbyak::Reg64 &offs) { - code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); - code.add(code.SCR2, offs); - SetPC64(code.SCR2); + code.mov(code.SCR2, code.qword[code.rbp + PC_OFFSET]); + code.add(code.SCR2, offs); + SetPC64(code.SCR2); } -void JIT::BranchAbsTaken(const s64 addr) { - SetPC64(addr); -} +void JIT::BranchAbsTaken(const s64 addr) { SetPC64(addr); } -void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { - SetPC64(addr); -} +void JIT::BranchAbsTaken(const Xbyak::Reg64 &addr) { SetPC64(addr); } void JIT::branch_constant(const bool cond, const s64 offset) { - if(cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } + if (cond) { + regs.delaySlot = true; + BranchTaken(offset); + branch_taken = true; + } } void JIT::branch_abs_constant(const bool cond, const s64 address) { - if(cond) { - regs.delaySlot = true; - BranchAbsTaken(address); - branch_taken = true; - } + if (cond) { + regs.delaySlot = true; + BranchAbsTaken(address); + branch_taken = true; + } } void JIT::branch_likely_constant(const bool cond, const s64 offset) { - if(cond) { - regs.delaySlot = true; - BranchTaken(offset); - branch_taken = true; - } else { - SetPC64(blockNextPC); - } + if (cond) { + regs.delaySlot = true; + BranchTaken(offset); + branch_taken = true; + } else { + SetPC64(blockNextPC); + } } -#define branch(offs, cond) do { \ - Xbyak::Label taken, not_taken; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ -} while(0) +#define branch(offs, cond) \ + do { \ + Xbyak::Label taken, not_taken; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchTaken(offs); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.L(not_taken); \ + } \ + while (0) -#define branch_abs(addr, cond) do { \ - Xbyak::Label taken, not_taken; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchAbsTaken(addr); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.L(not_taken); \ -} while(0) +#define branch_abs(addr, cond) \ + do { \ + Xbyak::Label taken, not_taken; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchAbsTaken(addr); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.L(not_taken); \ + } \ + while (0) -#define branch_likely(offs, cond) do { \ - Xbyak::Label taken, not_taken, end; \ - code.j## cond(taken); \ - code.jmp(not_taken); \ - code.L(taken); \ - BranchTaken(offs); \ - code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ - code.jmp(end); \ - code.L(not_taken); \ - SetPC64(blockNextPC); \ - code.L(end); \ -} while(0) +#define branch_likely(offs, cond) \ + do { \ + Xbyak::Label taken, not_taken, end; \ + code.j##cond(taken); \ + code.jmp(not_taken); \ + code.L(taken); \ + BranchTaken(offs); \ + code.mov(code.byte[code.rbp + BRANCH_TAKEN_OFFSET], 1); \ + code.jmp(end); \ + code.L(not_taken); \ + SetPC64(blockNextPC); \ + code.L(end); \ + } \ + while (0) void JIT::bltz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = static_cast(imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, l); } void JIT::bgez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, ge); } void JIT::bltzl(const Instruction instr) { - panic("Implement branch likely < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + panic("Implement branch likely < 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, l); } void JIT::bgezl(const Instruction instr) { - panic("Implement branch likely >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + panic("Implement branch likely >= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, ge); } void JIT::bltzal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, l); } void JIT::bgezal(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, ge); } void JIT::bltzall(const Instruction instr) { - panic("Implement branch likely and link < 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) < 0, offset); - return; - } + panic("Implement branch likely and link < 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) < 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, l); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, l); } void JIT::bgezall(const Instruction instr) { - panic("Implement branch likely and link >= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - regs.Write(31, blockNextPC); - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); - return; - } + panic("Implement branch likely and link >= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + regs.Write(31, blockNextPC); + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) >= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, ge); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, ge); } void JIT::beq(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, e); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch(offset, e); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch(offset, e); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, e); } void JIT::beql(const Instruction instr) { - panic("Implement branch likely =="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); - return; - } + panic("Implement branch likely =="); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_likely_constant(regs.Read(instr.rs()) == regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, e); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch_likely(offset, e); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch_likely(offset, e); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch_likely(offset, e); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, e); } void JIT::bne(const Instruction instr) { - const s16 imm = instr; - const s64 offset = static_cast(imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } + const s16 imm = instr; + const s64 offset = static_cast(imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch(offset, ne); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch(offset, ne); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch(offset, ne); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch(offset, ne); } void JIT::bnel(const Instruction instr) { - panic("Implement branch likely !="); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { - branch_likely_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); - return; - } + panic("Implement branch likely !="); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs()) && regs.IsRegConstant(instr.rt())) { + branch_likely_constant(regs.Read(instr.rs()) != regs.Read(instr.rt()), offset); + return; + } - if (regs.IsRegConstant(instr.rs())) { - regs.Read(instr.rt(), code.rax); - code.cmp(code.rax, regs.Read(instr.rs())); - branch_likely(offset, ne); - return; - } + if (regs.IsRegConstant(instr.rs())) { + regs.Read(instr.rt(), code.rax); + code.cmp(code.rax, regs.Read(instr.rs())); + branch_likely(offset, ne); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, regs.Read(instr.rt())); + branch_likely(offset, ne); + return; + } - if (regs.IsRegConstant(instr.rt())) { regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, regs.Read(instr.rt())); + regs.Read(instr.rt(), code.rdi); + code.cmp(code.rax, code.rdi); branch_likely(offset, ne); - return; - } - - regs.Read(instr.rs(), code.rax); - regs.Read(instr.rt(), code.rdi); - code.cmp(code.rax, code.rdi); - branch_likely(offset, ne); } void JIT::blez(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) <= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, le); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, le); } void JIT::blezl(const Instruction instr) { - panic("Implement branch likely <= 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) <= 0, offset); - return; - } + panic("Implement branch likely <= 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) <= 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, le); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, le); } void JIT::bgtz(const Instruction instr) { - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_constant(regs.Read(instr.rs()) > 0, offset); - return; - } + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_constant(regs.Read(instr.rs()) > 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch(offset, g); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch(offset, g); } void JIT::bgtzl(const Instruction instr) { - panic("Implement branch likely > 0"); - const s16 imm = instr; - const s64 offset = u64((s64)imm) << 2; - if (regs.IsRegConstant(instr.rs())) { - branch_likely_constant(regs.Read(instr.rs()) > 0, offset); - return; - } + panic("Implement branch likely > 0"); + const s16 imm = instr; + const s64 offset = u64((s64)imm) << 2; + if (regs.IsRegConstant(instr.rs())) { + branch_likely_constant(regs.Read(instr.rs()) > 0, offset); + return; + } - regs.Read(instr.rs(), code.rax); - code.cmp(code.rax, 0); - branch_likely(offset, g); + regs.Read(instr.rs(), code.rax); + code.cmp(code.rax, 0); + branch_likely(offset, g); } void JIT::dadd(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto rs = regs.Read(instr.rs()); - auto rt = regs.Read(instr.rt()); - u64 result = rt + rs; - if (check_signed_overflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADD!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto rs = regs.Read(instr.rs()); + auto rt = regs.Read(instr.rt()); + u64 result = rt + rs; + if (check_signed_overflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DADD!"); + } + regs.Write(instr.rd(), result); + return; } - regs.Write(instr.rd(), result); - return; - } - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.add(code.SCR1, rs); - regs.Write(instr.rd(), code.SCR1); - return; - } + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.add(code.SCR1, rs); + regs.Write(instr.rd(), code.SCR1); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.add(code.SCR1, rt); + regs.Write(instr.rd(), code.SCR1); + return; + } - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, rt); + regs.Read(instr.rt(), code.SCR2); + code.add(code.SCR1, code.SCR2); regs.Write(instr.rd(), code.SCR1); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.add(code.SCR1, code.SCR2); - regs.Write(instr.rd(), code.SCR1); } void JIT::daddu(const Instruction instr) { - // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY - dadd(instr); + // TODO: IMPLEMENT DADDU BY ITSELF ACTUALLY + dadd(instr); } void JIT::daddi(const Instruction instr) { - u64 imm = s64(s16(instr)); - if (regs.IsRegConstant(instr.rs())) { - auto rs = regs.Read(instr.rs()); - u64 result = imm + rs; - if (check_signed_overflow(rs, imm, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DADDI!"); + u64 imm = s64(s16(instr)); + if (regs.IsRegConstant(instr.rs())) { + auto rs = regs.Read(instr.rs()); + u64 result = imm + rs; + if (check_signed_overflow(rs, imm, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DADDI!"); + } + regs.Write(instr.rt(), result); + return; } - regs.Write(instr.rt(), result); - return; - } - regs.Read(instr.rs(), code.SCR1); - code.add(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.add(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::daddiu(const Instruction instr) { - // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY - daddi(instr); + // TODO: IMPLEMENT DADDIU BY ITSELF ACTUALLY + daddi(instr); } void JIT::ddiv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto 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 { - s64 quotient = dividend / divisor; - s64 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto 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 { + s64 quotient = dividend / divisor; + s64 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIV!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DDIV!"); + } } void JIT::ddivu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s64)dividend; - } else { - u64 quotient = dividend / divisor; - u64 remainder = dividend % divisor; - regs.lo = (s64)quotient; - regs.hi = (s64)remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto divisor = regs.Read(instr.rt()); + if (divisor == 0) { + regs.lo = -1; + regs.hi = (s64)dividend; + } else { + u64 quotient = dividend / divisor; + u64 remainder = dividend % divisor; + regs.lo = (s64)quotient; + regs.hi = (s64)remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DDIVU!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DDIVU!"); + } } void JIT::div(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 dividend = regs.Read(instr.rs()); - s64 divisor = regs.Read(instr.rt()); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s64 dividend = regs.Read(instr.rs()); + s64 divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.hi = dividend; - if (dividend >= 0) { - regs.lo = s64(-1); - } else { - regs.lo = s64(1); - } + if (divisor == 0) { + regs.hi = dividend; + if (dividend >= 0) { + regs.lo = s64(-1); + } else { + regs.lo = s64(1); + } + } else { + s32 quotient = dividend / divisor; + s32 remainder = dividend % divisor; + regs.lo = quotient; + regs.hi = remainder; + } + + regs.SetLOConstant(); + regs.SetHIConstant(); } else { - s32 quotient = dividend / divisor; - s32 remainder = dividend % divisor; - regs.lo = quotient; - regs.hi = remainder; + panic("[JIT]: Implement non constant DIV!"); } - - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIV!"); - } } void JIT::divu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - auto dividend = regs.Read(instr.rs()); - auto divisor = regs.Read(instr.rt()); - if (divisor == 0) { - regs.lo = -1; - regs.hi = (s32)dividend; - } else { - s32 quotient = (s32)(dividend / divisor); - s32 remainder = (s32)(dividend % divisor); - regs.lo = quotient; - regs.hi = remainder; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + auto dividend = regs.Read(instr.rs()); + auto divisor = regs.Read(instr.rt()); + if (divisor == 0) { + regs.lo = -1; + regs.hi = (s32)dividend; + } else { + s32 quotient = (s32)(dividend / divisor); + s32 remainder = (s32)(dividend % divisor); + regs.lo = quotient; + regs.hi = remainder; + } - regs.SetLOConstant(); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant DIVU!"); - } + regs.SetLOConstant(); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant DIVU!"); + } } void JIT::dmult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s128 result = (s128)rt * (s128)rs; - regs.lo = result & 0xFFFFFFFFFFFFFFFF; - regs.hi = result >> 64; - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s128 result = (s128)rt * (s128)rs; + regs.lo = result & 0xFFFFFFFFFFFFFFFF; + regs.hi = result >> 64; + regs.SetHIConstant(); + regs.SetLOConstant(); + } else { + panic("[JIT]: Implement non constant DMULT!"); + } } void JIT::dmultu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u128 result = (u128)rt * (u128)rs; - regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); - regs.hi = s64(result >> 64); - regs.SetHIConstant(); - regs.SetLOConstant(); - } else { - panic("[JIT]: Implement non constant DMULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + u128 result = (u128)rt * (u128)rs; + regs.lo = s64(result & 0xFFFFFFFFFFFFFFFF); + regs.hi = s64(result >> 64); + regs.SetHIConstant(); + regs.SetLOConstant(); + } else { + panic("[JIT]: Implement non constant DMULT!"); + } } void JIT::dsll(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL!"); - } + if (regs.IsRegConstant(instr.rt())) { + u8 sa = ((instr >> 6) & 0x1f); + auto result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLL!"); + } } void JIT::dsllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto sa = regs.Read(instr.rs()) & 63; - auto result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLLV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto sa = regs.Read(instr.rs()) & 63; + auto result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLLV!"); + } } void JIT::dsll32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u8 sa = ((instr >> 6) & 0x1f); - auto result = regs.Read(instr.rt()) << (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSLL32!"); - } + if (regs.IsRegConstant(instr.rt())) { + u8 sa = ((instr >> 6) & 0x1f); + auto result = regs.Read(instr.rt()) << (sa + 32); + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSLL32!"); + } } void JIT::dsra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRA!"); + } } void JIT::dsrav(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 sa = rs & 63; - s64 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRAV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 sa = rs & 63; + s64 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRAV!"); + } } void JIT::dsra32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s64 result = rt >> (sa + 32); - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSRA32!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s64 result = rt >> (sa + 32); + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSRA32!"); + } } void JIT::dsrl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> sa; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> sa; + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRL!"); + } } void JIT::dsrlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - u8 amount = regs.Read(instr.rs()) & 63; - auto rt = regs.Read(instr.rt()); - u64 result = rt >> amount; - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRLV!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + u8 amount = regs.Read(instr.rs()) & 63; + auto rt = regs.Read(instr.rt()); + u64 result = rt >> amount; + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRLV!"); + } } void JIT::dsrl32(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - auto rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u64 result = rt >> (sa + 32); - regs.Write(instr.rd(), s64(result)); - } else { - panic("[JIT]: Implement non constant DSRL32!"); - } + if (regs.IsRegConstant(instr.rt())) { + auto rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u64 result = rt >> (sa + 32); + regs.Write(instr.rd(), s64(result)); + } else { + panic("[JIT]: Implement non constant DSRL32!"); + } } void JIT::dsub(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - // regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); - panic("[JIT]: Unhandled Overflow exception in DSUB!"); + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = rs - rt; + if (check_signed_underflow(rs, rt, result)) { + // regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + panic("[JIT]: Unhandled Overflow exception in DSUB!"); + } else { + regs.Write(instr.rd(), result); + } } else { - regs.Write(instr.rd(), result); + panic("[JIT]: Implement non constant DSUB!"); } - } else { - panic("[JIT]: Implement non constant DSUB!"); - } } void JIT::dsubu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = rs - rt; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant DSUBU!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = rs - rt; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant DSUBU!"); + } } void JIT::j(const Instruction instr) { - const s32 target = (instr & 0x3ffffff) << 2; - const s64 address = blockOldPC & ~0xfffffff | target; - branch_abs_constant(true, address); + const s32 target = (instr & 0x3ffffff) << 2; + const s64 address = blockOldPC & ~0xfffffff | target; + branch_abs_constant(true, address); } void JIT::jr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - const auto address = regs.Read(instr.rs()); - branch_abs_constant(true, address); - return; - } + if (regs.IsRegConstant(instr.rs())) { + const auto address = regs.Read(instr.rs()); + branch_abs_constant(true, address); + return; + } - regs.Read(instr.rs(), code.rax); - branch_abs(code.rax, mp); + regs.Read(instr.rs(), code.rax); + branch_abs(code.rax, mp); } - + void JIT::jal(const Instruction instr) { - regs.Write(31, blockNextPC); - j(instr); + regs.Write(31, blockNextPC); + j(instr); } void JIT::jalr(const Instruction instr) { - regs.Write(instr.rd(), blockNextPC); - jr(instr); + regs.Write(instr.rd(), blockNextPC); + jr(instr); } void JIT::lbu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - 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); - panic("[JIT]: Unhandled TLBL exception in LBU!"); + if (regs.IsRegConstant(instr.rs())) { + 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); + panic("[JIT]: Unhandled TLBL exception in LBU!"); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LBU!"); } - } else { - panic("[JIT]: Implement non constant LBU!"); - } } void JIT::lb(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - 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); - panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); + if (regs.IsRegConstant(instr.rs())) { + 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); + panic("[JIT]: Unhandled TLBL exception in LB (pc: 0x{:016X})!", static_cast(blockPC)); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LB!"); } - } else { - panic("[JIT]: Implement non constant LB!"); - } } void JIT::ld(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - 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; - panic("[JIT]: Unhandled ADEL exception in LD!"); - } + if (regs.IsRegConstant(instr.rs())) { + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (Core::IsAddressError(0b111, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LD!"); + } - 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); - panic("[JIT]: Unhandled TLBL exception in LD!"); + 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); + panic("[JIT]: Unhandled TLBL exception in LD!"); + } else { + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + } } else { - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); + panic("[JIT]: Implement non constant LD!"); } - } else { - panic("[JIT]: Implement non constant LD!"); - } } void JIT::ldc1(const Instruction instr) { - if (regs.IsRegConstant(instr.base())) { - const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); + if (regs.IsRegConstant(instr.base())) { + const u64 addr = static_cast(static_cast(instr)) + regs.Read(instr.base()); - if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { - // regs.cop0.HandleTLBException(addr); - // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); - panic("[JIT]: Unhandled TLBL exception in LD1!"); + if (u32 physical; !regs.cop0.MapVAddr(Cop0::LOAD, addr, physical)) { + // regs.cop0.HandleTLBException(addr); + // regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC); + panic("[JIT]: Unhandled TLBL exception in LD1!"); + } else { + const u64 data = mem.Read(physical); + regs.cop1.FGR_T(regs.cop0.status, instr.ft()) = data; + regs.cop1.fgrIsConstant[instr.ft()] = true; + } } else { - const u64 data = mem.Read(physical); - regs.cop1.FGR_T(regs.cop0.status, instr.ft()) = data; - regs.cop1.fgrIsConstant[instr.ft()] = true; + panic("[JIT]: Implement non constant LD1!"); } - } else { - panic("[JIT]: Implement non constant LD1!"); - } } void JIT::ldl(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - 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); - panic("[JIT]: Unhandled TLBL exception in LDL!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + 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); + panic("[JIT]: Unhandled TLBL exception in LDL!"); + } else { + panic("[JIT]: Implement constant LDL!"); + 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); + } } else { - panic("[JIT]: Implement constant LDL!"); - 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); + panic("[JIT]: Implement non constant LDL!"); } - } else { - panic("[JIT]: Implement non constant LDL!"); - } } void JIT::ldr(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - 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); - panic("[JIT]: Unhandled TLBL exception in LDR!"); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + 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); + panic("[JIT]: Unhandled TLBL exception in LDR!"); + } 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); + } } 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); + panic("[JIT]: Implement non constant LDR!"); } - } else { - panic("[JIT]: Implement non constant LDR!"); - } } void JIT::lh(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - 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; - panic("[JIT]: Unhandled ADEL exception in LH!"); - return; + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + (s16)instr; + if (Core::IsAddressError(0b1, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LH!"); + 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); + panic("[JIT]: Unhandled TLBL exception in LH!"); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + 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); - panic("[JIT]: Unhandled TLBL exception in LH!"); - return; - } - - code.mov(code.ARG2, paddr); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); - return; - } - - panic("[JIT]: Implement non constant LH!"); + panic("[JIT]: Implement non constant LH!"); } void JIT::lhu(const Instruction instr) { - u32 paddr; - if (regs.IsRegConstant(instr.rs())) { - 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.IsRegConstant(instr.rs())) { + const s64 address = regs.Read(instr.rs()) + (s16)instr; + if (Core::IsAddressError(0b1, address)) { + regs.cop0.HandleTLBException(address); + regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + return; + } + + 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); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); } - 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); - return; - } + code.mov(code.ARG2, Cop0::LOAD); + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, s16(instr)); + code.mov(code.ARG4, reinterpret_cast(&paddr)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - code.mov(code.ARG2, paddr); + code.mov(code.ARG2, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(instr.rt(), code.rax); - } - - code.mov(code.ARG2, Cop0::LOAD); - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, s16(instr)); - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); } void JIT::ll(const Instruction) { panic("[JIT]: Implement constant LL!"); } @@ -1074,42 +1076,42 @@ void JIT::ll(const Instruction) { panic("[JIT]: Implement constant LL!"); } void JIT::lld(const Instruction) { panic("[JIT]: Implement constant LLD!"); } void JIT::lw(const Instruction instr) { - const s16 offset = instr; - u32 paddr = 0; - if (regs.IsRegConstant(instr.rs())) { - 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; - panic("[JIT]: Unhandled ADEL exception in LW!"); - return; + const s16 offset = instr; + u32 paddr = 0; + if (regs.IsRegConstant(instr.rs())) { + const u64 address = regs.Read(instr.rs()) + offset; + if (Core::IsAddressError(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.oldPC); + // return; + panic("[JIT]: Unhandled ADEL exception in LW!"); + return; + } + + 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); + panic("[JIT]: Unhandled TLBL exception in LW!"); + return; + } + + code.mov(code.ARG2, paddr); + emitMemberFunctionCall(&Mem::Read, &mem); + regs.Write(instr.rt(), code.rax); + return; } - 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); - panic("[JIT]: Unhandled TLBL exception in LW!"); - return; - } + code.mov(code.ARG2, Cop0::LOAD); - code.mov(code.ARG2, paddr); + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, offset); + + code.mov(code.ARG4, reinterpret_cast(&paddr)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); + + code.mov(code.ARG2, code.qword[code.ARG4]); emitMemberFunctionCall(&Mem::Read, &mem); regs.Write(instr.rt(), code.rax); - return; - } - - code.mov(code.ARG2, Cop0::LOAD); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&paddr)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - emitMemberFunctionCall(&Mem::Read, &mem); - regs.Write(instr.rt(), code.rax); } void JIT::lwc1(const Instruction) { panic("[JIT]: Implement constant LWC1!"); } @@ -1121,271 +1123,287 @@ void JIT::lwu(const Instruction) { panic("[JIT]: Implement constant LWU!"); } void JIT::lwr(const Instruction) { panic("[JIT]: Implement constant LWR!"); } void JIT::mfhi(const Instruction instr) { - if (regs.GetHIConstant()) { - regs.Write(instr.rd(), regs.hi); - } else { - code.mov(code.SCR1, code.qword[HI_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } + if (regs.GetHIConstant()) { + regs.Write(instr.rd(), regs.hi); + } else { + code.mov(code.SCR1, code.qword[HI_OFFSET]); + regs.Write(instr.rd(), code.SCR1); + } } void JIT::mflo(const Instruction instr) { - if (regs.GetLOConstant()) { - regs.Write(instr.rd(), regs.lo); - } else { - code.mov(code.SCR1, code.qword[LO_OFFSET]); - regs.Write(instr.rd(), code.SCR1); - } + if (regs.GetLOConstant()) { + regs.Write(instr.rd(), regs.lo); + } else { + code.mov(code.SCR1, code.qword[LO_OFFSET]); + regs.Write(instr.rd(), code.SCR1); + } } void JIT::mult(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - s64 result = (s64)rt * (s64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULT!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + s64 result = (s64)rt * (s64)rs; + regs.lo = (s64)((s32)result); + regs.SetLOConstant(); + regs.hi = (s64)((s32)(result >> 32)); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant MULT!"); + } } void JIT::multu(const Instruction instr) { - if (regs.IsRegConstant(instr.rt(), instr.rs())) { - auto rt = regs.Read(instr.rt()); - auto rs = regs.Read(instr.rs()); - u64 result = (u64)rt * (u64)rs; - regs.lo = (s64)((s32)result); - regs.SetLOConstant(); - regs.hi = (s64)((s32)(result >> 32)); - regs.SetHIConstant(); - } else { - panic("[JIT]: Implement non constant MULTU!"); - } + if (regs.IsRegConstant(instr.rt(), instr.rs())) { + auto rt = regs.Read(instr.rt()); + auto rs = regs.Read(instr.rs()); + u64 result = (u64)rt * (u64)rs; + regs.lo = (s64)((s32)result); + regs.SetLOConstant(); + regs.hi = (s64)((s32)(result >> 32)); + regs.SetHIConstant(); + } else { + panic("[JIT]: Implement non constant MULTU!"); + } } void JIT::mthi(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.hi = regs.Read(instr.rs()); - regs.SetHIConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[HI_OFFSET], code.SCR1); - regs.UnsetHIConstant(); - } + if (regs.IsRegConstant(instr.rs())) { + regs.hi = regs.Read(instr.rs()); + regs.SetHIConstant(); + } else { + regs.Read(instr.rs(), code.SCR1); + code.mov(code.qword[HI_OFFSET], code.SCR1); + regs.UnsetHIConstant(); + } } void JIT::mtlo(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - regs.lo = regs.Read(instr.rs()); - regs.SetLOConstant(); - } else { - regs.Read(instr.rs(), code.SCR1); - code.mov(code.qword[LO_OFFSET], code.SCR1); - regs.UnsetLOConstant(); - } + if (regs.IsRegConstant(instr.rs())) { + regs.lo = regs.Read(instr.rs()); + regs.SetLOConstant(); + } else { + regs.Read(instr.rs(), code.SCR1); + code.mov(code.qword[LO_OFFSET], code.SCR1); + regs.UnsetLOConstant(); + } } void JIT::nor(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); - } else { - panic("[JIT]: Implement non constant NOR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), ~(regs.Read(instr.rs()) | regs.Read(instr.rt()))); + } else { + panic("[JIT]: Implement non constant NOR!"); + } } void JIT::slti(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - return; - } + if (regs.IsRegConstant(instr.rs())) { + s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + return; + } - panic("[JIT]: Implement non constant SLTI!"); + panic("[JIT]: Implement non constant SLTI!"); } void JIT::sltiu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s16 imm = instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); - } else { - panic("[JIT]: Implement non constant SLTIU!"); - } + if (regs.IsRegConstant(instr.rs())) { + s16 imm = instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) < imm); + } else { + panic("[JIT]: Implement non constant SLTIU!"); + } } void JIT::slt(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - return; - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); + return; + } + + if (regs.IsRegConstant(instr.rt())) { + s64 rt = regs.Read(instr.rt()); + regs.Read(instr.rs(), code.SCR1); + code.cmp(code.SCR1, rt); + code.setl(code.SCR1.cvt8()); + regs.Write(instr.rd(), code.SCR1.cvt8()); + return; + } + + if (regs.IsRegConstant(instr.rs())) { + s64 rs = regs.Read(instr.rs()); + regs.Read(instr.rt(), code.SCR1); + code.cmp(code.SCR1, rs); + code.setge(code.SCR1.cvt8()); + regs.Write(instr.rd(), code.SCR1.cvt8()); + return; + } - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); regs.Read(instr.rs(), code.SCR1); - code.cmp(code.SCR1, rt); + regs.Read(instr.rt(), code.SCR2); + code.cmp(code.SCR1, code.SCR2); code.setl(code.SCR1.cvt8()); regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - if (regs.IsRegConstant(instr.rs())) { - s64 rs = regs.Read(instr.rs()); - regs.Read(instr.rt(), code.SCR1); - code.cmp(code.SCR1, rs); - code.setge(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); - return; - } - - regs.Read(instr.rs(), code.SCR1); - regs.Read(instr.rt(), code.SCR2); - code.cmp(code.SCR1, code.SCR2); - code.setl(code.SCR1.cvt8()); - regs.Write(instr.rd(), code.SCR1.cvt8()); } void JIT::sltu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant SLT!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) < regs.Read(instr.rt())); + } else { + panic("[JIT]: Implement non constant SLT!"); + } } void JIT::sll(const Instruction instr) { - const u8 sa = ((instr >> 6) & 0x1f); - if (regs.IsRegConstant(instr.rt())) { - const s32 result = regs.Read(instr.rt()) << sa; - regs.Write(instr.rd(), (s64)result); - } else { - regs.Read(instr.rt(), code.SCR1); - code.sal(code.SCR1, sa); - regs.Write(instr.rd(), code.SCR1.cvt32()); - } + const u8 sa = ((instr >> 6) & 0x1f); + if (regs.IsRegConstant(instr.rt())) { + const s32 result = regs.Read(instr.rt()) << sa; + regs.Write(instr.rd(), (s64)result); + } else { + regs.Read(instr.rt(), code.SCR1); + code.sal(code.SCR1, sa); + regs.Write(instr.rd(), code.SCR1.cvt32()); + } } void JIT::sllv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs())) & 0x1F; - u32 rt = regs.Read(instr.rt()); - s32 result = rt << sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SLLV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u8 sa = (regs.Read(instr.rs())) & 0x1F; + u32 rt = regs.Read(instr.rt()); + s32 result = rt << sa; + regs.Write(instr.rd(), (s64)result); + } else { + panic("[JIT]: Implement non constant SLLV!"); + } } void JIT::sub(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s32 rt = regs.Read(instr.rt()); - s32 rs = regs.Read(instr.rs()); - s32 result = rs - rt; - if (check_signed_underflow(rs, rt, result)) { - regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC); + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s32 rt = regs.Read(instr.rt()); + s32 rs = regs.Read(instr.rs()); + s32 result = rs - rt; + if (check_signed_underflow(rs, rt, result)) { + regs.cop0.FireException(Cop0::ExceptionCode::Overflow, 0, regs.oldPC); + } else { + regs.Write(instr.rd(), result); + } } else { - regs.Write(instr.rd(), result); + panic("[JIT]: Implement non constant SUB!"); } - } else { - panic("[JIT]: Implement non constant SUB!"); - } } void JIT::subu(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u32 rs = regs.Read(instr.rs()); - u32 result = rs - rt; - regs.Write(instr.rd(), (s64)((s32)result)); - } else { - panic("[JIT]: Implement non constant SUBU!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u32 rt = regs.Read(instr.rt()); + u32 rs = regs.Read(instr.rs()); + u32 result = rs - rt; + regs.Write(instr.rd(), (s64)((s32)result)); + } else { + panic("[JIT]: Implement non constant SUBU!"); + } } void JIT::sra(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - s64 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRA!"); - } + if (regs.IsRegConstant(instr.rt())) { + s64 rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + s32 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant SRA!"); + } } void JIT::srav(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - s64 rs = regs.Read(instr.rs()); - s64 rt = regs.Read(instr.rt()); - u8 sa = rs & 0x1f; - s32 result = rt >> sa; - regs.Write(instr.rd(), result); - } else { - panic("[JIT]: Implement non constant SRAV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + s64 rs = regs.Read(instr.rs()); + s64 rt = regs.Read(instr.rt()); + u8 sa = rs & 0x1f; + s32 result = rt >> sa; + regs.Write(instr.rd(), result); + } else { + panic("[JIT]: Implement non constant SRAV!"); + } } void JIT::srl(const Instruction instr) { - if (regs.IsRegConstant(instr.rt())) { - u32 rt = regs.Read(instr.rt()); - u8 sa = ((instr >> 6) & 0x1f); - u32 result = rt >> sa; - regs.Write(instr.rd(), (s32)result); - } else { - panic("[JIT]: Implement non constant SRL!"); - } + if (regs.IsRegConstant(instr.rt())) { + u32 rt = regs.Read(instr.rt()); + u8 sa = ((instr >> 6) & 0x1f); + u32 result = rt >> sa; + regs.Write(instr.rd(), (s32)result); + } else { + panic("[JIT]: Implement non constant SRL!"); + } } void JIT::sw(const Instruction instr) { - u32 physical; - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - 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); - panic("[JIT]: Unhandled ADES exception in SW!"); - return; + u32 physical; + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (Core::IsAddressError(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); + panic("[JIT]: Unhandled ADES exception in SW!"); + return; + } + + 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); + panic("[JIT]: Unhandled TLBS exception in SW!"); + } else { + code.mov(code.ARG2, physical); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + } + + return; } - 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); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); + if (regs.IsRegConstant(instr.rs())) { + const s16 offset = instr; + const u64 address = regs.Read(instr.rs()) + offset; + if (Core::IsAddressError(0b11, address)) { + // regs.cop0.HandleTLBException(address); + // regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorStore, 0, regs.oldPC); + panic("[JIT]: Unhandled ADES exception in SW!"); + return; + } + + 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); + panic("[JIT]: Unhandled TLBS exception in SW!"); + } else { + code.mov(code.ARG2, physical); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + } + + return; } - return; - } + if (regs.IsRegConstant(instr.rt())) { + const s16 offset = instr; + code.mov(code.ARG2, Cop0::STORE); - if (regs.IsRegConstant(instr.rs())) { - 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); - panic("[JIT]: Unhandled ADES exception in SW!"); - return; + regs.Read(instr.rs(), code.ARG3); + code.add(code.ARG3, offset); + + code.mov(code.ARG4, reinterpret_cast(&physical)); + emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); + + code.mov(code.ARG2, code.qword[code.ARG4]); + regs.Read(instr.rt(), code.ARG3); + emitMemberFunctionCall(&Mem::WriteJIT, &mem); + + return; } - 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); - panic("[JIT]: Unhandled TLBS exception in SW!"); - } else { - code.mov(code.ARG2, physical); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); - } - - return; - } - - if (regs.IsRegConstant(instr.rt())) { const s16 offset = instr; code.mov(code.ARG2, Cop0::STORE); @@ -1398,70 +1416,54 @@ void JIT::sw(const Instruction instr) { code.mov(code.ARG2, code.qword[code.ARG4]); regs.Read(instr.rt(), code.ARG3); emitMemberFunctionCall(&Mem::WriteJIT, &mem); - - return; - } - - const s16 offset = instr; - code.mov(code.ARG2, Cop0::STORE); - - regs.Read(instr.rs(), code.ARG3); - code.add(code.ARG3, offset); - - code.mov(code.ARG4, reinterpret_cast(&physical)); - emitMemberFunctionCall(&Cop0::MapVAddr, ®s.cop0); - - code.mov(code.ARG2, code.qword[code.ARG4]); - regs.Read(instr.rt(), code.ARG3); - emitMemberFunctionCall(&Mem::WriteJIT, &mem); } void JIT::srlv(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - u8 sa = (regs.Read(instr.rs()) & 0x1F); - u32 rt = regs.Read(instr.rt()); - s32 result = rt >> sa; - regs.Write(instr.rd(), (s64)result); - } else { - panic("[JIT]: Implement non constant SRLV!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + u8 sa = (regs.Read(instr.rs()) & 0x1F); + u32 rt = regs.Read(instr.rt()); + s32 result = rt >> sa; + regs.Write(instr.rd(), (s64)result); + } else { + panic("[JIT]: Implement non constant SRLV!"); + } } void JIT::or_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); - } else { - panic("[JIT]: Implement non constant OR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rs()) | regs.Read(instr.rt())); + } else { + panic("[JIT]: Implement non constant OR!"); + } } void JIT::ori(const Instruction instr) { - s64 imm = (u16)instr; - if (regs.IsRegConstant(instr.rs())) { - s64 result = imm | regs.Read(instr.rs()); - regs.Write(instr.rt(), result); - return; - } + s64 imm = (u16)instr; + if (regs.IsRegConstant(instr.rs())) { + s64 result = imm | regs.Read(instr.rs()); + regs.Write(instr.rt(), result); + return; + } - regs.Read(instr.rs(), code.SCR1); - code.or_(code.SCR1, imm); - regs.Write(instr.rt(), code.SCR1); + regs.Read(instr.rs(), code.SCR1); + code.or_(code.SCR1, imm); + regs.Write(instr.rt(), code.SCR1); } void JIT::xori(const Instruction instr) { - if (regs.IsRegConstant(instr.rs())) { - s64 imm = (u16)instr; - regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); - } else { - panic("[JIT]: Implement non constant XORI!"); - } + if (regs.IsRegConstant(instr.rs())) { + s64 imm = (u16)instr; + regs.Write(instr.rt(), regs.Read(instr.rs()) ^ imm); + } else { + panic("[JIT]: Implement non constant XORI!"); + } } void JIT::xor_(const Instruction instr) { - if (regs.IsRegConstant(instr.rs(), instr.rt())) { - regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); - } else { - panic("[JIT]: Implement non constant XOR!"); - } + if (regs.IsRegConstant(instr.rs(), instr.rt())) { + regs.Write(instr.rd(), regs.Read(instr.rt()) ^ regs.Read(instr.rs())); + } else { + panic("[JIT]: Implement non constant XOR!"); + } } } // namespace n64 diff --git a/src/backend/core/mmio/PI.cpp b/src/backend/core/mmio/PI.cpp index e3877a2..98726bb 100644 --- a/src/backend/core/mmio/PI.cpp +++ b/src/backend/core/mmio/PI.cpp @@ -8,593 +8,598 @@ namespace n64 { PI::PI() { Reset(); } void PI::Reset() { - dmaBusy = false; - ioBusy = false; - latch = 0; - dramAddr = 0; - cartAddr = 0; - rdLen = 0; - wrLen = 0; - piBsdDom1Lat = 0; - piBsdDom2Lat = 0; - piBsdDom1Pwd = 0; - piBsdDom2Pwd = 0; - piBsdDom1Pgs = 0; - piBsdDom2Pgs = 0; - piBsdDom1Rls = 0; - piBsdDom2Rls = 0; + dmaBusy = false; + ioBusy = false; + latch = 0; + dramAddr = 0; + cartAddr = 0; + rdLen = 0; + wrLen = 0; + piBsdDom1Lat = 0; + piBsdDom2Lat = 0; + piBsdDom1Pwd = 0; + piBsdDom2Pwd = 0; + piBsdDom1Pgs = 0; + piBsdDom2Pgs = 0; + piBsdDom1Rls = 0; + piBsdDom2Rls = 0; } bool PI::WriteLatch(u32 value) { - if (ioBusy) { - return false; - } else { - ioBusy = true; - latch = value; - Scheduler::GetInstance().EnqueueRelative(100, PI_BUS_WRITE_COMPLETE); - return true; - } + if (ioBusy) { + return false; + } else { + ioBusy = true; + latch = value; + Scheduler::GetInstance().EnqueueRelative(100, PI_BUS_WRITE_COMPLETE); + return true; + } } bool PI::ReadLatch() { - n64::Registers& regs = n64::Core::GetRegs(); - if (ioBusy) [[unlikely]] { - ioBusy = false; - regs.CpuStall(Scheduler::GetInstance().Remove(PI_BUS_WRITE_COMPLETE)); - return false; - } - return true; + n64::Registers ®s = n64::Core::GetRegs(); + if (ioBusy) [[unlikely]] { + ioBusy = false; + regs.CpuStall(Scheduler::GetInstance().Remove(PI_BUS_WRITE_COMPLETE)); + return false; + } + return true; } template <> auto PI::BusRead(u32 addr) -> u8 { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_REG: - panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_ROM: - warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " - "returning FF because it is not emulated", - addr); - return 0xFF; - case REGION_PI_SRAM: - return mem.BackupRead(addr - SREGION_PI_SRAM); - case REGION_PI_ROM: - { - // round to nearest 4 byte boundary, keeping old LSB - const u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; - if (index >= mem.rom.cart.size()) { - warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, - index, index, mem.rom.cart.size(), mem.rom.cart.size()); + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case REGION_PI_UNKNOWN: + mem.DumpRDRAM(); + panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " + "returning FF because it is not emulated (pc: 0x{:016X})", + addr, (u64)Core::GetRegs().oldPC); + case REGION_PI_64DD_REG: + panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_64DD_ROM: + warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " + "returning FF because it is not emulated", + addr); return 0xFF; - } - return mem.rom.cart[index]; + case REGION_PI_SRAM: + return mem.BackupRead(addr - SREGION_PI_SRAM); + case REGION_PI_ROM: + { + // round to nearest 4 byte boundary, keeping old LSB + const u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; + if (index >= mem.rom.cart.size()) { + warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, + index, index, mem.rom.cart.size(), mem.rom.cart.size()); + return 0xFF; + } + return mem.rom.cart[index]; + } + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> auto PI::BusRead(u32 addr) -> u8 { - n64::Mem& mem = n64::Core::GetMem(); - if (!ReadLatch()) [[unlikely]] { - return latch >> 24; - } + n64::Mem &mem = n64::Core::GetMem(); + if (!ReadLatch()) [[unlikely]] { + return latch >> 24; + } - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_REG: - panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_ROM: - warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " - "returning FF because it is not emulated", - addr); - return 0xFF; - case REGION_PI_SRAM: - return mem.BackupRead(addr - SREGION_PI_SRAM); - case REGION_PI_ROM: - { - addr = (addr + 2) & ~2; - // round to nearest 4 byte boundary, keeping old LSB - const u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; - if (index >= mem.rom.cart.size()) { - warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, - index, index, mem.rom.cart.size(), mem.rom.cart.size()); + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_64DD_REG: + panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_64DD_ROM: + warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " + "returning FF because it is not emulated", + addr); return 0xFF; - } - return mem.rom.cart[index]; + case REGION_PI_SRAM: + return mem.BackupRead(addr - SREGION_PI_SRAM); + case REGION_PI_ROM: + { + addr = (addr + 2) & ~2; + // round to nearest 4 byte boundary, keeping old LSB + const u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM; + if (index >= mem.rom.cart.size()) { + warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, + index, index, mem.rom.cart.size(), mem.rom.cart.size()); + return 0xFF; + } + return mem.rom.cart[index]; + } + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> void PI::BusWrite(u32 addr, u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); - case REGION_PI_64DD_REG: - if (addr == 0x05000020) { - fprintf(stderr, "%c", val); - } else { - warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); + case REGION_PI_64DD_REG: + if (addr == 0x05000020) { + fprintf(stderr, "%c", val); + } else { + warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr); + } + break; + case REGION_PI_64DD_ROM: + panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); + case REGION_PI_SRAM: + mem.BackupWrite(addr - SREGION_PI_SRAM, val); + break; + case REGION_PI_ROM: + warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); + break; + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - break; - case REGION_PI_64DD_ROM: - panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); - case REGION_PI_SRAM: - mem.BackupWrite(addr - SREGION_PI_SRAM, val); - break; - case REGION_PI_ROM: - warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); - break; - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> void PI::BusWrite(u32 addr, u32 val) { - u8 latch_shift = 24 - (addr & 1) * 8; + u8 latch_shift = 24 - (addr & 1) * 8; - if (!WriteLatch(val << latch_shift) && addr != 0x05000020) [[unlikely]] { - return; - } + if (!WriteLatch(val << latch_shift) && addr != 0x05000020) [[unlikely]] { + return; + } - BusWrite(addr, val); + BusWrite(addr, val); } template <> auto PI::BusRead(u32 addr) -> u16 { - n64::Mem& mem = n64::Core::GetMem(); - if (!ReadLatch()) [[unlikely]] { - return latch >> 16; - } + n64::Mem &mem = n64::Core::GetMem(); + if (!ReadLatch()) [[unlikely]] { + return latch >> 16; + } - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_REG: - panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_64DD_ROM: - panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " - "returning FF because it is not emulated", - addr); - case REGION_PI_SRAM: - panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); - case REGION_PI_ROM: - { - addr = (addr + 2) & ~3; - const u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM; - if (index > mem.rom.cart.size() - 1) { - panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index); - } - return ircolib::ReadAccess(mem.rom.cart, index); + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_64DD_REG: + panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_64DD_ROM: + panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " + "returning FF because it is not emulated", + addr); + case REGION_PI_SRAM: + panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); + case REGION_PI_ROM: + { + addr = (addr + 2) & ~3; + const u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM; + if (index > mem.rom.cart.size() - 1) { + panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, + index); + } + return ircolib::ReadAccess(mem.rom.cart, index); + } + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> auto PI::BusRead(u32 addr) -> u16 { - return BusRead(addr); + return BusRead(addr); } template <> void PI::BusWrite(u32 addr, u32 val) { - if (!WriteLatch(val << 16)) [[unlikely]] { - return; - } + if (!WriteLatch(val << 16)) [[unlikely]] { + return; + } - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); - case REGION_PI_64DD_REG: - panic("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", - val, addr); - case REGION_PI_64DD_ROM: - panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); - case REGION_PI_SRAM: - panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); - case REGION_PI_ROM: - warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); - break; - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); + case REGION_PI_64DD_REG: + panic("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", + val, addr); + case REGION_PI_64DD_ROM: + panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); + case REGION_PI_SRAM: + panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); + case REGION_PI_ROM: + warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); + break; + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); + } } template <> void PI::BusWrite(u32 addr, u32 val) { - BusWrite(addr, val); + BusWrite(addr, val); } template <> auto PI::BusRead(u32 addr) -> u32 { - n64::Mem& mem = n64::Core::GetMem(); - if (!ReadLatch()) [[unlikely]] { - return latch; - } + n64::Mem &mem = n64::Core::GetMem(); + if (!ReadLatch()) [[unlikely]] { + return latch; + } - switch (addr) { - case REGION_PI_UNKNOWN: - warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " - "returning FF because it is not emulated", - addr); - return 0xFF; - case REGION_PI_64DD_REG: - warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " - "returning FF because it is not emulated", - addr); - return 0xFF; - case REGION_PI_64DD_ROM: - warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " - "returning FF because it is not emulated", - addr); - return 0xFF; - case REGION_PI_SRAM: - return mem.BackupRead(addr); - case REGION_PI_ROM: - { - const u32 index = addr - SREGION_PI_ROM; - if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word - switch (addr) { - case REGION_CART_ISVIEWER_BUFFER: - return std::byteswap(ircolib::ReadAccess(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER)); - case CART_ISVIEWER_FLUSH: - panic("Read from ISViewer flush!"); - default: - break; + switch (addr) { + case REGION_PI_UNKNOWN: + warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, " + "returning FF because it is not emulated", + addr); + return 0xFF; + case REGION_PI_64DD_REG: + warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, " + "returning FF because it is not emulated", + addr); + return 0xFF; + case REGION_PI_64DD_ROM: + warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, " + "returning FF because it is not emulated", + addr); + return 0xFF; + case REGION_PI_SRAM: + return mem.BackupRead(addr); + case REGION_PI_ROM: + { + const u32 index = addr - SREGION_PI_ROM; + if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word + switch (addr) { + case REGION_CART_ISVIEWER_BUFFER: + return std::byteswap( + ircolib::ReadAccess(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER)); + case CART_ISVIEWER_FLUSH: + panic("Read from ISViewer flush!"); + default: + break; + } + warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index); + return 0; + } + + return ircolib::ReadAccess(mem.rom.cart, index); } - warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index); - return 0; - } - - return ircolib::ReadAccess(mem.rom.cart, index); + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> auto PI::BusRead(u32 addr) -> u32 { - return BusRead(addr); + return BusRead(addr); } template <> void PI::BusWrite(u32 addr, u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case REGION_PI_UNKNOWN: - if (!WriteLatch(val)) [[unlikely]] { - return; - } - warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); - return; - case REGION_PI_64DD_REG: - if (!WriteLatch(val)) [[unlikely]] { - return; - } - warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", - val, addr); - return; - case REGION_PI_64DD_ROM: - if (!WriteLatch(val)) [[unlikely]] { - return; - } - warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); - return; - case REGION_PI_SRAM: - if (!WriteLatch(val)) [[unlikely]] { - return; - } - mem.BackupWrite(addr - SREGION_PI_SRAM, val); - return; - case REGION_PI_ROM: + n64::Mem &mem = n64::Core::GetMem(); switch (addr) { - case REGION_CART_ISVIEWER_BUFFER: - ircolib::WriteAccess(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER, std::byteswap(val)); - break; - case CART_ISVIEWER_FLUSH: - { - if (val < CART_ISVIEWER_SIZE) { - std::string message(val, 0); - std::copy_n(mem.isviewer.begin(), val, message.begin()); - mem.isviewer_sink << message; - mem.isviewer_sink.flush(); - } else { - panic("ISViewer buffer size is emulated at {} bytes, but received a flush command for {} bytes!", - CART_ISVIEWER_SIZE, val); + case REGION_PI_UNKNOWN: + if (!WriteLatch(val)) [[unlikely]] { + return; } - break; - } - default: - if (!WriteLatch(val)) [[unlikely]] { - warn("Couldn't latch PI bus, ignoring write to REGION_PI_ROM"); + warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); return; - } - warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); + case REGION_PI_64DD_REG: + if (!WriteLatch(val)) [[unlikely]] { + return; + } + warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", + val, addr); + return; + case REGION_PI_64DD_ROM: + if (!WriteLatch(val)) [[unlikely]] { + return; + } + warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); + return; + case REGION_PI_SRAM: + if (!WriteLatch(val)) [[unlikely]] { + return; + } + mem.BackupWrite(addr - SREGION_PI_SRAM, val); + return; + case REGION_PI_ROM: + switch (addr) { + case REGION_CART_ISVIEWER_BUFFER: + ircolib::WriteAccess(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER, std::byteswap(val)); + break; + case CART_ISVIEWER_FLUSH: + { + if (val < CART_ISVIEWER_SIZE) { + std::string message(val, 0); + std::copy_n(mem.isviewer.begin(), val, message.begin()); + mem.isviewer_sink << message; + mem.isviewer_sink.flush(); + } else { + panic("ISViewer buffer size is emulated at {} bytes, but received a flush command for {} bytes!", + CART_ISVIEWER_SIZE, val); + } + break; + } + default: + if (!WriteLatch(val)) [[unlikely]] { + warn("Couldn't latch PI bus, ignoring write to REGION_PI_ROM"); + return; + } + warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); + } + return; + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - return; - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> void PI::BusWrite(u32 addr, u32 val) { - BusWrite(addr, val); + BusWrite(addr, val); } template <> auto PI::BusRead(u32 addr) -> u64 { - n64::Mem& mem = n64::Core::GetMem(); - if (!ReadLatch()) [[unlikely]] { - return static_cast(latch) << 32; - } + n64::Mem &mem = n64::Core::GetMem(); + if (!ReadLatch()) [[unlikely]] { + return static_cast(latch) << 32; + } - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr); - case REGION_PI_64DD_REG: - panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr); - case REGION_PI_64DD_ROM: - panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr); - case REGION_PI_SRAM: - panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); - case REGION_PI_ROM: - { - const u32 index = addr - SREGION_PI_ROM; - if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword - panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index); - } - return ircolib::ReadAccess(mem.rom.cart, index); + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr); + case REGION_PI_64DD_REG: + panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr); + case REGION_PI_64DD_ROM: + panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr); + case REGION_PI_SRAM: + panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr); + case REGION_PI_ROM: + { + const u32 index = addr - SREGION_PI_ROM; + if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword + panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, + index); + } + return ircolib::ReadAccess(mem.rom.cart, index); + } + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); } - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } } template <> auto PI::BusRead(u32 addr) -> u64 { - return BusRead(addr); + return BusRead(addr); } template <> void PI::BusWrite(u32 addr, u64 val) { - if (!WriteLatch(val >> 32)) [[unlikely]] { - return; - } + if (!WriteLatch(val >> 32)) [[unlikely]] { + return; + } - switch (addr) { - case REGION_PI_UNKNOWN: - panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); - case REGION_PI_64DD_REG: - panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", val, addr); - case REGION_PI_64DD_ROM: - panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); - case REGION_PI_SRAM: - panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); - case REGION_PI_ROM: - warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); - break; - default: - panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); - } + switch (addr) { + case REGION_PI_UNKNOWN: + panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr); + case REGION_PI_64DD_REG: + panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", val, addr); + case REGION_PI_64DD_ROM: + panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr); + case REGION_PI_SRAM: + panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr); + case REGION_PI_ROM: + warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr); + break; + default: + panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr); + } } template <> void PI::BusWrite(u32 addr, u64 val) { - BusWrite(addr, val); + BusWrite(addr, val); } auto PI::Read(u32 addr) const -> u32 { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case 0x04600000: - return dramAddr & 0x00FFFFFE; - case 0x04600004: - return cartAddr & 0xFFFFFFFE; - case 0x04600008: - return rdLen; - case 0x0460000C: - return wrLen; - case 0x04600010: - { - u32 value = 0; - value |= (dmaBusy << 0); // Is PI DMA active? - value |= (ioBusy << 1); // Is PI IO busy? - value |= (0 << 2); // PI DMA error? - value |= (mem.mmio.mi.intr.pi << 3); // PI interrupt? - return value; + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case 0x04600000: + return dramAddr & 0x00FFFFFE; + case 0x04600004: + return cartAddr & 0xFFFFFFFE; + case 0x04600008: + return rdLen; + case 0x0460000C: + return wrLen; + case 0x04600010: + { + u32 value = 0; + value |= (dmaBusy << 0); // Is PI DMA active? + value |= (ioBusy << 1); // Is PI IO busy? + value |= (0 << 2); // PI DMA error? + value |= (mem.mmio.mi.intr.pi << 3); // PI interrupt? + return value; + } + case 0x04600034: + case 0x04600014: + return piBsdDom1Lat; + case 0x04600018: + return piBsdDom1Pwd; + case 0x0460001C: + return piBsdDom1Pgs; + case 0x04600020: + return piBsdDom1Rls; + case 0x04600024: + return piBsdDom2Lat; + case 0x04600028: + return piBsdDom2Pwd; + case 0x0460002C: + return piBsdDom2Pgs; + case 0x04600030: + return piBsdDom2Rls; + default: + panic("Unhandled PI[{:08X}] read", addr); } - case 0x04600014: - return piBsdDom1Lat; - case 0x04600018: - return piBsdDom1Pwd; - case 0x0460001C: - return piBsdDom1Pgs; - case 0x04600020: - return piBsdDom1Rls; - case 0x04600024: - return piBsdDom2Lat; - case 0x04600028: - return piBsdDom2Pwd; - case 0x0460002C: - return piBsdDom2Pgs; - case 0x04600030: - return piBsdDom2Rls; - default: - panic("Unhandled PI[{:08X}] read", addr); - } } u8 PI::GetDomain(const u32 address) { - switch (address) { - case REGION_PI_UNKNOWN: - case REGION_PI_64DD_ROM: - case REGION_PI_ROM: - return 1; - case REGION_PI_64DD_REG: - case REGION_PI_SRAM: - return 2; - default: - panic("Unknown PI domain for address {:08X}!", address); - } + switch (address) { + case REGION_PI_UNKNOWN: + case REGION_PI_64DD_ROM: + case REGION_PI_ROM: + return 1; + case REGION_PI_64DD_REG: + case REGION_PI_SRAM: + return 2; + default: + panic("Unknown PI domain for address {:08X}!", address); + } } u32 PI::AccessTiming(const u8 domain, const u32 length) const { - uint32_t cycles = 0; - uint32_t latency = 0; - uint32_t pulse_width = 0; - uint32_t release = 0; - uint32_t page_size = 0; + uint32_t cycles = 0; + uint32_t latency = 0; + uint32_t pulse_width = 0; + uint32_t release = 0; + uint32_t page_size = 0; - switch (domain) { - case 1: - latency = piBsdDom1Lat + 1; - pulse_width = piBsdDom1Pwd + 1; - release = piBsdDom1Rls + 1; - page_size = 1 << (piBsdDom1Pgs + 2); - break; - case 2: - latency = piBsdDom2Lat + 1; - pulse_width = piBsdDom2Pwd + 1; - release = piBsdDom2Rls + 1; - page_size = 1 << (piBsdDom2Pgs + 2); - break; - default: - panic("Unknown PI domain: {}\n", domain); - } + switch (domain) { + case 1: + latency = piBsdDom1Lat + 1; + pulse_width = piBsdDom1Pwd + 1; + release = piBsdDom1Rls + 1; + page_size = 1 << (piBsdDom1Pgs + 2); + break; + case 2: + latency = piBsdDom2Lat + 1; + pulse_width = piBsdDom2Pwd + 1; + release = piBsdDom2Rls + 1; + page_size = 1 << (piBsdDom2Pgs + 2); + break; + default: + panic("Unknown PI domain: {}\n", domain); + } - const uint32_t pages = static_cast(ceil(static_cast(length) / static_cast(page_size))); + const uint32_t pages = static_cast(ceil(static_cast(length) / static_cast(page_size))); - cycles += (14 + latency) * pages; - cycles += (pulse_width + release) * (length / 2); - cycles += 5 * pages; - return cycles * 1.5; // Converting RCP clock speed to CPU clock speed + cycles += (14 + latency) * pages; + cycles += (pulse_width + release) * (length / 2); + cycles += 5 * pages; + return cycles * 1.5; // Converting RCP clock speed to CPU clock speed } // rdram -> cart template <> void PI::DMA() { - n64::Mem& mem = n64::Core::GetMem(); - const s32 len = rdLen + 1; - trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); + n64::Mem &mem = n64::Core::GetMem(); + const s32 len = rdLen + 1; + trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr); - for (int i = 0; i < len; i++) { - BusWrite(cartAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); - } - - dramAddr += len; - dramAddr = (dramAddr + 7) & ~7; - cartAddr += len; - if (cartAddr & 1) - cartAddr += 1; + for (int i = 0; i < len; i++) { + BusWrite(cartAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); + } - dmaBusy = true; - - u64 completo = AccessTiming(GetDomain(cartAddr), len); - trace("Will complete in {} cycles", completo); - Scheduler::GetInstance().EnqueueRelative(completo, PI_DMA_COMPLETE); + dramAddr += len; + dramAddr = (dramAddr + 7) & ~7; + cartAddr += len; + if (cartAddr & 1) + cartAddr += 1; + + dmaBusy = true; + + u64 completo = AccessTiming(GetDomain(cartAddr), len); + trace("Will complete in {} cycles", completo); + Scheduler::GetInstance().EnqueueRelative(completo, PI_DMA_COMPLETE); } // cart -> rdram template <> void PI::DMA() { - n64::Mem& mem = n64::Core::GetMem(); - const s32 len = wrLen + 1; - trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); + n64::Mem &mem = n64::Core::GetMem(); + const s32 len = wrLen + 1; + trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr); - if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { - cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb-1)) << 1); - } + if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < (CART_REGION_START_2_2 + 1_mb)) { + cartAddr = SREGION_PI_SRAM | ((cartAddr & (1_mb - 1)) << 1); + } - for (u32 i = 0; i < len; i++) { - mem.mmio.rdp.WriteRDRAM(dramAddr + i, BusRead(cartAddr + i)); - } - dramAddr += len; - dramAddr = (dramAddr + 7) & ~7; - cartAddr += len; - if (cartAddr & 1) - cartAddr += 1; + for (u32 i = 0; i < len; i++) { + mem.mmio.rdp.WriteRDRAM(dramAddr + i, BusRead(cartAddr + i)); + } + dramAddr += len; + dramAddr = (dramAddr + 7) & ~7; + cartAddr += len; + if (cartAddr & 1) + cartAddr += 1; - dmaBusy = true; - u64 completo = AccessTiming(GetDomain(cartAddr), len); - trace("Will complete in {} cycles", completo); - Scheduler::GetInstance().EnqueueRelative(completo, PI_DMA_COMPLETE); + dmaBusy = true; + u64 completo = AccessTiming(GetDomain(cartAddr), len); + trace("Will complete in {} cycles", completo); + Scheduler::GetInstance().EnqueueRelative(completo, PI_DMA_COMPLETE); } void PI::Write(u32 addr, u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - MI &mi = mem.mmio.mi; - switch (addr) { - case 0x04600000: - dramAddr = val & 0x00FFFFFE; - break; - case 0x04600004: - cartAddr = val & 0xFFFFFFFE; - break; - case 0x04600008: - rdLen = val & 0x00FFFFFF; - DMA(); - break; - case 0x0460000C: - wrLen = val & 0x00FFFFFF; - DMA(); - break; - case 0x04600010: - if (val & 2) { - mi.InterruptLower(MI::Interrupt::PI); + n64::Mem &mem = n64::Core::GetMem(); + MI &mi = mem.mmio.mi; + switch (addr) { + case 0x04600000: + dramAddr = val & 0x00FFFFFE; + break; + case 0x04600004: + cartAddr = val & 0xFFFFFFFE; + break; + case 0x04600008: + rdLen = val & 0x00FFFFFF; + DMA(); + break; + case 0x0460000C: + wrLen = val & 0x00FFFFFF; + DMA(); + break; + case 0x04600010: + if (val & 2) { + mi.InterruptLower(MI::Interrupt::PI); + } + break; + case 0x04600014: + piBsdDom1Lat = val & 0xff; + break; + case 0x04600018: + piBsdDom1Pwd = val & 0xff; + break; + case 0x0460001C: + piBsdDom1Pgs = val & 0xff; + break; + case 0x04600020: + piBsdDom1Rls = val & 0xff; + break; + case 0x04600024: + piBsdDom2Lat = val & 0xff; + break; + case 0x04600028: + piBsdDom2Pwd = val & 0xff; + break; + case 0x0460002C: + piBsdDom2Pgs = val & 0xff; + break; + case 0x04600030: + piBsdDom2Rls = val & 0xff; + break; + default: + panic("Unhandled PI[{:08X}] write ({:08X})", val, addr); } - break; - case 0x04600014: - piBsdDom1Lat = val & 0xff; - break; - case 0x04600018: - piBsdDom1Pwd = val & 0xff; - break; - case 0x0460001C: - piBsdDom1Pgs = val & 0xff; - break; - case 0x04600020: - piBsdDom1Rls = val & 0xff; - break; - case 0x04600024: - piBsdDom2Lat = val & 0xff; - break; - case 0x04600028: - piBsdDom2Pwd = val & 0xff; - break; - case 0x0460002C: - piBsdDom2Pgs = val & 0xff; - break; - case 0x04600030: - piBsdDom2Rls = val & 0xff; - break; - default: - panic("Unhandled PI[{:08X}] write ({:08X})", val, addr); - } } } // namespace n64 diff --git a/src/backend/core/mmio/PI.hpp b/src/backend/core/mmio/PI.hpp index 66642db..2d6508a 100644 --- a/src/backend/core/mmio/PI.hpp +++ b/src/backend/core/mmio/PI.hpp @@ -3,35 +3,35 @@ namespace n64 { struct PI { - PI(); - void Reset(); - [[nodiscard]] auto Read(u32) const -> u32; - void Write(u32, u32); + PI(); + void Reset(); + [[nodiscard]] auto Read(u32) const -> u32; + void Write(u32, u32); - template - void BusWrite(u32, u32); - template - void BusWrite(u32, u64); + template + void BusWrite(u32, u32); + template + void BusWrite(u32, u64); - template - auto BusRead(u32) -> T; + template + auto BusRead(u32) -> T; - bool ReadLatch(); - bool WriteLatch(u32 val); + bool ReadLatch(); + bool WriteLatch(u32 val); - static u8 GetDomain(u32 address); - [[nodiscard]] u32 AccessTiming(u8 domain, u32 length) const; - bool dmaBusy{}, ioBusy{}; - u32 latch{}; - u32 dramAddr{}, cartAddr{}; - u32 rdLen{}, wrLen{}; - u32 piBsdDom1Lat{}, piBsdDom2Lat{}; - u32 piBsdDom1Pwd{}, piBsdDom2Pwd{}; - u32 piBsdDom1Pgs{}, piBsdDom2Pgs{}; - u32 piBsdDom1Rls{}, piBsdDom2Rls{}; + static u8 GetDomain(u32 address); + [[nodiscard]] u32 AccessTiming(u8 domain, u32 length) const; + bool dmaBusy{}, ioBusy{}; + u32 latch{}; + u32 dramAddr{}, cartAddr{}; + u32 rdLen{}, wrLen{}; + u32 piBsdDom1Lat{}, piBsdDom2Lat{}; + u32 piBsdDom1Pwd{}, piBsdDom2Pwd{}; + u32 piBsdDom1Pgs{}, piBsdDom2Pgs{}; + u32 piBsdDom1Rls{}, piBsdDom2Rls{}; -private: - template - void DMA(); + private: + template + void DMA(); }; } // namespace n64 diff --git a/src/backend/core/mmio/SI.cpp b/src/backend/core/mmio/SI.cpp index 9bedbd8..9a35327 100644 --- a/src/backend/core/mmio/SI.cpp +++ b/src/backend/core/mmio/SI.cpp @@ -5,91 +5,91 @@ namespace n64 { SI::SI() { Reset(); } void SI::Reset() { - status.raw = 0; - dramAddr = 0; - pifAddr = 0; - toDram = false; - pif.Reset(); + status.raw = 0; + dramAddr = 0; + pifAddr = 0; + toDram = false; + pif.Reset(); } auto SI::Read(u32 addr) const -> u32 { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case 0x04800000: - return dramAddr; - case 0x04800004: - case 0x04800010: - return pifAddr; - case 0x0480000C: - return 0; - case 0x04800018: - { - u32 val = 0; - val |= status.dmaBusy; - val |= (0 << 1); - val |= (0 << 3); - val |= (mem.mmio.mi.intr.si << 12); - return val; + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case 0x04800000: + return dramAddr; + case 0x04800004: + case 0x04800010: + return pifAddr; + case 0x0480000C: + return 0; + case 0x04800018: + { + u32 val = 0; + val |= status.dmaBusy; + val |= (0 << 1); + val |= (0 << 3); + val |= (mem.mmio.mi.intr.si << 12); + return val; + } + default: + panic("Unhandled SI[{:08X}] read", addr); } - default: - panic("Unhandled SI[{:08X}] read", addr); - } } // pif -> rdram template <> void SI::DMA() { - n64::Mem& mem = n64::Core::GetMem(); - pif.ProcessCommands(); - for (int i = 0; i < 64; i++) { - mem.mmio.rdp.WriteRDRAM(dramAddr + i, pif.Read(pifAddr + i)); - } - trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr); + n64::Mem &mem = n64::Core::GetMem(); + pif.ProcessCommands(); + for (int i = 0; i < 64; i++) { + mem.mmio.rdp.WriteRDRAM(dramAddr + i, pif.Read(pifAddr + i)); + } + trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr); } // rdram -> pif template <> void SI::DMA() { - n64::Mem& mem = n64::Core::GetMem(); - for (int i = 0; i < 64; i++) { - pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); - } - trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr); + n64::Mem &mem = n64::Core::GetMem(); + for (int i = 0; i < 64; i++) { + pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM(dramAddr + i)); + } + trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr); } void SI::DMA() { - n64::Mem& mem = n64::Core::GetMem(); - status.dmaBusy = false; - if (toDram) - DMA(); - else - DMA(); - mem.mmio.mi.InterruptRaise(MI::Interrupt::SI); + n64::Mem &mem = n64::Core::GetMem(); + status.dmaBusy = false; + if (toDram) + DMA(); + else + DMA(); + mem.mmio.mi.InterruptRaise(MI::Interrupt::SI); } void SI::Write(u32 addr, u32 val) { - n64::Mem& mem = n64::Core::GetMem(); - switch (addr) { - case 0x04800000: - dramAddr = val & RDRAM_DSIZE; - break; - case 0x04800004: - pifAddr = val & 0x1FFFFFFF; - status.dmaBusy = true; - toDram = true; - Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA); - break; - case 0x04800010: - pifAddr = val & 0x1FFFFFFF; - status.dmaBusy = true; - toDram = false; - Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA); - break; - case 0x04800018: - mem.mmio.mi.InterruptLower(MI::Interrupt::SI); - break; - default: - panic("Unhandled SI[{:08X}] write ({:08X})", addr, val); - } + n64::Mem &mem = n64::Core::GetMem(); + switch (addr) { + case 0x04800000: + dramAddr = val & RDRAM_DSIZE; + break; + case 0x04800004: + pifAddr = val & 0x1FFFFFFF; + status.dmaBusy = true; + toDram = true; + Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA); + break; + case 0x04800010: + pifAddr = val & 0x1FFFFFFF; + status.dmaBusy = true; + toDram = false; + Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA); + break; + case 0x04800018: + mem.mmio.mi.InterruptLower(MI::Interrupt::SI); + break; + default: + panic("Unhandled SI[{:08X}] write ({:08X})", addr, val); + } } } // namespace n64 diff --git a/src/backend/core/mmio/SI.hpp b/src/backend/core/mmio/SI.hpp index 46d639f..ed57101 100644 --- a/src/backend/core/mmio/SI.hpp +++ b/src/backend/core/mmio/SI.hpp @@ -4,33 +4,32 @@ #include namespace n64 { +struct SI { + SI(); + void Reset(); + [[nodiscard]] auto Read(u32) const -> u32; + void Write(u32, u32); + template + void DMA(); + void DMA(); -union SIStatus { - u32 raw{}; - struct { - unsigned dmaBusy : 1; - unsigned ioBusy : 1; - unsigned reserved : 1; - unsigned dmaErr : 1; - unsigned : 8; - unsigned intr : 1; - }; -}; + union Status { + u32 raw{}; + struct { + unsigned dmaBusy : 1; + unsigned ioBusy : 1; + unsigned reserved : 1; + unsigned dmaErr : 1; + unsigned : 8; + unsigned intr : 1; + }; + }; -struct SI { - SI(); - void Reset(); - [[nodiscard]] auto Read(u32) const -> u32; - void Write(u32, u32); - template - void DMA(); - void DMA(); - - bool toDram = false; - SIStatus status{}; - u32 dramAddr{}; - u32 pifAddr{}; - PIF pif; + bool toDram = false; + Status status{}; + u32 dramAddr{}; + u32 pifAddr{}; + PIF pif; }; #define SI_DMA_DELAY (65536 * 2) diff --git a/src/backend/core/registers/Cop0.cpp b/src/backend/core/registers/Cop0.cpp index e1dba7e..16f3ba4 100644 --- a/src/backend/core/registers/Cop0.cpp +++ b/src/backend/core/registers/Cop0.cpp @@ -5,547 +5,571 @@ namespace n64 { Cop0::Cop0() { Reset(); } void Cop0::Reset() { - cause.raw = 0xB000007C; - status.raw = 0; - status.cu0 = 1; - status.cu1 = 1; - status.fr = 1; - PRId = 0x00000B22; - Config = 0x7006E463; - EPC = 0xFFFFFFFFFFFFFFFFll; - ErrorEPC = 0xFFFFFFFFFFFFFFFFll; - wired = 0; - index.raw = 63; - badVaddr = 0xFFFFFFFFFFFFFFFF; + cause.raw = 0xB000007C; + status.raw = 0; + status.cu0 = 1; + status.cu1 = 1; + status.fr = 1; + PRId = 0x00000B22; + Config = 0x7006E463; + EPC = 0xFFFFFFFFFFFFFFFFll; + ErrorEPC = 0xFFFFFFFFFFFFFFFFll; + wired = 0; + index.raw = 63; + badVaddr = 0xFFFFFFFFFFFFFFFF; - kernelMode = {true}; - supervisorMode = {false}; - userMode = {false}; - is64BitAddressing = {false}; - llbit = {}; + kernelMode = {true}; + supervisorMode = {false}; + userMode = {false}; + is64BitAddressing = {false}; + llbit = {}; - pageMask = {}; - entryHi = {}; - entryLo0 = {}, entryLo1 = {}; - context = {}; - wired = {}, r7 = {}; - count = {}; - compare = {}; - LLAddr = {}, WatchLo = {}, WatchHi = {}; - xcontext = {}; - r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {}; - ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {}; - ErrorEPC = {}; - r31 = {}; - memset(tlb, 0, sizeof(TLBEntry) * 32); - tlbError = NONE; - openbus = {}; + pageMask = {}; + entryHi = {}; + entryLo0 = {}, entryLo1 = {}; + context = {}; + wired = {}, r7 = {}; + count = {}; + compare = {}; + LLAddr = {}, WatchLo = {}, WatchHi = {}; + xcontext = {}; + r21 = {}, r22 = {}, r23 = {}, r24 = {}, r25 = {}; + ParityError = {}, CacheError = {}, tagLo = {}, TagHi = {}; + ErrorEPC = {}; + r31 = {}; + memset(tlb, 0, sizeof(TLBEntry) * 32); + tlbError = NONE; + openbus = {}; } u32 Cop0::GetReg32(const u8 addr) { - switch (addr) { - case COP0_REG_INDEX: - return index.raw & INDEX_MASK; - case COP0_REG_RANDOM: - return GetRandom(); - case COP0_REG_ENTRYLO0: - return entryLo0.raw; - case COP0_REG_ENTRYLO1: - return entryLo1.raw; - case COP0_REG_CONTEXT: - return context.raw; - case COP0_REG_PAGEMASK: - return pageMask.raw; - case COP0_REG_WIRED: - return wired; - case COP0_REG_BADVADDR: - return badVaddr; - case COP0_REG_COUNT: - return GetCount(); - case COP0_REG_ENTRYHI: - return entryHi.raw; - case COP0_REG_COMPARE: - return compare; - case COP0_REG_STATUS: - return status.raw; - case COP0_REG_CAUSE: - return cause.raw; - case COP0_REG_EPC: - return EPC; - case COP0_REG_PRID: - return PRId; - case COP0_REG_CONFIG: - return Config; - case COP0_REG_LLADDR: - return LLAddr; - case COP0_REG_WATCHLO: - return WatchLo; - case COP0_REG_WATCHHI: - return WatchHi; - case COP0_REG_XCONTEXT: - return xcontext.raw; - case COP0_REG_PARITY_ERR: - return ParityError; - case COP0_REG_CACHE_ERR: - return CacheError; - case COP0_REG_TAGLO: - return TagLo; - case COP0_REG_TAGHI: - return TagHi; - case COP0_REG_ERROREPC: - return ErrorEPC; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - return openbus; - default: - panic("Unsupported word read from COP0 register {}", addr); - } + switch (addr) { + case COP0_REG_INDEX: + return index.raw & INDEX_MASK; + case COP0_REG_RANDOM: + return GetRandom(); + case COP0_REG_ENTRYLO0: + return entryLo0.raw; + case COP0_REG_ENTRYLO1: + return entryLo1.raw; + case COP0_REG_CONTEXT: + return context.raw; + case COP0_REG_PAGEMASK: + return pageMask.raw; + case COP0_REG_WIRED: + return wired; + case COP0_REG_BADVADDR: + return badVaddr; + case COP0_REG_COUNT: + return GetCount(); + case COP0_REG_ENTRYHI: + return entryHi.raw; + case COP0_REG_COMPARE: + return compare; + case COP0_REG_STATUS: + return status.raw; + case COP0_REG_CAUSE: + return cause.raw; + case COP0_REG_EPC: + return EPC; + case COP0_REG_PRID: + return PRId; + case COP0_REG_CONFIG: + return Config; + case COP0_REG_LLADDR: + return LLAddr; + case COP0_REG_WATCHLO: + return WatchLo; + case COP0_REG_WATCHHI: + return WatchHi; + case COP0_REG_XCONTEXT: + return xcontext.raw; + case COP0_REG_PARITY_ERR: + return ParityError; + case COP0_REG_CACHE_ERR: + return CacheError; + case COP0_REG_TAGLO: + return tagLo.raw; + case COP0_REG_TAGHI: + return TagHi; + case COP0_REG_ERROREPC: + return ErrorEPC; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + return openbus; + default: + panic("Unsupported word read from COP0 register {}", addr); + } } u64 Cop0::GetReg64(const u8 addr) const { - switch (addr) { - case COP0_REG_ENTRYLO0: - return entryLo0.raw; - case COP0_REG_ENTRYLO1: - return entryLo1.raw; - case COP0_REG_CONTEXT: - return context.raw; - case COP0_REG_BADVADDR: - return badVaddr; - case COP0_REG_ENTRYHI: - return entryHi.raw; - case COP0_REG_STATUS: - return status.raw; - case COP0_REG_EPC: - return EPC; - case COP0_REG_PRID: - return PRId; - case COP0_REG_LLADDR: - return LLAddr; - case COP0_REG_XCONTEXT: - return xcontext.raw & 0xFFFFFFFFFFFFFFF0; - case COP0_REG_ERROREPC: - return ErrorEPC; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - return openbus; - default: - panic("Unsupported dword read from COP0 register {}", addr); - } + switch (addr) { + case COP0_REG_ENTRYLO0: + return entryLo0.raw; + case COP0_REG_ENTRYLO1: + return entryLo1.raw; + case COP0_REG_CONTEXT: + return context.raw; + case COP0_REG_BADVADDR: + return badVaddr; + case COP0_REG_ENTRYHI: + return entryHi.raw; + case COP0_REG_STATUS: + return status.raw; + case COP0_REG_EPC: + return EPC; + case COP0_REG_PRID: + return PRId; + case COP0_REG_LLADDR: + return LLAddr; + case COP0_REG_XCONTEXT: + return xcontext.raw & 0xFFFFFFFFFFFFFFF0; + case COP0_REG_ERROREPC: + return ErrorEPC; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + return openbus; + default: + panic("Unsupported dword read from COP0 register {}", addr); + } } void Cop0::SetReg32(const u8 addr, const u32 value) { - openbus = value & 0xFFFFFFFF; - switch (addr) { - case COP0_REG_INDEX: - index.raw = value & INDEX_MASK; - break; - case COP0_REG_RANDOM: - break; - case COP0_REG_ENTRYLO0: - entryLo0.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_ENTRYLO1: - entryLo1.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_CONTEXT: - context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); - break; - case COP0_REG_PAGEMASK: - pageMask.raw = value & PAGEMASK_MASK; - break; - case COP0_REG_WIRED: - wired = value & 63; - break; - case COP0_REG_BADVADDR: - break; - case COP0_REG_COUNT: - count = (u64)value << 1; - break; - case COP0_REG_ENTRYHI: - entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK; - break; - case COP0_REG_COMPARE: - compare = value; - cause.ip7 = false; - break; - case COP0_REG_STATUS: - status.raw &= ~STATUS_MASK; - status.raw |= (value & STATUS_MASK); - Update(); - break; - case COP0_REG_CAUSE: - { - Cop0Cause tmp{}; - tmp.raw = value; - cause.ip0 = tmp.ip0; - cause.ip1 = tmp.ip1; + openbus = value & 0xFFFFFFFF; + switch (addr) { + case COP0_REG_INDEX: + index.raw = value & INDEX_MASK; + break; + case COP0_REG_RANDOM: + break; + case COP0_REG_ENTRYLO0: + entryLo0.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_ENTRYLO1: + entryLo1.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_CONTEXT: + context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); + break; + case COP0_REG_PAGEMASK: + pageMask.raw = value & PAGEMASK_MASK; + break; + case COP0_REG_WIRED: + wired = value & 63; + break; + case COP0_REG_BADVADDR: + break; + case COP0_REG_COUNT: + count = (u64)value << 1; + break; + case COP0_REG_ENTRYHI: + entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK; + break; + case COP0_REG_COMPARE: + compare = value; + cause.ip7 = false; + break; + case COP0_REG_STATUS: + status.raw &= ~STATUS_MASK; + status.raw |= (value & STATUS_MASK); + Update(); + break; + case COP0_REG_CAUSE: + { + Cause tmp{}; + tmp.raw = value; + cause.ip0 = tmp.ip0; + cause.ip1 = tmp.ip1; + } + break; + case COP0_REG_EPC: + EPC = s64(s32(value)); + break; + case COP0_REG_PRID: + break; + case COP0_REG_CONFIG: + Config &= ~CONFIG_MASK; + Config |= (value & CONFIG_MASK); + break; + case COP0_REG_LLADDR: + LLAddr = value; + break; + case COP0_REG_WATCHLO: + WatchLo = value; + break; + case COP0_REG_WATCHHI: + WatchHi = value; + break; + case COP0_REG_XCONTEXT: + xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); + break; + case COP0_REG_PARITY_ERR: + ParityError = value & 0xff; + break; + case COP0_REG_CACHE_ERR: + break; + case COP0_REG_TAGLO: + tagLo.raw = value; + break; + case COP0_REG_TAGHI: + TagHi = value; + break; + case COP0_REG_ERROREPC: + ErrorEPC = s64(s32(value)); + break; + case 7: + case 21: + case 22: + case 23: + case 24: + case 25: + case 31: + break; + default: + panic("Unsupported word write from COP0 register {}", addr); } - break; - case COP0_REG_EPC: - EPC = s64(s32(value)); - break; - case COP0_REG_PRID: - break; - case COP0_REG_CONFIG: - Config &= ~CONFIG_MASK; - Config |= (value & CONFIG_MASK); - break; - case COP0_REG_LLADDR: - LLAddr = value; - break; - case COP0_REG_WATCHLO: - WatchLo = value; - break; - case COP0_REG_WATCHHI: - WatchHi = value; - break; - case COP0_REG_XCONTEXT: - xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); - break; - case COP0_REG_PARITY_ERR: - ParityError = value & 0xff; - break; - case COP0_REG_CACHE_ERR: - break; - case COP0_REG_TAGLO: - TagLo = value; - break; - case COP0_REG_TAGHI: - TagHi = value; - break; - case COP0_REG_ERROREPC: - ErrorEPC = s64(s32(value)); - break; - case 7: - case 21: - case 22: - case 23: - case 24: - case 25: - case 31: - break; - default: - panic("Unsupported word write from COP0 register {}", addr); - } } void Cop0::SetReg64(const u8 addr, const u64 value) { - openbus = value; - switch (addr) { - case COP0_REG_ENTRYLO0: - entryLo0.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_ENTRYLO1: - entryLo1.raw = value & ENTRY_LO_MASK; - break; - case COP0_REG_CONTEXT: - context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); - break; - case COP0_REG_XCONTEXT: - xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); - break; - case COP0_REG_ENTRYHI: - entryHi.raw = value & ENTRY_HI_MASK; - break; - case COP0_REG_STATUS: - status.raw = value; - break; - case COP0_REG_CAUSE: - { - Cop0Cause tmp{}; - tmp.raw = value; - cause.ip0 = tmp.ip0; - cause.ip1 = tmp.ip1; + openbus = value; + switch (addr) { + case COP0_REG_ENTRYLO0: + entryLo0.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_ENTRYLO1: + entryLo1.raw = value & ENTRY_LO_MASK; + break; + case COP0_REG_CONTEXT: + context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF); + break; + case COP0_REG_XCONTEXT: + xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF); + break; + case COP0_REG_ENTRYHI: + entryHi.raw = value & ENTRY_HI_MASK; + break; + case COP0_REG_STATUS: + status.raw = value; + break; + case COP0_REG_CAUSE: + { + Cause tmp{}; + tmp.raw = value; + cause.ip0 = tmp.ip0; + cause.ip1 = tmp.ip1; + } + break; + case COP0_REG_BADVADDR: + break; + case COP0_REG_EPC: + EPC = (s64)value; + break; + case COP0_REG_LLADDR: + LLAddr = value; + break; + case COP0_REG_ERROREPC: + ErrorEPC = (s64)value; + break; + default: + panic("Unsupported dword write to COP0 register {}", addr); } - break; - case COP0_REG_BADVADDR: - break; - case COP0_REG_EPC: - EPC = (s64)value; - break; - case COP0_REG_LLADDR: - LLAddr = value; - break; - case COP0_REG_ERROREPC: - ErrorEPC = (s64)value; - break; - default: - panic("Unsupported dword write to COP0 register {}", addr); - } } static FORCE_INLINE u64 getVPN(const u64 addr, const u64 pageMask) { - const u64 mask = pageMask | 0x1fff; - const u64 vpn = addr & 0xFFFFFFFFFF | addr >> 22 & 0x30000000000; + const u64 mask = pageMask | 0x1fff; + const u64 vpn = addr & 0xFFFFFFFFFF | addr >> 22 & 0x30000000000; - return vpn & ~mask; + return vpn & ~mask; } -TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) { - for (int i = 0; i < 32; i++) { - TLBEntry *entry = &tlb[i]; - if (!entry->initialized) - continue; - - const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); - const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); +Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr, int &index) { + for (int i = 0; i < 32; i++) { + TLBEntry *entry = &tlb[i]; + if (!entry->initialized) + continue; - const bool vpn_match = entry_vpn == vaddr_vpn; - const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; + const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); + const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); - if(!vpn_match || !asid_match) - continue; + const bool vpn_match = entry_vpn == vaddr_vpn; + const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; - index = i; - return entry; - } + if (!vpn_match || !asid_match) + continue; - return nullptr; + index = i; + return entry; + } + + return nullptr; } -TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) { - for (auto &t : tlb) { - TLBEntry *entry = &t; - if (!entry->initialized) - continue; - - const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); - const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); +Cop0::TLBEntry *Cop0::TLBTryMatch(const u64 vaddr) { + for (auto &t : tlb) { + TLBEntry *entry = &t; + if (!entry->initialized) + continue; - const bool vpn_match = entry_vpn == vaddr_vpn; - const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; - - if (vpn_match && asid_match) - return entry; - } + const u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw); + const u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw); - return nullptr; + const bool vpn_match = entry_vpn == vaddr_vpn; + const bool asid_match = entry->global || entryHi.asid == entry->entryHi.asid; + + if (vpn_match && asid_match) + return entry; + } + + return nullptr; } bool Cop0::ProbeTLB(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - const TLBEntry *entry = TLBTryMatch(vaddr); - if (!entry) { - tlbError = MISS; - return false; - } + const TLBEntry *entry = TLBTryMatch(vaddr); + if (!entry) { + tlbError = MISS; + return false; + } - const u32 mask = entry->pageMask.mask << 12 | 0xFFF; - const u32 odd = vaddr & mask + 1; + const u32 mask = entry->pageMask.mask << 12 | 0xFFF; + const u32 odd = vaddr & mask + 1; - const EntryLo entryLo = odd ? entry->entryLo1 : entry->entryLo0; + const EntryLo entryLo = odd ? entry->entryLo1 : entry->entryLo0; - if (!entryLo.v) { - tlbError = INVALID; - return false; - } + if (!entryLo.v) { + tlbError = INVALID; + return false; + } - if (accessType == STORE && !entryLo.d) { - tlbError = MODIFICATION; - return false; - } + if (accessType == STORE && !entryLo.d) { + tlbError = MODIFICATION; + return false; + } - paddr = entryLo.pfn << 12 | vaddr & mask; + paddr = entryLo.pfn << 12 | vaddr & mask; - return true; + return true; } void Cop0::FireException(const ExceptionCode code, const int cop, s64 pc) { - Registers& regs = Core::GetRegs(); + Registers ®s = Core::GetRegs(); - u16 vectorOffset = 0x0180; - if(tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { - if(!status.exl) { - if(is64BitAddressing) vectorOffset = 0x0080; - else vectorOffset = 0x0000; - } - } + Core::GetInstance().interpreter.SignalException(pc); - cause.copError = cop; - cause.exceptionCode = static_cast(code); - - if (!status.exl) { - if ((cause.branchDelay = regs.prevDelaySlot)) { - pc -= 4; + u16 vectorOffset = 0x0180; + if (tlbError == MISS && (code == ExceptionCode::TLBLoad || code == ExceptionCode::TLBStore)) { + if (!status.exl) { + vectorOffset = 0x0000; + if (is64BitAddressing) + vectorOffset = 0x0080; + } } - status.exl = true; - EPC = pc; - } + cause.copError = cop; + cause.exceptionCode = static_cast(code); - if (status.bev) { - panic("BEV bit set!"); - } + if (!status.exl) { + if ((cause.branchDelay = regs.prevDelaySlot)) { + pc -= 4; + } - regs.SetPC32(s32(0x80000000 + vectorOffset)); - Update(); + status.exl = true; + EPC = pc; + } + + if (status.bev) { + panic("BEV bit set!"); + } + + regs.SetPC32(s32(0x80000000 + vectorOffset)); + Update(); } void Cop0::HandleTLBException(const u64 vaddr) { - const u64 vpn2 = vaddr >> 13 & 0x7FFFF; - const u64 xvpn2 = vaddr >> 13 & 0x7FFFFFF; - badVaddr = vaddr; - context.badvpn2 = vpn2; - xcontext.badvpn2 = xvpn2; - xcontext.r = vaddr >> 62 & 3; - entryHi.vpn2 = xvpn2; - entryHi.r = vaddr >> 62 & 3; + const u64 vpn2 = vaddr >> 13 & 0x7FFFF; + const u64 xvpn2 = vaddr >> 13 & 0x7FFFFFF; + badVaddr = vaddr; + context.badvpn2 = vpn2; + xcontext.badvpn2 = xvpn2; + xcontext.r = vaddr >> 62 & 3; + entryHi.vpn2 = xvpn2; + entryHi.r = vaddr >> 62 & 3; } -ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) { - switch (error) { - case NONE: - panic("Getting TLB exception with error NONE"); - case INVALID: - case MISS: - return accessType == LOAD ? ExceptionCode::TLBLoad : ExceptionCode::TLBStore; - case MODIFICATION: - return ExceptionCode::TLBModification; - case DISALLOWED_ADDRESS: - return accessType == LOAD ? ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore; - default: - panic("Getting TLB exception for unknown error code! ({})", static_cast(error)); - return {}; - } +Cop0::ExceptionCode Cop0::GetTLBExceptionCode(const TLBError error, const TLBAccessType accessType) { + switch (error) { + case NONE: + panic("Getting TLB exception with error NONE"); + case INVALID: + case MISS: + return accessType == LOAD ? ExceptionCode::TLBLoad : ExceptionCode::TLBStore; + case MODIFICATION: + return ExceptionCode::TLBModification; + case DISALLOWED_ADDRESS: + return accessType == LOAD ? ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore; + default: + panic("Getting TLB exception for unknown error code! ({})", static_cast(error)); + return {}; + } } void Cop0::decode(const Instruction instr) { - Registers& regs = Core::GetRegs(); - switch (instr.cop_rs()) { - case 0x00: mfc0(instr); break; - case 0x01: dmfc0(instr); break; - case 0x04: mtc0(instr); break; - case 0x05: dmtc0(instr); break; + Registers ®s = Core::GetRegs(); + switch (instr.cop_rs()) { + case 0x00: + mfc0(instr); + break; + case 0x01: + dmfc0(instr); + break; + case 0x04: + mtc0(instr); + break; + case 0x05: + dmtc0(instr); + break; case 0x10 ... 0x1F: - switch (instr.cop_funct()) { - case 0x01: tlbr(); break; - case 0x02: tlbw(index.i); break; - case 0x06: tlbw(GetRandom()); break; - case 0x08: tlbp(); break; - case 0x18: eret(); break; + switch (instr.cop_funct()) { + case 0x01: + tlbr(); + break; + case 0x02: + tlbw(index.i); + break; + case 0x06: + tlbw(GetRandom()); + break; + case 0x08: + tlbp(); + break; + case 0x18: + eret(); + break; default: - panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), - regs.oldPC); - } - break; + panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC); + } + break; default: - panic("Unimplemented COP0 instruction {}", instr.cop_rs()); - } + panic("Unimplemented COP0 instruction {}", instr.cop_rs()); + } } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG)) - return ProbeTLB(accessType, s64(s32(vaddr)), paddr); - - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, START_VREGION_KUSEG, END_VREGION_KUSEG)) + return ProbeTLB(accessType, s64(s32(vaddr)), paddr); + + tlbError = DISALLOWED_ADDRESS; + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - u8 segment = static_cast(vaddr) >> 29 & 7; - if(ircolib::IsInsideRange(segment, 0, 3) || segment == 7) - return ProbeTLB(accessType, static_cast(vaddr), paddr); - - if(ircolib::IsInsideRange(segment, 4, 5)) { - paddr = vaddr & 0x1FFFFFFF; - return true; - } + u8 segment = static_cast(vaddr) >> 29 & 7; + if (ircolib::IsInsideRange(segment, 0, 3) || segment == 7) + return ProbeTLB(accessType, static_cast(vaddr), paddr); - if(segment == 6) - panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); + if (ircolib::IsInsideRange(segment, 4, 5)) { + paddr = vaddr & 0x1FFFFFFF; + return true; + } - panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr); + if (segment == 6) + panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr); - return false; + panic("Should never end up in base case in MapVirtualAddress! ({:08X})", vaddr); + + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF)) - return ProbeTLB(accessType, vaddr, paddr); - - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF)) + return ProbeTLB(accessType, vaddr, paddr); + + tlbError = DISALLOWED_ADDRESS; + return false; } template <> bool Cop0::MapVirtualAddress(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG - ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG - ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG - ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3 - return ProbeTLB(accessType, vaddr, paddr); - - if(ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS - if (!kernelMode) - panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr); - - const u8 high_two_bits = (vaddr >> 62) & 0b11; - if (high_two_bits != 0b10) - panic("Access to XKPHYS address 0x{:016X} with high two bits != 0b10!", vaddr); + if (ircolib::IsInsideRange(vaddr, 0x0000000000000000, 0x000000FFFFFFFFFF) || // VREGION_XKUSEG + ircolib::IsInsideRange(vaddr, 0x4000000000000000, 0x400000FFFFFFFFFF) || // VREGION_XKSSEG + ircolib::IsInsideRange(vaddr, 0xC000000000000000, 0xC00000FF7FFFFFFF) || // VREGION_XKSEG + ircolib::IsInsideRange(vaddr, 0xFFFFFFFFE0000000, 0xFFFFFFFFFFFFFFFF)) // VREGION_CKSEG3 + return ProbeTLB(accessType, vaddr, paddr); - const u8 subsegment = (vaddr >> 59) & 0b11; - bool cached = subsegment != 2; // do something with this eventually - // If any bits in the range of 58:32 are set, the address is invalid. - const bool valid = (vaddr & 0x07FFFFFF00000000) == 0; - if (!valid) { - tlbError = DISALLOWED_ADDRESS; - return false; + if (ircolib::IsInsideRange(vaddr, 0x8000000000000000, 0xBFFFFFFFFFFFFFFF)) { // VREGION_XKPHYS + if (!kernelMode) + panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr); + + const u8 high_two_bits = (vaddr >> 62) & 0b11; + if (high_two_bits != 0b10) + panic("Access to XKPHYS address 0x{:016X} with high two bits != 0b10!", vaddr); + + const u8 subsegment = (vaddr >> 59) & 0b11; + bool cached = subsegment != 2; // do something with this eventually + // If any bits in the range of 58:32 are set, the address is invalid. + const bool valid = (vaddr & 0x07FFFFFF00000000) == 0; + if (!valid) { + tlbError = DISALLOWED_ADDRESS; + return false; + } + + paddr = vaddr & 0xFFFFFFFF; + return true; } - paddr = vaddr & 0xFFFFFFFF; - return true; - } + if (ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0 + ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1 + u32 cut = u32(vaddr) >> 28; + u32 num = cut == 0xA; + // Identical to ksegX in 32 bit mode. + // Unmapped translation. Subtract the base address of the space to get the physical address. + paddr = vaddr - (cut << 28); // Implies cutting off the high 32 bits + trace("CKSEG{}: Translated 0x{:016X} to 0x{:08X}", num, vaddr, paddr); + return true; + } - if(ircolib::IsInsideRange(vaddr, 0xFFFFFFFF80000000, 0xFFFFFFFF9FFFFFFF) || // VREGION_CKSEG0 - ircolib::IsInsideRange(vaddr, 0xFFFFFFFFA0000000, 0xFFFFFFFFBFFFFFFF)) { // VREGION_CKSEG1 - u32 cut = u32(vaddr) >> 28; - u32 num = cut == 0xA; - // Identical to ksegX in 32 bit mode. - // Unmapped translation. Subtract the base address of the space to get the physical address. - paddr = vaddr - (cut << 28); // Implies cutting off the high 32 bits - trace("CKSEG{}: Translated 0x{:016X} to 0x{:08X}", num, vaddr, paddr); - return true; - } + if (ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1 + ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2 + ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3 + tlbError = DISALLOWED_ADDRESS; + return false; + } - if(ircolib::IsInsideRange(vaddr, 0x0000010000000000, 0x3FFFFFFFFFFFFFFF) || // VREGION_XBAD1 - ircolib::IsInsideRange(vaddr, 0x4000010000000000, 0x7FFFFFFFFFFFFFFF) || // VREGION_XBAD2 - ircolib::IsInsideRange(vaddr, 0xC00000FF80000000, 0xFFFFFFFF7FFFFFFF)) { // VREGION_XBAD3 - tlbError = DISALLOWED_ADDRESS; - return false; - } - - panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr); - return false; // just to silence warning + panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr); + return false; // just to silence warning } -bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { - if(supervisorMode) - panic("Supervisor mode memory access"); +bool Cop0::MapVAddr(const TLBAccessType accessType, const u64 vaddr, u32 &paddr) { + if (supervisorMode) + panic("Supervisor mode memory access"); - if (is64BitAddressing) [[unlikely]] { - if (kernelMode) [[likely]] return MapVirtualAddress(accessType, vaddr, paddr); - if (userMode) return MapVirtualAddress(accessType, vaddr, paddr); + if (is64BitAddressing) [[unlikely]] { + if (kernelMode) [[likely]] + return MapVirtualAddress(accessType, vaddr, paddr); + if (userMode) + return MapVirtualAddress(accessType, vaddr, paddr); + + panic("Unknown mode! This should never happen!"); + } + + if (kernelMode) [[likely]] + return MapVirtualAddress(accessType, vaddr, paddr); + if (userMode) + return MapVirtualAddress(accessType, vaddr, paddr); panic("Unknown mode! This should never happen!"); - } - - if (kernelMode) [[likely]] return MapVirtualAddress(accessType, vaddr, paddr); - if (userMode) return MapVirtualAddress(accessType, vaddr, paddr); - - panic("Unknown mode! This should never happen!"); } } // namespace n64 diff --git a/src/backend/core/registers/Cop0.hpp b/src/backend/core/registers/Cop0.hpp index 915c0c5..02b9db3 100644 --- a/src/backend/core/registers/Cop0.hpp +++ b/src/backend/core/registers/Cop0.hpp @@ -38,248 +38,259 @@ namespace n64 { #define ENTRY_HI_MASK 0xC00000FFFFFFE0FF #define PAGEMASK_MASK 0x1FFE000 -union Cop0Cause { - u32 raw; - struct { - unsigned : 8; - unsigned interruptPending : 8; - unsigned : 16; - } __attribute__((__packed__)); - struct { - unsigned : 2; - unsigned exceptionCode : 5; - unsigned : 1; - unsigned ip0 : 1; - unsigned ip1 : 1; - unsigned ip2 : 1; - unsigned ip3 : 1; - unsigned ip4 : 1; - unsigned ip5 : 1; - unsigned ip6 : 1; - unsigned ip7 : 1; - unsigned : 12; - unsigned copError : 2; - unsigned : 1; - unsigned branchDelay : 1; - } __attribute__((__packed__)); -}; - -union Cop0Status { - struct { - unsigned ie : 1; - unsigned exl : 1; - unsigned erl : 1; - unsigned ksu : 2; - unsigned ux : 1; - unsigned sx : 1; - unsigned kx : 1; - unsigned im : 8; - unsigned ds : 9; - unsigned re : 1; - unsigned fr : 1; - unsigned rp : 1; - unsigned cu0 : 1; - unsigned cu1 : 1; - unsigned cu2 : 1; - unsigned cu3 : 1; - } __attribute__((__packed__)); - struct { - unsigned : 16; - unsigned de : 1; - unsigned ce : 1; - unsigned ch : 1; - unsigned : 1; - unsigned sr : 1; - unsigned ts : 1; - unsigned bev : 1; - unsigned : 1; - unsigned its : 1; - unsigned : 7; - } __attribute__((__packed__)); - u32 raw; -} __attribute__((__packed__)); - -union EntryLo { - u32 raw; - struct { - unsigned g : 1; - unsigned v : 1; - unsigned d : 1; - unsigned c : 3; - unsigned pfn : 20; - unsigned : 6; - }; -}; - -union EntryHi { - u64 raw; - struct { - u64 asid : 8; - u64 : 5; - u64 vpn2 : 27; - u64 fill : 22; - u64 r : 2; - } __attribute__((__packed__)); -}; - -union PageMask { - u32 raw; - struct { - unsigned : 13; - unsigned mask : 12; - unsigned : 7; - }; -}; - -union Index { - u32 raw; - struct { - unsigned i : 6; - unsigned : 25; - unsigned p : 1; - }; -}; - -struct TLBEntry { - bool initialized; - EntryLo entryLo0, entryLo1; - EntryHi entryHi; - PageMask pageMask; - - bool global; -}; - -enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS }; - -enum class ExceptionCode : u8 { - Interrupt = 0, - TLBModification = 1, - TLBLoad = 2, - TLBStore = 3, - AddressErrorLoad = 4, - AddressErrorStore = 5, - InstructionBusError = 6, - DataBusError = 7, - Syscall = 8, - Breakpoint = 9, - ReservedInstruction = 10, - CoprocessorUnusable = 11, - Overflow = 12, - Trap = 13, - FloatingPointError = 15, - Watch = 23 -}; - -union Cop0Context { - u64 raw; - struct { - u64 : 4; - u64 badvpn2 : 19; - u64 ptebase : 41; - }; -}; - -union Cop0XContext { - u64 raw; - struct { - u64 : 4; - u64 badvpn2 : 27; - u64 r : 2; - u64 ptebase : 31; - } __attribute__((__packed__)); -}; - struct Cop0 { - Cop0(); + union Cause { + u32 raw; + struct { + unsigned : 8; + unsigned interruptPending : 8; + unsigned : 16; + } __attribute__((__packed__)); + struct { + unsigned : 2; + unsigned exceptionCode : 5; + unsigned : 1; + unsigned ip0 : 1; + unsigned ip1 : 1; + unsigned ip2 : 1; + unsigned ip3 : 1; + unsigned ip4 : 1; + unsigned ip5 : 1; + unsigned ip6 : 1; + unsigned ip7 : 1; + unsigned : 12; + unsigned copError : 2; + unsigned : 1; + unsigned branchDelay : 1; + } __attribute__((__packed__)); + }; - bool kernelMode{true}; - bool supervisorMode{false}; - bool userMode{false}; - bool is64BitAddressing{false}; - bool llbit{}; - TLBError tlbError = NONE; + union Status { + struct { + unsigned ie : 1; + unsigned exl : 1; + unsigned erl : 1; + unsigned ksu : 2; + unsigned ux : 1; + unsigned sx : 1; + unsigned kx : 1; + unsigned im : 8; + unsigned ds : 9; + unsigned re : 1; + unsigned fr : 1; + unsigned rp : 1; + unsigned cu0 : 1; + unsigned cu1 : 1; + unsigned cu2 : 1; + unsigned cu3 : 1; + } __attribute__((__packed__)); + struct { + unsigned : 16; + unsigned de : 1; + unsigned ce : 1; + unsigned ch : 1; + unsigned : 1; + unsigned sr : 1; + unsigned ts : 1; + unsigned bev : 1; + unsigned : 1; + unsigned its : 1; + unsigned : 7; + } __attribute__((__packed__)); + u32 raw; + } __attribute__((__packed__)); - PageMask pageMask{}; - EntryHi entryHi{}; - EntryLo entryLo0{}, entryLo1{}; - Index index{}; - Cop0Context context{}; - u32 wired{}, r7{}; - u32 compare{}; - Cop0Status status{}; - Cop0Cause cause{}; - u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{}; - u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{}; - u32 r31{}; - Cop0XContext xcontext{}; - u64 badVaddr{}, count{}; - s64 EPC{}; - s64 ErrorEPC{}; - s64 openbus{}; - TLBEntry tlb[32]{}; - - enum TLBAccessType { LOAD, STORE }; + union EntryLo { + u32 raw; + struct { + unsigned g : 1; + unsigned v : 1; + unsigned d : 1; + unsigned c : 3; + unsigned pfn : 20; + unsigned : 6; + }; + }; - u32 GetReg32(u8); - [[nodiscard]] u64 GetReg64(u8) const; + union EntryHi { + u64 raw; + struct { + u64 asid : 8; + u64 : 5; + u64 vpn2 : 27; + u64 fill : 22; + u64 r : 2; + } __attribute__((__packed__)); + }; - void SetReg32(u8, u32); - void SetReg64(u8, u64); + union PageMask { + u32 raw; + struct { + unsigned : 13; + unsigned mask : 12; + unsigned : 7; + }; + }; - void Reset(); + union Index { + u32 raw; + struct { + unsigned i : 6; + unsigned : 25; + unsigned p : 1; + }; + }; - bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr); - void FireException(ExceptionCode code, int cop, s64 pc); - bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr); + struct TLBEntry { + bool initialized; + EntryLo entryLo0, entryLo1; + EntryHi entryHi; + PageMask pageMask; - TLBEntry *TLBTryMatch(u64 vaddr, int &index); - TLBEntry *TLBTryMatch(u64 vaddr); - void HandleTLBException(u64 vaddr); - static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType); - void decode(const Instruction); + bool global; + }; - [[nodiscard]] FORCE_INLINE u32 GetRandom() const { - u32 val = rand(); - const auto wired_ = GetWired(); - u32 lower, upper; - if (wired_ > 31) { - lower = 0; - upper = 64; - } else { - lower = wired_; - upper = 32 - wired_; + enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS }; + + enum class ExceptionCode : u8 { + Interrupt = 0, + TLBModification = 1, + TLBLoad = 2, + TLBStore = 3, + AddressErrorLoad = 4, + AddressErrorStore = 5, + InstructionBusError = 6, + DataBusError = 7, + Syscall = 8, + Breakpoint = 9, + ReservedInstruction = 10, + CoprocessorUnusable = 11, + Overflow = 12, + Trap = 13, + FloatingPointError = 15, + Watch = 23 + }; + + union Context { + u64 raw; + struct { + u64 : 4; + u64 badvpn2 : 19; + u64 ptebase : 41; + }; + }; + + union XContext { + u64 raw; + struct { + u64 : 4; + u64 badvpn2 : 27; + u64 r : 2; + u64 ptebase : 31; + } __attribute__((__packed__)); + }; + + union TagLo { + u32 raw; + struct { + u64 : 4; + u64 ptaglo : 20; + u64 pstate : 2; + u64 : 6; + } __attribute__((__packed__)); + }; + + Cop0(); + + bool kernelMode{true}; + bool supervisorMode{false}; + bool userMode{false}; + bool is64BitAddressing{false}; + bool llbit{}; + TLBError tlbError = NONE; + + TagLo tagLo; + PageMask pageMask{}; + EntryHi entryHi{}; + EntryLo entryLo0{}, entryLo1{}; + Index index{}; + Context context{}; + u32 wired{}, r7{}; + u32 compare{}; + Status status{}; + Cause cause{}; + u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{}; + u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagHi{}; + u32 r31{}; + XContext xcontext{}; + u64 badVaddr{}, count{}; + s64 EPC{}; + s64 ErrorEPC{}; + s64 openbus{}; + TLBEntry tlb[32]{}; + + enum TLBAccessType { LOAD, STORE }; + + u32 GetReg32(u8); + [[nodiscard]] u64 GetReg64(u8) const; + + void SetReg32(u8, u32); + void SetReg64(u8, u64); + + void Reset(); + + bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr); + void FireException(ExceptionCode code, int cop, s64 pc); + bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr); + + TLBEntry *TLBTryMatch(u64 vaddr, int &index); + TLBEntry *TLBTryMatch(u64 vaddr); + void HandleTLBException(u64 vaddr); + static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType); + void decode(const Instruction); + + [[nodiscard]] FORCE_INLINE u32 GetRandom() const { + u32 val = rand(); + const auto wired_ = GetWired(); + u32 lower, upper; + if (wired_ > 31) { + lower = 0; + upper = 64; + } else { + lower = wired_; + upper = 32 - wired_; + } + + val = (val % upper) + lower; + return val; } - val = (val % upper) + lower; - return val; - } + FORCE_INLINE void Update() { + const bool exception = status.exl || status.erl; - FORCE_INLINE void Update() { - const bool exception = status.exl || status.erl; + kernelMode = exception || status.ksu == 0; + supervisorMode = !exception && status.ksu == 1; + userMode = !exception && status.ksu == 2; + is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux); + } - kernelMode = exception || status.ksu == 0; - supervisorMode = !exception && status.ksu == 1; - userMode = !exception && status.ksu == 2; - is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux); - } + private: + friend struct JIT; -private: - friend struct JIT; + [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } + [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } - [[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; } - [[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); } + void mtc0(const Instruction); + void dmtc0(const Instruction); + void mfc0(const Instruction); + void dmfc0(const Instruction) const; + void eret(); - void mtc0(const Instruction); - void dmtc0(const Instruction); - void mfc0(const Instruction); - void dmfc0(const Instruction) const; - void eret(); + void tlbr(); + void tlbw(int); + void tlbp(); - void tlbr(); - void tlbw(int); - void tlbp(); - - template - bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr); + template + bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr); }; } // namespace n64 diff --git a/src/backend/core/registers/Cop1.hpp b/src/backend/core/registers/Cop1.hpp index e125099..a79d4a7 100644 --- a/src/backend/core/registers/Cop1.hpp +++ b/src/backend/core/registers/Cop1.hpp @@ -7,241 +7,241 @@ namespace n64 { struct Cop1; union FCR31 { - FCR31() = default; - struct { - unsigned rounding_mode : 2; + FCR31() = default; struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - } flag; - struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - } enable; - struct { - unsigned inexact_operation : 1; - unsigned underflow : 1; - unsigned overflow : 1; - unsigned division_by_zero : 1; - unsigned invalid_operation : 1; - unsigned unimplemented_operation : 1; - } cause; - unsigned : 5; - unsigned compare : 1; - unsigned fs : 1; - unsigned : 7; - } __attribute__((__packed__)); + unsigned rounding_mode : 2; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + } flag; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + } enable; + struct { + unsigned inexact_operation : 1; + unsigned underflow : 1; + unsigned overflow : 1; + unsigned division_by_zero : 1; + unsigned invalid_operation : 1; + unsigned unimplemented_operation : 1; + } cause; + unsigned : 5; + unsigned compare : 1; + unsigned fs : 1; + unsigned : 7; + } __attribute__((__packed__)); - [[nodiscard]] u32 read() const { - u32 ret = 0; - ret |= (u32(fs) << 24); - ret |= (u32(compare) << 23); - ret |= (u32(cause.unimplemented_operation) << 17); - ret |= (u32(cause.invalid_operation) << 16); - ret |= (u32(cause.division_by_zero) << 15); - ret |= (u32(cause.overflow) << 14); - ret |= (u32(cause.underflow) << 13); - ret |= (u32(cause.inexact_operation) << 12); - ret |= (u32(enable.invalid_operation) << 11); - ret |= (u32(enable.division_by_zero) << 10); - ret |= (u32(enable.overflow) << 9); - ret |= (u32(enable.underflow) << 8); - ret |= (u32(enable.inexact_operation) << 7); - ret |= (u32(flag.invalid_operation) << 6); - ret |= (u32(flag.division_by_zero) << 5); - ret |= (u32(flag.overflow) << 4); - ret |= (u32(flag.underflow) << 3); - ret |= (u32(flag.inexact_operation) << 2); - ret |= (u32(rounding_mode) & 3); + [[nodiscard]] u32 read() const { + u32 ret = 0; + ret |= (u32(fs) << 24); + ret |= (u32(compare) << 23); + ret |= (u32(cause.unimplemented_operation) << 17); + ret |= (u32(cause.invalid_operation) << 16); + ret |= (u32(cause.division_by_zero) << 15); + ret |= (u32(cause.overflow) << 14); + ret |= (u32(cause.underflow) << 13); + ret |= (u32(cause.inexact_operation) << 12); + ret |= (u32(enable.invalid_operation) << 11); + ret |= (u32(enable.division_by_zero) << 10); + ret |= (u32(enable.overflow) << 9); + ret |= (u32(enable.underflow) << 8); + ret |= (u32(enable.inexact_operation) << 7); + ret |= (u32(flag.invalid_operation) << 6); + ret |= (u32(flag.division_by_zero) << 5); + ret |= (u32(flag.overflow) << 4); + ret |= (u32(flag.underflow) << 3); + ret |= (u32(flag.inexact_operation) << 2); + ret |= (u32(rounding_mode) & 3); - return ret; - } + return ret; + } - void write(u32 val) { - fs = val >> 24; - compare = val >> 23; - cause.unimplemented_operation = val >> 17; - cause.invalid_operation = val >> 16; - cause.division_by_zero = val >> 15; - cause.overflow = val >> 14; - cause.underflow = val >> 13; - cause.inexact_operation = val >> 12; - enable.invalid_operation = val >> 11; - enable.division_by_zero = val >> 10; - enable.overflow = val >> 9; - enable.underflow = val >> 8; - enable.inexact_operation = val >> 7; - flag.invalid_operation = val >> 6; - flag.division_by_zero = val >> 5; - flag.overflow = val >> 4; - flag.underflow = val >> 3; - flag.inexact_operation = val >> 2; - rounding_mode = val & 3; - } + void write(u32 val) { + fs = val >> 24; + compare = val >> 23; + cause.unimplemented_operation = val >> 17; + cause.invalid_operation = val >> 16; + cause.division_by_zero = val >> 15; + cause.overflow = val >> 14; + cause.underflow = val >> 13; + cause.inexact_operation = val >> 12; + enable.invalid_operation = val >> 11; + enable.division_by_zero = val >> 10; + enable.overflow = val >> 9; + enable.underflow = val >> 8; + enable.inexact_operation = val >> 7; + flag.invalid_operation = val >> 6; + flag.division_by_zero = val >> 5; + flag.overflow = val >> 4; + flag.underflow = val >> 3; + flag.inexact_operation = val >> 2; + rounding_mode = val & 3; + } }; union FloatingPointReg { - struct { - s32 int32; - s32 int32h; - }; - struct { - u32 uint32; - u32 uint32h; - }; - struct { - s64 int64; - }; - struct { - u64 uint64; - }; - struct { - float float32; - float float32h; - }; - struct { - double float64; - }; + struct { + s32 int32; + s32 int32h; + }; + struct { + u32 uint32; + u32 uint32h; + }; + struct { + s64 int64; + }; + struct { + u64 uint64; + }; + struct { + float float32; + float float32h; + }; + struct { + double float64; + }; }; struct Cop1 { - explicit Cop1(); - bool fgrIsConstant[32]{}; - u32 fcr0{}; - FCR31 fcr31{}; - FloatingPointReg fgr[32]{}; + explicit Cop1(); + bool fgrIsConstant[32]{}; + u32 fcr0{}; + FCR31 fcr31{}; + FloatingPointReg fgr[32]{}; - void Reset(); - void decode(const Instruction); - friend struct Interpreter; - friend struct JIT; + void Reset(); + void decode(const Instruction); + friend struct Interpreter; + friend struct JIT; - template - bool CheckFPUUsable(); - template - bool CheckResult(T &); - template - bool CheckArg(T); - template - bool CheckArgs(T, T); - template - bool isqnan(T); + template + bool CheckFPUUsable(); + template + bool CheckResult(T &); + template + bool CheckArg(T); + template + bool CheckArgs(T, T); + template + bool isqnan(T); - template - bool XORDERED(T fs, T ft); + template + bool XORDERED(T fs, T ft); - template - bool CheckCVTArg(float f); - template - bool CheckCVTArg(double f); + template + bool CheckCVTArg(float f); + template + bool CheckCVTArg(double f); - template - bool TestExceptions(); - void SetCauseUnimplemented(); - bool SetCauseUnderflow(); - bool SetCauseInexact(); - bool SetCauseDivisionByZero(); - bool SetCauseOverflow(); - bool SetCauseInvalid(); + template + bool TestExceptions(); + void SetCauseUnimplemented(); + bool SetCauseUnderflow(); + bool SetCauseInexact(); + bool SetCauseDivisionByZero(); + bool SetCauseOverflow(); + bool SetCauseInvalid(); -private: - template - auto FGR_T(const Cop0Status &, u32) -> T &; - template - auto FGR_S(const Cop0Status &, u32) -> T &; - template - auto FGR_D(const Cop0Status &, u32) -> T &; - void absd(const Instruction instr); - void abss(const Instruction instr); - void adds(const Instruction instr); - void addd(const Instruction instr); - void subs(const Instruction instr); - void subd(const Instruction instr); - void ceills(const Instruction instr); - void ceilws(const Instruction instr); - void ceilld(const Instruction instr); - void ceilwd(const Instruction instr); - void cfc1(const Instruction instr); - void ctc1(const Instruction instr); - void unimplemented(); - void roundls(const Instruction instr); - void roundld(const Instruction instr); - void roundws(const Instruction instr); - void roundwd(const Instruction instr); - void floorls(const Instruction instr); - void floorld(const Instruction instr); - void floorws(const Instruction instr); - void floorwd(const Instruction instr); - void cvtls(const Instruction instr); - void cvtws(const Instruction instr); - void cvtds(const Instruction instr); - void cvtsw(const Instruction instr); - void cvtdw(const Instruction instr); - void cvtsd(const Instruction instr); - void cvtwd(const Instruction instr); - void cvtld(const Instruction instr); - void cvtdl(const Instruction instr); - void cvtsl(const Instruction instr); - template - void cf(const Instruction instr); - template - void cun(const Instruction instr); - template - void ceq(const Instruction instr); - template - void cueq(const Instruction instr); - template - void colt(const Instruction instr); - template - void cult(const Instruction instr); - template - void cole(const Instruction instr); - template - void cule(const Instruction instr); - template - void csf(const Instruction instr); - template - void cngle(const Instruction instr); - template - void cseq(const Instruction instr); - template - void cngl(const Instruction instr); - template - void clt(const Instruction instr); - template - void cnge(const Instruction instr); - template - void cle(const Instruction instr); - template - void cngt(const Instruction instr); - void divs(const Instruction instr); - void divd(const Instruction instr); - void muls(const Instruction instr); - void muld(const Instruction instr); - void movs(const Instruction instr); - void movd(const Instruction instr); - void negs(const Instruction instr); - void negd(const Instruction instr); - void sqrts(const Instruction instr); - void sqrtd(const Instruction instr); - void lwc1(const Instruction instr); - void swc1(const Instruction instr); - void ldc1(const Instruction instr); - void sdc1(const Instruction instr); + private: + template + auto FGR_T(const Cop0::Status &, u32) -> T &; + template + auto FGR_S(const Cop0::Status &, u32) -> T &; + template + auto FGR_D(const Cop0::Status &, u32) -> T &; + void absd(const Instruction instr); + void abss(const Instruction instr); + void adds(const Instruction instr); + void addd(const Instruction instr); + void subs(const Instruction instr); + void subd(const Instruction instr); + void ceills(const Instruction instr); + void ceilws(const Instruction instr); + void ceilld(const Instruction instr); + void ceilwd(const Instruction instr); + void cfc1(const Instruction instr); + void ctc1(const Instruction instr); + void unimplemented(); + void roundls(const Instruction instr); + void roundld(const Instruction instr); + void roundws(const Instruction instr); + void roundwd(const Instruction instr); + void floorls(const Instruction instr); + void floorld(const Instruction instr); + void floorws(const Instruction instr); + void floorwd(const Instruction instr); + void cvtls(const Instruction instr); + void cvtws(const Instruction instr); + void cvtds(const Instruction instr); + void cvtsw(const Instruction instr); + void cvtdw(const Instruction instr); + void cvtsd(const Instruction instr); + void cvtwd(const Instruction instr); + void cvtld(const Instruction instr); + void cvtdl(const Instruction instr); + void cvtsl(const Instruction instr); + template + void cf(const Instruction instr); + template + void cun(const Instruction instr); + template + void ceq(const Instruction instr); + template + void cueq(const Instruction instr); + template + void colt(const Instruction instr); + template + void cult(const Instruction instr); + template + void cole(const Instruction instr); + template + void cule(const Instruction instr); + template + void csf(const Instruction instr); + template + void cngle(const Instruction instr); + template + void cseq(const Instruction instr); + template + void cngl(const Instruction instr); + template + void clt(const Instruction instr); + template + void cnge(const Instruction instr); + template + void cle(const Instruction instr); + template + void cngt(const Instruction instr); + void divs(const Instruction instr); + void divd(const Instruction instr); + void muls(const Instruction instr); + void muld(const Instruction instr); + void movs(const Instruction instr); + void movd(const Instruction instr); + void negs(const Instruction instr); + void negd(const Instruction instr); + void sqrts(const Instruction instr); + void sqrtd(const Instruction instr); + void lwc1(const Instruction instr); + void swc1(const Instruction instr); + void ldc1(const Instruction instr); + void sdc1(const Instruction instr); - void mfc1(const Instruction instr); - void dmfc1(const Instruction instr); - void mtc1(const Instruction instr); - void dmtc1(const Instruction instr); - void truncws(const Instruction instr); - void truncwd(const Instruction instr); - void truncls(const Instruction instr); - void truncld(const Instruction instr); + void mfc1(const Instruction instr); + void dmfc1(const Instruction instr); + void mtc1(const Instruction instr); + void dmtc1(const Instruction instr); + void truncws(const Instruction instr); + void truncwd(const Instruction instr); + void truncls(const Instruction instr); + void truncld(const Instruction instr); }; } // 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/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index dd4f4d8..7bb8cc0 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -5,456 +5,484 @@ #include #include -KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { - gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); - SDL_InitSubSystem(SDL_INIT_GAMEPAD); +KaizenGui::KaizenGui() noexcept : + window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), + emuThread(fpsCounter, settingsWindow) { + gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); + SDL_InitSubSystem(SDL_INIT_GAMEPAD); - SDL_AddGamepadMapping(gamecontrollerdb_str); + SDL_AddGamepadMapping(gamecontrollerdb_str); } KaizenGui::~KaizenGui() { - gui::Cleanup(); - SDL_Quit(); + gui::Cleanup(); + SDL_Quit(); } void KaizenGui::QueryDevices(const SDL_Event &event) { - switch (event.type) { - case SDL_EVENT_GAMEPAD_ADDED: - if (!gamepad) { - const auto index = event.gdevice.which; + switch (event.type) { + case SDL_EVENT_GAMEPAD_ADDED: + if (!gamepad) { + const auto index = event.gdevice.which; - gamepad = SDL_OpenGamepad(index); - info("Found controller!"); - info("Name: {}", SDL_GetGamepadName(gamepad)); - info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + gamepad = SDL_OpenGamepad(index); + info("Found controller!"); + info("Name: {}", SDL_GetGamepadName(gamepad)); + info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + break; + case SDL_EVENT_GAMEPAD_REMOVED: + if (gamepad) + SDL_CloseGamepad(gamepad); + break; + default: + break; } - break; - case SDL_EVENT_GAMEPAD_REMOVED: - if (gamepad) - SDL_CloseGamepad(gamepad); - break; - default: break; - } } void KaizenGui::HandleInput(const SDL_Event &event) { - const n64::Core& core = n64::Core::GetInstance(); - n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; - switch(event.type) { + const n64::Core &core = n64::Core::GetInstance(); + n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; + switch (event.type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: - if(!gamepad) + if (!gamepad) + break; + { + pif.UpdateButton(0, n64::Controller::Key::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::Key::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); + pif.UpdateButton(0, n64::Controller::Key::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); + + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + } break; - { - pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); - - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast( xclamped)); - } - break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: - if(!gamepad) + if (!gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); break; - - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - { - const auto keys = SDL_GetKeyboardState(nullptr); - if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { - fileDialogOpen = true; + { + const auto keys = SDL_GetKeyboardState(nullptr); + if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { + fileDialogOpen = true; + } + + fastForward = keys[SDL_SCANCODE_SPACE]; + if (!unlockFramerate) + core.parallel.SetFramerateUnlocked(fastForward); + + if (core.romLoaded) { + if (keys[SDL_SCANCODE_P]) { + emuThread.TogglePause(); + } + + if (keys[SDL_SCANCODE_R]) { + emuThread.Reset(); + } + + if (keys[SDL_SCANCODE_Q]) { + emuThread.Stop(); + } + } + + if (gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); + pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); + pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); + pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); + pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); + pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); + pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); + pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); + pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); + pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); + pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); + pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); + pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); + pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); + + float x = 0, y = 0; + + if (keys[SDL_SCANCODE_UP]) + y = 86; + if (keys[SDL_SCANCODE_DOWN]) + y = -86; + if (keys[SDL_SCANCODE_LEFT]) + x = -86; + if (keys[SDL_SCANCODE_RIGHT]) + x = 86; + + pif.UpdateAxis(0, n64::Controller::Axis::X, x); + pif.UpdateAxis(0, n64::Controller::Axis::Y, y); } - - fastForward = keys[SDL_SCANCODE_SPACE]; - if(!unlockFramerate) - core.parallel.SetFramerateUnlocked(fastForward); - - if(core.romLoaded) { - if(keys[SDL_SCANCODE_P]) { - emuThread.TogglePause(); - } - - if(keys[SDL_SCANCODE_R]) { - emuThread.Reset(); - } - - if(keys[SDL_SCANCODE_Q]) { - emuThread.Stop(); - } - } - - if(gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); - pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); - pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); - pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); - pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); - pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); - pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); - pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); - pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); - pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); - pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); - pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); - pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); - pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - - float x = 0, y = 0; - - if (keys[SDL_SCANCODE_UP]) y = 86; - if (keys[SDL_SCANCODE_DOWN]) y = -86; - if (keys[SDL_SCANCODE_LEFT]) x = -86; - if (keys[SDL_SCANCODE_RIGHT]) x = 86; - - pif.UpdateAxis(0, n64::Controller::Axis::X, x); - pif.UpdateAxis(0, n64::Controller::Axis::Y, y); - } - break; - default: break; - } + break; + default: + break; + } } std::pair, std::optional> RenderErrorMessageDetails() { - auto lastPC = Util::Error::GetLastPC(); - if(lastPC.has_value()) { - ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); - } + auto lastPC = Util::Error::GetLastPC(); + if (lastPC.has_value()) { + ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); + } - auto memoryAccess = Util::Error::GetMemoryAccess(); - if(memoryAccess.has_value()) { - const auto [is_write, size, address, written_val] = memoryAccess.value(); - ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", - static_cast(size), address, - is_write ? std::format(" (value = 0x{:X})", written_val) : "") - .c_str()); - } + auto memoryAccess = Util::Error::GetMemoryAccess(); + if (memoryAccess.has_value()) { + const auto [is_write, size, address, written_val] = memoryAccess.value(); + ImGui::Text("%s", + std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast(size), + address, is_write ? std::format(" (value = 0x{:X})", written_val) : "") + .c_str()); + } - return {lastPC, memoryAccess}; + return {lastPC, memoryAccess}; } void KaizenGui::RenderUI() { - n64::Core& core = n64::Core::GetInstance(); - gui::StartFrame(); + n64::Core &core = n64::Core::GetInstance(); + gui::StartFrame(); - if(ImGui::BeginMainMenuBar()) { - if(ImGui::BeginMenu("File")) { - if(ImGui::MenuItem("Open", "Ctrl-O")) { - fileDialogOpen = true; - } - if(ImGui::MenuItem("Exit")) { - quit = true; - emuThread.Stop(); - } - ImGui::EndMenu(); + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open", "Ctrl-O")) { + fileDialogOpen = true; + } + if (ImGui::MenuItem("Exit")) { + quit = true; + emuThread.Stop(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Emulation")) { + ImGui::BeginDisabled(!core.romLoaded); + + if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { + emuThread.TogglePause(); + } + + if (ImGui::MenuItem("Reset", "R")) { + emuThread.Reset(); + } + + if (ImGui::MenuItem("Stop", "Q")) { + emuThread.Stop(); + core.romLoaded = false; + } + + if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { + core.parallel.SetFramerateUnlocked(unlockFramerate); + } + + if (ImGui::MenuItem("Open Debugger")) { + debugger.Open(); + } + + ImGui::EndDisabled(); + + if (ImGui::MenuItem("Options")) { + settingsWindow.isOpen = true; + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) { + if (ImGui::MenuItem("About")) { + aboutOpen = true; + } + + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } - if(ImGui::BeginMenu("Emulation")) { - ImGui::BeginDisabled(!core.romLoaded); - - if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { - emuThread.TogglePause(); - } - - if(ImGui::MenuItem("Reset", "R")) { - emuThread.Reset(); - } - - if(ImGui::MenuItem("Stop", "Q")) { - emuThread.Stop(); - core.romLoaded = false; - } - - if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { - core.parallel.SetFramerateUnlocked(unlockFramerate); - } - if(ImGui::MenuItem("Open Debugger")) { - debugger.Open(); - } - - ImGui::EndDisabled(); - - if(ImGui::MenuItem("Options")) { - settingsWindow.isOpen = true; - } - ImGui::EndMenu(); + if (!Util::Error::IsHandled()) { + ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); } - if(ImGui::BeginMenu("Help")) { - if(ImGui::MenuItem("About")) { - aboutOpen = true; - } - - ImGui::EndMenu(); + + if (settingsWindow.isOpen) { + ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); } - ImGui::EndMainMenuBar(); - } - if(!Util::Error::IsHandled()) { - ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); - } - - if(settingsWindow.isOpen) { - ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); - } - - if(aboutOpen) { - ImGui::OpenPopup("About Kaizen"); - } - - settingsWindow.render(); - debugger.render(); - - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); - ImGui::Text("to offer a friendly user experience and compatibility."); - ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); - ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); - ImGui::Separator(); - ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); - ImGui::Separator(); - if(ImGui::Button("OK")) { - aboutOpen = false; - ImGui::CloseCurrentPopup(); + if (aboutOpen) { + ImGui::OpenPopup("About Kaizen"); } - - ImGui::EndPopup(); - } - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + settingsWindow.render(); + debugger.render(); - if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - emuThread.TogglePause(); - switch(Util::Error::GetSeverity().as_enum) { - case Util::Error::Severity::WARN: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); - ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - if(ignore || stop || chooseAnother) { - Util::Error::SetHandled(); + if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); + ImGui::Text("to offer a friendly user experience and compatibility."); + ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); + ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); + ImGui::Separator(); + ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); + ImGui::Separator(); + if (ImGui::Button("OK")) { + aboutOpen = false; ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - break; } - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::UNRECOVERABLE: { - emuThread.Stop(); - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::NON_FATAL: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An error has occurred!"); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; - if(ignore || stop || chooseAnother || openInDebugger) { - Util::Error::SetHandled(); - ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - - if(openInDebugger) { - if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) - n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); - - debugger.Open(); - emuThread.Reset(); - } - } break; - default: break; + ImGui::EndPopup(); } - - ImGui::EndPopup(); - } - - if(ImGui::BeginMainStatusBar()) { - ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); - ImGui::EndMainStatusBar(); - } - if (shouldDisplaySpinner) { - ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + emuThread.TogglePause(); + switch (Util::Error::GetSeverity().as_enum) { + case Util::Error::Severity::WARN: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); + ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); - ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); - ImGui::SameLine(); + if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + if (ignore || stop || chooseAnother) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); - ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); - ImGui::PopFont(); + if (ignore) { + emuThread.TogglePause(); + } - ImGui::End(); + if (stop || chooseAnother) { + emuThread.Stop(); + } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - } + if (chooseAnother) { + fileDialogOpen = true; + } + break; + } - ImGui::Render(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::UNRECOVERABLE: + { + emuThread.Stop(); + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::NON_FATAL: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An error has occurred!"); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - if(fileDialogOpen) { - fileDialogOpen = false; - constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { - auto kaizen = static_cast(userdata); - - if (!filelist) { - panic("An error occured: {}", SDL_GetError()); - } + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool openInDebugger = + lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; + if (ignore || stop || chooseAnother || openInDebugger) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); + if (ignore) { + emuThread.TogglePause(); + } + + if (stop || chooseAnother) { + emuThread.Stop(); + } + + if (chooseAnother) { + fileDialogOpen = true; + } + + if (openInDebugger) { + if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) + n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); + + debugger.Open(); + emuThread.Reset(); + } + } + break; + default: + break; + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginMainStatusBar()) { + ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); + ImGui::EndMainStatusBar(); + } + + if (shouldDisplaySpinner) { + ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, + ImVec2(0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + + ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); + ImGui::SameLine(); + + ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); + ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); + ImGui::PopFont(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + + ImGui::Render(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + if (fileDialogOpen) { + fileDialogOpen = false; + constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, + {"Nintendo 64 executable", "n64;z64;v64"}, + {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; + SDL_ShowOpenFileDialog( + [](void *userdata, const char *const *filelist, int) { + auto kaizen = static_cast(userdata); + + if (!filelist) { + panic("An error occured: {}", SDL_GetError()); + } + + if (!*filelist) { + warn("The user did not select any file."); + warn("Most likely, the dialog was canceled."); + return; + } + + kaizen->fileToLoad = *filelist; + kaizen->shouldDisplaySpinner = true; + std::thread fileWorker(&KaizenGui::FileWorker, kaizen); + fileWorker.detach(); + }, + this, window.getHandle(), filters, 3, nullptr, false); + } + + if (minimized) return; - } - kaizen->fileToLoad = *filelist; - kaizen->shouldDisplaySpinner = true; + if (core.romLoaded) { + core.parallel.UpdateScreen(); + return; + } - std::thread fileWorker(&KaizenGui::FileWorker, kaizen); - fileWorker.detach(); - }, this, window.getHandle(), filters, 3, nullptr, false); - } - - if(minimized) - return; - - if(core.romLoaded) { - core.parallel.UpdateScreen(); - return; - } - - core.parallel.UpdateScreen(); + core.parallel.UpdateScreen(); } void KaizenGui::LoadROM(const std::string &path) noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.LoadROM(path); - const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; - SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); + n64::Core &core = n64::Core::GetInstance(); + core.LoadROM(path); + const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; + SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); } void KaizenGui::run() { - while(!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL3_ProcessEvent(&e); - switch(e.type) { - case SDL_EVENT_QUIT: - quit = true; - emuThread.Stop(); - break; - case SDL_EVENT_WINDOW_MINIMIZED: - minimized = true; - break; - case SDL_EVENT_WINDOW_RESTORED: - minimized = false; - break; - default: - } - QueryDevices(e); - HandleInput(e); + while (!quit) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + ImGui_ImplSDL3_ProcessEvent(&e); + switch (e.type) { + case SDL_EVENT_QUIT: + quit = true; + emuThread.Stop(); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + minimized = true; + break; + case SDL_EVENT_WINDOW_RESTORED: + minimized = false; + break; + default: + break; + } + QueryDevices(e); + HandleInput(e); + } + + SDL_GetWindowSize(window.getHandle(), &width, &height); + + emuThread.run(); + RenderUI(); } - - SDL_GetWindowSize(window.getHandle(), &width, &height); - - emuThread.run(); - RenderUI(); - } } -void KaizenGui::LoadTAS(const std::string &path) noexcept { - n64::Core::GetInstance().LoadTAS(fs::path(path)); -} \ No newline at end of file +void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index d55a78d..9d0e9f1 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -10,7 +10,7 @@ class KaizenGui final { public: explicit KaizenGui() noexcept; ~KaizenGui(); - + double fpsCounter = -1.0; bool fastForward = false; bool unlockFramerate = false; @@ -22,7 +22,7 @@ public: Debugger debugger; SDL_Gamepad* gamepad = nullptr; - + void run(); static void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; @@ -37,12 +37,13 @@ private: void HandleInput(const SDL_Event &event); void QueryDevices(const SDL_Event &event); - [[noreturn]] void FileWorker() { + void FileWorker() { while (true) { if (!fileToLoad.empty()) { LoadROM(fileToLoad); shouldDisplaySpinner = false; fileToLoad = ""; + return; } } } diff --git a/src/frontend/Settings/CPUSettings.cpp b/src/frontend/Settings/CPUSettings.cpp index 0b7ee07..12e4ed4 100644 --- a/src/frontend/Settings/CPUSettings.cpp +++ b/src/frontend/Settings/CPUSettings.cpp @@ -2,42 +2,52 @@ #include #include #include +#include CPUSettings::CPUSettings() { - if (Options::GetInstance().GetValue("cpu", "type") == "jit") { - selectedCpuTypeIndex = 1; - } else { - selectedCpuTypeIndex = 0; - } + auto selectedCpuType = Options::GetInstance().GetValue("cpu", "type"); + if (selectedCpuType == "jit") { + selectedCpuTypeIndex = 2; + } else if (selectedCpuType == "cached_interpreter") { + selectedCpuTypeIndex = 1; + } else { + selectedCpuTypeIndex = 0; + } } void CPUSettings::render() { - const char* items[] = { - "Interpreter", - "Dynamic Recompiler" - }; + const char *items[] = {"Interpreter", "Cached 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"); + n64::Core::GetInstance().cpuType = n64::Core::Interpreted; + } else if (selectedCpuTypeIndex == 1) { + Options::GetInstance().SetValue("cpu", "type", "cached_interpreter"); + n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter; + } else { + Options::GetInstance().SetValue("cpu", "type", "jit"); + n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler; + } } - } } diff --git a/src/utils/Instruction.hpp b/src/utils/Instruction.hpp index acc53f6..ef4fc30 100644 --- a/src/utils/Instruction.hpp +++ b/src/utils/Instruction.hpp @@ -3,11 +3,13 @@ #include namespace n64 { + struct Instruction { + Instruction() = default; 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 +23,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 +36,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 +225,4 @@ struct Instruction { static constexpr u8 BLTZALL = 0b10010; static constexpr u8 BGEZALL = 0b10011; }; -} \ No newline at end of file +} // namespace n64