From 19d2f5f4020c4df33e4794f4da9b60ccd662de63 Mon Sep 17 00:00:00 2001 From: iris Date: Wed, 27 May 2026 17:32:17 +0200 Subject: [PATCH] i cannot figure it out istg --- src/backend/Core.cpp | 1 + src/backend/core/Interpreter.cpp | 70 ++- src/backend/core/Interpreter.hpp | 27 +- src/frontend/KaizenGui.cpp | 808 ++++++++++++++++--------------- src/frontend/KaizenGui.hpp | 7 +- 5 files changed, 465 insertions(+), 448 deletions(-) diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 446e265..c8a6003 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -116,6 +116,7 @@ void Core::Run(const float volumeL, const float volumeR) { } for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { + Scheduler::GetInstance().HandleEvents(); const u32 taken = StepCPU(); cycles += taken; frameCycles += taken; diff --git a/src/backend/core/Interpreter.cpp b/src/backend/core/Interpreter.cpp index 324ae43..105158c 100644 --- a/src/backend/core/Interpreter.cpp +++ b/src/backend/core/Interpreter.cpp @@ -96,65 +96,63 @@ u32 Interpreter::Step() { return 1; } -u32 DivideAddr(u32 addr, u32 &offset) { - offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4; - return addr / MAX_LINES_PER_BLOCK; -} - -std::shared_ptr CachedState::GetLine(u64 addr) { +const CachedLine &CachedState::GetLine(u64 addr) { u32 offset; u32 page = DivideAddr(addr, offset); - if (blocks[page]) - return blocks[page]->lines[offset]; - - return nullptr; + return blocks[page].lines[offset]; } -void CachedState::InsertLine(u64 addr, std::shared_ptr line) { +void CachedState::InsertLine(u64 addr, const CachedLine &line) { u32 offset; u32 page = DivideAddr(addr, offset); - if (!blocks[page]) - blocks[page] = std::make_unique(); - - blocks[page]->lines[offset] = line; + blocks[page].lines[offset] = line; } void CachedState::EvictLine(u64 addr) { u32 offset; u32 page = DivideAddr(addr, offset); - if (blocks[page]) { - blocks[page]->lines[offset].reset(); - blocks[page].reset(); - } + blocks[page].lines[offset] = CachedLine(); } u32 Interpreter::ExecuteCached() { auto addr = regs.pc; auto blockAddr = addr; - auto line = cachedState.GetLine(addr); + auto page = (addr >> 12) & 0xfffff; + auto offset = addr & 0xfff; + auto &blocks = cachedState.blocks; - if (line) { - for (u32 i = 0; i < line->len; i++) { - if (!MaybeAdvance()) - return i + 1; + if (page >= blocks.size()) + blocks.push_back(); - Instruction instr = line->code[i]; - DecodeExecute(instr); + if (page < blocks.size()) { + const auto &block = blocks[page]; + if (blocks[page].len > 0) { + for (u32 i = 0; i < block.len; i++) { + if (!MaybeAdvance()) + return i + 1; - // Branch likely with false condition, it wasn't taken so don't execute the delay slot - if (IsBranchLikely(instr) && !regs.delaySlot) - break; + Instruction instr = block.code[i]; + DecodeExecute(instr); + + // Branch likely with false condition, it wasn't taken so don't execute the delay slot + if (IsBranchLikely(instr) && !regs.delaySlot) + break; + } + + if (block.cycles == 0) + Scheduler::GetInstance().SkipToNext(); + + return block.cycles; } - - if (line->cycles == 0) - Scheduler::GetInstance().SkipToNext(); - - return line->cycles; } - std::array code; + if (line.code.size() > 0) { + } + + std::vector code; + code.resize(MAX_INSTRUCTION_PER_LINE); u32 i; bool fetchDelaySlot = false; @@ -180,7 +178,7 @@ u32 Interpreter::ExecuteCached() { } } - cachedState.InsertLine(blockAddr, std::make_shared(code, i, i)); + cachedState.InsertLine(blockAddr, {code, i, i}); return ExecuteCached(); } diff --git a/src/backend/core/Interpreter.hpp b/src/backend/core/Interpreter.hpp index 16ebd41..a029eb6 100644 --- a/src/backend/core/Interpreter.hpp +++ b/src/backend/core/Interpreter.hpp @@ -5,36 +5,26 @@ namespace n64 { struct Core; -static constexpr u32 MAX_LINES_PER_BLOCK = 1 << 12; -static constexpr u32 MAX_INSTRUCTION_PER_LINE = MAX_LINES_PER_BLOCK >> 2; +static constexpr u32 MAX_INSTRUCTION_PER_BLOCK = 1 << 12; -struct CachedLine { - std::array code = {}; +struct CachedBlock { + std::array code = {}; u32 cycles = 0; u32 len = 0; }; -struct CachedBlock { - std::array, MAX_LINES_PER_BLOCK / 4> lines = {}; -}; - -using CachedBlocks = std::vector>; - struct CachedState { - CachedState() { blocks.resize((u64(std::numeric_limits::max()) + 1) / MAX_LINES_PER_BLOCK); } - CachedBlocks blocks = {}; + CachedState() {} + std::vector blocks = {}; bool exception = false; void Reset() { - for (auto &block : blocks) { - block.reset(); - } - + blocks = {}; exception = false; } - std::shared_ptr GetLine(u64); - void InsertLine(u64, std::shared_ptr); + const CachedLine &GetLine(u64); + void InsertLine(u64, const CachedLine &); void EvictLine(u64); }; @@ -51,7 +41,6 @@ struct Interpreter final { cachedState.Reset(); } - CachedState cachedState; private: diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 3d9fb36..7bb8cc0 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -5,456 +5,484 @@ #include #include -KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { - gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); - SDL_InitSubSystem(SDL_INIT_GAMEPAD); +KaizenGui::KaizenGui() noexcept : + window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), + emuThread(fpsCounter, settingsWindow) { + gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); + SDL_InitSubSystem(SDL_INIT_GAMEPAD); - SDL_AddGamepadMapping(gamecontrollerdb_str); + SDL_AddGamepadMapping(gamecontrollerdb_str); } KaizenGui::~KaizenGui() { - gui::Cleanup(); - SDL_Quit(); + gui::Cleanup(); + SDL_Quit(); } void KaizenGui::QueryDevices(const SDL_Event &event) { - switch (event.type) { - case SDL_EVENT_GAMEPAD_ADDED: - if (!gamepad) { - const auto index = event.gdevice.which; + switch (event.type) { + case SDL_EVENT_GAMEPAD_ADDED: + if (!gamepad) { + const auto index = event.gdevice.which; - gamepad = SDL_OpenGamepad(index); - info("Found controller!"); - info("Name: {}", SDL_GetGamepadName(gamepad)); - info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + gamepad = SDL_OpenGamepad(index); + info("Found controller!"); + info("Name: {}", SDL_GetGamepadName(gamepad)); + info("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + break; + case SDL_EVENT_GAMEPAD_REMOVED: + if (gamepad) + SDL_CloseGamepad(gamepad); + break; + default: + break; } - break; - case SDL_EVENT_GAMEPAD_REMOVED: - if (gamepad) - SDL_CloseGamepad(gamepad); - break; - default: break; - } } void KaizenGui::HandleInput(const SDL_Event &event) { - const n64::Core& core = n64::Core::GetInstance(); - n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; - switch(event.type) { + const n64::Core &core = n64::Core::GetInstance(); + n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; + switch (event.type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: - if(!gamepad) + if (!gamepad) + break; + { + pif.UpdateButton(0, n64::Controller::Key::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::Key::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); + pif.UpdateButton(0, n64::Controller::Key::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); + pif.UpdateButton(0, n64::Controller::Key::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); + + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + } break; - { - pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127); - pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127); - pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127); - - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast( xclamped)); - } - break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: - if(!gamepad) + if (!gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); break; - - pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - break; case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: - { - const auto keys = SDL_GetKeyboardState(nullptr); - if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { - fileDialogOpen = true; + { + const auto keys = SDL_GetKeyboardState(nullptr); + if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { + fileDialogOpen = true; + } + + fastForward = keys[SDL_SCANCODE_SPACE]; + if (!unlockFramerate) + core.parallel.SetFramerateUnlocked(fastForward); + + if (core.romLoaded) { + if (keys[SDL_SCANCODE_P]) { + emuThread.TogglePause(); + } + + if (keys[SDL_SCANCODE_R]) { + emuThread.Reset(); + } + + if (keys[SDL_SCANCODE_Q]) { + emuThread.Stop(); + } + } + + if (gamepad) + break; + + pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); + pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); + pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); + pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); + pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); + pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); + pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); + pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); + pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); + pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); + pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); + pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); + pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); + pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); + + float x = 0, y = 0; + + if (keys[SDL_SCANCODE_UP]) + y = 86; + if (keys[SDL_SCANCODE_DOWN]) + y = -86; + if (keys[SDL_SCANCODE_LEFT]) + x = -86; + if (keys[SDL_SCANCODE_RIGHT]) + x = 86; + + pif.UpdateAxis(0, n64::Controller::Axis::X, x); + pif.UpdateAxis(0, n64::Controller::Axis::Y, y); } - - fastForward = keys[SDL_SCANCODE_SPACE]; - if(!unlockFramerate) - core.parallel.SetFramerateUnlocked(fastForward); - - if(core.romLoaded) { - if(keys[SDL_SCANCODE_P]) { - emuThread.TogglePause(); - } - - if(keys[SDL_SCANCODE_R]) { - emuThread.Reset(); - } - - if(keys[SDL_SCANCODE_Q]) { - emuThread.Stop(); - } - } - - if(gamepad) - break; - - pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]); - pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]); - pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]); - pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]); - pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]); - pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]); - pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]); - pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]); - pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]); - pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]); - pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]); - pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); - pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); - pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - - float x = 0, y = 0; - - if (keys[SDL_SCANCODE_UP]) y = 86; - if (keys[SDL_SCANCODE_DOWN]) y = -86; - if (keys[SDL_SCANCODE_LEFT]) x = -86; - if (keys[SDL_SCANCODE_RIGHT]) x = 86; - - pif.UpdateAxis(0, n64::Controller::Axis::X, x); - pif.UpdateAxis(0, n64::Controller::Axis::Y, y); - } - break; - default: break; - } + break; + default: + break; + } } std::pair, std::optional> RenderErrorMessageDetails() { - auto lastPC = Util::Error::GetLastPC(); - if(lastPC.has_value()) { - ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); - } + auto lastPC = Util::Error::GetLastPC(); + if (lastPC.has_value()) { + ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); + } - auto memoryAccess = Util::Error::GetMemoryAccess(); - if(memoryAccess.has_value()) { - const auto [is_write, size, address, written_val] = memoryAccess.value(); - ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", - static_cast(size), address, - is_write ? std::format(" (value = 0x{:X})", written_val) : "") - .c_str()); - } + auto memoryAccess = Util::Error::GetMemoryAccess(); + if (memoryAccess.has_value()) { + const auto [is_write, size, address, written_val] = memoryAccess.value(); + ImGui::Text("%s", + std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast(size), + address, is_write ? std::format(" (value = 0x{:X})", written_val) : "") + .c_str()); + } - return {lastPC, memoryAccess}; + return {lastPC, memoryAccess}; } void KaizenGui::RenderUI() { - n64::Core& core = n64::Core::GetInstance(); - gui::StartFrame(); + n64::Core &core = n64::Core::GetInstance(); + gui::StartFrame(); - if(ImGui::BeginMainMenuBar()) { - if(ImGui::BeginMenu("File")) { - if(ImGui::MenuItem("Open", "Ctrl-O")) { - fileDialogOpen = true; - } - if(ImGui::MenuItem("Exit")) { - quit = true; - emuThread.Stop(); - } - ImGui::EndMenu(); + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open", "Ctrl-O")) { + fileDialogOpen = true; + } + if (ImGui::MenuItem("Exit")) { + quit = true; + emuThread.Stop(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Emulation")) { + ImGui::BeginDisabled(!core.romLoaded); + + if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { + emuThread.TogglePause(); + } + + if (ImGui::MenuItem("Reset", "R")) { + emuThread.Reset(); + } + + if (ImGui::MenuItem("Stop", "Q")) { + emuThread.Stop(); + core.romLoaded = false; + } + + if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { + core.parallel.SetFramerateUnlocked(unlockFramerate); + } + + if (ImGui::MenuItem("Open Debugger")) { + debugger.Open(); + } + + ImGui::EndDisabled(); + + if (ImGui::MenuItem("Options")) { + settingsWindow.isOpen = true; + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) { + if (ImGui::MenuItem("About")) { + aboutOpen = true; + } + + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); } - if(ImGui::BeginMenu("Emulation")) { - ImGui::BeginDisabled(!core.romLoaded); - - if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) { - emuThread.TogglePause(); - } - - if(ImGui::MenuItem("Reset", "R")) { - emuThread.Reset(); - } - - if(ImGui::MenuItem("Stop", "Q")) { - emuThread.Stop(); - core.romLoaded = false; - } - - if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) { - core.parallel.SetFramerateUnlocked(unlockFramerate); - } - if(ImGui::MenuItem("Open Debugger")) { - debugger.Open(); - } - - ImGui::EndDisabled(); - - if(ImGui::MenuItem("Options")) { - settingsWindow.isOpen = true; - } - ImGui::EndMenu(); + if (!Util::Error::IsHandled()) { + ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); } - if(ImGui::BeginMenu("Help")) { - if(ImGui::MenuItem("About")) { - aboutOpen = true; - } - - ImGui::EndMenu(); + + if (settingsWindow.isOpen) { + ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); } - ImGui::EndMainMenuBar(); - } - if(!Util::Error::IsHandled()) { - ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); - } - - if(settingsWindow.isOpen) { - ImGui::OpenPopup("Settings", ImGuiPopupFlags_None); - } - - if(aboutOpen) { - ImGui::OpenPopup("About Kaizen"); - } - - settingsWindow.render(); - debugger.render(); - - const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); - ImGui::Text("to offer a friendly user experience and compatibility."); - ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); - ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); - ImGui::Separator(); - ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); - ImGui::Separator(); - if(ImGui::Button("OK")) { - aboutOpen = false; - ImGui::CloseCurrentPopup(); + if (aboutOpen) { + ImGui::OpenPopup("About Kaizen"); } - - ImGui::EndPopup(); - } - ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + settingsWindow.render(); + debugger.render(); - if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - emuThread.TogglePause(); - switch(Util::Error::GetSeverity().as_enum) { - case Util::Error::Severity::WARN: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); - ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - if(ignore || stop || chooseAnother) { - Util::Error::SetHandled(); + if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::Text("Kaizen is a Nintendo 64 emulator that strives"); + ImGui::Text("to offer a friendly user experience and compatibility."); + ImGui::Text("Kaizen is licensed under the BSD 3-clause license."); + ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd."); + ImGui::Separator(); + ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR); + ImGui::Separator(); + if (ImGui::Button("OK")) { + aboutOpen = false; ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - break; } - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::UNRECOVERABLE: { - emuThread.Stop(); - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - RenderErrorMessageDetails(); - if(ImGui::Button("OK")) - ImGui::CloseCurrentPopup(); - } break; - case Util::Error::Severity::NON_FATAL: { - ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); - ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); - ImGui::Text("An error has occurred!"); - ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); - auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - - const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - const bool chooseAnother = ImGui::Button("Choose another ROM"); - const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; - if(ignore || stop || chooseAnother || openInDebugger) { - Util::Error::SetHandled(); - ImGui::CloseCurrentPopup(); - } - - if(ignore) { - emuThread.TogglePause(); - } - - if(stop || chooseAnother) { - emuThread.Stop(); - } - - if(chooseAnother) { - fileDialogOpen = true; - } - - if(openInDebugger) { - if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) - n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); - - debugger.Open(); - emuThread.Reset(); - } - } break; - default: break; + ImGui::EndPopup(); } - - ImGui::EndPopup(); - } - - if(ImGui::BeginMainStatusBar()) { - ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); - ImGui::EndMainStatusBar(); - } - if (shouldDisplaySpinner) { - ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + emuThread.TogglePause(); + switch (Util::Error::GetSeverity().as_enum) { + case Util::Error::Severity::WARN: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1); + ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); - ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); - ImGui::SameLine(); + if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + if (ignore || stop || chooseAnother) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); - ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); - ImGui::PopFont(); + if (ignore) { + emuThread.TogglePause(); + } - ImGui::End(); + if (stop || chooseAnother) { + emuThread.Stop(); + } - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); - } + if (chooseAnother) { + fileDialogOpen = true; + } + break; + } - ImGui::Render(); - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); - } + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::UNRECOVERABLE: + { + emuThread.Stop(); + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped..."); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + RenderErrorMessageDetails(); + if (ImGui::Button("OK")) + ImGui::CloseCurrentPopup(); + } + break; + case Util::Error::Severity::NON_FATAL: + { + ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff); + ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf); + ImGui::Text("An error has occurred!"); + ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str()); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); + auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - if(fileDialogOpen) { - fileDialogOpen = false; - constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { - auto kaizen = static_cast(userdata); - - if (!filelist) { - panic("An error occured: {}", SDL_GetError()); - } + const bool ignore = ImGui::Button("Try continuing"); + ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); + ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool openInDebugger = + lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; + if (ignore || stop || chooseAnother || openInDebugger) { + Util::Error::SetHandled(); + ImGui::CloseCurrentPopup(); + } - if (!*filelist) { - warn("The user did not select any file."); - warn("Most likely, the dialog was canceled."); + if (ignore) { + emuThread.TogglePause(); + } + + if (stop || chooseAnother) { + emuThread.Stop(); + } + + if (chooseAnother) { + fileDialogOpen = true; + } + + if (openInDebugger) { + if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value())) + n64::Core::GetInstance().ToggleBreakpoint(lastPC.value()); + + debugger.Open(); + emuThread.Reset(); + } + } + break; + default: + break; + } + + ImGui::EndPopup(); + } + + if (ImGui::BeginMainStatusBar()) { + ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate); + ImGui::EndMainStatusBar(); + } + + if (shouldDisplaySpinner) { + ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, + ImVec2(0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + + ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); + ImGui::SameLine(); + + ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); + ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); + ImGui::PopFont(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + + ImGui::Render(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + + if (fileDialogOpen) { + fileDialogOpen = false; + constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, + {"Nintendo 64 executable", "n64;z64;v64"}, + {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; + SDL_ShowOpenFileDialog( + [](void *userdata, const char *const *filelist, int) { + auto kaizen = static_cast(userdata); + + if (!filelist) { + panic("An error occured: {}", SDL_GetError()); + } + + if (!*filelist) { + warn("The user did not select any file."); + warn("Most likely, the dialog was canceled."); + return; + } + + kaizen->fileToLoad = *filelist; + kaizen->shouldDisplaySpinner = true; + std::thread fileWorker(&KaizenGui::FileWorker, kaizen); + fileWorker.detach(); + }, + this, window.getHandle(), filters, 3, nullptr, false); + } + + if (minimized) return; - } - kaizen->fileToLoad = *filelist; - kaizen->shouldDisplaySpinner = true; + if (core.romLoaded) { + core.parallel.UpdateScreen(); + return; + } - std::thread fileWorker(&KaizenGui::FileWorker, kaizen); - fileWorker.detach(); - }, this, window.getHandle(), filters, 3, nullptr, false); - } - - if(minimized) - return; - - if(core.romLoaded) { - core.parallel.UpdateScreen(); - return; - } - - core.parallel.UpdateScreen(); + core.parallel.UpdateScreen(); } void KaizenGui::LoadROM(const std::string &path) noexcept { - n64::Core& core = n64::Core::GetInstance(); - core.LoadROM(path); - const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; - SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); + n64::Core &core = n64::Core::GetInstance(); + core.LoadROM(path); + const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; + SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); } void KaizenGui::run() { - while(!quit) { - SDL_Event e; - while (SDL_PollEvent(&e)) { - ImGui_ImplSDL3_ProcessEvent(&e); - switch(e.type) { - case SDL_EVENT_QUIT: - quit = true; - emuThread.Stop(); - break; - case SDL_EVENT_WINDOW_MINIMIZED: - minimized = true; - break; - case SDL_EVENT_WINDOW_RESTORED: - minimized = false; - break; - default: break; - } - QueryDevices(e); - HandleInput(e); + while (!quit) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + ImGui_ImplSDL3_ProcessEvent(&e); + switch (e.type) { + case SDL_EVENT_QUIT: + quit = true; + emuThread.Stop(); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + minimized = true; + break; + case SDL_EVENT_WINDOW_RESTORED: + minimized = false; + break; + default: + break; + } + QueryDevices(e); + HandleInput(e); + } + + SDL_GetWindowSize(window.getHandle(), &width, &height); + + emuThread.run(); + RenderUI(); } - - SDL_GetWindowSize(window.getHandle(), &width, &height); - - emuThread.run(); - RenderUI(); - } } -void KaizenGui::LoadTAS(const std::string &path) noexcept { - n64::Core::GetInstance().LoadTAS(fs::path(path)); -} +void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index d55a78d..9d0e9f1 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -10,7 +10,7 @@ class KaizenGui final { public: explicit KaizenGui() noexcept; ~KaizenGui(); - + double fpsCounter = -1.0; bool fastForward = false; bool unlockFramerate = false; @@ -22,7 +22,7 @@ public: Debugger debugger; SDL_Gamepad* gamepad = nullptr; - + void run(); static void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; @@ -37,12 +37,13 @@ private: void HandleInput(const SDL_Event &event); void QueryDevices(const SDL_Event &event); - [[noreturn]] void FileWorker() { + void FileWorker() { while (true) { if (!fileToLoad.empty()) { LoadROM(fileToLoad); shouldDisplaySpinner = false; fileToLoad = ""; + return; } } }