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