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