- Stop using inheritance for CPU, instead use composition.

- Introduce KAIZEN_JIT_ENABLED optional define instead of relying on __aarch64__ and the like.
- More cache work
This commit is contained in:
2026-04-28 18:01:43 +02:00
parent 68e613057e
commit e140a6d124
19 changed files with 2868 additions and 2835 deletions
+7
View File
@@ -13,6 +13,7 @@ if(APPLE)
enable_language(OBJC) enable_language(OBJC)
endif() endif()
set(USE_JIT FALSE)
set(VULKAN_VALIDATION FALSE) set(VULKAN_VALIDATION FALSE)
set(SANITIZERS FALSE) set(SANITIZERS FALSE)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
@@ -115,6 +116,9 @@ endif()
set(SIMD_FLAG NULL) set(SIMD_FLAG NULL)
if(ARM64) if(ARM64)
if(USE_JIT)
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
endif()
message("Defining USE_NEON...") message("Defining USE_NEON...")
add_compile_definitions(USE_NEON) add_compile_definitions(USE_NEON)
add_compile_definitions(SIMD_SUPPORT) add_compile_definitions(SIMD_SUPPORT)
@@ -182,6 +186,9 @@ endif()
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend) target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
if(USE_JIT)
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
endif()
if (SANITIZERS) if (SANITIZERS)
message("UBSAN AND ASAN: ON") message("UBSAN AND ASAN: ON")
+151 -149
View File
@@ -13,11 +13,11 @@ using namespace RDP;
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; } bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const { void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const {
if (unlocked) { if (unlocked) {
wsi->set_present_mode(PresentMode::UnlockedForceTearing); wsi->set_present_mode(PresentMode::UnlockedForceTearing);
} else { } else {
wsi->set_present_mode(PresentMode::SyncToVBlank); wsi->set_present_mode(PresentMode::SyncToVBlank);
} }
} }
Program *fullscreen_quad_program; Program *fullscreen_quad_program;
@@ -25,208 +25,210 @@ Program *fullscreen_quad_program;
// Copied and modified from WSI::init_context_from_platform // Copied and modified from WSI::init_context_from_platform
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices, Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
const Context::SystemHandles &system_handles) { const Context::SystemHandles &system_handles) {
VK_ASSERT(platform); VK_ASSERT(platform);
const auto instance_ext = platform->get_instance_extensions(); const auto instance_ext = platform->get_instance_extensions();
const auto device_ext = platform->get_device_extensions(); const auto device_ext = platform->get_device_extensions();
auto new_context = Util::make_handle<Context>(); auto new_context = Util::make_handle<Context>();
new_context->set_application_info(platform->get_application_info()); new_context->set_application_info(platform->get_application_info());
new_context->set_num_thread_indices(num_thread_indices); new_context->set_num_thread_indices(num_thread_indices);
new_context->set_system_handles(system_handles); new_context->set_system_handles(system_handles);
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) { if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
panic("Failed to create Vulkan instance.\n"); 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(), const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT); CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
if (tmp_surface) { if (tmp_surface) {
platform->destroy_surface(new_context->get_instance(), tmp_surface); platform->destroy_surface(new_context->get_instance(), tmp_surface);
} }
if (!ret) { if (!ret) {
panic("Failed to create Vulkan device.\n"); panic("Failed to create Vulkan device.\n");
} }
return new_context; return new_context;
} }
void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platform, void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platform,
const std::shared_ptr<WindowInfo> &newWindowInfo) { const std::shared_ptr<WindowInfo> &newWindowInfo) {
wsi = std::make_shared<WSI>(); wsi = std::make_shared<WSI>();
wsi->set_backbuffer_srgb(false); wsi->set_backbuffer_srgb(false);
wsi->set_platform(wsi_platform.get()); wsi->set_platform(wsi_platform.get());
wsi->set_present_mode(PresentMode::SyncToVBlank); wsi->set_present_mode(PresentMode::SyncToVBlank);
if (constexpr Context::SystemHandles handles; if (constexpr Context::SystemHandles handles;
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) { !wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
panic("Failed to initialize WSI: init_from_existing_context() failed"); panic("Failed to initialize WSI: init_from_existing_context() failed");
} }
if (!wsi->init_device()) { if (!wsi->init_device()) {
panic("Failed to initialize WSI: init_device() failed"); panic("Failed to initialize WSI: init_device() failed");
} }
if (!wsi->init_surface_swapchain()) { if (!wsi->init_surface_swapchain()) {
panic("Failed to initialize WSI: init_surface_swapchain() failed"); panic("Failed to initialize WSI: init_surface_swapchain() failed");
} }
windowInfo = newWindowInfo; windowInfo = newWindowInfo;
} }
void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform, void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
const std::shared_ptr<WindowInfo> &newWindowInfo, const u8 *rdram) { const std::shared_ptr<WindowInfo> &newWindowInfo, const u8 *rdram) {
LoadWSIPlatform(wsiPlatform, newWindowInfo); LoadWSIPlatform(wsiPlatform, newWindowInfo);
ResourceLayout vertLayout; ResourceLayout vertLayout;
ResourceLayout fragLayout; ResourceLayout fragLayout;
vertLayout.input_mask = 1; vertLayout.input_mask = 1;
vertLayout.output_mask = 1; vertLayout.output_mask = 1;
fragLayout.input_mask = 1; fragLayout.input_mask = 1;
fragLayout.output_mask = 1; fragLayout.output_mask = 1;
fragLayout.spec_constant_mask = 1; fragLayout.spec_constant_mask = 1;
fragLayout.push_constant_size = 4 * sizeof(float); fragLayout.push_constant_size = 4 * sizeof(float);
fragLayout.sets[0].sampled_image_mask = 1; fragLayout.sets[0].sampled_image_mask = 1;
fragLayout.sets[0].fp_mask = 1; fragLayout.sets[0].fp_mask = 1;
fragLayout.sets[0].array_size[0] = 1; fragLayout.sets[0].array_size[0] = 1;
constexpr auto sizeVert = sizeof(vertex_shader); constexpr auto sizeVert = sizeof(vertex_shader);
constexpr auto sizeFrag = sizeof(fragment_shader); constexpr auto sizeFrag = sizeof(fragment_shader);
fullscreen_quad_program = wsi->get_device().request_program( fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader), reinterpret_cast<const u32 *>(fragment_shader),
sizeFrag, &vertLayout, &fragLayout); sizeFrag, &vertLayout, &fragLayout);
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram); auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
uintptr_t offset = 0; uintptr_t offset = 0;
if (wsi->get_device().get_device_features().supports_external_memory_host) { if (wsi->get_device().get_device_features().supports_external_memory_host) {
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment; const size_t align =
offset = aligned_rdram & align - 1; wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
aligned_rdram -= offset; offset = aligned_rdram & align - 1;
} aligned_rdram -= offset;
}
CommandProcessorFlags flags = 0; CommandProcessorFlags flags = 0;
command_processor = std::make_shared<CommandProcessor>(wsi->get_device(), reinterpret_cast<void *>(aligned_rdram), command_processor = std::make_shared<CommandProcessor>(wsi->get_device(), reinterpret_cast<void *>(aligned_rdram),
offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags); offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags);
if (!command_processor->device_is_supported()) { 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!"); 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> image, void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
Util::IntrusivePtr<CommandBuffer> cmd) const { Util::IntrusivePtr<CommandBuffer> cmd) const {
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp); cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
cmd->set_program(fullscreen_quad_program); cmd->set_program(fullscreen_quad_program);
cmd->set_quad_state(); cmd->set_quad_state();
const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
data[0] = -1.0f; data[0] = -1.0f;
data[1] = -3.0f; data[1] = -3.0f;
data[2] = -1.0f; data[2] = -1.0f;
data[3] = +1.0f; data[3] = +1.0f;
data[4] = +3.0f; data[4] = +3.0f;
data[5] = +1.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<float>(wsi->get_platform().get_surface_width()), const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
y / static_cast<float>(wsi->get_platform().get_surface_height())); y / static_cast<float>(wsi->get_platform().get_surface_height()));
const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom; const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom; const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
const float uniform_data[] = {// Size const float uniform_data[] = {// Size
width, height, width, height,
// Offset // Offset
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f}; (1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
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_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
cmd->set_depth_test(false, false); cmd->set_depth_test(false, false);
cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS); cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS);
cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
cmd->draw(3, 1); cmd->draw(3, 1);
} }
void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const { void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
wsi->begin_frame(); wsi->begin_frame();
if (!image) { if (!image) {
auto info = ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM); 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.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.misc = IMAGE_MISC_MUTABLE_SRGB_BIT;
info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED; info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
image = wsi->get_device().create_image(info); 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, 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_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT); VK_ACCESS_TRANSFER_WRITE_BIT);
cmd->clear_image(*image, {}); cmd->clear_image(*image, {});
cmd->image_barrier(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 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_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT); VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
wsi->get_device().submit(cmd);
}
Util::IntrusivePtr<CommandBuffer> 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->get_device().submit(cmd);
} wsi->end_frame();
Util::IntrusivePtr<CommandBuffer> 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();
} }
template <> template <>
void ParallelRDP::UpdateScreen<true>() const { void ParallelRDP::UpdateScreen<true>() const {
const n64::VI& vi = n64::Core::GetMem().mmio.vi; 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::Control, vi.status.raw);
command_processor->set_vi_register(VIRegister::Origin, vi.origin); command_processor->set_vi_register(VIRegister::Origin, vi.origin);
command_processor->set_vi_register(VIRegister::Width, vi.width); command_processor->set_vi_register(VIRegister::Width, vi.width);
command_processor->set_vi_register(VIRegister::Intr, vi.intr); command_processor->set_vi_register(VIRegister::Intr, vi.intr);
command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); 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::Timing, vi.burst.raw);
command_processor->set_vi_register(VIRegister::VSync, vi.vsync); command_processor->set_vi_register(VIRegister::VSync, vi.vsync);
command_processor->set_vi_register(VIRegister::HSync, vi.hsync); 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::Leap, vi.hsyncLeap.raw);
command_processor->set_vi_register(VIRegister::HStart, vi.hstart.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::VStart, vi.vstart.raw);
command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); 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::XScale, vi.xscale.raw);
command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw);
ScanoutOptions opts; ScanoutOptions opts;
opts.persist_frame_on_invalid_input = true; opts.persist_frame_on_invalid_input = true;
opts.vi.aa = true; opts.vi.aa = true;
opts.vi.scale = true; opts.vi.scale = true;
opts.vi.dither_filter = true; opts.vi.dither_filter = true;
opts.vi.divot_filter = true; opts.vi.divot_filter = true;
opts.vi.gamma_dither = true; opts.vi.gamma_dither = true;
opts.downscale_steps = true; opts.downscale_steps = true;
opts.crop_overscan_pixels = true; opts.crop_overscan_pixels = true;
const Util::IntrusivePtr<Image> image = command_processor->scanout(opts); const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
UpdateScreen(image); UpdateScreen(image);
command_processor->begin_frame_context(); command_processor->begin_frame_context();
} }
template <> template <>
void ParallelRDP::UpdateScreen<false>() const { void ParallelRDP::UpdateScreen<false>() const {
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr)); UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
} }
void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const { void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const {
command_processor->enqueue_command(command_length, buffer); command_processor->enqueue_command(command_length, buffer);
} }
void ParallelRDP::OnFullSync() const { command_processor->wait_for_timeline(command_processor->signal_timeline()); } void ParallelRDP::OnFullSync() const { command_processor->wait_for_timeline(command_processor->signal_timeline()); }
+19 -9
View File
@@ -4,18 +4,18 @@
#include <Options.hpp> #include <Options.hpp>
namespace n64 { namespace n64 {
Core::Core() { Core::Core() :
interpreter(*mem, regs)
#ifdef KAIZEN_JIT_ENABLED
,
jit(*mem, regs)
#endif
{
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type"); const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
if (selectedCpu == "interpreter") { if (selectedCpu == "interpreter") {
cpuType = Interpreted; cpuType = Interpreted;
cpu = std::make_unique<Interpreter>(*mem, regs);
} else if (selectedCpu == "jit") { } else if (selectedCpu == "jit") {
#ifndef __aarch64__
cpuType = DynamicRecompiler; cpuType = DynamicRecompiler;
cpu = std::make_unique<JIT>(*mem, regs);
#else
panic("JIT currently unsupported on aarch64");
#endif
} else { } else {
panic("Unimplemented CPU type"); panic("Unimplemented CPU type");
} }
@@ -30,7 +30,7 @@ void Core::Stop() {
void Core::Reset() { void Core::Reset() {
regs.Reset(); regs.Reset();
mem->Reset(); mem->Reset();
cpu->Reset(); interpreter.Reset();
if (romLoaded) if (romLoaded)
mem->mmio.si.pif.Execute(); mem->mmio.si.pif.Execute();
} }
@@ -62,7 +62,17 @@ void Core::LoadROM(const std::string &rom_) {
romLoaded = true; romLoaded = true;
} }
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); } u32 Core::StepCPU() {
if (cpuType == Interpreted)
return interpreter.Step() + regs.PopStalledCycles();
#ifdef KAIZEN_JIT_ENABLED
if (cpuType == DynamicRecompiler)
return jit.Step() + regs.PopStalledCycles();
#endif
panic("Invalid CPU type?");
}
void Core::StepRSP(const u32 cpuCycles) { void Core::StepRSP(const u32 cpuCycles) {
MMIO &mmio = mem->mmio; MMIO &mmio = mem->mmio;
+46 -47
View File
@@ -1,62 +1,61 @@
#pragma once #pragma once
#include <ParallelRDPWrapper.hpp> #include <ParallelRDPWrapper.hpp>
#include <backend/core/Interpreter.hpp> #include <backend/core/Interpreter.hpp>
#ifdef KAIZEN_JIT_ENABLED
#include <backend/core/JIT.hpp> #include <backend/core/JIT.hpp>
#endif
#include <string> #include <string>
#include <set> #include <set>
#include <variant> #include <Registers.hpp>
namespace n64 { namespace n64 {
struct Core { struct Core {
enum CPUType { enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = Interpreted;
Interpreted,
DynamicRecompiler,
CachedInterpreter
} cpuType = Interpreted;
explicit Core();
static Core& GetInstance() {
static Core instance;
return instance;
}
static Registers& GetRegs() {
return GetInstance().regs;
}
static Mem& GetMem() {
return *GetInstance().mem;
}
u32 StepCPU(); explicit Core();
void StepRSP(u32 cpuCycles);
void Stop(); static Core &GetInstance() {
void Reset(); static Core instance;
void LoadROM(const std::string &); return instance;
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; static Registers &GetRegs() { return GetInstance().regs; }
bool romLoaded = false;
int slot = 0;
u32 cycles = 0;
size_t memSize{}, cpuSize{}, verSize{};
std::string rom;
std::set<s64> breakpoints{};
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
std::unique_ptr<BaseCPU> cpu;
Registers regs; static Mem &GetMem() { return *GetInstance().mem; }
ParallelRDP parallel;
u32 StepCPU();
void StepRSP(u32 cpuCycles);
void Stop();
void Reset();
void LoadROM(const std::string &);
void LoadTAS(const fs::path &) const;
void Run(float volumeL, float volumeR);
void TogglePause() { pause = !pause; }
inline void ToggleBreakpoint(s64 addr) {
if (breakpoints.contains(addr)) {
breakpoints.erase(addr);
return;
}
breakpoints.insert(addr);
}
bool pause = true;
bool romLoaded = false;
int slot = 0;
u32 cycles = 0;
size_t memSize{}, cpuSize{}, verSize{};
std::string rom;
std::set<s64> breakpoints{};
#ifdef KAIZEN_JIT_ENABLED
JIT jit;
std::unique_ptr<Mem> mem = std::make_unique<Mem>(jit);
Registers regs(jit);
#else
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
Registers regs;
#endif
Interpreter interpreter;
ParallelRDP parallel;
}; };
} // namespace n64 } // namespace n64
+2 -2
View File
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
file(GLOB HEADERS *.hpp) file(GLOB HEADERS *.hpp)
add_subdirectory(interpreter) add_subdirectory(interpreter)
if(NOT ARM64) if(USE_JIT)
add_subdirectory(jit) add_subdirectory(jit)
endif() endif()
add_subdirectory(mem) add_subdirectory(mem)
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
add_library(core ${SOURCES} ${HEADERS}) add_library(core ${SOURCES} ${HEADERS})
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp) target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
if(NOT ARM64) if(USE_JIT)
target_link_libraries(core PRIVATE jit) target_link_libraries(core PRIVATE jit)
endif() endif()
@@ -1,7 +1,5 @@
#pragma once #pragma once
#include <Mem.hpp> #include <common.hpp>
#include <Registers.hpp>
#include <Disassembler.hpp>
namespace n64 { namespace n64 {
struct alignas(32) InstructionCache { struct alignas(32) InstructionCache {
@@ -10,6 +8,7 @@ struct alignas(32) InstructionCache {
u32 ptag; u32 ptag;
private: private:
friend struct Interpreter;
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; } int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; } u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
}; };
@@ -21,13 +20,8 @@ struct alignas(32) DataCache {
int index; int index;
private: private:
friend struct Interpreter;
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; } int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; } u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
}; };
struct BaseCPU {
virtual ~BaseCPU() = default;
virtual u32 Step() = 0;
virtual void Reset() = 0;
};
} // namespace n64 } // namespace n64
+115 -112
View File
@@ -1,125 +1,128 @@
#pragma once #pragma once
#include <BaseCPU.hpp> #include <Cache.hpp>
#include <Mem.hpp> #include <Mem.hpp>
#include <vector>
namespace n64 { namespace n64 {
struct Core; struct Core;
struct Interpreter final : BaseCPU { struct Interpreter final {
explicit Interpreter(Mem&, Registers&); explicit Interpreter(Mem &, Registers &);
~Interpreter() override = default; ~Interpreter() = default;
u32 Step() override; u32 Step();
void Reset() override { void Reset() { cop2Latch = {}; }
cop2Latch = {};
}
private: private:
Registers& regs; InstructionCache icache;
Mem& mem; DataCache dcache;
u64 cop2Latch{}; Registers &regs;
friend struct Cop1; Mem &mem;
u64 cop2Latch{};
friend struct Cop1;
void cache_type_data(u8);
void cache_type_instruction(u8);
#define check_address_error(mask, vaddr) \ #define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
[[nodiscard]] bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt() const; void CheckCompareInterrupt() const;
void cop2Decode(Instruction); void cop2Decode(Instruction);
void special(Instruction); void special(Instruction);
void regimm(Instruction); void regimm(Instruction);
void Exec(Instruction); void Exec(Instruction);
void add(Instruction); void add(Instruction);
void addu(Instruction); void addu(Instruction);
void addi(Instruction); void addi(Instruction);
void addiu(Instruction); void addiu(Instruction);
void andi(Instruction); void andi(Instruction);
void and_(Instruction); void and_(Instruction);
void branch(bool, s64); void branch(bool, s64);
void branch_likely(bool, s64); void branch_likely(bool, s64);
void b(Instruction, bool); void b(Instruction, bool);
void blink(Instruction, bool); void blink(Instruction, bool);
void bl(Instruction, bool); void bl(Instruction, bool);
void bllink(Instruction, bool); void bllink(Instruction, bool);
void dadd(Instruction); void cache(Instruction);
void daddu(Instruction); void dadd(Instruction);
void daddi(Instruction); void daddu(Instruction);
void daddiu(Instruction); void daddi(Instruction);
void ddiv(Instruction); void daddiu(Instruction);
void ddivu(Instruction); void ddiv(Instruction);
void div(Instruction); void ddivu(Instruction);
void divu(Instruction); void div(Instruction);
void dmult(Instruction); void divu(Instruction);
void dmultu(Instruction); void dmult(Instruction);
void dsll(Instruction); void dmultu(Instruction);
void dsllv(Instruction); void dsll(Instruction);
void dsll32(Instruction); void dsllv(Instruction);
void dsra(Instruction); void dsll32(Instruction);
void dsrav(Instruction); void dsra(Instruction);
void dsra32(Instruction); void dsrav(Instruction);
void dsrl(Instruction); void dsra32(Instruction);
void dsrlv(Instruction); void dsrl(Instruction);
void dsrl32(Instruction); void dsrlv(Instruction);
void dsub(Instruction); void dsrl32(Instruction);
void dsubu(Instruction); void dsub(Instruction);
void j(Instruction); void dsubu(Instruction);
void jr(Instruction); void j(Instruction);
void jal(Instruction); void jr(Instruction);
void jalr(Instruction); void jal(Instruction);
void lui(Instruction); void jalr(Instruction);
void lbu(Instruction); void lui(Instruction);
void lb(Instruction); void lbu(Instruction);
void ld(Instruction); void lb(Instruction);
void ldl(Instruction); void ld(Instruction);
void ldr(Instruction); void ldl(Instruction);
void lh(Instruction); void ldr(Instruction);
void lhu(Instruction); void lh(Instruction);
void ll(Instruction); void lhu(Instruction);
void lld(Instruction); void ll(Instruction);
void lw(Instruction); void lld(Instruction);
void lwl(Instruction); void lw(Instruction);
void lwu(Instruction); void lwl(Instruction);
void lwr(Instruction); void lwu(Instruction);
void mfhi(Instruction); void lwr(Instruction);
void mflo(Instruction); void mfhi(Instruction);
void mult(Instruction); void mflo(Instruction);
void multu(Instruction); void mult(Instruction);
void mthi(Instruction); void multu(Instruction);
void mtlo(Instruction); void mthi(Instruction);
void nor(Instruction); void mtlo(Instruction);
void sb(Instruction); void nor(Instruction);
void sc(Instruction); void sb(Instruction);
void scd(Instruction); void sc(Instruction);
void sd(Instruction); void scd(Instruction);
void sdl(Instruction); void sd(Instruction);
void sdr(Instruction); void sdl(Instruction);
void sh(Instruction); void sdr(Instruction);
void sw(Instruction); void sh(Instruction);
void swl(Instruction); void sw(Instruction);
void swr(Instruction); void swl(Instruction);
void slti(Instruction); void swr(Instruction);
void sltiu(Instruction); void slti(Instruction);
void slt(Instruction); void sltiu(Instruction);
void sltu(Instruction); void slt(Instruction);
void sll(Instruction); void sltu(Instruction);
void sllv(Instruction); void sll(Instruction);
void sub(Instruction); void sllv(Instruction);
void subu(Instruction); void sub(Instruction);
void sra(Instruction); void subu(Instruction);
void srav(Instruction); void sra(Instruction);
void srl(Instruction); void srav(Instruction);
void srlv(Instruction); void srl(Instruction);
void trap(bool) const; void srlv(Instruction);
void or_(Instruction); void trap(bool) const;
void ori(Instruction); void or_(Instruction);
void xor_(Instruction); void ori(Instruction);
void xori(Instruction); void xor_(Instruction);
void xori(Instruction);
void mtc2(Instruction); void mtc2(Instruction);
void mfc2(Instruction); void mfc2(Instruction);
void dmtc2(Instruction); void dmtc2(Instruction);
void dmfc2(Instruction); void dmfc2(Instruction);
void ctc2(Instruction); void ctc2(Instruction);
void cfc2(Instruction); void cfc2(Instruction);
}; };
} // namespace n64 } // namespace n64
+195 -198
View File
@@ -1,246 +1,243 @@
#include <Core.hpp> #include <Core.hpp>
#include <jit/helpers.hpp> #include <JIT.hpp>
#include <Disassembler.hpp>
namespace n64 { namespace n64 {
#ifndef __aarch64__ JIT::JIT(Mem &mem, Registers &regs) : regs(regs), mem(mem) {
JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) { blockCache.resize(kUpperSize);
regs.SetJIT(this); if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
mem.SetJIT(this); CS_ERR_OK) {
blockCache.resize(kUpperSize); panic("Failed to initialize MIPS disassembler");
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(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>(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) { if (cs_open(CS_ARCH_X86, static_cast<cs_mode>(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) {
panic("Failed to initialize x86 disassembler"); panic("Failed to initialize x86 disassembler");
} }
} }
bool JIT::ShouldServiceInterrupt() const { bool JIT::ShouldServiceInterrupt() const {
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0; const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
const bool interrupts_enabled = regs.cop0.status.ie == 1; const bool interrupts_enabled = regs.cop0.status.ie == 1;
const bool currently_handling_exception = regs.cop0.status.exl == 1; const bool currently_handling_exception = regs.cop0.status.exl == 1;
const bool currently_handling_error = regs.cop0.status.erl == 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 { void JIT::CheckCompareInterrupt() const {
regs.cop0.count++; regs.cop0.count++;
regs.cop0.count &= 0x1FFFFFFFF; regs.cop0.count &= 0x1FFFFFFFF;
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) { if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
regs.cop0.cause.ip7 = 1; regs.cop0.cause.ip7 = 1;
Core::GetMem().mmio.mi.UpdateInterrupt(); Core::GetMem().mmio.mi.UpdateInterrupt();
} }
} }
void JIT::InvalidateBlock(const u32 paddr) { void JIT::InvalidateBlock(const u32 paddr) {
if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty()) if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty())
blockCache[index] = {}; blockCache[index] = {};
} }
std::optional<u32> JIT::FetchInstruction(s64 vaddr) { std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
u32 paddr = 0; u32 paddr = 0;
if (check_address_error(0b11, vaddr)) [[unlikely]] { if (check_address_error(0b11, vaddr)) [[unlikely]] {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC); regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, blockPC, {},
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!"); "[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
return std::nullopt; return std::nullopt;
} }
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) { if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!", "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD))); static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return std::nullopt; return std::nullopt;
} }
const u32 instr = Core::GetMem().Read<u32>(paddr); const u32 instr = Core::GetMem().Read<u32>(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) { void JIT::SetPC32(const s32 val) {
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); 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 + OLD_PC_OFFSET], code.SCR1);
code.mov(code.SCR1.cvt32(), val); code.mov(code.SCR1.cvt32(), val);
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
code.mov(code.SCR1.cvt32(), val + 4); code.mov(code.SCR1.cvt32(), val + 4);
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32()); code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
} }
void JIT::SetPC64(const s64 val) { void JIT::SetPC64(const s64 val) {
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); 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 + OLD_PC_OFFSET], code.SCR1);
code.mov(code.SCR1, val); code.mov(code.SCR1, val);
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1); code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
code.mov(code.SCR1, val + 4); code.mov(code.SCR1, val + 4);
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1); code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
} }
void JIT::SetPC32(const Xbyak::Reg32& val) { void JIT::SetPC32(const Xbyak::Reg32 &val) {
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); 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 + OLD_PC_OFFSET], code.SCR1);
code.movsxd(val.cvt64(), val); code.movsxd(val.cvt64(), val);
code.mov(code.qword[code.rbp + PC_OFFSET], val); code.mov(code.qword[code.rbp + PC_OFFSET], val);
code.add(val, 4); code.add(val, 4);
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
} }
void JIT::SetPC64(const Xbyak::Reg64& val) { void JIT::SetPC64(const Xbyak::Reg64 &val) {
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]); 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 + OLD_PC_OFFSET], code.SCR1);
code.mov(code.qword[code.rbp + PC_OFFSET], val); code.mov(code.qword[code.rbp + PC_OFFSET], val);
code.add(val, 4); code.add(val, 4);
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val); code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
} }
u32 JIT::Step() { u32 JIT::Step() {
blockOldPC = regs.oldPC; blockOldPC = regs.oldPC;
blockPC = regs.pc; blockPC = regs.pc;
blockNextPC = regs.nextPC; blockNextPC = regs.nextPC;
u32 paddr = 0; u32 paddr = 0;
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) { if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
/*regs.cop0.HandleTLBException(blockPC); /*regs.cop0.HandleTLBException(blockPC);
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC); regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
return 1;*/ return 1;*/
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
blockPC, {},
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0;
}
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<u64>(blockPC));
const auto blockInfo = code.getCurr();
const auto block = code.getCurr<BlockFn>();
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<uintptr_t>(this)); // Load context pointer
cs_insn *insn;
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(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())) {
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw(
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, {Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); "[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
return 0; 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<u64>(blockPC));
const auto blockInfo = code.getCurr();
const auto block = code.getCurr<BlockFn>();
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<uintptr_t>(this)); // Load context pointer
cs_insn *insn;
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this); emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
}
code.mov(code.rax, instructionsInBlock); while (true) {
code.pop(code.rbp); paddr = 0;
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<uintptr_t>(block)); auto instruction = FetchInstruction(blockPC);
const auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
if (count > 0) { if (!instruction)
for (size_t j = 0; j < count; j++) { return 0;
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
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); code.mov(code.rax, instructionsInBlock);
} code.pop(code.rbp);
// panic(""); code.add(code.rsp, 8);
return block(); code.ret();
code.setProtectModeRE();
static size_t blockInfoSize = 0;
blockInfoSize = code.getSize() - blockInfoSize;
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
const auto count =
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
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 { void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
}
#endif
} // namespace n64 } // namespace n64
+231 -243
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <BaseCPU.hpp> #include <Cache.hpp>
#include <Registers.hpp>
#include <Mem.hpp> #include <Mem.hpp>
#include <vector> #include <vector>
#include <xbyak.h> #include <xbyak.h>
@@ -25,262 +26,249 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
#define HI_OFFSET (reinterpret_cast<uintptr_t>(&regs.hi) - reinterpret_cast<uintptr_t>(this)) #define HI_OFFSET (reinterpret_cast<uintptr_t>(&regs.hi) - reinterpret_cast<uintptr_t>(this))
#define LO_OFFSET (reinterpret_cast<uintptr_t>(&regs.lo) - reinterpret_cast<uintptr_t>(this)) #define LO_OFFSET (reinterpret_cast<uintptr_t>(&regs.lo) - reinterpret_cast<uintptr_t>(this))
#ifdef __aarch64__ struct JIT final {
struct JIT : BaseCPU {}; explicit JIT(Mem &, Registers &);
#else ~JIT() = default;
struct JIT final : BaseCPU { u32 Step();
explicit JIT(Mem&, Registers&);
~JIT() override = default;
u32 Step() override;
void Reset() override { void Reset() {
code.reset(); code.reset();
blockCache = {}; blockCache = {};
blockCache.resize(kUpperSize); 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<std::vector<BlockFn>> blockCache;
template <typename T>
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( void DumpBlockCacheToDisk() const;
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T)); void AdvanceDelaySlot() {
return Xbyak::Address{0}; 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<std::vector<BlockFn>> blockCache;
template <typename T>
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 // Thanks to https://github.com/grumpycoders/pcsx-redux
// Load a pointer to the JIT object in "reg" // Load a pointer to the JIT object in "reg"
template <typename T> template <typename T>
void emitMemberFunctionCall(T func, void *thisObject) { void emitMemberFunctionCall(T func, void *thisObject) {
uintptr_t functionPtr; uintptr_t functionPtr;
auto thisPtr = reinterpret_cast<uintptr_t>(thisObject); auto thisPtr = reinterpret_cast<uintptr_t>(thisObject);
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer"); static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
std::memcpy(&functionPtr, &func, sizeof(T)); std::memcpy(&functionPtr, &func, sizeof(T));
#else #else
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer"); static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
uintptr_t arr[2]; uintptr_t arr[2];
std::memcpy(arr, &func, sizeof(T)); std::memcpy(arr, &func, sizeof(T));
// First 8 bytes correspond to the actual pointer to the function // First 8 bytes correspond to the actual pointer to the function
functionPtr = reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(arr[0])); functionPtr = reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(arr[0]));
// Next 8 bytes correspond to the "this" pointer adjustment // Next 8 bytes correspond to the "this" pointer adjustment
thisPtr += arr[1]; thisPtr += arr[1];
#endif #endif
code.mov(code.ARG1, thisPtr); code.mov(code.ARG1, thisPtr);
code.mov(code.rax, functionPtr); code.mov(code.rax, functionPtr);
code.sub(code.rsp, 8); code.sub(code.rsp, 8);
code.call(code.rax); code.call(code.rax);
code.add(code.rsp, 8); code.add(code.rsp, 8);
} }
void SetPC32(s32 val); void SetPC32(s32 val);
void SetPC64(s64 val); void SetPC64(s64 val);
void SetPC32(const Xbyak::Reg32& val); void SetPC32(const Xbyak::Reg32 &val);
void SetPC64(const Xbyak::Reg64& val); void SetPC64(const Xbyak::Reg64 &val);
void BranchNotTaken(); void BranchNotTaken();
void BranchTaken(s64 offs); void BranchTaken(s64 offs);
void BranchTaken(const Xbyak::Reg64 &offs); void BranchTaken(const Xbyak::Reg64 &offs);
void BranchAbsTaken(s64 addr); void BranchAbsTaken(s64 addr);
void BranchAbsTaken(const Xbyak::Reg64 &addr); void BranchAbsTaken(const Xbyak::Reg64 &addr);
#define check_address_error(mask, vaddr) \ #define check_address_error(mask, vaddr) \
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0)) (((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
[[nodiscard]] bool ShouldServiceInterrupt() const; [[nodiscard]] bool ShouldServiceInterrupt() const;
void CheckCompareInterrupt() const; void CheckCompareInterrupt() const;
std::optional<u32> FetchInstruction(s64); std::optional<u32> FetchInstruction(s64);
void Emit(Instruction); void Emit(Instruction);
void special(Instruction); void special(Instruction);
void regimm(Instruction); void regimm(Instruction);
void add(Instruction); void add(Instruction);
void addu(Instruction); void addu(Instruction);
void addi(Instruction); void addi(Instruction);
void addiu(Instruction); void addiu(Instruction);
void andi(Instruction); void andi(Instruction);
void and_(Instruction); void and_(Instruction);
void branch_constant(bool cond, s64 offset); void branch_constant(bool cond, s64 offset);
void branch_likely_constant(bool cond, s64 offset); void branch_likely_constant(bool cond, s64 offset);
void branch_abs_constant(bool cond, s64 address); void branch_abs_constant(bool cond, s64 address);
void bltz(Instruction); void bltz(Instruction);
void bgez(Instruction); void bgez(Instruction);
void bltzl(Instruction); void bltzl(Instruction);
void bgezl(Instruction); void bgezl(Instruction);
void bltzal(Instruction); void bltzal(Instruction);
void bgezal(Instruction); void bgezal(Instruction);
void bltzall(Instruction); void bltzall(Instruction);
void bgezall(Instruction); void bgezall(Instruction);
void beq(Instruction); void beq(Instruction);
void beql(Instruction); void beql(Instruction);
void bne(Instruction); void bne(Instruction);
void bnel(Instruction); void bnel(Instruction);
void blez(Instruction); void blez(Instruction);
void blezl(Instruction); void blezl(Instruction);
void bgtz(Instruction); void bgtz(Instruction);
void bgtzl(Instruction); void bgtzl(Instruction);
void bfc1(Instruction); void bfc1(Instruction);
void blfc1(Instruction); void blfc1(Instruction);
void bfc0(Instruction); void bfc0(Instruction);
void blfc0(Instruction); void blfc0(Instruction);
void dadd(Instruction); void dadd(Instruction);
void daddu(Instruction); void daddu(Instruction);
void daddi(Instruction); void daddi(Instruction);
void daddiu(Instruction); void daddiu(Instruction);
void ddiv(Instruction); void ddiv(Instruction);
void ddivu(Instruction); void ddivu(Instruction);
void div(Instruction); void div(Instruction);
void divu(Instruction); void divu(Instruction);
void dmult(Instruction); void dmult(Instruction);
void dmultu(Instruction); void dmultu(Instruction);
void dsll(Instruction); void dsll(Instruction);
void dsllv(Instruction); void dsllv(Instruction);
void dsll32(Instruction); void dsll32(Instruction);
void dsra(Instruction); void dsra(Instruction);
void dsrav(Instruction); void dsrav(Instruction);
void dsra32(Instruction); void dsra32(Instruction);
void dsrl(Instruction); void dsrl(Instruction);
void dsrlv(Instruction); void dsrlv(Instruction);
void dsrl32(Instruction); void dsrl32(Instruction);
void dsub(Instruction); void dsub(Instruction);
void dsubu(Instruction); void dsubu(Instruction);
void j(Instruction); void j(Instruction);
void jr(Instruction); void jr(Instruction);
void jal(Instruction); void jal(Instruction);
void jalr(Instruction); void jalr(Instruction);
void lui(Instruction); void lui(Instruction);
void lbu(Instruction); void lbu(Instruction);
void lb(Instruction); void lb(Instruction);
void ld(Instruction); void ld(Instruction);
void ldc1(Instruction); void ldc1(Instruction);
void ldl(Instruction); void ldl(Instruction);
void ldr(Instruction); void ldr(Instruction);
void lh(Instruction); void lh(Instruction);
void lhu(Instruction); void lhu(Instruction);
void ll(Instruction); void ll(Instruction);
void lld(Instruction); void lld(Instruction);
void lw(Instruction); void lw(Instruction);
void lwc1(Instruction); void lwc1(Instruction);
void lwl(Instruction); void lwl(Instruction);
void lwu(Instruction); void lwu(Instruction);
void lwr(Instruction); void lwr(Instruction);
void mfhi(Instruction); void mfhi(Instruction);
void mflo(Instruction); void mflo(Instruction);
void mult(Instruction); void mult(Instruction);
void multu(Instruction); void multu(Instruction);
void mthi(Instruction); void mthi(Instruction);
void mtlo(Instruction); void mtlo(Instruction);
void nor(Instruction); void nor(Instruction);
void sb(const Instruction) { void sb(const Instruction) {
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, blockPC, {}, "[JIT]: Unhandled 'sb'!");
blockPC, {}, "[JIT]: Unhandled 'sb'!"); }
} void sc(const Instruction) {
void sc(const Instruction) { Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw( blockPC, {}, "[JIT]: Unhandled 'sc'!");
{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},
void scd(const Instruction) { blockPC, {}, "[JIT]: Unhandled 'scd'!");
Util::Error::GetInstance().Throw( }
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void sd(const Instruction) {
blockPC, {}, "[JIT]: Unhandled 'scd'!"); Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
} blockPC, {}, "[JIT]: Unhandled 'sd'!");
void sd(const Instruction) { }
Util::Error::GetInstance().Throw( void sdc1(const Instruction) {
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
blockPC, {}, "[JIT]: Unhandled 'sd'!"); blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
} }
void sdc1(const Instruction) { void sdl(const Instruction) {
Util::Error::GetInstance().Throw( Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, blockPC, {}, "[JIT]: Unhandled 'sdl'!");
blockPC, {}, "[JIT]: Unhandled 'sdc1'!"); }
} void sdr(const Instruction) {
void sdl(const Instruction) { Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
Util::Error::GetInstance().Throw( blockPC, {}, "[JIT]: Unhandled 'sdr'!");
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, }
blockPC, {}, "[JIT]: Unhandled 'sdl'!"); void sh(const Instruction) {
} Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
void sdr(const Instruction) { blockPC, {}, "[JIT]: Unhandled 'sh'!");
Util::Error::GetInstance().Throw( }
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void sw(Instruction);
blockPC, {}, "[JIT]: Unhandled 'sdr'!"); void swl(const Instruction) {
} Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
void sh(const Instruction) { blockPC, {}, "[JIT]: Unhandled 'swl'!");
Util::Error::GetInstance().Throw( }
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void swr(const Instruction) {
blockPC, {}, "[JIT]: Unhandled 'sh'!"); Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
} blockPC, {}, "[JIT]: Unhandled 'swr'!");
void sw(Instruction); }
void swl(const Instruction) { void slti(Instruction);
Util::Error::GetInstance().Throw( void sltiu(Instruction);
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void slt(Instruction);
blockPC, {}, "[JIT]: Unhandled 'swl'!"); void sltu(Instruction);
} void sll(Instruction);
void swr(const Instruction) { void sllv(Instruction);
Util::Error::GetInstance().Throw( void sub(Instruction);
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION}, void subu(Instruction);
blockPC, {}, "[JIT]: Unhandled 'swr'!"); void swc1(const Instruction) {
} Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
void slti(Instruction); {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
void sltiu(Instruction); "[JIT]: Unhandled case of branch from delay slot!");
void slt(Instruction); }
void sltu(Instruction); void sra(Instruction);
void sll(Instruction); void srav(Instruction);
void sllv(Instruction); void srl(Instruction);
void sub(Instruction); void srlv(Instruction);
void subu(Instruction); void trap(bool) {
void swc1(const Instruction) { Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
Util::Error::GetInstance().Throw( {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, "[JIT]: Unhandled case of branch from delay slot!");
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!"); }
} void or_(Instruction);
void sra(Instruction); void ori(Instruction);
void srav(Instruction); void xor_(Instruction);
void srl(Instruction); void xori(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 } // namespace n64
+469 -451
View File
File diff suppressed because it is too large Load Diff
+113 -115
View File
@@ -11,144 +11,142 @@
namespace n64 { namespace n64 {
struct ROMHeader { struct ROMHeader {
u8 initialValues[4]; u8 initialValues[4];
char imageName[20]; char imageName[20];
char countryCode[2]; char countryCode[2];
u16 cartridgeId; u16 cartridgeId;
u32 clockRate; u32 clockRate;
u32 programCounter; u32 programCounter;
u32 release; u32 release;
u32 crc1; u32 crc1;
u32 crc2; u32 crc2;
u32 unknown2; u32 unknown2;
u32 manufacturerId; u32 manufacturerId;
u64 unknown; u64 unknown;
}; };
struct ROM { struct ROM {
bool pal; bool pal;
char gameNameCart[20]; char gameNameCart[20];
char code[4]; char code[4];
ROMHeader header; ROMHeader header;
size_t mask; size_t mask;
CICType cicType; CICType cicType;
std::vector<u8> cart; std::vector<u8> cart;
std::string gameNameDB; std::string gameNameDB;
}; };
enum class FlashState : u8 { Idle, Erase, Write, Read, Status }; enum class FlashState : u8 { Idle, Erase, Write, Read, Status };
struct Flash { struct Flash {
explicit Flash(mio::mmap_sink &); explicit Flash(mio::mmap_sink &);
~Flash() = default; ~Flash() = default;
void Reset(); void Reset();
void Load(SaveType, const std::string &); void Load(SaveType, const std::string &);
std::array<u8, 128> writeBuf{}; std::array<u8, 128> writeBuf{};
FlashState state{}; FlashState state{};
u64 status{}; u64 status{};
size_t eraseOffs{}; size_t eraseOffs{};
size_t writeOffs{}; size_t writeOffs{};
std::string flashPath{}; std::string flashPath{};
mio::mmap_sink &saveData; mio::mmap_sink &saveData;
enum FlashCommands : u8 { enum FlashCommands : u8 {
FLASH_COMMAND_EXECUTE = 0xD2, FLASH_COMMAND_EXECUTE = 0xD2,
FLASH_COMMAND_STATUS = 0xE1, FLASH_COMMAND_STATUS = 0xE1,
FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B, FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B,
FLASH_COMMAND_ERASE = 0x78, FLASH_COMMAND_ERASE = 0x78,
FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5, FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5,
FLASH_COMMAND_WRITE = 0xB4, FLASH_COMMAND_WRITE = 0xB4,
FLASH_COMMAND_READ = 0xF0, FLASH_COMMAND_READ = 0xF0,
}; };
void CommandExecute() const; void CommandExecute() const;
void CommandStatus(); void CommandStatus();
void CommandSetEraseOffs(u32); void CommandSetEraseOffs(u32);
void CommandErase(); void CommandErase();
void CommandSetWriteOffs(u32); void CommandSetWriteOffs(u32);
void CommandWrite(); void CommandWrite();
void CommandRead(); void CommandRead();
template <typename T> template <typename T>
void Write(u32 index, T val); void Write(u32 index, T val);
template <typename T> template <typename T>
T Read(u32 index) const; T Read(u32 index) const;
}; };
#ifdef KAIZEN_JIT_ENABLED
struct JIT; struct JIT;
struct Mem { struct Mem {
~Mem() = default; Mem(JIT &jit);
Mem(); JIT &jit;
void Reset(); #else
void LoadSRAM(SaveType, fs::path); struct Mem {
void LoadROM(bool, const std::string &); Mem();
void SetJIT(JIT* jit) { this->jit = jit; } ~Mem() = default;
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); } void Reset();
void LoadSRAM(SaveType, fs::path);
void LoadROM(bool, const std::string &);
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; } [[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
template <typename T> [[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
T Read(u32);
template <typename T>
void Write(u32, u32);
void Write(u32, u64);
template <typename T> template <typename T>
T BackupRead(u32); T Read(u32);
template <typename T> template <typename T>
void BackupWrite(u32, T); void Write(u32, u32);
void Write(u32, u64);
FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const { template <typename T>
std::vector<u8> temp{}; T BackupRead(u32);
temp.resize(size); template <typename T>
std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin()); void BackupWrite(u32, T);
ircolib::SwapBuffer<u32>(temp);
ircolib::WriteFileBinary(temp, "rdram.bin");
}
FORCE_INLINE void DumpIMEM() const { FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const {
std::array<u8, IMEM_SIZE> temp{}; std::vector<u8> temp{};
std::ranges::copy(mmio.rsp.imem, temp.begin()); temp.resize(size);
ircolib::SwapBuffer<u32>(temp); std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin());
ircolib::WriteFileBinary(temp, "imem.bin"); ircolib::SwapBuffer<u32>(temp);
} ircolib::WriteFileBinary(temp, "rdram.bin");
}
FORCE_INLINE void DumpDMEM() const { FORCE_INLINE void DumpIMEM() const {
std::array<u8, DMEM_SIZE> temp{}; std::array<u8, IMEM_SIZE> temp{};
std::ranges::copy(mmio.rsp.dmem, temp.begin()); std::ranges::copy(mmio.rsp.imem, temp.begin());
ircolib::SwapBuffer<u32>(temp); ircolib::SwapBuffer<u32>(temp);
ircolib::WriteFileBinary(temp, "dmem.bin"); ircolib::WriteFileBinary(temp, "imem.bin");
} }
MMIO mmio; FORCE_INLINE void DumpDMEM() const {
ROM rom; std::array<u8, DMEM_SIZE> temp{};
SaveType saveType = SAVE_NONE; std::ranges::copy(mmio.rsp.dmem, temp.begin());
Flash flash; ircolib::SwapBuffer<u32>(temp);
private: ircolib::WriteFileBinary(temp, "dmem.bin");
friend struct SI; }
friend struct PI;
friend struct AI;
friend struct RSP;
friend struct JIT;
friend struct Core;
template <typename T> MMIO mmio;
void WriteInterpreter(u32, u32); ROM rom;
void WriteInterpreter(u32, u64); SaveType saveType = SAVE_NONE;
template <typename T> Flash flash;
void WriteJIT(u32, u32);
void WriteJIT(u32, u64);
std::array<u8, ISVIEWER_SIZE> isviewer{}; private:
std::ofstream isviewer_sink{}; friend struct SI;
int mmioSize{}, flashSize{}; friend struct PI;
JIT *jit = nullptr; friend struct AI;
std::string sramPath{}; friend struct RSP;
mio::mmap_sink saveData{}; friend struct JIT;
friend struct Core;
[[nodiscard]] FORCE_INLINE bool IsROMPAL() const { std::array<u8, ISVIEWER_SIZE> isviewer{};
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'}; std::ofstream isviewer_sink{};
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; }); 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 } // namespace n64
+437 -432
View File
@@ -4,452 +4,457 @@
namespace n64 { namespace n64 {
void Interpreter::special(const Instruction instr) { void Interpreter::special(const Instruction instr) {
// 00rr_rccc // 00rr_rccc
switch (instr.special()) { switch (instr.special()) {
case Instruction::SLL: case Instruction::SLL:
if (instr.instr.raw != 0) { if (instr.instr.raw != 0) {
sll(instr); sll(instr);
}
break;
case Instruction::SRL:
srl(instr);
break;
case Instruction::SRA:
sra(instr);
break;
case Instruction::SLLV:
sllv(instr);
break;
case Instruction::SRLV:
srlv(instr);
break;
case Instruction::SRAV:
srav(instr);
break;
case Instruction::JR:
jr(instr);
break;
case Instruction::JALR:
jalr(instr);
break;
case Instruction::SYSCALL:
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
break;
case Instruction::BREAK:
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
break;
case Instruction::SYNC:
break; // SYNC
case Instruction::MFHI:
mfhi(instr);
break;
case Instruction::MTHI:
mthi(instr);
break;
case Instruction::MFLO:
mflo(instr);
break;
case Instruction::MTLO:
mtlo(instr);
break;
case Instruction::DSLLV:
dsllv(instr);
break;
case Instruction::DSRLV:
dsrlv(instr);
break;
case Instruction::DSRAV:
dsrav(instr);
break;
case Instruction::MULT:
mult(instr);
break;
case Instruction::MULTU:
multu(instr);
break;
case Instruction::DIV:
div(instr);
break;
case Instruction::DIVU:
divu(instr);
break;
case Instruction::DMULT:
dmult(instr);
break;
case Instruction::DMULTU:
dmultu(instr);
break;
case Instruction::DDIV:
ddiv(instr);
break;
case Instruction::DDIVU:
ddivu(instr);
break;
case Instruction::ADD:
add(instr);
break;
case Instruction::ADDU:
addu(instr);
break;
case Instruction::SUB:
sub(instr);
break;
case Instruction::SUBU:
subu(instr);
break;
case Instruction::AND:
and_(instr);
break;
case Instruction::OR:
or_(instr);
break;
case Instruction::XOR:
xor_(instr);
break;
case Instruction::NOR:
nor(instr);
break;
case Instruction::SLT:
slt(instr);
break;
case Instruction::SLTU:
sltu(instr);
break;
case Instruction::DADD:
dadd(instr);
break;
case Instruction::DADDU:
daddu(instr);
break;
case Instruction::DSUB:
dsub(instr);
break;
case Instruction::DSUBU:
dsubu(instr);
break;
case Instruction::TGE:
trap(regs.Read<s64>(instr.rs()) >= regs.Read<s64>(instr.rt()));
break;
case Instruction::TGEU:
trap(regs.Read<u64>(instr.rs()) >= regs.Read<u64>(instr.rt()));
break;
case Instruction::TLT:
trap(regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
break;
case Instruction::TLTU:
trap(regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
break;
case Instruction::TEQ:
trap(regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
break;
case Instruction::TNE:
trap(regs.Read<s64>(instr.rs()) != regs.Read<s64>(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<u64>(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<s64>(instr.rs()) >= regs.Read<s64>(instr.rt()));
break;
case Instruction::TGEU:
trap(regs.Read<u64>(instr.rs()) >= regs.Read<u64>(instr.rt()));
break;
case Instruction::TLT:
trap(regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
break;
case Instruction::TLTU:
trap(regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
break;
case Instruction::TEQ:
trap(regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
break;
case Instruction::TNE:
trap(regs.Read<s64>(instr.rs()) != regs.Read<s64>(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<u64>(regs.oldPC));
}
} }
void Interpreter::regimm(const Instruction instr) { void Interpreter::regimm(const Instruction instr) {
// 000r_rccc // 000r_rccc
switch (instr.regimm()) { switch (instr.regimm()) {
case Instruction::BLTZ: case Instruction::BLTZ:
b(instr, regs.Read<s64>(instr.rs()) < 0); b(instr, regs.Read<s64>(instr.rs()) < 0);
break; break;
case Instruction::BGEZ: case Instruction::BGEZ:
b(instr, regs.Read<s64>(instr.rs()) >= 0); b(instr, regs.Read<s64>(instr.rs()) >= 0);
break; break;
case Instruction::BLTZL: case Instruction::BLTZL:
bl(instr, regs.Read<s64>(instr.rs()) < 0); bl(instr, regs.Read<s64>(instr.rs()) < 0);
break; break;
case Instruction::BGEZL: case Instruction::BGEZL:
bl(instr, regs.Read<s64>(instr.rs()) >= 0); bl(instr, regs.Read<s64>(instr.rs()) >= 0);
break; break;
case Instruction::TGEI: case Instruction::TGEI:
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))); trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
break; break;
case Instruction::TGEIU: case Instruction::TGEIU:
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)))); trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
break; break;
case Instruction::TLTI: case Instruction::TLTI:
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))); trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
break; break;
case Instruction::TLTIU: case Instruction::TLTIU:
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)))); trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
break; break;
case Instruction::TEQI: case Instruction::TEQI:
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))); trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
break; break;
case Instruction::TNEI: case Instruction::TNEI:
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))); trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
break; break;
case Instruction::BLTZAL: case Instruction::BLTZAL:
blink(instr, regs.Read<s64>(instr.rs()) < 0); blink(instr, regs.Read<s64>(instr.rs()) < 0);
break; break;
case Instruction::BGEZAL: case Instruction::BGEZAL:
blink(instr, regs.Read<s64>(instr.rs()) >= 0); blink(instr, regs.Read<s64>(instr.rs()) >= 0);
break; break;
case Instruction::BLTZALL: case Instruction::BLTZALL:
bllink(instr, regs.Read<s64>(instr.rs()) < 0); bllink(instr, regs.Read<s64>(instr.rs()) < 0);
break; break;
case Instruction::BGEZALL: case Instruction::BGEZALL:
bllink(instr, regs.Read<s64>(instr.rs()) >= 0); bllink(instr, regs.Read<s64>(instr.rs()) >= 0);
break; break;
default: default:
panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi, panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi,
instr.instr.opcode.regimm_lo, u32(instr), static_cast<u64>(regs.oldPC)); instr.instr.opcode.regimm_lo, u32(instr), static_cast<u64>(regs.oldPC));
} }
} }
void Interpreter::cop2Decode(const Instruction instr) { void Interpreter::cop2Decode(const Instruction instr) {
if (!regs.cop0.status.cu2) { if (!regs.cop0.status.cu2) {
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC); regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
return; return;
} }
switch (instr.rs()) { switch (instr.rs()) {
case 0x00: case 0x00:
mfc2(instr); mfc2(instr);
break; break;
case 0x01: case 0x01:
dmfc2(instr); dmfc2(instr);
break; break;
case 0x02: case 0x02:
cfc2(instr); cfc2(instr);
break; break;
case 0x04: case 0x04:
mtc2(instr); mtc2(instr);
break; break;
case 0x05: case 0x05:
dmtc2(instr); dmtc2(instr);
break; break;
case 0x06: case 0x06:
ctc2(instr); ctc2(instr);
break; break;
default: default:
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC); regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
} }
} }
void Interpreter::Exec(const Instruction instr) { void Interpreter::Exec(const Instruction instr) {
// 00rr_rccc // 00rr_rccc
switch (instr.opcode()) { switch (instr.opcode()) {
case Instruction::SPECIAL: case Instruction::SPECIAL:
special(instr); special(instr);
break; break;
case Instruction::REGIMM: case Instruction::REGIMM:
regimm(instr); regimm(instr);
break; break;
case Instruction::J: case Instruction::J:
j(instr); j(instr);
break; break;
case Instruction::JAL: case Instruction::JAL:
jal(instr); jal(instr);
break; break;
case Instruction::BEQ: case Instruction::BEQ:
b(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt())); b(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
break; break;
case Instruction::BNE: case Instruction::BNE:
b(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt())); b(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
break; break;
case Instruction::BLEZ: case Instruction::BLEZ:
b(instr, regs.Read<s64>(instr.rs()) <= 0); b(instr, regs.Read<s64>(instr.rs()) <= 0);
break; break;
case Instruction::BGTZ: case Instruction::BGTZ:
b(instr, regs.Read<s64>(instr.rs()) > 0); b(instr, regs.Read<s64>(instr.rs()) > 0);
break; break;
case Instruction::ADDI: case Instruction::ADDI:
addi(instr); addi(instr);
break; break;
case Instruction::ADDIU: case Instruction::ADDIU:
addiu(instr); addiu(instr);
break; break;
case Instruction::SLTI: case Instruction::SLTI:
slti(instr); slti(instr);
break; break;
case Instruction::SLTIU: case Instruction::SLTIU:
sltiu(instr); sltiu(instr);
break; break;
case Instruction::ANDI: case Instruction::ANDI:
andi(instr); andi(instr);
break; break;
case Instruction::ORI: case Instruction::ORI:
ori(instr); ori(instr);
break; break;
case Instruction::XORI: case Instruction::XORI:
xori(instr); xori(instr);
break; break;
case Instruction::LUI: case Instruction::LUI:
lui(instr); lui(instr);
break; break;
case Instruction::COP0: case Instruction::COP0:
regs.cop0.decode(instr); regs.cop0.decode(instr);
break; break;
case Instruction::COP1: case Instruction::COP1:
if(instr.cop_rs() == 0x08) { if (instr.cop_rs() == 0x08) {
switch (instr.cop_rt()) { switch (instr.cop_rt()) {
case 0: case 0:
if (!regs.cop1.CheckFPUUsable()) 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; return;
b(instr, !regs.cop1.fcr31.compare); }
break; regs.cop1.decode(instr);
case 1: break;
if (!regs.cop1.CheckFPUUsable()) case Instruction::COP2:
cop2Decode(instr);
break;
case Instruction::BEQL:
bl(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
break;
case Instruction::BNEL:
bl(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
break;
case Instruction::BLEZL:
bl(instr, regs.Read<s64>(instr.rs()) <= 0);
break;
case Instruction::BGTZL:
bl(instr, regs.Read<s64>(instr.rs()) > 0);
break;
case Instruction::DADDI:
daddi(instr);
break;
case Instruction::DADDIU:
daddiu(instr);
break;
case Instruction::LDL:
ldl(instr);
break;
case Instruction::LDR:
ldr(instr);
break;
case 0x1F:
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
break;
case Instruction::LB:
lb(instr);
break;
case Instruction::LH:
lh(instr);
break;
case Instruction::LWL:
lwl(instr);
break;
case Instruction::LW:
lw(instr);
break;
case Instruction::LBU:
lbu(instr);
break;
case Instruction::LHU:
lhu(instr);
break;
case Instruction::LWR:
lwr(instr);
break;
case Instruction::LWU:
lwu(instr);
break;
case Instruction::SB:
sb(instr);
break;
case Instruction::SH:
sh(instr);
break;
case Instruction::SWL:
swl(instr);
break;
case Instruction::SW:
sw(instr);
break;
case Instruction::SDL:
sdl(instr);
break;
case Instruction::SDR:
sdr(instr);
break;
case Instruction::SWR:
swr(instr);
break;
case Instruction::CACHE:
{
panic("CACHE 0b{:05b}, 0x{:04X}({}/r{} = 0x{:08X})", instr.op(), instr.offset(),
Registers::regNames[instr.base()], instr.base(), regs.Read<u64>(instr.base()));
}
break;
case Instruction::LL:
ll(instr);
break;
case Instruction::LWC1:
if (!regs.cop1.CheckFPUUsable<true>())
return; return;
b(instr, regs.cop1.fcr31.compare); regs.cop1.lwc1(instr);
break; break;
case 2: case Instruction::LLD:
if (!regs.cop1.CheckFPUUsable()) lld(instr);
break;
case Instruction::LDC1:
if (!regs.cop1.CheckFPUUsable<true>())
return; return;
bl(instr, !regs.cop1.fcr31.compare); regs.cop1.ldc1(instr);
break; break;
case 3: case Instruction::LD:
if (!regs.cop1.CheckFPUUsable()) ld(instr);
break;
case Instruction::SC:
sc(instr);
break;
case Instruction::SWC1:
if (!regs.cop1.CheckFPUUsable<true>())
return; return;
bl(instr, regs.cop1.fcr31.compare); regs.cop1.swc1(instr);
break; break;
default: case Instruction::SCD:
panic("Undefined BC COP1 {:02X}", instr.cop_rt()); scd(instr);
} break;
return; case Instruction::SDC1:
if (!regs.cop1.CheckFPUUsable<true>())
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<u64>(regs.oldPC));
} }
regs.cop1.decode(instr);
break;
case Instruction::COP2:
cop2Decode(instr);
break;
case Instruction::BEQL:
bl(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
break;
case Instruction::BNEL:
bl(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
break;
case Instruction::BLEZL:
bl(instr, regs.Read<s64>(instr.rs()) <= 0);
break;
case Instruction::BGTZL:
bl(instr, regs.Read<s64>(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<true>())
return;
regs.cop1.lwc1(instr);
break;
case Instruction::LLD:
lld(instr);
break;
case Instruction::LDC1:
if (!regs.cop1.CheckFPUUsable<true>())
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<true>())
return;
regs.cop1.swc1(instr);
break;
case Instruction::SCD:
scd(instr);
break;
case Instruction::SDC1:
if (!regs.cop1.CheckFPUUsable<true>())
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<u64>(regs.oldPC));
}
} }
} // namespace n64 } // namespace n64
File diff suppressed because it is too large Load Diff
+151 -160
View File
@@ -3,361 +3,352 @@
#include <core/JIT.hpp> #include <core/JIT.hpp>
namespace n64 { namespace n64 {
#ifdef KAIZEN_JIT_ENABLED
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
#else
Registers::Registers() { Reset(); } Registers::Registers() { Reset(); }
#endif
void Registers::Reset() { void Registers::Reset() {
hi = 0; hi = 0;
lo = 0; lo = 0;
delaySlot = false; delaySlot = false;
prevDelaySlot = false; prevDelaySlot = false;
gpr.fill(0); gpr.fill(0);
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
cop0.Reset(); cop0.Reset();
cop1.Reset(); cop1.Reset();
steps = 0; steps = 0;
extraCycles = 0; extraCycles = 0;
} }
void Registers::SetPC64(s64 val) { void Registers::SetPC64(s64 val) {
oldPC = pc; oldPC = pc;
pc = val; pc = val;
nextPC = pc + 4; nextPC = pc + 4;
} }
void Registers::SetPC32(s32 val) { void Registers::SetPC32(s32 val) {
oldPC = pc; oldPC = pc;
pc = s64(val); pc = s64(val);
nextPC = pc + 4; nextPC = pc + 4;
} }
template <> template <>
u64 Registers::Read<u64>(size_t idx) { u64 Registers::Read<u64>(size_t idx) {
return gpr[idx]; return gpr[idx];
} }
template <> template <>
s64 Registers::Read<s64>(const size_t idx) { s64 Registers::Read<s64>(const size_t idx) {
return static_cast<s64>(Read<u64>(idx)); return static_cast<s64>(Read<u64>(idx));
} }
template <> template <>
u32 Registers::Read<u32>(size_t idx) { u32 Registers::Read<u32>(size_t idx) {
return gpr[idx]; return gpr[idx];
} }
template <> template <>
s32 Registers::Read<s32>(size_t idx) { s32 Registers::Read<s32>(size_t idx) {
return static_cast<s32>(Read<u32>(idx)); return static_cast<s32>(Read<u32>(idx));
} }
template <> template <>
u16 Registers::Read<u16>(size_t idx) { u16 Registers::Read<u16>(size_t idx) {
return gpr[idx]; return gpr[idx];
} }
template <> template <>
s16 Registers::Read<s16>(size_t idx) { s16 Registers::Read<s16>(size_t idx) {
return static_cast<s16>(Read<u16>(idx)); return static_cast<s16>(Read<u16>(idx));
} }
template <> template <>
u8 Registers::Read<u8>(size_t idx) { u8 Registers::Read<u8>(size_t idx) {
return gpr[idx]; return gpr[idx];
} }
template <> template <>
s8 Registers::Read<s8>(size_t idx) { s8 Registers::Read<s8>(size_t idx) {
return static_cast<s8>(Read<u8>(idx)); return static_cast<s8>(Read<u8>(idx));
} }
#ifndef __aarch64__ #ifdef KAIZEN_JIT_ENABLED
template <> template <>
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) { void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt64(), Read<u64>(idx)); jit.code.mov(reg.cvt64(), Read<u64>(idx));
return; return;
} }
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx)); jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
} }
template <> template <>
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) { void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt64(), Read<s64>(idx)); jit.code.mov(reg.cvt64(), Read<s64>(idx));
return; return;
} }
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx)); jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
} }
template <> template <>
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) { void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt32(), Read<u32>(idx)); jit.code.mov(reg.cvt32(), Read<u32>(idx));
return; return;
} }
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx)); jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
} }
template <> template <>
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) { void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt32(), Read<s32>(idx)); jit.code.mov(reg.cvt32(), Read<s32>(idx));
return; return;
} }
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx)); jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
} }
template <> template <>
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) { void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt16(), Read<u16>(idx)); jit.code.mov(reg.cvt16(), Read<u16>(idx));
return; return;
} }
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx)); jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
} }
template <> template <>
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) { void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt16(), Read<s16>(idx)); jit.code.mov(reg.cvt16(), Read<s16>(idx));
return; return;
} }
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx)); jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
} }
template <> template <>
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) { void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt8(), Read<u8>(idx)); jit.code.mov(reg.cvt8(), Read<u8>(idx));
return; return;
} }
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx)); jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
} }
template <> template <>
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) { void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
if(IsRegConstant(idx)) { if (IsRegConstant(idx)) {
jit->code.mov(reg.cvt8(), Read<s8>(idx)); jit.code.mov(reg.cvt8(), Read<s8>(idx));
return; return;
} }
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx)); jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
} }
#endif #endif
template <> template <>
void Registers::Write<bool>(size_t idx, bool v) { void Registers::Write<bool>(size_t idx, bool v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v; gpr[idx] = v;
} }
template <> template <>
void Registers::Write<u64>(size_t idx, u64 v) { void Registers::Write<u64>(size_t idx, u64 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<s64>(size_t idx, s64 v) { void Registers::Write<s64>(size_t idx, s64 v) {
Write<u64>(idx, v); Write<u64>(idx, v);
} }
template <> template <>
void Registers::Write<u32>(size_t idx, u32 v) { void Registers::Write<u32>(size_t idx, u32 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<s32>(size_t idx, s32 v) { void Registers::Write<s32>(size_t idx, s32 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<u16>(size_t idx, u16 v) { void Registers::Write<u16>(size_t idx, u16 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<s16>(size_t idx, s16 v) { void Registers::Write<s16>(size_t idx, s16 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<u8>(size_t idx, u8 v) { void Registers::Write<u8>(size_t idx, u8 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
template <> template <>
void Registers::Write<s8>(size_t idx, s8 v) { void Registers::Write<s8>(size_t idx, s8 v) {
if (idx == 0) if (idx == 0)
return; return;
if (jit) [[unlikely]]
regIsConstant |= (1 << idx); regIsConstant |= (1 << idx);
gpr[idx] = v;
gpr[idx] = v;
} }
#ifndef __aarch64__ #ifdef KAIZEN_JIT_ENABLED
template <> template <>
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) { void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) regIsConstant &= ~(1 << idx);
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx);
jit->code.movsx(v.cvt64(), v.cvt8()); jit.code.movsx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v); jit.code.mov(jit.GPR<u64>(idx), v);
} }
template <> template <>
void Registers::Write<s8>(size_t idx, Xbyak::Reg v) { void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) regIsConstant &= ~(1 << idx);
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx); jit.code.movsx(v.cvt64(), v.cvt8());
jit.code.mov(jit.GPR<u64>(idx), v);
jit->code.movsx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v);
} }
template <> template <>
void Registers::Write<u8>(size_t idx, Xbyak::Reg v) { void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) regIsConstant &= ~(1 << idx);
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx); jit.code.movzx(v.cvt64(), v.cvt8());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
jit->code.movzx(v.cvt64(), v.cvt8());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<s16>(size_t idx, Xbyak::Reg v) { void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) regIsConstant &= ~(1 << idx);
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
regIsConstant &= ~(1 << idx); jit.code.movsx(v.cvt64(), v.cvt16());
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
jit->code.movsx(v.cvt64(), v.cvt16());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<u16>(size_t idx, Xbyak::Reg v) { void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); 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.movzx(v.cvt64(), v.cvt16());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64()); jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<s32>(size_t idx, Xbyak::Reg v) { void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); 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.movsxd(v.cvt64(), v.cvt32());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64()); jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<u32>(size_t idx, Xbyak::Reg v) { void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); 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.movzx(v.cvt64(), v.cvt32());
jit->code.mov(jit->GPR<u64>(idx), v.cvt64()); jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<u64>(size_t idx, Xbyak::Reg v) { void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
if (idx == 0) if (idx == 0)
return; return;
if (!jit) if (!jit)
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?"); 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<u64>(idx), v.cvt64()); jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
} }
template <> template <>
void Registers::Write<s64>(size_t idx, Xbyak::Reg v) { void Registers::Write<s64>(size_t idx, Xbyak::Reg v) {
Write<u64>(idx, v); Write<u64>(idx, v);
} }
#endif #endif
} // namespace n64 } // namespace n64
+50 -55
View File
@@ -4,77 +4,72 @@
#include <backend/core/registers/Cop1.hpp> #include <backend/core/registers/Cop1.hpp>
namespace n64 { namespace n64 {
#ifdef KAIZEN_JIT_ENABLED
struct JIT; struct JIT;
struct Registers { struct Registers {
Registers(); JIT &jit;
void Reset(); Registers(JIT &jit);
void SetPC64(s64); #else
void SetPC32(s32); struct Registers {
void SetJIT(JIT* jit) { this->jit = jit; } Registers();
void Reset();
void SetPC64(s64);
void SetPC32(s32);
[[nodiscard]] bool IsRegConstant(const u32 index) const { [[nodiscard]] bool IsRegConstant(const u32 index) const {
if (index == 0) if (index == 0)
return true; return true;
return regIsConstant & (1 << index); return regIsConstant & (1 << index);
} }
[[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const { [[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const {
return IsRegConstant(index1) && IsRegConstant(index2); return IsRegConstant(index1) && IsRegConstant(index2);
} }
bool GetLOConstant() { bool GetLOConstant() { return regIsConstant & (1ull << 32); }
return regIsConstant & (1ull << 32);
}
bool GetHIConstant() { bool GetHIConstant() { return regIsConstant & (1ull << 33); }
return regIsConstant & (1ull << 33);
}
void SetLOConstant() { void SetLOConstant() { regIsConstant |= (1ull << 32); }
regIsConstant |= (1ull << 32);
}
void SetHIConstant() { void SetHIConstant() { regIsConstant |= (1ull << 33); }
regIsConstant |= (1ull << 33);
}
void UnsetLOConstant() { void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
regIsConstant &= ~(1ull << 32);
}
void UnsetHIConstant() { void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
regIsConstant &= ~(1ull << 33);
}
JIT *jit = nullptr; uint64_t regIsConstant = 0;
uint64_t regIsConstant = 0; bool prevDelaySlot{}, delaySlot{};
u32 steps = 0;
u32 extraCycles = 0;
s64 oldPC{}, pc{}, nextPC{};
s64 hi{}, lo{};
Cop0 cop0;
Cop1 cop1;
bool prevDelaySlot{}, delaySlot{}; void CpuStall(u32 cycles) { extraCycles += cycles; }
u32 steps = 0;
u32 extraCycles = 0;
s64 oldPC{}, pc{}, nextPC{};
s64 hi{}, lo{};
Cop0 cop0;
Cop1 cop1;
void CpuStall(u32 cycles) { extraCycles += cycles; } u32 PopStalledCycles() {
u32 ret = extraCycles;
extraCycles = 0;
return ret;
}
u32 PopStalledCycles() { template <typename T>
u32 ret = extraCycles; T Read(size_t);
extraCycles = 0; template <typename T>
return ret; void Read(size_t, Xbyak::Reg);
} template <typename T>
void Write(size_t, T);
template <typename T>
void Write(size_t, Xbyak::Reg);
template <typename T> std::array<s64, 32> gpr{};
T Read(size_t);
template <typename T>
void Read(size_t, Xbyak::Reg);
template <typename T>
void Write(size_t, T);
template <typename T>
void Write(size_t, Xbyak::Reg);
std::array<s64, 32> gpr{}; static inline const char *regNames[] = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2",
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
}; };
#endif
} // namespace n64 } // namespace n64
+207 -213
View File
@@ -1,249 +1,243 @@
#include <Debugger.hpp> #include <Debugger.hpp>
#include <imgui.h> #include <imgui.h>
#include <Registers.hpp>
char const* regNames[] = { void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
"zero", "at", "v0", "v1", n64::Core &core = n64::Core::GetInstance();
"a0", "a1", "a2", "a3", bool isBroken = core.breakpoints.contains(addr);
"t0", "t1", "t2", "t3", ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
"t4", "t5", "t6", "t7", ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
"s0", "s1", "s2", "s3", ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
"s4", "s5", "s6", "s7", ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
"t8", "t9", "k0", "k1", ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
"gp", "sp", "s8", "ra", if (ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
}; core.ToggleBreakpoint(addr);
}
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) { ImGui::PopStyleVar();
n64::Core& core = n64::Core::GetInstance(); ImGui::PopStyleColor();
bool isBroken = core.breakpoints.contains(addr); ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff); ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0); ImGui::PopStyleColor();
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) { void AddressFunc(s64, Disassembler::DisassemblyResult &disasm) {
if(!disasm.success) { if (!disasm.success) {
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????"); ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
return; 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;
} }
std::string op_str = disasm.ops[i].str; ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
if(!disasm.ops[i+1].str.empty()) }
op_str += ", ";
void InstructionFunc(s64, Disassembler::DisassemblyResult &disasm) {
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str()); 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); 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() { void Debugger::RegisterView() {
if(!ImGui::BeginTabItem("Registers")) if (!ImGui::BeginTabItem("Registers"))
return; return;
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
return;
ImGui::TableSetupColumn("Name"); if (!ImGui::BeginTable("##regs", 4,
ImGui::TableSetupColumn("Value"); ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
ImGui::TableSetupColumn("Name"); ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
ImGui::TableSetupColumn("Value"); ImGuiTableFlags_ContextMenuInBody))
return;
ImGui::TableHeadersRow();
auto renderMemoryTable = [&](u64 vaddr) { ImGui::TableSetupColumn("Name");
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip)) ImGui::TableSetupColumn("Value");
return; ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Value");
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::TableHeadersRow(); ImGui::TableHeadersRow();
for(u32 row = 0; row < 16; row++) { auto renderMemoryTable = [&](u64 vaddr) {
ImGui::TableNextRow(); if (!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
for(u32 col = 0; col < 16; col+=4) { return;
u32 paddr;
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
continue;
const u32 val = n64::Core::GetMem().Read<u32>(paddr); if (!ImGui::BeginTooltip())
return;
ImGui::TableSetColumnIndex(col+0); ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
ImGui::Text("%02X", (val >> 24) & 0xff); if (!ImGui::BeginTable("##memoryContents", 16))
ImGui::TableSetColumnIndex(col+1); return;
ImGui::Text("%02X", (val >> 16) & 0xff);
ImGui::TableSetColumnIndex(col+2); for (u32 col = 0; col < 16; col++)
ImGui::Text("%02X", (val >> 8) & 0xff); ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
ImGui::TableSetColumnIndex(col+3);
ImGui::Text("%02X", (val >> 0) & 0xff); 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<u32>(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 &regs = 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<u64>(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<u64>(i + 1);
ImGui::Text("%s", std::format("{:016X}", value).c_str());
renderMemoryTable(value);
} }
ImGui::EndTable(); ImGui::EndTable();
ImGui::EndTooltip(); ImGui::EndTabItem();
};
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<u64>(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<u64>(i+1);
ImGui::Text("%s", std::format("{:016X}", value).c_str());
renderMemoryTable(value);
}
ImGui::EndTable();
ImGui::EndTabItem();
} }
bool Debugger::render() { bool Debugger::render() {
n64::Core &core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
const n64::Registers& regs = n64::Core::GetRegs(); const n64::Registers &regs = n64::Core::GetRegs();
if(!enabled) if (!enabled)
return false; return false;
static s64 startAddr = 0xFFFF'FFFF'8000'0000; static s64 startAddr = 0xFFFF'FFFF'8000'0000;
constexpr int step = 4; constexpr int step = 4;
constexpr int stepFast = 256; constexpr int stepFast = 256;
if(!ImGui::Begin("Debugger", &enabled)) {
ImGui::End();
return false;
}
ImGui::BeginDisabled(followPC); if (!ImGui::Begin("Debugger", &enabled)) {
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal); ImGui::End();
ImGui::EndDisabled(); return false;
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(addrIsBreakpoint) { ImGui::BeginDisabled(followPC);
colorChoice = 0x800000ff; ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX",
colorChoiceAlt = 0x800000ff; 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); if (!ImGui::BeginTabBar("##debuggerTabs")) {
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value); ImGui::EndTabBar();
ImGui::End();
ImGui::TableNextRow(); return false;
for(int i = 0; auto &[_, func] : columns) {
ImGui::TableSetColumnIndex(i++);
func(addr, disasm);
} }
ImGui::PopStyleColor(); RegisterView();
ImGui::PopStyleColor();
}
ImGui::EndTable(); if (!ImGui::BeginTabItem("MIPS R4300i code view")) {
ImGui::EndTabBar();
ImGui::End();
return false;
}
ImGui::EndTabItem(); constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
ImGui::EndTabBar(); ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
ImGui::End(); if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
return true; 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;
} }
+26 -21
View File
@@ -1,28 +1,33 @@
#pragma once #pragma once
#include <backend/Core.hpp> #include <backend/Core.hpp>
#include <Disassembler.hpp>
void BreakpointFunc(s64, Disassembler::DisassemblyResult&); void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
void AddressFunc(s64, Disassembler::DisassemblyResult&); void AddressFunc(s64, Disassembler::DisassemblyResult &);
void InstructionFunc(s64, Disassembler::DisassemblyResult&); void InstructionFunc(s64, Disassembler::DisassemblyResult &);
class Debugger final { class Debugger final {
bool enabled = false; bool enabled = false;
static constexpr auto MAX_LINES_OF_DISASM = 150; static constexpr auto MAX_LINES_OF_DISASM = 150;
struct Column { struct Column {
const char* name = nullptr; const char *name = nullptr;
void (*func)(s64, Disassembler::DisassemblyResult&) = nullptr; void (*func)(s64, Disassembler::DisassemblyResult &) = nullptr;
}; };
std::array<Column, 3> columns = { std::array<Column, 3> columns = {
Column{"##BreakpointColumn", &BreakpointFunc}, Column{"##BreakpointColumn", &BreakpointFunc},
Column{"Address", &AddressFunc}, Column{"Address", &AddressFunc},
Column{"Instruction", &InstructionFunc}, Column{"Instruction", &InstructionFunc},
}; };
public:
static void RegisterView(); public:
bool followPC = true; static void RegisterView();
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; } bool followPC = true;
void Close() { enabled = false; } void Open(bool wantFollowPC = true) {
bool render(); enabled = true;
}; followPC = wantFollowPC;
}
void Close() { enabled = false; }
bool render();
};
+31 -30
View File
@@ -4,40 +4,41 @@
#include <imgui.h> #include <imgui.h>
CPUSettings::CPUSettings() { CPUSettings::CPUSettings() {
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") { if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") {
selectedCpuTypeIndex = 1; selectedCpuTypeIndex = 1;
} else { } else {
selectedCpuTypeIndex = 0; selectedCpuTypeIndex = 0;
} }
} }
void CPUSettings::render() { void CPUSettings::render() {
const char* items[] = { const char *items[] = {"Interpreter",
"Interpreter", #ifdef KAIZEN_JIT_ENABLED
"Dynamic Recompiler" "Dynamic Recompiler"
}; #endif
};
const char* combo_preview_value = items[selectedCpuTypeIndex]; const char *combo_preview_value = items[selectedCpuTypeIndex];
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) { if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
for (int n = 0; n < IM_ARRAYSIZE(items); n++) { for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
const bool is_selected = (selectedCpuTypeIndex == n); const bool is_selected = (selectedCpuTypeIndex == n);
if (ImGui::Selectable(items[n], is_selected)) { if (ImGui::Selectable(items[n], is_selected)) {
selectedCpuTypeIndex = n; selectedCpuTypeIndex = n;
modified = true; modified = true;
} }
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
if(modified) { // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
if(selectedCpuTypeIndex == 0) { if (is_selected)
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter"); ImGui::SetItemDefaultFocus();
} else { }
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit"); ImGui::EndCombo();
}
if (modified) {
if (selectedCpuTypeIndex == 0) {
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
} else {
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
}
} }
}
} }
+35 -33
View File
@@ -7,7 +7,7 @@ struct Instruction {
Instruction(u32 v) { instr.raw = v; } Instruction(u32 v) { instr.raw = v; }
void operator=(u32 v) { instr.raw = v; } void operator=(u32 v) { instr.raw = v; }
operator u32() const { return instr.raw; } operator u32() const { return instr.raw; }
inline u8 rs() const { return instr.rtype.rs; } inline u8 rs() const { return instr.rtype.rs; }
inline u8 rt() const { return instr.rtype.rt; } inline u8 rt() const { return instr.rtype.rt; }
inline u8 rd() const { return instr.rtype.rd; } inline u8 rd() const { return instr.rtype.rd; }
@@ -21,7 +21,9 @@ struct Instruction {
inline u8 vd() const { return fd(); } inline u8 vd() const { return fd(); }
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; } inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
inline u8 e2() const { return rs() & 0x0f; } inline u8 e2() const { return rs() & 0x0f; }
inline u8 op() const { return rt(); }
inline u16 imm() const { return instr.itype.imm; } inline u16 imm() const { return instr.itype.imm; }
inline u16 offset() const { return imm(); }
inline u32 target() const { return instr.jtype.target; } inline u32 target() const { return instr.jtype.target; }
inline u8 opcode() const { return instr.opcode.op; } inline u8 opcode() const { return instr.opcode.op; }
inline u8 special() const { return instr.opcode.special; } inline u8 special() const { return instr.opcode.special; }
@@ -32,61 +34,61 @@ struct Instruction {
union { union {
struct { struct {
unsigned imm:16; unsigned imm : 16;
unsigned rt:5; unsigned rt : 5;
unsigned rs:5; unsigned rs : 5;
unsigned op:6; unsigned op : 6;
} itype; } itype;
struct { struct {
unsigned target:26; unsigned target : 26;
unsigned op:6; unsigned op : 6;
} jtype; } jtype;
struct { struct {
unsigned funct:6; unsigned funct : 6;
unsigned sa:5; unsigned sa : 5;
unsigned rd:5; unsigned rd : 5;
unsigned rt:5; unsigned rt : 5;
unsigned rs:5; unsigned rs : 5;
unsigned op:6; unsigned op : 6;
} rtype; } rtype;
union { union {
struct { struct {
unsigned special_lo:3; unsigned special_lo : 3;
unsigned special_hi:3; unsigned special_hi : 3;
unsigned:26; unsigned : 26;
}; };
struct { struct {
unsigned special:6; unsigned special : 6;
unsigned:26; unsigned : 26;
}; };
struct { struct {
unsigned:16; unsigned : 16;
unsigned regimm_lo:3; unsigned regimm_lo : 3;
unsigned regimm_hi:2; unsigned regimm_hi : 2;
unsigned:11; unsigned : 11;
}; };
struct { struct {
unsigned:16; unsigned : 16;
unsigned regimm:5; unsigned regimm : 5;
unsigned:11; unsigned : 11;
}; };
struct { struct {
unsigned:26; unsigned : 26;
unsigned op:6; unsigned op : 6;
}; };
struct { struct {
unsigned funct:6; unsigned funct : 6;
unsigned:10; unsigned : 10;
unsigned cop_rt:5; unsigned cop_rt : 5;
unsigned cop_rs:5; unsigned cop_rs : 5;
unsigned:6; unsigned : 6;
}; };
u32 raw; u32 raw;
@@ -221,4 +223,4 @@ struct Instruction {
static constexpr u8 BLTZALL = 0b10010; static constexpr u8 BLTZALL = 0b10010;
static constexpr u8 BGEZALL = 0b10011; static constexpr u8 BGEZALL = 0b10011;
}; };
} } // namespace n64