From 7f99f82639559fb837769a352378784d768a1139 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:03:45 -0300 Subject: [PATCH 01/26] iris: Minor fixes --- .gitmodules | 3 +++ CMakeLists.txt | 7 ++++++- compat.txt | 2 ++ implot | 1 + main.cpp | 50 +++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 51 insertions(+), 12 deletions(-) create mode 160000 implot diff --git a/.gitmodules b/.gitmodules index 1e8cfea..775e0c4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,6 @@ [submodule "incbin"] path = incbin url = https://github.com/graphitemaster/incbin +[submodule "implot"] + path = implot + url = https://github.com/epezent/implot diff --git a/CMakeLists.txt b/CMakeLists.txt index e7a9a4d..c5d66e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ target_sources(iris PRIVATE frontend/ui/memory_card_tool.cpp frontend/ui/menubar.cpp frontend/ui/modules.cpp + frontend/ui/overlay.cpp frontend/ui/pad.cpp frontend/ui/settings.cpp frontend/ui/spu2.cpp @@ -171,13 +172,17 @@ target_sources(iris PRIVATE imgui/imgui_tables.cpp imgui/imgui_widgets.cpp imgui/backends/imgui_impl_sdl3.cpp - imgui/backends/imgui_impl_sdlgpu3.cpp) + imgui/backends/imgui_impl_sdlgpu3.cpp + implot/implot_demo.cpp + implot/implot_items.cpp + implot/implot.cpp) add_subdirectory(SDL EXCLUDE_FROM_ALL) target_include_directories(iris PRIVATE imgui imgui/backends + implot SDL/include frontend incbin diff --git a/compat.txt b/compat.txt index 81e1d1e..f19443b 100644 --- a/compat.txt +++ b/compat.txt @@ -11,10 +11,12 @@ "Klonoa 2 - Lunatea's Veil (USA)" - Doesn't work on Release builds (!), requires 16-bit DMAC writes "Namco Museum 50th Anniversary (USA)" - Requires MFIFO "R-Type Final (Japan)" - Requires MFIFO +"Sega Ages 2500 Series Vol. 23 - Sega Memorial Selection (Japan)" - GIF DMA read from NULL "Sega Genesis Collection (USA)" - Works, "forgets" to change the videomode causing wrong graphics at startup (needs 24-bit 640x448 interlaced?) "Simpsons, The - Hit & Run (USA)" - Needs MFIFO "Tekken 4 (USA)" - Needs MFIFO "Tekken Tag Tournament (USA) (v1.00)" - Needs MFIFO "Thunder Force VI (Japan)" - Crashes on an invalid GIF DMA address when starting up (compare against Dobie) "Virtua Fighter 4 - Evolution (USA)" - Works, uses filling mode +"Virtua Fighter - Cyber Generation - Judgment Six no Yabou (Japan)" - PMTHL unimplemented "We Love Katamari (USA)" - Uses filling mode, gets stuck trying to play an FMV after the Namco logo? \ No newline at end of file diff --git a/implot b/implot new file mode 160000 index 0000000..3da8bd3 --- /dev/null +++ b/implot @@ -0,0 +1 @@ +Subproject commit 3da8bd34299965d3b0ab124df743fe3e076fa222 diff --git a/main.cpp b/main.cpp index 9547500..590be29 100644 --- a/main.cpp +++ b/main.cpp @@ -15,6 +15,7 @@ #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imgui_impl_sdlgpu3.h" +#include "implot.h" // SDL3 includes #include @@ -29,6 +30,7 @@ #include "incbin.h" INCBIN(roboto, "../res/Roboto-Regular.ttf"); +INCBIN(roboto_black, "../res/Roboto-Black.ttf"); INCBIN(symbols, "../res/MaterialSymbolsRounded.ttf"); INCBIN(firacode, "../res/FiraCode-Regular.ttf"); INCBIN(ps1_memory_card_icon, "../res/ps1_mcd.png"); @@ -196,9 +198,10 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ SDL_ResumeAudioStreamDevice(iris->stream); - // Setup Dear ImGui context + // Setup Dear ImGui/ImPlot context IMGUI_CHECKVERSION(); ImGui::CreateContext(); + ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls @@ -231,6 +234,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { iris->font_body = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 16.0F, &config_no_own); iris->font_icons = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 20.0F, &config, g_icon_range); iris->font_icons_big = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 50.0F, &config_no_own, g_icon_range); + iris->font_black = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_black_data, g_roboto_black_size, 30.0F, &config_no_own); IM_ASSERT(iris->font_small_code != nullptr); IM_ASSERT(iris->font_code != nullptr); @@ -239,6 +243,7 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { IM_ASSERT(iris->font_body != nullptr); IM_ASSERT(iris->font_icons != nullptr); IM_ASSERT(iris->font_icons_big != nullptr); + IM_ASSERT(iris->font_black != nullptr); io.FontDefault = iris->font_body; @@ -349,6 +354,22 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); + ImPlotStyle& pstyle = ImPlot::GetStyle(); + + pstyle.MinorGridSize = ImVec2(0.0f, 0.0f); + pstyle.MajorGridSize = ImVec2(0.0f, 0.0f); + pstyle.MinorTickLen = ImVec2(0.0f, 0.0f); + pstyle.MajorTickLen = ImVec2(0.0f, 0.0f); + pstyle.PlotDefaultSize = ImVec2(250.0f, 150.0f); + pstyle.PlotPadding = ImVec2(0.0f, 0.0f); + pstyle.LegendPadding = ImVec2(0.0f, 0.0f); + pstyle.LegendInnerPadding = ImVec2(0.0f, 0.0f); + pstyle.LineWeight = 2.0f; + + pstyle.Colors[ImPlotCol_Line] = ImVec4(0.0f, 1.0f, 0.2f, 1.0f); + pstyle.Colors[ImPlotCol_FrameBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); + pstyle.Colors[ImPlotCol_PlotBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); + iris->open = true; // Initialize our emulator state @@ -424,6 +445,8 @@ SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { // Initialize appstate *appstate = iris; + SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height); + return SDL_APP_CONTINUE; } @@ -481,13 +504,15 @@ SDL_AppResult SDL_AppIterate(void* appstate) { // Execute until VBlank while (!ps2_gs_is_vblank(iris->ps2->gs)) { - do_cycle(iris); + ps2_cycle(iris->ps2); - if (iris->pause) { - iris::update_window(iris); + // do_cycle(iris); - return SDL_APP_CONTINUE; - } + // if (iris->pause) { + // iris::update_window(iris); + + // return SDL_APP_CONTINUE; + // } } // Draw frame @@ -495,13 +520,15 @@ SDL_AppResult SDL_AppIterate(void* appstate) { // Execute until vblank is over while (ps2_gs_is_vblank(iris->ps2->gs)) { - do_cycle(iris); + ps2_cycle(iris->ps2); - if (iris->pause) { - iris::update_window(iris); + // do_cycle(iris); - return SDL_APP_CONTINUE; - } + // if (iris->pause) { + // iris::update_window(iris); + + // return SDL_APP_CONTINUE; + // } } return SDL_APP_CONTINUE; @@ -572,6 +599,7 @@ void SDL_AppQuit(void* appstate, SDL_AppResult result) { SDL_WaitForGPUIdle(iris->device); ImGui_ImplSDL3_Shutdown(); ImGui_ImplSDLGPU3_Shutdown(); + ImPlot::DestroyContext(); ImGui::DestroyContext(); // Release resources From 718333a03ca8c6fbfcb8db9a4e38769b7f3b6d4e Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:04:53 -0300 Subject: [PATCH 02/26] input: Map keys to analog stick --- frontend/input.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/input.cpp b/frontend/input.cpp index ab674a1..cf3936b 100644 --- a/frontend/input.cpp +++ b/frontend/input.cpp @@ -151,6 +151,10 @@ void handle_keydown_event(iris::instance* iris, SDL_KeyboardEvent& key) { case SDLK_0: { ps2_iop_intc_irq(iris->ps2->iop_intc, IOP_INTC_USB); } break; + case SDLK_I: ds_analog_change(iris->ds[0], DS_AX_LEFT_V, 0xff); break; + case SDLK_J: ds_analog_change(iris->ds[0], DS_AX_LEFT_H, 0); break; + case SDLK_K: ds_analog_change(iris->ds[0], DS_AX_LEFT_V, 0); break; + case SDLK_L: ds_analog_change(iris->ds[0], DS_AX_LEFT_H, 0xff); break; } uint16_t mask = map_button(key.key); @@ -159,6 +163,12 @@ void handle_keydown_event(iris::instance* iris, SDL_KeyboardEvent& key) { } void handle_keyup_event(iris::instance* iris, SDL_KeyboardEvent& key) { + switch (key.key) { + case SDLK_I: ds_analog_change(iris->ds[0], DS_AX_LEFT_V, 0x80); break; + case SDLK_J: ds_analog_change(iris->ds[0], DS_AX_LEFT_H, 0x80); break; + case SDLK_K: ds_analog_change(iris->ds[0], DS_AX_LEFT_V, 0x80); break; + case SDLK_L: ds_analog_change(iris->ds[0], DS_AX_LEFT_H, 0x80); break; + } uint16_t mask = map_button(key.key); ds_button_release(iris->ds[0], mask); From 0d7b5d54572266b30c9ff85659a19345cd99e523 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:06:50 -0300 Subject: [PATCH 03/26] frontend: Implement performance overlay --- frontend/input.cpp | 1 + frontend/iris.cpp | 1 + frontend/iris.hpp | 39 +++++++++++++++ frontend/settings.cpp | 9 +++- frontend/ui/overlay.cpp | 103 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 frontend/ui/overlay.cpp diff --git a/frontend/input.cpp b/frontend/input.cpp index cf3936b..cfeb072 100644 --- a/frontend/input.cpp +++ b/frontend/input.cpp @@ -169,6 +169,7 @@ void handle_keyup_event(iris::instance* iris, SDL_KeyboardEvent& key) { case SDLK_K: ds_analog_change(iris->ds[0], DS_AX_LEFT_V, 0x80); break; case SDLK_L: ds_analog_change(iris->ds[0], DS_AX_LEFT_H, 0x80); break; } + uint16_t mask = map_button(key.key); ds_button_release(iris->ds[0], mask); diff --git a/frontend/iris.cpp b/frontend/iris.cpp index 87cf9b0..7b83b11 100644 --- a/frontend/iris.cpp +++ b/frontend/iris.cpp @@ -247,6 +247,7 @@ void update_window(iris::instance* iris) { // if (iris->show_gamelist) show_gamelist(iris); if (iris->show_imgui_demo) ShowDemoWindow(&iris->show_imgui_demo); if (iris->show_bios_setting_window) show_bios_setting_window(iris); + if (iris->show_overlay) show_overlay(iris); // Display little pause icon in the top right corner if (iris->pause) { diff --git a/frontend/iris.hpp b/frontend/iris.hpp index e5da456..5bf93d0 100644 --- a/frontend/iris.hpp +++ b/frontend/iris.hpp @@ -65,6 +65,40 @@ struct elf_symbol { uint32_t size; }; +// Event -> Action +enum { + INPUT_ACTION_PRESS_BUTTON, + INPUT_ACTION_RELEASE_BUTTON, + INPUT_ACTION_MOVE_AXIS +}; + +enum { + INPUT_CONTROLLER_DUALSHOCK2 + + // Large To-do list here, we're missing the Namco GunCon + // controllers, JogCon, NegCon, Buzz! Buzzer, the Train + // controllers, Taiko Drum Master controller, the Dance Dance + // Revolution mat, Guitar Hero controllers, etc. +}; + +// struct input_action { +// int action; + +// union { +// uint32_t button; +// uint8_t axis; +// }; +// }; + +// class input_device { +// int controller; + +// public: +// void set_controller(int controller); +// int get_controller(); +// virtual input_action map_event(SDL_Event* event) = 0; +// }; + struct instance { SDL_Window* window = nullptr; SDL_GPUDevice* device = nullptr; @@ -101,6 +135,7 @@ struct instance { ImFont* font_body = nullptr; ImFont* font_icons = nullptr; ImFont* font_icons_big = nullptr; + ImFont* font_black = nullptr; std::string elf_path = ""; std::string boot_path = ""; @@ -149,6 +184,7 @@ struct instance { bool show_memory_card_tool = false; bool show_imgui_demo = false; bool show_vu_disassembler = false; + bool show_overlay = false; // Special windows bool show_bios_setting_window = false; @@ -192,6 +228,8 @@ struct instance { struct ds_state* ds[2] = { nullptr }; struct mcd_state* mcd[2] = { nullptr }; + // input_device* device[2]; + float drop_file_alpha = 0.0f; float drop_file_alpha_delta = 0.0f; float drop_file_alpha_target = 0.0f; @@ -239,6 +277,7 @@ void show_settings(iris::instance* iris); void show_pad_debugger(iris::instance* iris); void show_symbols(iris::instance* iris); void show_threads(iris::instance* iris); +void show_overlay(iris::instance* iris); void show_memory_card_tool(iris::instance* iris); void show_bios_setting_window(iris::instance* iris); // void show_gamelist(iris::instance* iris); diff --git a/frontend/settings.cpp b/frontend/settings.cpp index d6c9888..8b4dc30 100644 --- a/frontend/settings.cpp +++ b/frontend/settings.cpp @@ -84,6 +84,8 @@ int parse_toml_settings(iris::instance* iris) { iris->integer_scaling = display["integer_scaling"].value_or(false); iris->scale = display["scale"].value_or(1.5f); iris->renderer_backend = display["renderer"].value_or(RENDERER_SOFTWARE_THREAD); + iris->window_width = display["window_width"].value_or(960); + iris->window_height = display["window_height"].value_or(720); auto audio = tbl["audio"]; iris->mute = audio["mute"].value_or(false); @@ -108,6 +110,8 @@ int parse_toml_settings(iris::instance* iris) { iris->show_status_bar = debugger["show_status_bar"].value_or(true); iris->show_pad_debugger = debugger["show_pad_debugger"].value_or(false); iris->show_threads = debugger["show_threads"].value_or(false); + iris->show_overlay = debugger["show_overlay"].value_or(false); + // iris->show_symbols = debugger["show_symbols"].value_or(false); iris->show_breakpoints = debugger["show_breakpoints"].value_or(false); iris->show_imgui_demo = debugger["show_imgui_demo"].value_or(false); @@ -282,6 +286,7 @@ void close_settings(iris::instance* iris) { { "show_breakpoints", iris->show_breakpoints }, { "show_threads", iris->show_threads }, { "show_imgui_demo", iris->show_imgui_demo }, + { "show_overlay", iris->show_overlay }, { "skip_fmv", iris->skip_fmv } } }, { "display", toml::table { @@ -290,7 +295,9 @@ void close_settings(iris::instance* iris) { { "integer_scaling", iris->integer_scaling }, { "fullscreen", iris->fullscreen }, { "bilinear", iris->bilinear }, - { "renderer", iris->renderer_backend } + { "renderer", iris->renderer_backend }, + { "window_width", iris->window_width }, + { "window_height", iris->window_height } } }, { "audio", toml::table { { "mute", iris->mute }, diff --git a/frontend/ui/overlay.cpp b/frontend/ui/overlay.cpp new file mode 100644 index 0000000..8385a80 --- /dev/null +++ b/frontend/ui/overlay.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include "iris.hpp" + +#include "res/IconsMaterialSymbols.h" + +#include "implot.h" + +#define MAX_SAMPLES 100 + +namespace iris { + +std::vector fps_history = { 0 }; +std::vector fps_history_avg = { 0 }; + +ImVec2 pos = ImVec2(10, 10); +const ImVec2 padding = ImVec2(5, 5); +const ImVec2 size = ImVec2(250, 100); +const float opacity = 0.75f; + +float max = 0.0; + +void update_overlay(iris::instance* iris) { + // if (fps_history.size() == MAX_SAMPLES) { + // if (fps_history.front() >= max) { + // max = 0.0; + + // for (int i = 1; i < MAX_SAMPLES; i++) { + // if (fps_history[i] > max) { + // max = fps_history[i]; + // } + // } + // } + + // fps_history.pop_front(); + // } + + float sample = 1.0 / ImGui::GetIO().DeltaTime; + + if (!iris->pause) { + if (fps_history.size() == MAX_SAMPLES) + fps_history.erase(fps_history.begin()); + + fps_history.push_back(sample); + + if (fps_history_avg.size() == MAX_SAMPLES) + fps_history_avg.erase(fps_history_avg.begin()); + + fps_history_avg.push_back(std::roundf(ImGui::GetIO().Framerate)); + } +} + +void show_overlay(iris::instance* iris) { + using namespace ImGui; + using namespace ImPlot; + + SetNextWindowBgAlpha(0.5f); + SetNextWindowPos(ImVec2(10.0, 10.0 + iris->menubar_height), ImGuiCond_Always); + + if (Begin("Overlay", nullptr, + ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_NoDocking)) { + update_overlay(iris); + + ImPlotFlags flags = + ImPlotFlags_NoTitle | + ImPlotFlags_NoLegend | + ImPlotFlags_NoMouseText | + ImPlotFlags_NoBoxSelect | + ImPlotFlags_NoFrame | + ImPlotFlags_NoMenus | + ImPlotFlags_CanvasOnly | + ImPlotFlags_NoInputs; + + ImPlotAxisFlags axis_flags = + ImPlotAxisFlags_NoTickLabels | + ImPlotAxisFlags_NoGridLines; + + if (BeginPlot("##overlay_plot", ImVec2(0, 0), flags)) { + SetupAxes(nullptr, nullptr, axis_flags, axis_flags); + SetupAxesLimits(0, (double)MAX_SAMPLES - 1, 0, 60, ImGuiCond_Always); + PlotLine("FPS", fps_history.data(), (int)fps_history.size()); + + EndPlot(); + } + + renderer_stats* stats = renderer_get_debug_stats(iris->ctx); + + PushFont(iris->font_black); + Text("%d fps", (int)std::roundf(1.0 / ImGui::GetIO().DeltaTime)); + PopFont(); + Text("Primitives: %d", stats->primitives); + Text("Texture uploads: %d", stats->texture_uploads); + Text("Texture blits: %d", stats->texture_blits); + } End(); +} + +} \ No newline at end of file From 4cc4f224c926fb9dc800c79c7fd6e98cd53a1fd1 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:12:42 -0300 Subject: [PATCH 04/26] timers: Initial T4 prescaler implementation --- src/iop/timers.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/iop/timers.c b/src/iop/timers.c index 7a1462f..0b381f1 100644 --- a/src/iop/timers.c +++ b/src/iop/timers.c @@ -49,10 +49,24 @@ void iop_timer_tick(struct ps2_iop_timers* timers, int i) { t->internal = 0; } } else { - ++t->counter; + t->counter += 2; } } else { - t->counter += 9; + if (i == 4) { + switch (t->t4_prescaler) { + case 0: t->counter += 2; break; + case 1: { + if (t->internal != 128) { + ++t->internal; + } else { + t->counter += 1; + t->internal = 0; + } + } break; + } + } else { + t->counter += 2; + } } if (t->counter >= t->target && prev < t->target) { From 33f755d65f1ae3fd3b997505285156471cdc6304 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:14:20 -0300 Subject: [PATCH 05/26] spu2: Fix SPU IRQs and implement capture buffers --- src/iop/spu2.c | 192 +++++++++++++++++++++++++++++++++++++++++-------- src/iop/spu2.h | 4 ++ 2 files changed, 166 insertions(+), 30 deletions(-) diff --git a/src/iop/spu2.c b/src/iop/spu2.c index fd785e2..e5dd8d8 100644 --- a/src/iop/spu2.c +++ b/src/iop/spu2.c @@ -5,6 +5,9 @@ #include "spu2.h" +FILE* output = NULL; +uint32_t chunk_size = 0; + static const int16_t g_spu_gauss_table[] = { -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, @@ -76,6 +79,34 @@ struct ps2_spu2* ps2_spu2_create(void) { return (struct ps2_spu2*)malloc(sizeof(struct ps2_spu2)); } +struct wav_hdr { + char riff[4]; + uint32_t size; + char wave[4]; + char fmt[4]; + uint32_t block_size; + uint16_t audio_format; + uint16_t num_channels; + uint32_t samplerate; + uint32_t bytes_per_sec; + uint16_t bytes_per_block; + uint16_t bits_per_sample; +}; + +struct wav_chunk { + char id[4]; // "data" + uint32_t size; +}; + +// FormatBlocID (4 bytes) : Identifier « fmt␣ » (0x66, 0x6D, 0x74, 0x20) +// BlocSize (4 bytes) : Chunk size minus 8 bytes, which is 16 bytes here (0x10) +// AudioFormat (2 bytes) : Audio format (1: PCM integer, 3: IEEE 754 float) +// NbrChannels (2 bytes) : Number of channels +// Frequency (4 bytes) : Sample rate (in hertz) +// BytePerSec (4 bytes) : Number of bytes to read per second (Frequency * BytePerBloc). +// BytePerBloc (2 bytes) : Number of bytes per block (NbrChannels * BitsPerSample / 8). +// BitsPerSample (2 bytes) : Number of bits per sample + void ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(spu2, 0, sizeof(struct ps2_spu2)); @@ -88,13 +119,17 @@ void ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_io spu2->c[1].stat = 0x80; spu2->c[0].endx = 0x00ffffff; spu2->c[1].endx = 0x00ffffff; + + output = fopen("adma.wav", "wb"); + + fseek(output, sizeof(struct wav_hdr) + sizeof(struct wav_chunk), SEEK_SET); } void spu2_irq(struct ps2_spu2* spu2, int c) { - if (spu2->spdif_irq & (8 << c)) + if (spu2->spdif_irq & (4 << c)) return; - spu2->spdif_irq |= 8 << c; + spu2->spdif_irq |= 4 << c; // printf("spu2: IRQ fired\n"); @@ -103,7 +138,7 @@ void spu2_irq(struct ps2_spu2* spu2, int c) { void spu2_check_irq(struct ps2_spu2* spu2, uint32_t addr) { for (int i = 0; i < 2; i++) { - if (addr == spu2->c[i].irqa && (spu2->c[i].attr & (1 << 6))) { + if ((addr == spu2->c[i].irqa) && (spu2->c[i].attr & (1 << 6))) { spu2_irq(spu2, i); } } @@ -125,8 +160,11 @@ void spu2_write_kon(struct ps2_spu2* spu2, int c, int h, uint64_t data) { if (idx >= 24) break; - struct spu2_voice* v = &spu2->c[c].v[idx]; struct spu2_core* cr = &spu2->c[c]; + struct spu2_voice* v = &cr->v[idx]; + + // if (v->playing) + // continue; // Make sure to clear the internal state of a voice // before playing @@ -157,7 +195,6 @@ void spu2_write_kon(struct ps2_spu2* spu2, int c, int h, uint64_t data) { v->adsr_pending_step = 0; v->adsr_sustain_level = 0; - v->playing = 1; v->nax = v->ssa; v->adsr_sustain_level = ((v->adsr1 & 0xf) + 1) * 0x800; @@ -184,8 +221,10 @@ void spu2_write_koff(struct ps2_spu2* spu2, int c, int h, uint64_t data) { // spu2->c[c].v[i+h*16].playing = 0; - // To-do: Enter ADSR release // printf("spu2: voice %d koff\n", v); + if (!spu2->c[c].v[v].playing) + continue; + adsr_load_release(spu2, &spu2->c[c], &spu2->c[c].v[v], v); } } @@ -235,11 +274,11 @@ void spu2_core1_reset_handler(void* udata, int overshoot) { } void spu2_write_attr(struct ps2_spu2* spu2, int c, uint64_t data) { - // if (spu2->c[c].attr & (1 << 6)) { - // if (!(data & (1 << 6))) { - // spu2->spdif_irq &= ~(4 << c); - // } - // } + if (spu2->c[c].attr & (1 << 6)) { + if (!(data & (1 << 6))) { + spu2->spdif_irq &= ~(4 << c); + } + } spu2->c[c].attr = data & 0x7fff; @@ -314,9 +353,13 @@ uint64_t ps2_spu2_read16(struct ps2_spu2* spu2, uint32_t addr) { if ((addr >= 0x1c0 && addr <= 0x2df) || (addr >= 0x5c0 && addr <= 0x6df)) { int core = (addr >> 10) & 1; - int voice = (addr - (0x1c0 + 0x400 * core)) / 12; - switch (addr % 0xc) { + addr -= 0x1c0 + 0x400 * core; + + int voice = addr / 12; + int reg = addr % 12; + + switch (reg) { case 0x0: return spu2->c[core].v[voice].ssa >> 16; case 0x2: return spu2->c[core].v[voice].ssa & 0xffff; case 0x4: return spu2->c[core].v[voice].lsax >> 16; @@ -614,7 +657,7 @@ void ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data) { case 0x7ac: spu2->c[1].in_coef_l = data; return; case 0x7ae: spu2->c[1].in_coef_r = data; return; case 0x7C0: spu2->spdif_out = data; return; - case 0x7C2: printf("spdif irq write %04x", data); spu2->spdif_irq = data; return; + case 0x7C2: spu2->spdif_irq = data; return; case 0x7C6: spu2->spdif_mode = data; return; case 0x7C8: spu2->spdif_media = data; return; case 0x7CA: spu2->spdif_copy = data; return; @@ -624,6 +667,46 @@ void ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data) { } void ps2_spu2_destroy(struct ps2_spu2* spu2) { + uint32_t size = ftell(output) - 8; + + struct wav_hdr hdr; + + hdr.riff[0] = 'R'; + hdr.riff[1] = 'I'; + hdr.riff[2] = 'F'; + hdr.riff[3] = 'F'; + hdr.size = size; + hdr.wave[0] = 'W'; + hdr.wave[1] = 'A'; + hdr.wave[2] = 'V'; + hdr.wave[3] = 'E'; + hdr.fmt[0] = 'f'; + hdr.fmt[1] = 'm'; + hdr.fmt[2] = 't'; + hdr.fmt[3] = ' '; + hdr.block_size = 16; + hdr.audio_format = 1; + hdr.num_channels = 2; + hdr.samplerate = 48000; + hdr.bits_per_sample = 16; + hdr.bytes_per_block = 4; + hdr.bytes_per_sec = 48000 * 4; + + struct wav_chunk chunk; + + chunk.id[0] = 'd'; + chunk.id[1] = 'a'; + chunk.id[2] = 't'; + chunk.id[3] = 'a'; + chunk.size = chunk_size; + + fseek(output, 0, SEEK_SET); + fwrite(&hdr, sizeof(struct wav_hdr), 1, output); + fwrite(&chunk, sizeof(struct wav_chunk), 1, output); + + fflush(output); + fclose(output); + free(spu2); } @@ -652,7 +735,13 @@ void spu2_decode_adpcm_block(struct ps2_spu2* spu2, struct spu2_voice* v) { // printf("spu2: start=%d loop=%d end=%d\n", v->loop_start, v->loop, v->loop_end); int shift_factor = (hdr & 0xf); - int coef_index = ((hdr >> 4) & 0xF); + int coef_index = ((hdr >> 4) & 0x7); + + if (coef_index > 4) { + // printf("spu2: Invalid ADPCM coefficient index %d\n", coef_index); + + coef_index = 4; + } int32_t f0 = ps_adpcm_coefs_i[coef_index][0]; int32_t f1 = ps_adpcm_coefs_i[coef_index][1]; @@ -723,12 +812,14 @@ void adsr_load_attack(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_vo adsr_calculate_values(spu2, v); - // printf("adsr: attack mode=%d shift=%d step=%d dir=%d envx=%d\n", + // printf("adsr: attack mode=%d shift=%d step=%d dir=%d envx=%d cycles=%d level_step=%d\n", // v->adsr_mode, // v->adsr_shift, // v->adsr_step, // v->adsr_dir, - // v->envx + // v->envx, + // CYCLES, + // LEVEL_STEP // ); } @@ -774,8 +865,6 @@ void spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_vo return; } - adsr_calculate_values(spu2, v); - int level = v->envx; level += LEVEL_STEP; @@ -808,12 +897,17 @@ void spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_vo PHASE = ADSR_END; CYCLES = 0; + // v->nax = 0; + // v->ssa = 0; + // v->lsax = 0; + v->envx = 0; v->playing = 0; } } break; case ADSR_END: { + level = 0; v->envx = 0; v->playing = 0; } break; @@ -821,6 +915,8 @@ void spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_vo v->envx = level; + adsr_calculate_values(spu2, v); + CYCLES = v->adsr_cycles_reload; } @@ -835,8 +931,8 @@ void spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_vo #undef MAX struct spu2_sample spu2_get_voice_sample(struct ps2_spu2* spu2, int cr, int vc) { - if (!spu2->c[cr].v[vc].playing) - return silence; + // if (!spu2->c[cr].v[vc].playing) + // return silence; struct spu2_core* c = &spu2->c[cr]; struct spu2_voice* v = &c->v[vc]; @@ -870,22 +966,25 @@ struct spu2_sample spu2_get_voice_sample(struct ps2_spu2* spu2, int cr, int vc) v->lsax = v->nax; v->nax += 8; + v->nax &= 0xfffff; } else if (v->loop_end) { - // printf("spu2: Voice %d loop end at 0x%08x (lsax=%08x ssa=%08x) loop=%d\n", vc, v->nax, v->lsax, v->ssa, v->loop); - v->nax = v->lsax; - - spu2_check_irq(spu2, v->nax); + // if (vc == 18 && cr == 1) + // printf("spu2: Voice %d loop end at 0x%08x (lsax=%08x ssa=%08x) loop=%d end=%d start=%d\n", vc, v->nax, v->lsax, v->ssa, v->loop, v->loop_end, v->loop_start); if (!v->loop) { adsr_load_release(spu2, c, v, vc); v->envx = 0; - v->playing = 0; + } else { + v->nax = v->lsax; } + + spu2_check_irq(spu2, v->nax); } else { spu2_check_irq(spu2, v->nax); v->nax += 8; + v->nax &= 0xfffff; } spu2_decode_adpcm_block(spu2, v); @@ -912,6 +1011,27 @@ struct spu2_sample spu2_get_voice_sample(struct ps2_spu2* spu2, int cr, int vc) out += (g2 * v->s[1]) >> 15; out += (g3 * v->s[0]) >> 15; + // Output voice 1 and 3 to the capture buffers + if (vc == 1) { + uint16_t addr = c->cb_out1_addr + (cr ? 0xc00 : 0x400); + + c->cb_out1_addr = (c->cb_out1_addr + 1) & 0x1ff; + + spu2->ram[addr] = out; + + spu2_check_irq(spu2, addr); + } + + if (vc == 3) { + uint16_t addr = c->cb_out3_addr + (cr ? 0xe00 : 0x600); + + c->cb_out3_addr = (c->cb_out3_addr + 1) & 0x1ff; + + spu2->ram[addr] = out; + + spu2_check_irq(spu2, addr); + } + s.s16[0] = (out * v->voll) >> 15; s.s16[1] = (out * v->volr) >> 15; s.s16[0] = ((int32_t)s.s16[0] * v->envx) >> 15; @@ -967,10 +1087,22 @@ struct spu2_sample ps2_spu2_get_sample(struct ps2_spu2* spu2) { struct spu2_sample c0_adma = spu2_get_adma_sample(spu2, 0); struct spu2_sample c1_adma = spu2_get_adma_sample(spu2, 1); - s.s16[0] += c0_adma.s16[0]; - s.s16[1] += c0_adma.s16[1]; - s.s16[0] += c1_adma.s16[0]; - s.s16[1] += c1_adma.s16[1]; + if (output) { + if (spu2->c[0].adma_playing) { + chunk_size += sizeof(int16_t) * 2; + fwrite(&c0_adma.s16, sizeof(int16_t), 2, output); + } + + if (spu2->c[1].adma_playing) { + chunk_size += sizeof(int16_t) * 2; + fwrite(&c1_adma.s16, sizeof(int16_t), 2, output); + } + } + + // s.s16[0] += c0_adma.s16[0]; + // s.s16[1] += c0_adma.s16[1]; + // s.s16[0] += c1_adma.s16[0]; + // s.s16[1] += c1_adma.s16[1]; for (int i = 0; i < 24; i++) { struct spu2_sample c0 = spu2_get_voice_sample(spu2, 0, i); diff --git a/src/iop/spu2.h b/src/iop/spu2.h index a73c488..1f2dc1d 100644 --- a/src/iop/spu2.h +++ b/src/iop/spu2.h @@ -176,6 +176,10 @@ struct spu2_core { uint32_t adma_ringbuf_write_idx; uint32_t adma_ringbuf_read_idx; int adma_ringbuf_full; + + // Capture buffers + uint16_t cb_out1_addr; + uint16_t cb_out3_addr; }; struct ps2_spu2 { From 49901adb230725ed9269983a3936dcc1b994333c Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:15:33 -0300 Subject: [PATCH 06/26] cdvd: Implement proper EEPROM support --- src/iop/cdvd.c | 206 ++++++++++++++++++++++++++++++++++++------------- src/iop/cdvd.h | 18 +++++ 2 files changed, 172 insertions(+), 52 deletions(-) diff --git a/src/iop/cdvd.c b/src/iop/cdvd.c index 6d2608c..000cb87 100644 --- a/src/iop/cdvd.c +++ b/src/iop/cdvd.c @@ -8,6 +8,32 @@ #include "cdvd.h" +#define printf(fmt,...)(0) + +struct nvram_layout g_spc970_layout = { + .bios_version = 0x00000000, + .config0_offset = 0x00000280, + .config1_offset = 0x00000300, + .config2_offset = 0x00000200, + .console_id_offset = 0x000001C8, + .ilink_id_offset = 0x000001C0, + .modelnum_offset = 0x000001A0, + .regparams_offset = 0x00000180, + .mac_offset = 0x00000198 +}; + +struct nvram_layout g_dragon_layout = { + .bios_version = 0x00000146, + .config0_offset = 0x00000270, + .config1_offset = 0x000002B0, + .config2_offset = 0x00000200, + .console_id_offset = 0x000001F0, + .ilink_id_offset = 0x000001E0, + .modelnum_offset = 0x000001B0, + .regparams_offset = 0x00000180, + .mac_offset = 0x00000198 +}; + static const uint8_t nvram_init_data[1024] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -176,7 +202,7 @@ static inline void cdvd_init_s_fifo(struct ps2_cdvd* cdvd, int size) { cdvd->s_stat &= ~0x40; } -static inline void cdvd_s_mechacon_version(struct ps2_cdvd* cdvd) { +static inline void cdvd_s_mechacon_cmd(struct ps2_cdvd* cdvd) { switch (cdvd->s_params[0]) { case 0x00: { cdvd_init_s_fifo(cdvd, 4); @@ -252,26 +278,31 @@ static inline void cdvd_s_write_nvram(struct ps2_cdvd* cdvd) { static inline void cdvd_s_read_ilink_id(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 9); - uint8_t id[9] = { - 0x00, 0xac, 0xff, 0xff, - 0xff, 0xff, 0xb9, 0x86, - 0x00 - }; + // uint8_t id[9] = { + // 0x00, 0xac, 0xff, 0xff, + // 0xff, 0xff, 0xb9, 0x86, + // 0x00 + // }; - for (int i = 0; i < 9; i++) { - cdvd->s_fifo[i] = id[i]; - } + // for (int i = 0; i < 9; i++) { + // cdvd->s_fifo[i] = id[i]; + // } + + int offset = g_dragon_layout.ilink_id_offset; + + memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8); } static inline void cdvd_s_forbid_dvd(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 5; } -static inline void cdvd_s_read_ilink_model(struct ps2_cdvd* cdvd) { +static inline void cdvd_s_read_model(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 9); - for (int i = 0; i < 9; i++) - cdvd->s_fifo[i] = 0; + int offset = g_dragon_layout.modelnum_offset + cdvd->s_params[0]; + + memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8); } static inline void cdvd_s_certify_boot(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); @@ -302,13 +333,29 @@ static inline void cdvd_s_rc_bypass_ctrl(struct ps2_cdvd* cdvd) { static inline void cdvd_s_open_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); + cdvd->config_rw = cdvd->s_params[0]; + cdvd->config_offset = cdvd->s_params[1]; + cdvd->config_numblocks = cdvd->s_params[2]; + cdvd->config_block_index = 0; + cdvd->s_fifo[0] = 0; } static inline void cdvd_s_read_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 16); - for (int i = 0; i < 16; i++) - cdvd->s_fifo[i] = 0; + int offset = 0; + + switch (cdvd->config_offset) { + case 0: offset = g_dragon_layout.config0_offset; break; + case 1: offset = g_dragon_layout.config1_offset; break; + case 2: offset = g_dragon_layout.config2_offset; break; + + default: offset = g_dragon_layout.config1_offset; break; + } + + offset += (cdvd->config_block_index++) * 16; + + memcpy(cdvd->s_fifo, &cdvd->nvram[offset], 16); } static inline void cdvd_s_write_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); @@ -393,6 +440,13 @@ static inline void cdvd_s_mg_write_data(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; + + printf("mg: Write KELF data params="); + + for (int i = 0; i < cdvd->s_param_index; i++) + printf("%02x ", cdvd->s_params[i]); + + printf("\n"); } static inline void cdvd_s_mechacon_auth_8f(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); @@ -402,20 +456,35 @@ static inline void cdvd_s_mechacon_auth_8f(struct ps2_cdvd* cdvd) { static inline void cdvd_s_mg_write_hdr_start(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); + printf("mg: Write KELF header params="); + + for (int i = 0; i < cdvd->s_param_index; i++) + printf("%02x ", cdvd->s_params[i]); + + printf("\n"); + cdvd->s_fifo[0] = 0; } static inline void cdvd_s_get_region_params(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 15); - //This is basically what PCSX2 returns on a blank NVM/MEC file + for (int i = 5; i < 15; i++) + cdvd->s_fifo[i] = 0; + + int offset = g_dragon_layout.regparams_offset; + cdvd->s_fifo[0] = 0; - cdvd->s_fifo[1] = 1 << 0x3; //MEC encryption zone + cdvd->s_fifo[1] = 1 << 3; // MechaCon encryption zone cdvd->s_fifo[2] = 0; - cdvd->s_fifo[3] = 0x80; //Region Params - cdvd->s_fifo[4] = 0x1; - for (int i = 5; i < 15; i++) - cdvd->s_fifo[i] = 0; + memcpy(&cdvd->s_fifo[3], &cdvd->nvram[offset], 8); + + //This is basically what PCSX2 returns on a blank NVM/MEC file + // cdvd->s_fifo[0] = 0; + // cdvd->s_fifo[1] = 1 << 0x3; //MEC encryption zone + // cdvd->s_fifo[2] = 0; + // cdvd->s_fifo[3] = 0x80; //Region Params + // cdvd->s_fifo[4] = 0x1; } static inline void cdvd_s_remote2_read(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 5); @@ -431,17 +500,17 @@ void cdvd_handle_s_command(struct ps2_cdvd* cdvd, uint8_t cmd) { cdvd->s_cmd = cmd; switch (cmd) { - case 0x03: printf("cdvd: mechacon_version\n"); cdvd_s_mechacon_version(cdvd); break; + case 0x03: printf("cdvd: mechacon_cmd(%02x)\n", cdvd->s_params[0]); cdvd_s_mechacon_cmd(cdvd); break; case 0x05: printf("cdvd: update_sticky_flags\n"); cdvd_s_update_sticky_flags(cdvd); break; // case 0x06: printf("cdvd: tray_ctrl\n"); cdvd_s_tray_ctrl(cdvd); break; - case 0x08: /* printf("cdvd: read_rtc\n"); */ cdvd_s_read_rtc(cdvd); break; + case 0x08: printf("cdvd: read_rtc\n"); cdvd_s_read_rtc(cdvd); break; case 0x09: printf("cdvd: write_rtc\n"); cdvd_s_write_rtc(cdvd); break; case 0x0a: printf("cdvd: read_nvram\n"); cdvd_s_read_nvram(cdvd); break; case 0x0b: printf("cdvd: write_nvram\n"); cdvd_s_write_nvram(cdvd); break; // case 0x0f: printf("cdvd: power_off\n"); cdvd_s_power_off(cdvd); break; case 0x12: printf("cdvd: read_ilink_id\n"); cdvd_s_read_ilink_id(cdvd); break; case 0x15: printf("cdvd: forbid_dvd\n"); cdvd_s_forbid_dvd(cdvd); break; - case 0x17: printf("cdvd: read_ilink_model\n"); cdvd_s_read_ilink_model(cdvd); break; + case 0x17: printf("cdvd: read_model\n"); cdvd_s_read_model(cdvd); break; case 0x1a: printf("cdvd: certify_boot\n"); cdvd_s_certify_boot(cdvd); break; case 0x1b: printf("cdvd: cancel_pwoff_ready\n"); cdvd_s_cancel_pwoff_ready(cdvd); break; @@ -479,13 +548,11 @@ void cdvd_handle_s_command(struct ps2_cdvd* cdvd, uint8_t cmd) { } static inline void cdvd_handle_s_param(struct ps2_cdvd* cdvd, uint8_t param) { - cdvd->s_params[cdvd->s_param_index++] = param; - - if (cdvd->s_param_index > 15) { + if (cdvd->s_param_index == 16) { printf("cdvd: S parameter FIFO overflow\n"); - - // exit(1); } + + cdvd->s_params[cdvd->s_param_index++] = param; } static inline uint8_t cdvd_read_s_response(struct ps2_cdvd* cdvd) { @@ -777,14 +844,14 @@ static inline void cdvd_n_read_cd(struct ps2_cdvd* cdvd) { CDVD_STATUS_SEEKING ); - printf("cdvd: ReadCd lba=%08x count=%08x size=%d cycles=%ld speed=%02x (%p)\n", - cdvd->read_lba, - cdvd->read_count, - cdvd->read_size, - event.cycles, - cdvd->n_params[9], - cdvd->disc->read_sector - ); + // printf("cdvd: ReadCd lba=%08x count=%08x size=%d cycles=%ld speed=%02x (%p)\n", + // cdvd->read_lba, + // cdvd->read_count, + // cdvd->read_size, + // event.cycles, + // cdvd->n_params[9], + // cdvd->disc->read_sector + // ); } static inline void cdvd_n_read_cdda(struct ps2_cdvd* cdvd) { /* Params: @@ -1041,6 +1108,14 @@ void ps2_cdvd_init(struct ps2_cdvd* cdvd, struct ps2_iop_dma* dma, struct ps2_io cdvd->intc = intc; memcpy(cdvd->nvram, nvram_init_data, 1024); + + FILE* file = fopen("nvram.bin", "rb"); + + if (!file) + return; + + fread(cdvd->nvram, 1, 1024, file); + fclose(file); } void ps2_cdvd_destroy(struct ps2_cdvd* cdvd) { @@ -1078,6 +1153,8 @@ int ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay) { cdvd->detected_disc_type = CDVD_DISC_PS2_CD; } + printf("cdvd: Opened \'%s\' (%s)\n", path, cdvd_get_type_name(cdvd->detected_disc_type)); + if (!delay) { cdvd->status &= ~CDVD_STATUS_TRAY_OPEN; @@ -1086,8 +1163,6 @@ int ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay) { return 0; } - printf("cdvd: Opened \'%s\' (%s)\n", path, cdvd_get_type_name(cdvd->detected_disc_type)); - switch (cdvd->detected_disc_type) { case CDVD_DISC_PS2_CD: case CDVD_DISC_PS2_CDDA: @@ -1161,43 +1236,44 @@ uint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr) { // printf("cdvd: read %08x\n", addr); switch (addr) { - case 0x1F402004: /* printf("cdvd: read n_cmd %x\n", cdvd->n_cmd); */ return cdvd->n_cmd; - case 0x1F402005: /* printf("cdvd: read n_stat %x\n", cdvd->n_stat); */ return cdvd->n_stat; + case 0x1F402004: printf("cdvd: read n_cmd %x\n", cdvd->n_cmd); return cdvd->n_cmd; + case 0x1F402005: printf("cdvd: read n_stat %x\n", cdvd->n_stat); return cdvd->n_stat; // case 0x1F402005: (W) - case 0x1F402006: /* printf("cdvd: read error %x\n", 0); */ return 0; //cdvd->error; + case 0x1F402006: printf("cdvd: read error %x\n", 0); return 0; //cdvd->error; // case 0x1F402007: (W) - case 0x1F402008: /* printf("cdvd: read i_stat %x\n", cdvd->i_stat); */ return cdvd->i_stat; - case 0x1F40200A: /* printf("cdvd: read status %x\n", cdvd->status); */ return cdvd->status; - case 0x1F40200B: /* printf("cdvd: read sticky_status %x\n", cdvd->sticky_status); */ return cdvd->sticky_status; - case 0x1F40200F: /* printf("cdvd: read disc_type %x\n", cdvd->disc_type); */ return cdvd->disc_type; - case 0x1F402013: /* printf("cdvd: read speed %x\n", cdvd_read_speed(cdvd)); return */ cdvd_read_speed(cdvd); - case 0x1F402016: /* printf("cdvd: read s_cmd %x\n", cdvd->s_cmd); */ return cdvd->s_cmd; - case 0x1F402017: /* printf("cdvd: read s_stat %x\n", cdvd->s_stat); */ return cdvd->s_stat; + case 0x1F402008: printf("cdvd: read i_stat %x\n", cdvd->i_stat); return cdvd->i_stat; + case 0x1F40200A: printf("cdvd: read status %x\n", cdvd->status); return cdvd->status; + case 0x1F40200B: printf("cdvd: read sticky_status %x\n", cdvd->sticky_status); return cdvd->sticky_status; + case 0x1F40200F: printf("cdvd: read disc_type %x\n", cdvd->disc_type); return cdvd->disc_type; + case 0x1F402013: printf("cdvd: read speed %x\n", cdvd_read_speed(cdvd)); return cdvd_read_speed(cdvd); + case 0x1F402016: printf("cdvd: read s_cmd %x\n", cdvd->s_cmd); return cdvd->s_cmd; + case 0x1F402017: printf("cdvd: read s_stat %x\n", cdvd->s_stat); return cdvd->s_stat; // case 0x1F402017: (W); - case 0x1F402018: return cdvd_read_s_response(cdvd); // { int r = cdvd_read_s_response(cdvd); printf("cdvd: read s_response %x\n", r); return r; } + case 0x1F402018: { int r = cdvd_read_s_response(cdvd); printf("cdvd: read s_response %x\n", r); return r; } case 0x1F402020: case 0x1F402021: case 0x1F402022: case 0x1F402023: case 0x1F402024: - // printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402020, cdvd->cdkey[addr - 0x1F402020]); + printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402020, cdvd->cdkey[addr - 0x1F402020]); return cdvd->cdkey[addr - 0x1F402020]; case 0x1F402028: case 0x1F402029: case 0x1F40202A: case 0x1F40202B: case 0x1F40202C: - // printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402023, cdvd->cdkey[addr - 0x1F402023]); + printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402023, cdvd->cdkey[addr - 0x1F402023]); return cdvd->cdkey[addr - 0x1F402023]; case 0x1F402030: case 0x1F402031: case 0x1F402032: case 0x1F402033: case 0x1F402034: - // printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402026, cdvd->cdkey[addr - 0x1F402026]); + printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402026, cdvd->cdkey[addr - 0x1F402026]); return cdvd->cdkey[addr - 0x1F402026]; case 0x1F402038: + printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402038, cdvd->cdkey[15]); return cdvd->cdkey[15]; } @@ -1226,4 +1302,30 @@ void ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data) { } return; +} + +void ps2_cdvd_reset(struct ps2_cdvd* cdvd) { + cdvd->n_stat = 0x4c; + // cdvd->s_stat = CDVD_S_STATUS_NO_DATA; + // cdvd->sticky_status = 0x1e; + // cdvd->s_cmd = 0; + // cdvd->s_stat = 0; + // cdvd->n_cmd = 0; + // cdvd->i_stat = 0; + + // cdvd->n_param_index = 0; + // cdvd->s_param_index = 0; + // cdvd->s_fifo_index = 0; + // cdvd->s_fifo_size = 0; + // cdvd->buf_size = 0; + + cdvd->read_lba = 0x150; + cdvd->read_count = 0; + cdvd->read_size = 0; + cdvd->read_speed = 0; + + cdvd->config_rw = 0; + cdvd->config_offset = 0; + cdvd->config_numblocks = 0; + cdvd->config_block_index = 0; } \ No newline at end of file diff --git a/src/iop/cdvd.h b/src/iop/cdvd.h index c50b014..fca758b 100644 --- a/src/iop/cdvd.h +++ b/src/iop/cdvd.h @@ -71,6 +71,18 @@ extern "C" { #define CDVD_CD_SS_2048 2048 #define CDVD_DVD_SS 2064 +struct nvram_layout { + uint32_t bios_version; // bios version that this eeprom layout is for + int32_t config0_offset; // offset of 1st config block + int32_t config1_offset; // offset of 2nd config block + int32_t config2_offset; // offset of 3rd config block + int32_t console_id_offset; // offset of console id (?) + int32_t ilink_id_offset; // offset of ilink id (ilink mac address) + int32_t modelnum_offset; // offset of ps2 model number (eg "SCPH-70002") + int32_t regparams_offset; // offset of RegionParams for PStwo + int32_t mac_offset; // offset of MAC address on PStwo +}; + struct ps2_cdvd { uint8_t n_cmd; uint8_t n_stat; @@ -118,6 +130,11 @@ struct ps2_cdvd { struct sched_state* sched; uint64_t layer2_lba; + uint32_t config_rw; + uint32_t config_offset; + uint32_t config_numblocks; + uint32_t config_block_index; + // To-do: // void (*poweroff_handler)(void* udata) // void (*trayctrl_handler)(void* udata, uint8_t ctrl) @@ -131,6 +148,7 @@ void ps2_cdvd_close(struct ps2_cdvd* cdvd); void ps2_cdvd_power_off(struct ps2_cdvd* cdvd); uint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr); void ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data); +void ps2_cdvd_reset(struct ps2_cdvd* cdvd); #undef ALIGNED_U32 From 986ee6044fef7161df126832640b0719421cda92 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:16:36 -0300 Subject: [PATCH 07/26] renderer: Implement debug stats --- src/gs/renderer/null.cpp | 10 ++++++++- src/gs/renderer/null.hpp | 1 + src/gs/renderer/renderer.cpp | 6 +++++ src/gs/renderer/renderer.hpp | 14 ++++++++++++ src/gs/renderer/software.cpp | 8 ++++++- src/gs/renderer/software.hpp | 1 + src/gs/renderer/software_thread.cpp | 34 ++++++++++++++++++++++++++++- src/gs/renderer/software_thread.hpp | 4 ++++ 8 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/gs/renderer/null.cpp b/src/gs/renderer/null.cpp index 01da8bd..ec9acdb 100644 --- a/src/gs/renderer/null.cpp +++ b/src/gs/renderer/null.cpp @@ -1,3 +1,5 @@ +#include "renderer.hpp" + #include "null.hpp" void null_init(void* ctx, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device) {} @@ -35,4 +37,10 @@ extern "C" void null_transfer_read(struct ps2_gs* gs, void* udata) {} void null_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} void null_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass) {} -void null_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} \ No newline at end of file +void null_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} + +renderer_stats* null_get_debug_stats(void* ctx) { + static renderer_stats stats = {}; + + return &stats; +} \ No newline at end of file diff --git a/src/gs/renderer/null.hpp b/src/gs/renderer/null.hpp index bad6fa9..0fe173e 100644 --- a/src/gs/renderer/null.hpp +++ b/src/gs/renderer/null.hpp @@ -18,6 +18,7 @@ void null_get_display_format(void* ctx, int* fmt); void null_get_interlace_mode(void* ctx, int* mode); void null_set_window_rect(void* ctx, int x, int y, int w, int h); void* null_get_buffer_data(void* ctx, int* w, int* h, int* bpp); +renderer_stats* null_get_debug_stats(void* ctx); const char* null_get_name(void* ctx); void null_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer); void null_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass); diff --git a/src/gs/renderer/renderer.cpp b/src/gs/renderer/renderer.cpp index 8dcd7a3..fa19743 100644 --- a/src/gs/renderer/renderer.cpp +++ b/src/gs/renderer/renderer.cpp @@ -24,6 +24,7 @@ void renderer_init_null(renderer_state* renderer, struct ps2_gs* gs, SDL_Window* renderer->set_window_rect = null_set_window_rect; renderer->get_buffer_data = null_get_buffer_data; renderer->get_name = null_get_name; + renderer->get_debug_stats = null_get_debug_stats; renderer->begin_render = null_begin_render; renderer->render = null_render; renderer->end_render = null_end_render; @@ -54,6 +55,7 @@ void renderer_init_software(renderer_state* renderer, struct ps2_gs* gs, SDL_Win renderer->set_window_rect = software_set_window_rect; renderer->get_buffer_data = software_get_buffer_data; renderer->get_name = software_get_name; + renderer->get_debug_stats = software_get_debug_stats; renderer->begin_render = software_begin_render; renderer->render = software_render; renderer->end_render = software_end_render; @@ -83,6 +85,7 @@ void renderer_init_software_thread(renderer_state* renderer, struct ps2_gs* gs, renderer->get_interlace_mode = software_thread_get_interlace_mode; renderer->set_window_rect = software_thread_set_window_rect; renderer->get_buffer_data = software_thread_get_buffer_data; + renderer->get_debug_stats = software_thread_get_debug_stats; renderer->get_name = software_thread_get_name; renderer->begin_render = software_thread_begin_render; renderer->render = software_thread_render; @@ -158,6 +161,9 @@ void* renderer_get_buffer_data(renderer_state* renderer, int* w, int* h, int* bp const char* renderer_get_name(renderer_state* renderer) { return renderer->get_name(renderer->udata); } +renderer_stats* renderer_get_debug_stats(renderer_state* renderer) { + return renderer->get_debug_stats(renderer->udata); +} void renderer_destroy(renderer_state* renderer) { if (renderer->udata) diff --git a/src/gs/renderer/renderer.hpp b/src/gs/renderer/renderer.hpp index 78824d7..c478152 100644 --- a/src/gs/renderer/renderer.hpp +++ b/src/gs/renderer/renderer.hpp @@ -27,6 +27,18 @@ enum : int { RENDERER_SOFTWARE_THREAD }; +struct renderer_stats { + unsigned int primitives = 0; + unsigned int triangles = 0; + unsigned int lines = 0; + unsigned int points = 0; + unsigned int sprites = 0; + unsigned int texture_uploads = 0; + unsigned int texture_blits = 0; + unsigned int frames_rendered = 0; + float frame_latency = 0.0f; +}; + struct renderer_state { struct ps2_gs* gs = nullptr; void* udata = nullptr; @@ -46,6 +58,7 @@ struct renderer_state { void (*set_render_context)(void*, SDL_GPUCommandBuffer*, SDL_GPURenderPass*); void* (*get_buffer_data)(void*, int*, int*, int*) = nullptr; const char* (*get_name)(void*) = nullptr; + renderer_stats* (*get_debug_stats)(void*) = nullptr; // Rendering interface void (*begin_render)(void*, SDL_GPUCommandBuffer*); @@ -69,6 +82,7 @@ void renderer_get_display_format(renderer_state* renderer, int* fmt); void renderer_get_interlace_mode(renderer_state* renderer, int* mode); void renderer_set_window_rect(renderer_state* renderer, int x, int y, int w, int h); void* renderer_get_buffer_data(renderer_state* renderer, int* w, int* h, int* bpp); +renderer_stats* renderer_get_debug_stats(renderer_state* renderer); const char* renderer_get_name(renderer_state* renderer); void renderer_destroy(renderer_state* renderer); diff --git a/src/gs/renderer/software.cpp b/src/gs/renderer/software.cpp index b4feed0..25b175d 100644 --- a/src/gs/renderer/software.cpp +++ b/src/gs/renderer/software.cpp @@ -8,6 +8,8 @@ #include +renderer_stats dummy = {}; + int software_psmct32_block[] = { 0 , 1 , 4 , 5 , 16, 17, 20, 21, 2 , 3 , 6 , 7 , 18, 19, 22, 23, @@ -1793,4 +1795,8 @@ extern "C" void software_transfer_read(struct ps2_gs* gs, void* udata) { void software_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} void software_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass) {} -void software_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} \ No newline at end of file +void software_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {} + +renderer_stats* software_get_debug_stats(void* udata) { + return &dummy; +} \ No newline at end of file diff --git a/src/gs/renderer/software.hpp b/src/gs/renderer/software.hpp index bf6a715..9bfd660 100644 --- a/src/gs/renderer/software.hpp +++ b/src/gs/renderer/software.hpp @@ -50,6 +50,7 @@ void software_get_display_format(void* ctx, int* fmt); void software_get_interlace_mode(void* ctx, int* mode); void software_set_window_rect(void* udata, int x, int y, int w, int h); void* software_get_buffer_data(void* udata, int* w, int* h, int* bpp); +renderer_stats* software_get_debug_stats(void* ctx); const char* software_get_name(void* ctx); void software_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer); void software_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass); diff --git a/src/gs/renderer/software_thread.cpp b/src/gs/renderer/software_thread.cpp index 9154df0..60ffc76 100644 --- a/src/gs/renderer/software_thread.cpp +++ b/src/gs/renderer/software_thread.cpp @@ -2727,6 +2727,8 @@ void transfer_start(struct ps2_gs* gs, void* udata) { if (ctx->xdir == 2) { ctx->render_mtx.lock(); + ctx->stats.texture_blits++; + software_thread_vram_blit(gs, ctx); ctx->render_mtx.unlock(); @@ -2734,6 +2736,8 @@ void transfer_start(struct ps2_gs* gs, void* udata) { printf("gs: Read transfer requested\n"); // exit(1); + } else { + ctx->stats.texture_uploads++; } } @@ -2755,6 +2759,9 @@ void transfer_read(struct ps2_gs* gs, void* udata) { extern "C" void software_thread_render_point(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; + ctx->stats.points++; + ctx->stats.primitives++; + ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); @@ -2768,6 +2775,9 @@ extern "C" void software_thread_render_point(struct ps2_gs* gs, void* udata) { extern "C" void software_thread_render_line(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; + ctx->stats.lines++; + ctx->stats.primitives++; + ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); @@ -2781,6 +2791,9 @@ extern "C" void software_thread_render_line(struct ps2_gs* gs, void* udata) { extern "C" void software_thread_render_triangle(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; + ctx->stats.triangles++; + ctx->stats.primitives++; + ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); @@ -2794,6 +2807,9 @@ extern "C" void software_thread_render_triangle(struct ps2_gs* gs, void* udata) extern "C" void software_thread_render_sprite(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; + ctx->stats.sprites++; + ctx->stats.primitives++; + ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); @@ -2978,7 +2994,7 @@ void software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buf if (ctx->gs->smode2 & 2) { gs_blit_dispfb_deinterlace_frame(ctx, dfb); } else { - gs_blit_dispfb_deinterlace_field(ctx, dfb); + gs_blit_dispfb_no_deinterlace(ctx, dfb); } } else { gs_blit_dispfb_no_deinterlace(ctx, dfb); @@ -3159,6 +3175,16 @@ void software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, S if (!ctx->texture) return; + ctx->stats.frames_rendered++; + ctx->last_frame_stats = ctx->stats; + ctx->stats.lines = 0; + ctx->stats.points = 0; + ctx->stats.triangles = 0; + ctx->stats.sprites = 0; + ctx->stats.primitives = 0; + ctx->stats.texture_uploads = 0; + ctx->stats.texture_blits = 0; + SDL_BindGPUGraphicsPipeline(render_pass, ctx->pipeline); // bind the vertex buffer @@ -3186,4 +3212,10 @@ void software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, S void software_thread_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) { // Nothing for now +} + +renderer_stats* software_thread_get_debug_stats(void* udata) { + software_thread_state* ctx = (software_thread_state*)udata; + + return &ctx->last_frame_stats; } \ No newline at end of file diff --git a/src/gs/renderer/software_thread.hpp b/src/gs/renderer/software_thread.hpp index 05c283e..616a0f9 100644 --- a/src/gs/renderer/software_thread.hpp +++ b/src/gs/renderer/software_thread.hpp @@ -78,6 +78,9 @@ struct software_thread_state { unsigned int frame = 0; uint32_t* buf = nullptr; + + renderer_stats stats = { 0 }; + renderer_stats last_frame_stats = { 0 }; }; void software_thread_init(void* udata, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device); @@ -93,6 +96,7 @@ void software_thread_get_display_format(void* udata, int* fmt); void software_thread_get_interlace_mode(void* udata, int* mode); void software_thread_set_window_rect(void* udata, int x, int y, int w, int h); void* software_thread_get_buffer_data(void* udata, int* w, int* h, int* bpp); +renderer_stats* software_thread_get_debug_stats(void* udata); const char* software_thread_get_name(void* udata); void software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer); void software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass); From d83803d816d5a72ddd8f80175e42d9b69dbf54c7 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:17:02 -0300 Subject: [PATCH 08/26] gs: Implement proper frame timings --- src/gs/gs.c | 13 +++++++++++-- src/gs/gs.h | 6 ++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/gs/gs.c b/src/gs/gs.c index cd7116b..422f413 100644 --- a/src/gs/gs.c +++ b/src/gs/gs.c @@ -70,14 +70,14 @@ void gs_handle_vblank_in(void* udata, int overshoot) { struct sched_event vblank_out_event; vblank_out_event.callback = gs_handle_vblank_out; - vblank_out_event.cycles = GS_VBLANK_NTSC; + vblank_out_event.cycles = GS_VBLANK_NTSC; vblank_out_event.name = "Vblank out event"; vblank_out_event.udata = gs; struct sched_event field_flip_event; field_flip_event.callback = gs_flip_field; - field_flip_event.cycles = 30000 * 4; + field_flip_event.cycles = 65622; field_flip_event.name = "Field flip event"; field_flip_event.udata = gs; @@ -319,6 +319,8 @@ uint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr) { // Hack toggle between FIFO empty and FIFO "Neither Empty nor Almost Full" gs->csr ^= 0x4000; + addr = (addr & 0xfffff000) | (addr & 0x3ff); + switch (addr) { case 0x12000000: return gs->csr | 0x551b0000; case 0x12000010: return gs->csr | 0x551b0000; @@ -339,6 +341,8 @@ uint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr) { case 0x12001010: return gs->csr | 0x551b0000; case 0x12001040: return gs->csr | 0x551b0000; case 0x12001080: return gs->siglblid; + + case 0x12001001: return (gs->csr >> 8) & 0xff; } printf("gs: Unhandled read from %08x\n", addr); @@ -376,6 +380,11 @@ void ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data) { case 0x120000D0: gs->extwrite = data; return; case 0x120000E0: gs->bgcolor = data; return; case 0x12001000: { + if (data & 8) { + // Game is requesting vsync + gs->vblank |= 1; + } + gs->csr = (gs->csr & 0xfffffe00) | (gs->csr & ~(data & 0xf)); if (gs->signal_pending && ((gs->csr & 1) == 0)) { diff --git a/src/gs/gs.h b/src/gs/gs.h index e8c6da6..10d660d 100644 --- a/src/gs/gs.h +++ b/src/gs/gs.h @@ -118,8 +118,10 @@ extern "C" { // EE clock: 294.912 MHz, 294912000 clocks/s // 294912000/60=4915200 clocks/frame -#define GS_FRAME_NTSC 4497600 // (240 * 9370) -#define GS_VBLANK_NTSC 417600 // (22 * 9370) +// #define GS_FRAME_NTSC (4497600) // (240 * 9370) +// #define GS_VBLANK_NTSC (417600) // (22 * 9370) +#define GS_FRAME_NTSC 4489019 // (240 * 9370) +#define GS_VBLANK_NTSC 431096 // (22 * 9370) #define GS_FRAME_PAL (286 * 9476) #define GS_VBLANK_PAL (26 * 9476) From 523e0258bd222af06c2df7ed4d34fe83042a3005 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:18:41 -0300 Subject: [PATCH 09/26] ee: Implement remaining MMI instructions ee: Add support for per-instruction timings --- src/ee/ee_cached.cpp | 1079 +++++++++++++++++++++++++----------------- src/ee/ee_def.hpp | 21 +- 2 files changed, 672 insertions(+), 428 deletions(-) diff --git a/src/ee/ee_cached.cpp b/src/ee/ee_cached.cpp index 0d58b9c..3a6c56f 100644 --- a/src/ee/ee_cached.cpp +++ b/src/ee/ee_cached.cpp @@ -2,8 +2,9 @@ #include #include #include -#include #include +#include +#include #include #ifdef _EE_USE_INTRINSICS @@ -59,6 +60,16 @@ static inline int16_t saturate16(int32_t word) { } } +static inline int32_t saturate32(int64_t word) { + if (word > (int32_t)0x7FFFFFFF) { + return 0x7FFFFFFF; + } else if (word < (int32_t)0x80000000) { + return 0x80000000; + } else { + return (int32_t)word; + } +} + #ifdef _EE_USE_INTRINSICS static inline __m128i _mm_adds_epi32(__m128i a, __m128i b) { const __m128i m = _mm_set1_epi32(0x7fffffff); @@ -1682,9 +1693,78 @@ static inline void ee_i_pcpyud(struct ee_state* ee, const ee_instruction& i) { ee->r[d].u64[0] = rs.u64[1]; ee->r[d].u64[1] = rt.u64[1]; } -static inline void ee_i_pdivbw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pdivbw unimplemented\n"); exit(1); } -static inline void ee_i_pdivuw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pdivuw unimplemented\n"); exit(1); } -static inline void ee_i_pdivw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pdivw unimplemented\n"); exit(1); } +static inline void ee_i_pdivbw(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + + for (int i = 0; i < 4; i++) { + if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u16[0] == 0xffff) { + ee->lo.u32[i] = 0x80000000; + ee->hi.u32[i] = 0; + } else if (ee->r[t].u16[0] != 0) { + ee->lo.u32[i] = ee->r[s].s32[i] / ee->r[t].s16[0]; + ee->hi.u32[i] = ee->r[s].s32[i] % ee->r[t].s16[0]; + } else { + if (ee->r[s].s32[i] < 0) { + ee->lo.u32[i] = 1; + } else { + ee->lo.u32[i] = -1; + } + + ee->hi.u32[i] = ee->r[s].s32[i]; + } + } + + // ee->hi.u32[0] = SE3216(ee->r[s].s32[0] % ee->r[t].s16[0]); + // ee->hi.u32[1] = SE3216(ee->r[s].s32[1] % ee->r[t].s16[0]); + // ee->hi.u32[2] = SE3216(ee->r[s].s32[2] % ee->r[t].s16[0]); + // ee->hi.u32[3] = SE3216(ee->r[s].s32[3] % ee->r[t].s16[0]); + // ee->lo.u32[0] = ee->r[s].s32[0] / ee->r[t].s16[0]; + // ee->lo.u32[1] = ee->r[s].s32[1] / ee->r[t].s16[0]; + // ee->lo.u32[2] = ee->r[s].s32[2] / ee->r[t].s16[0]; + // ee->lo.u32[3] = ee->r[s].s32[3] / ee->r[t].s16[0]; +} +static inline void ee_i_pdivuw(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + + for (int i = 0; i < 4; i += 2) { + if (ee->r[t].u32[i] != 0) { + ee->lo.u64[i/2] = SE6432(ee->r[s].u32[i] / ee->r[t].u32[i]); + ee->hi.u64[i/2] = SE6432(ee->r[s].u32[i] % ee->r[t].u32[i]); + } else { + ee->lo.u64[i/2] = (int64_t)-1; + ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i]; + } + } +} +static inline void ee_i_pdivw(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + + for (int i = 0; i < 4; i += 2) { + if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u32[i] == 0xffffffff) { + ee->lo.u64[i/2] = (int64_t)(int32_t)0x80000000; + ee->hi.u64[i/2] = 0; + } else if (ee->r[t].u32[i] != 0) { + ee->lo.u64[i/2] = SE6432(ee->r[s].s32[i] / ee->r[t].s32[i]); + ee->hi.u64[i/2] = SE6432(ee->r[s].s32[i] % ee->r[t].s32[i]); + } else { + if (ee->r[s].s32[i] < 0) { + ee->lo.u64[i/2] = 1; + } else { + ee->lo.u64[i/2] = (int64_t)-1; + } + + ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i]; + } + } + + // ee->hi.u64[0] = SE6432(ee->r[s].s32[0] % ee->r[t].s32[0]); + // ee->hi.u64[1] = SE6432(ee->r[s].s32[2] % ee->r[t].s32[2]); + // ee->lo.u64[0] = SE6432(ee->r[s].s32[0] / ee->r[t].s32[0]); + // ee->lo.u64[1] = SE6432(ee->r[s].s32[2] / ee->r[t].s32[2]); +} static inline void ee_i_pexch(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; @@ -1835,16 +1915,47 @@ static inline void ee_i_phmadh(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; - ee->r[d].u32[0] = ((int16_t)rs.u16[1] * (int16_t)rt.u16[1]) + ((int16_t)rs.u16[0] * (int16_t)rt.u16[0]); - ee->r[d].u32[1] = ((int16_t)rs.u16[3] * (int16_t)rt.u16[3]) + ((int16_t)rs.u16[2] * (int16_t)rt.u16[2]); - ee->r[d].u32[2] = ((int16_t)rs.u16[5] * (int16_t)rt.u16[5]) + ((int16_t)rs.u16[4] * (int16_t)rt.u16[4]); - ee->r[d].u32[3] = ((int16_t)rs.u16[7] * (int16_t)rt.u16[7]) + ((int16_t)rs.u16[6] * (int16_t)rt.u16[6]); + int32_t r0 = (int32_t)(rs.s16[1] * rt.s16[1]); + int32_t r1 = (int32_t)(rs.s16[3] * rt.s16[3]); + int32_t r2 = (int32_t)(rs.s16[5] * rt.s16[5]); + int32_t r3 = (int32_t)(rs.s16[7] * rt.s16[7]); + + ee->r[d].u32[0] = r0 + ((int16_t)rs.u16[0] * (int16_t)rt.u16[0]); + ee->r[d].u32[1] = r1 + ((int16_t)rs.u16[2] * (int16_t)rt.u16[2]); + ee->r[d].u32[2] = r2 + ((int16_t)rs.u16[4] * (int16_t)rt.u16[4]); + ee->r[d].u32[3] = r3 + ((int16_t)rs.u16[6] * (int16_t)rt.u16[6]); + ee->lo.u32[0] = ee->r[d].u32[0]; + ee->lo.u32[1] = r0; + ee->hi.u32[0] = ee->r[d].u32[1]; + ee->hi.u32[1] = r1; + ee->lo.u32[2] = ee->r[d].u32[2]; + ee->lo.u32[3] = r2; + ee->hi.u32[2] = ee->r[d].u32[3]; + ee->hi.u32[3] = r3; +} +static inline void ee_i_phmsbh(struct ee_state* ee, const ee_instruction& i) { + int d = EE_D_RD; + int s = EE_D_RS; + int t = EE_D_RT; + + int32_t r1 = ee->r[s].s16[1] * ee->r[t].s16[1]; + int32_t r3 = ee->r[s].s16[3] * ee->r[t].s16[3]; + int32_t r5 = ee->r[s].s16[5] * ee->r[t].s16[5]; + int32_t r7 = ee->r[s].s16[7] * ee->r[t].s16[7]; + + ee->r[d].u32[0] = (int32_t)(r1 - (ee->r[s].s16[0] * ee->r[t].s16[0])); + ee->r[d].u32[1] = (int32_t)(r3 - (ee->r[s].s16[2] * ee->r[t].s16[2])); + ee->r[d].u32[2] = (int32_t)(r5 - (ee->r[s].s16[4] * ee->r[t].s16[4])); + ee->r[d].u32[3] = (int32_t)(r7 - (ee->r[s].s16[6] * ee->r[t].s16[6])); ee->lo.u32[0] = ee->r[d].u32[0]; + ee->lo.u32[1] = ~r1; ee->hi.u32[0] = ee->r[d].u32[1]; + ee->hi.u32[1] = ~r3; ee->lo.u32[2] = ee->r[d].u32[2]; + ee->lo.u32[3] = ~r5; ee->hi.u32[2] = ee->r[d].u32[3]; + ee->hi.u32[3] = ~r7; } -static inline void ee_i_phmsbh(struct ee_state* ee, const ee_instruction& i) { printf("ee: phmsbh unimplemented\n"); exit(1); } static inline void ee_i_pinteh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; @@ -1920,8 +2031,36 @@ static inline void ee_i_pmaddh(struct ee_state* ee, const ee_instruction& i) { ee->lo.u32[2] = ee->r[d].u32[2]; ee->hi.u32[2] = ee->r[d].u32[3]; } -static inline void ee_i_pmadduw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmadduw unimplemented\n"); exit(1); } -static inline void ee_i_pmaddw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmaddw unimplemented\n"); exit(1); } +static inline void ee_i_pmadduw(struct ee_state* ee, const ee_instruction& i) { + int d = EE_D_RD; + int s = EE_D_RS; + int t = EE_D_RT; + + uint64_t r0 = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0]; + uint64_t r1 = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2]; + + ee->r[d].u64[0] = r0 + ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]); + ee->r[d].u64[1] = r1 + ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]); + ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); + ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); + ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); + ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); +} +static inline void ee_i_pmaddw(struct ee_state* ee, const ee_instruction& i) { + int d = EE_D_RD; + int s = EE_D_RS; + int t = EE_D_RT; + + uint64_t r0 = (int64_t)ee->r[s].s32[0] * (int64_t)ee->r[t].s32[0]; + uint64_t r1 = (int64_t)ee->r[s].s32[2] * (int64_t)ee->r[t].s32[2]; + + ee->r[d].u64[0] = r0 + ((((uint64_t)ee->hi.u32[0]) << 32) | (uint64_t)ee->lo.u32[0]); + ee->r[d].u64[1] = r1 + ((((uint64_t)ee->hi.u32[2]) << 32) | (uint64_t)ee->lo.u32[2]); + ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); + ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); + ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); + ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); +} static inline void ee_i_pmaxh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; @@ -1965,7 +2104,12 @@ static inline void ee_i_pmfhluw(struct ee_state* ee, const ee_instruction& i) { ee->r[d].u32[2] = ee->lo.u32[3]; ee->r[d].u32[3] = ee->hi.u32[3]; } -static inline void ee_i_pmfhlslw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmfhlslw unimplemented\n"); exit(1); } +static inline void ee_i_pmfhlslw(struct ee_state* ee, const ee_instruction& i) { + int d = EE_D_RD; + + ee->r[d].u64[0] = SE6432(saturate32(((uint64_t)ee->lo.u32[0]) | (ee->hi.u64[0] << 32))); + ee->r[d].u64[1] = SE6432(saturate32(((uint64_t)ee->lo.u32[2]) | (ee->hi.u64[1] << 32))); +} static inline void ee_i_pmfhllh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; @@ -2013,17 +2157,91 @@ static inline void ee_i_pminw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; - ee->r[d].u32[0] = ((int32_t)ee->r[s].u32[0] < (int32_t)ee->r[t].u32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0]; - ee->r[d].u32[1] = ((int32_t)ee->r[s].u32[1] < (int32_t)ee->r[t].u32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1]; - ee->r[d].u32[2] = ((int32_t)ee->r[s].u32[2] < (int32_t)ee->r[t].u32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2]; - ee->r[d].u32[3] = ((int32_t)ee->r[s].u32[3] < (int32_t)ee->r[t].u32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3]; + ee->r[d].u32[0] = (ee->r[s].s32[0] < ee->r[t].s32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0]; + ee->r[d].u32[1] = (ee->r[s].s32[1] < ee->r[t].s32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1]; + ee->r[d].u32[2] = (ee->r[s].s32[2] < ee->r[t].s32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2]; + ee->r[d].u32[3] = (ee->r[s].s32[3] < ee->r[t].s32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3]; +} +static inline void ee_i_pmsubh(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + int d = EE_D_RD; + + int32_t r0 = (int32_t)ee->r[s].s16[0] * (int32_t)ee->r[t].s16[0]; + int32_t r1 = (int32_t)ee->r[s].s16[1] * (int32_t)ee->r[t].s16[1]; + int32_t r2 = (int32_t)ee->r[s].s16[2] * (int32_t)ee->r[t].s16[2]; + int32_t r3 = (int32_t)ee->r[s].s16[3] * (int32_t)ee->r[t].s16[3]; + int32_t r4 = (int32_t)ee->r[s].s16[4] * (int32_t)ee->r[t].s16[4]; + int32_t r5 = (int32_t)ee->r[s].s16[5] * (int32_t)ee->r[t].s16[5]; + int32_t r6 = (int32_t)ee->r[s].s16[6] * (int32_t)ee->r[t].s16[6]; + int32_t r7 = (int32_t)ee->r[s].s16[7] * (int32_t)ee->r[t].s16[7]; + + ee->r[d].u32[0] = ee->lo.u32[0] - r0; + ee->r[d].u32[1] = ee->hi.u32[0] - r2; + ee->r[d].u32[2] = ee->lo.u32[2] - r4; + ee->r[d].u32[3] = ee->hi.u32[2] - r6; + ee->lo.u32[0] = ee->r[d].u32[0]; + ee->hi.u32[0] = ee->r[d].u32[1]; + ee->lo.u32[2] = ee->r[d].u32[2]; + ee->hi.u32[2] = ee->r[d].u32[3]; + + ee->lo.u32[1] = ee->lo.u32[1] - r1; + ee->lo.u32[3] = ee->lo.u32[3] - r5; + ee->hi.u32[1] = ee->hi.u32[1] - r3; + ee->hi.u32[3] = ee->hi.u32[3] - r7; +} +static inline void ee_i_pmsubw(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + int d = EE_D_RD; + + // int64_t r0 = ee->r[s].s32[0] * ee->r[t].s32[0]; + // int64_t r1 = ee->r[s].s32[2] * ee->r[t].s32[2]; + + // ee->r[d].u64[0] = ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]) - r0; + // ee->r[d].u64[1] = ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]) - r0; + + // ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); + // ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); + // ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); + // ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); + + int64_t op1 = (int64_t)ee->r[s].s32[0]; + int64_t op2 = (int64_t)ee->r[t].s32[0]; + int64_t result = op1 * op2; + int64_t result2 = ((int64_t)ee->hi.s32[0] << 32) - result; + + result2 = (int32_t)(result2 / 4294967295); + + ee->lo.s64[0] = ee->lo.s32[0] - (int32_t)(result & 0xffffffff); + ee->hi.s64[0] = (int32_t)result2; + + op1 = (int64_t)ee->r[s].s32[2]; + op2 = (int64_t)ee->r[t].s32[2]; + result = op1 * op2; + + result2 = ((int64_t)ee->hi.s32[2] << 32) - result; + result2 = (int32_t)(result2 / 4294967295); + + ee->lo.s64[1] = ee->lo.s32[2] - (int32_t)(result & 0xffffffff); + ee->hi.s64[1] = (int32_t)result2; + + ee->r[d].u32[0] = ee->lo.u32[0]; + ee->r[d].u32[1] = ee->hi.u32[0]; + ee->r[d].u32[2] = ee->lo.u32[2]; + ee->r[d].u32[3] = ee->hi.u32[2]; } -static inline void ee_i_pmsubh(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmsubh unimplemented\n"); exit(1); } -static inline void ee_i_pmsubw(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmsubw unimplemented\n"); exit(1); } static inline void ee_i_pmthi(struct ee_state* ee, const ee_instruction& i) { ee->hi = ee->r[EE_D_RS]; } -static inline void ee_i_pmthl(struct ee_state* ee, const ee_instruction& i) { printf("ee: pmthl unimplemented\n"); exit(1); } +static inline void ee_i_pmthl(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + + ee->lo.u32[0] = ee->r[s].u32[0]; + ee->lo.u32[2] = ee->r[s].u32[2]; + ee->hi.u32[0] = ee->r[s].u32[1]; + ee->hi.u32[2] = ee->r[s].u32[3]; +} static inline void ee_i_pmtlo(struct ee_state* ee, const ee_instruction& i) { ee->lo = ee->r[EE_D_RS]; } @@ -2225,7 +2443,14 @@ static inline void ee_i_psrah(struct ee_state* ee, const ee_instruction& i) { ee->r[d].u16[6] = ((int16_t)ee->r[t].u16[6]) >> sa; ee->r[d].u16[7] = ((int16_t)ee->r[t].u16[7]) >> sa; } -static inline void ee_i_psravw(struct ee_state* ee, const ee_instruction& i) { printf("ee: psravw unimplemented\n"); exit(1); } +static inline void ee_i_psravw(struct ee_state* ee, const ee_instruction& i) { + int s = EE_D_RS; + int t = EE_D_RT; + int d = EE_D_RD; + + ee->r[d].u64[0] = SE6432((int32_t)ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31)); + ee->r[d].u64[1] = SE6432((int32_t)ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31)); +} static inline void ee_i_psraw(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA; int t = EE_D_RT; @@ -2752,7 +2977,9 @@ static inline void ee_i_teq(struct ee_state* ee, const ee_instruction& i) { static inline void ee_i_teqi(struct ee_state* ee, const ee_instruction& i) { if (EE_RS == SE6416(EE_D_I16)) ee_exception_level1(ee, CAUSE_EXC1_TR); } -static inline void ee_i_tge(struct ee_state* ee, const ee_instruction& i) { printf("ee: tge unimplemented\n"); exit(1); } +static inline void ee_i_tge(struct ee_state* ee, const ee_instruction& i) { + if (EE_RS >= EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); +} static inline void ee_i_tgei(struct ee_state* ee, const ee_instruction& i) { printf("ee: tgei unimplemented\n"); exit(1); } static inline void ee_i_tgeiu(struct ee_state* ee, const ee_instruction& i) { printf("ee: tgeiu unimplemented\n"); exit(1); } static inline void ee_i_tgeu(struct ee_state* ee, const ee_instruction& i) { printf("ee: tgeu unimplemented\n"); exit(1); } @@ -2994,183 +3221,184 @@ ee_instruction ee_decode(uint32_t opcode) { i.i26 = opcode & 0x3ffffff; i.branch = 0; + i.cycles = 0; switch ((opcode & 0xFC000000) >> 26) { case 0x00000000 >> 26: { // special switch (opcode & 0x0000003F) { - case 0x00000000: i.func = ee_i_sll; return i; - case 0x00000002: i.func = ee_i_srl; return i; - case 0x00000003: i.func = ee_i_sra; return i; - case 0x00000004: i.func = ee_i_sllv; return i; - case 0x00000006: i.func = ee_i_srlv; return i; - case 0x00000007: i.func = ee_i_srav; return i; - case 0x00000008: i.branch = 1; i.func = ee_i_jr; return i; - case 0x00000009: i.branch = 1; i.func = ee_i_jalr; return i; - case 0x0000000A: i.func = ee_i_movz; return i; - case 0x0000000B: i.func = ee_i_movn; return i; - case 0x0000000C: i.branch = 2; i.func = ee_i_syscall; return i; - case 0x0000000D: i.branch = 2; i.func = ee_i_break; return i; - case 0x0000000F: i.func = ee_i_sync; return i; - case 0x00000010: i.func = ee_i_mfhi; return i; - case 0x00000011: i.func = ee_i_mthi; return i; - case 0x00000012: i.func = ee_i_mflo; return i; - case 0x00000013: i.func = ee_i_mtlo; return i; - case 0x00000014: i.func = ee_i_dsllv; return i; - case 0x00000016: i.func = ee_i_dsrlv; return i; - case 0x00000017: i.func = ee_i_dsrav; return i; - case 0x00000018: i.func = ee_i_mult; return i; - case 0x00000019: i.func = ee_i_multu; return i; - case 0x0000001A: i.func = ee_i_div; return i; - case 0x0000001B: i.func = ee_i_divu; return i; - case 0x00000020: i.branch = 4; i.func = ee_i_add; return i; - case 0x00000021: i.func = ee_i_addu; return i; - case 0x00000022: i.branch = 4; i.func = ee_i_sub; return i; - case 0x00000023: i.func = ee_i_subu; return i; - case 0x00000024: i.func = ee_i_and; return i; - case 0x00000025: i.func = ee_i_or; return i; - case 0x00000026: i.func = ee_i_xor; return i; - case 0x00000027: i.func = ee_i_nor; return i; - case 0x00000028: i.func = ee_i_mfsa; return i; - case 0x00000029: i.func = ee_i_mtsa; return i; - case 0x0000002A: i.func = ee_i_slt; return i; - case 0x0000002B: i.func = ee_i_sltu; return i; - case 0x0000002C: i.branch = 4; i.func = ee_i_dadd; return i; - case 0x0000002D: i.func = ee_i_daddu; return i; - case 0x0000002E: i.branch = 4; i.func = ee_i_dsub; return i; - case 0x0000002F: i.func = ee_i_dsubu; return i; - case 0x00000030: i.branch = 4; i.func = ee_i_tge; return i; - case 0x00000031: i.branch = 4; i.func = ee_i_tgeu; return i; - case 0x00000032: i.branch = 4; i.func = ee_i_tlt; return i; - case 0x00000033: i.branch = 4; i.func = ee_i_tltu; return i; - case 0x00000034: i.branch = 4; i.func = ee_i_teq; return i; - case 0x00000036: i.branch = 4; i.func = ee_i_tne; return i; - case 0x00000038: i.func = ee_i_dsll; return i; - case 0x0000003A: i.func = ee_i_dsrl; return i; - case 0x0000003B: i.func = ee_i_dsra; return i; - case 0x0000003C: i.func = ee_i_dsll32; return i; - case 0x0000003E: i.func = ee_i_dsrl32; return i; - case 0x0000003F: i.func = ee_i_dsra32; return i; + case 0x00000000: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sll; return i; + case 0x00000002: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srl; return i; + case 0x00000003: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sra; return i; + case 0x00000004: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sllv; return i; + case 0x00000006: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srlv; return i; + case 0x00000007: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srav; return i; + case 0x00000008: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jr; return i; + case 0x00000009: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jalr; return i; + case 0x0000000A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movz; return i; + case 0x0000000B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movn; return i; + case 0x0000000C: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_syscall; return i; + case 0x0000000D: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_break; return i; + case 0x0000000F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sync; return i; + case 0x00000010: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfhi; return i; + case 0x00000011: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mthi; return i; + case 0x00000012: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mflo; return i; + case 0x00000013: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtlo; return i; + case 0x00000014: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsllv; return i; + case 0x00000016: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrlv; return i; + case 0x00000017: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrav; return i; + case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult; return i; + case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu; return i; + case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div; return i; + case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu; return i; + case 0x00000020: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_add; return i; + case 0x00000021: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addu; return i; + case 0x00000022: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_sub; return i; + case 0x00000023: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_subu; return i; + case 0x00000024: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_and; return i; + case 0x00000025: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_or; return i; + case 0x00000026: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xor; return i; + case 0x00000027: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_nor; return i; + case 0x00000028: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfsa; return i; + case 0x00000029: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsa; return i; + case 0x0000002A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slt; return i; + case 0x0000002B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltu; return i; + case 0x0000002C: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dadd; return i; + case 0x0000002D: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddu; return i; + case 0x0000002E: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dsub; return i; + case 0x0000002F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsubu; return i; + case 0x00000030: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tge; return i; + case 0x00000031: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeu; return i; + case 0x00000032: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlt; return i; + case 0x00000033: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltu; return i; + case 0x00000034: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teq; return i; + case 0x00000036: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tne; return i; + case 0x00000038: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll; return i; + case 0x0000003A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl; return i; + case 0x0000003B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra; return i; + case 0x0000003C: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll32; return i; + case 0x0000003E: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl32; return i; + case 0x0000003F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra32; return i; } } break; case 0x04000000 >> 26: { // regimm switch ((opcode & 0x001F0000) >> 16) { - case 0x00000000 >> 16: i.branch = 1; i.func = ee_i_bltz; return i; - case 0x00010000 >> 16: i.branch = 1; i.func = ee_i_bgez; return i; - case 0x00020000 >> 16: i.branch = 3; i.func = ee_i_bltzl; return i; - case 0x00030000 >> 16: i.branch = 3; i.func = ee_i_bgezl; return i; - case 0x00080000 >> 16: i.branch = 4; i.func = ee_i_tgei; return i; - case 0x00090000 >> 16: i.branch = 4; i.func = ee_i_tgeiu; return i; - case 0x000A0000 >> 16: i.branch = 4; i.func = ee_i_tlti; return i; - case 0x000B0000 >> 16: i.branch = 4; i.func = ee_i_tltiu; return i; - case 0x000C0000 >> 16: i.branch = 4; i.func = ee_i_teqi; return i; - case 0x000E0000 >> 16: i.branch = 4; i.func = ee_i_tnei; return i; - case 0x00100000 >> 16: i.branch = 1; i.func = ee_i_bltzal; return i; - case 0x00110000 >> 16: i.branch = 1; i.func = ee_i_bgezal; return i; - case 0x00120000 >> 16: i.branch = 3; i.func = ee_i_bltzall; return i; - case 0x00130000 >> 16: i.branch = 3; i.func = ee_i_bgezall; return i; - case 0x00180000 >> 16: i.func = ee_i_mtsab; return i; - case 0x00190000 >> 16: i.func = ee_i_mtsah; return i; + case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltz; return i; + case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgez; return i; + case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzl; return i; + case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezl; return i; + case 0x00080000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgei; return i; + case 0x00090000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeiu; return i; + case 0x000A0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlti; return i; + case 0x000B0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltiu; return i; + case 0x000C0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teqi; return i; + case 0x000E0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tnei; return i; + case 0x00100000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltzal; return i; + case 0x00110000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgezal; return i; + case 0x00120000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzall; return i; + case 0x00130000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezall; return i; + case 0x00180000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsab; return i; + case 0x00190000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsah; return i; } } break; - case 0x08000000 >> 26: i.branch = 1; i.func = ee_i_j; return i; - case 0x0C000000 >> 26: i.branch = 1; i.func = ee_i_jal; return i; - case 0x10000000 >> 26: i.branch = 1; i.func = ee_i_beq; return i; - case 0x14000000 >> 26: i.branch = 1; i.func = ee_i_bne; return i; - case 0x18000000 >> 26: i.branch = 1; i.func = ee_i_blez; return i; - case 0x1C000000 >> 26: i.branch = 1; i.func = ee_i_bgtz; return i; - case 0x20000000 >> 26: i.branch = 4; i.func = ee_i_addi; return i; - case 0x24000000 >> 26: i.func = ee_i_addiu; return i; - case 0x28000000 >> 26: i.func = ee_i_slti; return i; - case 0x2C000000 >> 26: i.func = ee_i_sltiu; return i; - case 0x30000000 >> 26: i.func = ee_i_andi; return i; - case 0x34000000 >> 26: i.func = ee_i_ori; return i; - case 0x38000000 >> 26: i.func = ee_i_xori; return i; - case 0x3C000000 >> 26: i.func = ee_i_lui; return i; + case 0x08000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_j; return i; + case 0x0C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jal; return i; + case 0x10000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_beq; return i; + case 0x14000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bne; return i; + case 0x18000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_blez; return i; + case 0x1C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgtz; return i; + case 0x20000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_addi; return i; + case 0x24000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addiu; return i; + case 0x28000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slti; return i; + case 0x2C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltiu; return i; + case 0x30000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_andi; return i; + case 0x34000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_ori; return i; + case 0x38000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xori; return i; + case 0x3C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_lui; return i; case 0x40000000 >> 26: { // cop0 switch ((opcode & 0x03E00000) >> 21) { - case 0x00000000 >> 21: i.func = ee_i_mfc0; return i; - case 0x00800000 >> 21: i.func = ee_i_mtc0; return i; + case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc0; return i; + case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc0; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { - case 0x00000000 >> 16: i.branch = 1; i.func = ee_i_bc0f; return i; - case 0x00010000 >> 16: i.branch = 1; i.func = ee_i_bc0t; return i; - case 0x00020000 >> 16: i.branch = 3; i.func = ee_i_bc0fl; return i; - case 0x00030000 >> 16: i.branch = 3; i.func = ee_i_bc0tl; return i; + case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0f; return i; + case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0t; return i; + case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0fl; return i; + case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0tl; return i; } } break; case 0x02000000 >> 21: { switch (opcode & 0x0000003F) { - case 0x00000001: i.func = ee_i_tlbr; return i; - case 0x00000002: i.func = ee_i_tlbwi; return i; - case 0x00000006: i.func = ee_i_tlbwr; return i; - case 0x00000008: i.func = ee_i_tlbp; return i; - case 0x00000018: i.branch = 2; i.func = ee_i_eret; return i; - case 0x00000038: i.func = ee_i_ei; return i; - case 0x00000039: i.func = ee_i_di; return i; + case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbr; return i; + case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbwi; return i; + case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbwr; return i; + case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbp; return i; + case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_eret; return i; + case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ei; return i; + case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_di; return i; } } break; } } break; case 0x44000000 >> 26: { // cop1 switch ((opcode & 0x03E00000) >> 21) { - case 0x00000000 >> 21: i.func = ee_i_mfc1; return i; - case 0x00400000 >> 21: i.func = ee_i_cfc1; return i; - case 0x00800000 >> 21: i.func = ee_i_mtc1; return i; - case 0x00C00000 >> 21: i.func = ee_i_ctc1; return i; + case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc1; return i; + case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc1; return i; + case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc1; return i; + case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc1; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { - case 0x00000000 >> 16: i.branch = 1; i.func = ee_i_bc1f; return i; - case 0x00010000 >> 16: i.branch = 1; i.func = ee_i_bc1t; return i; - case 0x00020000 >> 16: i.branch = 3; i.func = ee_i_bc1fl; return i; - case 0x00030000 >> 16: i.branch = 3; i.func = ee_i_bc1tl; return i; + case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1f; return i; + case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1t; return i; + case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1fl; return i; + case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1tl; return i; } } break; case 0x02000000 >> 21: { switch (opcode & 0x0000003F) { - case 0x00000000: i.func = ee_i_adds; return i; - case 0x00000001: i.func = ee_i_subs; return i; - case 0x00000002: i.func = ee_i_muls; return i; - case 0x00000003: i.func = ee_i_divs; return i; - case 0x00000004: i.func = ee_i_sqrts; return i; - case 0x00000005: i.func = ee_i_abss; return i; - case 0x00000006: i.func = ee_i_movs; return i; - case 0x00000007: i.func = ee_i_negs; return i; - case 0x00000016: i.func = ee_i_rsqrts; return i; - case 0x00000018: i.func = ee_i_addas; return i; - case 0x00000019: i.func = ee_i_subas; return i; - case 0x0000001A: i.func = ee_i_mulas; return i; - case 0x0000001C: i.func = ee_i_madds; return i; - case 0x0000001D: i.func = ee_i_msubs; return i; - case 0x0000001E: i.func = ee_i_maddas; return i; - case 0x0000001F: i.func = ee_i_msubas; return i; - case 0x00000024: i.func = ee_i_cvtw; return i; - case 0x00000028: i.func = ee_i_maxs; return i; - case 0x00000029: i.func = ee_i_mins; return i; - case 0x00000030: i.func = ee_i_cf; return i; - case 0x00000032: i.func = ee_i_ceq; return i; - case 0x00000034: i.func = ee_i_clt; return i; - case 0x00000036: i.func = ee_i_cle; return i; + case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_adds; return i; + case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subs; return i; + case 0x00000002: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_muls; return i; + case 0x00000003: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_divs; return i; + case 0x00000004: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_sqrts; return i; + case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_abss; return i; + case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_movs; return i; + case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_negs; return i; + case 0x00000016: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_rsqrts; return i; + case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_addas; return i; + case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subas; return i; + case 0x0000001A: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_mulas; return i; + case 0x0000001C: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_madds; return i; + case 0x0000001D: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubs; return i; + case 0x0000001E: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_maddas; return i; + case 0x0000001F: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubas; return i; + case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvtw; return i; + case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_maxs; return i; + case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mins; return i; + case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cf; return i; + case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ceq; return i; + case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_clt; return i; + case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cle; return i; } } break; case 0x02800000 >> 21: { switch (opcode & 0x0000003F) { - case 0x00000020: i.func = ee_i_cvts; return i; + case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvts; return i; } } break; } } break; case 0x48000000 >> 26: { // cop2 switch ((opcode & 0x03E00000) >> 21) { - case 0x00200000 >> 21: i.func = ee_i_qmfc2; return i; - case 0x00400000 >> 21: i.func = ee_i_cfc2; return i; - case 0x00A00000 >> 21: i.func = ee_i_qmtc2; return i; - case 0x00C00000 >> 21: i.func = ee_i_ctc2; return i; + case 0x00200000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmfc2; return i; + case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc2; return i; + case 0x00A00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmtc2; return i; + case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc2; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { - case 0x00000000 >> 16: i.branch = 1; i.func = ee_i_bc2f; return i; - case 0x00010000 >> 16: i.branch = 1; i.func = ee_i_bc2t; return i; - case 0x00020000 >> 16: i.branch = 3; i.func = ee_i_bc2fl; return i; - case 0x00030000 >> 16: i.branch = 3; i.func = ee_i_bc2tl; return i; + case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2f; return i; + case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2t; return i; + case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2fl; return i; + case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2tl; return i; } } break; case 0x02000000 >> 21: @@ -3190,61 +3418,61 @@ ee_instruction ee_decode(uint32_t opcode) { case 0x03C00000 >> 21: case 0x03E00000 >> 21: { switch (opcode & 0x0000003F) { - case 0x00000000: i.func = ee_i_vaddx; return i; - case 0x00000001: i.func = ee_i_vaddy; return i; - case 0x00000002: i.func = ee_i_vaddz; return i; - case 0x00000003: i.func = ee_i_vaddw; return i; - case 0x00000004: i.func = ee_i_vsubx; return i; - case 0x00000005: i.func = ee_i_vsuby; return i; - case 0x00000006: i.func = ee_i_vsubz; return i; - case 0x00000007: i.func = ee_i_vsubw; return i; - case 0x00000008: i.func = ee_i_vmaddx; return i; - case 0x00000009: i.func = ee_i_vmaddy; return i; - case 0x0000000A: i.func = ee_i_vmaddz; return i; - case 0x0000000B: i.func = ee_i_vmaddw; return i; - case 0x0000000C: i.func = ee_i_vmsubx; return i; - case 0x0000000D: i.func = ee_i_vmsuby; return i; - case 0x0000000E: i.func = ee_i_vmsubz; return i; - case 0x0000000F: i.func = ee_i_vmsubw; return i; - case 0x00000010: i.func = ee_i_vmaxx; return i; - case 0x00000011: i.func = ee_i_vmaxy; return i; - case 0x00000012: i.func = ee_i_vmaxz; return i; - case 0x00000013: i.func = ee_i_vmaxw; return i; - case 0x00000014: i.func = ee_i_vminix; return i; - case 0x00000015: i.func = ee_i_vminiy; return i; - case 0x00000016: i.func = ee_i_vminiz; return i; - case 0x00000017: i.func = ee_i_vminiw; return i; - case 0x00000018: i.func = ee_i_vmulx; return i; - case 0x00000019: i.func = ee_i_vmuly; return i; - case 0x0000001A: i.func = ee_i_vmulz; return i; - case 0x0000001B: i.func = ee_i_vmulw; return i; - case 0x0000001C: i.func = ee_i_vmulq; return i; - case 0x0000001D: i.func = ee_i_vmaxi; return i; - case 0x0000001E: i.func = ee_i_vmuli; return i; - case 0x0000001F: i.func = ee_i_vminii; return i; - case 0x00000020: i.func = ee_i_vaddq; return i; - case 0x00000021: i.func = ee_i_vmaddq; return i; - case 0x00000022: i.func = ee_i_vaddi; return i; - case 0x00000023: i.func = ee_i_vmaddi; return i; - case 0x00000024: i.func = ee_i_vsubq; return i; - case 0x00000025: i.func = ee_i_vmsubq; return i; - case 0x00000026: i.func = ee_i_vsubi; return i; - case 0x00000027: i.func = ee_i_vmsubi; return i; - case 0x00000028: i.func = ee_i_vadd; return i; - case 0x00000029: i.func = ee_i_vmadd; return i; - case 0x0000002A: i.func = ee_i_vmul; return i; - case 0x0000002B: i.func = ee_i_vmax; return i; - case 0x0000002C: i.func = ee_i_vsub; return i; - case 0x0000002D: i.func = ee_i_vmsub; return i; - case 0x0000002E: i.func = ee_i_vopmsub; return i; - case 0x0000002F: i.func = ee_i_vmini; return i; - case 0x00000030: i.func = ee_i_viadd; return i; - case 0x00000031: i.func = ee_i_visub; return i; - case 0x00000032: i.func = ee_i_viaddi; return i; - case 0x00000034: i.func = ee_i_viand; return i; - case 0x00000035: i.func = ee_i_vior; return i; - case 0x00000038: i.func = ee_i_vcallms; return i; - case 0x00000039: i.func = ee_i_vcallmsr; return i; + case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddx; return i; + case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddy; return i; + case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddz; return i; + case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddw; return i; + case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubx; return i; + case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuby; return i; + case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubz; return i; + case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubw; return i; + case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddx; return i; + case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddy; return i; + case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddz; return i; + case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddw; return i; + case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubx; return i; + case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuby; return i; + case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubz; return i; + case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubw; return i; + case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxx; return i; + case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxy; return i; + case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxz; return i; + case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxw; return i; + case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminix; return i; + case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiy; return i; + case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiz; return i; + case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiw; return i; + case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulx; return i; + case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuly; return i; + case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulz; return i; + case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulw; return i; + case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulq; return i; + case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxi; return i; + case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuli; return i; + case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminii; return i; + case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddq; return i; + case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddq; return i; + case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddi; return i; + case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddi; return i; + case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubq; return i; + case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubq; return i; + case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubi; return i; + case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubi; return i; + case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadd; return i; + case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadd; return i; + case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmul; return i; + case 0x0000002B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmax; return i; + case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsub; return i; + case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsub; return i; + case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmsub; return i; + case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmini; return i; + case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viadd; return i; + case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_visub; return i; + case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viaddi; return i; + case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viand; return i; + case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vior; return i; + case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallms; return i; + case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallmsr; return i; case 0x0000003C: case 0x0000003D: case 0x0000003E: @@ -3252,237 +3480,237 @@ ee_instruction ee_decode(uint32_t opcode) { uint32_t func = (opcode & 3) | ((opcode & 0x7c0) >> 4); switch (func) { - case 0x00000000: i.func = ee_i_vaddax; return i; - case 0x00000001: i.func = ee_i_vadday; return i; - case 0x00000002: i.func = ee_i_vaddaz; return i; - case 0x00000003: i.func = ee_i_vaddaw; return i; - case 0x00000004: i.func = ee_i_vsubax; return i; - case 0x00000005: i.func = ee_i_vsubay; return i; - case 0x00000006: i.func = ee_i_vsubaz; return i; - case 0x00000007: i.func = ee_i_vsubaw; return i; - case 0x00000008: i.func = ee_i_vmaddax; return i; - case 0x00000009: i.func = ee_i_vmadday; return i; - case 0x0000000A: i.func = ee_i_vmaddaz; return i; - case 0x0000000B: i.func = ee_i_vmaddaw; return i; - case 0x0000000C: i.func = ee_i_vmsubax; return i; - case 0x0000000D: i.func = ee_i_vmsubay; return i; - case 0x0000000E: i.func = ee_i_vmsubaz; return i; - case 0x0000000F: i.func = ee_i_vmsubaw; return i; - case 0x00000010: i.func = ee_i_vitof0; return i; - case 0x00000011: i.func = ee_i_vitof4; return i; - case 0x00000012: i.func = ee_i_vitof12; return i; - case 0x00000013: i.func = ee_i_vitof15; return i; - case 0x00000014: i.func = ee_i_vftoi0; return i; - case 0x00000015: i.func = ee_i_vftoi4; return i; - case 0x00000016: i.func = ee_i_vftoi12; return i; - case 0x00000017: i.func = ee_i_vftoi15; return i; - case 0x00000018: i.func = ee_i_vmulax; return i; - case 0x00000019: i.func = ee_i_vmulay; return i; - case 0x0000001A: i.func = ee_i_vmulaz; return i; - case 0x0000001B: i.func = ee_i_vmulaw; return i; - case 0x0000001C: i.func = ee_i_vmulaq; return i; - case 0x0000001D: i.func = ee_i_vabs; return i; - case 0x0000001E: i.func = ee_i_vmulai; return i; - case 0x0000001F: i.func = ee_i_vclipw; return i; - case 0x00000020: i.func = ee_i_vaddaq; return i; - case 0x00000021: i.func = ee_i_vmaddaq; return i; - case 0x00000022: i.func = ee_i_vaddai; return i; - case 0x00000023: i.func = ee_i_vmaddai; return i; - case 0x00000024: i.func = ee_i_vsubaq; return i; - case 0x00000025: i.func = ee_i_vmsubaq; return i; - case 0x00000026: i.func = ee_i_vsubai; return i; - case 0x00000027: i.func = ee_i_vmsubai; return i; - case 0x00000028: i.func = ee_i_vadda; return i; - case 0x00000029: i.func = ee_i_vmadda; return i; - case 0x0000002A: i.func = ee_i_vmula; return i; - case 0x0000002C: i.func = ee_i_vsuba; return i; - case 0x0000002D: i.func = ee_i_vmsuba; return i; - case 0x0000002E: i.func = ee_i_vopmula; return i; - case 0x0000002F: i.func = ee_i_vnop; return i; - case 0x00000030: i.func = ee_i_vmove; return i; - case 0x00000031: i.func = ee_i_vmr32; return i; - case 0x00000034: i.func = ee_i_vlqi; return i; - case 0x00000035: i.func = ee_i_vsqi; return i; - case 0x00000036: i.func = ee_i_vlqd; return i; - case 0x00000037: i.func = ee_i_vsqd; return i; - case 0x00000038: i.func = ee_i_vdiv; return i; - case 0x00000039: i.func = ee_i_vsqrt; return i; - case 0x0000003A: i.func = ee_i_vrsqrt; return i; - case 0x0000003B: i.func = ee_i_vwaitq; return i; - case 0x0000003C: i.func = ee_i_vmtir; return i; - case 0x0000003D: i.func = ee_i_vmfir; return i; - case 0x0000003E: i.func = ee_i_vilwr; return i; - case 0x0000003F: i.func = ee_i_viswr; return i; - case 0x00000040: i.func = ee_i_vrnext; return i; - case 0x00000041: i.func = ee_i_vrget; return i; - case 0x00000042: i.func = ee_i_vrinit; return i; - case 0x00000043: i.func = ee_i_vrxor; return i; + case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddax; return i; + case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadday; return i; + case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaz; return i; + case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaw; return i; + case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubax; return i; + case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubay; return i; + case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaz; return i; + case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaw; return i; + case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddax; return i; + case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadday; return i; + case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaz; return i; + case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaw; return i; + case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubax; return i; + case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubay; return i; + case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaz; return i; + case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaw; return i; + case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof0; return i; + case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof4; return i; + case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof12; return i; + case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof15; return i; + case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi0; return i; + case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi4; return i; + case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi12; return i; + case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi15; return i; + case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulax; return i; + case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulay; return i; + case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaz; return i; + case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaw; return i; + case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaq; return i; + case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vabs; return i; + case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulai; return i; + case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vclipw; return i; + case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaq; return i; + case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaq; return i; + case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddai; return i; + case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddai; return i; + case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaq; return i; + case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaq; return i; + case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubai; return i; + case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubai; return i; + case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadda; return i; + case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadda; return i; + case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmula; return i; + case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuba; return i; + case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuba; return i; + case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmula; return i; + case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vnop; return i; + case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmove; return i; + case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmr32; return i; + case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqi; return i; + case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqi; return i; + case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqd; return i; + case 0x00000037: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqd; return i; + case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vdiv; return i; + case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqrt; return i; + case 0x0000003A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrsqrt; return i; + case 0x0000003B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vwaitq; return i; + case 0x0000003C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmtir; return i; + case 0x0000003D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmfir; return i; + case 0x0000003E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vilwr; return i; + case 0x0000003F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viswr; return i; + case 0x00000040: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrnext; return i; + case 0x00000041: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrget; return i; + case 0x00000042: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrinit; return i; + case 0x00000043: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrxor; return i; } } break; } } break; } } break; - case 0x50000000 >> 26: i.branch = 3; i.func = ee_i_beql; return i; - case 0x54000000 >> 26: i.branch = 3; i.func = ee_i_bnel; return i; - case 0x58000000 >> 26: i.branch = 3; i.func = ee_i_blezl; return i; - case 0x5C000000 >> 26: i.branch = 3; i.func = ee_i_bgtzl; return i; - case 0x60000000 >> 26: i.branch = 4; i.func = ee_i_daddi; return i; - case 0x64000000 >> 26: i.func = ee_i_daddiu; return i; - case 0x68000000 >> 26: i.func = ee_i_ldl; return i; - case 0x6C000000 >> 26: i.func = ee_i_ldr; return i; + case 0x50000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_beql; return i; + case 0x54000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bnel; return i; + case 0x58000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_blezl; return i; + case 0x5C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgtzl; return i; + case 0x60000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_daddi; return i; + case 0x64000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddiu; return i; + case 0x68000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldl; return i; + case 0x6C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldr; return i; case 0x70000000 >> 26: { // mmi switch (opcode & 0x0000003F) { - case 0x00000000: i.func = ee_i_madd; return i; - case 0x00000001: i.func = ee_i_maddu; return i; - case 0x00000004: i.func = ee_i_plzcw; return i; + case 0x00000000: i.cycles = EE_CYC_MULT; i.func = ee_i_madd; return i; + case 0x00000001: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu; return i; + case 0x00000004: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_plzcw; return i; case 0x00000008: { switch ((opcode & 0x000007C0) >> 6) { - case 0x00000000 >> 6: i.func = ee_i_paddw; return i; - case 0x00000040 >> 6: i.func = ee_i_psubw; return i; - case 0x00000080 >> 6: i.func = ee_i_pcgtw; return i; - case 0x000000C0 >> 6: i.func = ee_i_pmaxw; return i; - case 0x00000100 >> 6: i.func = ee_i_paddh; return i; - case 0x00000140 >> 6: i.func = ee_i_psubh; return i; - case 0x00000180 >> 6: i.func = ee_i_pcgth; return i; - case 0x000001C0 >> 6: i.func = ee_i_pmaxh; return i; - case 0x00000200 >> 6: i.func = ee_i_paddb; return i; - case 0x00000240 >> 6: i.func = ee_i_psubb; return i; - case 0x00000280 >> 6: i.func = ee_i_pcgtb; return i; - case 0x00000400 >> 6: i.func = ee_i_paddsw; return i; - case 0x00000440 >> 6: i.func = ee_i_psubsw; return i; - case 0x00000480 >> 6: i.func = ee_i_pextlw; return i; - case 0x000004C0 >> 6: i.func = ee_i_ppacw; return i; - case 0x00000500 >> 6: i.func = ee_i_paddsh; return i; - case 0x00000540 >> 6: i.func = ee_i_psubsh; return i; - case 0x00000580 >> 6: i.func = ee_i_pextlh; return i; - case 0x000005C0 >> 6: i.func = ee_i_ppach; return i; - case 0x00000600 >> 6: i.func = ee_i_paddsb; return i; - case 0x00000640 >> 6: i.func = ee_i_psubsb; return i; - case 0x00000680 >> 6: i.func = ee_i_pextlb; return i; - case 0x000006C0 >> 6: i.func = ee_i_ppacb; return i; - case 0x00000780 >> 6: i.func = ee_i_pext5; return i; - case 0x000007C0 >> 6: i.func = ee_i_ppac5; return i; + case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddw; return i; + case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubw; return i; + case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtw; return i; + case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxw; return i; + case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddh; return i; + case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubh; return i; + case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgth; return i; + case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxh; return i; + case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddb; return i; + case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubb; return i; + case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtb; return i; + case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsw; return i; + case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsw; return i; + case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlw; return i; + case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacw; return i; + case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsh; return i; + case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsh; return i; + case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlh; return i; + case 0x000005C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppach; return i; + case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsb; return i; + case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsb; return i; + case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlb; return i; + case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacb; return i; + case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pext5; return i; + case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppac5; return i; } } break; case 0x00000009: { switch ((opcode & 0x000007C0) >> 6) { - case 0x00000000 >> 6: i.func = ee_i_pmaddw; return i; - case 0x00000080 >> 6: i.func = ee_i_psllvw; return i; - case 0x000000C0 >> 6: i.func = ee_i_psrlvw; return i; - case 0x00000100 >> 6: i.func = ee_i_pmsubw; return i; - case 0x00000200 >> 6: i.func = ee_i_pmfhi; return i; - case 0x00000240 >> 6: i.func = ee_i_pmflo; return i; - case 0x00000280 >> 6: i.func = ee_i_pinth; return i; - case 0x00000300 >> 6: i.func = ee_i_pmultw; return i; - case 0x00000340 >> 6: i.func = ee_i_pdivw; return i; - case 0x00000380 >> 6: i.func = ee_i_pcpyld; return i; - case 0x00000400 >> 6: i.func = ee_i_pmaddh; return i; - case 0x00000440 >> 6: i.func = ee_i_phmadh; return i; - case 0x00000480 >> 6: i.func = ee_i_pand; return i; - case 0x000004C0 >> 6: i.func = ee_i_pxor; return i; - case 0x00000500 >> 6: i.func = ee_i_pmsubh; return i; - case 0x00000540 >> 6: i.func = ee_i_phmsbh; return i; - case 0x00000680 >> 6: i.func = ee_i_pexeh; return i; - case 0x000006C0 >> 6: i.func = ee_i_prevh; return i; - case 0x00000700 >> 6: i.func = ee_i_pmulth; return i; - case 0x00000740 >> 6: i.func = ee_i_pdivbw; return i; - case 0x00000780 >> 6: i.func = ee_i_pexew; return i; - case 0x000007C0 >> 6: i.func = ee_i_prot3w; return i; + case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddw; return i; + case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllvw; return i; + case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlvw; return i; + case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubw; return i; + case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhi; return i; + case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmflo; return i; + case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinth; return i; + case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultw; return i; + case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivw; return i; + case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyld; return i; + case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddh; return i; + case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmadh; return i; + case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pand; return i; + case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pxor; return i; + case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubh; return i; + case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmsbh; return i; + case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexeh; return i; + case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prevh; return i; + case 0x00000700 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmulth; return i; + case 0x00000740 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivbw; return i; + case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexew; return i; + case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prot3w; return i; } } break; - case 0x00000010: i.func = ee_i_mfhi1; return i; - case 0x00000011: i.func = ee_i_mthi1; return i; - case 0x00000012: i.func = ee_i_mflo1; return i; - case 0x00000013: i.func = ee_i_mtlo1; return i; - case 0x00000018: i.func = ee_i_mult1; return i; - case 0x00000019: i.func = ee_i_multu1; return i; - case 0x0000001A: i.func = ee_i_div1; return i; - case 0x0000001B: i.func = ee_i_divu1; return i; - case 0x00000020: i.func = ee_i_madd1; return i; - case 0x00000021: i.func = ee_i_maddu1; return i; + case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfhi1; return i; + case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mthi1; return i; + case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mflo1; return i; + case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtlo1; return i; + case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult1; return i; + case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu1; return i; + case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div1; return i; + case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu1; return i; + case 0x00000020: i.cycles = EE_CYC_MULT; i.func = ee_i_madd1; return i; + case 0x00000021: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu1; return i; case 0x00000028: { switch ((opcode & 0x000007C0) >> 6) { - case 0x00000040 >> 6: i.func = ee_i_pabsw; return i; - case 0x00000080 >> 6: i.func = ee_i_pceqw; return i; - case 0x000000C0 >> 6: i.func = ee_i_pminw; return i; - case 0x00000100 >> 6: i.func = ee_i_padsbh; return i; - case 0x00000140 >> 6: i.func = ee_i_pabsh; return i; - case 0x00000180 >> 6: i.func = ee_i_pceqh; return i; - case 0x000001C0 >> 6: i.func = ee_i_pminh; return i; - case 0x00000280 >> 6: i.func = ee_i_pceqb; return i; - case 0x00000400 >> 6: i.func = ee_i_padduw; return i; - case 0x00000440 >> 6: i.func = ee_i_psubuw; return i; - case 0x00000480 >> 6: i.func = ee_i_pextuw; return i; - case 0x00000500 >> 6: i.func = ee_i_padduh; return i; - case 0x00000540 >> 6: i.func = ee_i_psubuh; return i; - case 0x00000580 >> 6: i.func = ee_i_pextuh; return i; - case 0x00000600 >> 6: i.func = ee_i_paddub; return i; - case 0x00000640 >> 6: i.func = ee_i_psubub; return i; - case 0x00000680 >> 6: i.func = ee_i_pextub; return i; - case 0x000006C0 >> 6: i.func = ee_i_qfsrv; return i; + case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsw; return i; + case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqw; return i; + case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminw; return i; + case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padsbh; return i; + case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsh; return i; + case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqh; return i; + case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminh; return i; + case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqb; return i; + case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduw; return i; + case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuw; return i; + case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuw; return i; + case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduh; return i; + case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuh; return i; + case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuh; return i; + case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddub; return i; + case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubub; return i; + case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextub; return i; + case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_qfsrv; return i; } } break; case 0x00000029: { switch ((opcode & 0x000007C0) >> 6) { - case 0x00000000 >> 6: i.func = ee_i_pmadduw; return i; - case 0x000000C0 >> 6: i.func = ee_i_psravw; return i; - case 0x00000200 >> 6: i.func = ee_i_pmthi; return i; - case 0x00000240 >> 6: i.func = ee_i_pmtlo; return i; - case 0x00000280 >> 6: i.func = ee_i_pinteh; return i; - case 0x00000300 >> 6: i.func = ee_i_pmultuw; return i; - case 0x00000340 >> 6: i.func = ee_i_pdivuw; return i; - case 0x00000380 >> 6: i.func = ee_i_pcpyud; return i; - case 0x00000480 >> 6: i.func = ee_i_por; return i; - case 0x000004C0 >> 6: i.func = ee_i_pnor; return i; - case 0x00000680 >> 6: i.func = ee_i_pexch; return i; - case 0x000006C0 >> 6: i.func = ee_i_pcpyh; return i; - case 0x00000780 >> 6: i.func = ee_i_pexcw; return i; + case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmadduw; return i; + case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psravw; return i; + case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthi; return i; + case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmtlo; return i; + case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinteh; return i; + case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultuw; return i; + case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivuw; return i; + case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyud; return i; + case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_por; return i; + case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pnor; return i; + case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexch; return i; + case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyh; return i; + case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexcw; return i; } } break; case 0x00000030: { switch ((opcode & 0x000007C0) >> 6) { - case 0x00000000 >> 6: i.func = ee_i_pmfhllw; return i; - case 0x00000040 >> 6: i.func = ee_i_pmfhluw; return i; - case 0x00000080 >> 6: i.func = ee_i_pmfhlslw; return i; - case 0x000000c0 >> 6: i.func = ee_i_pmfhllh; return i; - case 0x00000100 >> 6: i.func = ee_i_pmfhlsh; return i; + case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllw; return i; + case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhluw; return i; + case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlslw; return i; + case 0x000000c0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllh; return i; + case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlsh; return i; } } break; - case 0x00000031: i.func = ee_i_pmthl; return i; - case 0x00000034: i.func = ee_i_psllh; return i; - case 0x00000036: i.func = ee_i_psrlh; return i; - case 0x00000037: i.func = ee_i_psrah; return i; - case 0x0000003C: i.func = ee_i_psllw; return i; - case 0x0000003E: i.func = ee_i_psrlw; return i; - case 0x0000003F: i.func = ee_i_psraw; return i; + case 0x00000031: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthl; return i; + case 0x00000034: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllh; return i; + case 0x00000036: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlh; return i; + case 0x00000037: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrah; return i; + case 0x0000003C: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllw; return i; + case 0x0000003E: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlw; return i; + case 0x0000003F: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psraw; return i; } } break; - case 0x78000000 >> 26: i.func = ee_i_lq; return i; - case 0x7C000000 >> 26: i.func = ee_i_sq; return i; - case 0x80000000 >> 26: i.func = ee_i_lb; return i; - case 0x84000000 >> 26: i.func = ee_i_lh; return i; - case 0x88000000 >> 26: i.func = ee_i_lwl; return i; - case 0x8C000000 >> 26: i.func = ee_i_lw; return i; - case 0x90000000 >> 26: i.func = ee_i_lbu; return i; - case 0x94000000 >> 26: i.func = ee_i_lhu; return i; - case 0x98000000 >> 26: i.func = ee_i_lwr; return i; - case 0x9C000000 >> 26: i.func = ee_i_lwu; return i; - case 0xA0000000 >> 26: i.func = ee_i_sb; return i; - case 0xA4000000 >> 26: i.func = ee_i_sh; return i; - case 0xA8000000 >> 26: i.func = ee_i_swl; return i; - case 0xAC000000 >> 26: i.func = ee_i_sw; return i; - case 0xB0000000 >> 26: i.func = ee_i_sdl; return i; - case 0xB4000000 >> 26: i.func = ee_i_sdr; return i; - case 0xB8000000 >> 26: i.func = ee_i_swr; return i; - case 0xBC000000 >> 26: i.func = ee_i_cache; return i; - case 0xC4000000 >> 26: i.func = ee_i_lwc1; return i; - case 0xCC000000 >> 26: i.func = ee_i_pref; return i; - case 0xD8000000 >> 26: i.func = ee_i_lqc2; return i; - case 0xDC000000 >> 26: i.func = ee_i_ld; return i; - case 0xE4000000 >> 26: i.func = ee_i_swc1; return i; - case 0xF8000000 >> 26: i.func = ee_i_sqc2; return i; - case 0xFC000000 >> 26: i.func = ee_i_sd; return i; + case 0x78000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lq; return i; + case 0x7C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_sq; return i; + case 0x80000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lb; return i; + case 0x84000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lh; return i; + case 0x88000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwl; return i; + case 0x8C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lw; return i; + case 0x90000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lbu; return i; + case 0x94000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lhu; return i; + case 0x98000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwr; return i; + case 0x9C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwu; return i; + case 0xA0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sb; return i; + case 0xA4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sh; return i; + case 0xA8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swl; return i; + case 0xAC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sw; return i; + case 0xB0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdl; return i; + case 0xB4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdr; return i; + case 0xB8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swr; return i; + case 0xBC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_cache; return i; + case 0xC4000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwc1; return i; + case 0xCC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_pref; return i; + case 0xD8000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lqc2; return i; + case 0xDC000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ld; return i; + case 0xE4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swc1; return i; + case 0xF8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sqc2; return i; + case 0xFC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sd; return i; } i.func = ee_i_invalid; @@ -3491,7 +3719,7 @@ ee_instruction ee_decode(uint32_t opcode) { } int ee_run_block(struct ee_state* ee, int max_cycles) { - // This is the entrypoint to the EENULL process. + // This is the entrypoint to the EENULL thread. // If we hit this address, the program is basically idling // so we "fast-forward" 1024 cycles if (ee->pc == 0x81fc0) { @@ -3510,7 +3738,7 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { int cycles = 0; - for (const auto& i : block) { + for (const auto& i : block.instructions) { ee->delay_slot = ee->branch; ee->branch = 0; @@ -3518,17 +3746,15 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { if (ee_check_irq(ee)) break; - cycles++; - ee->pc = ee->next_pc; ee->next_pc += 4; i.func(ee, i); + ee->count++; ee->r[0] = { 0 }; - ++ee->total_cycles; - ++ee->count; + cycles++; // An exception occurred or likely branch was taken // break immediately and clear the exception flag @@ -3546,14 +3772,11 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { uint32_t pc = ee->pc; uint32_t block_pc = ee->pc; - - std::vector block; - - block.reserve(max_cycles); - - int prev_branch = 0; - ee_instruction i; + ee_block block; + + block.cycles = 0; + block.instructions.reserve(max_cycles); while (max_cycles) { ee->opcode = bus_read32(ee, pc); @@ -3561,14 +3784,16 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { if (ee->opcode != 0) { i = ee_decode(ee->opcode); - block.push_back(i); + block.instructions.push_back(i); } else { i.func = ee_i_nop; i.branch = 0; - block.push_back(i); + block.instructions.push_back(i); } + block.cycles += i.cycles; + if (i.branch == 1 || i.branch == 3) { max_cycles = 2; } else if (i.branch != 0) { diff --git a/src/ee/ee_def.hpp b/src/ee/ee_def.hpp index 5b8fbe7..3914288 100644 --- a/src/ee/ee_def.hpp +++ b/src/ee/ee_def.hpp @@ -21,6 +21,19 @@ #define EE_ALIGNED16 #endif +#define EE_CYC_DEFAULT 9 +#define EE_CYC_BRANCH 11 +#define EE_CYC_COP_DEFAULT 7 +#define EE_CYC_MULT 2*8 +#define EE_CYC_DIV 14*8 +#define EE_CYC_MMI_MULT 3*8 +#define EE_CYC_MMI_DIV 22*8 +#define EE_CYC_MMI_DEFAULT 14 +#define EE_CYC_FPU_MULT 4*8 +#define EE_CYC_FPU_DIV 6*8 +#define EE_CYC_STORE 14 +#define EE_CYC_LOAD 14 + struct ee_instruction { uint32_t opcode; int32_t rs; @@ -37,16 +50,22 @@ struct ee_instruction { // 3 - likely branch // 4 - conditional exception int branch; + int cycles; void (*func)(struct ee_state*, const ee_instruction&); }; +struct ee_block { + std::vector instructions; + uint32_t cycles; +}; + struct ee_state { struct ee_bus_s bus; uint32_t block_pc; - std::unordered_map > block_cache; + std::unordered_map block_cache; uint128_t r[32] EE_ALIGNED16; uint128_t hi EE_ALIGNED16; From aa98a374a609b1a5d93537e4d7781baa2b6bc36e Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:20:10 -0300 Subject: [PATCH 10/26] ps2: Run scheduler at real EE speed ps2: Add support for changing timescale at runtime --- src/ps2.c | 37 +++++++++---------------------------- src/ps2.h | 2 ++ 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/ps2.c b/src/ps2.c index dda13dd..d73ebd7 100644 --- a/src/ps2.c +++ b/src/ps2.c @@ -5,10 +5,6 @@ #include "ps2.h" -#ifndef _PS2_TIMESCALE -#define _PS2_TIMESCALE 4 -#endif - struct ps2_state* ps2_create(void) { return malloc(sizeof(struct ps2_state)); } @@ -149,6 +145,7 @@ void ps2_init(struct ps2_state* ps2) { ps2_ipu_reset(ps2->ipu); ps2->ee_cycles = 7; + ps2->timescale = 1; } void ps2_init_kputchar(struct ps2_state* ps2, void (*ee_kputchar)(void*, char), void* ee_udata, void (*iop_kputchar)(void*, char), void* iop_udata) { @@ -210,6 +207,7 @@ void ps2_reset(struct ps2_state* ps2) { ps2_usb_init(ps2->usb); ps2_fw_init(ps2->fw, ps2->iop_intc); ps2_sbus_init(ps2->sbus, ps2->ee_intc, ps2->iop_intc, ps2->sched); + ps2_cdvd_reset(ps2->cdvd); ps2_gs_reset(ps2->gs); ps2_ram_reset(ps2->ee_ram); @@ -242,38 +240,17 @@ void ps2_reset(struct ps2_state* ps2) { // } void ps2_cycle(struct ps2_state* ps2) { - // if (ps2->ee->pc == 0xe0040) - // printf("ee: Entry @ cyc=%ld\n", ps2->ee->total_cycles); - - // ps2_trace(ps2); - - // Waitloop detection - // if (ps2->ee->pc == 0x81fc0) { - // while (!sched_tick(ps2->sched, 4)) { - // --ps2->ee_cycles; - - // if (!ps2->ee_cycles) { - // iop_cycle(ps2->iop); - // ps2_iop_timers_tick(ps2->iop_timers); - - // ps2->ee_cycles = 7; - // } - // } - - // ee_cycle(ps2->ee); - - // return; - // } - int cycles = ee_run_block(ps2->ee, 128); while (!cycles) { cycles = ee_run_block(ps2->ee, 128); } + cycles *= ps2->timescale; + ps2->ee_cycles += cycles; - sched_tick(ps2->sched, (2 * _PS2_TIMESCALE) * cycles); + sched_tick(ps2->sched, cycles); ps2_ipu_run(ps2->ipu); @@ -304,6 +281,10 @@ void ps2_iop_cycle(struct ps2_state* ps2) { // ps2->ee_cycles = 7; } +void ps2_set_timescale(struct ps2_state* ps2, int timescale) { + ps2->timescale = timescale; +} + void ps2_destroy(struct ps2_state* ps2) { free(ps2->strtab); free(ps2->func); diff --git a/src/ps2.h b/src/ps2.h index 6b750f5..9bc4f88 100644 --- a/src/ps2.h +++ b/src/ps2.h @@ -86,6 +86,7 @@ struct ps2_state { struct sched_state* sched; int ee_cycles; + int timescale; // Debug struct ps2_elf_function* func; @@ -102,6 +103,7 @@ void ps2_load_bios(struct ps2_state* ps2, const char* path); void ps2_load_rom1(struct ps2_state* ps2, const char* path); void ps2_load_rom2(struct ps2_state* ps2, const char* path); void ps2_cycle(struct ps2_state* ps2); +void ps2_set_timescale(struct ps2_state* ps2, int timescale); void ps2_iop_cycle(struct ps2_state* ps2); void ps2_destroy(struct ps2_state* ps2); From 0d588bfdac9b0c4edc241001fcc7e35a54a6d9e9 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:21:31 -0300 Subject: [PATCH 11/26] dmac: Fix SPR_TO NULL dereference --- src/ee/dmac.c | 118 ++++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/src/ee/dmac.c b/src/ee/dmac.c index 600ac36..fc884bc 100644 --- a/src/ee/dmac.c +++ b/src/ee/dmac.c @@ -134,16 +134,16 @@ static inline void dmac_process_source_tag(struct ps2_dmac* dmac, struct dmac_ch c->tag.mem = TAG_MEM(tag); c->tag.data = TAG_DATA(tag); - if (dmac->mfifo_drain) - printf("ee: dmac tag %016lx %016lx qwc=%08x id=%d irq=%d addr=%08x mem=%d data=%016lx\n", - tag.u64[1], tag.u64[0], - c->tag.qwc, - c->tag.id, - c->tag.irq, - c->tag.addr, - c->tag.mem, - c->tag.data - ); + // if (dmac->mfifo_drain) + // printf("ee: dmac tag %016lx %016lx qwc=%08x id=%d irq=%d addr=%08x mem=%d data=%016lx\n", + // tag.u64[1], tag.u64[0], + // c->tag.qwc, + // c->tag.id, + // c->tag.irq, + // c->tag.addr, + // c->tag.mem, + // c->tag.data + // ); c->tag.end = 0; c->qwc = c->tag.qwc; @@ -418,24 +418,23 @@ void mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) { } void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { - printf("dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x rbor=%08x rbsr=%08x sprfrom.madr=%08x\n", - dmac->vif1.chcr & 1, - (dmac->vif1.chcr >> 2) & 3, - (dmac->vif1.chcr >> 6) & 1, - (dmac->vif1.chcr >> 7) & 1, - dmac->vif1.qwc, - dmac->vif1.madr, - dmac->vif1.tadr, - dmac->rbor, - dmac->rbsr, - dmac->spr_from.madr - ); + // printf("dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x rbor=%08x rbsr=%08x sprfrom.madr=%08x\n", + // dmac->vif1.chcr & 1, + // (dmac->vif1.chcr >> 2) & 3, + // (dmac->vif1.chcr >> 6) & 1, + // (dmac->vif1.chcr >> 7) & 1, + // dmac->vif1.qwc, + // dmac->vif1.madr, + // dmac->vif1.tadr, + // dmac->rbor, + // dmac->rbsr, + // dmac->spr_from.madr + // ); int mfifo_drain = (dmac->ctrl >> 2) & 3; - if (mfifo_drain == 2) { + if (mfifo_drain == 2) return; - } int tte = (dmac->vif1.chcr >> 6) & 1; int mode = (dmac->vif1.chcr >> 2) & 3; @@ -558,18 +557,18 @@ void dmac_handle_gif_transfer(struct ps2_dmac* dmac) { sched_schedule(dmac->sched, event); - printf("dmac: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x rbor=%08x rbsr=%08x sprfrom.madr=%08x\n", - dmac->gif.chcr & 1, - (dmac->gif.chcr >> 2) & 3, - (dmac->gif.chcr >> 6) & 1, - (dmac->gif.chcr >> 7) & 1, - dmac->gif.qwc, - dmac->gif.madr, - dmac->gif.tadr, - dmac->rbor, - dmac->rbsr, - dmac->spr_from.madr - ); + // printf("dmac: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x rbor=%08x rbsr=%08x sprfrom.madr=%08x\n", + // dmac->gif.chcr & 1, + // (dmac->gif.chcr >> 2) & 3, + // (dmac->gif.chcr >> 6) & 1, + // (dmac->gif.chcr >> 7) & 1, + // dmac->gif.qwc, + // dmac->gif.madr, + // dmac->gif.tadr, + // dmac->rbor, + // dmac->rbsr, + // dmac->spr_from.madr + // ); int mfifo_drain = (dmac->ctrl >> 2) & 3; @@ -843,6 +842,7 @@ void dmac_handle_sif0_transfer(struct ps2_dmac* dmac) { } void dmac_handle_sif1_transfer(struct ps2_dmac* dmac) { assert(!dmac->sif1.qwc); + assert(((dmac->sif1.chcr >> 2) & 3) == 1); // This should be ok? // if (!ps2_sif_fifo_is_empty(dmac->sif)) { @@ -928,18 +928,18 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { dmac->spr_from.chcr &= ~0x100; - printf("dmac: spr_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x rbor=%08x rbsr=%08x\n", - dmac->spr_from.chcr, - dmac->spr_from.chcr & 1, - (dmac->spr_from.chcr >> 2) & 3, - !!(dmac->spr_from.chcr & 0x40), - dmac->spr_from.madr, - dmac->spr_from.qwc, - dmac->spr_from.tadr, - dmac->spr_from.sadr, - dmac->rbor, - dmac->rbsr - ); + // printf("dmac: spr_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x rbor=%08x rbsr=%08x\n", + // dmac->spr_from.chcr, + // dmac->spr_from.chcr & 1, + // (dmac->spr_from.chcr >> 2) & 3, + // !!(dmac->spr_from.chcr & 0x40), + // dmac->spr_from.madr, + // dmac->spr_from.qwc, + // dmac->spr_from.tadr, + // dmac->spr_from.sadr, + // dmac->rbor, + // dmac->rbsr + // ); int mode = (dmac->spr_from.chcr >> 2) & 3; @@ -1087,32 +1087,30 @@ void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) { dmac->spr_to.qwc = 0; // We're done - if (mode == 0) { + if (dmac->spr_to.tag.end) return; - } // Chain mode do { uint128_t tag = dmac_read_qword(dmac, dmac->spr_to.tadr, 0); - dmac_process_source_tag(dmac, &dmac->spr_to, tag); - if ((dmac->spr_to.chcr >> 6) & 1) { ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, tag); - dmac->spr_to.madr += 0x10; + dmac->spr_to.sadr += 0x10; } - // printf("ee: spr_to tag qwc=%08lx madr=%08lx tadr=%08lx id=%ld addr=%08lx mem=%ld data=%016lx end=%d tte=%d\n", + dmac_process_source_tag(dmac, &dmac->spr_to, tag); + + // printf("ee: spr_to tag qwc=%08lx madr=%08lx tadr=%08lx id=%ld addr=%08lx mem=%ld end=%d data=%08x%08x\n", // dmac->spr_to.tag.qwc, // dmac->spr_to.madr, // dmac->spr_to.tadr, // dmac->spr_to.tag.id, // dmac->spr_to.tag.addr, // dmac->spr_to.tag.mem, - // dmac->spr_to.tag.data, // dmac->spr_to.tag.end, - // (dmac->spr_to.chcr >> 7) & 1 + // tag.u32[1], tag.u32[0] // ); for (int i = 0; i < dmac->spr_to.qwc; i++) { @@ -1377,11 +1375,17 @@ void ps2_dmac_write8(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) { } } } return; + + // ENABLEW (byte 2) + case 0x1000f592: { + dmac->enable &= 0xff00ffff; + dmac->enable |= (data & 0xff) << 16; + } return; } printf("dmac: 8-bit write to %08x (%02x)\n", addr, data); - exit(1); + // exit(1); return; } From 1244de2dc5323c07fe4ad61acbfcde95b91b8525 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:22:01 -0300 Subject: [PATCH 12/26] timers: Don't update timers when CUE is reset --- src/ee/timers.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/ee/timers.c b/src/ee/timers.c index 6f549ef..ed6399b 100644 --- a/src/ee/timers.c +++ b/src/ee/timers.c @@ -113,20 +113,23 @@ static inline void ee_timers_write_mode(struct ps2_ee_timers* timers, int t, uin timer->cmpe = (data >> 8) & 1; timer->ovfe = (data >> 9) & 1; + if (!timer->cue) + return; + if (timer->gate) { printf("timers: Timer %d gate write %08x\n", t, data); // exit(1); } - // printf("timers: Timer %d mode write %08x mode=%08x counter=%04x compare=%04x clks=%d gate=%d gats=%d gatm=%d zret=%d cue=%d cmpe=%d ovfe=%d\n", - // t, data, - // timer->mode, - // timer->counter, - // timer->compare, - // timer->clks, timer->gate, timer->gats, timer->gatm, - // timer->zret, timer->cue, timer->cmpe, timer->ovfe - // ); + printf("timers: Timer %d mode write %08x mode=%08x counter=%04x compare=%04x clks=%d gate=%d gats=%d gatm=%d zret=%d cue=%d cmpe=%d ovfe=%d\n", + t, data, + timer->mode, + timer->counter, + timer->compare, + timer->clks, timer->gate, timer->gats, timer->gatm, + timer->zret, timer->cue, timer->cmpe, timer->ovfe + ); switch (timer->clks) { case 0: timer->delta = 1; break; From cedb5470ead790b1a833914584109a8d8dd41b59 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:24:35 -0300 Subject: [PATCH 13/26] misc: Add signed fields to uint128 union --- src/u128.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/u128.h b/src/u128.h index 3b467df..02445d0 100644 --- a/src/u128.h +++ b/src/u128.h @@ -14,6 +14,10 @@ typedef union { uint32_t u32[4]; uint16_t u16[8]; uint8_t u8[16]; + int64_t s64[2]; + int32_t s32[4]; + int16_t s16[8]; + int8_t s8[16]; uint64_t ul64; uint32_t ul32; uint16_t ul16; From 6dbb4bc947c07942ab4c526de3c177f062148cf9 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:25:31 -0300 Subject: [PATCH 14/26] frontend: Show ImGui framerate on statusbar --- frontend/ui/statusbar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/ui/statusbar.cpp b/frontend/ui/statusbar.cpp index a354aaa..382092e 100644 --- a/frontend/ui/statusbar.cpp +++ b/frontend/ui/statusbar.cpp @@ -77,8 +77,11 @@ void show_status_bar(iris::instance* iris) { vp_w, vp_h, mode == 3 ? "Interlaced" : "Progressive", get_format_bpp(disp_fmt), - iris->fps * 2.0f + GetIO().Framerate ); + + // iris->avg_frames++; + // iris->avg_fps += iris->fps; } else { Text(ICON_MS_MONITOR " %s | No image", renderer_get_name(iris->ctx) From a9b082aed5f01a949a0c6443cc4d61144782808c Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:26:08 -0300 Subject: [PATCH 15/26] frontend: Add remaining icons frontend: Add window size selector frontend: Add performance overlay to debug menu --- frontend/ui/menubar.cpp | 44 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/frontend/ui/menubar.cpp b/frontend/ui/menubar.cpp index f309005..a474f00 100644 --- a/frontend/ui/menubar.cpp +++ b/frontend/ui/menubar.cpp @@ -234,7 +234,7 @@ void show_main_menubar(iris::instance* iris) { ImGui::EndMenu(); } - if (BeginMenu("Scale")) { + if (BeginMenu(ICON_MS_CROP " Scale")) { for (int i = 2; i <= 6; i++) { char buf[16]; snprintf(buf, 16, "%.1fx", (float)i * 0.5f); @@ -248,7 +248,7 @@ void show_main_menubar(iris::instance* iris) { ImGui::EndMenu(); } - if (BeginMenu("Aspect mode")) { + if (BeginMenu(ICON_MS_ASPECT_RATIO " Aspect mode")) { for (int i = 0; i < 7; i++) { if (Selectable(aspect_mode_names[i], iris->aspect_mode == i)) { iris->aspect_mode = i; @@ -260,7 +260,7 @@ void show_main_menubar(iris::instance* iris) { ImGui::EndMenu(); } - if (BeginMenu("Scaling filter")) { + if (BeginMenu(ICON_MS_FILTER " Scaling filter")) { if (Selectable("Nearest", !iris->bilinear)) { iris->bilinear = false; @@ -276,11 +276,43 @@ void show_main_menubar(iris::instance* iris) { ImGui::EndMenu(); } - if (MenuItem("Integer scaling", nullptr, &iris->integer_scaling)) { + if (BeginMenu(ICON_MS_ASPECT_RATIO " Window size")) { + const char* sizes[] = { + "640x480", + "800x600", + "960x720", + "1024x768", + "1280x720", + "1280x800" + }; + + int widths[] = { + 640, 800, 960, 1024, 1280, 1280 + }; + + int heights[] = { + 480, 600, 720, 768, 720, 800 + }; + + for (int i = 0; i < 6; i++) { + bool selected = iris->window_width == widths[i] && iris->window_height == heights[i]; + + if (MenuItem(sizes[i], nullptr, selected)) { + iris->window_width = widths[i]; + iris->window_height = heights[i]; + + SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height); + } + } + + ImGui::EndMenu(); + } + + if (MenuItem(ICON_MS_SPEED_2X " Integer scaling", nullptr, &iris->integer_scaling)) { renderer_set_integer_scaling(iris->ctx, iris->integer_scaling); } - if (MenuItem("Fullscreen", "F11", &iris->fullscreen)) { + if (MenuItem(ICON_MS_FULLSCREEN " Fullscreen", "F11", &iris->fullscreen)) { SDL_SetWindowFullscreen(iris->window, iris->fullscreen); } @@ -385,6 +417,7 @@ void show_main_menubar(iris::instance* iris) { if (MenuItem(ICON_MS_MEMORY " Memory viewer", NULL, &iris->show_memory_viewer)); if (MenuItem(ICON_MS_VIEW_IN_AR " VU disassembler", NULL, &iris->show_vu_disassembler)); if (MenuItem(ICON_MS_GAMEPAD " DualShock debugger", NULL, &iris->show_pad_debugger)); + if (MenuItem(ICON_MS_BUG_REPORT " Performance overlay", NULL, &iris->show_overlay)); Separator(); @@ -413,6 +446,7 @@ void show_main_menubar(iris::instance* iris) { iris->show_symbols = false; iris->show_threads = false; iris->show_breakpoints = false; + iris->show_overlay = false; } ImGui::EndMenu(); From 4cd90170e94b3bffb718c17b09ba6ce2c2866077 Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:26:28 -0300 Subject: [PATCH 16/26] frontend: Show correct bss address on module viewer --- frontend/ui/modules.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/ui/modules.cpp b/frontend/ui/modules.cpp index 3c4f106..0427b70 100644 --- a/frontend/ui/modules.cpp +++ b/frontend/ui/modules.cpp @@ -61,7 +61,7 @@ static inline void show_modules_table(iris::instance* iris) { // bss section TableSetColumnIndex(3); addr += mod->data_size; - Text("0x%08x", iop->module_list[i].text_addr); + Text("0x%08x", addr); PopFont(); } From 9b97fe8806a78617f8161020b0bd13f58932a98f Mon Sep 17 00:00:00 2001 From: allkern Date: Fri, 19 Sep 2025 09:38:58 -0300 Subject: [PATCH 17/26] iris: Add Roboto Black font (performance overlay) --- res/Roboto-Black.ttf | Bin 0 -> 164936 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/Roboto-Black.ttf diff --git a/res/Roboto-Black.ttf b/res/Roboto-Black.ttf new file mode 100644 index 0000000000000000000000000000000000000000..86ec2b29ba56a3d6c45f1a8584ff3780fa70c60e GIT binary patch literal 164936 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 Date: Wed, 1 Oct 2025 11:33:48 -0300 Subject: [PATCH 18/26] iris: Add timescale setting --- frontend/iris.hpp | 1 + frontend/settings.cpp | 6 +++++- frontend/ui/menubar.cpp | 14 ++++++++++++++ src/ps2.c | 4 +--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/iris.hpp b/frontend/iris.hpp index 5bf93d0..b668cca 100644 --- a/frontend/iris.hpp +++ b/frontend/iris.hpp @@ -213,6 +213,7 @@ struct instance { int menubar_height = 0; bool mute = false; float volume = 1.0f; + int timescale = 8; bool limit_fps = true; float fps_cap = 60.0f; diff --git a/frontend/settings.cpp b/frontend/settings.cpp index 8b4dc30..e0ce1a4 100644 --- a/frontend/settings.cpp +++ b/frontend/settings.cpp @@ -116,6 +116,7 @@ int parse_toml_settings(iris::instance* iris) { iris->show_breakpoints = debugger["show_breakpoints"].value_or(false); iris->show_imgui_demo = debugger["show_imgui_demo"].value_or(false); iris->skip_fmv = debugger["skip_fmv"].value_or(false); + iris->timescale = debugger["timescale"].value_or(8); toml::array* recents = tbl["recents"]["array"].as_array(); @@ -127,6 +128,8 @@ int parse_toml_settings(iris::instance* iris) { renderer_set_integer_scaling(iris->ctx, iris->integer_scaling); renderer_set_scale(iris->ctx, iris->scale); + ps2_set_timescale(iris->ps2, iris->timescale); + ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv); return 0; @@ -287,7 +290,8 @@ void close_settings(iris::instance* iris) { { "show_threads", iris->show_threads }, { "show_imgui_demo", iris->show_imgui_demo }, { "show_overlay", iris->show_overlay }, - { "skip_fmv", iris->skip_fmv } + { "skip_fmv", iris->skip_fmv }, + { "timescale", iris->timescale } } }, { "display", toml::table { { "scale", iris->scale }, diff --git a/frontend/ui/menubar.cpp b/frontend/ui/menubar.cpp index a474f00..346f124 100644 --- a/frontend/ui/menubar.cpp +++ b/frontend/ui/menubar.cpp @@ -421,6 +421,20 @@ void show_main_menubar(iris::instance* iris) { Separator(); + if (BeginMenu(ICON_MS_MORE_TIME " Timescale")) { + for (int i = 0; i < 9; i++) { + char buf[16]; snprintf(buf, 16, "%dx", 1 << i); + + if (Selectable(buf, iris->timescale == (1 << i))) { + iris->timescale = (1 << i); + + ps2_set_timescale(iris->ps2, iris->timescale); + } + } + + ImGui::EndMenu(); + } + if (MenuItem(ICON_MS_SKIP_NEXT " Skip FMVs", NULL, &iris->skip_fmv)) { printf("Skip FMVs: %d\n", iris->skip_fmv); ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv); diff --git a/src/ps2.c b/src/ps2.c index d73ebd7..64b7541 100644 --- a/src/ps2.c +++ b/src/ps2.c @@ -246,11 +246,9 @@ void ps2_cycle(struct ps2_state* ps2) { cycles = ee_run_block(ps2->ee, 128); } - cycles *= ps2->timescale; - ps2->ee_cycles += cycles; - sched_tick(ps2->sched, cycles); + sched_tick(ps2->sched, ps2->timescale * cycles); ps2_ipu_run(ps2->ipu); From 8f5ac3275b617f210079c4d1afb3ddcab68bf488 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:37:34 -0300 Subject: [PATCH 19/26] iris: Misc cleanup --- src/ee/bus.c | 12 ++++++++---- src/ee/gif.c | 19 +++++++++++-------- src/ee/timers.c | 16 ++++++++-------- src/iop/bus.c | 28 ++++++++++++++-------------- src/iop/timers.c | 28 ++++++++++++++-------------- src/iop/usb.c | 4 ---- src/shared/ram.h | 1 + 7 files changed, 56 insertions(+), 52 deletions(-) diff --git a/src/ee/bus.c b/src/ee/bus.c index 5ac75da..f6afd15 100644 --- a/src/ee/bus.c +++ b/src/ee/bus.c @@ -159,8 +159,11 @@ uint64_t ee_bus_read8(void* udata, uint32_t addr) { MAP_REG_READ(8, 0x1F402004, 0x1F402018, cdvd, cdvd); MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2); + MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function + + if ((addr >> 16) == 0x1f80) return 0; - printf("bus: Unhandled 8-bit read from physical address 0x%08x\n", addr); + // printf("bus: Unhandled 8-bit read from physical address 0x%08x\n", addr); // *(int*)0 = 0; return 0; } @@ -322,7 +325,7 @@ uint128_t ee_bus_read128(void* udata, uint32_t addr) { printf("bus: Unhandled 128-bit read from physical address 0x%08x\n", addr); // exit(1); - *(int*)0 = 0; + // *(int*)0 = 0; return (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; } @@ -352,7 +355,7 @@ void ee_bus_write8(void* udata, uint32_t addr, uint64_t data) { if (addr == 0x1000f180) { bus->kputchar(bus->kputchar_udata, data & 0xff); return; } - printf("bus: Unhandled 8-bit write to physical address 0x%08x (0x%02lx)\n", addr, data); + // printf("bus: Unhandled 8-bit write to physical address 0x%08x (0x%02lx)\n", addr, data); } void ee_bus_write16(void* udata, uint32_t addr, uint64_t data) { @@ -384,7 +387,7 @@ void ee_bus_write16(void* udata, uint32_t addr, uint64_t data) { case 0x1f801472: return; } - printf("bus: Unhandled 16-bit write to physical address 0x%08x (0x%04lx)\n", addr, data); + // printf("bus: Unhandled 16-bit write to physical address 0x%08x (0x%04lx)\n", addr, data); } void ee_bus_write32(void* udata, uint32_t addr, uint64_t data) { @@ -473,6 +476,7 @@ void ee_bus_write64(void* udata, uint32_t addr, uint64_t data) { MAP_REG_WRITE(64, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_WRITE(32, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_WRITE(32, 0x1000F520, 0x1000F5FF, dmac, dmac); + MAP_REG_WRITE(32, 0x10000000, 0x10001FFF, ee_timers, timers); // Reuse 32-bit function MAP_MEM_WRITE(64, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(64, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_WRITE(64, 0x1000F000, 0x1000F01F, intc, intc); diff --git a/src/ee/gif.c b/src/ee/gif.c index 9dc9e9e..a797d95 100644 --- a/src/ee/gif.c +++ b/src/ee/gif.c @@ -109,7 +109,13 @@ uint64_t ps2_gif_read32(struct ps2_gif* gif, uint32_t addr) { void ps2_gif_write32(struct ps2_gif* gif, uint32_t addr, uint64_t data) { switch (addr) { - case 0x10003000: gif->ctrl = data; return; + case 0x10003000: { + if (data & 1) { + ps2_gif_init(gif, gif->vu1, gif->gs); + } + + printf("gif: ctrl=%08x\n", data); + } return; case 0x10003010: gif->mode = data; return; } } @@ -191,8 +197,8 @@ void gif_handle_tag(struct ps2_gif* gif, uint128_t data) { } break; } - // printf("giftag: nloop=%04lx eop=%d prim=%d fmt=%d nregs=%d reg=%016lx\n", - // gif->tag.nloop, gif->tag.eop, gif->tag.prim, gif->tag.fmt, gif->tag.nregs, gif->tag.reg + // printf("giftag: nloop=%04lx eop=%d prim=%04x (pre=%d) fmt=%d nregs=%d reg=%08x%08x\n", + // gif->tag.nloop, gif->tag.eop, gif->tag.prim, gif->tag.pre, gif->tag.fmt, gif->tag.nregs, gif->tag.reg >> 32, gif->tag.reg & 0xffffffff // ); if (gif->tag.pre) { @@ -213,7 +219,7 @@ void gif_handle_packed(struct ps2_gif* gif, uint128_t data) { case 0x01: /* printf("gif: RGBAQ <- %016lx\n", data.u64[0]); */ gif_write_rgbaq(gif, data); break; case 0x02: /* printf("gif: STQ <- %016lx\n", data.u64[0]); */ gif_write_stq(gif, data); break; case 0x03: /* printf("gif: UV <- %016lx\n", data.u64[0]); */ gif_write_uv(gif, data); break; - case 0x04: /* printf("gif: XYZF23 <- %016lx\n", data.u64[0]); */ gif_write_xyzf23(gif, data); break; + case 0x04: /* printf("gif: XYZF23 <- %08x%08x %08x%08x\n", data.u32[3], data.u32[2], data.u32[1], data.u32[0]); */ gif_write_xyzf23(gif, data); break; case 0x05: /* printf("gif: XYZ23 <- %016lx\n", data.u64[0]); */ gif_write_xyz23(gif, data); break; case 0x06: /* printf("gif: TEX0_1 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_1, data.u64[0]); break; case 0x07: /* printf("gif: TEX0_2 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_2, data.u64[0]); break; @@ -273,11 +279,8 @@ void gif_handle_reglist(struct ps2_gif* gif, uint128_t data) { } // Note: This handles odd NREGS*NLOOP case - if (gif->tag.index == gif->tag.remaining) { - gif->state = GIF_STATE_RECV_TAG; - + if (gif->tag.index == gif->tag.remaining) break; - } } gif->tag.qwc--; diff --git a/src/ee/timers.c b/src/ee/timers.c index ed6399b..4ceb61b 100644 --- a/src/ee/timers.c +++ b/src/ee/timers.c @@ -122,14 +122,14 @@ static inline void ee_timers_write_mode(struct ps2_ee_timers* timers, int t, uin // exit(1); } - printf("timers: Timer %d mode write %08x mode=%08x counter=%04x compare=%04x clks=%d gate=%d gats=%d gatm=%d zret=%d cue=%d cmpe=%d ovfe=%d\n", - t, data, - timer->mode, - timer->counter, - timer->compare, - timer->clks, timer->gate, timer->gats, timer->gatm, - timer->zret, timer->cue, timer->cmpe, timer->ovfe - ); + // printf("timers: Timer %d mode write %08x mode=%08x counter=%04x compare=%04x clks=%d gate=%d gats=%d gatm=%d zret=%d cue=%d cmpe=%d ovfe=%d\n", + // t, data, + // timer->mode, + // timer->counter, + // timer->compare, + // timer->clks, timer->gate, timer->gats, timer->gatm, + // timer->zret, timer->cue, timer->cmpe, timer->ovfe + // ); switch (timer->clks) { case 0: timer->delta = 1; break; diff --git a/src/iop/bus.c b/src/iop/bus.c index 7d84996..a5515ab 100644 --- a/src/iop/bus.c +++ b/src/iop/bus.c @@ -123,18 +123,7 @@ uint32_t iop_bus_read8(void* udata, uint32_t addr) { MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2); - // Namco System 246 DIP switches - if (addr == 0x1f803204) { - return 0x81; - } - - if (addr == 0x1f803100) { - return 0x82; - } - - if (addr == 0x1f803200) { - return 0x83; - } + if (addr == 0x1f80146e) { return 0x30; } printf("iop_bus: Unhandled 8-bit read from physical address 0x%08x\n", addr); @@ -162,10 +151,21 @@ uint32_t iop_bus_read16(void* udata, uint32_t addr) { MAP_MEM_READ(16, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(16, 0x1E400000, 0x1E7FFFFF, bios, rom2); - // PCMCIA (CXD9566) + // 0x20 - PCMCIA (CXD9566) + // 0x30 - Expansion bay if (addr == 0x1f80146e) { return 0x30; } - // printf("iop_bus: Unhandled 16-bit read from physical address 0x%08x\n", addr); + // SPEED rev3 (Capabilities) + // bit 0 - SMAP + // bit 1 - ATA + // bit 3 - UART + // bit 4 - DVR + // bit 5 - FLASH + // if (addr == 0x10000004) { return 0x03; } + // if (addr == 0x1000205c) { return 0xffff; } + // if (addr == 0x1000205e) { return 0xffff; } + + printf("iop_bus: Unhandled 16-bit read from physical address 0x%08x\n", addr); return 0; } diff --git a/src/iop/timers.c b/src/iop/timers.c index 0b381f1..1d7bcc6 100644 --- a/src/iop/timers.c +++ b/src/iop/timers.c @@ -192,20 +192,20 @@ void iop_timer_handle_mode_write(struct ps2_iop_timers* timers, int t, uint64_t // ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(t)); } - printf("iop: Timer %d mode write %08x -> %08x gate_en=%d gate_mode=%d irq_reset=%d cmp_irq=%d ovf_irq=%d rep_irq=%d levl=%d use_ext=%d irq_en=%d t4_prescaler=%d\n", - t, timers->timer[t].mode, - data, - timers->timer[t].gate_en, - timers->timer[t].gate_mode, - timers->timer[t].irq_reset, - timers->timer[t].cmp_irq, - timers->timer[t].ovf_irq, - timers->timer[t].rep_irq, - timers->timer[t].levl, - timers->timer[t].use_ext, - timers->timer[t].irq_en, - timers->timer[t].t4_prescaler - ); + // printf("iop: Timer %d mode write %08x -> %08x gate_en=%d gate_mode=%d irq_reset=%d cmp_irq=%d ovf_irq=%d rep_irq=%d levl=%d use_ext=%d irq_en=%d t4_prescaler=%d\n", + // t, timers->timer[t].mode, + // data, + // timers->timer[t].gate_en, + // timers->timer[t].gate_mode, + // timers->timer[t].irq_reset, + // timers->timer[t].cmp_irq, + // timers->timer[t].ovf_irq, + // timers->timer[t].rep_irq, + // timers->timer[t].levl, + // timers->timer[t].use_ext, + // timers->timer[t].irq_en, + // timers->timer[t].t4_prescaler + // ); /* To-do: Schedule timer interrupts for better performance */ } diff --git a/src/iop/usb.c b/src/iop/usb.c index 7bd1910..9d59cc1 100644 --- a/src/iop/usb.c +++ b/src/iop/usb.c @@ -50,8 +50,6 @@ uint64_t ps2_usb_read32(struct ps2_usb* usb, uint32_t addr) { printf("usb: Unhandled read at %08x\n", addr); - exit(1); - return 0; } @@ -90,7 +88,5 @@ void ps2_usb_write32(struct ps2_usb* usb, uint32_t addr, uint64_t data) { printf("usb: Unhandled write at %08x (%08x)\n", addr, data); - exit(1); - return; } \ No newline at end of file diff --git a/src/shared/ram.h b/src/shared/ram.h index ec28f33..2b4d3cf 100644 --- a/src/shared/ram.h +++ b/src/shared/ram.h @@ -16,6 +16,7 @@ struct ps2_ram { #define RAM_SIZE_1KB 0x400 #define RAM_SIZE_2MB 0x200000 +#define RAM_SIZE_4MB 0x400000 #define RAM_SIZE_32MB 0x2000000 #define RAM_SIZE_64MB 0x4000000 #define RAM_SIZE_128MB 0x8000000 From e69f737af32d3bdc0860fe893f50e02a757114b2 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:40:17 -0300 Subject: [PATCH 20/26] renderer: Fix Z format blits --- src/gs/renderer/software_thread.cpp | 82 ++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/src/gs/renderer/software_thread.cpp b/src/gs/renderer/software_thread.cpp index 60ffc76..67d5641 100644 --- a/src/gs/renderer/software_thread.cpp +++ b/src/gs/renderer/software_thread.cpp @@ -559,7 +559,10 @@ void software_thread_set_size(void* udata, int width, int height) { uint64_t display = 0, dispfb = 0; - if (en1) { + if (en1 && en2) { + display = ctx->gs->display1 > ctx->gs->display2 ? ctx->gs->display1 : ctx->gs->display2; + dispfb = ctx->gs->dispfb1 > ctx->gs->dispfb2 ? ctx->gs->dispfb1 : ctx->gs->dispfb2; + } else if (en1) { display = ctx->gs->display1; dispfb = ctx->gs->dispfb1; } else if (en2) { @@ -594,7 +597,16 @@ void software_thread_set_size(void* udata, int width, int height) { // tex_h /= 2; // } - // printf("gsr: Setting framebuffer size to %dx%d fmt=%02x\n", tex_w, tex_h, ctx->disp_fmt); + // Weird dobiestation hack, should fix Silent Hill 2, Choro Q HG, etc. + if (tex_h >= (tex_w * 1.3)) + tex_h = tex_h / 2; + + // printf("gsr: Setting framebuffer size to %dx%d fmt=%02x magh=%d magv=%d en=(%d,%d) dwh=(%d,%d) dxy=(%d,%d)\n", tex_w, tex_h, tex_fmt, magh, magv, en1, en2, + // (int)((display >> 32) & 0xfff) + 1, + // (int)((display >> 44) & 0x7ff) + 1, + // (int)(display & 0xfff), + // (int)((display >> 12) & 0xfff) + // ); // Do nothing if the size hasn't changed if (tex_w == ctx->tex_w && tex_h == ctx->tex_h && tex_fmt == ctx->disp_fmt) { @@ -1851,6 +1863,28 @@ static inline uint32_t gs_generic_read(struct ps2_gs* gs, uint32_t bp, uint32_t return data >> 28; } break; + case GS_PSMZ32: { + return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff]; + } break; + case GS_PSMZ24: { + return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] & 0xffffff; + } break; + case GS_PSMZ16: { + uint32_t addr = psmz16_addr(bp, bw, u, v); + uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); + + int idx = (u & 15) + ((v & 1) * 16); + + return vram[psmct16_shift[idx] & 0xfffff]; + } break; + case GS_PSMZ16S: { + uint32_t addr = psmz16s_addr(bp, bw, u, v); + uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); + + int idx = (u & 15) + ((v & 1) * 16); + + return vram[psmct16_shift[idx] & 0xfffff]; + } break; default: { // printf("Unsupported PSMT %02x for generic read\n", gs->ctx->tbpsm); // exit(1); @@ -1863,8 +1897,11 @@ static inline void gs_generic_write(struct ps2_gs* gs, uint32_t bp, uint32_t bw, case GS_PSMCT32: gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff] = data; break; - case GS_PSMCT24: - gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff] = data; + case GS_PSMCT24: { + uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff; + + gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff); + } break; break; case GS_PSMCT16: { uint32_t addr = psmct16_addr(bp, bw, u, v); @@ -1915,6 +1952,31 @@ static inline void gs_generic_write(struct ps2_gs* gs, uint32_t bp, uint32_t bw, gs->vram[addr] = (gs->vram[addr] & 0x0fffffff) | ((data & 0xf) << 28); } break; + case GS_PSMZ32: + gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] = data; + break; + case GS_PSMZ24: { + uint32_t addr = psmz32_addr(bp, bw, u, v) & 0xfffff; + + gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff); + } break; + break; + case GS_PSMZ16: { + uint32_t addr = psmz16_addr(bp, bw, u, v); + uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); + + int idx = (u & 15) + ((v & 1) * 16); + + vram[psmct16_shift[idx] & 0xfffff] = data; + } break; + case GS_PSMZ16S: { + uint32_t addr = psmz16s_addr(bp, bw, u, v); + uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); + + int idx = (u & 15) + ((v & 1) * 16); + + vram[psmct16_shift[idx] & 0xfffff] = data; + } break; default: { // printf("Unsupported PSMT %02x for write\n", gs->ctx->tbpsm); // exit(1); @@ -2060,11 +2122,10 @@ void render_triangle(struct ps2_gs* gs, void* udata) { area = EDGE(v0, v1, v2); } - // printf("triangle: v0=(%04x,%04x) v1=(%04x,%04x) v2=(%04x,%04x) v3=(%04x,%04x)\n", + // printf("triangle: v0=(%04x,%04x) v1=(%04x,%04x) v2=(%04x,%04x)\n", // v0.x, v0.y, // v1.x, v1.y, - // v2.x, v2.y, - // gs->vq[3].x, gs->vq[3].y + // v2.x, v2.y // ); v0.x -= gs->ctx->ofx; @@ -2106,7 +2167,12 @@ void render_triangle(struct ps2_gs* gs, void* udata) { int w1_row = EDGE(v2, v0, p); int w2_row = EDGE(v0, v1, p); - // if (gs->fge) + // printf("triangle: v0=(%d.%d,%d.%d) v1=(%d.%d,%d.%d) v2=(%d.%d,%d.%d) v3=(%d.%d,%d.%d)\n", + // v0.x >> 4, (v0.x & 0xf) * 625, v0.y >> 4, (v0.y & 0xf) * 625, + // v1.x >> 4, (v1.x & 0xf) * 625, v1.y >> 4, (v1.y & 0xf) * 625, + // v2.x >> 4, (v2.x & 0xf) * 625, v2.y >> 4, (v2.y & 0xf) * 625, + // gs->vq[3].x >> 4, (gs->vq[3].x & 0xf) * 625, gs->vq[3].y >> 4, (gs->vq[3].y & 0xf) * 625 + // ); for (p.y = ymin; p.y < ymax; p.y += 16) { // Barycentric coordinates at start of row From fcda3715209eeb2c886a792ab3136db4900c0144 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:41:27 -0300 Subject: [PATCH 21/26] gs: Fix double SIGNAL writes Fixes Soulcalibur II & III --- src/gs/gs.c | 39 +++++++++++++++++++++++++-------------- src/gs/gs.h | 4 ++++ 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/gs/gs.c b/src/gs/gs.c index 422f413..8bab620 100644 --- a/src/gs/gs.c +++ b/src/gs/gs.c @@ -16,6 +16,8 @@ static inline void gs_test_gs_irq(struct ps2_gs* gs) { uint32_t stat = gs->csr & 0x1f; if (stat & (~mask)) { + // printf("gs: IRQ triggered! stat=%02x mask=%02x\n", stat, mask); + ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } } @@ -382,20 +384,26 @@ void ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data) { case 0x12001000: { if (data & 8) { // Game is requesting vsync - gs->vblank |= 1; + // gs->vblank |= 1; } gs->csr = (gs->csr & 0xfffffe00) | (gs->csr & ~(data & 0xf)); - if (gs->signal_pending && ((gs->csr & 1) == 0)) { - gs->csr |= 1; + if (data & 1) { + if (gs->signal_pending) { + gs->siglblid &= ~0xffffffffull; + gs->siglblid |= gs->stall_sigid; + } } } return; case 0x12001010: { + int prev_signal = (gs->imr >> 8) & 1; + int new_signal = (data >> 8) & 1; + gs->imr = data; - if (gs->signal_pending && (((gs->imr >> 8) & 1) == 0)) { - gs->signal_pending = 0; + if (gs->signal_pending && (prev_signal && !new_signal)) { + gs->signal_pending--; ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } @@ -685,30 +693,35 @@ void ps2_gs_write_internal(struct ps2_gs* gs, int reg, uint64_t data) { case 0x53: /* printf("gs: TRXDIR <- %016lx\n", data); */ gs->trxdir = data; gs->backend.transfer_start(gs, gs->backend.udata); return; case 0x54: gs->hwreg = data; gs->backend.transfer_write(gs, gs->backend.udata); return; case 0x60: /* printf("gs: SIGNAL <- %016lx\n", data); */ { + uint64_t mask = data >> 32; + uint64_t value = data & mask; + if (gs->csr & 1) { - gs->signal_pending = 1; + gs->signal_pending++; + + gs->stall_sigid = gs->siglblid & 0xffffffff; + gs->stall_sigid &= ~mask; + gs->stall_sigid |= value; return; } - gs->signal_pending = 0; + gs->signal_pending++; gs->signal = data; - uint64_t mask = data >> 32; - gs->csr |= 1; gs->siglblid &= ~mask; - gs->siglblid |= data & mask; + gs->siglblid |= value; gs_test_gs_irq(gs); } return; - case 0x61: { + case 0x61: /* printf("gs: FINISH <- %016lx\n", data); */ { // Trigger FINISH event gs->csr |= 2; gs_test_gs_irq(gs); } return; - case 0x62: { + case 0x62: /* printf("gs: LABEL <- %016lx\n", data); */ { gs->label = data; uint64_t mask = data >> 32; @@ -720,8 +733,6 @@ void ps2_gs_write_internal(struct ps2_gs* gs, int reg, uint64_t data) { // printf("gs: Invalid privileged register %02x write %016lx\n", reg, data); return; - - exit(1); } } } diff --git a/src/gs/gs.h b/src/gs/gs.h index 10d660d..6a5ac58 100644 --- a/src/gs/gs.h +++ b/src/gs/gs.h @@ -264,7 +264,11 @@ struct ps2_gs { uint32_t* vram; int vblank; + + // SIGNAL stuff int signal_pending; + int signal_stall; + uint32_t stall_sigid; // 1KB CLUT cache uint32_t clut_cache[0x100]; From ad8b08baab22865b2bb287b8aa20d57d06b65da6 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:42:31 -0300 Subject: [PATCH 22/26] vu: Implement hazard detection --- src/ee/vu.c | 1086 ++++++++++++++++++++++++++++++++------------------- src/ee/vu.h | 363 +++++++++-------- 2 files changed, 886 insertions(+), 563 deletions(-) diff --git a/src/ee/vu.c b/src/ee/vu.c index d59eac9..fcd80d3 100644 --- a/src/ee/vu.c +++ b/src/ee/vu.c @@ -9,34 +9,24 @@ // #define printf(fmt, ...)(0) -#define VU_LD_DEST ((vu->lower >> 21) & 0xf) -#define VU_LD_DI(i) (vu->lower & (1 << (24 - i))) -#define VU_LD_DX ((vu->lower >> 24) & 1) -#define VU_LD_DY ((vu->lower >> 23) & 1) -#define VU_LD_DZ ((vu->lower >> 22) & 1) -#define VU_LD_DW ((vu->lower >> 21) & 1) -#define VU_LD_D ((vu->lower >> 6) & 0x1f) -#define VU_LD_S ((vu->lower >> 11) & 0x1f) -#define VU_LD_T ((vu->lower >> 16) & 0x1f) -#define VU_LD_SF ((vu->lower >> 21) & 3) -#define VU_LD_TF ((vu->lower >> 23) & 3) -#define VU_LD_IMM5 (((int32_t)(VU_LD_D << 27)) >> 27) -#define VU_LD_IMM11 (((int32_t)((vu->lower & 0x7ff) << 21)) >> 21) -#define VU_LD_IMM12 ((((vu->lower >> 21) & 0x1) << 11) | (vu->lower & 0x7ff)) -#define VU_LD_IMM15 ((vu->lower & 0x7ff) | ((vu->lower & 0x1e00000) >> 10)) -#define VU_LD_IMM24 (vu->lower & 0xffffff) +#define VU_LD_DI(i) (ins->ld_di[i]) +#define VU_LD_D (ins->ld_d) +#define VU_LD_S (ins->ld_s) +#define VU_LD_T (ins->ld_t) +#define VU_LD_SF (ins->ld_sf) +#define VU_LD_TF (ins->ld_tf) +#define VU_LD_IMM5 (ins->ld_imm5) +#define VU_LD_IMM11 (ins->ld_imm11) +#define VU_LD_IMM12 (ins->ld_imm12) +#define VU_LD_IMM15 (ins->ld_imm15) +#define VU_LD_IMM24 (ins->ld_imm24) #define VU_ID vu->vi[VU_LD_D] #define VU_IS vu->vi[VU_LD_S] #define VU_IT vu->vi[VU_LD_T] -#define VU_UD_DEST ((vu->upper >> 21) & 0xf) -#define VU_UD_DI(i) (vu->upper & (1 << (24 - i))) -#define VU_UD_DX ((vu->upper >> 24) & 1) -#define VU_UD_DY ((vu->upper >> 23) & 1) -#define VU_UD_DZ ((vu->upper >> 22) & 1) -#define VU_UD_DW ((vu->upper >> 21) & 1) -#define VU_UD_D ((vu->upper >> 6) & 0x1f) -#define VU_UD_S ((vu->upper >> 11) & 0x1f) -#define VU_UD_T ((vu->upper >> 16) & 0x1f) +#define VU_UD_DI(i) (ins->ud_di[i]) +#define VU_UD_D (ins->ud_d) +#define VU_UD_S (ins->ud_s) +#define VU_UD_T (ins->ud_t) struct vu_state* vu_create(void) { return (struct vu_state*)malloc(sizeof(struct vu_state)); @@ -262,12 +252,14 @@ static inline void vu_mem_write(struct vu_state* vu, uint32_t addr, uint32_t dat } else if (addr == 0x43a) { vu->vu1->tpc = data; } else { - printf("vu: oob write\n"); + // printf("vu: oob write\n"); - exit(1); + // exit(1); } } } else { + // if (addr == 0x0000013d) *(int*)0 = 0; + vu->vu_mem[addr & 0x3ff].u32[i] = data; } } @@ -314,7 +306,7 @@ static inline uint128_t vu_mem_read(struct vu_state* vu, uint32_t addr) { } // Upper pipeline -void vu_i_abs(struct vu_state* vu) { +void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -322,7 +314,7 @@ void vu_i_abs(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, fabsf(vu_vf_i(vu, s, i))); } } -void vu_i_add(struct vu_state* vu) { +void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; @@ -339,7 +331,7 @@ void vu_i_add(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addi(struct vu_state* vu) { +void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -355,7 +347,7 @@ void vu_i_addi(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addq(struct vu_state* vu) { +void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -371,7 +363,7 @@ void vu_i_addq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addx(struct vu_state* vu) { +void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -390,7 +382,7 @@ void vu_i_addx(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addy(struct vu_state* vu) { +void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -409,7 +401,7 @@ void vu_i_addy(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addz(struct vu_state* vu) { +void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -428,7 +420,7 @@ void vu_i_addz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addw(struct vu_state* vu) { +void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -447,7 +439,7 @@ void vu_i_addw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_adda(struct vu_state* vu) { +void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -463,7 +455,7 @@ void vu_i_adda(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addai(struct vu_state* vu) { +void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { @@ -478,9 +470,8 @@ void vu_i_addai(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addaq(struct vu_state* vu) { +void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; - int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { @@ -494,7 +485,7 @@ void vu_i_addaq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addax(struct vu_state* vu) { +void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -512,7 +503,7 @@ void vu_i_addax(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_adday(struct vu_state* vu) { +void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -530,7 +521,7 @@ void vu_i_adday(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addaz(struct vu_state* vu) { +void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -548,7 +539,7 @@ void vu_i_addaz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_addaw(struct vu_state* vu) { +void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -566,7 +557,7 @@ void vu_i_addaw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_sub(struct vu_state* vu) { +void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; @@ -583,7 +574,7 @@ void vu_i_sub(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subi(struct vu_state* vu) { +void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -599,7 +590,7 @@ void vu_i_subi(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subq(struct vu_state* vu) { +void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -615,7 +606,7 @@ void vu_i_subq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subx(struct vu_state* vu) { +void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -634,7 +625,7 @@ void vu_i_subx(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_suby(struct vu_state* vu) { +void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -653,7 +644,7 @@ void vu_i_suby(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subz(struct vu_state* vu) { +void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -672,7 +663,7 @@ void vu_i_subz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subw(struct vu_state* vu) { +void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -691,7 +682,7 @@ void vu_i_subw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_suba(struct vu_state* vu) { +void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -707,7 +698,7 @@ void vu_i_suba(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subai(struct vu_state* vu) { +void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { @@ -722,9 +713,8 @@ void vu_i_subai(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subaq(struct vu_state* vu) { +void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; - int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { @@ -738,7 +728,7 @@ void vu_i_subaq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subax(struct vu_state* vu) { +void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -756,7 +746,7 @@ void vu_i_subax(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subay(struct vu_state* vu) { +void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -774,7 +764,7 @@ void vu_i_subay(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subaz(struct vu_state* vu) { +void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -792,7 +782,7 @@ void vu_i_subaz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_subaw(struct vu_state* vu) { +void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -810,7 +800,7 @@ void vu_i_subaw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mul(struct vu_state* vu) { +void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; @@ -827,7 +817,7 @@ void vu_i_mul(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_muli(struct vu_state* vu) { +void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -843,7 +833,7 @@ void vu_i_muli(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulq(struct vu_state* vu) { +void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; @@ -859,7 +849,7 @@ void vu_i_mulq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulx(struct vu_state* vu) { +void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -878,7 +868,7 @@ void vu_i_mulx(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_muly(struct vu_state* vu) { +void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -897,7 +887,7 @@ void vu_i_muly(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulz(struct vu_state* vu) { +void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -916,7 +906,7 @@ void vu_i_mulz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulw(struct vu_state* vu) { +void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -935,7 +925,7 @@ void vu_i_mulw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mula(struct vu_state* vu) { +void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -951,7 +941,7 @@ void vu_i_mula(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulai(struct vu_state* vu) { +void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { @@ -966,9 +956,8 @@ void vu_i_mulai(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulaq(struct vu_state* vu) { +void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; - int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { @@ -982,7 +971,7 @@ void vu_i_mulaq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulax(struct vu_state* vu) { +void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1000,7 +989,7 @@ void vu_i_mulax(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulay(struct vu_state* vu) { +void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1018,7 +1007,7 @@ void vu_i_mulay(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulaz(struct vu_state* vu) { +void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1036,7 +1025,7 @@ void vu_i_mulaz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_mulaw(struct vu_state* vu) { +void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1054,7 +1043,7 @@ void vu_i_mulaw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_madd(struct vu_state* vu) { +void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1071,7 +1060,7 @@ void vu_i_madd(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddi(struct vu_state* vu) { +void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1087,7 +1076,7 @@ void vu_i_maddi(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddq(struct vu_state* vu) { +void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1103,7 +1092,7 @@ void vu_i_maddq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddx(struct vu_state* vu) { +void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1122,7 +1111,7 @@ void vu_i_maddx(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddy(struct vu_state* vu) { +void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1141,7 +1130,7 @@ void vu_i_maddy(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddz(struct vu_state* vu) { +void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1160,7 +1149,7 @@ void vu_i_maddz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddw(struct vu_state* vu) { +void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1179,7 +1168,7 @@ void vu_i_maddw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_madda(struct vu_state* vu) { +void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1195,7 +1184,7 @@ void vu_i_madda(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddai(struct vu_state* vu) { +void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { @@ -1210,9 +1199,8 @@ void vu_i_maddai(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddaq(struct vu_state* vu) { +void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; - int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { @@ -1226,7 +1214,7 @@ void vu_i_maddaq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddax(struct vu_state* vu) { +void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1244,7 +1232,7 @@ void vu_i_maddax(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_madday(struct vu_state* vu) { +void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1262,7 +1250,7 @@ void vu_i_madday(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddaz(struct vu_state* vu) { +void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1280,7 +1268,7 @@ void vu_i_maddaz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_maddaw(struct vu_state* vu) { +void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1298,7 +1286,7 @@ void vu_i_maddaw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msub(struct vu_state* vu) { +void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1315,7 +1303,7 @@ void vu_i_msub(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubi(struct vu_state* vu) { +void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1331,7 +1319,7 @@ void vu_i_msubi(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubq(struct vu_state* vu) { +void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1347,7 +1335,7 @@ void vu_i_msubq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubx(struct vu_state* vu) { +void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1366,7 +1354,7 @@ void vu_i_msubx(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msuby(struct vu_state* vu) { +void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1385,7 +1373,7 @@ void vu_i_msuby(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubz(struct vu_state* vu) { +void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1404,7 +1392,7 @@ void vu_i_msubz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubw(struct vu_state* vu) { +void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1423,7 +1411,7 @@ void vu_i_msubw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msuba(struct vu_state* vu) { +void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1439,7 +1427,7 @@ void vu_i_msuba(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubai(struct vu_state* vu) { +void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { @@ -1454,9 +1442,8 @@ void vu_i_msubai(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubaq(struct vu_state* vu) { +void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; - int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { @@ -1470,7 +1457,7 @@ void vu_i_msubaq(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubax(struct vu_state* vu) { +void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1488,7 +1475,7 @@ void vu_i_msubax(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubay(struct vu_state* vu) { +void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1506,7 +1493,7 @@ void vu_i_msubay(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubaz(struct vu_state* vu) { +void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1524,7 +1511,7 @@ void vu_i_msubaz(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_msubaw(struct vu_state* vu) { +void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1542,7 +1529,7 @@ void vu_i_msubaw(struct vu_state* vu) { vu_update_status(vu); } -void vu_i_max(struct vu_state* vu) { +void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1555,7 +1542,7 @@ void vu_i_max(struct vu_state* vu) { } } } -void vu_i_maxi(struct vu_state* vu) { +void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1567,7 +1554,7 @@ void vu_i_maxi(struct vu_state* vu) { } } } -void vu_i_maxx(struct vu_state* vu) { +void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1584,7 +1571,7 @@ void vu_i_maxx(struct vu_state* vu) { } } } -void vu_i_maxy(struct vu_state* vu) { +void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1601,7 +1588,7 @@ void vu_i_maxy(struct vu_state* vu) { } } } -void vu_i_maxz(struct vu_state* vu) { +void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1618,7 +1605,7 @@ void vu_i_maxz(struct vu_state* vu) { } } } -void vu_i_maxw(struct vu_state* vu) { +void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1635,7 +1622,7 @@ void vu_i_maxw(struct vu_state* vu) { } } } -void vu_i_mini(struct vu_state* vu) { +void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1648,7 +1635,7 @@ void vu_i_mini(struct vu_state* vu) { } } } -void vu_i_minii(struct vu_state* vu) { +void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; @@ -1660,7 +1647,7 @@ void vu_i_minii(struct vu_state* vu) { } } } -void vu_i_minix(struct vu_state* vu) { +void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1675,7 +1662,7 @@ void vu_i_minix(struct vu_state* vu) { } } } -void vu_i_miniy(struct vu_state* vu) { +void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1690,7 +1677,7 @@ void vu_i_miniy(struct vu_state* vu) { } } } -void vu_i_miniz(struct vu_state* vu) { +void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1705,7 +1692,7 @@ void vu_i_miniz(struct vu_state* vu) { } } } -void vu_i_miniw(struct vu_state* vu) { +void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; @@ -1720,7 +1707,7 @@ void vu_i_miniw(struct vu_state* vu) { } } } -void vu_i_opmula(struct vu_state* vu) { +void vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1756,7 +1743,7 @@ void vu_i_opmula(struct vu_state* vu) { vu_clear_flags(vu, 3); vu_update_status(vu); } -void vu_i_opmsub(struct vu_state* vu) { +void vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; @@ -1791,10 +1778,10 @@ void vu_i_opmsub(struct vu_state* vu) { vu_clear_flags(vu, 3); vu_update_status(vu); } -void vu_i_nop(struct vu_state* vu) { +void vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } -void vu_i_ftoi0(struct vu_state* vu) { +void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1802,7 +1789,7 @@ void vu_i_ftoi0(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i))); } } -void vu_i_ftoi4(struct vu_state* vu) { +void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1810,7 +1797,7 @@ void vu_i_ftoi4(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.0625f))); } } -void vu_i_ftoi12(struct vu_state* vu) { +void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1818,7 +1805,7 @@ void vu_i_ftoi12(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000244140625f))); } } -void vu_i_ftoi15(struct vu_state* vu) { +void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1826,7 +1813,7 @@ void vu_i_ftoi15(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000030517578125f))); } } -void vu_i_itof0(struct vu_state* vu) { +void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1834,7 +1821,7 @@ void vu_i_itof0(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)vu->vf[s].s32[i]); } } -void vu_i_itof4(struct vu_state* vu) { +void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1842,7 +1829,7 @@ void vu_i_itof4(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.0625f)); } } -void vu_i_itof12(struct vu_state* vu) { +void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1850,7 +1837,7 @@ void vu_i_itof12(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000244140625f)); } } -void vu_i_itof15(struct vu_state* vu) { +void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; @@ -1858,7 +1845,7 @@ void vu_i_itof15(struct vu_state* vu) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000030517578125f)); } } -void vu_i_clip(struct vu_state* vu) { +void vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_UD_T; int s = VU_UD_S; @@ -1879,16 +1866,16 @@ void vu_i_clip(struct vu_state* vu) { } // Lower pipeline -void vu_i_b(struct vu_state* vu) { +void vu_i_b(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } -void vu_i_bal(struct vu_state* vu) { +void vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins) { // Instruction next to the delay slot VU_IT = vu->tpc + 1; vu->next_tpc = vu->tpc + VU_LD_IMM11; } -void vu_i_div(struct vu_state* vu) { +void vu_i_div(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; int s = VU_LD_S; int tf = VU_LD_TF; @@ -1897,7 +1884,7 @@ void vu_i_div(struct vu_state* vu) { vu->q.f = vu_vf_i(vu, s, sf) / vu_vf_i(vu, t, tf); vu->q.f = vu_cvtf(vu->q.u32); } -void vu_i_eatan(struct vu_state* vu) { +void vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins) { float x = vu_vf_i(vu, VU_LD_S, VU_LD_SF); if (x == -1.0f) { @@ -1908,7 +1895,7 @@ void vu_i_eatan(struct vu_state* vu) { vu->p.f = vu_atan(x); } } -void vu_i_eatanxy(struct vu_state* vu) { +void vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float y = vu_vf_y(vu, s); @@ -1921,7 +1908,7 @@ void vu_i_eatanxy(struct vu_state* vu) { vu->p.f = vu_atan(x); } } -void vu_i_eatanxz(struct vu_state* vu) { +void vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float z = vu_vf_z(vu, s); @@ -1935,7 +1922,7 @@ void vu_i_eatanxz(struct vu_state* vu) { vu->p.f = vu_atan(x); } } -void vu_i_eexp(struct vu_state* vu) { +void vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins) { const static float coeffs[] = { 0.249998688697815f, 0.031257584691048f, 0.002591371303424f, 0.000171562001924f, @@ -1959,7 +1946,7 @@ void vu_i_eexp(struct vu_state* vu) { vu->p.f = 1.0 / value; } -void vu_i_eleng(struct vu_state* vu) { +void vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); @@ -1968,10 +1955,10 @@ void vu_i_eleng(struct vu_state* vu) { vu->p.f = sqrtf(x2 + y2 + z2); } -void vu_i_ercpr(struct vu_state* vu) { +void vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / vu_vf_i(vu, VU_LD_S, VU_LD_SF); } -void vu_i_erleng(struct vu_state* vu) { +void vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); @@ -1980,7 +1967,7 @@ void vu_i_erleng(struct vu_state* vu) { vu->p.f = 1.0f / sqrtf(x2 + y2 + z2); } -void vu_i_ersadd(struct vu_state* vu) { +void vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); @@ -1989,10 +1976,10 @@ void vu_i_ersadd(struct vu_state* vu) { vu->p.f = 1.0f / (x2 + y2 + z2); } -void vu_i_ersqrt(struct vu_state* vu) { +void vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } -void vu_i_esadd(struct vu_state* vu) { +void vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); @@ -2001,105 +1988,105 @@ void vu_i_esadd(struct vu_state* vu) { vu->p.f = x2 + y2 + z2; } -void vu_i_esin(struct vu_state* vu) { +void vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sinf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } -void vu_i_esqrt(struct vu_state* vu) { +void vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } -void vu_i_esum(struct vu_state* vu) { +void vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->p.f = vu_vf_x(vu, s) + vu_vf_y(vu, s) + vu_vf_z(vu, s) + vu_vf_w(vu, s); } -void vu_i_fcand(struct vu_state* vu) { +void vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) & VU_LD_IMM24) != 0; } -void vu_i_fceq(struct vu_state* vu) { +void vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = (vu->clip & 0xffffff) == VU_LD_IMM24; } -void vu_i_fcget(struct vu_state* vu) { +void vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; vu->vi[VU_LD_T] = vu->clip & 0xfff; } -void vu_i_fcor(struct vu_state* vu) { +void vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) | VU_LD_IMM24) == 0xffffff; } -void vu_i_fcset(struct vu_state* vu) { +void vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins) { vu->clip = VU_LD_IMM24; } -void vu_i_fmand(struct vu_state* vu) { +void vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->mac_pipeline[3] & VU_IS); } -void vu_i_fmeq(struct vu_state* vu) { +void vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) == (vu->mac_pipeline[3] & 0xffff)); } -void vu_i_fmor(struct vu_state* vu) { +void vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) | (vu->mac_pipeline[3] & 0xffff)); } -void vu_i_fsand(struct vu_state* vu) { +void vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->status & VU_LD_IMM12); } -void vu_i_fseq(struct vu_state* vu) { +void vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) == VU_LD_IMM12); } -void vu_i_fsor(struct vu_state* vu) { +void vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) | VU_LD_IMM12); } -void vu_i_fsset(struct vu_state* vu) { +void vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins) { vu->status &= 0x3f; vu->status |= VU_LD_IMM12 & 0xfc0; } -void vu_i_iadd(struct vu_state* vu) { +void vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins) { // printf("iadd vi%02u, vi%02u (%04x), vi%02u (%04x)\n", VU_LD_D, VU_LD_S, VU_IS, VU_LD_T, VU_IT); vu_set_vi(vu, VU_LD_D, VU_IS + VU_IT); } -void vu_i_iaddi(struct vu_state* vu) { +void vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM5); } -void vu_i_iaddiu(struct vu_state* vu) { +void vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM15); } -void vu_i_iand(struct vu_state* vu) { +void vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_D, VU_IS & VU_IT); } -void vu_i_ibeq(struct vu_state* vu) { +void vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins) { if (VU_IT == VU_IS) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_ibgez(struct vu_state* vu) { +void vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins) { if ((int16_t)VU_IS >= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_ibgtz(struct vu_state* vu) { +void vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins) { if ((int16_t)VU_IS > 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_iblez(struct vu_state* vu) { +void vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins) { if ((int16_t)VU_IS <= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_ibltz(struct vu_state* vu) { +void vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins) { if ((int16_t)VU_IS < 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_ibne(struct vu_state* vu) { +void vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins) { // printf("ibne vi%02u (%04x), vi%02u (%04x), 0x%08x\n", VU_LD_T, VU_IT, VU_LD_S, VU_IS, vu->tpc + VU_LD_IMM11); if (VU_IT != VU_IS) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } -void vu_i_ilw(struct vu_state* vu) { +void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; @@ -2111,7 +2098,7 @@ void vu_i_ilw(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vi[t] = data.u32[i]; } } -void vu_i_ilwr(struct vu_state* vu) { +void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2124,16 +2111,16 @@ void vu_i_ilwr(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vi[t] = data.u32[i]; } } -void vu_i_ior(struct vu_state* vu) { +void vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_D, VU_IS | VU_IT); } -void vu_i_isub(struct vu_state* vu) { +void vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_D, VU_IS - VU_IT); } -void vu_i_isubiu(struct vu_state* vu) { +void vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, VU_IS - VU_LD_IMM15); } -void vu_i_isw(struct vu_state* vu) { +void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2143,7 +2130,7 @@ void vu_i_isw(struct vu_state* vu) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i); } } -void vu_i_iswr(struct vu_state* vu) { +void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2153,17 +2140,17 @@ void vu_i_iswr(struct vu_state* vu) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i); } } -void vu_i_jalr(struct vu_state* vu) { +void vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t s = VU_IS; VU_IT = vu->tpc + 1; vu->next_tpc = s; } -void vu_i_jr(struct vu_state* vu) { +void vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = VU_IS; } -void vu_i_lq(struct vu_state* vu) { +void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2176,7 +2163,7 @@ void vu_i_lq(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; } } -void vu_i_lqd(struct vu_state* vu) { +void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2191,15 +2178,17 @@ void vu_i_lqd(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; } } -void vu_i_lqi(struct vu_state* vu) { +void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; - uint32_t addr = vu->vi[s]; - uint128_t data = vu_mem_read(vu, addr); + if (t) { + uint32_t addr = vu->vi[s]; + uint128_t data = vu_mem_read(vu, addr); - for (int i = 0; i < 4; i++) { - if (VU_LD_DI(i)) if (t) vu->vf[t].u32[i] = data.u32[i]; + for (int i = 0; i < 4; i++) { + if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; + } } // printf(" vf%02u, (vi%02u++) (%04x) (%f %f %f %f)\n", @@ -2214,16 +2203,16 @@ void vu_i_lqi(struct vu_state* vu) { if (s) vu->vi[s]++; } -void vu_i_mfir(struct vu_state* vu) { +void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; for (int i = 0; i < 4; i++) { - if (VU_LD_DI(i)) vu->vf[t].u32[i] = (int32_t)(*((int16_t*)&VU_IS)); + if (VU_LD_DI(i)) vu->vf[t].u32[i] = (int32_t)(int16_t)VU_IS; } } -void vu_i_mfp(struct vu_state* vu) { +void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; @@ -2232,7 +2221,7 @@ void vu_i_mfp(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->p.f; } } -void vu_i_move(struct vu_state* vu) { +void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2242,7 +2231,7 @@ void vu_i_move(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->vf[s].u32[i]; } } -void vu_i_mr32(struct vu_state* vu) { +void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; @@ -2260,10 +2249,10 @@ void vu_i_mr32(struct vu_state* vu) { if (VU_LD_DI(2)) vu->vf[t].u32[2] = vu->vf[s].u32[3]; if (VU_LD_DI(3)) vu->vf[t].u32[3] = x; } -void vu_i_mtir(struct vu_state* vu) { +void vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->vf[VU_LD_S].u32[VU_LD_SF] & 0xffff); } -void vu_i_rget(struct vu_state* vu) { +void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; @@ -2272,7 +2261,7 @@ void vu_i_rget(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32; } } -void vu_i_rinit(struct vu_state* vu) { +void vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->r.u32 = 0x3f800000; @@ -2281,7 +2270,7 @@ void vu_i_rinit(struct vu_state* vu) { vu->r.u32 |= vu->vf[s].u32[VU_LD_SF] & 0x007fffff; } -void vu_i_rnext(struct vu_state* vu) { +void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; @@ -2297,24 +2286,26 @@ void vu_i_rnext(struct vu_state* vu) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32; } } -void vu_i_rsqrt(struct vu_state* vu) { +void vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->q.f = vu_vf_i(vu, VU_LD_S, VU_LD_SF) / sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); vu->q.f = vu_cvtf(vu->q.u32); } -void vu_i_rxor(struct vu_state* vu) { +void vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins) { vu->r.u32 = 0x3F800000 | ((vu->r.u32 ^ vu->vf[VU_LD_S].u32[VU_LD_SF]) & 0x007FFFFF); } -void vu_i_sq(struct vu_state* vu) { +void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[t] + VU_LD_IMM11; + // printf("vu: sq addr=%08x vf%02d=%08x %08x %08x %08x\n", addr, s, vu->vf[s].u32[3], vu->vf[s].u32[2], vu->vf[s].u32[1], vu->vf[s].u32[0]); + for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } } -void vu_i_sqd(struct vu_state* vu) { +void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2326,7 +2317,7 @@ void vu_i_sqd(struct vu_state* vu) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } } -void vu_i_sqi(struct vu_state* vu) { +void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; @@ -2338,19 +2329,19 @@ void vu_i_sqi(struct vu_state* vu) { vu_set_vi(vu, t, vu->vi[t] + 1); } -void vu_i_sqrt(struct vu_state* vu) { +void vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->q.f = sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); vu->q.f = vu_cvtf(vu->q.u32); } -void vu_i_waitp(struct vu_state* vu) { +void vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } -void vu_i_waitq(struct vu_state* vu) { +void vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins) { // No operation vu->q_delay = 0; } -void vu_i_xgkick(struct vu_state* vu) { +void vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t addr = VU_IS; int eop = 1; @@ -2360,6 +2351,8 @@ void vu_i_xgkick(struct vu_state* vu) { addr &= 0x7ff; + if (addr == 0) break; + // printf("tag: addr=%08x %08x %08x %08x %08x\n", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]); ps2_gif_write128(vu->gif, 0, tag); @@ -2373,8 +2366,8 @@ void vu_i_xgkick(struct vu_state* vu) { if (!nloop) continue; - // if (!nregs) - // nregs = 16; + if (!nregs) + nregs = 16; int qwc = 0; @@ -2391,6 +2384,9 @@ void vu_i_xgkick(struct vu_state* vu) { } break; } + if (qwc >= 0x7ff) + continue; + // printf("vu: nloop=%d nregs=%d eop=%d flg=%d qwc=%d\n", // nloop, // nregs, @@ -2411,13 +2407,18 @@ void vu_i_xgkick(struct vu_state* vu) { ps2_gif_write128(vu->gif, 0, vu_mem_read(vu, addr++)); addr &= 0x7ff; + + if (addr == 0) { + eop = 1; + break; + } } } while (!eop); } -void vu_i_xitop(struct vu_state* vu) { +void vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->vif->itop); } -void vu_i_xtop(struct vu_state* vu) { +void vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins) { if (vu->id == 0) { printf("vu: xtop used in VU0\n"); @@ -2538,7 +2539,224 @@ void ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data) { } } -static inline void vu_execute_upper(struct vu_state* vu, uint32_t opcode) { +#define VU_FLD_X 1 +#define VU_FLD_Y 2 +#define VU_FLD_Z 4 +#define VU_FLD_W 8 + +#define VU_DEC_UD_S_SRC_T_BROADCAST(bc, f) \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = (opcode >> 21) & 0xf; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = bc; \ + vu->upper.func = f; + +#define VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(bc, f) \ + vu->upper.dst.reg = vu->upper.ud_d; \ + vu->upper.dst.field = (opcode >> 21) & 0xf; \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = vu->upper.dst.field; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = bc; \ + vu->upper.func = f; + +#define VU_DEC_UD_D_DST_S_SRC_T_SRC(f) \ + vu->upper.dst.reg = vu->upper.ud_d; \ + vu->upper.dst.field = (opcode >> 21) & 0xf; \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = vu->upper.dst.field; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = vu->upper.dst.field; \ + vu->upper.func = f; + +#define VU_DEC_UD_D_DST_S_SRC(f) \ + vu->upper.dst.reg = vu->upper.ud_d; \ + vu->upper.dst.field = (opcode >> 21) & 0xf; \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = vu->upper.dst.field; \ + vu->upper.func = f; + +#define VU_DEC_UD_T_DST_S_SRC(f) \ + vu->upper.dst.reg = vu->upper.ud_t; \ + vu->upper.dst.field = (opcode >> 21) & 0xf; \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = vu->upper.dst.field; \ + vu->upper.func = f; + +#define VU_DEC_UD_S_SRC_T_SRC(f) \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = (opcode >> 21) & 0xf; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = vu->upper.src[0].field; \ + vu->upper.func = f; + +#define VU_DEC_UD_S_SRC(f) \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = (opcode >> 21) & 0xf; \ + vu->upper.func = f; + +#define VU_DEC_OPMULA() \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = vu->upper.src[0].field; \ + vu->upper.func = vu_i_opmula; + +#define VU_DEC_OPMSUB() \ + vu->upper.dst.reg = vu->upper.ud_d; \ + vu->upper.dst.field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = vu->upper.dst.field; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = vu->upper.dst.field; \ + vu->upper.func = vu_i_opmsub; + +#define VU_DEC_CLIP() \ + vu->upper.src[0].reg = vu->upper.ud_s; \ + vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ + vu->upper.src[1].reg = vu->upper.ud_t; \ + vu->upper.src[1].field = VU_FLD_W; \ + vu->upper.func = vu_i_clip; + +#define VU_DEC_LD_NONE(f) \ + vu->lower.func = f; + +#define VU_DEC_UD_NONE(f) \ + vu->upper.func = f; + +#define VU_DEC_LD_T_DST_S_VISRC(f) \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_DST_S_VISRC_S_VIDST(f) \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.vi_dst = vu->lower.ld_s; \ + vu->lower.vi_src[0] = vu->lower.vi_dst; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(f) \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = (opcode >> 21) & 0xf; \ + vu->lower.vi_dst = vu->lower.ld_t; \ + vu->lower.vi_src[0] = vu->lower.vi_dst; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_SF_SRC_T_TF_SRC(f) \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = vu->lower.ld_sf; \ + vu->lower.src[1].reg = vu->lower.ld_t; \ + vu->lower.src[1].field = vu->lower.ld_tf; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_VIDST_S_SF_SRC(f) \ + vu->lower.vi_dst = vu->lower.ld_t; \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = vu->lower.ld_sf; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_DST_S_VISRC(f) \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_TF_SRC(f) \ + vu->lower.src[0].reg = vu->lower.ld_t; \ + vu->lower.src[0].field = vu->lower.ld_tf; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_SRC_T_VISRC(f) \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = (opcode >> 21) & 0xf; \ + vu->lower.vi_src[0] = vu->lower.ld_t; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_VIDST_S_VISRC(f) \ + vu->lower.vi_dst = vu->lower.ld_t; \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_VISRC_T_VISRC(f) \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.vi_src[1] = vu->lower.ld_t; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_VIDST_S_VISRC(f) \ + vu->lower.vi_dst = vu->lower.ld_t; \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_VISRC_S_VISRC(f) \ + vu->lower.vi_src[0] = vu->lower.ld_t; \ + vu->lower.vi_src[1] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_VIDST(v, f) \ + vu->lower.vi_dst = v; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_VIDST(f) \ + vu->lower.vi_dst = vu->lower.ld_t; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_VISRC(f) \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_FLD_SRC(fld, f) \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = fld; \ + vu->lower.func = f; + +#define VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(f) \ + vu->lower.vi_dst = vu->lower.ld_d; \ + vu->lower.vi_src[0] = vu->lower.ld_s; \ + vu->lower.vi_src[1] = vu->lower.ld_t; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_DST_S_SRC(f) \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = vu->lower.dst.field; \ + vu->lower.func = f; + +#define VU_DEC_LD_T_DST(f) \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.func = f; + +#define VU_DEC_LD_S_SF_SRC(f) \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = vu->lower.ld_sf; \ + vu->lower.func = f; + +#define VU_DEC_MR32() \ + vu->lower.dst.reg = vu->lower.ld_t; \ + vu->lower.dst.field = (opcode >> 21) & 0xf; \ + vu->lower.src[0].reg = vu->lower.ld_s; \ + vu->lower.src[0].field = (vu->lower.dst.field >> 1) | ((vu->lower.dst.field & 1) << 3); \ + vu->lower.func = vu_i_mr32; + +void vu_decode_upper(struct vu_state* vu, uint32_t opcode) { + vu->upper.ud_d = (opcode >> 6) & 0x1f; + vu->upper.ud_s = (opcode >> 11) & 0x1f; + vu->upper.ud_t = (opcode >> 16) & 0x1f; + + for (int i = 0; i < 4; i++) + vu->upper.ud_di[i] = opcode & (1 << (24 - i)); + + vu->upper.func = NULL; + vu->upper.dst.reg = 0; + vu->upper.dst.field = 0; + vu->upper.src[0].reg = 0; + vu->upper.src[0].field = 0; + vu->upper.src[1].reg = 0; + vu->upper.src[1].field = 0; + // Decode 000007FF style instruction if ((opcode & 0x3c) == 0x3c) { // 0EEEE 1111 EE @@ -2550,186 +2768,211 @@ static inline void vu_execute_upper(struct vu_state* vu, uint32_t opcode) { // bits 0-1 and bits 6-9 (6 bits) are enough to decode // all of the following switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) { - case 0x00: vu_i_addax(vu); return; - case 0x01: vu_i_adday(vu); return; - case 0x02: vu_i_addaz(vu); return; - case 0x03: vu_i_addaw(vu); return; - case 0x04: vu_i_subax(vu); return; - case 0x05: vu_i_subay(vu); return; - case 0x06: vu_i_subaz(vu); return; - case 0x07: vu_i_subaw(vu); return; - case 0x08: vu_i_maddax(vu); return; - case 0x09: vu_i_madday(vu); return; - case 0x0A: vu_i_maddaz(vu); return; - case 0x0B: vu_i_maddaw(vu); return; - case 0x0C: vu_i_msubax(vu); return; - case 0x0D: vu_i_msubay(vu); return; - case 0x0E: vu_i_msubaz(vu); return; - case 0x0F: vu_i_msubaw(vu); return; - case 0x10: vu_i_itof0(vu); return; - case 0x11: vu_i_itof4(vu); return; - case 0x12: vu_i_itof12(vu); return; - case 0x13: vu_i_itof15(vu); return; - case 0x14: vu_i_ftoi0(vu); return; - case 0x15: vu_i_ftoi4(vu); return; - case 0x16: vu_i_ftoi12(vu); return; - case 0x17: vu_i_ftoi15(vu); return; - case 0x18: vu_i_mulax(vu); return; - case 0x19: vu_i_mulay(vu); return; - case 0x1A: vu_i_mulaz(vu); return; - case 0x1B: vu_i_mulaw(vu); return; - case 0x1C: vu_i_mulaq(vu); return; - case 0x1D: vu_i_abs(vu); return; - case 0x1E: vu_i_mulai(vu); return; - case 0x1F: vu_i_clip(vu); return; - case 0x20: vu_i_addaq(vu); return; - case 0x21: vu_i_maddaq(vu); return; - case 0x22: vu_i_addai(vu); return; - case 0x23: vu_i_maddai(vu); return; - case 0x24: vu_i_subaq(vu); return; - case 0x25: vu_i_msubaq(vu); return; - case 0x26: vu_i_subai(vu); return; - case 0x27: vu_i_msubai(vu); return; - case 0x28: vu_i_adda(vu); return; - case 0x29: vu_i_madda(vu); return; - case 0x2A: vu_i_mula(vu); return; - case 0x2C: vu_i_suba(vu); return; - case 0x2D: vu_i_msuba(vu); return; - case 0x2E: vu_i_opmula(vu); return; - case 0x2F: vu_i_nop(vu); return; + case 0x00: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addax); return; + case 0x01: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_adday); return; + case 0x02: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addaz); return; + case 0x03: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addaw); return; + case 0x04: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subax); return; + case 0x05: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_subay); return; + case 0x06: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subaz); return; + case 0x07: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subaw); return; + case 0x08: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddax); return; + case 0x09: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_madday); return; + case 0x0A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddaz); return; + case 0x0B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddaw); return; + case 0x0C: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubax); return; + case 0x0D: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msubay); return; + case 0x0E: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubaz); return; + case 0x0F: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubaw); return; + case 0x10: VU_DEC_UD_T_DST_S_SRC(vu_i_itof0); return; + case 0x11: VU_DEC_UD_T_DST_S_SRC(vu_i_itof4); return; + case 0x12: VU_DEC_UD_T_DST_S_SRC(vu_i_itof12); return; + case 0x13: VU_DEC_UD_T_DST_S_SRC(vu_i_itof15); return; + case 0x14: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi0); return; + case 0x15: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi4); return; + case 0x16: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi12); return; + case 0x17: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi15); return; + case 0x18: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulax); return; + case 0x19: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_mulay); return; + case 0x1A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulaz); return; + case 0x1B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulaw); return; + case 0x1C: VU_DEC_UD_S_SRC(vu_i_mulaq); return; + case 0x1D: VU_DEC_UD_T_DST_S_SRC(vu_i_abs); return; + case 0x1E: VU_DEC_UD_S_SRC(vu_i_mulai); return; + case 0x1F: VU_DEC_CLIP(); return; + case 0x20: VU_DEC_UD_S_SRC(vu_i_addaq); return; + case 0x21: VU_DEC_UD_S_SRC(vu_i_maddaq); return; + case 0x22: VU_DEC_UD_S_SRC(vu_i_addai); return; + case 0x23: VU_DEC_UD_S_SRC(vu_i_maddai); return; + case 0x24: VU_DEC_UD_S_SRC(vu_i_subaq); return; + case 0x25: VU_DEC_UD_S_SRC(vu_i_msubaq); return; + case 0x26: VU_DEC_UD_S_SRC(vu_i_subai); return; + case 0x27: VU_DEC_UD_S_SRC(vu_i_msubai); return; + case 0x28: VU_DEC_UD_S_SRC_T_SRC(vu_i_adda); return; + case 0x29: VU_DEC_UD_S_SRC_T_SRC(vu_i_madda); return; + case 0x2A: VU_DEC_UD_S_SRC_T_SRC(vu_i_mula); return; + case 0x2C: VU_DEC_UD_S_SRC_T_SRC(vu_i_suba); return; + case 0x2D: VU_DEC_UD_S_SRC_T_SRC(vu_i_msuba); return; + case 0x2E: VU_DEC_OPMULA(); return; + case 0x2F: VU_DEC_UD_NONE(vu_i_nop); return; } } else { // Decode 0000003F style instruction switch (opcode & 0x3f) { - case 0x00: vu_i_addx(vu); return; - case 0x01: vu_i_addy(vu); return; - case 0x02: vu_i_addz(vu); return; - case 0x03: vu_i_addw(vu); return; - case 0x04: vu_i_subx(vu); return; - case 0x05: vu_i_suby(vu); return; - case 0x06: vu_i_subz(vu); return; - case 0x07: vu_i_subw(vu); return; - case 0x08: vu_i_maddx(vu); return; - case 0x09: vu_i_maddy(vu); return; - case 0x0A: vu_i_maddz(vu); return; - case 0x0B: vu_i_maddw(vu); return; - case 0x0C: vu_i_msubx(vu); return; - case 0x0D: vu_i_msuby(vu); return; - case 0x0E: vu_i_msubz(vu); return; - case 0x0F: vu_i_msubw(vu); return; - case 0x10: vu_i_maxx(vu); return; - case 0x11: vu_i_maxy(vu); return; - case 0x12: vu_i_maxz(vu); return; - case 0x13: vu_i_maxw(vu); return; - case 0x14: vu_i_minix(vu); return; - case 0x15: vu_i_miniy(vu); return; - case 0x16: vu_i_miniz(vu); return; - case 0x17: vu_i_miniw(vu); return; - case 0x18: vu_i_mulx(vu); return; - case 0x19: vu_i_muly(vu); return; - case 0x1A: vu_i_mulz(vu); return; - case 0x1B: vu_i_mulw(vu); return; - case 0x1C: vu_i_mulq(vu); return; - case 0x1D: vu_i_maxi(vu); return; - case 0x1E: vu_i_muli(vu); return; - case 0x1F: vu_i_minii(vu); return; - case 0x20: vu_i_addq(vu); return; - case 0x21: vu_i_maddq(vu); return; - case 0x22: vu_i_addi(vu); return; - case 0x23: vu_i_maddi(vu); return; - case 0x24: vu_i_subq(vu); return; - case 0x25: vu_i_msubq(vu); return; - case 0x26: vu_i_subi(vu); return; - case 0x27: vu_i_msubi(vu); return; - case 0x28: vu_i_add(vu); return; - case 0x29: vu_i_madd(vu); return; - case 0x2A: vu_i_mul(vu); return; - case 0x2B: vu_i_max(vu); return; - case 0x2C: vu_i_sub(vu); return; - case 0x2D: vu_i_msub(vu); return; - case 0x2E: vu_i_opmsub(vu); return; - case 0x2F: vu_i_mini(vu); return; - } - } -} - -static inline void vu_execute_lower(struct vu_state* vu, uint32_t opcode) { + case 0x00: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addx); return; + case 0x01: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_addy); return; + case 0x02: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addz); return; + case 0x03: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addw); return; + case 0x04: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subx); return; + case 0x05: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_suby); return; + case 0x06: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subz); return; + case 0x07: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subw); return; + case 0x08: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddx); return; + case 0x09: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maddy); return; + case 0x0A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddz); return; + case 0x0B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddw); return; + case 0x0C: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubx); return; + case 0x0D: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msuby); return; + case 0x0E: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubz); return; + case 0x0F: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubw); return; + case 0x10: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maxx); return; + case 0x11: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maxy); return; + case 0x12: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maxz); return; + case 0x13: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maxw); return; + case 0x14: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_minix); return; + case 0x15: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_miniy); return; + case 0x16: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_miniz); return; + case 0x17: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_miniw); return; + case 0x18: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulx); return; + case 0x19: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_muly); return; + case 0x1A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulz); return; + case 0x1B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulw); return; + case 0x1C: VU_DEC_UD_D_DST_S_SRC(vu_i_mulq); return; + case 0x1D: VU_DEC_UD_D_DST_S_SRC(vu_i_maxi); return; + case 0x1E: VU_DEC_UD_D_DST_S_SRC(vu_i_muli); return; + case 0x1F: VU_DEC_UD_D_DST_S_SRC(vu_i_minii); return; + case 0x20: VU_DEC_UD_D_DST_S_SRC(vu_i_addq); return; + case 0x21: VU_DEC_UD_D_DST_S_SRC(vu_i_maddq); return; + case 0x22: VU_DEC_UD_D_DST_S_SRC(vu_i_addi); return; + case 0x23: VU_DEC_UD_D_DST_S_SRC(vu_i_maddi); return; + case 0x24: VU_DEC_UD_D_DST_S_SRC(vu_i_subq); return; + case 0x25: VU_DEC_UD_D_DST_S_SRC(vu_i_msubq); return; + case 0x26: VU_DEC_UD_D_DST_S_SRC(vu_i_subi); return; + case 0x27: VU_DEC_UD_D_DST_S_SRC(vu_i_msubi); return; + case 0x28: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_add); return; + case 0x29: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_madd); return; + case 0x2A: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mul); return; + case 0x2B: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_max); return; + case 0x2C: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_sub); return; + case 0x2D: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_msub); return; + case 0x2E: VU_DEC_OPMSUB(); return; + case 0x2F: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mini); return; + } + } +} + +void vu_decode_lower(struct vu_state* vu, uint32_t opcode) { + vu->lower.ld_d = (opcode >> 6) & 0x1f; + vu->lower.ld_s = (opcode >> 11) & 0x1f; + vu->lower.ld_t = (opcode >> 16) & 0x1f; + vu->lower.ld_sf = (opcode >> 21) & 3; + vu->lower.ld_tf = (opcode >> 23) & 3; + vu->lower.ld_imm5 = ((int32_t)(((opcode >> 6) & 0x1f) << 27)) >> 27; + vu->lower.ld_imm11 = ((int32_t)((opcode & 0x7ff) << 21)) >> 21; + vu->lower.ld_imm12 = (((opcode >> 21) & 1) << 11) | (opcode & 0x7ff); + vu->lower.ld_imm15 = (opcode & 0x7ff) | ((opcode & 0x1e00000) >> 10); + vu->lower.ld_imm24 = opcode & 0xffffff; + + for (int i = 0; i < 4; i++) + vu->lower.ld_di[i] = opcode & (1 << (24 - i)); + + vu->lower.func = NULL; + vu->lower.dst.reg = 0; + vu->lower.dst.field = 0; + vu->lower.src[0].reg = 0; + vu->lower.src[0].field = 0; + vu->lower.src[1].reg = 0; + vu->lower.src[1].field = 0; + vu->lower.vi_src[0] = 0; + vu->lower.vi_src[1] = 0; + vu->lower.vi_dst = 0; + switch ((opcode & 0xFE000000) >> 25) { - case 0x00: vu_i_lq(vu); return; - case 0x01: vu_i_sq(vu); return; - case 0x04: vu_i_ilw(vu); return; - case 0x05: vu_i_isw(vu); return; - case 0x08: vu_i_iaddiu(vu); return; - case 0x09: vu_i_isubiu(vu); return; - case 0x10: vu_i_fceq(vu); return; - case 0x11: vu_i_fcset(vu); return; - case 0x12: vu_i_fcand(vu); return; - case 0x13: vu_i_fcor(vu); return; - case 0x14: vu_i_fseq(vu); return; - case 0x15: vu_i_fsset(vu); return; - case 0x16: vu_i_fsand(vu); return; - case 0x17: vu_i_fsor(vu); return; - case 0x18: vu_i_fmeq(vu); return; - case 0x1A: vu_i_fmand(vu); return; - case 0x1B: vu_i_fmor(vu); return; - case 0x1C: vu_i_fcget(vu); return; - case 0x20: vu_i_b(vu); return; - case 0x21: vu_i_bal(vu); return; - case 0x24: vu_i_jr(vu); return; - case 0x25: vu_i_jalr(vu); return; - case 0x28: vu_i_ibeq(vu); return; - case 0x29: vu_i_ibne(vu); return; - case 0x2C: vu_i_ibltz(vu); return; - case 0x2D: vu_i_ibgtz(vu); return; - case 0x2E: vu_i_iblez(vu); return; - case 0x2F: vu_i_ibgez(vu); return; + case 0x00: VU_DEC_LD_T_DST_S_VISRC(vu_i_lq); return; + case 0x01: VU_DEC_LD_S_SRC_T_VISRC(vu_i_sq); return; + case 0x04: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilw); return; + case 0x05: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_isw); return; + case 0x08: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddiu); return; + case 0x09: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_isubiu); return; + case 0x10: VU_DEC_LD_VIDST(1, vu_i_fceq); return; + case 0x11: VU_DEC_LD_NONE(vu_i_fcset); return; + case 0x12: VU_DEC_LD_VIDST(1, vu_i_fcand); return; + case 0x13: VU_DEC_LD_VIDST(1, vu_i_fcor); return; + case 0x14: VU_DEC_LD_T_VIDST(vu_i_fseq); return; + case 0x15: VU_DEC_LD_NONE(vu_i_fsset); return; + case 0x16: VU_DEC_LD_T_VIDST(vu_i_fsand); return; + case 0x17: VU_DEC_LD_T_VIDST(vu_i_fsor); return; + case 0x18: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmeq); return; + case 0x1A: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmand); return; + case 0x1B: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmor); return; + case 0x1C: VU_DEC_LD_T_VIDST(vu_i_fcget); return; + case 0x20: VU_DEC_LD_NONE(vu_i_b); return; + case 0x21: VU_DEC_LD_T_VIDST(vu_i_bal); return; + case 0x24: VU_DEC_LD_S_VISRC(vu_i_jr); return; + case 0x25: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_jalr); return; + case 0x28: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibeq); return; + case 0x29: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibne); return; + case 0x2C: VU_DEC_LD_S_VISRC(vu_i_ibltz); return; + case 0x2D: VU_DEC_LD_S_VISRC(vu_i_ibgtz); return; + case 0x2E: VU_DEC_LD_S_VISRC(vu_i_iblez); return; + case 0x2F: VU_DEC_LD_S_VISRC(vu_i_ibgez); return; case 0x40: { if ((opcode & 0x3C) == 0x3C) { switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) { - case 0x30: vu_i_move(vu); return; - case 0x31: vu_i_mr32(vu); return; - case 0x34: vu_i_lqi(vu); return; - case 0x35: vu_i_sqi(vu); return; - case 0x36: vu_i_lqd(vu); return; - case 0x37: vu_i_sqd(vu); return; - case 0x38: vu_i_div(vu); return; - case 0x39: vu_i_sqrt(vu); return; - case 0x3A: vu_i_rsqrt(vu); return; - case 0x3B: vu_i_waitq(vu); return; - case 0x3C: vu_i_mtir(vu); return; - case 0x3D: vu_i_mfir(vu); return; - case 0x3E: vu_i_ilwr(vu); return; - case 0x3F: vu_i_iswr(vu); return; - case 0x40: vu_i_rnext(vu); return; - case 0x41: vu_i_rget(vu); return; - case 0x42: vu_i_rinit(vu); return; - case 0x43: vu_i_rxor(vu); return; - case 0x64: vu_i_mfp(vu); return; - case 0x68: vu_i_xtop(vu); return; - case 0x69: vu_i_xitop(vu); return; - case 0x6C: vu_i_xgkick(vu); return; - case 0x70: vu_i_esadd(vu); return; - case 0x71: vu_i_ersadd(vu); return; - case 0x72: vu_i_eleng(vu); return; - case 0x73: vu_i_erleng(vu); return; - case 0x74: vu_i_eatanxy(vu); return; - case 0x75: vu_i_eatanxz(vu); return; - case 0x76: vu_i_esum(vu); return; - case 0x78: vu_i_esqrt(vu); return; - case 0x79: vu_i_ersqrt(vu); return; - case 0x7A: vu_i_ercpr(vu); return; - case 0x7B: vu_i_waitp(vu); return; - case 0x7C: vu_i_esin(vu); return; - case 0x7D: vu_i_eatan(vu); return; - case 0x7E: vu_i_eexp(vu); return; + case 0x30: VU_DEC_LD_T_DST_S_SRC(vu_i_move); return; + case 0x31: VU_DEC_MR32(); return; + case 0x34: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqi); return; + case 0x35: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqi); return; + case 0x36: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqd); return; + case 0x37: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqd); return; + case 0x38: VU_DEC_LD_S_SF_SRC_T_TF_SRC(vu_i_div); return; + case 0x39: VU_DEC_LD_T_TF_SRC(vu_i_sqrt); return; + case 0x3A: VU_DEC_LD_S_SF_SRC_T_TF_SRC(vu_i_rsqrt); return; + case 0x3B: VU_DEC_LD_NONE(vu_i_waitq); return; + case 0x3C: VU_DEC_LD_T_VIDST_S_SF_SRC(vu_i_mtir); return; + case 0x3D: VU_DEC_LD_T_DST_S_VISRC(vu_i_mfir); return; + case 0x3E: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilwr); return; + case 0x3F: VU_DEC_LD_T_VISRC_S_VISRC(vu_i_iswr); return; + case 0x40: VU_DEC_LD_T_DST(vu_i_rnext); return; + case 0x41: VU_DEC_LD_T_DST(vu_i_rget); return; + case 0x42: VU_DEC_LD_S_SF_SRC(vu_i_rinit); return; + case 0x43: VU_DEC_LD_S_SF_SRC(vu_i_rxor); return; + case 0x64: VU_DEC_LD_T_DST(vu_i_mfp); return; + case 0x68: VU_DEC_LD_T_VIDST(vu_i_xtop); return; + case 0x69: VU_DEC_LD_T_VIDST(vu_i_xitop); return; + case 0x6C: VU_DEC_LD_S_VISRC(vu_i_xgkick); return; + case 0x70: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_esadd); return; + case 0x71: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_ersadd); return; + case 0x72: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_eleng); return; + case 0x73: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_erleng); return; + case 0x74: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y, vu_i_eatanxy); return; + case 0x75: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Z, vu_i_eatanxz); return; + case 0x76: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z | VU_FLD_W, vu_i_esum); return; + case 0x78: VU_DEC_LD_S_SF_SRC(vu_i_esqrt); return; + case 0x79: VU_DEC_LD_S_SF_SRC(vu_i_ersqrt); return; + case 0x7A: VU_DEC_LD_S_SF_SRC(vu_i_ercpr); return; + case 0x7B: VU_DEC_LD_NONE(vu_i_waitp); return; + case 0x7C: VU_DEC_LD_S_SF_SRC(vu_i_esin); return; + case 0x7D: VU_DEC_LD_S_SF_SRC(vu_i_eatan); return; + case 0x7E: VU_DEC_LD_S_SF_SRC(vu_i_eexp); return; } } else { switch (opcode & 0x3F) { - case 0x30: vu_i_iadd(vu); return; - case 0x31: vu_i_isub(vu); return; - case 0x32: vu_i_iaddi(vu); return; - case 0x34: vu_i_iand(vu); return; - case 0x35: vu_i_ior(vu); return; + case 0x30: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iadd); return; + case 0x31: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_isub); return; + case 0x32: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddi); return; + case 0x34: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iand); return; + case 0x35: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_ior); return; } } } break; @@ -2750,8 +2993,6 @@ void vu_execute_program(struct vu_state* vu, uint32_t addr) { vu->tpc = addr; vu->next_tpc = addr + 1; - vu->upper = 0; - vu->lower = 0; vu->i_bit = 0; vu->e_bit = 0; vu->m_bit = 0; @@ -2773,36 +3014,83 @@ void vu_execute_program(struct vu_state* vu, uint32_t addr) { delayed_e_bit = vu->e_bit != 0; - vu->upper = liw >> 32; - vu->lower = liw & 0xffffffff; - vu->i_bit = (vu->upper & 0x80000000) != 0; - vu->e_bit = (vu->upper & 0x40000000) != 0; - vu->m_bit = (vu->upper & 0x20000000) != 0; - vu->d_bit = (vu->upper & 0x10000000) != 0; - vu->t_bit = (vu->upper & 0x08000000) != 0; + uint32_t upper = liw >> 32; + uint32_t lower = liw & 0xffffffff; + + vu->i_bit = (upper & 0x80000000) != 0; + vu->e_bit = (upper & 0x40000000) != 0; + vu->m_bit = (upper & 0x20000000) != 0; + vu->d_bit = (upper & 0x10000000) != 0; + vu->t_bit = (upper & 0x08000000) != 0; vu->q_delay--; vu_update_status(vu); + + vu_decode_upper(vu, upper & 0x7ffffff); - // printf("%04x: %08x %08x %s", tpc, vu->upper, vu->lower, vu->e_bit ? "[e] " : " "); - - // vu->status = vu->mac_pipeline[3]; + // char ubuf[512]; + // printf("%04x: %08x %08x ", tpc, upper, lower); + // printf("%-40s", vu_disassemble_upper(ubuf, upper & 0x7ffffff, &ds)); - vu_execute_upper(vu, vu->upper & 0x7ffffff); - if (vu->i_bit) { - // printf("loi %08x\n", vu->lower); + // printf("loi 0x%08x\n", lower); + + vu->upper.func(vu, &vu->upper); // LOI - vu->i.u32 = vu->lower; - } else { - // char ud[512], ld[512]; + vu->i.u32 = lower; + vu->lower.func = NULL; + vu->lower.dst.reg = 0; + vu->lower.dst.field = 0; + vu->lower.src[0].reg = 0; + vu->lower.src[0].field = 0; + vu->lower.src[1].reg = 0; + vu->lower.src[1].field = 0; + vu->lower.vi_src[0] = 0; + vu->lower.vi_src[1] = 0; + vu->lower.vi_dst = 0; + } else { + vu_decode_lower(vu, lower); + + // char lbuf[512]; + // printf("%-40s\n", vu_disassemble_lower(lbuf, lower & 0xffffffff, &ds)); + + int hazard0 = vu->upper.dst.reg == vu->lower.src[0].reg; + int hazard1 = vu->upper.dst.reg == vu->lower.src[1].reg; + int hazard2 = vu->upper.dst.reg == vu->lower.dst.reg; + + if (!vu->upper.dst.reg) { + vu->upper.func(vu, &vu->upper); + vu->lower.func(vu, &vu->lower); + } else if (hazard0 || hazard1) { + // Upper instruction writes to a register that the lower + // instruction reads from. In this case the lower instruction + // gets the previous value of the register, executing the lower + // instruction first does the trick. + + vu->lower.func(vu, &vu->lower); + vu->upper.func(vu, &vu->upper); + } else if (hazard2) { + // Upper and lower instructions write to the same register. + // In this case the upper instruction takes priority, so we + // restore the value of the register after executing the lower + // instruction. + + vu->upper.func(vu, &vu->upper); + + struct vu_reg tmp = vu->vf[vu->upper.dst.reg]; + + vu->lower.func(vu, &vu->lower); + + vu->vf[vu->upper.dst.reg] = tmp; + } else { + vu->upper.func(vu, &vu->upper); + vu->lower.func(vu, &vu->lower); + } + } - // printf("%-40s%-40s\n", vu_disassemble_upper(ud, vu->upper, &ds), vu_disassemble_lower(ld, vu->lower, &ds)); - vu_execute_lower(vu, vu->lower); - } vu->mac_pipeline[3] = vu->mac_pipeline[2]; vu->mac_pipeline[2] = vu->mac_pipeline[1]; @@ -2937,8 +3225,6 @@ void ps2_vu_reset(struct vu_state* vu) { vu->clip_pipeline[3] = 0; vu->tpc = 0; vu->next_tpc = 1; - vu->upper = 0; - vu->lower = 0; vu->i_bit = 0; vu->e_bit = 0; vu->m_bit = 0; @@ -2948,4 +3234,12 @@ void ps2_vu_reset(struct vu_state* vu) { vu->vf[0].w = 1.0; } +void ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode) { + vu_decode_upper(vu, opcode); +} + +void ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode) { + vu_decode_lower(vu, opcode); +} + // #undef printf \ No newline at end of file diff --git a/src/ee/vu.h b/src/ee/vu.h index de6a81a..764a0f5 100644 --- a/src/ee/vu.h +++ b/src/ee/vu.h @@ -29,11 +29,41 @@ struct vu_reg { }; }; +struct vu_instruction { + uint32_t ld_di[4]; + uint32_t ld_d; + uint32_t ld_s; + uint32_t ld_t; + uint32_t ld_sf; + uint32_t ld_tf; + int32_t ld_imm5; + int32_t ld_imm11; + uint32_t ld_imm12; + uint32_t ld_imm15; + uint32_t ld_imm24; + uint32_t ud_di[4]; + uint32_t ud_d; + uint32_t ud_s; + uint32_t ud_t; + + struct { + int reg; + int field; + } dst, src[2]; + + int vi_dst; + int vi_src[2]; + + void (*func)(struct vu_state* vu, const struct vu_instruction* i); +}; + struct vu_state { struct vu_reg vf[32]; uint16_t vi[16]; struct vu_reg acc; + struct vu_instruction upper, lower; + uint64_t micro_mem[0x800]; uint128_t vu_mem[0x400]; @@ -41,9 +71,6 @@ struct vu_state { int vu_mem_size; int id; - uint32_t upper; - uint32_t lower; - int i_bit; int e_bit; int m_bit; @@ -117,172 +144,172 @@ void vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* v void vu_destroy(struct vu_state* vu); // Upper pipeline -void vu_i_abs(struct vu_state* vu); -void vu_i_add(struct vu_state* vu); -void vu_i_addi(struct vu_state* vu); -void vu_i_addq(struct vu_state* vu); -void vu_i_addx(struct vu_state* vu); -void vu_i_addy(struct vu_state* vu); -void vu_i_addz(struct vu_state* vu); -void vu_i_addw(struct vu_state* vu); -void vu_i_adda(struct vu_state* vu); -void vu_i_addai(struct vu_state* vu); -void vu_i_addaq(struct vu_state* vu); -void vu_i_addax(struct vu_state* vu); -void vu_i_adday(struct vu_state* vu); -void vu_i_addaz(struct vu_state* vu); -void vu_i_addaw(struct vu_state* vu); -void vu_i_sub(struct vu_state* vu); -void vu_i_subi(struct vu_state* vu); -void vu_i_subq(struct vu_state* vu); -void vu_i_subx(struct vu_state* vu); -void vu_i_suby(struct vu_state* vu); -void vu_i_subz(struct vu_state* vu); -void vu_i_subw(struct vu_state* vu); -void vu_i_suba(struct vu_state* vu); -void vu_i_subai(struct vu_state* vu); -void vu_i_subaq(struct vu_state* vu); -void vu_i_subax(struct vu_state* vu); -void vu_i_subay(struct vu_state* vu); -void vu_i_subaz(struct vu_state* vu); -void vu_i_subaw(struct vu_state* vu); -void vu_i_mul(struct vu_state* vu); -void vu_i_muli(struct vu_state* vu); -void vu_i_mulq(struct vu_state* vu); -void vu_i_mulx(struct vu_state* vu); -void vu_i_muly(struct vu_state* vu); -void vu_i_mulz(struct vu_state* vu); -void vu_i_mulw(struct vu_state* vu); -void vu_i_mula(struct vu_state* vu); -void vu_i_mulai(struct vu_state* vu); -void vu_i_mulaq(struct vu_state* vu); -void vu_i_mulax(struct vu_state* vu); -void vu_i_mulay(struct vu_state* vu); -void vu_i_mulaz(struct vu_state* vu); -void vu_i_mulaw(struct vu_state* vu); -void vu_i_madd(struct vu_state* vu); -void vu_i_maddi(struct vu_state* vu); -void vu_i_maddq(struct vu_state* vu); -void vu_i_maddx(struct vu_state* vu); -void vu_i_maddy(struct vu_state* vu); -void vu_i_maddz(struct vu_state* vu); -void vu_i_maddw(struct vu_state* vu); -void vu_i_madda(struct vu_state* vu); -void vu_i_maddai(struct vu_state* vu); -void vu_i_maddaq(struct vu_state* vu); -void vu_i_maddax(struct vu_state* vu); -void vu_i_madday(struct vu_state* vu); -void vu_i_maddaz(struct vu_state* vu); -void vu_i_maddaw(struct vu_state* vu); -void vu_i_msub(struct vu_state* vu); -void vu_i_msubi(struct vu_state* vu); -void vu_i_msubq(struct vu_state* vu); -void vu_i_msubx(struct vu_state* vu); -void vu_i_msuby(struct vu_state* vu); -void vu_i_msubz(struct vu_state* vu); -void vu_i_msubw(struct vu_state* vu); -void vu_i_msuba(struct vu_state* vu); -void vu_i_msubai(struct vu_state* vu); -void vu_i_msubaq(struct vu_state* vu); -void vu_i_msubax(struct vu_state* vu); -void vu_i_msubay(struct vu_state* vu); -void vu_i_msubaz(struct vu_state* vu); -void vu_i_msubaw(struct vu_state* vu); -void vu_i_max(struct vu_state* vu); -void vu_i_maxi(struct vu_state* vu); -void vu_i_maxx(struct vu_state* vu); -void vu_i_maxy(struct vu_state* vu); -void vu_i_maxz(struct vu_state* vu); -void vu_i_maxw(struct vu_state* vu); -void vu_i_mini(struct vu_state* vu); -void vu_i_minii(struct vu_state* vu); -void vu_i_minix(struct vu_state* vu); -void vu_i_miniy(struct vu_state* vu); -void vu_i_miniz(struct vu_state* vu); -void vu_i_miniw(struct vu_state* vu); -void vu_i_opmula(struct vu_state* vu); -void vu_i_opmsub(struct vu_state* vu); -void vu_i_nop(struct vu_state* vu); -void vu_i_ftoi0(struct vu_state* vu); -void vu_i_ftoi4(struct vu_state* vu); -void vu_i_ftoi12(struct vu_state* vu); -void vu_i_ftoi15(struct vu_state* vu); -void vu_i_itof0(struct vu_state* vu); -void vu_i_itof4(struct vu_state* vu); -void vu_i_itof12(struct vu_state* vu); -void vu_i_itof15(struct vu_state* vu); -void vu_i_clip(struct vu_state* vu); +void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins); // Lower pipeline -void vu_i_b(struct vu_state* vu); -void vu_i_bal(struct vu_state* vu); -void vu_i_div(struct vu_state* vu); -void vu_i_eatan(struct vu_state* vu); -void vu_i_eatanxy(struct vu_state* vu); -void vu_i_eatanxz(struct vu_state* vu); -void vu_i_eexp(struct vu_state* vu); -void vu_i_eleng(struct vu_state* vu); -void vu_i_ercpr(struct vu_state* vu); -void vu_i_erleng(struct vu_state* vu); -void vu_i_ersadd(struct vu_state* vu); -void vu_i_ersqrt(struct vu_state* vu); -void vu_i_esadd(struct vu_state* vu); -void vu_i_esin(struct vu_state* vu); -void vu_i_esqrt(struct vu_state* vu); -void vu_i_esum(struct vu_state* vu); -void vu_i_fcand(struct vu_state* vu); -void vu_i_fceq(struct vu_state* vu); -void vu_i_fcget(struct vu_state* vu); -void vu_i_fcor(struct vu_state* vu); -void vu_i_fcset(struct vu_state* vu); -void vu_i_fmand(struct vu_state* vu); -void vu_i_fmeq(struct vu_state* vu); -void vu_i_fmor(struct vu_state* vu); -void vu_i_fsand(struct vu_state* vu); -void vu_i_fseq(struct vu_state* vu); -void vu_i_fsor(struct vu_state* vu); -void vu_i_fsset(struct vu_state* vu); -void vu_i_iadd(struct vu_state* vu); -void vu_i_iaddi(struct vu_state* vu); -void vu_i_iaddiu(struct vu_state* vu); -void vu_i_iand(struct vu_state* vu); -void vu_i_ibeq(struct vu_state* vu); -void vu_i_ibgez(struct vu_state* vu); -void vu_i_ibgtz(struct vu_state* vu); -void vu_i_iblez(struct vu_state* vu); -void vu_i_ibltz(struct vu_state* vu); -void vu_i_ibne(struct vu_state* vu); -void vu_i_ilw(struct vu_state* vu); -void vu_i_ilwr(struct vu_state* vu); -void vu_i_ior(struct vu_state* vu); -void vu_i_isub(struct vu_state* vu); -void vu_i_isubiu(struct vu_state* vu); -void vu_i_isw(struct vu_state* vu); -void vu_i_iswr(struct vu_state* vu); -void vu_i_jalr(struct vu_state* vu); -void vu_i_jr(struct vu_state* vu); -void vu_i_lq(struct vu_state* vu); -void vu_i_lqd(struct vu_state* vu); -void vu_i_lqi(struct vu_state* vu); -void vu_i_mfir(struct vu_state* vu); -void vu_i_mfp(struct vu_state* vu); -void vu_i_move(struct vu_state* vu); -void vu_i_mr32(struct vu_state* vu); -void vu_i_mtir(struct vu_state* vu); -void vu_i_rget(struct vu_state* vu); -void vu_i_rinit(struct vu_state* vu); -void vu_i_rnext(struct vu_state* vu); -void vu_i_rsqrt(struct vu_state* vu); -void vu_i_rxor(struct vu_state* vu); -void vu_i_sq(struct vu_state* vu); -void vu_i_sqd(struct vu_state* vu); -void vu_i_sqi(struct vu_state* vu); -void vu_i_sqrt(struct vu_state* vu); -void vu_i_waitp(struct vu_state* vu); -void vu_i_waitq(struct vu_state* vu); -void vu_i_xgkick(struct vu_state* vu); -void vu_i_xitop(struct vu_state* vu); -void vu_i_xtop(struct vu_state* vu); +void vu_i_b(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_div(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins); +void vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins); // VU mem bus interface uint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr); @@ -298,6 +325,8 @@ void ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data); void ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value); uint32_t ps2_vu_read_vi(struct vu_state* vu, int index); void ps2_vu_reset(struct vu_state* vu); +void ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode); +void ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode); void vu_cycle(struct vu_state* vu); void vu_execute_program(struct vu_state* vu, uint32_t addr); From 58450a40413066369a13ddf0abd29cc93419518d Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:43:41 -0300 Subject: [PATCH 23/26] ee: Implement INTC/CSR spin skip --- src/ee/ee.h | 2 ++ src/ee/ee_cached.cpp | 77 ++++++++++++++++++++++++++++---------------- src/ee/ee_def.hpp | 4 +++ src/ee/intc.c | 21 ++++++++++++ 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/ee/ee.h b/src/ee/ee.h index 77d29fa..86ca501 100644 --- a/src/ee/ee.h +++ b/src/ee/ee.h @@ -143,6 +143,8 @@ uint32_t ee_get_pc(struct ee_state* ee); struct ps2_ram* ee_get_spr(struct ee_state* ee); int ee_run_block(struct ee_state* ee, int cycles); void ee_set_fmv_skip(struct ee_state* ee, int v); +void ee_reset_intc_reads(struct ee_state* ee); +void ee_reset_csr_reads(struct ee_state* ee); #undef EE_ALIGNED16 diff --git a/src/ee/ee_cached.cpp b/src/ee/ee_cached.cpp index 3a6c56f..ce8bdcd 100644 --- a/src/ee/ee_cached.cpp +++ b/src/ee/ee_cached.cpp @@ -35,8 +35,8 @@ #endif // file = fopen("vu.dump", "a"); fprintf(file, #ins "\n"); fclose(file); -#define VU_LOWER(ins) { ee->vu0->lower = i.opcode; vu_i_ ## ins(ee->vu0); } -#define VU_UPPER(ins) { ee->vu0->upper = i.opcode; vu_i_ ## ins(ee->vu0); } +#define VU_LOWER(ins) { ps2_vu_decode_lower(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->lower); } +#define VU_UPPER(ins) { ps2_vu_decode_upper(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->upper); } static inline int fast_abs32(int a) { uint32_t m = a >> 31; @@ -357,11 +357,9 @@ static inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t if ((addr & 0xf0000000) == 0x70000000) \ return ps2_ram_read ## b(ee->scratchpad, addr & 0x3fff); \ uint32_t phys; \ - if (ee_translate_virt(ee, addr, &phys)) { \ - printf("ee: TLB mapping error\n"); \ - exit(1); \ - return 0; \ - } \ + ee_translate_virt(ee, addr, &phys); \ + if (phys == 0x1000f000) ee->intc_reads++; \ + if (phys == 0x12001000) ee->csr_reads++; \ return ee->bus.read ## b(ee->bus.udata, phys); \ } @@ -370,11 +368,7 @@ static inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t if ((addr & 0xf0000000) == 0x70000000) \ { ps2_ram_write ## b(ee->scratchpad, addr & 0x3fff, data); return; } \ uint32_t phys; \ - if (ee_translate_virt(ee, addr, &phys)) { \ - printf("ee: TLB mapping error\n"); \ - exit(1); \ - return; \ - } \ + ee_translate_virt(ee, addr, &phys); \ ee->bus.write ## b(ee->bus.udata, phys, data); \ } @@ -543,6 +537,8 @@ static inline int ee_check_irq(struct ee_state* ee) { // ee->delay_slot // ); + ee->intc_reads = 0; + ee_exception_level1(ee, CAUSE_EXC1_INT); return 1; @@ -642,16 +638,16 @@ static inline void ee_i_bc0tl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc1f(struct ee_state* ee, const ee_instruction& i) { - BRANCH((ee->fcr & (1 << 23)) == 0, EE_D_SI16); + BRANCH((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16); } static inline void ee_i_bc1fl(struct ee_state* ee, const ee_instruction& i) { - BRANCH_LIKELY((ee->fcr & (1 << 23)) == 0, EE_D_SI16); + BRANCH_LIKELY((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16); } static inline void ee_i_bc1t(struct ee_state* ee, const ee_instruction& i) { - BRANCH((ee->fcr & (1 << 23)) != 0, EE_D_SI16); + BRANCH((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16); } static inline void ee_i_bc1tl(struct ee_state* ee, const ee_instruction& i) { - BRANCH_LIKELY((ee->fcr & (1 << 23)) != 0, EE_D_SI16); + BRANCH_LIKELY((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16); } static inline void ee_i_bc2f(struct ee_state* ee, const ee_instruction& i) { BRANCH(1, EE_D_SI16); } static inline void ee_i_bc2fl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(1, EE_D_SI16); } @@ -721,39 +717,39 @@ static inline void ee_i_cache(struct ee_state* ee, const ee_instruction& i) { } static inline void ee_i_ceq(struct ee_state* ee, const ee_instruction& i) { if (EE_FS == EE_FT) { - ee->fcr |= 1 << 23; + ee->fcr |= FPU_FLG_C; } else { - ee->fcr &= ~(1 << 23); + ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_cf(struct ee_state* ee, const ee_instruction& i) { - ee->fcr &= ~(1 << 23); + ee->fcr &= ~FPU_FLG_C; } static inline void ee_i_cfc1(struct ee_state* ee, const ee_instruction& i) { - EE_RT = (EE_D_FS >= 16) ? ee->fcr : 0x2e30; + EE_RT = SE6432((EE_D_FS >= 16) ? ee->fcr : 0x2e30); } static inline void ee_i_cfc2(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(ps2_vu_read_vi(ee->vu0, EE_D_RD)); } static inline void ee_i_cle(struct ee_state* ee, const ee_instruction& i) { if (EE_FS <= EE_FT) { - ee->fcr |= 1 << 23; + ee->fcr |= FPU_FLG_C; } else { - ee->fcr &= ~(1 << 23); + ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_clt(struct ee_state* ee, const ee_instruction& i) { if (EE_FS < EE_FT) { - ee->fcr |= 1 << 23; + ee->fcr |= FPU_FLG_C; } else { - ee->fcr &= ~(1 << 23); + ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_ctc1(struct ee_state* ee, const ee_instruction& i) { if (EE_D_FS < 16) return; - ee->fcr = (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078); + ee->fcr = EE_RT32; // (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078); } static inline void ee_i_ctc2(struct ee_state* ee, const ee_instruction& i) { // To-do: Handle FBRST, VPU_STAT, CMSAR1 @@ -3189,6 +3185,8 @@ void ee_reset(struct ee_state* ee) { ee->prid = 0x2e20; ee->pc = EE_VEC_RESET; ee->next_pc = ee->pc + 4; + ee->intc_reads = 0; + ee->csr_reads = 0; ee->block_cache.clear(); @@ -3725,8 +3723,21 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { if (ee->pc == 0x81fc0) { ee_check_irq(ee); - ee->total_cycles += 1024; - ee->count += 1024; + ee->total_cycles += 8 * 128; + ee->count += 8 * 128; + // ee->eenull_counter += 8 * 64; + + return 8 * 128; + } + + if (ee->intc_reads >= 10000) { + ee_check_irq(ee); + + return 1024; + } + + if (ee->csr_reads >= 1000) { + ee_check_irq(ee); return 1024; } @@ -3746,6 +3757,10 @@ int ee_run_block(struct ee_state* ee, int max_cycles) { if (ee_check_irq(ee)) break; + // if (ee->pc >= 0x81fc0 && ee->pc <= 0x81fdc) { + // ee->eenull_counter++; + // } + ee->pc = ee->next_pc; ee->next_pc += 4; @@ -3820,4 +3835,12 @@ struct ps2_ram* ee_get_spr(struct ee_state* ee) { void ee_set_fmv_skip(struct ee_state* ee, int v) { ee->fmv_skip = v; +} + +void ee_reset_intc_reads(struct ee_state* ee) { + ee->intc_reads = 0; +} + +void ee_reset_csr_reads(struct ee_state* ee) { + ee->csr_reads = 0; } \ No newline at end of file diff --git a/src/ee/ee_def.hpp b/src/ee/ee_def.hpp index 3914288..07d4482 100644 --- a/src/ee/ee_def.hpp +++ b/src/ee/ee_def.hpp @@ -138,6 +138,10 @@ struct ee_state { struct vu_state* vu1; struct ee_vtlb_entry vtlb[48]; + + int eenull_counter; + int csr_reads; + int intc_reads; }; #define THS_RUN 0x01 diff --git a/src/ee/intc.c b/src/ee/intc.c index 42f1975..1de9bd9 100644 --- a/src/ee/intc.c +++ b/src/ee/intc.c @@ -101,6 +101,24 @@ void ps2_intc_write64(struct ps2_intc* intc, uint32_t addr, uint64_t data) { void ps2_intc_irq(struct ps2_intc* intc, int dev) { intc->stat |= 1 << dev; + static const char* dev_names[] = { + "GS", + "SBUS", + "VBLANK_IN", + "VBLANK_OUT", + "VIF0", + "VIF1", + "VU0", + "VU1", + "IPU", + "TIMER0", + "TIMER1", + "TIMER2", + "TIMER3", + "SFIFO", + "VU0_WD" + }; + struct sched_event event; event.callback = intc_check_irq_event; @@ -108,5 +126,8 @@ void ps2_intc_irq(struct ps2_intc* intc, int dev) { event.name = "INTC IRQ check"; event.udata = intc; + // Notify EE that an interrupt has occurred + ee_reset_intc_reads(intc->ee); + sched_schedule(intc->sched, event); } \ No newline at end of file From a1bbc8de6a0828af2e1c18057fad95e86b0076a0 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:44:54 -0300 Subject: [PATCH 24/26] dmac: Fix PATH2 garbage data This fixes graphics on Crazy Taxi, Mushihimesama, Ibara, and probably more games --- src/ee/dmac.c | 163 +++++++++++++++++++++++++++++--------------------- src/ee/dmac.h | 4 +- 2 files changed, 95 insertions(+), 72 deletions(-) diff --git a/src/ee/dmac.c b/src/ee/dmac.c index fc884bc..d602547 100644 --- a/src/ee/dmac.c +++ b/src/ee/dmac.c @@ -7,8 +7,8 @@ #define printf(fmt, ...)(0) -static inline uint128_t dmac_read_qword(struct ps2_dmac* dmac, uint32_t addr, int mem) { - int spr = mem || (addr & 0x80000000); +static inline uint128_t dmac_read_qword(struct ps2_dmac* dmac, uint32_t addr) { + int spr = addr & 0x80000000; if (!spr) return ee_bus_read128(dmac->bus, addr & 0xfffffff0); @@ -131,7 +131,6 @@ static inline void dmac_process_source_tag(struct ps2_dmac* dmac, struct dmac_ch c->tag.id = TAG_ID(tag); c->tag.irq = TAG_IRQ(tag); c->tag.addr = TAG_ADDR(tag); - c->tag.mem = TAG_MEM(tag); c->tag.data = TAG_DATA(tag); // if (dmac->mfifo_drain) @@ -227,7 +226,6 @@ static inline void dmac_process_dest_tag(struct ps2_dmac* dmac, struct dmac_chan c->tag.id = TAG_ID(tag); c->tag.irq = TAG_IRQ(tag); c->tag.addr = TAG_ADDR(tag); - c->tag.mem = TAG_MEM(tag); c->tag.data = TAG_DATA(tag); c->qwc = c->tag.qwc; @@ -278,7 +276,7 @@ void dmac_handle_vif0_transfer(struct ps2_dmac* dmac) { int mode = (dmac->vif0.chcr >> 2) & 3; for (int i = 0; i < dmac->vif0.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr, 0); + uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr); // VIF0 FIFO address ee_bus_write128(dmac->bus, 0x10004000, q); @@ -297,7 +295,7 @@ void dmac_handle_vif0_transfer(struct ps2_dmac* dmac) { // Chain mode do { - uint128_t tag = dmac_read_qword(dmac, dmac->vif0.tadr, 0); + uint128_t tag = dmac_read_qword(dmac, dmac->vif0.tadr); dmac_process_source_tag(dmac, &dmac->vif0, tag); @@ -316,7 +314,7 @@ void dmac_handle_vif0_transfer(struct ps2_dmac* dmac) { } for (int i = 0; i < dmac->vif0.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr, dmac->vif0.tag.mem); + uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr); // printf("ee: Sending %016lx%016lx from %08x to VIF0 FIFO\n", // q.u64[1], q.u64[0], @@ -354,11 +352,11 @@ void mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) { struct dmac_channel* c = dmac->mfifo_drain; if (c->qwc) { - uint128_t q = dmac_read_qword(dmac, c->madr, c->tag.mem); + uint128_t q = dmac_read_qword(dmac, c->madr); if (c == &dmac->vif1) { // VIF1 FIFO - ee_bus_write128(dmac->bus, 0x10007010, q); + ee_bus_write128(dmac->bus, 0x10005000, q); } else { // GIF FIFO ee_bus_write128(dmac->bus, 0x10006000, q); @@ -367,35 +365,35 @@ void mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) { c->madr += 16; c->qwc--; - printf("dmac: mfifo channel qwc=%d\n", c->qwc); + // printf("dmac: mfifo channel qwc=%d\n", c->qwc); - if (channel_is_done(c)) { - printf("dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); - dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); + if (c->qwc == 0) { + if (channel_is_done(c)) { + printf("dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); + dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); - c->chcr &= ~0x100; - c->qwc = 0; + c->chcr &= ~0x100; - return; + return; + } + + if (c->tag.id == 1) { + c->tadr = dmac->rbor | (c->madr & dmac->rbsr); + } } return; } - if (channel_is_done(c)) { - printf("dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); - dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); + uint128_t tag = dmac_read_qword(dmac, c->tadr); - c->chcr &= ~0x100; - c->qwc = 0; + dmac_process_source_tag(dmac, c, tag); - return; + if ((c->chcr >> 6) & 1) { + ee_bus_write32(dmac->bus, 0x10005000, c->tag.data & 0xffffffff); + ee_bus_write32(dmac->bus, 0x10005000, c->tag.data >> 32); } - uint128_t tag = dmac_read_qword(dmac, c->tadr, 0); - - dmac_process_source_tag(dmac, c, tag); - c->tadr = dmac->rbor | (c->tadr & dmac->rbsr); switch (c->tag.id) { @@ -408,17 +406,28 @@ void mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) { } break; } - printf("dmac: tadr=%08x madr=%08x\n", c->tadr, c->madr); + printf("dmac: tadr=%08x madr=%08x qwc=%d tagid=%d end=%d\n", c->tadr, c->madr, c->qwc, c->tag.id, c->tag.end); + + if (c->qwc == 0) { + if (channel_is_done(c)) { + printf("dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); + dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); + + c->chcr &= ~0x100; + + return; + } + } if (c->tadr == dmac->spr_from.madr) { - printf("dmac: MFIFO empty\n"); + fprintf(stdout, "dmac: MFIFO empty\n"); - exit(1); + dmac_set_irq(dmac, DMAC_MEIS); } } void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { - // printf("dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x rbor=%08x rbsr=%08x sprfrom.madr=%08x\n", + // fprintf(stdout, "dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x end=%d\n", // dmac->vif1.chcr & 1, // (dmac->vif1.chcr >> 2) & 3, // (dmac->vif1.chcr >> 6) & 1, @@ -426,9 +435,7 @@ void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { // dmac->vif1.qwc, // dmac->vif1.madr, // dmac->vif1.tadr, - // dmac->rbor, - // dmac->rbsr, - // dmac->spr_from.madr + // dmac->vif1.tag.end // ); int mfifo_drain = (dmac->ctrl >> 2) & 3; @@ -465,7 +472,7 @@ void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { } for (int i = 0; i < dmac->vif1.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr, 0); + uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr); // VIF1 FIFO address ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]); @@ -484,26 +491,22 @@ void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { return; } - int id = (dmac->vif1.chcr >> 28) & 7; - - if (mode == 1 && (id == 0 || id == 7) && dmac->vif1.qwc) { - sched_schedule(dmac->sched, event); - - return; - } - // Chain mode do { - uint128_t tag = dmac_read_qword(dmac, dmac->vif1.tadr, 0); + uint128_t tag = dmac_read_qword(dmac, dmac->vif1.tadr); dmac_process_source_tag(dmac, &dmac->vif1, tag); - // printf("ee: vif1 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x\n", + // fprintf(stdout, "ee: vif1 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x data=%08x %08x tte=%d mem=%d\n", // dmac->vif1.tag.qwc, // dmac->vif1.madr, // dmac->vif1.tadr, // dmac->vif1.tag.id, - // dmac->vif1.tag.addr + // dmac->vif1.tag.addr, + // dmac->vif1.tag.data & 0xffffffff, + // dmac->vif1.tag.data >> 32, + // (dmac->vif1.chcr >> 6) & 1, + // dmac->vif1.tag.mem // ); // CHCR.TTE: Transfer tag DATA field @@ -513,12 +516,7 @@ void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { } for (int i = 0; i < dmac->vif1.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr, dmac->vif1.tag.mem); - - // printf("ee: Sending %016lx%016lx from %08x to VIF1 FIFO\n", - // q.u64[1], q.u64[0], - // dmac->vif1.madr - // ); + uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr); ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[1]); @@ -548,6 +546,9 @@ void dmac_send_gif_irq(void* udata, int overshoot) { void dmac_handle_gif_transfer(struct ps2_dmac* dmac) { struct sched_event event; + assert(((dmac->gif.chcr >> 6) & 1) == 0); + assert(dmac->gif.qwc == 0); + int mode = (dmac->gif.chcr >> 2) & 3; event.name = "GIF DMA IRQ"; @@ -586,7 +587,7 @@ void dmac_handle_gif_transfer(struct ps2_dmac* dmac) { // ); for (int i = 0; i < dmac->gif.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->gif.madr, 0); + uint128_t q = dmac_read_qword(dmac, dmac->gif.madr); // fprintf(file, "ee: Sending %016lx%016lx from %08x to GIF FIFO (burst)\n", // q.u64[1], q.u64[0], @@ -611,14 +612,14 @@ void dmac_handle_gif_transfer(struct ps2_dmac* dmac) { // Chain mode do { - uint128_t tag = dmac_read_qword(dmac, dmac->gif.tadr, 0); + uint128_t tag = dmac_read_qword(dmac, dmac->gif.tadr); dmac_process_source_tag(dmac, &dmac->gif, tag); // printf("ee: gif tag qwc=%08x madr=%08x tadr=%08x mem=%d\n", dmac->gif.qwc, dmac->gif.madr, dmac->gif.tadr, dmac->gif.tag.mem); for (int i = 0; i < dmac->gif.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->gif.madr, dmac->gif.tag.mem); + uint128_t q = dmac_read_qword(dmac, dmac->gif.madr); // fprintf(file, "ee: Sending %016lx%016lx from %08x to GIF FIFO (chain)\n", // q.u64[1], q.u64[0], @@ -695,7 +696,7 @@ int dmac_transfer_ipu_to_qword(struct ps2_dmac* dmac) { } if (dmac->ipu_to.qwc) { - uint128_t q = dmac_read_qword(dmac, dmac->ipu_to.madr, dmac->ipu_to.tag.mem); + uint128_t q = dmac_read_qword(dmac, dmac->ipu_to.madr); ee_bus_write128(dmac->bus, 0x10007010, q); @@ -714,7 +715,13 @@ int dmac_transfer_ipu_to_qword(struct ps2_dmac* dmac) { return 0; } - uint128_t tag = dmac_read_qword(dmac, dmac->ipu_to.tadr, 0); + if (dmac->ipu_to.tag.id == 1) { + dmac->ipu_to.tadr = dmac->ipu_to.madr; + printf("dmac: ipu_to tag id=1, setting tadr to %08x\n", dmac->ipu_to.tadr); + exit(1); + } + + uint128_t tag = dmac_read_qword(dmac, dmac->ipu_to.tadr); dmac_process_source_tag(dmac, &dmac->ipu_to, tag); @@ -763,6 +770,15 @@ void dmac_handle_sif0_transfer(struct ps2_dmac* dmac) { if (!(dmac->sif0.chcr & 0x100)) { return; } + // fprintf(stdout, "dmac: sif0 start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x\n", + // dmac->sif0.chcr, + // dmac->sif0.chcr & 1, + // (dmac->sif0.chcr >> 2) & 3, + // !!(dmac->sif0.chcr & 0x40), + // dmac->sif0.madr, + // dmac->sif0.qwc, + // dmac->sif0.tadr + // ); while (!ps2_sif0_is_empty(dmac->sif)) { uint128_t tag = ps2_sif0_read(dmac->sif); @@ -814,7 +830,7 @@ void dmac_handle_sif0_transfer(struct ps2_dmac* dmac) { // printf("ee: Writing %016lx %016lx to %08x\n", q.u64[1], q.u64[0], dmac->sif0.madr); - dmac_write_qword(dmac, dmac->sif0.madr, dmac->sif0.tag.mem, q); + dmac_write_qword(dmac, dmac->sif0.madr, 0, q); dmac->sif0.madr += 16; } @@ -850,7 +866,7 @@ void dmac_handle_sif1_transfer(struct ps2_dmac* dmac) { // } do { - uint128_t tag = dmac_read_qword(dmac, dmac->sif1.tadr, 0); + uint128_t tag = dmac_read_qword(dmac, dmac->sif1.tadr); dmac_process_source_tag(dmac, &dmac->sif1, tag); @@ -867,7 +883,7 @@ void dmac_handle_sif1_transfer(struct ps2_dmac* dmac) { // printf("ee: SIF1 tag madr=%08x\n", dmac->sif1.madr); for (int i = 0; i < dmac->sif1.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->sif1.madr, dmac->sif1.tag.mem); + uint128_t q = dmac_read_qword(dmac, dmac->sif1.madr); // printf("%08x: ", dmac->sif1.madr); @@ -887,6 +903,14 @@ void dmac_handle_sif1_transfer(struct ps2_dmac* dmac) { dmac->sif1.madr += 16; } + + if (dmac->sif1.tag.id == 1) { + dmac->sif1.tadr = dmac->sif1.madr; + + printf("dmac: SIF1 tag id=1, setting TADR to MADR=%08x\n", dmac->sif1.madr); + + exit(1); + } } while (!channel_is_done(&dmac->sif1)); iop_dma_handle_sif1_transfer(dmac->iop_dma); @@ -928,7 +952,7 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { dmac->spr_from.chcr &= ~0x100; - // printf("dmac: spr_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x rbor=%08x rbsr=%08x\n", + // fprintf(stdout, "dmac: spr_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x rbor=%08x rbsr=%08x\n", // dmac->spr_from.chcr, // dmac->spr_from.chcr & 1, // (dmac->spr_from.chcr >> 2) & 3, @@ -941,6 +965,8 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { // dmac->rbsr // ); + // exit(1); + int mode = (dmac->spr_from.chcr >> 2) & 3; if (dmac->mfifo_drain) { @@ -949,7 +975,7 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { dmac->spr_from.madr = dmac->rbor | (dmac->spr_from.madr & dmac->rbsr); for (int i = 0; i < dmac->spr_from.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_from.sadr, 1); + uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); @@ -972,7 +998,7 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { } for (int i = 0; i < dmac->spr_from.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_from.sadr, 1); + uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); @@ -988,9 +1014,8 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { } // Chain mode - int loop = 0; do { - uint128_t tag = dmac_read_qword(dmac, dmac->spr_from.sadr, 1); + uint128_t tag = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; @@ -1016,7 +1041,7 @@ void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { // ); for (int i = 0; i < dmac->spr_from.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_from.sadr, 1); + uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); @@ -1037,7 +1062,7 @@ void dmac_spr_to_interleave(struct ps2_dmac* dmac) { while (dmac->spr_to.qwc) { for (int i = 0; i < tqwc && dmac->spr_to.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr, 0); + uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); @@ -1075,7 +1100,7 @@ void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) { } for (int i = 0; i < dmac->spr_to.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr, 0); + uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); @@ -1092,7 +1117,7 @@ void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) { // Chain mode do { - uint128_t tag = dmac_read_qword(dmac, dmac->spr_to.tadr, 0); + uint128_t tag = dmac_read_qword(dmac, dmac->spr_to.tadr); if ((dmac->spr_to.chcr >> 6) & 1) { ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, tag); @@ -1114,7 +1139,7 @@ void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) { // ); for (int i = 0; i < dmac->spr_to.qwc; i++) { - uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr, dmac->spr_to.tag.mem); + uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); diff --git a/src/ee/dmac.h b/src/ee/dmac.h index dfcd199..98d800d 100644 --- a/src/ee/dmac.h +++ b/src/ee/dmac.h @@ -20,8 +20,7 @@ extern "C" { #define TAG_PCT(d) ((d.u64[0] >> 26) & 3) #define TAG_ID(d) ((d.u64[0] >> 28) & 7) #define TAG_IRQ(d) ((d.u64[0] >> 31) & 1) -#define TAG_ADDR(d) ((d.u64[0] >> 32) & 0x7fffffff) -#define TAG_MEM(d) ((d.u64[0] >> 63) & 1) +#define TAG_ADDR(d) ((d.u64[0] >> 32) & 0xfffffff0) #define TAG_DATA(d) (d.u64[1]) #define DMAC_VIF0 0 @@ -42,7 +41,6 @@ struct dmac_tag { uint64_t id; uint64_t irq; uint64_t addr; - uint64_t mem; uint64_t data; int end; }; From 45426e17dea1259bf8cefa2a58b343a5e6fcea19 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:45:21 -0300 Subject: [PATCH 25/26] iris: Reimplement breakpoints --- main.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/main.cpp b/main.cpp index 590be29..a1f894c 100644 --- a/main.cpp +++ b/main.cpp @@ -504,15 +504,13 @@ SDL_AppResult SDL_AppIterate(void* appstate) { // Execute until VBlank while (!ps2_gs_is_vblank(iris->ps2->gs)) { - ps2_cycle(iris->ps2); + do_cycle(iris); - // do_cycle(iris); + if (iris->pause) { + iris::update_window(iris); - // if (iris->pause) { - // iris::update_window(iris); - - // return SDL_APP_CONTINUE; - // } + return SDL_APP_CONTINUE; + } } // Draw frame @@ -520,17 +518,23 @@ SDL_AppResult SDL_AppIterate(void* appstate) { // Execute until vblank is over while (ps2_gs_is_vblank(iris->ps2->gs)) { - ps2_cycle(iris->ps2); + do_cycle(iris); - // do_cycle(iris); + if (iris->pause) { + iris::update_window(iris); - // if (iris->pause) { - // iris::update_window(iris); - - // return SDL_APP_CONTINUE; - // } + return SDL_APP_CONTINUE; + } } + float p = ((float)iris->ps2->ee->eenull_counter / (float)(4920115)) * 100.0f; + + // printf("ee: Time spent idling: %ld cycles (%.2f%%) INTC reads: %d CSR reads: %d (%.1f fps)\n", iris->ps2->ee->eenull_counter, p, iris->ps2->ee->intc_reads, iris->ps2->ee->csr_reads, 1.0f / ImGui::GetIO().DeltaTime); + + iris->ps2->ee->eenull_counter = 0; + iris->ps2->ee->intc_reads = 0; + iris->ps2->ee->csr_reads = 0; + return SDL_APP_CONTINUE; } From 5c1439e691644ad0abcf4a32ccd02d593a5d5b19 Mon Sep 17 00:00:00 2001 From: allkern Date: Wed, 1 Oct 2025 11:46:15 -0300 Subject: [PATCH 26/26] vif: Fix STAT and CODE reads after IRQs Improves Gran Turismo 3/4 speed issues --- src/ee/vif.c | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/ee/vif.c b/src/ee/vif.c index d6e315d..4e3c93d 100644 --- a/src/ee/vif.c +++ b/src/ee/vif.c @@ -6,7 +6,7 @@ #include "vif.h" -// #define printf(fmt, ...)(0) +#define printf(fmt, ...)(0) struct ps2_vif* ps2_vif_create(void) { return malloc(sizeof(struct ps2_vif)); @@ -185,6 +185,18 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { // printf("vif%d: FLUSHE\n", vif->id); } break; case VIF_CMD_FLUSH: { + // Note: MASSIVE GRAN TURISMO HACK! + // GT3/4 expect IBT and stall bits to be set when a + // VIF IRQ occurs, CODE also needs to be set to the + // last command that caused a stall. + // This is admittedly a huge hack, but we can't really + // emulate any of this without properly implementing + // DMA timings. + if (data & 0x80000000) { + vif->stat |= 0x00000c02; + vif->stat ^= 0x0f000000; + vif->code = data; + } // printf("vif%d: FLUSH\n", vif->id); } break; case VIF_CMD_FLUSHA: { @@ -269,7 +281,7 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { vif->shift = 0; } break; case VIF_CMD_DIRECT: { - //printf("vif%d: DIRECT(%04x)\n", vif->id, data & 0xffff); + // fprintf(stdout, "vif%d: DIRECT(%04x)\n", vif->id, data & 0xffff); int imm = data & 0xffff; @@ -282,7 +294,7 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { vif->shift = 0; } break; case VIF_CMD_DIRECTHL: { - // printf("vif%d: DIRECTHL(%04x)\n", vif->id, data & 0xffff); + // fprintf(stdout, "vif%d: DIRECTHL(%04x)\n", vif->id, data & 0xffff); int imm = data & 0xffff; @@ -346,7 +358,7 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { vif->shift = 0; vif->addr = addr; - // printf("vif%d: UNPACK %02x fmt=%02x flg=%d num=%02x addr=%08x tops=%08x usn=%d wr=%d mode=%d\n", vif->id, data >> 24, vif->unpack_fmt, flg, vif->unpack_num, addr, vif->tops, vif->unpack_usn, vif->pending_words, vif->mode); + // fprintf(stdout, "vif%d: UNPACK %02x fmt=%02x flg=%d num=%02x addr=%08x tops=%08x usn=%d wr=%d mode=%d\n", vif->id, data >> 24, vif->unpack_fmt, flg, vif->unpack_num, addr, vif->tops, vif->unpack_usn, vif->pending_words, vif->mode); } break; default: { // printf("vif%d: Unhandled command %02x\n", vif->id, vif->cmd); @@ -375,12 +387,13 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { } } break; case VIF_CMD_MPG: { - // printf("vif%d: Writing %08x to MicroMem\n", vif->id, data); - if (!vif->shift) { vif->data.u32[vif->shift++] = data; } else { vif->data.u32[1] = data; + + // fprintf(stdout, "vif%d: Writing %08x %08x to MicroMem addr=%04x\n", vif->id, vif->data.u32[0], vif->data.u32[1], vif->addr); + vif->vu->micro_mem[(vif->addr++) & 0x7ff] = vif->data.u64[0]; vif->shift = 0; } @@ -393,13 +406,18 @@ static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { case VIF_CMD_DIRECT: { vif->data.u32[vif->shift++] = data; + vif->pending_words--; + if (vif->shift == 4) { + // fprintf(stdout, "vif%d: Writing %08x %08x %08x %08x to GIF FIFO pending=%d\n", vif->id, vif->data.u32[3], vif->data.u32[2], vif->data.u32[1], vif->data.u32[0], vif->pending_words); ee_bus_write128(vif->bus, 0x10006000, vif->data); vif->shift = 0; } - if (!(--vif->pending_words)) { + if (!vif->pending_words) { + // fprintf(stdout, "vif%d: DIRECT complete\n", vif->id); + vif->state = VIF_IDLE; } } break; @@ -805,7 +823,11 @@ uint64_t ps2_vif_read32(struct ps2_vif* vif, uint32_t addr) { case 0x10003970: return vif->c[3]; // VIF1 registers - case 0x10003c00: return vif->stat; + case 0x10003c00: { + uint32_t stat = vif->stat; vif->stat = 0; + + return stat; + } break; case 0x10003c10: return vif->fbrst; case 0x10003c20: return vif->err; case 0x10003c30: return vif->mark; @@ -877,15 +899,15 @@ void ps2_vif_write32(struct ps2_vif* vif, uint32_t addr, uint64_t data) { default: { printf("vif%d: Unhandled 32-bit write to %08x\n", vif->id, addr); - exit(1); + // exit(1); } break; } } uint128_t ps2_vif_read128(struct ps2_vif* vif, uint32_t addr) { switch (addr) { - case 0x10004000: // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; - case 0x10005000: // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; + case 0x10004000: break; // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; + case 0x10005000: break; // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; default: { printf("vif%d: Unhandled 128-bit read to %08x\n", vif->id, addr);