Compare commits
12 Commits
6710c37adc
...
58e9cc1bc3
| Author | SHA1 | Date | |
|---|---|---|---|
| 58e9cc1bc3 | |||
| 24fb2898e9 | |||
| 2147195774 | |||
| bb97dcc23f | |||
| 920b77d381 | |||
| 430ccdab40 | |||
| 4f42a673a3 | |||
| c9a0307878 | |||
| 5fbda03ceb | |||
| 366637aba3 | |||
| 609fa2fb08 | |||
| e140a6d124 |
+4
-1
@@ -4,6 +4,7 @@ saves/
|
||||
.cache/
|
||||
.vs/
|
||||
.vscode/
|
||||
.zed/
|
||||
out/
|
||||
*.toml
|
||||
*.ini
|
||||
@@ -20,9 +21,11 @@ vgcore.*
|
||||
*.data
|
||||
disasm.txt
|
||||
*log*.txt
|
||||
*.log
|
||||
CMakeSettings.json
|
||||
compile_commands.json
|
||||
*.diagsession
|
||||
tests/
|
||||
.DS_Store
|
||||
resources/version.hpp
|
||||
resources/version.hpp
|
||||
__cmake_systeminformation/CMakeFiles/
|
||||
|
||||
@@ -13,6 +13,7 @@ if(APPLE)
|
||||
enable_language(OBJC)
|
||||
endif()
|
||||
|
||||
set(USE_JIT FALSE)
|
||||
set(VULKAN_VALIDATION FALSE)
|
||||
set(SANITIZERS FALSE)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -115,6 +116,9 @@ endif()
|
||||
set(SIMD_FLAG NULL)
|
||||
|
||||
if(ARM64)
|
||||
if(USE_JIT)
|
||||
message(FATAL_ERROR "JIT unsupported in aarch64 at the moment")
|
||||
endif()
|
||||
message("Defining USE_NEON...")
|
||||
add_compile_definitions(USE_NEON)
|
||||
add_compile_definitions(SIMD_SUPPORT)
|
||||
@@ -182,6 +186,9 @@ endif()
|
||||
|
||||
target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
|
||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||
if(USE_JIT)
|
||||
target_compile_definitions(kaizen PUBLIC KAIZEN_JIT_ENABLED)
|
||||
endif()
|
||||
|
||||
if (SANITIZERS)
|
||||
message("UBSAN AND ASAN: ON")
|
||||
|
||||
+151
-149
@@ -13,11 +13,11 @@ using namespace RDP;
|
||||
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
|
||||
|
||||
void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const {
|
||||
if (unlocked) {
|
||||
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
||||
} else {
|
||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||
}
|
||||
if (unlocked) {
|
||||
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
||||
} else {
|
||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||
}
|
||||
}
|
||||
|
||||
Program *fullscreen_quad_program;
|
||||
@@ -25,208 +25,210 @@ Program *fullscreen_quad_program;
|
||||
// Copied and modified from WSI::init_context_from_platform
|
||||
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
|
||||
const Context::SystemHandles &system_handles) {
|
||||
VK_ASSERT(platform);
|
||||
const auto instance_ext = platform->get_instance_extensions();
|
||||
const auto device_ext = platform->get_device_extensions();
|
||||
auto new_context = Util::make_handle<Context>();
|
||||
VK_ASSERT(platform);
|
||||
const auto instance_ext = platform->get_instance_extensions();
|
||||
const auto device_ext = platform->get_device_extensions();
|
||||
auto new_context = Util::make_handle<Context>();
|
||||
|
||||
new_context->set_application_info(platform->get_application_info());
|
||||
new_context->set_num_thread_indices(num_thread_indices);
|
||||
new_context->set_system_handles(system_handles);
|
||||
new_context->set_application_info(platform->get_application_info());
|
||||
new_context->set_num_thread_indices(num_thread_indices);
|
||||
new_context->set_system_handles(system_handles);
|
||||
|
||||
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
||||
panic("Failed to create Vulkan instance.\n");
|
||||
}
|
||||
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
|
||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
|
||||
panic("Failed to create Vulkan instance.\n");
|
||||
}
|
||||
|
||||
const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
||||
const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
||||
|
||||
const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
||||
const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
||||
|
||||
if (tmp_surface) {
|
||||
platform->destroy_surface(new_context->get_instance(), tmp_surface);
|
||||
}
|
||||
if (tmp_surface) {
|
||||
platform->destroy_surface(new_context->get_instance(), tmp_surface);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
panic("Failed to create Vulkan device.\n");
|
||||
}
|
||||
if (!ret) {
|
||||
panic("Failed to create Vulkan device.\n");
|
||||
}
|
||||
|
||||
return new_context;
|
||||
return new_context;
|
||||
}
|
||||
|
||||
void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platform,
|
||||
const std::shared_ptr<WindowInfo> &newWindowInfo) {
|
||||
wsi = std::make_shared<WSI>();
|
||||
wsi->set_backbuffer_srgb(false);
|
||||
wsi->set_platform(wsi_platform.get());
|
||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||
wsi = std::make_shared<WSI>();
|
||||
wsi->set_backbuffer_srgb(false);
|
||||
wsi->set_platform(wsi_platform.get());
|
||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||
|
||||
if (constexpr Context::SystemHandles handles;
|
||||
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
||||
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
||||
}
|
||||
if (constexpr Context::SystemHandles handles;
|
||||
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
||||
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
||||
}
|
||||
|
||||
if (!wsi->init_device()) {
|
||||
panic("Failed to initialize WSI: init_device() failed");
|
||||
}
|
||||
if (!wsi->init_device()) {
|
||||
panic("Failed to initialize WSI: init_device() failed");
|
||||
}
|
||||
|
||||
if (!wsi->init_surface_swapchain()) {
|
||||
panic("Failed to initialize WSI: init_surface_swapchain() failed");
|
||||
}
|
||||
if (!wsi->init_surface_swapchain()) {
|
||||
panic("Failed to initialize WSI: init_surface_swapchain() failed");
|
||||
}
|
||||
|
||||
windowInfo = newWindowInfo;
|
||||
windowInfo = newWindowInfo;
|
||||
}
|
||||
|
||||
void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
||||
const std::shared_ptr<WindowInfo> &newWindowInfo, const u8 *rdram) {
|
||||
LoadWSIPlatform(wsiPlatform, newWindowInfo);
|
||||
LoadWSIPlatform(wsiPlatform, newWindowInfo);
|
||||
|
||||
ResourceLayout vertLayout;
|
||||
ResourceLayout fragLayout;
|
||||
ResourceLayout vertLayout;
|
||||
ResourceLayout fragLayout;
|
||||
|
||||
vertLayout.input_mask = 1;
|
||||
vertLayout.output_mask = 1;
|
||||
vertLayout.input_mask = 1;
|
||||
vertLayout.output_mask = 1;
|
||||
|
||||
fragLayout.input_mask = 1;
|
||||
fragLayout.output_mask = 1;
|
||||
fragLayout.spec_constant_mask = 1;
|
||||
fragLayout.push_constant_size = 4 * sizeof(float);
|
||||
fragLayout.input_mask = 1;
|
||||
fragLayout.output_mask = 1;
|
||||
fragLayout.spec_constant_mask = 1;
|
||||
fragLayout.push_constant_size = 4 * sizeof(float);
|
||||
|
||||
fragLayout.sets[0].sampled_image_mask = 1;
|
||||
fragLayout.sets[0].fp_mask = 1;
|
||||
fragLayout.sets[0].array_size[0] = 1;
|
||||
fragLayout.sets[0].sampled_image_mask = 1;
|
||||
fragLayout.sets[0].fp_mask = 1;
|
||||
fragLayout.sets[0].array_size[0] = 1;
|
||||
|
||||
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||
|
||||
fullscreen_quad_program = wsi->get_device().request_program(
|
||||
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
||||
sizeFrag, &vertLayout, &fragLayout);
|
||||
fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast<const u32 *>(vertex_shader), sizeVert,
|
||||
reinterpret_cast<const u32 *>(fragment_shader),
|
||||
sizeFrag, &vertLayout, &fragLayout);
|
||||
|
||||
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
||||
uintptr_t offset = 0;
|
||||
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
|
||||
uintptr_t offset = 0;
|
||||
|
||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
||||
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||
offset = aligned_rdram & align - 1;
|
||||
aligned_rdram -= offset;
|
||||
}
|
||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
||||
const size_t align =
|
||||
wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||
offset = aligned_rdram & align - 1;
|
||||
aligned_rdram -= offset;
|
||||
}
|
||||
|
||||
CommandProcessorFlags flags = 0;
|
||||
CommandProcessorFlags flags = 0;
|
||||
|
||||
command_processor = std::make_shared<CommandProcessor>(wsi->get_device(), reinterpret_cast<void *>(aligned_rdram),
|
||||
offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags);
|
||||
command_processor = std::make_shared<CommandProcessor>(wsi->get_device(), reinterpret_cast<void *>(aligned_rdram),
|
||||
offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags);
|
||||
|
||||
if (!command_processor->device_is_supported()) {
|
||||
panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!");
|
||||
}
|
||||
if (!command_processor->device_is_supported()) {
|
||||
panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!");
|
||||
}
|
||||
}
|
||||
|
||||
void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
||||
Util::IntrusivePtr<CommandBuffer> cmd) const {
|
||||
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
||||
cmd->set_program(fullscreen_quad_program);
|
||||
cmd->set_quad_state();
|
||||
const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
||||
data[0] = -1.0f;
|
||||
data[1] = -3.0f;
|
||||
data[2] = -1.0f;
|
||||
data[3] = +1.0f;
|
||||
data[4] = +3.0f;
|
||||
data[5] = +1.0f;
|
||||
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
||||
cmd->set_program(fullscreen_quad_program);
|
||||
cmd->set_quad_state();
|
||||
const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
||||
data[0] = -1.0f;
|
||||
data[1] = -3.0f;
|
||||
data[2] = -1.0f;
|
||||
data[3] = +1.0f;
|
||||
data[4] = +3.0f;
|
||||
data[5] = +1.0f;
|
||||
|
||||
const auto [x, y] = windowInfo->get_window_size();
|
||||
const auto [x, y] = windowInfo->get_window_size();
|
||||
|
||||
const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
|
||||
y / static_cast<float>(wsi->get_platform().get_surface_height()));
|
||||
const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
|
||||
y / static_cast<float>(wsi->get_platform().get_surface_height()));
|
||||
|
||||
const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
|
||||
const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
|
||||
const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
|
||||
const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
|
||||
|
||||
const float uniform_data[] = {// Size
|
||||
width, height,
|
||||
// Offset
|
||||
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
||||
const float uniform_data[] = {// Size
|
||||
width, height,
|
||||
// Offset
|
||||
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
||||
|
||||
cmd->push_constants(uniform_data, 0, sizeof(uniform_data));
|
||||
cmd->push_constants(uniform_data, 0, sizeof(uniform_data));
|
||||
|
||||
cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
|
||||
cmd->set_depth_test(false, false);
|
||||
cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS);
|
||||
cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
cmd->draw(3, 1);
|
||||
cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
|
||||
cmd->set_depth_test(false, false);
|
||||
cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS);
|
||||
cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
cmd->draw(3, 1);
|
||||
}
|
||||
|
||||
void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
||||
wsi->begin_frame();
|
||||
wsi->begin_frame();
|
||||
|
||||
if (!image) {
|
||||
auto info = ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM);
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT;
|
||||
info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image = wsi->get_device().create_image(info);
|
||||
if (!image) {
|
||||
auto info = ImageCreateInfo::immutable_2d_image(800, 600, VK_FORMAT_R8G8B8A8_UNORM);
|
||||
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT;
|
||||
info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
image = wsi->get_device().create_image(info);
|
||||
|
||||
auto cmd = wsi->get_device().request_command_buffer();
|
||||
auto cmd = wsi->get_device().request_command_buffer();
|
||||
|
||||
cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
cmd->clear_image(*image, {});
|
||||
cmd->image_barrier(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
cmd->image_barrier(*image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT);
|
||||
cmd->clear_image(*image, {});
|
||||
cmd->image_barrier(*image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
wsi->get_device().submit(cmd);
|
||||
}
|
||||
|
||||
Util::IntrusivePtr<CommandBuffer> cmd = wsi->get_device().request_command_buffer();
|
||||
|
||||
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
||||
DrawFullscreenTexturedQuad(image, cmd);
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->get_command_buffer());
|
||||
cmd->end_render_pass();
|
||||
wsi->get_device().submit(cmd);
|
||||
}
|
||||
|
||||
Util::IntrusivePtr<CommandBuffer> cmd = wsi->get_device().request_command_buffer();
|
||||
|
||||
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
|
||||
DrawFullscreenTexturedQuad(image, cmd);
|
||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd->get_command_buffer());
|
||||
cmd->end_render_pass();
|
||||
wsi->get_device().submit(cmd);
|
||||
wsi->end_frame();
|
||||
wsi->end_frame();
|
||||
}
|
||||
|
||||
template <>
|
||||
void ParallelRDP::UpdateScreen<true>() const {
|
||||
const n64::VI& vi = n64::Core::GetMem().mmio.vi;
|
||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||
command_processor->set_vi_register(VIRegister::Intr, vi.intr);
|
||||
command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current);
|
||||
command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw);
|
||||
command_processor->set_vi_register(VIRegister::VSync, vi.vsync);
|
||||
command_processor->set_vi_register(VIRegister::HSync, vi.hsync);
|
||||
command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw);
|
||||
command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw);
|
||||
command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw);
|
||||
command_processor->set_vi_register(VIRegister::VBurst, vi.vburst);
|
||||
command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw);
|
||||
command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw);
|
||||
ScanoutOptions opts;
|
||||
opts.persist_frame_on_invalid_input = true;
|
||||
opts.vi.aa = true;
|
||||
opts.vi.scale = true;
|
||||
opts.vi.dither_filter = true;
|
||||
opts.vi.divot_filter = true;
|
||||
opts.vi.gamma_dither = true;
|
||||
opts.downscale_steps = true;
|
||||
opts.crop_overscan_pixels = true;
|
||||
const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||
UpdateScreen(image);
|
||||
command_processor->begin_frame_context();
|
||||
const n64::VI &vi = n64::Core::GetMem().mmio.vi;
|
||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
||||
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
|
||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||
command_processor->set_vi_register(VIRegister::Intr, vi.intr);
|
||||
command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current);
|
||||
command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw);
|
||||
command_processor->set_vi_register(VIRegister::VSync, vi.vsync);
|
||||
command_processor->set_vi_register(VIRegister::HSync, vi.hsync);
|
||||
command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw);
|
||||
command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw);
|
||||
command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw);
|
||||
command_processor->set_vi_register(VIRegister::VBurst, vi.vburst);
|
||||
command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw);
|
||||
command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw);
|
||||
ScanoutOptions opts;
|
||||
opts.persist_frame_on_invalid_input = true;
|
||||
opts.vi.aa = true;
|
||||
opts.vi.scale = true;
|
||||
opts.vi.dither_filter = true;
|
||||
opts.vi.divot_filter = true;
|
||||
opts.vi.gamma_dither = true;
|
||||
opts.downscale_steps = true;
|
||||
opts.crop_overscan_pixels = true;
|
||||
const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||
UpdateScreen(image);
|
||||
command_processor->begin_frame_context();
|
||||
}
|
||||
|
||||
template <>
|
||||
void ParallelRDP::UpdateScreen<false>() const {
|
||||
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
||||
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
||||
}
|
||||
|
||||
void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const {
|
||||
command_processor->enqueue_command(command_length, buffer);
|
||||
command_processor->enqueue_command(command_length, buffer);
|
||||
}
|
||||
|
||||
void ParallelRDP::OnFullSync() const { command_processor->wait_for_timeline(command_processor->signal_timeline()); }
|
||||
|
||||
+30
-28
@@ -4,18 +4,20 @@
|
||||
#include <Options.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Core::Core() {
|
||||
Core::Core() :
|
||||
interpreter(*mem, regs)
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
,
|
||||
jit(*mem, regs)
|
||||
#endif
|
||||
{
|
||||
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||
if (selectedCpu == "interpreter") {
|
||||
cpuType = Interpreted;
|
||||
cpu = std::make_unique<Interpreter>(*mem, regs);
|
||||
} else if (selectedCpu == "jit") {
|
||||
#ifndef __aarch64__
|
||||
cpuType = DynamicRecompiler;
|
||||
cpu = std::make_unique<JIT>(*mem, regs);
|
||||
#else
|
||||
panic("JIT currently unsupported on aarch64");
|
||||
#endif
|
||||
} else if (selectedCpu == "cached_interpreter") {
|
||||
cpuType = CachedInterpreter;
|
||||
} else {
|
||||
panic("Unimplemented CPU type");
|
||||
}
|
||||
@@ -30,7 +32,7 @@ void Core::Stop() {
|
||||
void Core::Reset() {
|
||||
regs.Reset();
|
||||
mem->Reset();
|
||||
cpu->Reset();
|
||||
interpreter.Reset();
|
||||
if (romLoaded)
|
||||
mem->mmio.si.pif.Execute();
|
||||
}
|
||||
@@ -62,7 +64,20 @@ void Core::LoadROM(const std::string &rom_) {
|
||||
romLoaded = true;
|
||||
}
|
||||
|
||||
u32 Core::StepCPU() { return cpu->Step() + regs.PopStalledCycles(); }
|
||||
u32 Core::StepCPU() {
|
||||
if (cpuType == Interpreted)
|
||||
return interpreter.Step() + regs.PopStalledCycles();
|
||||
|
||||
if (cpuType == CachedInterpreter)
|
||||
return interpreter.ExecuteCached() + regs.PopStalledCycles();
|
||||
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
if (cpuType == DynamicRecompiler)
|
||||
return jit.Step() + regs.PopStalledCycles();
|
||||
#endif
|
||||
|
||||
panic("Invalid CPU type?");
|
||||
}
|
||||
|
||||
void Core::StepRSP(const u32 cpuCycles) {
|
||||
MMIO &mmio = mem->mmio;
|
||||
@@ -91,35 +106,25 @@ void Core::Run(const float volumeL, const float volumeR) {
|
||||
|
||||
bool broken = false;
|
||||
for (int field = 0; field < mmio.vi.numFields; field++) {
|
||||
Scheduler::GetInstance().HandleEvents();
|
||||
u32 frameCycles = 0;
|
||||
for (int i = 0; i < mmio.vi.numHalflines; i++) {
|
||||
mmio.vi.current = (i << 1) + field;
|
||||
for (int halfline = 0; halfline < mmio.vi.numHalflines; halfline++) {
|
||||
mmio.vi.current = (halfline << 1) + field;
|
||||
|
||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
|
||||
while (cycles < mem->mmio.vi.cyclesPerHalfline) {
|
||||
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
|
||||
Scheduler::GetInstance().HandleEvents();
|
||||
const u32 taken = StepCPU();
|
||||
cycles += taken;
|
||||
|
||||
if ((broken = breakpoints.contains(regs.nextPC)))
|
||||
break;
|
||||
|
||||
StepRSP(taken);
|
||||
frameCycles += taken;
|
||||
StepRSP(taken);
|
||||
Scheduler::GetInstance().Tick(taken);
|
||||
}
|
||||
|
||||
if (broken)
|
||||
break;
|
||||
|
||||
cycles -= mmio.vi.cyclesPerHalfline;
|
||||
}
|
||||
|
||||
if (broken)
|
||||
break;
|
||||
|
||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
@@ -127,8 +132,5 @@ void Core::Run(const float volumeL, const float volumeR) {
|
||||
mmio.ai.Step(frameCycles, volumeL, volumeR);
|
||||
Scheduler::GetInstance().Tick(frameCycles);
|
||||
}
|
||||
|
||||
if (broken)
|
||||
pause = true;
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
+50
-47
@@ -1,62 +1,65 @@
|
||||
#pragma once
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <backend/core/Interpreter.hpp>
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
#include <backend/core/JIT.hpp>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <variant>
|
||||
#include <Registers.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct Core {
|
||||
enum CPUType {
|
||||
Interpreted,
|
||||
DynamicRecompiler,
|
||||
CachedInterpreter
|
||||
} cpuType = Interpreted;
|
||||
|
||||
explicit Core();
|
||||
|
||||
static Core& GetInstance() {
|
||||
static Core instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static Registers& GetRegs() {
|
||||
return GetInstance().regs;
|
||||
}
|
||||
|
||||
static Mem& GetMem() {
|
||||
return *GetInstance().mem;
|
||||
}
|
||||
enum CPUType { Interpreted, DynamicRecompiler, CachedInterpreter } cpuType = CachedInterpreter;
|
||||
|
||||
u32 StepCPU();
|
||||
void StepRSP(u32 cpuCycles);
|
||||
void Stop();
|
||||
void Reset();
|
||||
void LoadROM(const std::string &);
|
||||
void LoadTAS(const fs::path &) const;
|
||||
void Run(float volumeL, float volumeR);
|
||||
void TogglePause() { pause = !pause; }
|
||||
inline void ToggleBreakpoint(s64 addr) {
|
||||
if(breakpoints.contains(addr)) {
|
||||
breakpoints.erase(addr);
|
||||
return;
|
||||
explicit Core();
|
||||
|
||||
static Core &GetInstance() {
|
||||
static Core instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
breakpoints.insert(addr);
|
||||
}
|
||||
|
||||
bool pause = true;
|
||||
bool romLoaded = false;
|
||||
int slot = 0;
|
||||
u32 cycles = 0;
|
||||
size_t memSize{}, cpuSize{}, verSize{};
|
||||
std::string rom;
|
||||
std::set<s64> breakpoints{};
|
||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
||||
std::unique_ptr<BaseCPU> cpu;
|
||||
static inline bool IsAddressError(u8 mask, u64 vaddr) {
|
||||
auto regs = GetRegs();
|
||||
return (!regs.cop0.is64BitAddressing && s32(vaddr) != vaddr) || (vaddr & mask) != 0;
|
||||
}
|
||||
|
||||
Registers regs;
|
||||
ParallelRDP parallel;
|
||||
static Registers &GetRegs() { return GetInstance().regs; }
|
||||
|
||||
static Mem &GetMem() { return *GetInstance().mem; }
|
||||
|
||||
u32 StepCPU();
|
||||
void StepRSP(u32 cpuCycles);
|
||||
void Stop();
|
||||
void Reset();
|
||||
void LoadROM(const std::string &);
|
||||
void LoadTAS(const fs::path &) const;
|
||||
void Run(float volumeL, float volumeR);
|
||||
void TogglePause() { pause = !pause; }
|
||||
inline void ToggleBreakpoint(s64 addr) {
|
||||
if (breakpoints.contains(addr)) {
|
||||
breakpoints.erase(addr);
|
||||
return;
|
||||
}
|
||||
|
||||
breakpoints.insert(addr);
|
||||
}
|
||||
|
||||
bool pause = true;
|
||||
bool romLoaded = false;
|
||||
int slot = 0;
|
||||
size_t memSize{}, cpuSize{}, verSize{};
|
||||
std::string rom;
|
||||
std::set<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
|
||||
|
||||
+53
-37
@@ -5,47 +5,63 @@ void Scheduler::EnqueueRelative(const u64 t, const EventType type) { EnqueueAbso
|
||||
|
||||
void Scheduler::EnqueueAbsolute(const u64 t, const EventType type) { events.push({t, type}); }
|
||||
|
||||
Event *Scheduler::Find(const EventType eventType) const {
|
||||
for (auto &event : events) {
|
||||
if (event.type == eventType) {
|
||||
const u64 ret = event.time - ticks;
|
||||
return &event;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u64 Scheduler::Remove(const EventType eventType) const {
|
||||
for (auto &[time, type] : events) {
|
||||
if (type == eventType) {
|
||||
const u64 ret = time - ticks;
|
||||
type = NONE;
|
||||
time = ticks;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
auto event = Find(eventType);
|
||||
if (!event)
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
const u64 ret = event->time - ticks;
|
||||
event->type = NONE;
|
||||
event->time = ticks;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Scheduler::Tick(const u64 t) {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ticks += t;
|
||||
n64::MI &mi = mem.mmio.mi;
|
||||
n64::SI &si = mem.mmio.si;
|
||||
n64::PI &pi = mem.mmio.pi;
|
||||
void Scheduler::SkipToNext() { ticks = events.top().time; }
|
||||
|
||||
while (ticks >= events.top().time) {
|
||||
switch (const auto type = events.top().type) {
|
||||
case SI_DMA:
|
||||
si.DMA();
|
||||
break;
|
||||
case PI_DMA_COMPLETE:
|
||||
mi.InterruptRaise(n64::MI::Interrupt::PI);
|
||||
pi.dmaBusy = false;
|
||||
break;
|
||||
case PI_BUS_WRITE_COMPLETE:
|
||||
pi.ioBusy = false;
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
case IMPOSSIBLE:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
|
||||
return;
|
||||
default:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(type));
|
||||
return;
|
||||
void Scheduler::Tick(const u64 t) { ticks += t; }
|
||||
|
||||
void Scheduler::HandleEvents() {
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
n64::MI &mi = mem.mmio.mi;
|
||||
n64::SI &si = mem.mmio.si;
|
||||
n64::PI &pi = mem.mmio.pi;
|
||||
|
||||
while (ticks >= events.top().time) {
|
||||
switch (const auto type = events.top().type) {
|
||||
case SI_DMA:
|
||||
si.DMA();
|
||||
break;
|
||||
case PI_DMA_COMPLETE:
|
||||
mi.InterruptRaise(n64::MI::Interrupt::PI);
|
||||
pi.dmaBusy = false;
|
||||
break;
|
||||
case PI_BUS_WRITE_COMPLETE:
|
||||
pi.ioBusy = false;
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
case IMPOSSIBLE:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
||||
"Unrecognized rom endianness");
|
||||
return;
|
||||
default:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE},
|
||||
{Util::Error::Type::ROM_LOAD_ERROR}, {}, {},
|
||||
"Unknown scheduler event type {}", static_cast<int>(type));
|
||||
return;
|
||||
}
|
||||
events.pop();
|
||||
}
|
||||
events.pop();
|
||||
}
|
||||
}
|
||||
|
||||
+27
-24
@@ -6,41 +6,44 @@
|
||||
enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
|
||||
|
||||
struct Event {
|
||||
u64 time;
|
||||
EventType type;
|
||||
u64 time;
|
||||
EventType type;
|
||||
|
||||
friend bool operator<(const Event &rhs, const Event &lhs) { return rhs.time < lhs.time; }
|
||||
friend bool operator<(const Event &rhs, const Event &lhs) { return rhs.time < lhs.time; }
|
||||
|
||||
friend bool operator>(const Event &rhs, const Event &lhs) { return rhs.time > lhs.time; }
|
||||
friend bool operator>(const Event &rhs, const Event &lhs) { return rhs.time > lhs.time; }
|
||||
|
||||
friend bool operator>=(const Event &rhs, const Event &lhs) { return rhs.time >= lhs.time; }
|
||||
friend bool operator>=(const Event &rhs, const Event &lhs) { return rhs.time >= lhs.time; }
|
||||
};
|
||||
|
||||
struct IterableEvents {
|
||||
std::priority_queue<Event, std::vector<Event>, std::greater<>> events;
|
||||
std::priority_queue<Event, std::vector<Event>, std::greater<>> events;
|
||||
|
||||
explicit IterableEvents() = default;
|
||||
[[nodiscard]] auto top() const { return events.top(); }
|
||||
auto pop() { events.pop(); }
|
||||
[[nodiscard]] auto begin() const { return const_cast<Event *>(&events.top()); }
|
||||
[[nodiscard]] auto end() const { return begin() + events.size(); }
|
||||
auto push(const Event e) { events.push(e); }
|
||||
explicit IterableEvents() = default;
|
||||
[[nodiscard]] auto top() const { return events.top(); }
|
||||
auto pop() { events.pop(); }
|
||||
[[nodiscard]] auto begin() const { return const_cast<Event *>(&events.top()); }
|
||||
[[nodiscard]] auto end() const { return begin() + events.size(); }
|
||||
auto push(const Event e) { events.push(e); }
|
||||
};
|
||||
|
||||
struct Scheduler {
|
||||
Scheduler() { EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE); }
|
||||
Scheduler() { EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE); }
|
||||
|
||||
static Scheduler &GetInstance() {
|
||||
static Scheduler instance;
|
||||
return instance;
|
||||
}
|
||||
static Scheduler &GetInstance() {
|
||||
static Scheduler instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void EnqueueRelative(u64, EventType);
|
||||
void EnqueueAbsolute(u64, EventType);
|
||||
[[nodiscard]] u64 Remove(EventType) const;
|
||||
void Tick(u64 t);
|
||||
void HandleEvents();
|
||||
void EnqueueRelative(u64, EventType);
|
||||
void EnqueueAbsolute(u64, EventType);
|
||||
[[nodiscard]] u64 Remove(EventType) const;
|
||||
[[nodiscard]] Event *Find(EventType) const;
|
||||
void Tick(u64 t);
|
||||
void SkipToNext();
|
||||
|
||||
u8 index = 0;
|
||||
u64 ticks = 0;
|
||||
IterableEvents events{};
|
||||
u8 index = 0;
|
||||
u64 ticks = 0;
|
||||
IterableEvents events{};
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
#include <Mem.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct alignas(32) InstructionCache {
|
||||
bool valid;
|
||||
u32 data[8];
|
||||
u32 ptag;
|
||||
|
||||
private:
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||
};
|
||||
|
||||
struct alignas(32) DataCache {
|
||||
bool valid, dirty;
|
||||
u8 data[16];
|
||||
u32 ptag;
|
||||
int index;
|
||||
|
||||
private:
|
||||
int GetLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||
u32 GetLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||
};
|
||||
|
||||
struct BaseCPU {
|
||||
virtual ~BaseCPU() = default;
|
||||
virtual u32 Step() = 0;
|
||||
virtual void Reset() = 0;
|
||||
};
|
||||
} // namespace n64
|
||||
@@ -2,7 +2,7 @@ file(GLOB SOURCES *.cpp)
|
||||
file(GLOB HEADERS *.hpp)
|
||||
|
||||
add_subdirectory(interpreter)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
add_subdirectory(jit)
|
||||
endif()
|
||||
add_subdirectory(mem)
|
||||
@@ -12,6 +12,6 @@ add_subdirectory(rsp)
|
||||
|
||||
add_library(core ${SOURCES} ${HEADERS})
|
||||
target_link_libraries(core PRIVATE interpreter mem mmio unarr registers rsp)
|
||||
if(NOT ARM64)
|
||||
if(USE_JIT)
|
||||
target_link_libraries(core PRIVATE jit)
|
||||
endif()
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <Core.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void InstructionCache::FillLine(u64 vaddr, u32 paddr) {
|
||||
int index = GetICacheLineIndex(vaddr);
|
||||
auto &line = lines[index];
|
||||
line.valid = true;
|
||||
line.ptag = paddr & ~0x00000fff;
|
||||
auto &mmio = Core::GetMem().mmio;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
line.data[i] = mmio.rdp.ReadRDRAM<u32>(line.ptag | index);
|
||||
}
|
||||
}
|
||||
|
||||
void InstructionCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
|
||||
auto &line = lines[GetICacheLineIndex(vaddr)];
|
||||
|
||||
line.valid = (cop0.tagLo.pstate >> 1) & 1;
|
||||
line.ptag = ptag;
|
||||
}
|
||||
|
||||
void DataCache::StoreTag(u64 vaddr, u32 ptag, Cop0 &cop0) {
|
||||
auto &line = lines[GetDCacheLineIndex(vaddr)];
|
||||
|
||||
line.valid = (cop0.tagLo.pstate >> 1) & 1;
|
||||
line.dirty = (cop0.tagLo.pstate >> 0) & 1;
|
||||
line.ptag = ptag;
|
||||
}
|
||||
|
||||
void InstructionCache::LoadTag(u64 vaddr) {
|
||||
auto &cop0 = Core::GetRegs().cop0;
|
||||
auto &line = lines[GetICacheLineIndex(vaddr)];
|
||||
cop0.tagLo.pstate = line.valid << 1;
|
||||
cop0.tagLo.ptaglo = line.ptag;
|
||||
}
|
||||
|
||||
void DataCache::LoadTag(u64 vaddr) {
|
||||
auto &cop0 = Core::GetRegs().cop0;
|
||||
auto &line = lines[GetDCacheLineIndex(vaddr)];
|
||||
cop0.tagLo.pstate = (line.valid << 1) | line.dirty;
|
||||
cop0.tagLo.ptaglo = line.ptag;
|
||||
}
|
||||
|
||||
template <>
|
||||
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr) {
|
||||
auto &mmio = Core::GetMem().mmio;
|
||||
|
||||
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||
if (!line.valid)
|
||||
return;
|
||||
|
||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||
u32 lineStart = GetDCacheLineStart(origPhysAddr);
|
||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||
}
|
||||
|
||||
line.dirty = false;
|
||||
}
|
||||
|
||||
template <>
|
||||
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr) {
|
||||
WriteBack<false>(vaddr, paddr);
|
||||
lines[GetDCacheLineIndex(vaddr)].valid = false;
|
||||
}
|
||||
|
||||
template <>
|
||||
void DataCache::WriteBack<false>(u64 vaddr, u32 paddr, u32 ptag) {
|
||||
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||
if (line.ptag == ptag)
|
||||
WriteBack(vaddr, paddr);
|
||||
}
|
||||
|
||||
template <>
|
||||
void DataCache::WriteBack<true>(u64 vaddr, u32 paddr, u32 ptag) {
|
||||
DCacheLine &line = lines[GetDCacheLineIndex(vaddr)];
|
||||
if (line.ptag == ptag)
|
||||
WriteBack<true>(vaddr, paddr);
|
||||
}
|
||||
|
||||
void InstructionCache::WriteBack(u64 vaddr, u32 paddr, u32 ptag) {
|
||||
auto &mmio = Core::GetMem().mmio;
|
||||
ICacheLine &line = lines[GetICacheLineIndex(vaddr)];
|
||||
|
||||
if (line.ptag == ptag && line.valid) {
|
||||
u32 origPhysAddr = (line.ptag << 12) | (paddr & 0xfff);
|
||||
u32 lineStart = GetICacheLineStart(origPhysAddr);
|
||||
Core::GetInstance().interpreter.cachedState.EvictCachedBlock(vaddr);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mmio.rdp.WriteRDRAM(lineStart + i, line.data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <array>
|
||||
|
||||
namespace n64 {
|
||||
struct alignas(32) ICacheLine {
|
||||
bool valid;
|
||||
u32 data[8];
|
||||
u32 ptag;
|
||||
};
|
||||
|
||||
struct alignas(32) DCacheLine {
|
||||
bool valid, dirty;
|
||||
u8 data[16];
|
||||
u32 ptag;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct Cop0;
|
||||
|
||||
inline const u32 GetPhysicalAddressPTag(const u32 paddr) { return paddr >> 12; }
|
||||
inline const int GetICacheLineIndex(u64 vaddr) { return (vaddr >> 5) & 0x1FF; }
|
||||
inline const u32 GetICacheLineStart(u64 paddr) { return paddr & ~0x1F; }
|
||||
inline const int GetDCacheLineIndex(u64 vaddr) { return (vaddr >> 4) & 0x1FF; }
|
||||
inline const u32 GetDCacheLineStart(u64 paddr) { return paddr & ~0xF; }
|
||||
|
||||
struct InstructionCache {
|
||||
std::array<ICacheLine, 512> lines;
|
||||
|
||||
void FillLine(u64, u32);
|
||||
void InvalidateIndex(u64 vaddr) { lines[GetICacheLineIndex(vaddr)].valid = false; }
|
||||
void StoreTag(u64, u32, Cop0 &);
|
||||
void LoadTag(u64 vaddr);
|
||||
|
||||
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
|
||||
void InvalidateIndex(u64 vaddr, u32 ptag) {
|
||||
int lineIndex = GetICacheLineIndex(vaddr);
|
||||
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
|
||||
lines[lineIndex].valid = false;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
struct DataCache {
|
||||
std::array<DCacheLine, 512> lines;
|
||||
|
||||
void StoreTag(u64, u32, Cop0 &);
|
||||
void LoadTag(u64 vaddr);
|
||||
template <bool invalidate = false>
|
||||
void WriteBack(u64 vaddr, u32 paddr);
|
||||
template <bool invalidate = false>
|
||||
void WriteBack(u64 vaddr, u32 paddr, u32 ptag);
|
||||
void InvalidateIndex(u64 vaddr) { lines[GetDCacheLineIndex(vaddr)].valid = false; }
|
||||
void InvalidateIndex(u64 vaddr, u32 ptag) {
|
||||
int lineIndex = GetDCacheLineIndex(vaddr);
|
||||
if (lines[lineIndex].valid && lines[lineIndex].ptag == ptag)
|
||||
lines[lineIndex].valid = false;
|
||||
}
|
||||
};
|
||||
} // namespace n64
|
||||
@@ -1,58 +1,181 @@
|
||||
#include <Core.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
#include "jit/helpers.hpp"
|
||||
|
||||
namespace n64 {
|
||||
Interpreter::Interpreter(Mem& mem, Registers& regs) : regs(regs), mem(mem) {}
|
||||
Interpreter::Interpreter(Mem &mem, Registers ®s) : regs(regs), mem(mem) {}
|
||||
|
||||
bool Interpreter::ShouldServiceInterrupt() const {
|
||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||
const bool interrupts_enabled = regs.cop0.status.ie == 1;
|
||||
const bool currently_handling_exception = regs.cop0.status.exl == 1;
|
||||
const bool currently_handling_error = regs.cop0.status.erl == 1;
|
||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||
const bool interrupts_enabled = regs.cop0.status.ie == 1;
|
||||
const bool currently_handling_exception = regs.cop0.status.exl == 1;
|
||||
const bool currently_handling_error = regs.cop0.status.erl == 1;
|
||||
|
||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
||||
}
|
||||
|
||||
void Interpreter::CheckCompareInterrupt() const {
|
||||
regs.cop0.count++;
|
||||
regs.cop0.count &= 0x1FFFFFFFF;
|
||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||
regs.cop0.cause.ip7 = 1;
|
||||
mem.mmio.mi.UpdateInterrupt();
|
||||
}
|
||||
void Interpreter::UpdateCompareInterrupt() const {
|
||||
regs.cop0.count++;
|
||||
regs.cop0.count &= 0x1FFFFFFFF;
|
||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||
regs.cop0.cause.ip7 = 1;
|
||||
mem.mmio.mi.UpdateInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
bool Interpreter::Fetch(Instruction &instr, u64 vaddr) {
|
||||
u32 paddr = 0;
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||
regs.cop0.HandleTLBException(vaddr);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, vaddr);
|
||||
return false;
|
||||
}
|
||||
|
||||
instr = mem.Read<u32>(paddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interpreter::MaybeAdvance() {
|
||||
UpdateCompareInterrupt();
|
||||
|
||||
regs.prevDelaySlot = regs.delaySlot;
|
||||
regs.delaySlot = false;
|
||||
|
||||
if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
|
||||
regs.cop0.HandleTLBException(regs.pc);
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ShouldServiceInterrupt()) {
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
||||
return false;
|
||||
}
|
||||
|
||||
regs.oldPC = regs.pc;
|
||||
regs.pc = regs.nextPC;
|
||||
regs.nextPC += 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Interpreter::FetchThenMaybeAdvance(Instruction &instr) {
|
||||
UpdateCompareInterrupt();
|
||||
|
||||
regs.prevDelaySlot = regs.delaySlot;
|
||||
regs.delaySlot = false;
|
||||
|
||||
if (Core::IsAddressError(0b11, u64(regs.pc))) [[unlikely]] {
|
||||
regs.cop0.HandleTLBException(regs.pc);
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Fetch(instr, regs.pc))
|
||||
return false;
|
||||
|
||||
if (ShouldServiceInterrupt()) {
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Interrupt, 0, regs.pc);
|
||||
return false;
|
||||
}
|
||||
|
||||
regs.oldPC = regs.pc;
|
||||
regs.pc = regs.nextPC;
|
||||
regs.nextPC += 4;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 Interpreter::Step() {
|
||||
CheckCompareInterrupt();
|
||||
Instruction instr;
|
||||
if (!FetchThenMaybeAdvance(instr))
|
||||
return 1;
|
||||
|
||||
regs.prevDelaySlot = regs.delaySlot;
|
||||
regs.delaySlot = false;
|
||||
DecodeExecute(instr);
|
||||
|
||||
if (check_address_error(0b11, u64(regs.pc))) [[unlikely]] {
|
||||
regs.cop0.HandleTLBException(regs.pc);
|
||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.pc);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 paddr = 0;
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, regs.pc, paddr)) {
|
||||
regs.cop0.HandleTLBException(regs.pc);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.pc);
|
||||
return 1;
|
||||
}
|
||||
u32 Interpreter::CacheBlock(u32 addr) {
|
||||
u32 blockAddr = addr;
|
||||
|
||||
const u32 instruction = mem.Read<u32>(paddr);
|
||||
CachedLine line;
|
||||
u32 i;
|
||||
bool fetchDelaySlot = false;
|
||||
|
||||
if (ShouldServiceInterrupt()) {
|
||||
regs.cop0.FireException(ExceptionCode::Interrupt, 0, regs.pc);
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < MAX_INSTR_PER_BLOCK; i++) {
|
||||
Instruction instr;
|
||||
if (!Fetch(instr, addr))
|
||||
return i + 1;
|
||||
|
||||
regs.oldPC = regs.pc;
|
||||
regs.pc = regs.nextPC;
|
||||
regs.nextPC += 4;
|
||||
addr += 4;
|
||||
line.code[i] = instr;
|
||||
|
||||
Exec(instruction);
|
||||
if (fetchDelaySlot) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
if (InstrEndsBlock(instr)) {
|
||||
if (InstrHasDelaySlot(instr) && !fetchDelaySlot) {
|
||||
fetchDelaySlot = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
i = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line.cycles = i;
|
||||
line.len = i;
|
||||
cachedState.blocks[CACHE_GET_BLOCK(blockAddr)]->lines[CACHE_GET_LINE(blockAddr)] = new CachedLine(line);
|
||||
|
||||
return ExecuteCached();
|
||||
}
|
||||
|
||||
u32 Interpreter::ExecuteCached() {
|
||||
u32 addr = regs.pc;
|
||||
auto &blocks = cachedState.blocks;
|
||||
|
||||
if (!blocks[CACHE_GET_BLOCK(addr)]) {
|
||||
blocks[CACHE_GET_BLOCK(addr)] = new CachedBlock<cachedState.MAX_LINES / 4>();
|
||||
return CacheBlock(addr);
|
||||
}
|
||||
|
||||
const auto line = blocks[CACHE_GET_BLOCK(addr)]->lines[CACHE_GET_LINE(addr)];
|
||||
if (line) {
|
||||
cachedState.exception = false;
|
||||
// i copy the block cycles here in case the block evicts itself when executing which would set the cycles to
|
||||
// 0, making so the emulator halts cause the outer loop won't advance
|
||||
const auto blockCycles = line->cycles;
|
||||
for (u32 i = 0; i < line->len; i++) {
|
||||
if (!MaybeAdvance())
|
||||
return i + 1;
|
||||
|
||||
Instruction instr = line->code[i];
|
||||
DecodeExecute(instr);
|
||||
|
||||
if (cachedState.exception)
|
||||
return i + 1;
|
||||
|
||||
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
||||
if (IsBranchLikely(instr) && !regs.delaySlot)
|
||||
break;
|
||||
}
|
||||
|
||||
if (line->idleSkip) {
|
||||
Scheduler::GetInstance().SkipToNext();
|
||||
}
|
||||
|
||||
if (blockCycles == 0)
|
||||
panic("Cycles are 0");
|
||||
|
||||
return blockCycles;
|
||||
}
|
||||
|
||||
return CacheBlock(addr);
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
+133
-113
@@ -1,125 +1,145 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
#include <JITUtils.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct Core;
|
||||
|
||||
struct Interpreter final : BaseCPU {
|
||||
explicit Interpreter(Mem&, Registers&);
|
||||
~Interpreter() override = default;
|
||||
u32 Step() override;
|
||||
struct Interpreter final {
|
||||
explicit Interpreter(Mem &, Registers &);
|
||||
~Interpreter() = default;
|
||||
u32 Step();
|
||||
u32 ExecuteCached();
|
||||
bool FetchThenMaybeAdvance(Instruction &);
|
||||
bool MaybeAdvance();
|
||||
u32 CacheBlock(u32 addr);
|
||||
|
||||
void Reset() override {
|
||||
cop2Latch = {};
|
||||
}
|
||||
void SignalException(u32 addr) { cachedState.exception = true; }
|
||||
|
||||
private:
|
||||
Registers& regs;
|
||||
Mem& mem;
|
||||
u64 cop2Latch{};
|
||||
friend struct Cop1;
|
||||
#define check_address_error(mask, vaddr) \
|
||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
void CheckCompareInterrupt() const;
|
||||
void Reset() {
|
||||
cop2Latch = {};
|
||||
cachedState.Reset();
|
||||
}
|
||||
|
||||
void cop2Decode(Instruction);
|
||||
void special(Instruction);
|
||||
void regimm(Instruction);
|
||||
void Exec(Instruction);
|
||||
void add(Instruction);
|
||||
void addu(Instruction);
|
||||
void addi(Instruction);
|
||||
void addiu(Instruction);
|
||||
void andi(Instruction);
|
||||
void and_(Instruction);
|
||||
void branch(bool, s64);
|
||||
void branch_likely(bool, s64);
|
||||
void b(Instruction, bool);
|
||||
void blink(Instruction, bool);
|
||||
void bl(Instruction, bool);
|
||||
void bllink(Instruction, bool);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
void daddiu(Instruction);
|
||||
void ddiv(Instruction);
|
||||
void ddivu(Instruction);
|
||||
void div(Instruction);
|
||||
void divu(Instruction);
|
||||
void dmult(Instruction);
|
||||
void dmultu(Instruction);
|
||||
void dsll(Instruction);
|
||||
void dsllv(Instruction);
|
||||
void dsll32(Instruction);
|
||||
void dsra(Instruction);
|
||||
void dsrav(Instruction);
|
||||
void dsra32(Instruction);
|
||||
void dsrl(Instruction);
|
||||
void dsrlv(Instruction);
|
||||
void dsrl32(Instruction);
|
||||
void dsub(Instruction);
|
||||
void dsubu(Instruction);
|
||||
void j(Instruction);
|
||||
void jr(Instruction);
|
||||
void jal(Instruction);
|
||||
void jalr(Instruction);
|
||||
void lui(Instruction);
|
||||
void lbu(Instruction);
|
||||
void lb(Instruction);
|
||||
void ld(Instruction);
|
||||
void ldl(Instruction);
|
||||
void ldr(Instruction);
|
||||
void lh(Instruction);
|
||||
void lhu(Instruction);
|
||||
void ll(Instruction);
|
||||
void lld(Instruction);
|
||||
void lw(Instruction);
|
||||
void lwl(Instruction);
|
||||
void lwu(Instruction);
|
||||
void lwr(Instruction);
|
||||
void mfhi(Instruction);
|
||||
void mflo(Instruction);
|
||||
void mult(Instruction);
|
||||
void multu(Instruction);
|
||||
void mthi(Instruction);
|
||||
void mtlo(Instruction);
|
||||
void nor(Instruction);
|
||||
void sb(Instruction);
|
||||
void sc(Instruction);
|
||||
void scd(Instruction);
|
||||
void sd(Instruction);
|
||||
void sdl(Instruction);
|
||||
void sdr(Instruction);
|
||||
void sh(Instruction);
|
||||
void sw(Instruction);
|
||||
void swl(Instruction);
|
||||
void swr(Instruction);
|
||||
void slti(Instruction);
|
||||
void sltiu(Instruction);
|
||||
void slt(Instruction);
|
||||
void sltu(Instruction);
|
||||
void sll(Instruction);
|
||||
void sllv(Instruction);
|
||||
void sub(Instruction);
|
||||
void subu(Instruction);
|
||||
void sra(Instruction);
|
||||
void srav(Instruction);
|
||||
void srl(Instruction);
|
||||
void srlv(Instruction);
|
||||
void trap(bool) const;
|
||||
void or_(Instruction);
|
||||
void ori(Instruction);
|
||||
void xor_(Instruction);
|
||||
void xori(Instruction);
|
||||
CachedState<12, std::numeric_limits<u32>::max()> cachedState;
|
||||
|
||||
void mtc2(Instruction);
|
||||
void mfc2(Instruction);
|
||||
void dmtc2(Instruction);
|
||||
void dmfc2(Instruction);
|
||||
void ctc2(Instruction);
|
||||
void cfc2(Instruction);
|
||||
private:
|
||||
friend struct Cop1;
|
||||
friend struct Mem;
|
||||
|
||||
void MaybeIdleSkip();
|
||||
|
||||
InstructionCache icache;
|
||||
DataCache dcache;
|
||||
Registers ®s;
|
||||
Mem &mem;
|
||||
u64 cop2Latch{};
|
||||
u32 rspSyncCount = 0;
|
||||
|
||||
bool Fetch(Instruction &, u64);
|
||||
void CacheTypeData(u8, u64, u32, u32);
|
||||
void CacheTypeInstruction(u8, u64, u32, u32);
|
||||
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
void UpdateCompareInterrupt() const;
|
||||
|
||||
void cop2Decode(Instruction);
|
||||
void special(Instruction);
|
||||
void regimm(Instruction);
|
||||
void DecodeExecute(Instruction);
|
||||
void add(Instruction);
|
||||
void addu(Instruction);
|
||||
void addi(Instruction);
|
||||
void addiu(Instruction);
|
||||
void andi(Instruction);
|
||||
void and_(Instruction);
|
||||
void branch(bool, s64);
|
||||
void branch_likely(bool, s64);
|
||||
void b(Instruction, bool);
|
||||
void blink(Instruction, bool);
|
||||
void bl(Instruction, bool);
|
||||
void bllink(Instruction, bool);
|
||||
void cache(Instruction);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
void daddiu(Instruction);
|
||||
void ddiv(Instruction);
|
||||
void ddivu(Instruction);
|
||||
void div(Instruction);
|
||||
void divu(Instruction);
|
||||
void dmult(Instruction);
|
||||
void dmultu(Instruction);
|
||||
void dsll(Instruction);
|
||||
void dsllv(Instruction);
|
||||
void dsll32(Instruction);
|
||||
void dsra(Instruction);
|
||||
void dsrav(Instruction);
|
||||
void dsra32(Instruction);
|
||||
void dsrl(Instruction);
|
||||
void dsrlv(Instruction);
|
||||
void dsrl32(Instruction);
|
||||
void dsub(Instruction);
|
||||
void dsubu(Instruction);
|
||||
void j(Instruction);
|
||||
void jr(Instruction);
|
||||
void jal(Instruction);
|
||||
void jalr(Instruction);
|
||||
void lui(Instruction);
|
||||
void lbu(Instruction);
|
||||
void lb(Instruction);
|
||||
void ld(Instruction);
|
||||
void ldl(Instruction);
|
||||
void ldr(Instruction);
|
||||
void lh(Instruction);
|
||||
void lhu(Instruction);
|
||||
void ll(Instruction);
|
||||
void lld(Instruction);
|
||||
void lw(Instruction);
|
||||
void lwl(Instruction);
|
||||
void lwu(Instruction);
|
||||
void lwr(Instruction);
|
||||
void mfhi(Instruction);
|
||||
void mflo(Instruction);
|
||||
void mult(Instruction);
|
||||
void multu(Instruction);
|
||||
void mthi(Instruction);
|
||||
void mtlo(Instruction);
|
||||
void nor(Instruction);
|
||||
void sb(Instruction);
|
||||
void sc(Instruction);
|
||||
void scd(Instruction);
|
||||
void sd(Instruction);
|
||||
void sdl(Instruction);
|
||||
void sdr(Instruction);
|
||||
void sh(Instruction);
|
||||
void sw(Instruction);
|
||||
void swl(Instruction);
|
||||
void swr(Instruction);
|
||||
void slti(Instruction);
|
||||
void sltiu(Instruction);
|
||||
void slt(Instruction);
|
||||
void sltu(Instruction);
|
||||
void sll(Instruction);
|
||||
void sllv(Instruction);
|
||||
void sub(Instruction);
|
||||
void subu(Instruction);
|
||||
void sra(Instruction);
|
||||
void srav(Instruction);
|
||||
void srl(Instruction);
|
||||
void srlv(Instruction);
|
||||
void trap(bool) const;
|
||||
void or_(Instruction);
|
||||
void ori(Instruction);
|
||||
void xor_(Instruction);
|
||||
void xori(Instruction);
|
||||
|
||||
void mtc2(Instruction);
|
||||
void mfc2(Instruction);
|
||||
void dmtc2(Instruction);
|
||||
void dmfc2(Instruction);
|
||||
void ctc2(Instruction);
|
||||
void cfc2(Instruction);
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
+195
-198
@@ -1,246 +1,243 @@
|
||||
#include <Core.hpp>
|
||||
#include <jit/helpers.hpp>
|
||||
#include <JIT.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifndef __aarch64__
|
||||
JIT::JIT(Mem& mem, Registers& regs) : regs(regs), mem(mem) {
|
||||
regs.SetJIT(this);
|
||||
mem.SetJIT(this);
|
||||
blockCache.resize(kUpperSize);
|
||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
||||
CS_ERR_OK) {
|
||||
panic("Failed to initialize MIPS disassembler");
|
||||
}
|
||||
JIT::JIT(Mem &mem, Registers ®s) : regs(regs), mem(mem) {
|
||||
blockCache.resize(kUpperSize);
|
||||
if (cs_open(CS_ARCH_MIPS, static_cast<cs_mode>(CS_MODE_MIPS64 | CS_MODE_BIG_ENDIAN), &disassemblerMips) !=
|
||||
CS_ERR_OK) {
|
||||
panic("Failed to initialize MIPS disassembler");
|
||||
}
|
||||
|
||||
if (cs_open(CS_ARCH_X86, static_cast<cs_mode>(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) {
|
||||
panic("Failed to initialize x86 disassembler");
|
||||
}
|
||||
if (cs_open(CS_ARCH_X86, static_cast<cs_mode>(CS_MODE_64 | CS_MODE_LITTLE_ENDIAN), &disassemblerX86) != CS_ERR_OK) {
|
||||
panic("Failed to initialize x86 disassembler");
|
||||
}
|
||||
}
|
||||
|
||||
bool JIT::ShouldServiceInterrupt() const {
|
||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||
const bool interrupts_enabled = regs.cop0.status.ie == 1;
|
||||
const bool currently_handling_exception = regs.cop0.status.exl == 1;
|
||||
const bool currently_handling_error = regs.cop0.status.erl == 1;
|
||||
const bool interrupts_pending = (regs.cop0.status.im & regs.cop0.cause.interruptPending) != 0;
|
||||
const bool interrupts_enabled = regs.cop0.status.ie == 1;
|
||||
const bool currently_handling_exception = regs.cop0.status.exl == 1;
|
||||
const bool currently_handling_error = regs.cop0.status.erl == 1;
|
||||
|
||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
||||
return interrupts_pending && interrupts_enabled && !currently_handling_exception && !currently_handling_error;
|
||||
}
|
||||
|
||||
void JIT::CheckCompareInterrupt() const {
|
||||
regs.cop0.count++;
|
||||
regs.cop0.count &= 0x1FFFFFFFF;
|
||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||
regs.cop0.cause.ip7 = 1;
|
||||
Core::GetMem().mmio.mi.UpdateInterrupt();
|
||||
}
|
||||
regs.cop0.count++;
|
||||
regs.cop0.count &= 0x1FFFFFFFF;
|
||||
if (regs.cop0.count == static_cast<u64>(regs.cop0.compare) << 1) {
|
||||
regs.cop0.cause.ip7 = 1;
|
||||
Core::GetMem().mmio.mi.UpdateInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::InvalidateBlock(const u32 paddr) {
|
||||
if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty())
|
||||
blockCache[index] = {};
|
||||
if (const u32 index = paddr >> kUpperShift; !blockCache[index].empty())
|
||||
blockCache[index] = {};
|
||||
}
|
||||
|
||||
std::optional<u32> JIT::FetchInstruction(s64 vaddr) {
|
||||
u32 paddr = 0;
|
||||
u32 paddr = 0;
|
||||
|
||||
if (check_address_error(0b11, vaddr)) [[unlikely]] {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||
return std::nullopt;
|
||||
}
|
||||
if (Core::IsAddressError(0b11, vaddr)) [[unlikely]] {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::AddressErrorLoad, 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
||||
blockPC, {},
|
||||
"[JIT]: Unhandled exception ADL due to unaligned PC virtual value!");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||
return std::nullopt;
|
||||
}
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, vaddr, paddr)) {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const u32 instr = Core::GetMem().Read<u32>(paddr);
|
||||
const u32 instr = Core::GetMem().Read<u32>(paddr);
|
||||
|
||||
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
||||
info("{}", Disassembler::GetInstance().DisassembleSimple(paddr, instr).full);
|
||||
|
||||
return instr;
|
||||
return instr;
|
||||
}
|
||||
|
||||
void JIT::SetPC32(const s32 val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1.cvt32(), val);
|
||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1.cvt32(), val + 4);
|
||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1.cvt32(), val);
|
||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1.cvt32(), val + 4);
|
||||
code.movsxd(code.SCR1.cvt64(), code.SCR1.cvt32());
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
}
|
||||
|
||||
void JIT::SetPC64(const s64 val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, val);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, val + 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, val);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, val + 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
}
|
||||
|
||||
void JIT::SetPC32(const Xbyak::Reg32& val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.movsxd(val.cvt64(), val);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||
code.add(val, 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||
void JIT::SetPC32(const Xbyak::Reg32 &val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.movsxd(val.cvt64(), val);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||
code.add(val, 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||
}
|
||||
|
||||
void JIT::SetPC64(const Xbyak::Reg64& val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||
code.add(val, 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||
void JIT::SetPC64(const Xbyak::Reg64 &val) {
|
||||
code.mov(code.SCR1, code.qword[code.rbp + PC_OFFSET]);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], val);
|
||||
code.add(val, 4);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], val);
|
||||
}
|
||||
|
||||
u32 JIT::Step() {
|
||||
blockOldPC = regs.oldPC;
|
||||
blockPC = regs.pc;
|
||||
blockNextPC = regs.nextPC;
|
||||
u32 paddr = 0;
|
||||
blockOldPC = regs.oldPC;
|
||||
blockPC = regs.pc;
|
||||
blockNextPC = regs.nextPC;
|
||||
u32 paddr = 0;
|
||||
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION},
|
||||
blockPC, {},
|
||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u32 upperIndex = paddr >> kUpperShift;
|
||||
const u32 lowerIndex = paddr & kLowerMask;
|
||||
|
||||
if (!blockCache[upperIndex].empty()) {
|
||||
if (blockCache[upperIndex][lowerIndex]) {
|
||||
// trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC);
|
||||
return blockCache[upperIndex][lowerIndex]();
|
||||
}
|
||||
} else {
|
||||
blockCache[upperIndex].resize(kLowerSize);
|
||||
}
|
||||
|
||||
info("[JIT]: Compiling block @ 0x{:016X}:", static_cast<u64>(blockPC));
|
||||
const auto blockInfo = code.getCurr();
|
||||
const auto block = code.getCurr<BlockFn>();
|
||||
blockCache[upperIndex][lowerIndex] = block;
|
||||
|
||||
code.setProtectModeRW();
|
||||
|
||||
u32 instructionsInBlock = 0;
|
||||
|
||||
bool instrEndsBlock = false;
|
||||
|
||||
code.sub(code.rsp, 8);
|
||||
code.push(code.rbp);
|
||||
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
|
||||
|
||||
cs_insn *insn;
|
||||
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
|
||||
while (true) {
|
||||
paddr = 0;
|
||||
|
||||
auto instruction = FetchInstruction(blockPC);
|
||||
|
||||
if(!instruction)
|
||||
return 0;
|
||||
|
||||
instructionsInBlock++;
|
||||
|
||||
blockOldPC = blockPC;
|
||||
blockPC = blockNextPC;
|
||||
blockNextPC += 4;
|
||||
|
||||
if(InstrEndsBlock(instruction.value())) {
|
||||
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||
if(!delay_instruction)
|
||||
return 0;
|
||||
|
||||
if(InstrEndsBlock(delay_instruction.value())) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, blockPC, paddr)) {
|
||||
/*regs.cop0.HandleTLBException(blockPC);
|
||||
regs.cop0.FireException(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, blockPC);
|
||||
return 1;*/
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_EXCEPTION}, blockPC, {},
|
||||
"[JIT]: Unhandled exception TLB exception {} when retrieving PC physical address!",
|
||||
static_cast<int>(Cop0::GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
instructionsInBlock++;
|
||||
|
||||
blockOldPC = blockPC;
|
||||
blockPC = blockNextPC;
|
||||
blockNextPC += 4;
|
||||
|
||||
Emit(delay_instruction.value());
|
||||
Emit(instruction.value());
|
||||
|
||||
if(!branch_taken) {
|
||||
Xbyak::Label runtime_branch_taken;
|
||||
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
||||
code.cmp(code.SCR1, 0);
|
||||
code.jne(runtime_branch_taken);
|
||||
code.mov(code.SCR1, blockOldPC);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, blockPC);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, blockNextPC);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
code.L(runtime_branch_taken);
|
||||
}
|
||||
|
||||
if(branch_taken) branch_taken = false;
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Emit(instruction.value());
|
||||
const u32 upperIndex = paddr >> kUpperShift;
|
||||
const u32 lowerIndex = paddr & kLowerMask;
|
||||
|
||||
if (!blockCache[upperIndex].empty()) {
|
||||
if (blockCache[upperIndex][lowerIndex]) {
|
||||
// trace("[JIT]: Executing already compiled block @ 0x{:016X}", blockPC);
|
||||
return blockCache[upperIndex][lowerIndex]();
|
||||
}
|
||||
} else {
|
||||
blockCache[upperIndex].resize(kLowerSize);
|
||||
}
|
||||
|
||||
info("[JIT]: Compiling block @ 0x{:016X}:", static_cast<u64>(blockPC));
|
||||
const auto blockInfo = code.getCurr();
|
||||
const auto block = code.getCurr<BlockFn>();
|
||||
blockCache[upperIndex][lowerIndex] = block;
|
||||
|
||||
code.setProtectModeRW();
|
||||
|
||||
u32 instructionsInBlock = 0;
|
||||
|
||||
bool instrEndsBlock = false;
|
||||
|
||||
code.sub(code.rsp, 8);
|
||||
code.push(code.rbp);
|
||||
code.mov(code.rbp, reinterpret_cast<uintptr_t>(this)); // Load context pointer
|
||||
|
||||
cs_insn *insn;
|
||||
info("\tMIPS code (guest PC = 0x{:016X}):", static_cast<u64>(blockPC));
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
}
|
||||
|
||||
code.mov(code.rax, instructionsInBlock);
|
||||
code.pop(code.rbp);
|
||||
code.add(code.rsp, 8);
|
||||
code.ret();
|
||||
code.setProtectModeRE();
|
||||
static size_t blockInfoSize = 0;
|
||||
blockInfoSize = code.getSize() - blockInfoSize;
|
||||
while (true) {
|
||||
paddr = 0;
|
||||
|
||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
||||
const auto count = cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
||||
if (count > 0) {
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
|
||||
auto instruction = FetchInstruction(blockPC);
|
||||
|
||||
if (!instruction)
|
||||
return 0;
|
||||
|
||||
instructionsInBlock++;
|
||||
|
||||
blockOldPC = blockPC;
|
||||
blockPC = blockNextPC;
|
||||
blockNextPC += 4;
|
||||
|
||||
if (InstrEndsBlock(instruction.value())) {
|
||||
const auto delay_instruction = FetchInstruction(blockPC); // get instruction in delay slot
|
||||
if (!delay_instruction)
|
||||
return 0;
|
||||
|
||||
if (InstrEndsBlock(delay_instruction.value())) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||
"[JIT]: Unhandled case of branch from delay slot!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
instructionsInBlock++;
|
||||
|
||||
blockOldPC = blockPC;
|
||||
blockPC = blockNextPC;
|
||||
blockNextPC += 4;
|
||||
|
||||
Emit(delay_instruction.value());
|
||||
Emit(instruction.value());
|
||||
|
||||
if (!branch_taken) {
|
||||
Xbyak::Label runtime_branch_taken;
|
||||
code.mov(code.SCR1, code.byte[code.rbp + BRANCH_TAKEN_OFFSET]);
|
||||
code.cmp(code.SCR1, 0);
|
||||
code.jne(runtime_branch_taken);
|
||||
code.mov(code.SCR1, blockOldPC);
|
||||
code.mov(code.qword[code.rbp + OLD_PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, blockPC);
|
||||
code.mov(code.qword[code.rbp + PC_OFFSET], code.SCR1);
|
||||
code.mov(code.SCR1, blockNextPC);
|
||||
code.mov(code.qword[code.rbp + NEXT_PC_OFFSET], code.SCR1);
|
||||
code.L(runtime_branch_taken);
|
||||
}
|
||||
|
||||
if (branch_taken)
|
||||
branch_taken = false;
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Emit(instruction.value());
|
||||
|
||||
emitMemberFunctionCall(&JIT::AdvanceDelaySlot, this);
|
||||
}
|
||||
|
||||
cs_free(insn, count);
|
||||
}
|
||||
// panic("");
|
||||
return block();
|
||||
|
||||
code.mov(code.rax, instructionsInBlock);
|
||||
code.pop(code.rbp);
|
||||
code.add(code.rsp, 8);
|
||||
code.ret();
|
||||
code.setProtectModeRE();
|
||||
static size_t blockInfoSize = 0;
|
||||
blockInfoSize = code.getSize() - blockInfoSize;
|
||||
|
||||
info("\tX86 code (block address = 0x{:016X}):", reinterpret_cast<uintptr_t>(block));
|
||||
const auto count =
|
||||
cs_disasm(disassemblerX86, blockInfo, blockInfoSize, reinterpret_cast<uintptr_t>(block), 0, &insn);
|
||||
if (count > 0) {
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
info("\t\t0x{:016X}:\t{}\t\t{}", insn[j].address, insn[j].mnemonic, insn[j].op_str);
|
||||
}
|
||||
|
||||
cs_free(insn, count);
|
||||
}
|
||||
// panic("");
|
||||
return block();
|
||||
}
|
||||
|
||||
void JIT::DumpBlockCacheToDisk() const {
|
||||
ircolib::WriteFileBinary(code.getCode<u8*>(), code.getSize(), "jit.dump");
|
||||
}
|
||||
#endif
|
||||
void JIT::DumpBlockCacheToDisk() const { ircolib::WriteFileBinary(code.getCode<u8 *>(), code.getSize(), "jit.dump"); }
|
||||
} // namespace n64
|
||||
|
||||
+230
-245
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <BaseCPU.hpp>
|
||||
#include <Cache.hpp>
|
||||
#include <Registers.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <vector>
|
||||
#include <xbyak.h>
|
||||
@@ -25,262 +26,246 @@ static constexpr u32 kCodeCacheAllocSize = kCodeCacheSize + 4_kb;
|
||||
#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))
|
||||
|
||||
#ifdef __aarch64__
|
||||
struct JIT : BaseCPU {};
|
||||
#else
|
||||
struct JIT final : BaseCPU {
|
||||
explicit JIT(Mem&, Registers&);
|
||||
~JIT() override = default;
|
||||
u32 Step() override;
|
||||
struct JIT final {
|
||||
explicit JIT(Mem &, Registers &);
|
||||
~JIT() = default;
|
||||
u32 Step();
|
||||
|
||||
void Reset() override {
|
||||
code.reset();
|
||||
blockCache = {};
|
||||
blockCache.resize(kUpperSize);
|
||||
}
|
||||
|
||||
void DumpBlockCacheToDisk() const;
|
||||
|
||||
void AdvanceDelaySlot() {
|
||||
regs.prevDelaySlot = regs.delaySlot;
|
||||
regs.delaySlot = false;
|
||||
}
|
||||
|
||||
void InvalidateBlock(u32);
|
||||
private:
|
||||
friend struct Cop1;
|
||||
friend struct Registers;
|
||||
using BlockFn = int (*)();
|
||||
|
||||
bool branch_taken;
|
||||
Registers& regs;
|
||||
Mem& mem;
|
||||
u64 cop2Latch{};
|
||||
s64 blockOldPC = 0, blockPC = 0, blockNextPC = 0;
|
||||
Xbyak::CodeGenerator code{kCodeCacheAllocSize};
|
||||
csh disassemblerMips{}, disassemblerX86{};
|
||||
std::vector<std::vector<BlockFn>> blockCache;
|
||||
|
||||
template <typename T>
|
||||
Xbyak::Address GPR(const size_t index) {
|
||||
if constexpr (sizeof(T) == 1) {
|
||||
return code.byte[code.rbp + GPR_OFFSET(index)];
|
||||
} else if constexpr (sizeof(T) == 2) {
|
||||
return code.word[code.rbp + GPR_OFFSET(index)];
|
||||
} else if constexpr (sizeof(T) == 4) {
|
||||
return code.dword[code.rbp + GPR_OFFSET(index)];
|
||||
} else if constexpr (sizeof(T) == 8) {
|
||||
return code.qword[code.rbp + GPR_OFFSET(index)];
|
||||
void Reset() {
|
||||
code.reset();
|
||||
blockCache = {};
|
||||
blockCache.resize(kUpperSize);
|
||||
}
|
||||
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::JIT_INVALID_X86_REG_ADDRESSING},
|
||||
blockPC, {}, "[JIT]: Invalid register addressing mode {}!", sizeof(T));
|
||||
return Xbyak::Address{0};
|
||||
}
|
||||
void DumpBlockCacheToDisk() const;
|
||||
|
||||
void AdvanceDelaySlot() {
|
||||
regs.prevDelaySlot = regs.delaySlot;
|
||||
regs.delaySlot = false;
|
||||
}
|
||||
|
||||
void InvalidateBlock(u32);
|
||||
|
||||
private:
|
||||
friend struct Cop1;
|
||||
friend struct Registers;
|
||||
using BlockFn = int (*)();
|
||||
|
||||
bool branch_taken;
|
||||
Registers ®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
|
||||
// Load a pointer to the JIT object in "reg"
|
||||
template <typename T>
|
||||
void emitMemberFunctionCall(T func, void *thisObject) {
|
||||
uintptr_t functionPtr;
|
||||
auto thisPtr = reinterpret_cast<uintptr_t>(thisObject);
|
||||
// Thanks to https://github.com/grumpycoders/pcsx-redux
|
||||
// Load a pointer to the JIT object in "reg"
|
||||
template <typename T>
|
||||
void emitMemberFunctionCall(T func, void *thisObject) {
|
||||
uintptr_t functionPtr;
|
||||
auto thisPtr = reinterpret_cast<uintptr_t>(thisObject);
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
||||
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
|
||||
std::memcpy(&functionPtr, &func, sizeof(T));
|
||||
static_assert(sizeof(T) == 8, "[x64 JIT] Invalid size for member function pointer");
|
||||
std::memcpy(&functionPtr, &func, sizeof(T));
|
||||
#else
|
||||
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
|
||||
uintptr_t arr[2];
|
||||
std::memcpy(arr, &func, sizeof(T));
|
||||
// First 8 bytes correspond to the actual pointer to the function
|
||||
functionPtr = reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(arr[0]));
|
||||
// Next 8 bytes correspond to the "this" pointer adjustment
|
||||
thisPtr += arr[1];
|
||||
static_assert(sizeof(T) == 16, "[x64 JIT] Invalid size for member function pointer");
|
||||
uintptr_t arr[2];
|
||||
std::memcpy(arr, &func, sizeof(T));
|
||||
// First 8 bytes correspond to the actual pointer to the function
|
||||
functionPtr = reinterpret_cast<uintptr_t>(reinterpret_cast<void *>(arr[0]));
|
||||
// Next 8 bytes correspond to the "this" pointer adjustment
|
||||
thisPtr += arr[1];
|
||||
#endif
|
||||
|
||||
code.mov(code.ARG1, thisPtr);
|
||||
code.mov(code.rax, functionPtr);
|
||||
code.sub(code.rsp, 8);
|
||||
code.call(code.rax);
|
||||
code.add(code.rsp, 8);
|
||||
}
|
||||
code.mov(code.ARG1, thisPtr);
|
||||
code.mov(code.rax, functionPtr);
|
||||
code.sub(code.rsp, 8);
|
||||
code.call(code.rax);
|
||||
code.add(code.rsp, 8);
|
||||
}
|
||||
|
||||
void SetPC32(s32 val);
|
||||
void SetPC64(s64 val);
|
||||
void SetPC32(const Xbyak::Reg32& val);
|
||||
void SetPC64(const Xbyak::Reg64& val);
|
||||
void BranchNotTaken();
|
||||
void BranchTaken(s64 offs);
|
||||
void BranchTaken(const Xbyak::Reg64 &offs);
|
||||
void BranchAbsTaken(s64 addr);
|
||||
void BranchAbsTaken(const Xbyak::Reg64 &addr);
|
||||
void SetPC32(s32 val);
|
||||
void SetPC64(s64 val);
|
||||
void SetPC32(const Xbyak::Reg32 &val);
|
||||
void SetPC64(const Xbyak::Reg64 &val);
|
||||
void BranchNotTaken();
|
||||
void BranchTaken(s64 offs);
|
||||
void BranchTaken(const Xbyak::Reg64 &offs);
|
||||
void BranchAbsTaken(s64 addr);
|
||||
void BranchAbsTaken(const Xbyak::Reg64 &addr);
|
||||
|
||||
#define check_address_error(mask, vaddr) \
|
||||
(((!regs.cop0.is64BitAddressing) && (s32)(vaddr) != (vaddr)) || (((vaddr) & (mask)) != 0))
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
void CheckCompareInterrupt() const;
|
||||
std::optional<u32> FetchInstruction(s64);
|
||||
|
||||
[[nodiscard]] bool ShouldServiceInterrupt() const;
|
||||
void CheckCompareInterrupt() const;
|
||||
std::optional<u32> FetchInstruction(s64);
|
||||
|
||||
void Emit(Instruction);
|
||||
void special(Instruction);
|
||||
void regimm(Instruction);
|
||||
void add(Instruction);
|
||||
void addu(Instruction);
|
||||
void addi(Instruction);
|
||||
void addiu(Instruction);
|
||||
void andi(Instruction);
|
||||
void and_(Instruction);
|
||||
void branch_constant(bool cond, s64 offset);
|
||||
void branch_likely_constant(bool cond, s64 offset);
|
||||
void branch_abs_constant(bool cond, s64 address);
|
||||
void bltz(Instruction);
|
||||
void bgez(Instruction);
|
||||
void bltzl(Instruction);
|
||||
void bgezl(Instruction);
|
||||
void bltzal(Instruction);
|
||||
void bgezal(Instruction);
|
||||
void bltzall(Instruction);
|
||||
void bgezall(Instruction);
|
||||
void beq(Instruction);
|
||||
void beql(Instruction);
|
||||
void bne(Instruction);
|
||||
void bnel(Instruction);
|
||||
void blez(Instruction);
|
||||
void blezl(Instruction);
|
||||
void bgtz(Instruction);
|
||||
void bgtzl(Instruction);
|
||||
void bfc1(Instruction);
|
||||
void blfc1(Instruction);
|
||||
void bfc0(Instruction);
|
||||
void blfc0(Instruction);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
void daddiu(Instruction);
|
||||
void ddiv(Instruction);
|
||||
void ddivu(Instruction);
|
||||
void div(Instruction);
|
||||
void divu(Instruction);
|
||||
void dmult(Instruction);
|
||||
void dmultu(Instruction);
|
||||
void dsll(Instruction);
|
||||
void dsllv(Instruction);
|
||||
void dsll32(Instruction);
|
||||
void dsra(Instruction);
|
||||
void dsrav(Instruction);
|
||||
void dsra32(Instruction);
|
||||
void dsrl(Instruction);
|
||||
void dsrlv(Instruction);
|
||||
void dsrl32(Instruction);
|
||||
void dsub(Instruction);
|
||||
void dsubu(Instruction);
|
||||
void j(Instruction);
|
||||
void jr(Instruction);
|
||||
void jal(Instruction);
|
||||
void jalr(Instruction);
|
||||
void lui(Instruction);
|
||||
void lbu(Instruction);
|
||||
void lb(Instruction);
|
||||
void ld(Instruction);
|
||||
void ldc1(Instruction);
|
||||
void ldl(Instruction);
|
||||
void ldr(Instruction);
|
||||
void lh(Instruction);
|
||||
void lhu(Instruction);
|
||||
void ll(Instruction);
|
||||
void lld(Instruction);
|
||||
void lw(Instruction);
|
||||
void lwc1(Instruction);
|
||||
void lwl(Instruction);
|
||||
void lwu(Instruction);
|
||||
void lwr(Instruction);
|
||||
void mfhi(Instruction);
|
||||
void mflo(Instruction);
|
||||
void mult(Instruction);
|
||||
void multu(Instruction);
|
||||
void mthi(Instruction);
|
||||
void mtlo(Instruction);
|
||||
void nor(Instruction);
|
||||
void sb(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
||||
}
|
||||
void sc(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
||||
}
|
||||
void scd(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
||||
}
|
||||
void sd(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
||||
}
|
||||
void sdc1(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
||||
}
|
||||
void sdl(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
||||
}
|
||||
void sdr(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
||||
}
|
||||
void sh(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
||||
}
|
||||
void sw(Instruction);
|
||||
void swl(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
||||
}
|
||||
void swr(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
||||
}
|
||||
void slti(Instruction);
|
||||
void sltiu(Instruction);
|
||||
void slt(Instruction);
|
||||
void sltu(Instruction);
|
||||
void sll(Instruction);
|
||||
void sllv(Instruction);
|
||||
void sub(Instruction);
|
||||
void subu(Instruction);
|
||||
void swc1(const Instruction) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void sra(Instruction);
|
||||
void srav(Instruction);
|
||||
void srl(Instruction);
|
||||
void srlv(Instruction);
|
||||
void trap(bool) {
|
||||
Util::Error::GetInstance().Throw(
|
||||
{Util::Error::Severity::NON_FATAL}, {Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT},
|
||||
blockPC, {}, "[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void or_(Instruction);
|
||||
void ori(Instruction);
|
||||
void xor_(Instruction);
|
||||
void xori(Instruction);
|
||||
void Emit(Instruction);
|
||||
void special(Instruction);
|
||||
void regimm(Instruction);
|
||||
void add(Instruction);
|
||||
void addu(Instruction);
|
||||
void addi(Instruction);
|
||||
void addiu(Instruction);
|
||||
void andi(Instruction);
|
||||
void and_(Instruction);
|
||||
void branch_constant(bool cond, s64 offset);
|
||||
void branch_likely_constant(bool cond, s64 offset);
|
||||
void branch_abs_constant(bool cond, s64 address);
|
||||
void bltz(Instruction);
|
||||
void bgez(Instruction);
|
||||
void bltzl(Instruction);
|
||||
void bgezl(Instruction);
|
||||
void bltzal(Instruction);
|
||||
void bgezal(Instruction);
|
||||
void bltzall(Instruction);
|
||||
void bgezall(Instruction);
|
||||
void beq(Instruction);
|
||||
void beql(Instruction);
|
||||
void bne(Instruction);
|
||||
void bnel(Instruction);
|
||||
void blez(Instruction);
|
||||
void blezl(Instruction);
|
||||
void bgtz(Instruction);
|
||||
void bgtzl(Instruction);
|
||||
void bfc1(Instruction);
|
||||
void blfc1(Instruction);
|
||||
void bfc0(Instruction);
|
||||
void blfc0(Instruction);
|
||||
void dadd(Instruction);
|
||||
void daddu(Instruction);
|
||||
void daddi(Instruction);
|
||||
void daddiu(Instruction);
|
||||
void ddiv(Instruction);
|
||||
void ddivu(Instruction);
|
||||
void div(Instruction);
|
||||
void divu(Instruction);
|
||||
void dmult(Instruction);
|
||||
void dmultu(Instruction);
|
||||
void dsll(Instruction);
|
||||
void dsllv(Instruction);
|
||||
void dsll32(Instruction);
|
||||
void dsra(Instruction);
|
||||
void dsrav(Instruction);
|
||||
void dsra32(Instruction);
|
||||
void dsrl(Instruction);
|
||||
void dsrlv(Instruction);
|
||||
void dsrl32(Instruction);
|
||||
void dsub(Instruction);
|
||||
void dsubu(Instruction);
|
||||
void j(Instruction);
|
||||
void jr(Instruction);
|
||||
void jal(Instruction);
|
||||
void jalr(Instruction);
|
||||
void lui(Instruction);
|
||||
void lbu(Instruction);
|
||||
void lb(Instruction);
|
||||
void ld(Instruction);
|
||||
void ldc1(Instruction);
|
||||
void ldl(Instruction);
|
||||
void ldr(Instruction);
|
||||
void lh(Instruction);
|
||||
void lhu(Instruction);
|
||||
void ll(Instruction);
|
||||
void lld(Instruction);
|
||||
void lw(Instruction);
|
||||
void lwc1(Instruction);
|
||||
void lwl(Instruction);
|
||||
void lwu(Instruction);
|
||||
void lwr(Instruction);
|
||||
void mfhi(Instruction);
|
||||
void mflo(Instruction);
|
||||
void mult(Instruction);
|
||||
void multu(Instruction);
|
||||
void mthi(Instruction);
|
||||
void mtlo(Instruction);
|
||||
void nor(Instruction);
|
||||
void sb(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sb'!");
|
||||
}
|
||||
void sc(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sc'!");
|
||||
}
|
||||
void scd(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'scd'!");
|
||||
}
|
||||
void sd(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sd'!");
|
||||
}
|
||||
void sdc1(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdc1'!");
|
||||
}
|
||||
void sdl(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdl'!");
|
||||
}
|
||||
void sdr(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sdr'!");
|
||||
}
|
||||
void sh(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'sh'!");
|
||||
}
|
||||
void sw(Instruction);
|
||||
void swl(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'swl'!");
|
||||
}
|
||||
void swr(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL}, {Util::Error::Type::UNHANDLED_INSTRUCTION},
|
||||
blockPC, {}, "[JIT]: Unhandled 'swr'!");
|
||||
}
|
||||
void slti(Instruction);
|
||||
void sltiu(Instruction);
|
||||
void slt(Instruction);
|
||||
void sltu(Instruction);
|
||||
void sll(Instruction);
|
||||
void sllv(Instruction);
|
||||
void sub(Instruction);
|
||||
void subu(Instruction);
|
||||
void swc1(const Instruction) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||
"[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void sra(Instruction);
|
||||
void srav(Instruction);
|
||||
void srl(Instruction);
|
||||
void srlv(Instruction);
|
||||
void trap(bool) {
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::NON_FATAL},
|
||||
{Util::Error::Type::JIT_BRANCH_INSIDE_DELAY_SLOT}, blockPC, {},
|
||||
"[JIT]: Unhandled case of branch from delay slot!");
|
||||
}
|
||||
void or_(Instruction);
|
||||
void ori(Instruction);
|
||||
void xor_(Instruction);
|
||||
void xori(Instruction);
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include <Instruction.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace n64 {
|
||||
static constexpr u32 MAX_INSTR_PER_BLOCK = 128;
|
||||
|
||||
#define CACHE_GET_BLOCK(addr) (addr / (cachedState.MAX_LINES))
|
||||
#define CACHE_GET_LINE(addr) ((addr & ((cachedState.MAX_LINES) - 1)) >> 2)
|
||||
|
||||
struct CachedLine {
|
||||
bool idleSkip = false;
|
||||
std::array<Instruction, MAX_INSTR_PER_BLOCK> code = {};
|
||||
u32 len = 0;
|
||||
u32 cycles = 0;
|
||||
};
|
||||
|
||||
template <u32 lineAmount>
|
||||
struct CachedBlock {
|
||||
CachedBlock() { lines.resize(lineAmount); }
|
||||
std::vector<CachedLine *> lines = {};
|
||||
};
|
||||
|
||||
template <u32 blockBits, u64 addressSpace>
|
||||
struct CachedState {
|
||||
static constexpr u32 MAX_LINES = 1 << blockBits;
|
||||
std::vector<CachedBlock<MAX_LINES / 4> *> blocks = {};
|
||||
bool exception = false;
|
||||
|
||||
void EvictCachedBlock(u32 addr) { blocks[addr / MAX_LINES] = {}; }
|
||||
|
||||
void Reset() {
|
||||
for (auto block : blocks) {
|
||||
if (block)
|
||||
for (auto line : block->lines)
|
||||
delete line;
|
||||
|
||||
delete block;
|
||||
}
|
||||
blocks = {};
|
||||
blocks.resize((addressSpace + 1) / MAX_LINES);
|
||||
}
|
||||
};
|
||||
} // namespace n64
|
||||
+473
-451
File diff suppressed because it is too large
Load Diff
+113
-115
@@ -11,144 +11,142 @@
|
||||
|
||||
namespace n64 {
|
||||
struct ROMHeader {
|
||||
u8 initialValues[4];
|
||||
char imageName[20];
|
||||
char countryCode[2];
|
||||
u16 cartridgeId;
|
||||
u32 clockRate;
|
||||
u32 programCounter;
|
||||
u32 release;
|
||||
u32 crc1;
|
||||
u32 crc2;
|
||||
u32 unknown2;
|
||||
u32 manufacturerId;
|
||||
u64 unknown;
|
||||
u8 initialValues[4];
|
||||
char imageName[20];
|
||||
char countryCode[2];
|
||||
u16 cartridgeId;
|
||||
u32 clockRate;
|
||||
u32 programCounter;
|
||||
u32 release;
|
||||
u32 crc1;
|
||||
u32 crc2;
|
||||
u32 unknown2;
|
||||
u32 manufacturerId;
|
||||
u64 unknown;
|
||||
};
|
||||
|
||||
struct ROM {
|
||||
bool pal;
|
||||
char gameNameCart[20];
|
||||
char code[4];
|
||||
ROMHeader header;
|
||||
size_t mask;
|
||||
CICType cicType;
|
||||
std::vector<u8> cart;
|
||||
std::string gameNameDB;
|
||||
bool pal;
|
||||
char gameNameCart[20];
|
||||
char code[4];
|
||||
ROMHeader header;
|
||||
size_t mask;
|
||||
CICType cicType;
|
||||
std::vector<u8> cart;
|
||||
std::string gameNameDB;
|
||||
};
|
||||
|
||||
enum class FlashState : u8 { Idle, Erase, Write, Read, Status };
|
||||
|
||||
struct Flash {
|
||||
explicit Flash(mio::mmap_sink &);
|
||||
~Flash() = default;
|
||||
void Reset();
|
||||
void Load(SaveType, const std::string &);
|
||||
std::array<u8, 128> writeBuf{};
|
||||
FlashState state{};
|
||||
u64 status{};
|
||||
size_t eraseOffs{};
|
||||
size_t writeOffs{};
|
||||
std::string flashPath{};
|
||||
mio::mmap_sink &saveData;
|
||||
explicit Flash(mio::mmap_sink &);
|
||||
~Flash() = default;
|
||||
void Reset();
|
||||
void Load(SaveType, const std::string &);
|
||||
std::array<u8, 128> writeBuf{};
|
||||
FlashState state{};
|
||||
u64 status{};
|
||||
size_t eraseOffs{};
|
||||
size_t writeOffs{};
|
||||
std::string flashPath{};
|
||||
mio::mmap_sink &saveData;
|
||||
|
||||
enum FlashCommands : u8 {
|
||||
FLASH_COMMAND_EXECUTE = 0xD2,
|
||||
FLASH_COMMAND_STATUS = 0xE1,
|
||||
FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B,
|
||||
FLASH_COMMAND_ERASE = 0x78,
|
||||
FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5,
|
||||
FLASH_COMMAND_WRITE = 0xB4,
|
||||
FLASH_COMMAND_READ = 0xF0,
|
||||
};
|
||||
enum FlashCommands : u8 {
|
||||
FLASH_COMMAND_EXECUTE = 0xD2,
|
||||
FLASH_COMMAND_STATUS = 0xE1,
|
||||
FLASH_COMMAND_SET_ERASE_OFFSET = 0x4B,
|
||||
FLASH_COMMAND_ERASE = 0x78,
|
||||
FLASH_COMMAND_SET_WRITE_OFFSET = 0xA5,
|
||||
FLASH_COMMAND_WRITE = 0xB4,
|
||||
FLASH_COMMAND_READ = 0xF0,
|
||||
};
|
||||
|
||||
void CommandExecute() const;
|
||||
void CommandStatus();
|
||||
void CommandSetEraseOffs(u32);
|
||||
void CommandErase();
|
||||
void CommandSetWriteOffs(u32);
|
||||
void CommandWrite();
|
||||
void CommandRead();
|
||||
template <typename T>
|
||||
void Write(u32 index, T val);
|
||||
template <typename T>
|
||||
T Read(u32 index) const;
|
||||
void CommandExecute() const;
|
||||
void CommandStatus();
|
||||
void CommandSetEraseOffs(u32);
|
||||
void CommandErase();
|
||||
void CommandSetWriteOffs(u32);
|
||||
void CommandWrite();
|
||||
void CommandRead();
|
||||
template <typename T>
|
||||
void Write(u32 index, T val);
|
||||
template <typename T>
|
||||
T Read(u32 index) const;
|
||||
};
|
||||
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
struct JIT;
|
||||
|
||||
struct Mem {
|
||||
~Mem() = default;
|
||||
Mem();
|
||||
void Reset();
|
||||
void LoadSRAM(SaveType, fs::path);
|
||||
void LoadROM(bool, const std::string &);
|
||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
||||
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
||||
Mem(JIT &jit);
|
||||
JIT &jit;
|
||||
#else
|
||||
struct Mem {
|
||||
Mem();
|
||||
~Mem() = default;
|
||||
void Reset();
|
||||
void LoadSRAM(SaveType, fs::path);
|
||||
void LoadROM(bool, const std::string &);
|
||||
|
||||
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
||||
[[nodiscard]] auto GetRDRAMPtr() -> u8 * { return mmio.rdp.rdram.data(); }
|
||||
|
||||
template <typename T>
|
||||
T Read(u32);
|
||||
template <typename T>
|
||||
void Write(u32, u32);
|
||||
void Write(u32, u64);
|
||||
[[nodiscard]] auto GetRDRAM() -> std::vector<u8> & { return mmio.rdp.rdram; }
|
||||
|
||||
template <typename T>
|
||||
T BackupRead(u32);
|
||||
template <typename T>
|
||||
void BackupWrite(u32, T);
|
||||
template <typename T>
|
||||
T Read(u32);
|
||||
template <typename T>
|
||||
void Write(u32, u32);
|
||||
void Write(u32, u64);
|
||||
|
||||
FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const {
|
||||
std::vector<u8> temp{};
|
||||
temp.resize(size);
|
||||
std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "rdram.bin");
|
||||
}
|
||||
template <typename T>
|
||||
T BackupRead(u32);
|
||||
template <typename T>
|
||||
void BackupWrite(u32, T);
|
||||
|
||||
FORCE_INLINE void DumpIMEM() const {
|
||||
std::array<u8, IMEM_SIZE> temp{};
|
||||
std::ranges::copy(mmio.rsp.imem, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "imem.bin");
|
||||
}
|
||||
FORCE_INLINE void DumpRDRAM(u32 start = 0, u32 size = RDRAM_SIZE) const {
|
||||
std::vector<u8> temp{};
|
||||
temp.resize(size);
|
||||
std::copy(mmio.rdp.rdram.begin() + start, mmio.rdp.rdram.begin() + size - 1, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "rdram.bin");
|
||||
}
|
||||
|
||||
FORCE_INLINE void DumpDMEM() const {
|
||||
std::array<u8, DMEM_SIZE> temp{};
|
||||
std::ranges::copy(mmio.rsp.dmem, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "dmem.bin");
|
||||
}
|
||||
FORCE_INLINE void DumpIMEM() const {
|
||||
std::array<u8, IMEM_SIZE> temp{};
|
||||
std::ranges::copy(mmio.rsp.imem, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "imem.bin");
|
||||
}
|
||||
|
||||
MMIO mmio;
|
||||
ROM rom;
|
||||
SaveType saveType = SAVE_NONE;
|
||||
Flash flash;
|
||||
private:
|
||||
friend struct SI;
|
||||
friend struct PI;
|
||||
friend struct AI;
|
||||
friend struct RSP;
|
||||
friend struct JIT;
|
||||
friend struct Core;
|
||||
FORCE_INLINE void DumpDMEM() const {
|
||||
std::array<u8, DMEM_SIZE> temp{};
|
||||
std::ranges::copy(mmio.rsp.dmem, temp.begin());
|
||||
ircolib::SwapBuffer<u32>(temp);
|
||||
ircolib::WriteFileBinary(temp, "dmem.bin");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteInterpreter(u32, u32);
|
||||
void WriteInterpreter(u32, u64);
|
||||
template <typename T>
|
||||
void WriteJIT(u32, u32);
|
||||
void WriteJIT(u32, u64);
|
||||
MMIO mmio;
|
||||
ROM rom;
|
||||
SaveType saveType = SAVE_NONE;
|
||||
Flash flash;
|
||||
|
||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||
std::ofstream isviewer_sink{};
|
||||
int mmioSize{}, flashSize{};
|
||||
JIT *jit = nullptr;
|
||||
std::string sramPath{};
|
||||
mio::mmap_sink saveData{};
|
||||
private:
|
||||
friend struct SI;
|
||||
friend struct PI;
|
||||
friend struct AI;
|
||||
friend struct RSP;
|
||||
friend struct JIT;
|
||||
friend struct Core;
|
||||
|
||||
[[nodiscard]] FORCE_INLINE bool IsROMPAL() const {
|
||||
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};
|
||||
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
||||
}
|
||||
std::array<u8, ISVIEWER_SIZE> isviewer{};
|
||||
std::ofstream isviewer_sink{};
|
||||
int mmioSize{}, flashSize{};
|
||||
std::string sramPath{};
|
||||
mio::mmap_sink saveData{};
|
||||
|
||||
[[nodiscard]] FORCE_INLINE bool IsROMPAL() const {
|
||||
static constexpr char pal_codes[] = {'D', 'F', 'I', 'P', 'S', 'U', 'X', 'Y'};
|
||||
return std::ranges::any_of(pal_codes, [this](char a) { return rom.cart[0x3d] == a; });
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
+168
-169
@@ -4,75 +4,75 @@
|
||||
|
||||
namespace n64 {
|
||||
RDP::RDP() {
|
||||
rdram.resize(RDRAM_SIZE);
|
||||
Reset();
|
||||
rdram.resize(RDRAM_SIZE);
|
||||
Reset();
|
||||
}
|
||||
|
||||
void RDP::Reset() {
|
||||
dpc = {};
|
||||
dpc.status.raw = 0x80;
|
||||
std::ranges::fill(rdram, 0);
|
||||
std::ranges::fill(cmd_buf, 0);
|
||||
dpc = {};
|
||||
dpc.status.raw = 0x80;
|
||||
std::ranges::fill(rdram, 0);
|
||||
std::ranges::fill(cmd_buf, 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u8>(const size_t idx, const u8 v) {
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
rdram[real] = v;
|
||||
}
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
rdram[real] = v;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u16>(const size_t idx, const u16 v) {
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u16>(rdram, real, v);
|
||||
}
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u16>(rdram, real, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u32>(const size_t idx, const u32 v) {
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u32>(rdram, idx, v);
|
||||
}
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u32>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u64>(const size_t idx, const u64 v) {
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u64>(rdram, idx, v);
|
||||
}
|
||||
if (idx < RDRAM_SIZE) [[likely]] {
|
||||
ircolib::WriteAccess<u64>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
u8 RDP::ReadRDRAM<u8>(const size_t idx) {
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return rdram[real];
|
||||
if (const size_t real = BYTE_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return rdram[real];
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u16 RDP::ReadRDRAM<u16>(const size_t idx) {
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u16>(rdram, real);
|
||||
if (const size_t real = HALF_ADDRESS(idx); real < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u16>(rdram, real);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u32 RDP::ReadRDRAM<u32>(const size_t idx) {
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u32>(rdram, idx);
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u32>(rdram, idx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 RDP::ReadRDRAM<u64>(const size_t idx) {
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u64>(rdram, idx);
|
||||
if (idx < RDRAM_SIZE) [[likely]]
|
||||
return ircolib::ReadAccess<u64>(rdram, idx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2,
|
||||
@@ -80,79 +80,78 @@ static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
|
||||
|
||||
auto RDP::Read(const u32 addr) const -> u32 {
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
return dpc.start;
|
||||
case 0x04100004:
|
||||
return dpc.end;
|
||||
case 0x04100008:
|
||||
return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010:
|
||||
return dpc.clock;
|
||||
case 0x04100014:
|
||||
return dpc.status.cmdBusy;
|
||||
case 0x04100018:
|
||||
return dpc.status.pipeBusy;
|
||||
case 0x0410001C:
|
||||
return dpc.tmem;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
return dpc.start;
|
||||
case 0x04100004:
|
||||
return dpc.end;
|
||||
case 0x04100008:
|
||||
return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010:
|
||||
return dpc.clock;
|
||||
case 0x04100014:
|
||||
return dpc.status.cmdBusy;
|
||||
case 0x04100018:
|
||||
return dpc.status.pipeBusy;
|
||||
case 0x0410001C:
|
||||
return dpc.tmem;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RDP::Write(const u32 addr, const u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
WriteStart(val);
|
||||
break;
|
||||
case 0x04100004:
|
||||
WriteEnd(val);
|
||||
break;
|
||||
case 0x0410000C:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
WriteStart(val);
|
||||
break;
|
||||
case 0x04100004:
|
||||
WriteEnd(val);
|
||||
break;
|
||||
case 0x0410000C:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void RDP::WriteStatus(const u32 val) {
|
||||
DPCStatusWrite temp{};
|
||||
temp.raw = val;
|
||||
bool unfrozen = false;
|
||||
DPCStatusWrite temp{};
|
||||
temp.raw = val;
|
||||
bool unfrozen = false;
|
||||
#define CLEAR_SET(val, clear, set) \
|
||||
do { \
|
||||
if ((clear)) \
|
||||
(val) = 0; \
|
||||
if ((set)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
if ((clear)) \
|
||||
(val) = 0; \
|
||||
if ((set)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma);
|
||||
if (temp.clearFreeze) {
|
||||
dpc.status.freeze = false;
|
||||
unfrozen = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma);
|
||||
if (temp.clearFreeze) {
|
||||
dpc.status.freeze = false;
|
||||
unfrozen = true;
|
||||
}
|
||||
|
||||
if (temp.setFreeze) {
|
||||
dpc.status.freeze = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush);
|
||||
CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false);
|
||||
CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false);
|
||||
CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false);
|
||||
CLEAR_SET(dpc.clock, temp.clearClock, false);
|
||||
if (temp.setFreeze) {
|
||||
dpc.status.freeze = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush);
|
||||
CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false);
|
||||
CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false);
|
||||
CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false);
|
||||
CLEAR_SET(dpc.clock, temp.clearClock, false);
|
||||
|
||||
if (!unfrozen) {
|
||||
RunCommand();
|
||||
}
|
||||
if (!unfrozen) {
|
||||
RunCommand();
|
||||
}
|
||||
}
|
||||
/*
|
||||
FORCE_INLINE void logCommand(u8 cmd) {
|
||||
@@ -197,102 +196,102 @@ FORCE_INLINE void logCommand(u8 cmd) {
|
||||
*/
|
||||
|
||||
void RDP::RunCommand() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
||||
if (dpc.status.freeze) {
|
||||
return;
|
||||
}
|
||||
dpc.status.pipeBusy = true;
|
||||
dpc.status.startGclk = true;
|
||||
if (dpc.end > dpc.current) {
|
||||
dpc.status.freeze = true;
|
||||
|
||||
static int remaining_cmds = 0;
|
||||
|
||||
const u32 current = dpc.current & 0xFFFFF8;
|
||||
const u32 end = dpc.end & 0xFFFFF8;
|
||||
|
||||
const auto len = static_cast<s32>(end) - static_cast<s32>(current);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) {
|
||||
panic("Too many RDP commands");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpc.status.xbusDmemDma) {
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(mem.mmio.rsp.dmem, current + i & 0xFFF);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
} else {
|
||||
if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE)
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||
if (dpc.status.freeze) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(rdram, current + i);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
}
|
||||
dpc.status.pipeBusy = true;
|
||||
dpc.status.startGclk = true;
|
||||
if (dpc.end > dpc.current) {
|
||||
dpc.status.freeze = true;
|
||||
|
||||
const int word_len = (len >> 2) + remaining_cmds;
|
||||
int buf_index = 0;
|
||||
static int remaining_cmds = 0;
|
||||
|
||||
bool processed_all = true;
|
||||
const u32 current = dpc.current & 0xFFFFF8;
|
||||
const u32 end = dpc.end & 0xFFFFF8;
|
||||
|
||||
while (buf_index < word_len) {
|
||||
const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F;
|
||||
const auto len = static_cast<s32>(end) - static_cast<s32>(current);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
const int cmd_len = cmd_lens[cmd];
|
||||
if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) {
|
||||
remaining_cmds = word_len - buf_index;
|
||||
|
||||
u32 tmp[remaining_cmds];
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
tmp[i] = cmd_buf[buf_index + i];
|
||||
if (len + remaining_cmds * 4 > COMMAND_BUFFER_SIZE) {
|
||||
panic("Too many RDP commands");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
cmd_buf[i] = tmp[i];
|
||||
if (dpc.status.xbusDmemDma) {
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(mem.mmio.rsp.dmem, current + i & 0xFFF);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
} else {
|
||||
if (end > 0x7FFFFFF || current > 0x7FFFFFF) { // if (end > RDRAM_DSIZE || current > RDRAM_DSIZE)
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i += 4) {
|
||||
const u32 cmd = ircolib::ReadAccess<u32>(rdram, current + i);
|
||||
cmd_buf[remaining_cmds + (i >> 2)] = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
processed_all = false;
|
||||
break;
|
||||
}
|
||||
const int word_len = (len >> 2) + remaining_cmds;
|
||||
int buf_index = 0;
|
||||
|
||||
if (cmd >= 8) {
|
||||
parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]);
|
||||
}
|
||||
bool processed_all = true;
|
||||
|
||||
if (cmd == 0x29) {
|
||||
OnFullSync();
|
||||
}
|
||||
while (buf_index < word_len) {
|
||||
const u8 cmd = cmd_buf[buf_index] >> 24 & 0x3F;
|
||||
|
||||
buf_index += cmd_len;
|
||||
const int cmd_len = cmd_lens[cmd];
|
||||
if ((buf_index + cmd_len) * 4 > len + remaining_cmds * 4) {
|
||||
remaining_cmds = word_len - buf_index;
|
||||
|
||||
u32 tmp[remaining_cmds];
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
tmp[i] = cmd_buf[buf_index + i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < remaining_cmds; i++) {
|
||||
cmd_buf[i] = tmp[i];
|
||||
}
|
||||
|
||||
processed_all = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd >= 8) {
|
||||
parallel.EnqueueCommand(cmd_len, &cmd_buf[buf_index]);
|
||||
}
|
||||
|
||||
if (cmd == 0x29) {
|
||||
OnFullSync();
|
||||
}
|
||||
|
||||
buf_index += cmd_len;
|
||||
}
|
||||
|
||||
if (processed_all) {
|
||||
remaining_cmds = 0;
|
||||
}
|
||||
|
||||
dpc.current = end;
|
||||
dpc.end = end;
|
||||
dpc.status.freeze = false;
|
||||
}
|
||||
|
||||
if (processed_all) {
|
||||
remaining_cmds = 0;
|
||||
}
|
||||
|
||||
dpc.current = end;
|
||||
dpc.end = end;
|
||||
dpc.status.freeze = false;
|
||||
}
|
||||
dpc.status.cbufReady = true;
|
||||
dpc.status.cbufReady = true;
|
||||
}
|
||||
|
||||
void RDP::OnFullSync() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ParallelRDP& parallel = n64::Core::GetInstance().parallel;
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
ParallelRDP ¶llel = n64::Core::GetInstance().parallel;
|
||||
|
||||
parallel.OnFullSync();
|
||||
parallel.OnFullSync();
|
||||
|
||||
dpc.status.pipeBusy = false;
|
||||
dpc.status.startGclk = false;
|
||||
dpc.status.cbufReady = false;
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::DP);
|
||||
dpc.status.pipeBusy = false;
|
||||
dpc.status.startGclk = false;
|
||||
dpc.status.cbufReady = false;
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::DP);
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
+159
-185
@@ -1,228 +1,202 @@
|
||||
#include <Core.hpp>
|
||||
#include <log.hpp>
|
||||
#include <jit/helpers.hpp>
|
||||
|
||||
namespace n64 {
|
||||
RSP::RSP() { Reset(); }
|
||||
|
||||
void RSP::Reset() {
|
||||
lastSuccessfulSPAddr.raw = 0;
|
||||
lastSuccessfulDRAMAddr.raw = 0;
|
||||
spStatus.raw = 0;
|
||||
spStatus.halt = true;
|
||||
oldPC = 0;
|
||||
pc = 0;
|
||||
nextPC = 4;
|
||||
spDMASPAddr.raw = 0;
|
||||
spDMADRAMAddr.raw = 0;
|
||||
spDMALen.raw = 0;
|
||||
dmem = {};
|
||||
imem = {};
|
||||
memset(vpr, 0, 32 * sizeof(VPR));
|
||||
memset(gpr, 0, 32 * sizeof(u32));
|
||||
memset(&vce, 0, sizeof(VPR));
|
||||
memset(&acc, 0, 3 * sizeof(VPR));
|
||||
memset(&vcc, 0, 2 * sizeof(VPR));
|
||||
memset(&vco, 0, 2 * sizeof(VPR));
|
||||
semaphore = false;
|
||||
divIn = 0;
|
||||
divOut = 0;
|
||||
divInLoaded = false;
|
||||
steps = 0;
|
||||
lastSuccessfulSPAddr.raw = 0;
|
||||
lastSuccessfulDRAMAddr.raw = 0;
|
||||
spStatus.raw = 0;
|
||||
spStatus.halt = true;
|
||||
oldPC = 0;
|
||||
pc = 0;
|
||||
nextPC = 4;
|
||||
spDMASPAddr.raw = 0;
|
||||
spDMADRAMAddr.raw = 0;
|
||||
spDMALen.raw = 0;
|
||||
dmem = {};
|
||||
imem = {};
|
||||
memset(vpr, 0, 32 * sizeof(VPR));
|
||||
memset(gpr, 0, 32 * sizeof(u32));
|
||||
memset(&vce, 0, sizeof(VPR));
|
||||
memset(&acc, 0, 3 * sizeof(VPR));
|
||||
memset(&vcc, 0, 2 * sizeof(VPR));
|
||||
memset(&vco, 0, 2 * sizeof(VPR));
|
||||
semaphore = false;
|
||||
divIn = 0;
|
||||
divOut = 0;
|
||||
divInLoaded = false;
|
||||
steps = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
FORCE_INLINE void logRSP(const RSP& rsp, const u32 instr) {
|
||||
debug("{:04X} {:08X} ", rsp.oldPC, instr);
|
||||
for (auto gpr : rsp.gpr) {
|
||||
debug("{:08X} ", gpr);
|
||||
}
|
||||
|
||||
for (auto vpr : rsp.vpr) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
debug("{:04X}", vpr.element[i]);
|
||||
}
|
||||
debug(" ");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
debug("{:04X}", rsp.acc.h.element[i]);
|
||||
}
|
||||
debug(" ");
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
debug("{:04X}", rsp.acc.m.element[i]);
|
||||
}
|
||||
debug(" ");
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
debug("{:04X}", rsp.acc.l.element[i]);
|
||||
}
|
||||
|
||||
debug(" {:04X} {:04X} {:02X}", rsp.GetVCC(), rsp.GetVCO(), rsp.GetVCE());
|
||||
debug("DMEM: {:02X}{:02X}", rsp.dmem[0x3c4], rsp.dmem[0x3c5]);
|
||||
}
|
||||
*/
|
||||
|
||||
auto RSP::Read(const u32 addr) -> u32 {
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
return lastSuccessfulSPAddr.raw & 0x1FF8;
|
||||
case 0x04040004:
|
||||
return lastSuccessfulDRAMAddr.raw & 0xFFFFF8;
|
||||
case 0x04040008:
|
||||
case 0x0404000C:
|
||||
return spDMALen.raw;
|
||||
case 0x04040010:
|
||||
return spStatus.raw;
|
||||
case 0x04040014:
|
||||
return spStatus.dmaFull;
|
||||
case 0x04040018:
|
||||
return 0;
|
||||
case 0x0404001C:
|
||||
return AcquireSemaphore();
|
||||
case 0x04080000:
|
||||
return pc & 0xFFC;
|
||||
default:
|
||||
panic("Unimplemented SP register read {:08X}", addr);
|
||||
}
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
return lastSuccessfulSPAddr.raw & 0x1FF8;
|
||||
case 0x04040004:
|
||||
return lastSuccessfulDRAMAddr.raw & 0xFFFFF8;
|
||||
case 0x04040008:
|
||||
case 0x0404000C:
|
||||
return spDMALen.raw;
|
||||
case 0x04040010:
|
||||
return spStatus.raw;
|
||||
case 0x04040014:
|
||||
return spStatus.dmaFull;
|
||||
case 0x04040018:
|
||||
return 0;
|
||||
case 0x0404001C:
|
||||
return AcquireSemaphore();
|
||||
case 0x04080000:
|
||||
return pc & 0xFFC;
|
||||
default:
|
||||
{
|
||||
auto ®s = Core::GetRegs();
|
||||
|
||||
panic("Unimplemented SP register read {:08X} (cpu pc: 0x{:016X}, rsp pc: 0x{:04X}, ra: 0x{:016X})", addr,
|
||||
(u64)regs.oldPC, pc & 0xffc, (u64)regs.gpr[31]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RSP::WriteStatus(const u32 value) {
|
||||
Mem& mem = Core::GetMem();
|
||||
Registers& regs = Core::GetRegs();
|
||||
MI &mi = mem.mmio.mi;
|
||||
const auto write = SPStatusWrite{.raw = value};
|
||||
if (write.clearHalt && !write.setHalt) {
|
||||
spStatus.halt = false;
|
||||
}
|
||||
if (write.setHalt && !write.clearHalt) {
|
||||
regs.steps = 0;
|
||||
spStatus.halt = true;
|
||||
}
|
||||
if (write.clearBroke)
|
||||
spStatus.broke = false;
|
||||
if (write.clearIntr && !write.setIntr)
|
||||
mi.InterruptLower(MI::Interrupt::SP);
|
||||
if (write.setIntr && !write.clearIntr)
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
Mem &mem = Core::GetMem();
|
||||
Registers ®s = Core::GetRegs();
|
||||
MI &mi = mem.mmio.mi;
|
||||
const auto write = SPStatusWrite{.raw = value};
|
||||
if (write.clearHalt && !write.setHalt) {
|
||||
spStatus.halt = false;
|
||||
}
|
||||
if (write.setHalt && !write.clearHalt) {
|
||||
regs.steps = 0;
|
||||
spStatus.halt = true;
|
||||
}
|
||||
if (write.clearBroke)
|
||||
spStatus.broke = false;
|
||||
if (write.clearIntr && !write.setIntr)
|
||||
mi.InterruptLower(MI::Interrupt::SP);
|
||||
if (write.setIntr && !write.clearIntr)
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
|
||||
#define CLEAR_SET(val, clear, set) \
|
||||
do { \
|
||||
if ((clear) && !(set)) \
|
||||
(val) = 0; \
|
||||
if ((set) && !(clear)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
if ((clear) && !(set)) \
|
||||
(val) = 0; \
|
||||
if ((set) && !(clear)) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
||||
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
||||
CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0);
|
||||
CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1);
|
||||
CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2);
|
||||
CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3);
|
||||
CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4);
|
||||
CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5);
|
||||
CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6);
|
||||
CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7);
|
||||
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
||||
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
||||
CLEAR_SET(spStatus.signal0, write.clearSignal0, write.setSignal0);
|
||||
CLEAR_SET(spStatus.signal1, write.clearSignal1, write.setSignal1);
|
||||
CLEAR_SET(spStatus.signal2, write.clearSignal2, write.setSignal2);
|
||||
CLEAR_SET(spStatus.signal3, write.clearSignal3, write.setSignal3);
|
||||
CLEAR_SET(spStatus.signal4, write.clearSignal4, write.setSignal4);
|
||||
CLEAR_SET(spStatus.signal5, write.clearSignal5, write.setSignal5);
|
||||
CLEAR_SET(spStatus.signal6, write.clearSignal6, write.setSignal6);
|
||||
CLEAR_SET(spStatus.signal7, write.clearSignal7, write.setSignal7);
|
||||
#undef CLEAR_SET
|
||||
}
|
||||
|
||||
template <>
|
||||
void RSP::DMA<true>() {
|
||||
Mem& mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
Mem &mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
|
||||
length = (length + 0x7) & ~0x7;
|
||||
length = (length + 0x7) & ~0x7;
|
||||
|
||||
const auto &src = spDMASPAddr.bank ? imem : dmem;
|
||||
const auto &src = spDMASPAddr.bank ? imem : dmem;
|
||||
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address);
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RSP to RDRAM (size: {} B, {:08X} to {:08X})", length, mem_address, dram_address);
|
||||
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(BYTE_ADDRESS(dram_address + j), src[(mem_address + j) & DMEM_DSIZE]);
|
||||
}
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
}
|
||||
|
||||
template <>
|
||||
void RSP::DMA<false>() {
|
||||
Mem& mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
Mem &mem = Core::GetMem();
|
||||
u32 length = spDMALen.len + 1;
|
||||
|
||||
length = (length + 0x7) & ~0x7;
|
||||
length = (length + 0x7) & ~0x7;
|
||||
|
||||
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
||||
auto &dst = spDMASPAddr.bank ? imem : dmem;
|
||||
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
||||
u32 mem_address = spDMASPAddr.address & 0xFF8;
|
||||
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j));
|
||||
u32 dram_address = spDMADRAMAddr.address & 0xFFFFF8;
|
||||
trace("SP DMA from RDRAM to RSP (size: {} B, {:08X} to {:08X})", length, dram_address, mem_address);
|
||||
|
||||
for (u32 i = 0; i < spDMALen.count + 1; i++) {
|
||||
for (u32 j = 0; j < length; j++) {
|
||||
dst[(mem_address + j) & DMEM_DSIZE] = mem.mmio.rdp.ReadRDRAM<u8>(BYTE_ADDRESS(dram_address + j));
|
||||
}
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
const int skip = i == spDMALen.count ? 0 : spDMALen.skip;
|
||||
|
||||
dram_address += (length + skip);
|
||||
dram_address &= 0xFFFFF8;
|
||||
mem_address += length;
|
||||
mem_address &= 0xFF8;
|
||||
}
|
||||
trace("Addresses after: RSP: 0x{:08X}, Dram: 0x{:08X}", mem_address, dram_address);
|
||||
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
lastSuccessfulSPAddr.address = mem_address;
|
||||
lastSuccessfulSPAddr.bank = spDMASPAddr.bank;
|
||||
lastSuccessfulDRAMAddr.address = dram_address;
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
}
|
||||
|
||||
void RSP::Write(const u32 addr, const u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
spDMASPAddr.raw = val & 0x1FF8;
|
||||
break;
|
||||
case 0x04040004:
|
||||
spDMADRAMAddr.raw = val & 0xFFFFF8;
|
||||
break;
|
||||
case 0x04040008:
|
||||
spDMALen.raw = val;
|
||||
DMA<false>();
|
||||
break;
|
||||
case 0x0404000C:
|
||||
spDMALen.raw = val;
|
||||
DMA<true>();
|
||||
break;
|
||||
case 0x04040010:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
case 0x0404001C:
|
||||
ReleaseSemaphore();
|
||||
break;
|
||||
case 0x04080000:
|
||||
if (spStatus.halt) {
|
||||
SetPC(val);
|
||||
switch (addr) {
|
||||
case 0x04040000:
|
||||
spDMASPAddr.raw = val & 0x1FF8;
|
||||
break;
|
||||
case 0x04040004:
|
||||
spDMADRAMAddr.raw = val & 0xFFFFF8;
|
||||
break;
|
||||
case 0x04040008:
|
||||
spDMALen.raw = val;
|
||||
DMA<false>();
|
||||
break;
|
||||
case 0x0404000C:
|
||||
spDMALen.raw = val;
|
||||
DMA<true>();
|
||||
break;
|
||||
case 0x04040010:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
case 0x0404001C:
|
||||
ReleaseSemaphore();
|
||||
break;
|
||||
case 0x04080000:
|
||||
if (spStatus.halt) {
|
||||
SetPC(val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented SP register write {:08X}, val: {:08X}", addr, val);
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
+325
-333
@@ -5,110 +5,111 @@
|
||||
#include <core/RDP.hpp>
|
||||
#include <core/mmio/MI.hpp>
|
||||
#include <Instruction.hpp>
|
||||
#include <JITUtils.hpp>
|
||||
|
||||
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
|
||||
#define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1))
|
||||
#define SET_RSP_HALF(addr, value) \
|
||||
do { \
|
||||
RSP_BYTE(addr) = ((value) >> 8) & 0xFF; \
|
||||
RSP_BYTE((addr) + 1) = (value) & 0xFF; \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
RSP_BYTE(addr) = ((value) >> 8) & 0xFF; \
|
||||
RSP_BYTE((addr) + 1) = (value) & 0xFF; \
|
||||
} \
|
||||
while (0)
|
||||
#define GET_RSP_WORD(addr) ((GET_RSP_HALF(addr) << 16) | GET_RSP_HALF((addr) + 2))
|
||||
#define SET_RSP_WORD(addr, value) \
|
||||
do { \
|
||||
SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); \
|
||||
SET_RSP_HALF((addr) + 2, (value) & 0xFFFF); \
|
||||
} \
|
||||
while (0)
|
||||
do { \
|
||||
SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); \
|
||||
SET_RSP_HALF((addr) + 2, (value) & 0xFFFF); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
namespace n64 {
|
||||
union SPStatus {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned halt : 1;
|
||||
unsigned broke : 1;
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned dmaFull : 1;
|
||||
unsigned ioFull : 1;
|
||||
unsigned singleStep : 1;
|
||||
unsigned interruptOnBreak : 1;
|
||||
unsigned signal0 : 1;
|
||||
unsigned signal1 : 1;
|
||||
unsigned signal2 : 1;
|
||||
unsigned signal3 : 1;
|
||||
unsigned signal4 : 1;
|
||||
unsigned signal5 : 1;
|
||||
unsigned signal6 : 1;
|
||||
unsigned signal7 : 1;
|
||||
unsigned : 17;
|
||||
};
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned halt : 1;
|
||||
unsigned broke : 1;
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned dmaFull : 1;
|
||||
unsigned ioFull : 1;
|
||||
unsigned singleStep : 1;
|
||||
unsigned interruptOnBreak : 1;
|
||||
unsigned signal0 : 1;
|
||||
unsigned signal1 : 1;
|
||||
unsigned signal2 : 1;
|
||||
unsigned signal3 : 1;
|
||||
unsigned signal4 : 1;
|
||||
unsigned signal5 : 1;
|
||||
unsigned signal6 : 1;
|
||||
unsigned signal7 : 1;
|
||||
unsigned : 17;
|
||||
};
|
||||
};
|
||||
|
||||
union SPStatusWrite {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned clearHalt : 1;
|
||||
unsigned setHalt : 1;
|
||||
unsigned clearBroke : 1;
|
||||
unsigned clearIntr : 1;
|
||||
unsigned setIntr : 1;
|
||||
unsigned clearSstep : 1;
|
||||
unsigned setSstep : 1;
|
||||
unsigned clearIntrOnBreak : 1;
|
||||
unsigned setIntrOnBreak : 1;
|
||||
unsigned clearSignal0 : 1;
|
||||
unsigned setSignal0 : 1;
|
||||
unsigned clearSignal1 : 1;
|
||||
unsigned setSignal1 : 1;
|
||||
unsigned clearSignal2 : 1;
|
||||
unsigned setSignal2 : 1;
|
||||
unsigned clearSignal3 : 1;
|
||||
unsigned setSignal3 : 1;
|
||||
unsigned clearSignal4 : 1;
|
||||
unsigned setSignal4 : 1;
|
||||
unsigned clearSignal5 : 1;
|
||||
unsigned setSignal5 : 1;
|
||||
unsigned clearSignal6 : 1;
|
||||
unsigned setSignal6 : 1;
|
||||
unsigned clearSignal7 : 1;
|
||||
unsigned setSignal7 : 1;
|
||||
unsigned : 7;
|
||||
};
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned clearHalt : 1;
|
||||
unsigned setHalt : 1;
|
||||
unsigned clearBroke : 1;
|
||||
unsigned clearIntr : 1;
|
||||
unsigned setIntr : 1;
|
||||
unsigned clearSstep : 1;
|
||||
unsigned setSstep : 1;
|
||||
unsigned clearIntrOnBreak : 1;
|
||||
unsigned setIntrOnBreak : 1;
|
||||
unsigned clearSignal0 : 1;
|
||||
unsigned setSignal0 : 1;
|
||||
unsigned clearSignal1 : 1;
|
||||
unsigned setSignal1 : 1;
|
||||
unsigned clearSignal2 : 1;
|
||||
unsigned setSignal2 : 1;
|
||||
unsigned clearSignal3 : 1;
|
||||
unsigned setSignal3 : 1;
|
||||
unsigned clearSignal4 : 1;
|
||||
unsigned setSignal4 : 1;
|
||||
unsigned clearSignal5 : 1;
|
||||
unsigned setSignal5 : 1;
|
||||
unsigned clearSignal6 : 1;
|
||||
unsigned setSignal6 : 1;
|
||||
unsigned clearSignal7 : 1;
|
||||
unsigned setSignal7 : 1;
|
||||
unsigned : 7;
|
||||
};
|
||||
};
|
||||
|
||||
union SPDMALen {
|
||||
struct {
|
||||
unsigned len : 12;
|
||||
unsigned count : 8;
|
||||
unsigned skip : 12;
|
||||
};
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned len : 12;
|
||||
unsigned count : 8;
|
||||
unsigned skip : 12;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
union SPDMASPAddr {
|
||||
struct {
|
||||
unsigned address : 12;
|
||||
unsigned bank : 1;
|
||||
unsigned : 19;
|
||||
};
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned address : 12;
|
||||
unsigned bank : 1;
|
||||
unsigned : 19;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
union SPDMADRAMAddr {
|
||||
struct {
|
||||
unsigned address : 24;
|
||||
unsigned : 8;
|
||||
};
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned address : 24;
|
||||
unsigned : 8;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
union VPR {
|
||||
s16 selement[8];
|
||||
u16 element[8];
|
||||
u8 byte[16];
|
||||
u32 word[4];
|
||||
m128i single;
|
||||
s16 selement[8];
|
||||
u16 element[8];
|
||||
u8 byte[16];
|
||||
u32 word[4];
|
||||
m128i single;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(sizeof(VPR) == 16);
|
||||
@@ -119,277 +120,268 @@ struct Registers;
|
||||
#define DE(x) (((x) >> 11) & 0x1F)
|
||||
|
||||
struct RSP {
|
||||
bool divInLoaded = false;
|
||||
bool semaphore = false;
|
||||
std::array<u8, DMEM_SIZE> dmem{};
|
||||
std::array<u8, IMEM_SIZE> imem{};
|
||||
u16 oldPC{}, pc{}, nextPC{};
|
||||
s16 divIn{}, divOut{};
|
||||
u32 steps = 0;
|
||||
SPStatus spStatus{};
|
||||
SPDMASPAddr spDMASPAddr{};
|
||||
SPDMADRAMAddr spDMADRAMAddr{};
|
||||
SPDMASPAddr lastSuccessfulSPAddr{};
|
||||
SPDMADRAMAddr lastSuccessfulDRAMAddr{};
|
||||
SPDMALen spDMALen{};
|
||||
s32 gpr[32]{};
|
||||
VPR vpr[32]{};
|
||||
VPR vte{};
|
||||
VPR vce{};
|
||||
bool divInLoaded = false;
|
||||
bool semaphore = false;
|
||||
std::array<u8, DMEM_SIZE> dmem{};
|
||||
std::array<u8, IMEM_SIZE> imem{};
|
||||
u16 oldPC{}, pc{}, nextPC{};
|
||||
s16 divIn{}, divOut{};
|
||||
u32 steps = 0;
|
||||
SPStatus spStatus{};
|
||||
SPDMASPAddr spDMASPAddr{};
|
||||
SPDMADRAMAddr spDMADRAMAddr{};
|
||||
SPDMASPAddr lastSuccessfulSPAddr{};
|
||||
SPDMADRAMAddr lastSuccessfulDRAMAddr{};
|
||||
SPDMALen spDMALen{};
|
||||
s32 gpr[32]{};
|
||||
VPR vpr[32]{};
|
||||
VPR vte{};
|
||||
VPR vce{};
|
||||
|
||||
struct {
|
||||
VPR h{}, m{}, l{};
|
||||
} acc;
|
||||
struct {
|
||||
VPR h{}, m{}, l{};
|
||||
} acc;
|
||||
|
||||
struct {
|
||||
VPR l{}, h{};
|
||||
} vcc, vco;
|
||||
struct {
|
||||
VPR l{}, h{};
|
||||
} vcc, vco;
|
||||
|
||||
RSP();
|
||||
void Reset();
|
||||
RSP();
|
||||
void Reset();
|
||||
|
||||
FORCE_INLINE void Step() {
|
||||
gpr[0] = 0;
|
||||
const u32 instr = ircolib::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
|
||||
oldPC = pc & 0xFFC;
|
||||
pc = nextPC & 0xFFC;
|
||||
nextPC += 4;
|
||||
FORCE_INLINE void Step() {
|
||||
gpr[0] = 0;
|
||||
const u32 instr = ircolib::ReadAccess<u32>(imem, pc & IMEM_DSIZE);
|
||||
oldPC = pc & 0xFFC;
|
||||
pc = nextPC & 0xFFC;
|
||||
nextPC += 4;
|
||||
|
||||
Exec(instr);
|
||||
}
|
||||
|
||||
void SetVTE(const VPR &vt, u8 e);
|
||||
auto Read(u32 addr) -> u32;
|
||||
void Write(u32 addr, u32 val);
|
||||
void Exec(Instruction instr);
|
||||
|
||||
FORCE_INLINE void SetPC(const u16 val) {
|
||||
oldPC = pc & 0xFFC;
|
||||
pc = val & 0xFFC;
|
||||
nextPC = pc + 4;
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE s64 GetACC(const int e) const {
|
||||
s64 val = u64(acc.h.element[e]) << 32;
|
||||
val |= u64(acc.m.element[e]) << 16;
|
||||
val |= u64(acc.l.element[e]) << 00;
|
||||
if ((val & 0x0000800000000000) != 0) {
|
||||
val |= 0xFFFF000000000000;
|
||||
Exec(instr);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
FORCE_INLINE void SetACC(const int e, const s64 val) {
|
||||
acc.h.element[e] = val >> 32;
|
||||
acc.m.element[e] = val >> 16;
|
||||
acc.l.element[e] = val;
|
||||
}
|
||||
void SetVTE(const VPR &vt, u8 e);
|
||||
auto Read(u32 addr) -> u32;
|
||||
void Write(u32 addr, u32 val);
|
||||
void Exec(Instruction instr);
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u16 GetVCO() const {
|
||||
u16 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool h = vco.h.element[7 - i] != 0;
|
||||
const bool l = vco.l.element[7 - i] != 0;
|
||||
const u32 mask = (l << i) | (h << (i + 8));
|
||||
value |= mask;
|
||||
FORCE_INLINE void SetPC(const u16 val) {
|
||||
oldPC = pc & 0xFFC;
|
||||
pc = val & 0xFFC;
|
||||
nextPC = pc + 4;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u16 GetVCC() const {
|
||||
u16 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool h = vcc.h.element[7 - i] != 0;
|
||||
const bool l = vcc.l.element[7 - i] != 0;
|
||||
const u32 mask = (l << i) | (h << (i + 8));
|
||||
value |= mask;
|
||||
[[nodiscard]] FORCE_INLINE s64 GetACC(const int e) const {
|
||||
s64 val = u64(acc.h.element[e]) << 32;
|
||||
val |= u64(acc.m.element[e]) << 16;
|
||||
val |= u64(acc.l.element[e]) << 00;
|
||||
if ((val & 0x0000800000000000) != 0) {
|
||||
val |= 0xFFFF000000000000;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u8 GetVCE() const {
|
||||
u8 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool l = vce.element[ELEMENT_INDEX(i)] != 0;
|
||||
value |= (l << i);
|
||||
FORCE_INLINE void SetACC(const int e, const s64 val) {
|
||||
acc.h.element[e] = val >> 32;
|
||||
acc.m.element[e] = val >> 16;
|
||||
acc.l.element[e] = val;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u32 ReadWord(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return GET_RSP_WORD(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteWord(u32 addr, const u32 val) {
|
||||
addr &= 0xfff;
|
||||
SET_RSP_WORD(addr, val);
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u16 ReadHalf(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return GET_RSP_HALF(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteHalf(u32 addr, const u16 val) {
|
||||
addr &= 0xfff;
|
||||
SET_RSP_HALF(addr, val);
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u8 ReadByte(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return RSP_BYTE(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteByte(u32 addr, const u8 val) {
|
||||
addr &= 0xfff;
|
||||
RSP_BYTE(addr) = val;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool AcquireSemaphore() {
|
||||
if (semaphore) {
|
||||
return true;
|
||||
} else {
|
||||
semaphore = true;
|
||||
return false;
|
||||
[[nodiscard]] FORCE_INLINE u16 GetVCO() const {
|
||||
u16 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool h = vco.h.element[7 - i] != 0;
|
||||
const bool l = vco.l.element[7 - i] != 0;
|
||||
const u32 mask = (l << i) | (h << (i + 8));
|
||||
value |= mask;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
|
||||
|
||||
void special(Instruction instr);
|
||||
void regimm(Instruction instr);
|
||||
void lwc2(Instruction instr);
|
||||
void swc2(Instruction instr);
|
||||
void cop2(Instruction instr);
|
||||
void cop0(Instruction instr);
|
||||
|
||||
void add(Instruction instr);
|
||||
void addi(Instruction instr);
|
||||
void and_(Instruction instr);
|
||||
void andi(Instruction instr);
|
||||
void b(Instruction instr, bool cond);
|
||||
void blink(Instruction instr, bool cond);
|
||||
void cfc2(Instruction instr);
|
||||
void ctc2(Instruction instr);
|
||||
void lb(Instruction instr);
|
||||
void lh(Instruction instr);
|
||||
void lw(Instruction instr);
|
||||
void lbu(Instruction instr);
|
||||
void lhu(Instruction instr);
|
||||
void lui(Instruction instr);
|
||||
void luv(Instruction instr);
|
||||
void lbv(Instruction instr);
|
||||
void ldv(Instruction instr);
|
||||
void lsv(Instruction instr);
|
||||
void llv(Instruction instr);
|
||||
void lrv(Instruction instr);
|
||||
void lqv(Instruction instr);
|
||||
void lfv(Instruction instr);
|
||||
void lhv(Instruction instr);
|
||||
void ltv(Instruction instr);
|
||||
void lpv(Instruction instr);
|
||||
void j(Instruction instr);
|
||||
void jal(Instruction instr);
|
||||
void jr(Instruction instr);
|
||||
void jalr(Instruction instr);
|
||||
void nor(Instruction instr);
|
||||
void or_(Instruction instr);
|
||||
void ori(Instruction instr);
|
||||
void xor_(Instruction instr);
|
||||
void xori(Instruction instr);
|
||||
void sb(Instruction instr);
|
||||
void sh(Instruction instr);
|
||||
void sw(Instruction instr);
|
||||
void swv(Instruction instr);
|
||||
void sub(Instruction instr);
|
||||
void sbv(Instruction instr);
|
||||
void sdv(Instruction instr);
|
||||
void stv(Instruction instr);
|
||||
void sqv(Instruction instr);
|
||||
void ssv(Instruction instr);
|
||||
void suv(Instruction instr);
|
||||
void slv(Instruction instr);
|
||||
void shv(Instruction instr);
|
||||
void sfv(Instruction instr);
|
||||
void srv(Instruction instr);
|
||||
void spv(Instruction instr);
|
||||
void sllv(Instruction instr);
|
||||
void srlv(Instruction instr);
|
||||
void srav(Instruction instr);
|
||||
void sll(Instruction instr);
|
||||
void srl(Instruction instr);
|
||||
void sra(Instruction instr);
|
||||
void slt(Instruction instr);
|
||||
void sltu(Instruction instr);
|
||||
void slti(Instruction instr);
|
||||
void sltiu(Instruction instr);
|
||||
void vabs(Instruction instr);
|
||||
void vadd(Instruction instr);
|
||||
void vaddc(Instruction instr);
|
||||
void vand(Instruction instr);
|
||||
void vnand(Instruction instr);
|
||||
void vch(Instruction instr);
|
||||
void vcr(Instruction instr);
|
||||
void vcl(Instruction instr);
|
||||
void vmacf(Instruction instr);
|
||||
void vmacu(Instruction instr);
|
||||
void vmacq(Instruction instr);
|
||||
void vmadh(Instruction instr);
|
||||
void vmadl(Instruction instr);
|
||||
void vmadm(Instruction instr);
|
||||
void vmadn(Instruction instr);
|
||||
void vmov(Instruction instr);
|
||||
void vmulf(Instruction instr);
|
||||
void vmulu(Instruction instr);
|
||||
void vmulq(Instruction instr);
|
||||
void vmudl(Instruction instr);
|
||||
void vmudh(Instruction instr);
|
||||
void vmudm(Instruction instr);
|
||||
void vmudn(Instruction instr);
|
||||
void vmrg(Instruction instr);
|
||||
void vlt(Instruction instr);
|
||||
void veq(Instruction instr);
|
||||
void vne(Instruction instr);
|
||||
void vge(Instruction instr);
|
||||
void vrcp(Instruction instr);
|
||||
void vrsq(Instruction instr);
|
||||
void vrcpl(Instruction instr);
|
||||
void vrsql(Instruction instr);
|
||||
void vrndp(Instruction instr);
|
||||
void vrndn(Instruction instr);
|
||||
void vrcph(Instruction instr);
|
||||
void vsar(Instruction instr);
|
||||
void vsub(Instruction instr);
|
||||
void vsubc(Instruction instr);
|
||||
void vxor(Instruction instr);
|
||||
void vnxor(Instruction instr);
|
||||
void vor(Instruction instr);
|
||||
void vnor(Instruction instr);
|
||||
void vzero(Instruction instr);
|
||||
void mfc0(const RDP &rdp, Instruction instr);
|
||||
void mtc0(Instruction instr) const;
|
||||
void mfc2(Instruction instr);
|
||||
void mtc2(Instruction instr);
|
||||
|
||||
template <bool toRdram>
|
||||
void DMA();
|
||||
void WriteStatus(u32 value);
|
||||
|
||||
private:
|
||||
FORCE_INLINE void branch(const u16 address, const bool cond) {
|
||||
if (cond) {
|
||||
nextPC = address & 0xFFC;
|
||||
[[nodiscard]] FORCE_INLINE u16 GetVCC() const {
|
||||
u16 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool h = vcc.h.element[7 - i] != 0;
|
||||
const bool l = vcc.l.element[7 - i] != 0;
|
||||
const u32 mask = (l << i) | (h << (i + 8));
|
||||
value |= mask;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void branch_likely(const u16 address, const bool cond) {
|
||||
if (cond) {
|
||||
nextPC = address & 0xFFC;
|
||||
} else {
|
||||
pc = nextPC & 0xFFC;
|
||||
nextPC = pc + 4;
|
||||
[[nodiscard]] FORCE_INLINE u8 GetVCE() const {
|
||||
u8 value = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
const bool l = vce.element[ELEMENT_INDEX(i)] != 0;
|
||||
value |= (l << i);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u32 ReadWord(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return GET_RSP_WORD(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteWord(u32 addr, const u32 val) {
|
||||
addr &= 0xfff;
|
||||
SET_RSP_WORD(addr, val);
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u16 ReadHalf(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return GET_RSP_HALF(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteHalf(u32 addr, const u16 val) {
|
||||
addr &= 0xfff;
|
||||
SET_RSP_HALF(addr, val);
|
||||
}
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u8 ReadByte(u32 addr) const {
|
||||
addr &= 0xfff;
|
||||
return RSP_BYTE(addr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteByte(u32 addr, const u8 val) {
|
||||
addr &= 0xfff;
|
||||
RSP_BYTE(addr) = val;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool AcquireSemaphore() {
|
||||
if (semaphore) {
|
||||
return true;
|
||||
} else {
|
||||
semaphore = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
|
||||
|
||||
void special(Instruction instr);
|
||||
void regimm(Instruction instr);
|
||||
void lwc2(Instruction instr);
|
||||
void swc2(Instruction instr);
|
||||
void cop2(Instruction instr);
|
||||
void cop0(Instruction instr);
|
||||
|
||||
void add(Instruction instr);
|
||||
void addi(Instruction instr);
|
||||
void and_(Instruction instr);
|
||||
void andi(Instruction instr);
|
||||
void b(Instruction instr, bool cond);
|
||||
void blink(Instruction instr, bool cond);
|
||||
void cfc2(Instruction instr);
|
||||
void ctc2(Instruction instr);
|
||||
void lb(Instruction instr);
|
||||
void lh(Instruction instr);
|
||||
void lw(Instruction instr);
|
||||
void lbu(Instruction instr);
|
||||
void lhu(Instruction instr);
|
||||
void lui(Instruction instr);
|
||||
void luv(Instruction instr);
|
||||
void lbv(Instruction instr);
|
||||
void ldv(Instruction instr);
|
||||
void lsv(Instruction instr);
|
||||
void llv(Instruction instr);
|
||||
void lrv(Instruction instr);
|
||||
void lqv(Instruction instr);
|
||||
void lfv(Instruction instr);
|
||||
void lhv(Instruction instr);
|
||||
void ltv(Instruction instr);
|
||||
void lpv(Instruction instr);
|
||||
void j(Instruction instr);
|
||||
void jal(Instruction instr);
|
||||
void jr(Instruction instr);
|
||||
void jalr(Instruction instr);
|
||||
void nor(Instruction instr);
|
||||
void or_(Instruction instr);
|
||||
void ori(Instruction instr);
|
||||
void xor_(Instruction instr);
|
||||
void xori(Instruction instr);
|
||||
void sb(Instruction instr);
|
||||
void sh(Instruction instr);
|
||||
void sw(Instruction instr);
|
||||
void swv(Instruction instr);
|
||||
void sub(Instruction instr);
|
||||
void sbv(Instruction instr);
|
||||
void sdv(Instruction instr);
|
||||
void stv(Instruction instr);
|
||||
void sqv(Instruction instr);
|
||||
void ssv(Instruction instr);
|
||||
void suv(Instruction instr);
|
||||
void slv(Instruction instr);
|
||||
void shv(Instruction instr);
|
||||
void sfv(Instruction instr);
|
||||
void srv(Instruction instr);
|
||||
void spv(Instruction instr);
|
||||
void sllv(Instruction instr);
|
||||
void srlv(Instruction instr);
|
||||
void srav(Instruction instr);
|
||||
void sll(Instruction instr);
|
||||
void srl(Instruction instr);
|
||||
void sra(Instruction instr);
|
||||
void slt(Instruction instr);
|
||||
void sltu(Instruction instr);
|
||||
void slti(Instruction instr);
|
||||
void sltiu(Instruction instr);
|
||||
void vabs(Instruction instr);
|
||||
void vadd(Instruction instr);
|
||||
void vaddc(Instruction instr);
|
||||
void vand(Instruction instr);
|
||||
void vnand(Instruction instr);
|
||||
void vch(Instruction instr);
|
||||
void vcr(Instruction instr);
|
||||
void vcl(Instruction instr);
|
||||
void vmacf(Instruction instr);
|
||||
void vmacu(Instruction instr);
|
||||
void vmacq(Instruction instr);
|
||||
void vmadh(Instruction instr);
|
||||
void vmadl(Instruction instr);
|
||||
void vmadm(Instruction instr);
|
||||
void vmadn(Instruction instr);
|
||||
void vmov(Instruction instr);
|
||||
void vmulf(Instruction instr);
|
||||
void vmulu(Instruction instr);
|
||||
void vmulq(Instruction instr);
|
||||
void vmudl(Instruction instr);
|
||||
void vmudh(Instruction instr);
|
||||
void vmudm(Instruction instr);
|
||||
void vmudn(Instruction instr);
|
||||
void vmrg(Instruction instr);
|
||||
void vlt(Instruction instr);
|
||||
void veq(Instruction instr);
|
||||
void vne(Instruction instr);
|
||||
void vge(Instruction instr);
|
||||
void vrcp(Instruction instr);
|
||||
void vrsq(Instruction instr);
|
||||
void vrcpl(Instruction instr);
|
||||
void vrsql(Instruction instr);
|
||||
void vrndp(Instruction instr);
|
||||
void vrndn(Instruction instr);
|
||||
void vrcph(Instruction instr);
|
||||
void vsar(Instruction instr);
|
||||
void vsub(Instruction instr);
|
||||
void vsubc(Instruction instr);
|
||||
void vxor(Instruction instr);
|
||||
void vnxor(Instruction instr);
|
||||
void vor(Instruction instr);
|
||||
void vnor(Instruction instr);
|
||||
void vzero(Instruction instr);
|
||||
void mfc0(const RDP &rdp, Instruction instr);
|
||||
void mtc0(Instruction instr) const;
|
||||
void mfc2(Instruction instr);
|
||||
void mtc2(Instruction instr);
|
||||
|
||||
template <bool toRdram>
|
||||
void DMA();
|
||||
void WriteStatus(u32 value);
|
||||
|
||||
private:
|
||||
FORCE_INLINE void branch(const u16 address, const bool cond) {
|
||||
if (cond) {
|
||||
nextPC = address & 0xFFC;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
@@ -4,90 +4,90 @@
|
||||
|
||||
namespace n64 {
|
||||
void Cop0::mtc0(const Instruction instr) {
|
||||
Registers& regs = Core::GetRegs();
|
||||
SetReg32(instr.rd(), regs.Read<u32>(instr.rt()));
|
||||
Registers ®s = Core::GetRegs();
|
||||
SetReg32(instr.rd(), regs.Read<u32>(instr.rt()));
|
||||
}
|
||||
|
||||
void Cop0::dmtc0(const Instruction instr) {
|
||||
Registers& regs = Core::GetRegs();
|
||||
SetReg64(instr.rd(), regs.Read<u64>(instr.rt()));
|
||||
void Cop0::dmtc0(const Instruction instr) {
|
||||
Registers ®s = Core::GetRegs();
|
||||
SetReg64(instr.rd(), regs.Read<u64>(instr.rt()));
|
||||
}
|
||||
|
||||
void Cop0::mfc0(const Instruction instr) {
|
||||
Registers& regs = Core::GetRegs();
|
||||
regs.Write(instr.rt(), s32(GetReg32(instr.rd())));
|
||||
void Cop0::mfc0(const Instruction instr) {
|
||||
Registers ®s = Core::GetRegs();
|
||||
regs.Write(instr.rt(), s32(GetReg32(instr.rd())));
|
||||
}
|
||||
|
||||
void Cop0::dmfc0(const Instruction instr) const {
|
||||
Registers& regs = Core::GetRegs();
|
||||
regs.Write(instr.rt(), s64(GetReg64(instr.rd())));
|
||||
void Cop0::dmfc0(const Instruction instr) const {
|
||||
Registers ®s = Core::GetRegs();
|
||||
regs.Write(instr.rt(), s64(GetReg64(instr.rd())));
|
||||
}
|
||||
|
||||
void Cop0::eret() {
|
||||
Registers& regs = Core::GetRegs();
|
||||
if (!regs.cop0.kernelMode) {
|
||||
FireException(ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
if (status.erl) {
|
||||
regs.SetPC64(ErrorEPC);
|
||||
status.erl = false;
|
||||
} else {
|
||||
regs.SetPC64(EPC);
|
||||
status.exl = false;
|
||||
}
|
||||
regs.cop0.Update();
|
||||
llbit = false;
|
||||
Registers ®s = Core::GetRegs();
|
||||
if (!regs.cop0.kernelMode) {
|
||||
FireException(Cop0::ExceptionCode::CoprocessorUnusable, 0, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
if (status.erl) {
|
||||
regs.SetPC64(ErrorEPC);
|
||||
status.erl = false;
|
||||
} else {
|
||||
regs.SetPC64(EPC);
|
||||
status.exl = false;
|
||||
}
|
||||
regs.cop0.Update();
|
||||
llbit = false;
|
||||
}
|
||||
|
||||
|
||||
void Cop0::tlbr() {
|
||||
if (index.i >= 32) {
|
||||
panic("TLBR with TLB index {}", index.i);
|
||||
}
|
||||
if (index.i >= 32) {
|
||||
panic("TLBR with TLB index {}", index.i);
|
||||
}
|
||||
|
||||
const TLBEntry entry = tlb[index.i];
|
||||
const TLBEntry entry = tlb[index.i];
|
||||
|
||||
entryHi.raw = entry.entryHi.raw;
|
||||
entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF;
|
||||
entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF;
|
||||
entryHi.raw = entry.entryHi.raw;
|
||||
entryLo0.raw = entry.entryLo0.raw & 0x3FFFFFFF;
|
||||
entryLo1.raw = entry.entryLo1.raw & 0x3FFFFFFF;
|
||||
|
||||
entryLo0.g = entry.global;
|
||||
entryLo1.g = entry.global;
|
||||
pageMask.raw = entry.pageMask.raw;
|
||||
entryLo0.g = entry.global;
|
||||
entryLo1.g = entry.global;
|
||||
pageMask.raw = entry.pageMask.raw;
|
||||
}
|
||||
|
||||
void Cop0::tlbw(const int index_) {
|
||||
PageMask page_mask{};
|
||||
page_mask = pageMask;
|
||||
const u32 top = page_mask.mask & 0xAAA;
|
||||
page_mask.mask = top | (top >> 1);
|
||||
PageMask page_mask{};
|
||||
page_mask = pageMask;
|
||||
const u32 top = page_mask.mask & 0xAAA;
|
||||
page_mask.mask = top | (top >> 1);
|
||||
|
||||
if (index_ >= 32) {
|
||||
panic("TLBWI with TLB index {}", index_);
|
||||
}
|
||||
if (index_ >= 32) {
|
||||
panic("TLBWI with TLB index {}", index_);
|
||||
}
|
||||
|
||||
tlb[index_].entryHi.raw = entryHi.raw;
|
||||
tlb[index_].entryHi.vpn2 &= ~page_mask.mask;
|
||||
tlb[index_].entryHi.raw = entryHi.raw;
|
||||
tlb[index_].entryHi.vpn2 &= ~page_mask.mask;
|
||||
|
||||
tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE;
|
||||
tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE;
|
||||
tlb[index_].pageMask.raw = page_mask.raw;
|
||||
tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE;
|
||||
tlb[index_].entryLo1.raw = entryLo1.raw & 0x03FFFFFE;
|
||||
tlb[index_].pageMask.raw = page_mask.raw;
|
||||
|
||||
tlb[index_].global = entryLo0.g && entryLo1.g;
|
||||
tlb[index_].initialized = true;
|
||||
tlb[index_].global = entryLo0.g && entryLo1.g;
|
||||
tlb[index_].initialized = true;
|
||||
}
|
||||
|
||||
void Cop0::tlbp() {
|
||||
int match = -1;
|
||||
const TLBEntry *entry = TLBTryMatch(entryHi.raw, match);
|
||||
if (match >= 0) {
|
||||
index.raw = match;
|
||||
return;
|
||||
}
|
||||
int match = -1;
|
||||
const TLBEntry *entry = TLBTryMatch(entryHi.raw, match);
|
||||
if (match >= 0) {
|
||||
index.raw = match;
|
||||
return;
|
||||
}
|
||||
|
||||
index.raw = 0;
|
||||
index.p = 1;
|
||||
index.raw = 0;
|
||||
index.p = 1;
|
||||
}
|
||||
|
||||
} // namespace n64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,452 +4,454 @@
|
||||
|
||||
namespace n64 {
|
||||
void Interpreter::special(const Instruction instr) {
|
||||
// 00rr_rccc
|
||||
switch (instr.special()) {
|
||||
case Instruction::SLL:
|
||||
if (instr.instr.raw != 0) {
|
||||
sll(instr);
|
||||
// 00rr_rccc
|
||||
switch (instr.special()) {
|
||||
case Instruction::SLL:
|
||||
if (instr.instr.raw != 0) {
|
||||
sll(instr);
|
||||
}
|
||||
break;
|
||||
case Instruction::SRL:
|
||||
srl(instr);
|
||||
break;
|
||||
case Instruction::SRA:
|
||||
sra(instr);
|
||||
break;
|
||||
case Instruction::SLLV:
|
||||
sllv(instr);
|
||||
break;
|
||||
case Instruction::SRLV:
|
||||
srlv(instr);
|
||||
break;
|
||||
case Instruction::SRAV:
|
||||
srav(instr);
|
||||
break;
|
||||
case Instruction::JR:
|
||||
jr(instr);
|
||||
break;
|
||||
case Instruction::JALR:
|
||||
jalr(instr);
|
||||
break;
|
||||
case Instruction::SYSCALL:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::BREAK:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::SYNC:
|
||||
break; // SYNC
|
||||
case Instruction::MFHI:
|
||||
mfhi(instr);
|
||||
break;
|
||||
case Instruction::MTHI:
|
||||
mthi(instr);
|
||||
break;
|
||||
case Instruction::MFLO:
|
||||
mflo(instr);
|
||||
break;
|
||||
case Instruction::MTLO:
|
||||
mtlo(instr);
|
||||
break;
|
||||
case Instruction::DSLLV:
|
||||
dsllv(instr);
|
||||
break;
|
||||
case Instruction::DSRLV:
|
||||
dsrlv(instr);
|
||||
break;
|
||||
case Instruction::DSRAV:
|
||||
dsrav(instr);
|
||||
break;
|
||||
case Instruction::MULT:
|
||||
mult(instr);
|
||||
break;
|
||||
case Instruction::MULTU:
|
||||
multu(instr);
|
||||
break;
|
||||
case Instruction::DIV:
|
||||
div(instr);
|
||||
break;
|
||||
case Instruction::DIVU:
|
||||
divu(instr);
|
||||
break;
|
||||
case Instruction::DMULT:
|
||||
dmult(instr);
|
||||
break;
|
||||
case Instruction::DMULTU:
|
||||
dmultu(instr);
|
||||
break;
|
||||
case Instruction::DDIV:
|
||||
ddiv(instr);
|
||||
break;
|
||||
case Instruction::DDIVU:
|
||||
ddivu(instr);
|
||||
break;
|
||||
case Instruction::ADD:
|
||||
add(instr);
|
||||
break;
|
||||
case Instruction::ADDU:
|
||||
addu(instr);
|
||||
break;
|
||||
case Instruction::SUB:
|
||||
sub(instr);
|
||||
break;
|
||||
case Instruction::SUBU:
|
||||
subu(instr);
|
||||
break;
|
||||
case Instruction::AND:
|
||||
and_(instr);
|
||||
break;
|
||||
case Instruction::OR:
|
||||
or_(instr);
|
||||
break;
|
||||
case Instruction::XOR:
|
||||
xor_(instr);
|
||||
break;
|
||||
case Instruction::NOR:
|
||||
nor(instr);
|
||||
break;
|
||||
case Instruction::SLT:
|
||||
slt(instr);
|
||||
break;
|
||||
case Instruction::SLTU:
|
||||
sltu(instr);
|
||||
break;
|
||||
case Instruction::DADD:
|
||||
dadd(instr);
|
||||
break;
|
||||
case Instruction::DADDU:
|
||||
daddu(instr);
|
||||
break;
|
||||
case Instruction::DSUB:
|
||||
dsub(instr);
|
||||
break;
|
||||
case Instruction::DSUBU:
|
||||
dsubu(instr);
|
||||
break;
|
||||
case Instruction::TGE:
|
||||
trap(regs.Read<s64>(instr.rs()) >= regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TGEU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= regs.Read<u64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TLT:
|
||||
trap(regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TLTU:
|
||||
trap(regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TEQ:
|
||||
trap(regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TNE:
|
||||
trap(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::DSLL:
|
||||
dsll(instr);
|
||||
break;
|
||||
case Instruction::DSRL:
|
||||
dsrl(instr);
|
||||
break;
|
||||
case Instruction::DSRA:
|
||||
dsra(instr);
|
||||
break;
|
||||
case Instruction::DSLL32:
|
||||
dsll32(instr);
|
||||
break;
|
||||
case Instruction::DSRL32:
|
||||
dsrl32(instr);
|
||||
break;
|
||||
case Instruction::DSRA32:
|
||||
dsra32(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi,
|
||||
instr.instr.opcode.special_lo, instr.instr.raw, static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
break;
|
||||
case Instruction::SRL:
|
||||
srl(instr);
|
||||
break;
|
||||
case Instruction::SRA:
|
||||
sra(instr);
|
||||
break;
|
||||
case Instruction::SLLV:
|
||||
sllv(instr);
|
||||
break;
|
||||
case Instruction::SRLV:
|
||||
srlv(instr);
|
||||
break;
|
||||
case Instruction::SRAV:
|
||||
srav(instr);
|
||||
break;
|
||||
case Instruction::JR:
|
||||
jr(instr);
|
||||
break;
|
||||
case Instruction::JALR:
|
||||
jalr(instr);
|
||||
break;
|
||||
case Instruction::SYSCALL:
|
||||
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::BREAK:
|
||||
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::SYNC:
|
||||
break; // SYNC
|
||||
case Instruction::MFHI:
|
||||
mfhi(instr);
|
||||
break;
|
||||
case Instruction::MTHI:
|
||||
mthi(instr);
|
||||
break;
|
||||
case Instruction::MFLO:
|
||||
mflo(instr);
|
||||
break;
|
||||
case Instruction::MTLO:
|
||||
mtlo(instr);
|
||||
break;
|
||||
case Instruction::DSLLV:
|
||||
dsllv(instr);
|
||||
break;
|
||||
case Instruction::DSRLV:
|
||||
dsrlv(instr);
|
||||
break;
|
||||
case Instruction::DSRAV:
|
||||
dsrav(instr);
|
||||
break;
|
||||
case Instruction::MULT:
|
||||
mult(instr);
|
||||
break;
|
||||
case Instruction::MULTU:
|
||||
multu(instr);
|
||||
break;
|
||||
case Instruction::DIV:
|
||||
div(instr);
|
||||
break;
|
||||
case Instruction::DIVU:
|
||||
divu(instr);
|
||||
break;
|
||||
case Instruction::DMULT:
|
||||
dmult(instr);
|
||||
break;
|
||||
case Instruction::DMULTU:
|
||||
dmultu(instr);
|
||||
break;
|
||||
case Instruction::DDIV:
|
||||
ddiv(instr);
|
||||
break;
|
||||
case Instruction::DDIVU:
|
||||
ddivu(instr);
|
||||
break;
|
||||
case Instruction::ADD:
|
||||
add(instr);
|
||||
break;
|
||||
case Instruction::ADDU:
|
||||
addu(instr);
|
||||
break;
|
||||
case Instruction::SUB:
|
||||
sub(instr);
|
||||
break;
|
||||
case Instruction::SUBU:
|
||||
subu(instr);
|
||||
break;
|
||||
case Instruction::AND:
|
||||
and_(instr);
|
||||
break;
|
||||
case Instruction::OR:
|
||||
or_(instr);
|
||||
break;
|
||||
case Instruction::XOR:
|
||||
xor_(instr);
|
||||
break;
|
||||
case Instruction::NOR:
|
||||
nor(instr);
|
||||
break;
|
||||
case Instruction::SLT:
|
||||
slt(instr);
|
||||
break;
|
||||
case Instruction::SLTU:
|
||||
sltu(instr);
|
||||
break;
|
||||
case Instruction::DADD:
|
||||
dadd(instr);
|
||||
break;
|
||||
case Instruction::DADDU:
|
||||
daddu(instr);
|
||||
break;
|
||||
case Instruction::DSUB:
|
||||
dsub(instr);
|
||||
break;
|
||||
case Instruction::DSUBU:
|
||||
dsubu(instr);
|
||||
break;
|
||||
case Instruction::TGE:
|
||||
trap(regs.Read<s64>(instr.rs()) >= regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TGEU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= regs.Read<u64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TLT:
|
||||
trap(regs.Read<s64>(instr.rs()) < regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TLTU:
|
||||
trap(regs.Read<u64>(instr.rs()) < regs.Read<u64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TEQ:
|
||||
trap(regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::TNE:
|
||||
trap(regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::DSLL:
|
||||
dsll(instr);
|
||||
break;
|
||||
case Instruction::DSRL:
|
||||
dsrl(instr);
|
||||
break;
|
||||
case Instruction::DSRA:
|
||||
dsra(instr);
|
||||
break;
|
||||
case Instruction::DSLL32:
|
||||
dsll32(instr);
|
||||
break;
|
||||
case Instruction::DSRL32:
|
||||
dsrl32(instr);
|
||||
break;
|
||||
case Instruction::DSRA32:
|
||||
dsra32(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.special_hi, instr.instr.opcode.special_lo, instr.instr.raw,
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::regimm(const Instruction instr) {
|
||||
// 000r_rccc
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::BLTZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::TGEI:
|
||||
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TGEIU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
|
||||
break;
|
||||
case Instruction::TLTI:
|
||||
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TLTIU:
|
||||
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
|
||||
break;
|
||||
case Instruction::TEQI:
|
||||
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TNEI:
|
||||
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::BLTZAL:
|
||||
blink(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZAL:
|
||||
blink(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::BLTZALL:
|
||||
bllink(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZALL:
|
||||
bllink(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi,
|
||||
instr.instr.opcode.regimm_lo, u32(instr), static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
// 000r_rccc
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::BLTZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::TGEI:
|
||||
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TGEIU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
|
||||
break;
|
||||
case Instruction::TLTI:
|
||||
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TLTIU:
|
||||
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr.instr.itype.imm))));
|
||||
break;
|
||||
case Instruction::TEQI:
|
||||
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::TNEI:
|
||||
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr.instr.itype.imm)));
|
||||
break;
|
||||
case Instruction::BLTZAL:
|
||||
blink(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZAL:
|
||||
blink(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
case Instruction::BLTZALL:
|
||||
bllink(instr, regs.Read<s64>(instr.rs()) < 0);
|
||||
break;
|
||||
case Instruction::BGEZALL:
|
||||
bllink(instr, regs.Read<s64>(instr.rs()) >= 0);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", instr.instr.opcode.regimm_hi,
|
||||
instr.instr.opcode.regimm_lo, u32(instr), static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::cop2Decode(const Instruction instr) {
|
||||
if (!regs.cop0.status.cu2) {
|
||||
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
switch (instr.rs()) {
|
||||
case 0x00:
|
||||
mfc2(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
dmfc2(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
cfc2(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
mtc2(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
dmtc2(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
ctc2(instr);
|
||||
break;
|
||||
default:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
||||
}
|
||||
if (!regs.cop0.status.cu2) {
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
switch (instr.rs()) {
|
||||
case 0x00:
|
||||
mfc2(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
dmfc2(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
cfc2(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
mtc2(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
dmtc2(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
ctc2(instr);
|
||||
break;
|
||||
default:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::Exec(const Instruction instr) {
|
||||
// 00rr_rccc
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
special(instr);
|
||||
break;
|
||||
case Instruction::REGIMM:
|
||||
regimm(instr);
|
||||
break;
|
||||
case Instruction::J:
|
||||
j(instr);
|
||||
break;
|
||||
case Instruction::JAL:
|
||||
jal(instr);
|
||||
break;
|
||||
case Instruction::BEQ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BNE:
|
||||
b(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BLEZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) <= 0);
|
||||
break;
|
||||
case Instruction::BGTZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) > 0);
|
||||
break;
|
||||
case Instruction::ADDI:
|
||||
addi(instr);
|
||||
break;
|
||||
case Instruction::ADDIU:
|
||||
addiu(instr);
|
||||
break;
|
||||
case Instruction::SLTI:
|
||||
slti(instr);
|
||||
break;
|
||||
case Instruction::SLTIU:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case Instruction::ANDI:
|
||||
andi(instr);
|
||||
break;
|
||||
case Instruction::ORI:
|
||||
ori(instr);
|
||||
break;
|
||||
case Instruction::XORI:
|
||||
xori(instr);
|
||||
break;
|
||||
case Instruction::LUI:
|
||||
lui(instr);
|
||||
break;
|
||||
case Instruction::COP0:
|
||||
regs.cop0.decode(instr);
|
||||
break;
|
||||
case Instruction::COP1:
|
||||
if(instr.cop_rs() == 0x08) {
|
||||
switch (instr.cop_rt()) {
|
||||
case 0:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
void Interpreter::DecodeExecute(const Instruction instr) {
|
||||
// 00rr_rccc
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
special(instr);
|
||||
break;
|
||||
case Instruction::REGIMM:
|
||||
regimm(instr);
|
||||
break;
|
||||
case Instruction::J:
|
||||
j(instr);
|
||||
break;
|
||||
case Instruction::JAL:
|
||||
jal(instr);
|
||||
break;
|
||||
case Instruction::BEQ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BNE:
|
||||
b(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BLEZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) <= 0);
|
||||
break;
|
||||
case Instruction::BGTZ:
|
||||
b(instr, regs.Read<s64>(instr.rs()) > 0);
|
||||
break;
|
||||
case Instruction::ADDI:
|
||||
addi(instr);
|
||||
break;
|
||||
case Instruction::ADDIU:
|
||||
addiu(instr);
|
||||
break;
|
||||
case Instruction::SLTI:
|
||||
slti(instr);
|
||||
break;
|
||||
case Instruction::SLTIU:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case Instruction::ANDI:
|
||||
andi(instr);
|
||||
break;
|
||||
case Instruction::ORI:
|
||||
ori(instr);
|
||||
break;
|
||||
case Instruction::XORI:
|
||||
xori(instr);
|
||||
break;
|
||||
case Instruction::LUI:
|
||||
lui(instr);
|
||||
break;
|
||||
case Instruction::COP0:
|
||||
regs.cop0.decode(instr);
|
||||
break;
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
switch (instr.cop_rt()) {
|
||||
case 0:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
return;
|
||||
b(instr, !regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 1:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
return;
|
||||
b(instr, regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 2:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
return;
|
||||
bl(instr, !regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 3:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
return;
|
||||
bl(instr, regs.cop1.fcr31.compare);
|
||||
break;
|
||||
default:
|
||||
panic("Undefined BC COP1 {:02X}", instr.cop_rt());
|
||||
}
|
||||
return;
|
||||
b(instr, !regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 1:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
}
|
||||
regs.cop1.decode(instr);
|
||||
break;
|
||||
case Instruction::COP2:
|
||||
cop2Decode(instr);
|
||||
break;
|
||||
case Instruction::BEQL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BNEL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BLEZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) <= 0);
|
||||
break;
|
||||
case Instruction::BGTZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) > 0);
|
||||
break;
|
||||
case Instruction::DADDI:
|
||||
daddi(instr);
|
||||
break;
|
||||
case Instruction::DADDIU:
|
||||
daddiu(instr);
|
||||
break;
|
||||
case Instruction::LDL:
|
||||
ldl(instr);
|
||||
break;
|
||||
case Instruction::LDR:
|
||||
ldr(instr);
|
||||
break;
|
||||
case 0x1F:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::LB:
|
||||
lb(instr);
|
||||
break;
|
||||
case Instruction::LH:
|
||||
lh(instr);
|
||||
break;
|
||||
case Instruction::LWL:
|
||||
lwl(instr);
|
||||
break;
|
||||
case Instruction::LW:
|
||||
lw(instr);
|
||||
break;
|
||||
case Instruction::LBU:
|
||||
lbu(instr);
|
||||
break;
|
||||
case Instruction::LHU:
|
||||
lhu(instr);
|
||||
break;
|
||||
case Instruction::LWR:
|
||||
lwr(instr);
|
||||
break;
|
||||
case Instruction::LWU:
|
||||
lwu(instr);
|
||||
break;
|
||||
case Instruction::SB:
|
||||
sb(instr);
|
||||
break;
|
||||
case Instruction::SH:
|
||||
sh(instr);
|
||||
break;
|
||||
case Instruction::SWL:
|
||||
swl(instr);
|
||||
break;
|
||||
case Instruction::SW:
|
||||
sw(instr);
|
||||
break;
|
||||
case Instruction::SDL:
|
||||
sdl(instr);
|
||||
break;
|
||||
case Instruction::SDR:
|
||||
sdr(instr);
|
||||
break;
|
||||
case Instruction::SWR:
|
||||
swr(instr);
|
||||
break;
|
||||
case Instruction::CACHE:
|
||||
// cache(instr);
|
||||
break;
|
||||
case Instruction::LL:
|
||||
ll(instr);
|
||||
break;
|
||||
case Instruction::LWC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
b(instr, regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 2:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
regs.cop1.lwc1(instr);
|
||||
break;
|
||||
case Instruction::LLD:
|
||||
lld(instr);
|
||||
break;
|
||||
case Instruction::LDC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
bl(instr, !regs.cop1.fcr31.compare);
|
||||
break;
|
||||
case 3:
|
||||
if (!regs.cop1.CheckFPUUsable())
|
||||
regs.cop1.ldc1(instr);
|
||||
break;
|
||||
case Instruction::LD:
|
||||
ld(instr);
|
||||
break;
|
||||
case Instruction::SC:
|
||||
sc(instr);
|
||||
break;
|
||||
case Instruction::SWC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
bl(instr, regs.cop1.fcr31.compare);
|
||||
break;
|
||||
default:
|
||||
panic("Undefined BC COP1 {:02X}", instr.cop_rt());
|
||||
}
|
||||
return;
|
||||
regs.cop1.swc1(instr);
|
||||
break;
|
||||
case Instruction::SCD:
|
||||
scd(instr);
|
||||
break;
|
||||
case Instruction::SDC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
regs.cop1.sdc1(instr);
|
||||
break;
|
||||
case Instruction::SD:
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
regs.cop1.decode(instr);
|
||||
break;
|
||||
case Instruction::COP2:
|
||||
cop2Decode(instr);
|
||||
break;
|
||||
case Instruction::BEQL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) == regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BNEL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) != regs.Read<s64>(instr.rt()));
|
||||
break;
|
||||
case Instruction::BLEZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) <= 0);
|
||||
break;
|
||||
case Instruction::BGTZL:
|
||||
bl(instr, regs.Read<s64>(instr.rs()) > 0);
|
||||
break;
|
||||
case Instruction::DADDI:
|
||||
daddi(instr);
|
||||
break;
|
||||
case Instruction::DADDIU:
|
||||
daddiu(instr);
|
||||
break;
|
||||
case Instruction::LDL:
|
||||
ldl(instr);
|
||||
break;
|
||||
case Instruction::LDR:
|
||||
ldr(instr);
|
||||
break;
|
||||
case 0x1F:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::LB:
|
||||
lb(instr);
|
||||
break;
|
||||
case Instruction::LH:
|
||||
lh(instr);
|
||||
break;
|
||||
case Instruction::LWL:
|
||||
lwl(instr);
|
||||
break;
|
||||
case Instruction::LW:
|
||||
lw(instr);
|
||||
break;
|
||||
case Instruction::LBU:
|
||||
lbu(instr);
|
||||
break;
|
||||
case Instruction::LHU:
|
||||
lhu(instr);
|
||||
break;
|
||||
case Instruction::LWR:
|
||||
lwr(instr);
|
||||
break;
|
||||
case Instruction::LWU:
|
||||
lwu(instr);
|
||||
break;
|
||||
case Instruction::SB:
|
||||
sb(instr);
|
||||
break;
|
||||
case Instruction::SH:
|
||||
sh(instr);
|
||||
break;
|
||||
case Instruction::SWL:
|
||||
swl(instr);
|
||||
break;
|
||||
case Instruction::SW:
|
||||
sw(instr);
|
||||
break;
|
||||
case Instruction::SDL:
|
||||
sdl(instr);
|
||||
break;
|
||||
case Instruction::SDR:
|
||||
sdr(instr);
|
||||
break;
|
||||
case Instruction::SWR:
|
||||
swr(instr);
|
||||
break;
|
||||
case Instruction::CACHE:
|
||||
break; // CACHE
|
||||
case Instruction::LL:
|
||||
ll(instr);
|
||||
break;
|
||||
case Instruction::LWC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
regs.cop1.lwc1(instr);
|
||||
break;
|
||||
case Instruction::LLD:
|
||||
lld(instr);
|
||||
break;
|
||||
case Instruction::LDC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
regs.cop1.ldc1(instr);
|
||||
break;
|
||||
case Instruction::LD:
|
||||
ld(instr);
|
||||
break;
|
||||
case Instruction::SC:
|
||||
sc(instr);
|
||||
break;
|
||||
case Instruction::SWC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
regs.cop1.swc1(instr);
|
||||
break;
|
||||
case Instruction::SCD:
|
||||
scd(instr);
|
||||
break;
|
||||
case Instruction::SDC1:
|
||||
if (!regs.cop1.CheckFPUUsable<true>())
|
||||
return;
|
||||
regs.cop1.sdc1(instr);
|
||||
break;
|
||||
case Instruction::SD:
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.instr.opcode.op, u32(instr), static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+442
-442
@@ -3,463 +3,463 @@
|
||||
|
||||
namespace n64 {
|
||||
void JIT::special(const Instruction instr) {
|
||||
// 00rr_rccc
|
||||
switch (instr.special()) {
|
||||
case Instruction::SLL:
|
||||
if (instr != 0) {
|
||||
sll(instr);
|
||||
// 00rr_rccc
|
||||
switch (instr.special()) {
|
||||
case Instruction::SLL:
|
||||
if (instr != 0) {
|
||||
sll(instr);
|
||||
}
|
||||
break;
|
||||
case Instruction::SRL:
|
||||
srl(instr);
|
||||
break;
|
||||
case Instruction::SRA:
|
||||
sra(instr);
|
||||
break;
|
||||
case Instruction::SLLV:
|
||||
sllv(instr);
|
||||
break;
|
||||
case Instruction::SRLV:
|
||||
srlv(instr);
|
||||
break;
|
||||
case Instruction::SRAV:
|
||||
srav(instr);
|
||||
break;
|
||||
case Instruction::JR:
|
||||
jr(instr);
|
||||
break;
|
||||
case Instruction::JALR:
|
||||
jalr(instr);
|
||||
break;
|
||||
case Instruction::SYSCALL:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Syscall, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::BREAK:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::SYNC:
|
||||
break; // SYNC
|
||||
case Instruction::MFHI:
|
||||
mfhi(instr);
|
||||
break;
|
||||
case Instruction::MTHI:
|
||||
mthi(instr);
|
||||
break;
|
||||
case Instruction::MFLO:
|
||||
mflo(instr);
|
||||
break;
|
||||
case Instruction::MTLO:
|
||||
mtlo(instr);
|
||||
break;
|
||||
case Instruction::DSLLV:
|
||||
dsllv(instr);
|
||||
break;
|
||||
case Instruction::DSRLV:
|
||||
dsrlv(instr);
|
||||
break;
|
||||
case Instruction::DSRAV:
|
||||
dsrav(instr);
|
||||
break;
|
||||
case Instruction::MULT:
|
||||
mult(instr);
|
||||
break;
|
||||
case Instruction::MULTU:
|
||||
multu(instr);
|
||||
break;
|
||||
case Instruction::DIV:
|
||||
div(instr);
|
||||
break;
|
||||
case Instruction::DIVU:
|
||||
divu(instr);
|
||||
break;
|
||||
case Instruction::DMULT:
|
||||
dmult(instr);
|
||||
break;
|
||||
case Instruction::DMULTU:
|
||||
dmultu(instr);
|
||||
break;
|
||||
case Instruction::DDIV:
|
||||
ddiv(instr);
|
||||
break;
|
||||
case Instruction::DDIVU:
|
||||
ddivu(instr);
|
||||
break;
|
||||
case Instruction::ADD:
|
||||
add(instr);
|
||||
break;
|
||||
case Instruction::ADDU:
|
||||
addu(instr);
|
||||
break;
|
||||
case Instruction::SUB:
|
||||
sub(instr);
|
||||
break;
|
||||
case Instruction::SUBU:
|
||||
subu(instr);
|
||||
break;
|
||||
case Instruction::AND:
|
||||
and_(instr);
|
||||
break;
|
||||
case Instruction::OR:
|
||||
or_(instr);
|
||||
break;
|
||||
case Instruction::XOR:
|
||||
xor_(instr);
|
||||
break;
|
||||
case Instruction::NOR:
|
||||
nor(instr);
|
||||
break;
|
||||
case Instruction::SLT:
|
||||
slt(instr);
|
||||
break;
|
||||
case Instruction::SLTU:
|
||||
sltu(instr);
|
||||
break;
|
||||
case Instruction::DADD:
|
||||
dadd(instr);
|
||||
break;
|
||||
case Instruction::DADDU:
|
||||
daddu(instr);
|
||||
break;
|
||||
case Instruction::DSUB:
|
||||
dsub(instr);
|
||||
break;
|
||||
case Instruction::DSUBU:
|
||||
dsubu(instr);
|
||||
break;
|
||||
case Instruction::TGE:
|
||||
trap(regs.Read<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.special(), u32(instr),
|
||||
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.special(), u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::regimm(const Instruction instr) {
|
||||
// 000r_rccc
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZ:
|
||||
bltz(instr);
|
||||
break;
|
||||
case Instruction::BGEZ:
|
||||
bgez(instr);
|
||||
break;
|
||||
case Instruction::BLTZL:
|
||||
bltzl(instr);
|
||||
break;
|
||||
case Instruction::BGEZL:
|
||||
bgezl(instr);
|
||||
break;
|
||||
case Instruction::TGEI:
|
||||
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TGEIU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
||||
break;
|
||||
case Instruction::TLTI:
|
||||
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TLTIU:
|
||||
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
||||
break;
|
||||
case Instruction::TEQI:
|
||||
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TNEI:
|
||||
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::BLTZAL:
|
||||
bltzal(instr);
|
||||
break;
|
||||
case Instruction::BGEZAL:
|
||||
bgezal(instr);
|
||||
break;
|
||||
case Instruction::BLTZALL:
|
||||
bltzall(instr);
|
||||
break;
|
||||
case Instruction::BGEZALL:
|
||||
bgezall(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
// 000r_rccc
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZ:
|
||||
bltz(instr);
|
||||
break;
|
||||
case Instruction::BGEZ:
|
||||
bgez(instr);
|
||||
break;
|
||||
case Instruction::BLTZL:
|
||||
bltzl(instr);
|
||||
break;
|
||||
case Instruction::BGEZL:
|
||||
bgezl(instr);
|
||||
break;
|
||||
case Instruction::TGEI:
|
||||
trap(regs.Read<s64>(instr.rs()) >= static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TGEIU:
|
||||
trap(regs.Read<u64>(instr.rs()) >= static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
||||
break;
|
||||
case Instruction::TLTI:
|
||||
trap(regs.Read<s64>(instr.rs()) < static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TLTIU:
|
||||
trap(regs.Read<u64>(instr.rs()) < static_cast<u64>(static_cast<s64>(static_cast<s16>(instr))));
|
||||
break;
|
||||
case Instruction::TEQI:
|
||||
trap(regs.Read<s64>(instr.rs()) == static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::TNEI:
|
||||
trap(regs.Read<s64>(instr.rs()) != static_cast<s64>(static_cast<s16>(instr)));
|
||||
break;
|
||||
case Instruction::BLTZAL:
|
||||
bltzal(instr);
|
||||
break;
|
||||
case Instruction::BGEZAL:
|
||||
bgezal(instr);
|
||||
break;
|
||||
case Instruction::BLTZALL:
|
||||
bltzall(instr);
|
||||
break;
|
||||
case Instruction::BGEZALL:
|
||||
bgezall(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented regimm {} ({:08X}) (pc: {:016X})", instr.regimm(), u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
|
||||
void JIT::Emit(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
special(instr);
|
||||
break;
|
||||
case Instruction::REGIMM:
|
||||
regimm(instr);
|
||||
break;
|
||||
case Instruction::J:
|
||||
j(instr);
|
||||
break;
|
||||
case Instruction::JAL:
|
||||
jal(instr);
|
||||
break;
|
||||
case Instruction::BEQ:
|
||||
beq(instr);
|
||||
break;
|
||||
case Instruction::BNE:
|
||||
bne(instr);
|
||||
break;
|
||||
case Instruction::BLEZ:
|
||||
blez(instr);
|
||||
break;
|
||||
case Instruction::BGTZ:
|
||||
bgtz(instr);
|
||||
break;
|
||||
case Instruction::ADDI:
|
||||
addi(instr);
|
||||
break;
|
||||
case Instruction::ADDIU:
|
||||
addiu(instr);
|
||||
break;
|
||||
case Instruction::SLTI:
|
||||
slti(instr);
|
||||
break;
|
||||
case Instruction::SLTIU:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case Instruction::ANDI:
|
||||
andi(instr);
|
||||
break;
|
||||
case Instruction::ORI:
|
||||
ori(instr);
|
||||
break;
|
||||
case Instruction::XORI:
|
||||
xori(instr);
|
||||
break;
|
||||
case Instruction::LUI:
|
||||
lui(instr);
|
||||
break;
|
||||
case Instruction::COP0:
|
||||
switch (instr.cop_rs()) {
|
||||
case 0x00:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0);
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
special(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0);
|
||||
case Instruction::REGIMM:
|
||||
regimm(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0);
|
||||
case Instruction::J:
|
||||
j(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0);
|
||||
case Instruction::JAL:
|
||||
jal(instr);
|
||||
break;
|
||||
case 0x10 ... 0x1F:
|
||||
switch (instr.cop_funct()) {
|
||||
case 0x01:
|
||||
emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0);
|
||||
break;
|
||||
case 0x02:
|
||||
code.mov(code.ARG2, COP0_REG_INDEX);
|
||||
emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0);
|
||||
code.mov(code.ARG2, code.rax);
|
||||
code.and_(code.ARG2, 0x3F);
|
||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||
break;
|
||||
case 0x06:
|
||||
emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0);
|
||||
code.mov(code.ARG2, code.rax);
|
||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||
break;
|
||||
case 0x08:
|
||||
emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0);
|
||||
break;
|
||||
case 0x18:
|
||||
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr),
|
||||
regs.oldPC);
|
||||
}
|
||||
case Instruction::BEQ:
|
||||
beq(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented COP0 instruction {}", instr.cop_rs());
|
||||
}
|
||||
break;
|
||||
case Instruction::COP1:
|
||||
{
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
switch (instr.cop_rt()) {
|
||||
case 0:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
bfc0(instr);
|
||||
break;
|
||||
case 1:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
bfc1(instr);
|
||||
break;
|
||||
case 2:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
blfc0(instr);
|
||||
break;
|
||||
case 3:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
blfc1(instr);
|
||||
break;
|
||||
case Instruction::BNE:
|
||||
bne(instr);
|
||||
break;
|
||||
case Instruction::BLEZ:
|
||||
blez(instr);
|
||||
break;
|
||||
case Instruction::BGTZ:
|
||||
bgtz(instr);
|
||||
break;
|
||||
case Instruction::ADDI:
|
||||
addi(instr);
|
||||
break;
|
||||
case Instruction::ADDIU:
|
||||
addiu(instr);
|
||||
break;
|
||||
case Instruction::SLTI:
|
||||
slti(instr);
|
||||
break;
|
||||
case Instruction::SLTIU:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case Instruction::ANDI:
|
||||
andi(instr);
|
||||
break;
|
||||
case Instruction::ORI:
|
||||
ori(instr);
|
||||
break;
|
||||
case Instruction::XORI:
|
||||
xori(instr);
|
||||
break;
|
||||
case Instruction::LUI:
|
||||
lui(instr);
|
||||
break;
|
||||
case Instruction::COP0:
|
||||
switch (instr.cop_rs()) {
|
||||
case 0x00:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::mfc0, ®s.cop0);
|
||||
break;
|
||||
case 0x01:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::dmfc0, ®s.cop0);
|
||||
break;
|
||||
case 0x04:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::mtc0, ®s.cop0);
|
||||
break;
|
||||
case 0x05:
|
||||
code.mov(code.ARG2, instr);
|
||||
emitMemberFunctionCall(&Cop0::dmtc0, ®s.cop0);
|
||||
break;
|
||||
case 0x10 ... 0x1F:
|
||||
switch (instr.cop_funct()) {
|
||||
case 0x01:
|
||||
emitMemberFunctionCall(&Cop0::tlbr, ®s.cop0);
|
||||
break;
|
||||
case 0x02:
|
||||
code.mov(code.ARG2, COP0_REG_INDEX);
|
||||
emitMemberFunctionCall(&Cop0::GetReg32, ®s.cop0);
|
||||
code.mov(code.ARG2, code.rax);
|
||||
code.and_(code.ARG2, 0x3F);
|
||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||
break;
|
||||
case 0x06:
|
||||
emitMemberFunctionCall(&Cop0::GetRandom, ®s.cop0);
|
||||
code.mov(code.ARG2, code.rax);
|
||||
emitMemberFunctionCall(&Cop0::tlbw, ®s.cop0);
|
||||
break;
|
||||
case 0x08:
|
||||
emitMemberFunctionCall(&Cop0::tlbp, ®s.cop0);
|
||||
break;
|
||||
case 0x18:
|
||||
emitMemberFunctionCall(&Cop0::eret, ®s.cop0);
|
||||
break;
|
||||
default:
|
||||
panic("Unimplemented COP0 function {} ({:08X}) ({:016X})", instr.cop_funct(), u32(instr), regs.oldPC);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("Undefined BC COP1 {:02X}", instr.cop_rt());
|
||||
panic("Unimplemented COP0 instruction {}", instr.cop_rs());
|
||||
}
|
||||
break;
|
||||
}
|
||||
regs.cop1.decode(instr);
|
||||
case Instruction::COP1:
|
||||
{
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
switch (instr.cop_rt()) {
|
||||
case 0:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
bfc0(instr);
|
||||
break;
|
||||
case 1:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
bfc1(instr);
|
||||
break;
|
||||
case 2:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
blfc0(instr);
|
||||
break;
|
||||
case 3:
|
||||
// if (!regs.cop1.CheckFPUUsable())
|
||||
// return;
|
||||
blfc1(instr);
|
||||
break;
|
||||
default:
|
||||
panic("Undefined BC COP1 {:02X}", instr.cop_rt());
|
||||
}
|
||||
break;
|
||||
}
|
||||
regs.cop1.decode(instr);
|
||||
}
|
||||
break;
|
||||
case Instruction::COP2:
|
||||
break;
|
||||
case Instruction::BEQL:
|
||||
beql(instr);
|
||||
break;
|
||||
case Instruction::BNEL:
|
||||
bnel(instr);
|
||||
break;
|
||||
case Instruction::BLEZL:
|
||||
blezl(instr);
|
||||
break;
|
||||
case Instruction::BGTZL:
|
||||
bgtzl(instr);
|
||||
break;
|
||||
case Instruction::DADDI:
|
||||
daddi(instr);
|
||||
break;
|
||||
case Instruction::DADDIU:
|
||||
daddiu(instr);
|
||||
break;
|
||||
case Instruction::LDL:
|
||||
ldl(instr);
|
||||
break;
|
||||
case Instruction::LDR:
|
||||
ldr(instr);
|
||||
break;
|
||||
case 0x1F:
|
||||
regs.cop0.FireException(Cop0::ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::LB:
|
||||
lb(instr);
|
||||
break;
|
||||
case Instruction::LH:
|
||||
lh(instr);
|
||||
break;
|
||||
case Instruction::LWL:
|
||||
lwl(instr);
|
||||
break;
|
||||
case Instruction::LW:
|
||||
lw(instr);
|
||||
break;
|
||||
case Instruction::LBU:
|
||||
lbu(instr);
|
||||
break;
|
||||
case Instruction::LHU:
|
||||
lhu(instr);
|
||||
break;
|
||||
case Instruction::LWR:
|
||||
lwr(instr);
|
||||
break;
|
||||
case Instruction::LWU:
|
||||
lwu(instr);
|
||||
break;
|
||||
case Instruction::SB:
|
||||
sb(instr);
|
||||
break;
|
||||
case Instruction::SH:
|
||||
sh(instr);
|
||||
break;
|
||||
case Instruction::SWL:
|
||||
swl(instr);
|
||||
break;
|
||||
case Instruction::SW:
|
||||
sw(instr);
|
||||
break;
|
||||
case Instruction::SDL:
|
||||
sdl(instr);
|
||||
break;
|
||||
case Instruction::SDR:
|
||||
sdr(instr);
|
||||
break;
|
||||
case Instruction::SWR:
|
||||
swr(instr);
|
||||
break;
|
||||
case Instruction::CACHE:
|
||||
break; // CACHE
|
||||
case Instruction::LL:
|
||||
ll(instr);
|
||||
break;
|
||||
case Instruction::LWC1:
|
||||
lwc1(instr);
|
||||
break;
|
||||
case Instruction::LLD:
|
||||
lld(instr);
|
||||
break;
|
||||
case Instruction::LDC1:
|
||||
ldc1(instr);
|
||||
break;
|
||||
case Instruction::LD:
|
||||
ld(instr);
|
||||
break;
|
||||
case Instruction::SC:
|
||||
sc(instr);
|
||||
break;
|
||||
case Instruction::SWC1:
|
||||
swc1(instr);
|
||||
break;
|
||||
case Instruction::SCD:
|
||||
scd(instr);
|
||||
break;
|
||||
case Instruction::SDC1:
|
||||
sdc1(instr);
|
||||
break;
|
||||
case Instruction::SD:
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
DumpBlockCacheToDisk();
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr),
|
||||
static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
break;
|
||||
case Instruction::COP2:
|
||||
break;
|
||||
case Instruction::BEQL:
|
||||
beql(instr);
|
||||
break;
|
||||
case Instruction::BNEL:
|
||||
bnel(instr);
|
||||
break;
|
||||
case Instruction::BLEZL:
|
||||
blezl(instr);
|
||||
break;
|
||||
case Instruction::BGTZL:
|
||||
bgtzl(instr);
|
||||
break;
|
||||
case Instruction::DADDI:
|
||||
daddi(instr);
|
||||
break;
|
||||
case Instruction::DADDIU:
|
||||
daddiu(instr);
|
||||
break;
|
||||
case Instruction::LDL:
|
||||
ldl(instr);
|
||||
break;
|
||||
case Instruction::LDR:
|
||||
ldr(instr);
|
||||
break;
|
||||
case 0x1F:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||
break;
|
||||
case Instruction::LB:
|
||||
lb(instr);
|
||||
break;
|
||||
case Instruction::LH:
|
||||
lh(instr);
|
||||
break;
|
||||
case Instruction::LWL:
|
||||
lwl(instr);
|
||||
break;
|
||||
case Instruction::LW:
|
||||
lw(instr);
|
||||
break;
|
||||
case Instruction::LBU:
|
||||
lbu(instr);
|
||||
break;
|
||||
case Instruction::LHU:
|
||||
lhu(instr);
|
||||
break;
|
||||
case Instruction::LWR:
|
||||
lwr(instr);
|
||||
break;
|
||||
case Instruction::LWU:
|
||||
lwu(instr);
|
||||
break;
|
||||
case Instruction::SB:
|
||||
sb(instr);
|
||||
break;
|
||||
case Instruction::SH:
|
||||
sh(instr);
|
||||
break;
|
||||
case Instruction::SWL:
|
||||
swl(instr);
|
||||
break;
|
||||
case Instruction::SW:
|
||||
sw(instr);
|
||||
break;
|
||||
case Instruction::SDL:
|
||||
sdl(instr);
|
||||
break;
|
||||
case Instruction::SDR:
|
||||
sdr(instr);
|
||||
break;
|
||||
case Instruction::SWR:
|
||||
swr(instr);
|
||||
break;
|
||||
case Instruction::CACHE:
|
||||
break; // CACHE
|
||||
case Instruction::LL:
|
||||
ll(instr);
|
||||
break;
|
||||
case Instruction::LWC1:
|
||||
lwc1(instr);
|
||||
break;
|
||||
case Instruction::LLD:
|
||||
lld(instr);
|
||||
break;
|
||||
case Instruction::LDC1:
|
||||
ldc1(instr);
|
||||
break;
|
||||
case Instruction::LD:
|
||||
ld(instr);
|
||||
break;
|
||||
case Instruction::SC:
|
||||
sc(instr);
|
||||
break;
|
||||
case Instruction::SWC1:
|
||||
swc1(instr);
|
||||
break;
|
||||
case Instruction::SCD:
|
||||
scd(instr);
|
||||
break;
|
||||
case Instruction::SDC1:
|
||||
sdc1(instr);
|
||||
break;
|
||||
case Instruction::SD:
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
DumpBlockCacheToDisk();
|
||||
panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", instr.opcode(), u32(instr), static_cast<u64>(regs.oldPC));
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,71 +3,141 @@
|
||||
|
||||
namespace n64 {
|
||||
static bool SpecialEndsBlock(const Instruction instr) {
|
||||
switch (instr.special()) {
|
||||
case Instruction::JR:
|
||||
case Instruction::JALR:
|
||||
case Instruction::SYSCALL:
|
||||
case Instruction::BREAK:
|
||||
case Instruction::TGE:
|
||||
case Instruction::TGEU:
|
||||
case Instruction::TLT:
|
||||
case Instruction::TLTU:
|
||||
case Instruction::TEQ:
|
||||
case Instruction::TNE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (instr.special()) {
|
||||
case Instruction::JR:
|
||||
case Instruction::JALR:
|
||||
case Instruction::SYSCALL:
|
||||
case Instruction::BREAK:
|
||||
case Instruction::TGE:
|
||||
case Instruction::TGEU:
|
||||
case Instruction::TLT:
|
||||
case Instruction::TLTU:
|
||||
case Instruction::TEQ:
|
||||
case Instruction::TNE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool InstrHasDelaySlot(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
if (instr.special() == Instruction::JR || instr.special() == Instruction::JALR)
|
||||
return true;
|
||||
return false;
|
||||
case Instruction::REGIMM:
|
||||
case Instruction::J:
|
||||
case Instruction::JAL:
|
||||
case Instruction::BEQ:
|
||||
case Instruction::BNE:
|
||||
case Instruction::BLEZ:
|
||||
case Instruction::BGTZ:
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 8)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool InstrEndsBlock(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
return SpecialEndsBlock(instr);
|
||||
case Instruction::REGIMM:
|
||||
case Instruction::J:
|
||||
case Instruction::JAL:
|
||||
case Instruction::BEQ:
|
||||
case Instruction::BNE:
|
||||
case Instruction::BLEZ:
|
||||
case Instruction::BGTZ:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::SPECIAL:
|
||||
return SpecialEndsBlock(instr);
|
||||
case Instruction::REGIMM:
|
||||
case Instruction::J:
|
||||
case Instruction::JAL:
|
||||
case Instruction::BEQ:
|
||||
case Instruction::BNE:
|
||||
case Instruction::BLEZ:
|
||||
case Instruction::BGTZ:
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 8)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
case Instruction::COP0:
|
||||
switch (instr.cop_rs()) {
|
||||
case 0x10 ... 0x1F:
|
||||
switch (instr.cop_funct()) {
|
||||
case 0x18: // eret
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsBranchLikely(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::REGIMM:
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZL:
|
||||
case Instruction::BGEZL:
|
||||
case Instruction::BLTZALL:
|
||||
case Instruction::BGEZALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Instruction::COP1:
|
||||
{
|
||||
if (instr.cop_rs() == 0x08) {
|
||||
if (instr.cop_rt() == 2 || instr.cop_rt() == 3)
|
||||
return true;
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::BEQL:
|
||||
case Instruction::BNEL:
|
||||
case Instruction::BLEZL:
|
||||
case Instruction::BGTZL:
|
||||
return true;
|
||||
case Instruction::REGIMM:
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZL:
|
||||
case Instruction::BGEZL:
|
||||
case Instruction::BLTZALL:
|
||||
case Instruction::BGEZALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 8 && (instr.cop_rt() == 2 || instr.cop_rt() == 3))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
static bool IsBranch(const Instruction instr) {
|
||||
switch (instr.opcode()) {
|
||||
case Instruction::BEQ:
|
||||
case Instruction::BNE:
|
||||
case Instruction::BLEZ:
|
||||
case Instruction::BGTZ:
|
||||
return true;
|
||||
case Instruction::REGIMM:
|
||||
switch (instr.regimm()) {
|
||||
case Instruction::BLTZ:
|
||||
case Instruction::BGEZ:
|
||||
case Instruction::BLTZAL:
|
||||
case Instruction::BGEZAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Instruction::COP1:
|
||||
if (instr.cop_rs() == 8 && (instr.cop_rt() == 0 || instr.cop_rt() == 1))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
+1058
-1056
File diff suppressed because it is too large
Load Diff
+469
-464
File diff suppressed because it is too large
Load Diff
@@ -3,35 +3,35 @@
|
||||
|
||||
namespace n64 {
|
||||
struct PI {
|
||||
PI();
|
||||
void Reset();
|
||||
[[nodiscard]] auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
PI();
|
||||
void Reset();
|
||||
[[nodiscard]] auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
|
||||
template <typename T, bool isDma>
|
||||
void BusWrite(u32, u32);
|
||||
template <bool isDma>
|
||||
void BusWrite(u32, u64);
|
||||
template <typename T, bool isDma>
|
||||
void BusWrite(u32, u32);
|
||||
template <bool isDma>
|
||||
void BusWrite(u32, u64);
|
||||
|
||||
template <typename T, bool isDma>
|
||||
auto BusRead(u32) -> T;
|
||||
template <typename T, bool isDma>
|
||||
auto BusRead(u32) -> T;
|
||||
|
||||
bool ReadLatch();
|
||||
bool WriteLatch(u32 val);
|
||||
bool ReadLatch();
|
||||
bool WriteLatch(u32 val);
|
||||
|
||||
static u8 GetDomain(u32 address);
|
||||
[[nodiscard]] u32 AccessTiming(u8 domain, u32 length) const;
|
||||
bool dmaBusy{}, ioBusy{};
|
||||
u32 latch{};
|
||||
u32 dramAddr{}, cartAddr{};
|
||||
u32 rdLen{}, wrLen{};
|
||||
u32 piBsdDom1Lat{}, piBsdDom2Lat{};
|
||||
u32 piBsdDom1Pwd{}, piBsdDom2Pwd{};
|
||||
u32 piBsdDom1Pgs{}, piBsdDom2Pgs{};
|
||||
u32 piBsdDom1Rls{}, piBsdDom2Rls{};
|
||||
static u8 GetDomain(u32 address);
|
||||
[[nodiscard]] u32 AccessTiming(u8 domain, u32 length) const;
|
||||
bool dmaBusy{}, ioBusy{};
|
||||
u32 latch{};
|
||||
u32 dramAddr{}, cartAddr{};
|
||||
u32 rdLen{}, wrLen{};
|
||||
u32 piBsdDom1Lat{}, piBsdDom2Lat{};
|
||||
u32 piBsdDom1Pwd{}, piBsdDom2Pwd{};
|
||||
u32 piBsdDom1Pgs{}, piBsdDom2Pgs{};
|
||||
u32 piBsdDom1Rls{}, piBsdDom2Rls{};
|
||||
|
||||
private:
|
||||
template <bool toDram>
|
||||
void DMA();
|
||||
private:
|
||||
template <bool toDram>
|
||||
void DMA();
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
@@ -5,91 +5,91 @@ namespace n64 {
|
||||
SI::SI() { Reset(); }
|
||||
|
||||
void SI::Reset() {
|
||||
status.raw = 0;
|
||||
dramAddr = 0;
|
||||
pifAddr = 0;
|
||||
toDram = false;
|
||||
pif.Reset();
|
||||
status.raw = 0;
|
||||
dramAddr = 0;
|
||||
pifAddr = 0;
|
||||
toDram = false;
|
||||
pif.Reset();
|
||||
}
|
||||
|
||||
auto SI::Read(u32 addr) const -> u32 {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
return dramAddr;
|
||||
case 0x04800004:
|
||||
case 0x04800010:
|
||||
return pifAddr;
|
||||
case 0x0480000C:
|
||||
return 0;
|
||||
case 0x04800018:
|
||||
{
|
||||
u32 val = 0;
|
||||
val |= status.dmaBusy;
|
||||
val |= (0 << 1);
|
||||
val |= (0 << 3);
|
||||
val |= (mem.mmio.mi.intr.si << 12);
|
||||
return val;
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
return dramAddr;
|
||||
case 0x04800004:
|
||||
case 0x04800010:
|
||||
return pifAddr;
|
||||
case 0x0480000C:
|
||||
return 0;
|
||||
case 0x04800018:
|
||||
{
|
||||
u32 val = 0;
|
||||
val |= status.dmaBusy;
|
||||
val |= (0 << 1);
|
||||
val |= (0 << 3);
|
||||
val |= (mem.mmio.mi.intr.si << 12);
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
panic("Unhandled SI[{:08X}] read", addr);
|
||||
}
|
||||
default:
|
||||
panic("Unhandled SI[{:08X}] read", addr);
|
||||
}
|
||||
}
|
||||
|
||||
// pif -> rdram
|
||||
template <>
|
||||
void SI::DMA<true>() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
pif.ProcessCommands();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, pif.Read(pifAddr + i));
|
||||
}
|
||||
trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr);
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
pif.ProcessCommands();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, pif.Read(pifAddr + i));
|
||||
}
|
||||
trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr);
|
||||
}
|
||||
|
||||
// rdram -> pif
|
||||
template <>
|
||||
void SI::DMA<false>() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM<u8>(dramAddr + i));
|
||||
}
|
||||
trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr);
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM<u8>(dramAddr + i));
|
||||
}
|
||||
trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr);
|
||||
}
|
||||
|
||||
void SI::DMA() {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
status.dmaBusy = false;
|
||||
if (toDram)
|
||||
DMA<true>();
|
||||
else
|
||||
DMA<false>();
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::SI);
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
status.dmaBusy = false;
|
||||
if (toDram)
|
||||
DMA<true>();
|
||||
else
|
||||
DMA<false>();
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::SI);
|
||||
}
|
||||
|
||||
void SI::Write(u32 addr, u32 val) {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
dramAddr = val & RDRAM_DSIZE;
|
||||
break;
|
||||
case 0x04800004:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = true;
|
||||
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800010:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = false;
|
||||
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800018:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::SI);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled SI[{:08X}] write ({:08X})", addr, val);
|
||||
}
|
||||
n64::Mem &mem = n64::Core::GetMem();
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
dramAddr = val & RDRAM_DSIZE;
|
||||
break;
|
||||
case 0x04800004:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = true;
|
||||
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800010:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = false;
|
||||
Scheduler::GetInstance().EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800018:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::SI);
|
||||
break;
|
||||
default:
|
||||
panic("Unhandled SI[{:08X}] write ({:08X})", addr, val);
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -4,33 +4,32 @@
|
||||
#include <core/mmio/PIF.hpp>
|
||||
|
||||
namespace n64 {
|
||||
struct SI {
|
||||
SI();
|
||||
void Reset();
|
||||
[[nodiscard]] auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
template <bool toDram>
|
||||
void DMA();
|
||||
void DMA();
|
||||
|
||||
union SIStatus {
|
||||
u32 raw{};
|
||||
struct {
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned ioBusy : 1;
|
||||
unsigned reserved : 1;
|
||||
unsigned dmaErr : 1;
|
||||
unsigned : 8;
|
||||
unsigned intr : 1;
|
||||
};
|
||||
};
|
||||
union Status {
|
||||
u32 raw{};
|
||||
struct {
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned ioBusy : 1;
|
||||
unsigned reserved : 1;
|
||||
unsigned dmaErr : 1;
|
||||
unsigned : 8;
|
||||
unsigned intr : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct SI {
|
||||
SI();
|
||||
void Reset();
|
||||
[[nodiscard]] auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
template <bool toDram>
|
||||
void DMA();
|
||||
void DMA();
|
||||
|
||||
bool toDram = false;
|
||||
SIStatus status{};
|
||||
u32 dramAddr{};
|
||||
u32 pifAddr{};
|
||||
PIF pif;
|
||||
bool toDram = false;
|
||||
Status status{};
|
||||
u32 dramAddr{};
|
||||
u32 pifAddr{};
|
||||
PIF pif;
|
||||
};
|
||||
|
||||
#define SI_DMA_DELAY (65536 * 2)
|
||||
|
||||
+477
-453
File diff suppressed because it is too large
Load Diff
+237
-226
@@ -38,248 +38,259 @@ namespace n64 {
|
||||
#define ENTRY_HI_MASK 0xC00000FFFFFFE0FF
|
||||
#define PAGEMASK_MASK 0x1FFE000
|
||||
|
||||
union Cop0Cause {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned : 8;
|
||||
unsigned interruptPending : 8;
|
||||
unsigned : 16;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned : 2;
|
||||
unsigned exceptionCode : 5;
|
||||
unsigned : 1;
|
||||
unsigned ip0 : 1;
|
||||
unsigned ip1 : 1;
|
||||
unsigned ip2 : 1;
|
||||
unsigned ip3 : 1;
|
||||
unsigned ip4 : 1;
|
||||
unsigned ip5 : 1;
|
||||
unsigned ip6 : 1;
|
||||
unsigned ip7 : 1;
|
||||
unsigned : 12;
|
||||
unsigned copError : 2;
|
||||
unsigned : 1;
|
||||
unsigned branchDelay : 1;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
union Cop0Status {
|
||||
struct {
|
||||
unsigned ie : 1;
|
||||
unsigned exl : 1;
|
||||
unsigned erl : 1;
|
||||
unsigned ksu : 2;
|
||||
unsigned ux : 1;
|
||||
unsigned sx : 1;
|
||||
unsigned kx : 1;
|
||||
unsigned im : 8;
|
||||
unsigned ds : 9;
|
||||
unsigned re : 1;
|
||||
unsigned fr : 1;
|
||||
unsigned rp : 1;
|
||||
unsigned cu0 : 1;
|
||||
unsigned cu1 : 1;
|
||||
unsigned cu2 : 1;
|
||||
unsigned cu3 : 1;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned : 16;
|
||||
unsigned de : 1;
|
||||
unsigned ce : 1;
|
||||
unsigned ch : 1;
|
||||
unsigned : 1;
|
||||
unsigned sr : 1;
|
||||
unsigned ts : 1;
|
||||
unsigned bev : 1;
|
||||
unsigned : 1;
|
||||
unsigned its : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
u32 raw;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
union EntryLo {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned g : 1;
|
||||
unsigned v : 1;
|
||||
unsigned d : 1;
|
||||
unsigned c : 3;
|
||||
unsigned pfn : 20;
|
||||
unsigned : 6;
|
||||
};
|
||||
};
|
||||
|
||||
union EntryHi {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 asid : 8;
|
||||
u64 : 5;
|
||||
u64 vpn2 : 27;
|
||||
u64 fill : 22;
|
||||
u64 r : 2;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
union PageMask {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned : 13;
|
||||
unsigned mask : 12;
|
||||
unsigned : 7;
|
||||
};
|
||||
};
|
||||
|
||||
union Index {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned i : 6;
|
||||
unsigned : 25;
|
||||
unsigned p : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct TLBEntry {
|
||||
bool initialized;
|
||||
EntryLo entryLo0, entryLo1;
|
||||
EntryHi entryHi;
|
||||
PageMask pageMask;
|
||||
|
||||
bool global;
|
||||
};
|
||||
|
||||
enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS };
|
||||
|
||||
enum class ExceptionCode : u8 {
|
||||
Interrupt = 0,
|
||||
TLBModification = 1,
|
||||
TLBLoad = 2,
|
||||
TLBStore = 3,
|
||||
AddressErrorLoad = 4,
|
||||
AddressErrorStore = 5,
|
||||
InstructionBusError = 6,
|
||||
DataBusError = 7,
|
||||
Syscall = 8,
|
||||
Breakpoint = 9,
|
||||
ReservedInstruction = 10,
|
||||
CoprocessorUnusable = 11,
|
||||
Overflow = 12,
|
||||
Trap = 13,
|
||||
FloatingPointError = 15,
|
||||
Watch = 23
|
||||
};
|
||||
|
||||
union Cop0Context {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 19;
|
||||
u64 ptebase : 41;
|
||||
};
|
||||
};
|
||||
|
||||
union Cop0XContext {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 27;
|
||||
u64 r : 2;
|
||||
u64 ptebase : 31;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
struct Cop0 {
|
||||
Cop0();
|
||||
union Cause {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned : 8;
|
||||
unsigned interruptPending : 8;
|
||||
unsigned : 16;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned : 2;
|
||||
unsigned exceptionCode : 5;
|
||||
unsigned : 1;
|
||||
unsigned ip0 : 1;
|
||||
unsigned ip1 : 1;
|
||||
unsigned ip2 : 1;
|
||||
unsigned ip3 : 1;
|
||||
unsigned ip4 : 1;
|
||||
unsigned ip5 : 1;
|
||||
unsigned ip6 : 1;
|
||||
unsigned ip7 : 1;
|
||||
unsigned : 12;
|
||||
unsigned copError : 2;
|
||||
unsigned : 1;
|
||||
unsigned branchDelay : 1;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
bool kernelMode{true};
|
||||
bool supervisorMode{false};
|
||||
bool userMode{false};
|
||||
bool is64BitAddressing{false};
|
||||
bool llbit{};
|
||||
TLBError tlbError = NONE;
|
||||
union Status {
|
||||
struct {
|
||||
unsigned ie : 1;
|
||||
unsigned exl : 1;
|
||||
unsigned erl : 1;
|
||||
unsigned ksu : 2;
|
||||
unsigned ux : 1;
|
||||
unsigned sx : 1;
|
||||
unsigned kx : 1;
|
||||
unsigned im : 8;
|
||||
unsigned ds : 9;
|
||||
unsigned re : 1;
|
||||
unsigned fr : 1;
|
||||
unsigned rp : 1;
|
||||
unsigned cu0 : 1;
|
||||
unsigned cu1 : 1;
|
||||
unsigned cu2 : 1;
|
||||
unsigned cu3 : 1;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned : 16;
|
||||
unsigned de : 1;
|
||||
unsigned ce : 1;
|
||||
unsigned ch : 1;
|
||||
unsigned : 1;
|
||||
unsigned sr : 1;
|
||||
unsigned ts : 1;
|
||||
unsigned bev : 1;
|
||||
unsigned : 1;
|
||||
unsigned its : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
u32 raw;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
PageMask pageMask{};
|
||||
EntryHi entryHi{};
|
||||
EntryLo entryLo0{}, entryLo1{};
|
||||
Index index{};
|
||||
Cop0Context context{};
|
||||
u32 wired{}, r7{};
|
||||
u32 compare{};
|
||||
Cop0Status status{};
|
||||
Cop0Cause cause{};
|
||||
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
||||
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagLo{}, TagHi{};
|
||||
u32 r31{};
|
||||
Cop0XContext xcontext{};
|
||||
u64 badVaddr{}, count{};
|
||||
s64 EPC{};
|
||||
s64 ErrorEPC{};
|
||||
s64 openbus{};
|
||||
TLBEntry tlb[32]{};
|
||||
|
||||
enum TLBAccessType { LOAD, STORE };
|
||||
union EntryLo {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned g : 1;
|
||||
unsigned v : 1;
|
||||
unsigned d : 1;
|
||||
unsigned c : 3;
|
||||
unsigned pfn : 20;
|
||||
unsigned : 6;
|
||||
};
|
||||
};
|
||||
|
||||
u32 GetReg32(u8);
|
||||
[[nodiscard]] u64 GetReg64(u8) const;
|
||||
union EntryHi {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 asid : 8;
|
||||
u64 : 5;
|
||||
u64 vpn2 : 27;
|
||||
u64 fill : 22;
|
||||
u64 r : 2;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
void SetReg32(u8, u32);
|
||||
void SetReg64(u8, u64);
|
||||
union PageMask {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned : 13;
|
||||
unsigned mask : 12;
|
||||
unsigned : 7;
|
||||
};
|
||||
};
|
||||
|
||||
void Reset();
|
||||
union Index {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned i : 6;
|
||||
unsigned : 25;
|
||||
unsigned p : 1;
|
||||
};
|
||||
};
|
||||
|
||||
bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
void FireException(ExceptionCode code, int cop, s64 pc);
|
||||
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
struct TLBEntry {
|
||||
bool initialized;
|
||||
EntryLo entryLo0, entryLo1;
|
||||
EntryHi entryHi;
|
||||
PageMask pageMask;
|
||||
|
||||
TLBEntry *TLBTryMatch(u64 vaddr, int &index);
|
||||
TLBEntry *TLBTryMatch(u64 vaddr);
|
||||
void HandleTLBException(u64 vaddr);
|
||||
static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType);
|
||||
void decode(const Instruction);
|
||||
bool global;
|
||||
};
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u32 GetRandom() const {
|
||||
u32 val = rand();
|
||||
const auto wired_ = GetWired();
|
||||
u32 lower, upper;
|
||||
if (wired_ > 31) {
|
||||
lower = 0;
|
||||
upper = 64;
|
||||
} else {
|
||||
lower = wired_;
|
||||
upper = 32 - wired_;
|
||||
enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS };
|
||||
|
||||
enum class ExceptionCode : u8 {
|
||||
Interrupt = 0,
|
||||
TLBModification = 1,
|
||||
TLBLoad = 2,
|
||||
TLBStore = 3,
|
||||
AddressErrorLoad = 4,
|
||||
AddressErrorStore = 5,
|
||||
InstructionBusError = 6,
|
||||
DataBusError = 7,
|
||||
Syscall = 8,
|
||||
Breakpoint = 9,
|
||||
ReservedInstruction = 10,
|
||||
CoprocessorUnusable = 11,
|
||||
Overflow = 12,
|
||||
Trap = 13,
|
||||
FloatingPointError = 15,
|
||||
Watch = 23
|
||||
};
|
||||
|
||||
union Context {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 19;
|
||||
u64 ptebase : 41;
|
||||
};
|
||||
};
|
||||
|
||||
union XContext {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 27;
|
||||
u64 r : 2;
|
||||
u64 ptebase : 31;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
union TagLo {
|
||||
u32 raw;
|
||||
struct {
|
||||
u64 : 4;
|
||||
u64 ptaglo : 20;
|
||||
u64 pstate : 2;
|
||||
u64 : 6;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
Cop0();
|
||||
|
||||
bool kernelMode{true};
|
||||
bool supervisorMode{false};
|
||||
bool userMode{false};
|
||||
bool is64BitAddressing{false};
|
||||
bool llbit{};
|
||||
TLBError tlbError = NONE;
|
||||
|
||||
TagLo tagLo;
|
||||
PageMask pageMask{};
|
||||
EntryHi entryHi{};
|
||||
EntryLo entryLo0{}, entryLo1{};
|
||||
Index index{};
|
||||
Context context{};
|
||||
u32 wired{}, r7{};
|
||||
u32 compare{};
|
||||
Status status{};
|
||||
Cause cause{};
|
||||
u32 PRId{}, Config{}, LLAddr{}, WatchLo{}, WatchHi{};
|
||||
u32 r21{}, r22{}, r23{}, r24{}, r25{}, ParityError{}, CacheError{}, TagHi{};
|
||||
u32 r31{};
|
||||
XContext xcontext{};
|
||||
u64 badVaddr{}, count{};
|
||||
s64 EPC{};
|
||||
s64 ErrorEPC{};
|
||||
s64 openbus{};
|
||||
TLBEntry tlb[32]{};
|
||||
|
||||
enum TLBAccessType { LOAD, STORE };
|
||||
|
||||
u32 GetReg32(u8);
|
||||
[[nodiscard]] u64 GetReg64(u8) const;
|
||||
|
||||
void SetReg32(u8, u32);
|
||||
void SetReg64(u8, u64);
|
||||
|
||||
void Reset();
|
||||
|
||||
bool ProbeTLB(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
void FireException(ExceptionCode code, int cop, s64 pc);
|
||||
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
|
||||
TLBEntry *TLBTryMatch(u64 vaddr, int &index);
|
||||
TLBEntry *TLBTryMatch(u64 vaddr);
|
||||
void HandleTLBException(u64 vaddr);
|
||||
static ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType accessType);
|
||||
void decode(const Instruction);
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u32 GetRandom() const {
|
||||
u32 val = rand();
|
||||
const auto wired_ = GetWired();
|
||||
u32 lower, upper;
|
||||
if (wired_ > 31) {
|
||||
lower = 0;
|
||||
upper = 64;
|
||||
} else {
|
||||
lower = wired_;
|
||||
upper = 32 - wired_;
|
||||
}
|
||||
|
||||
val = (val % upper) + lower;
|
||||
return val;
|
||||
}
|
||||
|
||||
val = (val % upper) + lower;
|
||||
return val;
|
||||
}
|
||||
FORCE_INLINE void Update() {
|
||||
const bool exception = status.exl || status.erl;
|
||||
|
||||
FORCE_INLINE void Update() {
|
||||
const bool exception = status.exl || status.erl;
|
||||
kernelMode = exception || status.ksu == 0;
|
||||
supervisorMode = !exception && status.ksu == 1;
|
||||
userMode = !exception && status.ksu == 2;
|
||||
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
||||
}
|
||||
|
||||
kernelMode = exception || status.ksu == 0;
|
||||
supervisorMode = !exception && status.ksu == 1;
|
||||
userMode = !exception && status.ksu == 2;
|
||||
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
||||
}
|
||||
private:
|
||||
friend struct JIT;
|
||||
|
||||
private:
|
||||
friend struct JIT;
|
||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
||||
|
||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
||||
void mtc0(const Instruction);
|
||||
void dmtc0(const Instruction);
|
||||
void mfc0(const Instruction);
|
||||
void dmfc0(const Instruction) const;
|
||||
void eret();
|
||||
|
||||
void mtc0(const Instruction);
|
||||
void dmtc0(const Instruction);
|
||||
void mfc0(const Instruction);
|
||||
void dmfc0(const Instruction) const;
|
||||
void eret();
|
||||
void tlbr();
|
||||
void tlbw(int);
|
||||
void tlbp();
|
||||
|
||||
void tlbr();
|
||||
void tlbw(int);
|
||||
void tlbp();
|
||||
|
||||
template <typename T, bool User>
|
||||
bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
template <typename T, bool User>
|
||||
bool MapVirtualAddress(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
+219
-219
@@ -7,241 +7,241 @@ namespace n64 {
|
||||
struct Cop1;
|
||||
|
||||
union FCR31 {
|
||||
FCR31() = default;
|
||||
struct {
|
||||
unsigned rounding_mode : 2;
|
||||
FCR31() = default;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} flag;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} enable;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
unsigned unimplemented_operation : 1;
|
||||
} cause;
|
||||
unsigned : 5;
|
||||
unsigned compare : 1;
|
||||
unsigned fs : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
unsigned rounding_mode : 2;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} flag;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} enable;
|
||||
struct {
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
unsigned unimplemented_operation : 1;
|
||||
} cause;
|
||||
unsigned : 5;
|
||||
unsigned compare : 1;
|
||||
unsigned fs : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
[[nodiscard]] u32 read() const {
|
||||
u32 ret = 0;
|
||||
ret |= (u32(fs) << 24);
|
||||
ret |= (u32(compare) << 23);
|
||||
ret |= (u32(cause.unimplemented_operation) << 17);
|
||||
ret |= (u32(cause.invalid_operation) << 16);
|
||||
ret |= (u32(cause.division_by_zero) << 15);
|
||||
ret |= (u32(cause.overflow) << 14);
|
||||
ret |= (u32(cause.underflow) << 13);
|
||||
ret |= (u32(cause.inexact_operation) << 12);
|
||||
ret |= (u32(enable.invalid_operation) << 11);
|
||||
ret |= (u32(enable.division_by_zero) << 10);
|
||||
ret |= (u32(enable.overflow) << 9);
|
||||
ret |= (u32(enable.underflow) << 8);
|
||||
ret |= (u32(enable.inexact_operation) << 7);
|
||||
ret |= (u32(flag.invalid_operation) << 6);
|
||||
ret |= (u32(flag.division_by_zero) << 5);
|
||||
ret |= (u32(flag.overflow) << 4);
|
||||
ret |= (u32(flag.underflow) << 3);
|
||||
ret |= (u32(flag.inexact_operation) << 2);
|
||||
ret |= (u32(rounding_mode) & 3);
|
||||
[[nodiscard]] u32 read() const {
|
||||
u32 ret = 0;
|
||||
ret |= (u32(fs) << 24);
|
||||
ret |= (u32(compare) << 23);
|
||||
ret |= (u32(cause.unimplemented_operation) << 17);
|
||||
ret |= (u32(cause.invalid_operation) << 16);
|
||||
ret |= (u32(cause.division_by_zero) << 15);
|
||||
ret |= (u32(cause.overflow) << 14);
|
||||
ret |= (u32(cause.underflow) << 13);
|
||||
ret |= (u32(cause.inexact_operation) << 12);
|
||||
ret |= (u32(enable.invalid_operation) << 11);
|
||||
ret |= (u32(enable.division_by_zero) << 10);
|
||||
ret |= (u32(enable.overflow) << 9);
|
||||
ret |= (u32(enable.underflow) << 8);
|
||||
ret |= (u32(enable.inexact_operation) << 7);
|
||||
ret |= (u32(flag.invalid_operation) << 6);
|
||||
ret |= (u32(flag.division_by_zero) << 5);
|
||||
ret |= (u32(flag.overflow) << 4);
|
||||
ret |= (u32(flag.underflow) << 3);
|
||||
ret |= (u32(flag.inexact_operation) << 2);
|
||||
ret |= (u32(rounding_mode) & 3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void write(u32 val) {
|
||||
fs = val >> 24;
|
||||
compare = val >> 23;
|
||||
cause.unimplemented_operation = val >> 17;
|
||||
cause.invalid_operation = val >> 16;
|
||||
cause.division_by_zero = val >> 15;
|
||||
cause.overflow = val >> 14;
|
||||
cause.underflow = val >> 13;
|
||||
cause.inexact_operation = val >> 12;
|
||||
enable.invalid_operation = val >> 11;
|
||||
enable.division_by_zero = val >> 10;
|
||||
enable.overflow = val >> 9;
|
||||
enable.underflow = val >> 8;
|
||||
enable.inexact_operation = val >> 7;
|
||||
flag.invalid_operation = val >> 6;
|
||||
flag.division_by_zero = val >> 5;
|
||||
flag.overflow = val >> 4;
|
||||
flag.underflow = val >> 3;
|
||||
flag.inexact_operation = val >> 2;
|
||||
rounding_mode = val & 3;
|
||||
}
|
||||
void write(u32 val) {
|
||||
fs = val >> 24;
|
||||
compare = val >> 23;
|
||||
cause.unimplemented_operation = val >> 17;
|
||||
cause.invalid_operation = val >> 16;
|
||||
cause.division_by_zero = val >> 15;
|
||||
cause.overflow = val >> 14;
|
||||
cause.underflow = val >> 13;
|
||||
cause.inexact_operation = val >> 12;
|
||||
enable.invalid_operation = val >> 11;
|
||||
enable.division_by_zero = val >> 10;
|
||||
enable.overflow = val >> 9;
|
||||
enable.underflow = val >> 8;
|
||||
enable.inexact_operation = val >> 7;
|
||||
flag.invalid_operation = val >> 6;
|
||||
flag.division_by_zero = val >> 5;
|
||||
flag.overflow = val >> 4;
|
||||
flag.underflow = val >> 3;
|
||||
flag.inexact_operation = val >> 2;
|
||||
rounding_mode = val & 3;
|
||||
}
|
||||
};
|
||||
|
||||
union FloatingPointReg {
|
||||
struct {
|
||||
s32 int32;
|
||||
s32 int32h;
|
||||
};
|
||||
struct {
|
||||
u32 uint32;
|
||||
u32 uint32h;
|
||||
};
|
||||
struct {
|
||||
s64 int64;
|
||||
};
|
||||
struct {
|
||||
u64 uint64;
|
||||
};
|
||||
struct {
|
||||
float float32;
|
||||
float float32h;
|
||||
};
|
||||
struct {
|
||||
double float64;
|
||||
};
|
||||
struct {
|
||||
s32 int32;
|
||||
s32 int32h;
|
||||
};
|
||||
struct {
|
||||
u32 uint32;
|
||||
u32 uint32h;
|
||||
};
|
||||
struct {
|
||||
s64 int64;
|
||||
};
|
||||
struct {
|
||||
u64 uint64;
|
||||
};
|
||||
struct {
|
||||
float float32;
|
||||
float float32h;
|
||||
};
|
||||
struct {
|
||||
double float64;
|
||||
};
|
||||
};
|
||||
|
||||
struct Cop1 {
|
||||
explicit Cop1();
|
||||
bool fgrIsConstant[32]{};
|
||||
u32 fcr0{};
|
||||
FCR31 fcr31{};
|
||||
FloatingPointReg fgr[32]{};
|
||||
explicit Cop1();
|
||||
bool fgrIsConstant[32]{};
|
||||
u32 fcr0{};
|
||||
FCR31 fcr31{};
|
||||
FloatingPointReg fgr[32]{};
|
||||
|
||||
void Reset();
|
||||
void decode(const Instruction);
|
||||
friend struct Interpreter;
|
||||
friend struct JIT;
|
||||
void Reset();
|
||||
void decode(const Instruction);
|
||||
friend struct Interpreter;
|
||||
friend struct JIT;
|
||||
|
||||
template <bool preserveCause = false>
|
||||
bool CheckFPUUsable();
|
||||
template <typename T>
|
||||
bool CheckResult(T &);
|
||||
template <typename T>
|
||||
bool CheckArg(T);
|
||||
template <typename T>
|
||||
bool CheckArgs(T, T);
|
||||
template <typename T>
|
||||
bool isqnan(T);
|
||||
template <bool preserveCause = false>
|
||||
bool CheckFPUUsable();
|
||||
template <typename T>
|
||||
bool CheckResult(T &);
|
||||
template <typename T>
|
||||
bool CheckArg(T);
|
||||
template <typename T>
|
||||
bool CheckArgs(T, T);
|
||||
template <typename T>
|
||||
bool isqnan(T);
|
||||
|
||||
template <typename T, bool quiet, bool cf>
|
||||
bool XORDERED(T fs, T ft);
|
||||
template <typename T, bool quiet, bool cf>
|
||||
bool XORDERED(T fs, T ft);
|
||||
|
||||
template <typename T>
|
||||
bool CheckCVTArg(float f);
|
||||
template <typename T>
|
||||
bool CheckCVTArg(double f);
|
||||
template <typename T>
|
||||
bool CheckCVTArg(float f);
|
||||
template <typename T>
|
||||
bool CheckCVTArg(double f);
|
||||
|
||||
template <bool cvt = false>
|
||||
bool TestExceptions();
|
||||
void SetCauseUnimplemented();
|
||||
bool SetCauseUnderflow();
|
||||
bool SetCauseInexact();
|
||||
bool SetCauseDivisionByZero();
|
||||
bool SetCauseOverflow();
|
||||
bool SetCauseInvalid();
|
||||
template <bool cvt = false>
|
||||
bool TestExceptions();
|
||||
void SetCauseUnimplemented();
|
||||
bool SetCauseUnderflow();
|
||||
bool SetCauseInexact();
|
||||
bool SetCauseDivisionByZero();
|
||||
bool SetCauseOverflow();
|
||||
bool SetCauseInvalid();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
auto FGR_T(const Cop0Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_S(const Cop0Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_D(const Cop0Status &, u32) -> T &;
|
||||
void absd(const Instruction instr);
|
||||
void abss(const Instruction instr);
|
||||
void adds(const Instruction instr);
|
||||
void addd(const Instruction instr);
|
||||
void subs(const Instruction instr);
|
||||
void subd(const Instruction instr);
|
||||
void ceills(const Instruction instr);
|
||||
void ceilws(const Instruction instr);
|
||||
void ceilld(const Instruction instr);
|
||||
void ceilwd(const Instruction instr);
|
||||
void cfc1(const Instruction instr);
|
||||
void ctc1(const Instruction instr);
|
||||
void unimplemented();
|
||||
void roundls(const Instruction instr);
|
||||
void roundld(const Instruction instr);
|
||||
void roundws(const Instruction instr);
|
||||
void roundwd(const Instruction instr);
|
||||
void floorls(const Instruction instr);
|
||||
void floorld(const Instruction instr);
|
||||
void floorws(const Instruction instr);
|
||||
void floorwd(const Instruction instr);
|
||||
void cvtls(const Instruction instr);
|
||||
void cvtws(const Instruction instr);
|
||||
void cvtds(const Instruction instr);
|
||||
void cvtsw(const Instruction instr);
|
||||
void cvtdw(const Instruction instr);
|
||||
void cvtsd(const Instruction instr);
|
||||
void cvtwd(const Instruction instr);
|
||||
void cvtld(const Instruction instr);
|
||||
void cvtdl(const Instruction instr);
|
||||
void cvtsl(const Instruction instr);
|
||||
template <typename T>
|
||||
void cf(const Instruction instr);
|
||||
template <typename T>
|
||||
void cun(const Instruction instr);
|
||||
template <typename T>
|
||||
void ceq(const Instruction instr);
|
||||
template <typename T>
|
||||
void cueq(const Instruction instr);
|
||||
template <typename T>
|
||||
void colt(const Instruction instr);
|
||||
template <typename T>
|
||||
void cult(const Instruction instr);
|
||||
template <typename T>
|
||||
void cole(const Instruction instr);
|
||||
template <typename T>
|
||||
void cule(const Instruction instr);
|
||||
template <typename T>
|
||||
void csf(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngle(const Instruction instr);
|
||||
template <typename T>
|
||||
void cseq(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngl(const Instruction instr);
|
||||
template <typename T>
|
||||
void clt(const Instruction instr);
|
||||
template <typename T>
|
||||
void cnge(const Instruction instr);
|
||||
template <typename T>
|
||||
void cle(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngt(const Instruction instr);
|
||||
void divs(const Instruction instr);
|
||||
void divd(const Instruction instr);
|
||||
void muls(const Instruction instr);
|
||||
void muld(const Instruction instr);
|
||||
void movs(const Instruction instr);
|
||||
void movd(const Instruction instr);
|
||||
void negs(const Instruction instr);
|
||||
void negd(const Instruction instr);
|
||||
void sqrts(const Instruction instr);
|
||||
void sqrtd(const Instruction instr);
|
||||
void lwc1(const Instruction instr);
|
||||
void swc1(const Instruction instr);
|
||||
void ldc1(const Instruction instr);
|
||||
void sdc1(const Instruction instr);
|
||||
private:
|
||||
template <typename T>
|
||||
auto FGR_T(const Cop0::Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_S(const Cop0::Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_D(const Cop0::Status &, u32) -> T &;
|
||||
void absd(const Instruction instr);
|
||||
void abss(const Instruction instr);
|
||||
void adds(const Instruction instr);
|
||||
void addd(const Instruction instr);
|
||||
void subs(const Instruction instr);
|
||||
void subd(const Instruction instr);
|
||||
void ceills(const Instruction instr);
|
||||
void ceilws(const Instruction instr);
|
||||
void ceilld(const Instruction instr);
|
||||
void ceilwd(const Instruction instr);
|
||||
void cfc1(const Instruction instr);
|
||||
void ctc1(const Instruction instr);
|
||||
void unimplemented();
|
||||
void roundls(const Instruction instr);
|
||||
void roundld(const Instruction instr);
|
||||
void roundws(const Instruction instr);
|
||||
void roundwd(const Instruction instr);
|
||||
void floorls(const Instruction instr);
|
||||
void floorld(const Instruction instr);
|
||||
void floorws(const Instruction instr);
|
||||
void floorwd(const Instruction instr);
|
||||
void cvtls(const Instruction instr);
|
||||
void cvtws(const Instruction instr);
|
||||
void cvtds(const Instruction instr);
|
||||
void cvtsw(const Instruction instr);
|
||||
void cvtdw(const Instruction instr);
|
||||
void cvtsd(const Instruction instr);
|
||||
void cvtwd(const Instruction instr);
|
||||
void cvtld(const Instruction instr);
|
||||
void cvtdl(const Instruction instr);
|
||||
void cvtsl(const Instruction instr);
|
||||
template <typename T>
|
||||
void cf(const Instruction instr);
|
||||
template <typename T>
|
||||
void cun(const Instruction instr);
|
||||
template <typename T>
|
||||
void ceq(const Instruction instr);
|
||||
template <typename T>
|
||||
void cueq(const Instruction instr);
|
||||
template <typename T>
|
||||
void colt(const Instruction instr);
|
||||
template <typename T>
|
||||
void cult(const Instruction instr);
|
||||
template <typename T>
|
||||
void cole(const Instruction instr);
|
||||
template <typename T>
|
||||
void cule(const Instruction instr);
|
||||
template <typename T>
|
||||
void csf(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngle(const Instruction instr);
|
||||
template <typename T>
|
||||
void cseq(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngl(const Instruction instr);
|
||||
template <typename T>
|
||||
void clt(const Instruction instr);
|
||||
template <typename T>
|
||||
void cnge(const Instruction instr);
|
||||
template <typename T>
|
||||
void cle(const Instruction instr);
|
||||
template <typename T>
|
||||
void cngt(const Instruction instr);
|
||||
void divs(const Instruction instr);
|
||||
void divd(const Instruction instr);
|
||||
void muls(const Instruction instr);
|
||||
void muld(const Instruction instr);
|
||||
void movs(const Instruction instr);
|
||||
void movd(const Instruction instr);
|
||||
void negs(const Instruction instr);
|
||||
void negd(const Instruction instr);
|
||||
void sqrts(const Instruction instr);
|
||||
void sqrtd(const Instruction instr);
|
||||
void lwc1(const Instruction instr);
|
||||
void swc1(const Instruction instr);
|
||||
void ldc1(const Instruction instr);
|
||||
void sdc1(const Instruction instr);
|
||||
|
||||
void mfc1(const Instruction instr);
|
||||
void dmfc1(const Instruction instr);
|
||||
void mtc1(const Instruction instr);
|
||||
void dmtc1(const Instruction instr);
|
||||
void truncws(const Instruction instr);
|
||||
void truncwd(const Instruction instr);
|
||||
void truncls(const Instruction instr);
|
||||
void truncld(const Instruction instr);
|
||||
void mfc1(const Instruction instr);
|
||||
void dmfc1(const Instruction instr);
|
||||
void mtc1(const Instruction instr);
|
||||
void dmtc1(const Instruction instr);
|
||||
void truncws(const Instruction instr);
|
||||
void truncwd(const Instruction instr);
|
||||
void truncls(const Instruction instr);
|
||||
void truncld(const Instruction instr);
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,361 +3,352 @@
|
||||
#include <core/JIT.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
Registers::Registers(JIT &jit) : jit(jit) { Reset(); }
|
||||
#else
|
||||
Registers::Registers() { Reset(); }
|
||||
#endif
|
||||
|
||||
void Registers::Reset() {
|
||||
hi = 0;
|
||||
lo = 0;
|
||||
delaySlot = false;
|
||||
prevDelaySlot = false;
|
||||
gpr.fill(0);
|
||||
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
|
||||
hi = 0;
|
||||
lo = 0;
|
||||
delaySlot = false;
|
||||
prevDelaySlot = false;
|
||||
gpr.fill(0);
|
||||
regIsConstant = 1; // first bit is true indicating $zero is constant which yes it is always
|
||||
|
||||
cop0.Reset();
|
||||
cop1.Reset();
|
||||
cop0.Reset();
|
||||
cop1.Reset();
|
||||
|
||||
steps = 0;
|
||||
extraCycles = 0;
|
||||
steps = 0;
|
||||
extraCycles = 0;
|
||||
}
|
||||
|
||||
void Registers::SetPC64(s64 val) {
|
||||
oldPC = pc;
|
||||
pc = val;
|
||||
nextPC = pc + 4;
|
||||
oldPC = pc;
|
||||
pc = val;
|
||||
nextPC = pc + 4;
|
||||
}
|
||||
|
||||
void Registers::SetPC32(s32 val) {
|
||||
oldPC = pc;
|
||||
pc = s64(val);
|
||||
nextPC = pc + 4;
|
||||
oldPC = pc;
|
||||
pc = s64(val);
|
||||
nextPC = pc + 4;
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 Registers::Read<u64>(size_t idx) {
|
||||
return gpr[idx];
|
||||
return gpr[idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
s64 Registers::Read<s64>(const size_t idx) {
|
||||
return static_cast<s64>(Read<u64>(idx));
|
||||
return static_cast<s64>(Read<u64>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
u32 Registers::Read<u32>(size_t idx) {
|
||||
return gpr[idx];
|
||||
return gpr[idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
s32 Registers::Read<s32>(size_t idx) {
|
||||
return static_cast<s32>(Read<u32>(idx));
|
||||
return static_cast<s32>(Read<u32>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
u16 Registers::Read<u16>(size_t idx) {
|
||||
return gpr[idx];
|
||||
return gpr[idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
s16 Registers::Read<s16>(size_t idx) {
|
||||
return static_cast<s16>(Read<u16>(idx));
|
||||
return static_cast<s16>(Read<u16>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
u8 Registers::Read<u8>(size_t idx) {
|
||||
return gpr[idx];
|
||||
return gpr[idx];
|
||||
}
|
||||
|
||||
template <>
|
||||
s8 Registers::Read<s8>(size_t idx) {
|
||||
return static_cast<s8>(Read<u8>(idx));
|
||||
return static_cast<s8>(Read<u8>(idx));
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
template <>
|
||||
void Registers::Read<u64>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt64(), Read<u64>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt64(), Read<u64>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s64>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt64(), Read<s64>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt64(), Read<s64>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt64(), jit->GPR<u64>(idx));
|
||||
jit.code.mov(reg.cvt64(), jit.GPR<u64>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u32>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt32(), Read<u32>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt32(), Read<u32>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt32(), jit->GPR<u32>(idx));
|
||||
jit.code.mov(reg.cvt32(), jit.GPR<u32>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s32>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt32(), Read<s32>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt32(), Read<s32>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt32(), jit->GPR<s32>(idx));
|
||||
jit.code.mov(reg.cvt32(), jit.GPR<s32>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u16>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt16(), Read<u16>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt16(), Read<u16>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s16>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt16(), Read<s16>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt16(), Read<s16>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt16(), jit->GPR<u16>(idx));
|
||||
jit.code.mov(reg.cvt16(), jit.GPR<u16>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<u8>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt8(), Read<u8>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt8(), Read<u8>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt8(), jit->GPR<u8>(idx));
|
||||
jit.code.mov(reg.cvt8(), jit.GPR<u8>(idx));
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Read<s8>(size_t idx, Xbyak::Reg reg) {
|
||||
if(IsRegConstant(idx)) {
|
||||
jit->code.mov(reg.cvt8(), Read<s8>(idx));
|
||||
return;
|
||||
}
|
||||
if (IsRegConstant(idx)) {
|
||||
jit.code.mov(reg.cvt8(), Read<s8>(idx));
|
||||
return;
|
||||
}
|
||||
|
||||
jit->code.mov(reg.cvt8(), jit->GPR<s8>(idx));
|
||||
jit.code.mov(reg.cvt8(), jit.GPR<s8>(idx));
|
||||
}
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void Registers::Write<bool>(size_t idx, bool v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
|
||||
gpr[idx] = v;
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u64>(size_t idx, u64 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<s64>(size_t idx, s64 v) {
|
||||
Write<u64>(idx, v);
|
||||
Write<u64>(idx, v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u32>(size_t idx, u32 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void Registers::Write<s32>(size_t idx, s32 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u16>(size_t idx, u16 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void Registers::Write<s16>(size_t idx, s16 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u8>(size_t idx, u8 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
|
||||
template <>
|
||||
void Registers::Write<s8>(size_t idx, s8 v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (jit) [[unlikely]]
|
||||
regIsConstant |= (1 << idx);
|
||||
gpr[idx] = v;
|
||||
|
||||
gpr[idx] = v;
|
||||
}
|
||||
|
||||
#ifndef __aarch64__
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
template <>
|
||||
void Registers::Write<bool>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<s8>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v);
|
||||
jit.code.movsx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u8>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt8());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt8());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<s16>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsx(v.cvt64(), v.cvt16());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movsx(v.cvt64(), v.cvt16());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u16>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt16());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt16());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<s32>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movsxd(v.cvt64(), v.cvt32());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movsxd(v.cvt64(), v.cvt32());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u32>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.movzx(v.cvt64(), v.cvt32());
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.movzx(v.cvt64(), v.cvt32());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<u64>(size_t idx, Xbyak::Reg v) {
|
||||
if (idx == 0)
|
||||
return;
|
||||
if (idx == 0)
|
||||
return;
|
||||
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
if (!jit)
|
||||
panic("Did you try to call Registers::Write(size_t, *Xbyak::Reg*) from the interpreter?");
|
||||
|
||||
regIsConstant &= ~(1 << idx);
|
||||
regIsConstant &= ~(1 << idx);
|
||||
|
||||
jit->code.mov(jit->GPR<u64>(idx), v.cvt64());
|
||||
jit.code.mov(jit.GPR<u64>(idx), v.cvt64());
|
||||
}
|
||||
|
||||
template <>
|
||||
void Registers::Write<s64>(size_t idx, Xbyak::Reg v) {
|
||||
Write<u64>(idx, v);
|
||||
Write<u64>(idx, v);
|
||||
}
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
@@ -4,77 +4,72 @@
|
||||
#include <backend/core/registers/Cop1.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
struct JIT;
|
||||
struct Registers {
|
||||
Registers();
|
||||
void Reset();
|
||||
void SetPC64(s64);
|
||||
void SetPC32(s32);
|
||||
void SetJIT(JIT* jit) { this->jit = jit; }
|
||||
JIT &jit;
|
||||
Registers(JIT &jit);
|
||||
#else
|
||||
struct Registers {
|
||||
Registers();
|
||||
void Reset();
|
||||
void SetPC64(s64);
|
||||
void SetPC32(s32);
|
||||
|
||||
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||
if (index == 0)
|
||||
return true;
|
||||
return regIsConstant & (1 << index);
|
||||
}
|
||||
[[nodiscard]] bool IsRegConstant(const u32 index) const {
|
||||
if (index == 0)
|
||||
return true;
|
||||
return regIsConstant & (1 << index);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const {
|
||||
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||
}
|
||||
[[nodiscard]] bool IsRegConstant(const u32 index1, const u32 index2) const {
|
||||
return IsRegConstant(index1) && IsRegConstant(index2);
|
||||
}
|
||||
|
||||
bool GetLOConstant() {
|
||||
return regIsConstant & (1ull << 32);
|
||||
}
|
||||
bool GetLOConstant() { return regIsConstant & (1ull << 32); }
|
||||
|
||||
bool GetHIConstant() {
|
||||
return regIsConstant & (1ull << 33);
|
||||
}
|
||||
bool GetHIConstant() { return regIsConstant & (1ull << 33); }
|
||||
|
||||
void SetLOConstant() {
|
||||
regIsConstant |= (1ull << 32);
|
||||
}
|
||||
void SetLOConstant() { regIsConstant |= (1ull << 32); }
|
||||
|
||||
void SetHIConstant() {
|
||||
regIsConstant |= (1ull << 33);
|
||||
}
|
||||
void SetHIConstant() { regIsConstant |= (1ull << 33); }
|
||||
|
||||
void UnsetLOConstant() {
|
||||
regIsConstant &= ~(1ull << 32);
|
||||
}
|
||||
void UnsetLOConstant() { regIsConstant &= ~(1ull << 32); }
|
||||
|
||||
void UnsetHIConstant() {
|
||||
regIsConstant &= ~(1ull << 33);
|
||||
}
|
||||
void UnsetHIConstant() { regIsConstant &= ~(1ull << 33); }
|
||||
|
||||
JIT *jit = nullptr;
|
||||
uint64_t regIsConstant = 0;
|
||||
|
||||
uint64_t regIsConstant = 0;
|
||||
bool prevDelaySlot{}, delaySlot{};
|
||||
u32 steps = 0;
|
||||
u32 extraCycles = 0;
|
||||
s64 oldPC{}, pc{}, nextPC{};
|
||||
s64 hi{}, lo{};
|
||||
Cop0 cop0;
|
||||
Cop1 cop1;
|
||||
|
||||
bool prevDelaySlot{}, delaySlot{};
|
||||
u32 steps = 0;
|
||||
u32 extraCycles = 0;
|
||||
s64 oldPC{}, pc{}, nextPC{};
|
||||
s64 hi{}, lo{};
|
||||
Cop0 cop0;
|
||||
Cop1 cop1;
|
||||
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
||||
|
||||
void CpuStall(u32 cycles) { extraCycles += cycles; }
|
||||
u32 PopStalledCycles() {
|
||||
u32 ret = extraCycles;
|
||||
extraCycles = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 PopStalledCycles() {
|
||||
u32 ret = extraCycles;
|
||||
extraCycles = 0;
|
||||
return ret;
|
||||
}
|
||||
template <typename T>
|
||||
T Read(size_t);
|
||||
template <typename T>
|
||||
void Read(size_t, Xbyak::Reg);
|
||||
template <typename T>
|
||||
void Write(size_t, T);
|
||||
template <typename T>
|
||||
void Write(size_t, Xbyak::Reg);
|
||||
|
||||
template <typename T>
|
||||
T Read(size_t);
|
||||
template <typename T>
|
||||
void Read(size_t, Xbyak::Reg);
|
||||
template <typename T>
|
||||
void Write(size_t, T);
|
||||
template <typename T>
|
||||
void Write(size_t, Xbyak::Reg);
|
||||
std::array<s64, 32> gpr{};
|
||||
|
||||
std::array<s64, 32> gpr{};
|
||||
static inline const char *regNames[] = {"zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2",
|
||||
"t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5",
|
||||
"s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra"};
|
||||
};
|
||||
#endif
|
||||
} // namespace n64
|
||||
|
||||
+207
-213
@@ -1,249 +1,243 @@
|
||||
#include <Debugger.hpp>
|
||||
#include <imgui.h>
|
||||
#include <Registers.hpp>
|
||||
|
||||
char const* regNames[] = {
|
||||
"zero", "at", "v0", "v1",
|
||||
"a0", "a1", "a2", "a3",
|
||||
"t0", "t1", "t2", "t3",
|
||||
"t4", "t5", "t6", "t7",
|
||||
"s0", "s1", "s2", "s3",
|
||||
"s4", "s5", "s6", "s7",
|
||||
"t8", "t9", "k0", "k1",
|
||||
"gp", "sp", "s8", "ra",
|
||||
};
|
||||
|
||||
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult&) {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
bool isBroken = core.breakpoints.contains(addr);
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
|
||||
if(ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
|
||||
core.ToggleBreakpoint(addr);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
void BreakpointFunc(s64 addr, Disassembler::DisassemblyResult &) {
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
bool isBroken = core.breakpoints.contains(addr);
|
||||
ImGui::PushStyleColor(ImGuiCol_CheckMark, 0xff0000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, 0);
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, 0x800000ff);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.5f);
|
||||
if (ImGui::Checkbox(std::format("##toggleBreakpoint{}", addr).c_str(), &isBroken)) {
|
||||
core.ToggleBreakpoint(addr);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||
}
|
||||
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult& disasm) {
|
||||
if(!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
for(int i = 0; i < 3; i++) {
|
||||
if(disasm.ops[i].str.empty())
|
||||
continue;
|
||||
|
||||
if(i >= 2) {
|
||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
continue;
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult &disasm) {
|
||||
if (!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "????????????????");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string op_str = disasm.ops[i].str;
|
||||
if(!disasm.ops[i+1].str.empty())
|
||||
op_str += ", ";
|
||||
|
||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
||||
ImGui::TextColored(ImColor(0xffeaefb6), "%s", std::format("{:016X}:", disasm.address).c_str());
|
||||
}
|
||||
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult &disasm) {
|
||||
if (!disasm.success) {
|
||||
ImGui::TextColored(ImColor(0xffcbf1ae), "Disassembly unsuccessful...");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xffcbf1ae), "%s", std::format("{} ", disasm.mnemonic).c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (disasm.ops[i].str.empty())
|
||||
continue;
|
||||
|
||||
if (i >= 2) {
|
||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", disasm.ops[i].str.c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string op_str = disasm.ops[i].str;
|
||||
if (!disasm.ops[i + 1].str.empty())
|
||||
op_str += ", ";
|
||||
|
||||
ImGui::TextColored(ImColor(disasm.ops[i].color), "%s", op_str.c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::RegisterView() {
|
||||
if(!ImGui::BeginTabItem("Registers"))
|
||||
return;
|
||||
|
||||
if(!ImGui::BeginTable("##regs", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody))
|
||||
return;
|
||||
if (!ImGui::BeginTabItem("Registers"))
|
||||
return;
|
||||
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
if (!ImGui::BeginTable("##regs", 4,
|
||||
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
|
||||
ImGuiTableFlags_ContextMenuInBody))
|
||||
return;
|
||||
|
||||
auto renderMemoryTable = [&](u64 vaddr) {
|
||||
if(!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
||||
return;
|
||||
|
||||
if(!ImGui::BeginTooltip())
|
||||
return;
|
||||
|
||||
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
||||
if(!ImGui::BeginTable("##memoryContents", 16))
|
||||
return;
|
||||
|
||||
for(u32 col = 0; col < 16; col++)
|
||||
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for(u32 row = 0; row < 16; row++) {
|
||||
ImGui::TableNextRow();
|
||||
for(u32 col = 0; col < 16; col+=4) {
|
||||
u32 paddr;
|
||||
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
|
||||
continue;
|
||||
auto renderMemoryTable = [&](u64 vaddr) {
|
||||
if (!ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_ForTooltip))
|
||||
return;
|
||||
|
||||
const u32 val = n64::Core::GetMem().Read<u32>(paddr);
|
||||
if (!ImGui::BeginTooltip())
|
||||
return;
|
||||
|
||||
ImGui::TableSetColumnIndex(col+0);
|
||||
ImGui::Text("%02X", (val >> 24) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+1);
|
||||
ImGui::Text("%02X", (val >> 16) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+2);
|
||||
ImGui::Text("%02X", (val >> 8) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col+3);
|
||||
ImGui::Text("%02X", (val >> 0) & 0xff);
|
||||
}
|
||||
ImGui::Text("%s", std::format("Memory contents @ 0x{:016X}", vaddr).c_str());
|
||||
if (!ImGui::BeginTable("##memoryContents", 16))
|
||||
return;
|
||||
|
||||
for (u32 col = 0; col < 16; col++)
|
||||
ImGui::TableSetupColumn(std::format("##hexCol{}", col).c_str());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (u32 row = 0; row < 16; row++) {
|
||||
ImGui::TableNextRow();
|
||||
for (u32 col = 0; col < 16; col += 4) {
|
||||
u32 paddr;
|
||||
if (!n64::Core::GetRegs().cop0.MapVAddr(n64::Cop0::LOAD, vaddr + row * 0x10 + col, paddr))
|
||||
continue;
|
||||
|
||||
const u32 val = n64::Core::GetMem().Read<u32>(paddr);
|
||||
|
||||
ImGui::TableSetColumnIndex(col + 0);
|
||||
ImGui::Text("%02X", (val >> 24) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col + 1);
|
||||
ImGui::Text("%02X", (val >> 16) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col + 2);
|
||||
ImGui::Text("%02X", (val >> 8) & 0xff);
|
||||
ImGui::TableSetColumnIndex(col + 3);
|
||||
ImGui::Text("%02X", (val >> 0) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::EndTooltip();
|
||||
};
|
||||
|
||||
n64::Registers ®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::EndTooltip();
|
||||
};
|
||||
|
||||
n64::Registers& regs = n64::Core::GetRegs();
|
||||
|
||||
for(int i = 0; i < 32; i+=2) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("%s", regNames[i]);
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
auto value = regs.Read<u64>(i);
|
||||
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
||||
renderMemoryTable(value);
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("%s", regNames[i+1]);
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
value = regs.Read<u64>(i+1);
|
||||
ImGui::Text("%s", std::format("{:016X}", value).c_str());
|
||||
renderMemoryTable(value);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::EndTabItem();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
bool Debugger::render() {
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
const n64::Registers& regs = n64::Core::GetRegs();
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
const n64::Registers ®s = n64::Core::GetRegs();
|
||||
|
||||
if(!enabled)
|
||||
return false;
|
||||
if (!enabled)
|
||||
return false;
|
||||
|
||||
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
||||
constexpr int step = 4;
|
||||
constexpr int stepFast = 256;
|
||||
|
||||
if(!ImGui::Begin("Debugger", &enabled)) {
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
static s64 startAddr = 0xFFFF'FFFF'8000'0000;
|
||||
constexpr int step = 4;
|
||||
constexpr int stepFast = 256;
|
||||
|
||||
ImGui::BeginDisabled(followPC);
|
||||
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text("Follow program counter:");
|
||||
ImGui::SameLine(0,0);
|
||||
|
||||
ImGui::Checkbox("##followPC", &followPC);
|
||||
ImGui::SameLine(0,0);
|
||||
|
||||
ImGui::Text("Add a breakpoint");
|
||||
ImGui::SameLine(0,0);
|
||||
|
||||
if(followPC)
|
||||
startAddr = regs.pc - 256; // TODO: arbitrary???
|
||||
|
||||
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
||||
core.ToggleBreakpoint(startAddr);
|
||||
}
|
||||
|
||||
if(!ImGui::BeginTabBar("##debuggerTabs")) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
RegisterView();
|
||||
|
||||
if(!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter |
|
||||
ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
||||
|
||||
if(!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto &[name, _] : columns)
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for(auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
||||
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
||||
const auto addrIsCurrent = addr == regs.nextPC;
|
||||
const auto addrIsBreakpoint = core.breakpoints.contains(addr);
|
||||
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
|
||||
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
|
||||
if(addrIsCurrent) {
|
||||
colorChoice = 0x80e27fbc;
|
||||
colorChoiceAlt = 0x80e27fbc;
|
||||
if (!ImGui::Begin("Debugger", &enabled)) {
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(addrIsBreakpoint) {
|
||||
colorChoice = 0x800000ff;
|
||||
colorChoiceAlt = 0x800000ff;
|
||||
ImGui::BeginDisabled(followPC);
|
||||
ImGui::InputScalar("Address", ImGuiDataType_S64, &startAddr, &step, &stepFast, "%016lX",
|
||||
ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Text("Follow program counter:");
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::Checkbox("##followPC", &followPC);
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::Text("Add a breakpoint");
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
if (followPC)
|
||||
startAddr = regs.pc - 256; // TODO: arbitrary???
|
||||
|
||||
if (ImGui::Button(core.breakpoints.contains(startAddr) ? "-" : "+")) {
|
||||
core.ToggleBreakpoint(startAddr);
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value);
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
for(int i = 0; auto &[_, func] : columns) {
|
||||
ImGui::TableSetColumnIndex(i++);
|
||||
func(addr, disasm);
|
||||
if (!ImGui::BeginTabBar("##debuggerTabs")) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
RegisterView();
|
||||
|
||||
ImGui::EndTable();
|
||||
if (!ImGui::BeginTabItem("MIPS R4300i code view")) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
ImGui::EndTabBar();
|
||||
constexpr auto disasmTableFlags = ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody;
|
||||
|
||||
ImGui::End();
|
||||
return true;
|
||||
if (!ImGui::BeginTable("Disassembly", columns.size(), disasmTableFlags)) {
|
||||
ImGui::EndTabBar();
|
||||
ImGui::End();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto &[name, _] : columns)
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (auto addr = startAddr; addr < startAddr + MAX_LINES_OF_DISASM * sizeof(u32); addr += sizeof(u32)) {
|
||||
auto disasm = Disassembler::GetInstance().Disassemble(addr);
|
||||
const auto addrIsCurrent = addr == regs.nextPC;
|
||||
const auto addrIsBreakpoint = core.breakpoints.contains(addr);
|
||||
ImColor colorChoice = ImGui::GetStyle().Colors[ImGuiCol_TableRowBg];
|
||||
ImColor colorChoiceAlt = ImGui::GetStyle().Colors[ImGuiCol_TableRowBgAlt];
|
||||
if (addrIsCurrent) {
|
||||
colorChoice = 0x80e27fbc;
|
||||
colorChoiceAlt = 0x80e27fbc;
|
||||
}
|
||||
|
||||
if (addrIsBreakpoint) {
|
||||
colorChoice = 0x800000ff;
|
||||
colorChoiceAlt = 0x800000ff;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBg, colorChoice.Value);
|
||||
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, colorChoiceAlt.Value);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
for (int i = 0; auto &[_, func] : columns) {
|
||||
ImGui::TableSetColumnIndex(i++);
|
||||
func(addr, disasm);
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
ImGui::EndTabBar();
|
||||
|
||||
ImGui::End();
|
||||
return true;
|
||||
}
|
||||
|
||||
+26
-21
@@ -1,28 +1,33 @@
|
||||
#pragma once
|
||||
#include <backend/Core.hpp>
|
||||
#include <Disassembler.hpp>
|
||||
|
||||
void BreakpointFunc(s64, Disassembler::DisassemblyResult&);
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult&);
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult&);
|
||||
void BreakpointFunc(s64, Disassembler::DisassemblyResult &);
|
||||
void AddressFunc(s64, Disassembler::DisassemblyResult &);
|
||||
void InstructionFunc(s64, Disassembler::DisassemblyResult &);
|
||||
|
||||
class Debugger final {
|
||||
bool enabled = false;
|
||||
static constexpr auto MAX_LINES_OF_DISASM = 150;
|
||||
bool enabled = false;
|
||||
static constexpr auto MAX_LINES_OF_DISASM = 150;
|
||||
|
||||
struct Column {
|
||||
const char* name = nullptr;
|
||||
void (*func)(s64, Disassembler::DisassemblyResult&) = nullptr;
|
||||
};
|
||||
struct Column {
|
||||
const char *name = nullptr;
|
||||
void (*func)(s64, Disassembler::DisassemblyResult &) = nullptr;
|
||||
};
|
||||
|
||||
std::array<Column, 3> columns = {
|
||||
Column{"##BreakpointColumn", &BreakpointFunc},
|
||||
Column{"Address", &AddressFunc},
|
||||
Column{"Instruction", &InstructionFunc},
|
||||
};
|
||||
public:
|
||||
static void RegisterView();
|
||||
bool followPC = true;
|
||||
void Open(bool wantFollowPC = true) { enabled = true; followPC = wantFollowPC; }
|
||||
void Close() { enabled = false; }
|
||||
bool render();
|
||||
};
|
||||
std::array<Column, 3> columns = {
|
||||
Column{"##BreakpointColumn", &BreakpointFunc},
|
||||
Column{"Address", &AddressFunc},
|
||||
Column{"Instruction", &InstructionFunc},
|
||||
};
|
||||
|
||||
public:
|
||||
static void RegisterView();
|
||||
bool followPC = true;
|
||||
void Open(bool wantFollowPC = true) {
|
||||
enabled = true;
|
||||
followPC = wantFollowPC;
|
||||
}
|
||||
void Close() { enabled = false; }
|
||||
bool render();
|
||||
};
|
||||
|
||||
+418
-390
@@ -5,456 +5,484 @@
|
||||
#include <ImGuiImpl/StatusBar.hpp>
|
||||
#include <resources/gamecontrollerdb.h>
|
||||
|
||||
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) {
|
||||
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
KaizenGui::KaizenGui() noexcept :
|
||||
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
|
||||
emuThread(fpsCounter, settingsWindow) {
|
||||
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
|
||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||
}
|
||||
|
||||
KaizenGui::~KaizenGui() {
|
||||
gui::Cleanup();
|
||||
SDL_Quit();
|
||||
gui::Cleanup();
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
if (!gamepad) {
|
||||
const auto index = event.gdevice.which;
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
if (!gamepad) {
|
||||
const auto index = event.gdevice.which;
|
||||
|
||||
gamepad = SDL_OpenGamepad(index);
|
||||
info("Found controller!");
|
||||
info("Name: {}", SDL_GetGamepadName(gamepad));
|
||||
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
||||
gamepad = SDL_OpenGamepad(index);
|
||||
info("Found controller!");
|
||||
info("Name: {}", SDL_GetGamepadName(gamepad));
|
||||
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
if (gamepad)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
if (gamepad)
|
||||
SDL_CloseGamepad(gamepad);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||
const n64::Core& core = n64::Core::GetInstance();
|
||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||
switch(event.type) {
|
||||
const n64::Core &core = n64::Core::GetInstance();
|
||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
if(!gamepad)
|
||||
if (!gamepad)
|
||||
break;
|
||||
{
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight,
|
||||
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
||||
|
||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||
if (xclamped < 0) {
|
||||
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||
} else {
|
||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
xclamped *= 86;
|
||||
|
||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
if (yclamped < 0) {
|
||||
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
yclamped *= 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
|
||||
}
|
||||
break;
|
||||
{
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
||||
|
||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||
if (xclamped < 0) {
|
||||
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||
} else {
|
||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
xclamped *= 86;
|
||||
|
||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||
if (yclamped < 0) {
|
||||
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
}
|
||||
|
||||
yclamped *= 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
if(!gamepad)
|
||||
if (!gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||
fileDialogOpen = true;
|
||||
{
|
||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||
if (!unlockFramerate)
|
||||
core.parallel.SetFramerateUnlocked(fastForward);
|
||||
|
||||
if (core.romLoaded) {
|
||||
if (keys[SDL_SCANCODE_P]) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if (keys[SDL_SCANCODE_R]) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if (keys[SDL_SCANCODE_Q]) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||
|
||||
float x = 0, y = 0;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP])
|
||||
y = 86;
|
||||
if (keys[SDL_SCANCODE_DOWN])
|
||||
y = -86;
|
||||
if (keys[SDL_SCANCODE_LEFT])
|
||||
x = -86;
|
||||
if (keys[SDL_SCANCODE_RIGHT])
|
||||
x = 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
||||
}
|
||||
|
||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||
if(!unlockFramerate)
|
||||
core.parallel.SetFramerateUnlocked(fastForward);
|
||||
|
||||
if(core.romLoaded) {
|
||||
if(keys[SDL_SCANCODE_P]) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(keys[SDL_SCANCODE_R]) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if(keys[SDL_SCANCODE_Q]) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
if(gamepad)
|
||||
break;
|
||||
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||
|
||||
float x = 0, y = 0;
|
||||
|
||||
if (keys[SDL_SCANCODE_UP]) y = 86;
|
||||
if (keys[SDL_SCANCODE_DOWN]) y = -86;
|
||||
if (keys[SDL_SCANCODE_LEFT]) x = -86;
|
||||
if (keys[SDL_SCANCODE_RIGHT]) x = 86;
|
||||
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
||||
auto lastPC = Util::Error::GetLastPC();
|
||||
if(lastPC.has_value()) {
|
||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||
}
|
||||
auto lastPC = Util::Error::GetLastPC();
|
||||
if (lastPC.has_value()) {
|
||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||
}
|
||||
|
||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||
if(memoryAccess.has_value()) {
|
||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
||||
static_cast<u8>(size), address,
|
||||
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||
.c_str());
|
||||
}
|
||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||
if (memoryAccess.has_value()) {
|
||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||
ImGui::Text("%s",
|
||||
std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
|
||||
address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||
.c_str());
|
||||
}
|
||||
|
||||
return {lastPC, memoryAccess};
|
||||
return {lastPC, memoryAccess};
|
||||
}
|
||||
|
||||
void KaizenGui::RenderUI() {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
gui::StartFrame();
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
gui::StartFrame();
|
||||
|
||||
if(ImGui::BeginMainMenuBar()) {
|
||||
if(ImGui::BeginMenu("File")) {
|
||||
if(ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
if(ImGui::MenuItem("Exit")) {
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
if (ImGui::MenuItem("Exit")) {
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Emulation")) {
|
||||
ImGui::BeginDisabled(!core.romLoaded);
|
||||
|
||||
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Reset", "R")) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Stop", "Q")) {
|
||||
emuThread.Stop();
|
||||
core.romLoaded = false;
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Open Debugger")) {
|
||||
debugger.Open();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::MenuItem("Options")) {
|
||||
settingsWindow.isOpen = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About")) {
|
||||
aboutOpen = true;
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
if(ImGui::BeginMenu("Emulation")) {
|
||||
ImGui::BeginDisabled(!core.romLoaded);
|
||||
|
||||
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Reset", "R")) {
|
||||
emuThread.Reset();
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Stop", "Q")) {
|
||||
emuThread.Stop();
|
||||
core.romLoaded = false;
|
||||
}
|
||||
|
||||
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||
}
|
||||
|
||||
if(ImGui::MenuItem("Open Debugger")) {
|
||||
debugger.Open();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if(ImGui::MenuItem("Options")) {
|
||||
settingsWindow.isOpen = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
if (!Util::Error::IsHandled()) {
|
||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||
}
|
||||
if(ImGui::BeginMenu("Help")) {
|
||||
if(ImGui::MenuItem("About")) {
|
||||
aboutOpen = true;
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
|
||||
if (settingsWindow.isOpen) {
|
||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
if(!Util::Error::IsHandled()) {
|
||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||
}
|
||||
|
||||
if(settingsWindow.isOpen) {
|
||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||
}
|
||||
|
||||
if(aboutOpen) {
|
||||
ImGui::OpenPopup("About Kaizen");
|
||||
}
|
||||
|
||||
settingsWindow.render();
|
||||
debugger.render();
|
||||
|
||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||
ImGui::Separator();
|
||||
if(ImGui::Button("OK")) {
|
||||
aboutOpen = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (aboutOpen) {
|
||||
ImGui::OpenPopup("About Kaizen");
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
settingsWindow.render();
|
||||
debugger.render();
|
||||
|
||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
emuThread.TogglePause();
|
||||
switch(Util::Error::GetSeverity().as_enum) {
|
||||
case Util::Error::Severity::WARN: {
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
if(ignore || stop || chooseAnother) {
|
||||
Util::Error::SetHandled();
|
||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("OK")) {
|
||||
aboutOpen = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if(chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
} break;
|
||||
case Util::Error::Severity::UNRECOVERABLE: {
|
||||
emuThread.Stop();
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
if(ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
} break;
|
||||
case Util::Error::Severity::NON_FATAL: {
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An error has occurred!");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||
|
||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if(ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if(stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if(chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
if(openInDebugger) {
|
||||
if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||
|
||||
debugger.Open();
|
||||
emuThread.Reset();
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if(ImGui::BeginMainStatusBar()) {
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::EndMainStatusBar();
|
||||
}
|
||||
|
||||
if (shouldDisplaySpinner) {
|
||||
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
|
||||
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
emuThread.TogglePause();
|
||||
switch (Util::Error::GetSeverity().as_enum) {
|
||||
case Util::Error::Severity::WARN:
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
|
||||
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||
ImGui::SameLine();
|
||||
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||
const bool ignore = ImGui::Button("Try continuing");
|
||||
ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation");
|
||||
ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
if (ignore || stop || chooseAnother) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||
ImGui::PopFont();
|
||||
if (ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
if (stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
if (ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
break;
|
||||
case Util::Error::Severity::UNRECOVERABLE:
|
||||
{
|
||||
emuThread.Stop();
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
RenderErrorMessageDetails();
|
||||
if (ImGui::Button("OK"))
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
break;
|
||||
case Util::Error::Severity::NON_FATAL:
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||
ImGui::Text("An error has occurred!");
|
||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||
|
||||
if(fileDialogOpen) {
|
||||
fileDialogOpen = false;
|
||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
||||
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
|
||||
auto kaizen = static_cast<KaizenGui*>(userdata);
|
||||
|
||||
if (!filelist) {
|
||||
panic("An error occured: {}", SDL_GetError());
|
||||
}
|
||||
const bool ignore = ImGui::Button("Try continuing");
|
||||
ImGui::SameLine();
|
||||
const bool stop = ImGui::Button("Stop emulation");
|
||||
ImGui::SameLine();
|
||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||
const bool openInDebugger =
|
||||
lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||
if (ignore || stop || chooseAnother || openInDebugger) {
|
||||
Util::Error::SetHandled();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (!*filelist) {
|
||||
warn("The user did not select any file.");
|
||||
warn("Most likely, the dialog was canceled.");
|
||||
if (ignore) {
|
||||
emuThread.TogglePause();
|
||||
}
|
||||
|
||||
if (stop || chooseAnother) {
|
||||
emuThread.Stop();
|
||||
}
|
||||
|
||||
if (chooseAnother) {
|
||||
fileDialogOpen = true;
|
||||
}
|
||||
|
||||
if (openInDebugger) {
|
||||
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||
|
||||
debugger.Open();
|
||||
emuThread.Reset();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMainStatusBar()) {
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::EndMainStatusBar();
|
||||
}
|
||||
|
||||
if (shouldDisplaySpinner) {
|
||||
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
|
||||
ImVec2(0.5f, 0.5f));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
|
||||
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||
|
||||
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
if (fileDialogOpen) {
|
||||
fileDialogOpen = false;
|
||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
|
||||
{"Nintendo 64 executable", "n64;z64;v64"},
|
||||
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
||||
SDL_ShowOpenFileDialog(
|
||||
[](void *userdata, const char *const *filelist, int) {
|
||||
auto kaizen = static_cast<KaizenGui *>(userdata);
|
||||
|
||||
if (!filelist) {
|
||||
panic("An error occured: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
if (!*filelist) {
|
||||
warn("The user did not select any file.");
|
||||
warn("Most likely, the dialog was canceled.");
|
||||
return;
|
||||
}
|
||||
|
||||
kaizen->fileToLoad = *filelist;
|
||||
kaizen->shouldDisplaySpinner = true;
|
||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||
fileWorker.detach();
|
||||
},
|
||||
this, window.getHandle(), filters, 3, nullptr, false);
|
||||
}
|
||||
|
||||
if (minimized)
|
||||
return;
|
||||
}
|
||||
|
||||
kaizen->fileToLoad = *filelist;
|
||||
kaizen->shouldDisplaySpinner = true;
|
||||
if (core.romLoaded) {
|
||||
core.parallel.UpdateScreen<true>();
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||
fileWorker.detach();
|
||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
||||
}
|
||||
|
||||
if(minimized)
|
||||
return;
|
||||
|
||||
if(core.romLoaded) {
|
||||
core.parallel.UpdateScreen<true>();
|
||||
return;
|
||||
}
|
||||
|
||||
core.parallel.UpdateScreen<false>();
|
||||
core.parallel.UpdateScreen<false>();
|
||||
}
|
||||
|
||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
core.LoadROM(path);
|
||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||
n64::Core &core = n64::Core::GetInstance();
|
||||
core.LoadROM(path);
|
||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||
}
|
||||
|
||||
void KaizenGui::run() {
|
||||
while(!quit) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
switch(e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
minimized = true;
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
minimized = false;
|
||||
break;
|
||||
default:
|
||||
}
|
||||
QueryDevices(e);
|
||||
HandleInput(e);
|
||||
while (!quit) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||
switch (e.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
quit = true;
|
||||
emuThread.Stop();
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||
minimized = true;
|
||||
break;
|
||||
case SDL_EVENT_WINDOW_RESTORED:
|
||||
minimized = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
QueryDevices(e);
|
||||
HandleInput(e);
|
||||
}
|
||||
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
|
||||
emuThread.run();
|
||||
RenderUI();
|
||||
}
|
||||
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
|
||||
emuThread.run();
|
||||
RenderUI();
|
||||
}
|
||||
}
|
||||
|
||||
void KaizenGui::LoadTAS(const std::string &path) noexcept {
|
||||
n64::Core::GetInstance().LoadTAS(fs::path(path));
|
||||
}
|
||||
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
|
||||
|
||||
@@ -10,7 +10,7 @@ class KaizenGui final {
|
||||
public:
|
||||
explicit KaizenGui() noexcept;
|
||||
~KaizenGui();
|
||||
|
||||
|
||||
double fpsCounter = -1.0;
|
||||
bool fastForward = false;
|
||||
bool unlockFramerate = false;
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
Debugger debugger;
|
||||
|
||||
SDL_Gamepad* gamepad = nullptr;
|
||||
|
||||
|
||||
void run();
|
||||
static void LoadTAS(const std::string &path) noexcept;
|
||||
void LoadROM(const std::string &path) noexcept;
|
||||
@@ -37,12 +37,13 @@ private:
|
||||
void HandleInput(const SDL_Event &event);
|
||||
void QueryDevices(const SDL_Event &event);
|
||||
|
||||
[[noreturn]] void FileWorker() {
|
||||
void FileWorker() {
|
||||
while (true) {
|
||||
if (!fileToLoad.empty()) {
|
||||
LoadROM(fileToLoad);
|
||||
shouldDisplaySpinner = false;
|
||||
fileToLoad = "";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,42 +2,52 @@
|
||||
#include <Options.hpp>
|
||||
#include <log.hpp>
|
||||
#include <imgui.h>
|
||||
#include <Core.hpp>
|
||||
|
||||
CPUSettings::CPUSettings() {
|
||||
if (Options::GetInstance().GetValue<std::string>("cpu", "type") == "jit") {
|
||||
selectedCpuTypeIndex = 1;
|
||||
} else {
|
||||
selectedCpuTypeIndex = 0;
|
||||
}
|
||||
auto selectedCpuType = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||
if (selectedCpuType == "jit") {
|
||||
selectedCpuTypeIndex = 2;
|
||||
} else if (selectedCpuType == "cached_interpreter") {
|
||||
selectedCpuTypeIndex = 1;
|
||||
} else {
|
||||
selectedCpuTypeIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPUSettings::render() {
|
||||
const char* items[] = {
|
||||
"Interpreter",
|
||||
"Dynamic Recompiler"
|
||||
};
|
||||
const char *items[] = {"Interpreter", "Cached Interpreter",
|
||||
#ifdef KAIZEN_JIT_ENABLED
|
||||
"Dynamic Recompiler"
|
||||
#endif
|
||||
};
|
||||
|
||||
const char* combo_preview_value = items[selectedCpuTypeIndex];
|
||||
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
|
||||
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
|
||||
const bool is_selected = (selectedCpuTypeIndex == n);
|
||||
if (ImGui::Selectable(items[n], is_selected)) {
|
||||
selectedCpuTypeIndex = n;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
const char *combo_preview_value = items[selectedCpuTypeIndex];
|
||||
if (ImGui::BeginCombo("CPU Type", combo_preview_value)) {
|
||||
for (int n = 0; n < IM_ARRAYSIZE(items); n++) {
|
||||
const bool is_selected = (selectedCpuTypeIndex == n);
|
||||
if (ImGui::Selectable(items[n], is_selected)) {
|
||||
selectedCpuTypeIndex = n;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if(modified) {
|
||||
if(selectedCpuTypeIndex == 0) {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
|
||||
} else {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||
// Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
|
||||
if (is_selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
if (selectedCpuTypeIndex == 0) {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "interpreter");
|
||||
n64::Core::GetInstance().cpuType = n64::Core::Interpreted;
|
||||
} else if (selectedCpuTypeIndex == 1) {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "cached_interpreter");
|
||||
n64::Core::GetInstance().cpuType = n64::Core::CachedInterpreter;
|
||||
} else {
|
||||
Options::GetInstance().SetValue<std::string>("cpu", "type", "jit");
|
||||
n64::Core::GetInstance().cpuType = n64::Core::DynamicRecompiler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+37
-33
@@ -3,11 +3,13 @@
|
||||
#include <common.hpp>
|
||||
|
||||
namespace n64 {
|
||||
|
||||
struct Instruction {
|
||||
Instruction() = default;
|
||||
Instruction(u32 v) { instr.raw = v; }
|
||||
void operator=(u32 v) { instr.raw = v; }
|
||||
operator u32() const { return instr.raw; }
|
||||
|
||||
|
||||
inline u8 rs() const { return instr.rtype.rs; }
|
||||
inline u8 rt() const { return instr.rtype.rt; }
|
||||
inline u8 rd() const { return instr.rtype.rd; }
|
||||
@@ -21,7 +23,9 @@ struct Instruction {
|
||||
inline u8 vd() const { return fd(); }
|
||||
inline u8 e1() const { return (instr.raw >> 7) & 0x0f; }
|
||||
inline u8 e2() const { return rs() & 0x0f; }
|
||||
inline u8 op() const { return rt(); }
|
||||
inline u16 imm() const { return instr.itype.imm; }
|
||||
inline u16 offset() const { return imm(); }
|
||||
inline u32 target() const { return instr.jtype.target; }
|
||||
inline u8 opcode() const { return instr.opcode.op; }
|
||||
inline u8 special() const { return instr.opcode.special; }
|
||||
@@ -32,61 +36,61 @@ struct Instruction {
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned imm:16;
|
||||
unsigned rt:5;
|
||||
unsigned rs:5;
|
||||
unsigned op:6;
|
||||
unsigned imm : 16;
|
||||
unsigned rt : 5;
|
||||
unsigned rs : 5;
|
||||
unsigned op : 6;
|
||||
} itype;
|
||||
|
||||
struct {
|
||||
unsigned target:26;
|
||||
unsigned op:6;
|
||||
unsigned target : 26;
|
||||
unsigned op : 6;
|
||||
} jtype;
|
||||
|
||||
struct {
|
||||
unsigned funct:6;
|
||||
unsigned sa:5;
|
||||
unsigned rd:5;
|
||||
unsigned rt:5;
|
||||
unsigned rs:5;
|
||||
unsigned op:6;
|
||||
unsigned funct : 6;
|
||||
unsigned sa : 5;
|
||||
unsigned rd : 5;
|
||||
unsigned rt : 5;
|
||||
unsigned rs : 5;
|
||||
unsigned op : 6;
|
||||
} rtype;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned special_lo:3;
|
||||
unsigned special_hi:3;
|
||||
unsigned:26;
|
||||
unsigned special_lo : 3;
|
||||
unsigned special_hi : 3;
|
||||
unsigned : 26;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned special:6;
|
||||
unsigned:26;
|
||||
unsigned special : 6;
|
||||
unsigned : 26;
|
||||
};
|
||||
struct {
|
||||
unsigned:16;
|
||||
unsigned regimm_lo:3;
|
||||
unsigned regimm_hi:2;
|
||||
unsigned:11;
|
||||
unsigned : 16;
|
||||
unsigned regimm_lo : 3;
|
||||
unsigned regimm_hi : 2;
|
||||
unsigned : 11;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned:16;
|
||||
unsigned regimm:5;
|
||||
unsigned:11;
|
||||
unsigned : 16;
|
||||
unsigned regimm : 5;
|
||||
unsigned : 11;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned:26;
|
||||
unsigned op:6;
|
||||
unsigned : 26;
|
||||
unsigned op : 6;
|
||||
};
|
||||
|
||||
struct {
|
||||
unsigned funct:6;
|
||||
unsigned:10;
|
||||
unsigned cop_rt:5;
|
||||
unsigned cop_rs:5;
|
||||
unsigned:6;
|
||||
unsigned funct : 6;
|
||||
unsigned : 10;
|
||||
unsigned cop_rt : 5;
|
||||
unsigned cop_rs : 5;
|
||||
unsigned : 6;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
@@ -221,4 +225,4 @@ struct Instruction {
|
||||
static constexpr u8 BLTZALL = 0b10010;
|
||||
static constexpr u8 BGEZALL = 0b10011;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
Reference in New Issue
Block a user