i cannot figure it out istg

This commit is contained in:
2026-05-27 17:32:17 +02:00
parent 4d9cb49b73
commit 19d2f5f402
5 changed files with 465 additions and 448 deletions
+1
View File
@@ -116,6 +116,7 @@ void Core::Run(const float volumeL, const float volumeR) {
} }
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
Scheduler::GetInstance().HandleEvents();
const u32 taken = StepCPU(); const u32 taken = StepCPU();
cycles += taken; cycles += taken;
frameCycles += taken; frameCycles += taken;
+34 -36
View File
@@ -96,65 +96,63 @@ u32 Interpreter::Step() {
return 1; return 1;
} }
u32 DivideAddr(u32 addr, u32 &offset) { const CachedLine &CachedState::GetLine(u64 addr) {
offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4;
return addr / MAX_LINES_PER_BLOCK;
}
std::shared_ptr<CachedLine> CachedState::GetLine(u64 addr) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (blocks[page]) return blocks[page].lines[offset];
return blocks[page]->lines[offset];
return nullptr;
} }
void CachedState::InsertLine(u64 addr, std::shared_ptr<CachedLine> line) { void CachedState::InsertLine(u64 addr, const CachedLine &line) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (!blocks[page]) blocks[page].lines[offset] = line;
blocks[page] = std::make_unique<CachedBlock>();
blocks[page]->lines[offset] = line;
} }
void CachedState::EvictLine(u64 addr) { void CachedState::EvictLine(u64 addr) {
u32 offset; u32 offset;
u32 page = DivideAddr(addr, offset); u32 page = DivideAddr(addr, offset);
if (blocks[page]) { blocks[page].lines[offset] = CachedLine();
blocks[page]->lines[offset].reset();
blocks[page].reset();
}
} }
u32 Interpreter::ExecuteCached() { u32 Interpreter::ExecuteCached() {
auto addr = regs.pc; auto addr = regs.pc;
auto blockAddr = addr; auto blockAddr = addr;
auto line = cachedState.GetLine(addr); auto page = (addr >> 12) & 0xfffff;
auto offset = addr & 0xfff;
auto &blocks = cachedState.blocks;
if (line) { if (page >= blocks.size())
for (u32 i = 0; i < line->len; i++) { blocks.push_back();
if (!MaybeAdvance())
return i + 1;
Instruction instr = line->code[i]; if (page < blocks.size()) {
DecodeExecute(instr); 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 Instruction instr = block.code[i];
if (IsBranchLikely(instr) && !regs.delaySlot) DecodeExecute(instr);
break;
// 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<Instruction, MAX_INSTRUCTION_PER_LINE> code; if (line.code.size() > 0) {
}
std::vector<Instruction> code;
code.resize(MAX_INSTRUCTION_PER_LINE);
u32 i; u32 i;
bool fetchDelaySlot = false; bool fetchDelaySlot = false;
@@ -180,7 +178,7 @@ u32 Interpreter::ExecuteCached() {
} }
} }
cachedState.InsertLine(blockAddr, std::make_shared<CachedLine>(code, i, i)); cachedState.InsertLine(blockAddr, {code, i, i});
return ExecuteCached(); return ExecuteCached();
} }
+8 -19
View File
@@ -5,36 +5,26 @@
namespace n64 { namespace n64 {
struct Core; struct Core;
static constexpr u32 MAX_LINES_PER_BLOCK = 1 << 12; static constexpr u32 MAX_INSTRUCTION_PER_BLOCK = 1 << 12;
static constexpr u32 MAX_INSTRUCTION_PER_LINE = MAX_LINES_PER_BLOCK >> 2;
struct CachedLine { struct CachedBlock {
std::array<Instruction, MAX_INSTRUCTION_PER_LINE> code = {}; std::array<Instruction, MAX_INSTRUCTION_PER_BLOCK> code = {};
u32 cycles = 0; u32 cycles = 0;
u32 len = 0; u32 len = 0;
}; };
struct CachedBlock {
std::array<std::shared_ptr<CachedLine>, MAX_LINES_PER_BLOCK / 4> lines = {};
};
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
struct CachedState { struct CachedState {
CachedState() { blocks.resize((u64(std::numeric_limits<u32>::max()) + 1) / MAX_LINES_PER_BLOCK); } CachedState() {}
CachedBlocks blocks = {}; std::vector<CachedBlock> blocks = {};
bool exception = false; bool exception = false;
void Reset() { void Reset() {
for (auto &block : blocks) { blocks = {};
block.reset();
}
exception = false; exception = false;
} }
std::shared_ptr<CachedLine> GetLine(u64); const CachedLine &GetLine(u64);
void InsertLine(u64, std::shared_ptr<CachedLine>); void InsertLine(u64, const CachedLine &);
void EvictLine(u64); void EvictLine(u64);
}; };
@@ -51,7 +41,6 @@ struct Interpreter final {
cachedState.Reset(); cachedState.Reset();
} }
CachedState cachedState; CachedState cachedState;
private: private:
+418 -390
View File
@@ -5,456 +5,484 @@
#include <ImGuiImpl/StatusBar.hpp> #include <ImGuiImpl/StatusBar.hpp>
#include <resources/gamecontrollerdb.h> #include <resources/gamecontrollerdb.h>
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) { KaizenGui::KaizenGui() noexcept :
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle()); window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
SDL_InitSubSystem(SDL_INIT_GAMEPAD); 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() { KaizenGui::~KaizenGui() {
gui::Cleanup(); gui::Cleanup();
SDL_Quit(); SDL_Quit();
} }
void KaizenGui::QueryDevices(const SDL_Event &event) { void KaizenGui::QueryDevices(const SDL_Event &event) {
switch (event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_ADDED:
if (!gamepad) { if (!gamepad) {
const auto index = event.gdevice.which; const auto index = event.gdevice.which;
gamepad = SDL_OpenGamepad(index); gamepad = SDL_OpenGamepad(index);
info("Found controller!"); info("Found controller!");
info("Name: {}", SDL_GetGamepadName(gamepad)); info("Name: {}", SDL_GetGamepadName(gamepad));
info("Vendor: {}", SDL_GetGamepadVendor(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) { void KaizenGui::HandleInput(const SDL_Event &event) {
const n64::Core& core = n64::Core::GetInstance(); const n64::Core &core = n64::Core::GetInstance();
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
switch(event.type) { switch (event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION: case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if(!gamepad) if (!gamepad)
break;
{
pif.UpdateButton(0, n64::Controller::Key::Z,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else {
xclamped /= SDL_JOYSTICK_AXIS_MAX;
}
xclamped *= 86;
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) {
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
}
break; break;
{
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else {
xclamped /= SDL_JOYSTICK_AXIS_MAX;
}
xclamped *= 86;
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) {
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
}
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP: 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; 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_DOWN:
case SDL_EVENT_KEY_UP: case SDL_EVENT_KEY_UP:
{ {
const auto keys = SDL_GetKeyboardState(nullptr); const auto keys = SDL_GetKeyboardState(nullptr);
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true; 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);
} }
break;
fastForward = keys[SDL_SCANCODE_SPACE]; default:
if(!unlockFramerate) break;
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;
}
} }
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() { std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
auto lastPC = Util::Error::GetLastPC(); auto lastPC = Util::Error::GetLastPC();
if(lastPC.has_value()) { if (lastPC.has_value()) {
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
} }
auto memoryAccess = Util::Error::GetMemoryAccess(); auto memoryAccess = Util::Error::GetMemoryAccess();
if(memoryAccess.has_value()) { if (memoryAccess.has_value()) {
const auto [is_write, size, address, written_val] = memoryAccess.value(); const auto [is_write, size, address, written_val] = memoryAccess.value();
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", ImGui::Text("%s",
static_cast<u8>(size), address, std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
is_write ? std::format(" (value = 0x{:X})", written_val) : "") address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
.c_str()); .c_str());
} }
return {lastPC, memoryAccess}; return {lastPC, memoryAccess};
} }
void KaizenGui::RenderUI() { void KaizenGui::RenderUI() {
n64::Core& core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
gui::StartFrame(); gui::StartFrame();
if(ImGui::BeginMainMenuBar()) { if (ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("File")) {
if(ImGui::MenuItem("Open", "Ctrl-O")) { if (ImGui::MenuItem("Open", "Ctrl-O")) {
fileDialogOpen = true; fileDialogOpen = true;
} }
if(ImGui::MenuItem("Exit")) { if (ImGui::MenuItem("Exit")) {
quit = true; quit = true;
emuThread.Stop(); emuThread.Stop();
} }
ImGui::EndMenu(); 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")) { if (!Util::Error::IsHandled()) {
debugger.Open(); ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
}
ImGui::EndDisabled();
if(ImGui::MenuItem("Options")) {
settingsWindow.isOpen = true;
}
ImGui::EndMenu();
} }
if(ImGui::BeginMenu("Help")) {
if(ImGui::MenuItem("About")) { if (settingsWindow.isOpen) {
aboutOpen = true; ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
}
ImGui::EndMenu();
} }
ImGui::EndMainMenuBar();
}
if(!Util::Error::IsHandled()) { if (aboutOpen) {
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str()); ImGui::OpenPopup("About Kaizen");
}
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();
} }
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)) { const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
emuThread.TogglePause(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
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();
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); ImGui::Text("to offer a friendly user experience and compatibility.");
const bool chooseAnother = ImGui::Button("Choose another ROM"); ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
if(ignore || stop || chooseAnother) { ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
Util::Error::SetHandled(); 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(); ImGui::CloseCurrentPopup();
}
if(ignore) {
emuThread.TogglePause();
}
if(stop || chooseAnother) {
emuThread.Stop();
}
if(chooseAnother) {
fileDialogOpen = true;
}
break;
} }
if(ImGui::Button("OK")) ImGui::EndPopup();
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();
}
if(ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar();
}
if (shouldDisplaySpinner) { ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); 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])); if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
ImGui::SameLine(); 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); if (ignore) {
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str()); emuThread.TogglePause();
ImGui::PopFont(); }
ImGui::End(); if (stop || chooseAnother) {
emuThread.Stop();
}
ImGui::PopStyleVar(); if (chooseAnother) {
ImGui::PopStyleColor(); fileDialogOpen = true;
} }
break;
}
ImGui::Render(); if (ImGui::Button("OK"))
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::CloseCurrentPopup();
ImGui::UpdatePlatformWindows(); }
ImGui::RenderPlatformWindowsDefault(); 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) { const bool ignore = ImGui::Button("Try continuing");
fileDialogOpen = false; ImGui::SameLine();
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; const bool stop = ImGui::Button("Stop emulation");
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { ImGui::SameLine();
auto kaizen = static_cast<KaizenGui*>(userdata); const bool chooseAnother = ImGui::Button("Choose another ROM");
const bool openInDebugger =
if (!filelist) { lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
panic("An error occured: {}", SDL_GetError()); if (ignore || stop || chooseAnother || openInDebugger) {
} Util::Error::SetHandled();
ImGui::CloseCurrentPopup();
}
if (!*filelist) { if (ignore) {
warn("The user did not select any file."); emuThread.TogglePause();
warn("Most likely, the dialog was canceled."); }
if (stop || chooseAnother) {
emuThread.Stop();
}
if (chooseAnother) {
fileDialogOpen = true;
}
if (openInDebugger) {
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
debugger.Open();
emuThread.Reset();
}
}
break;
default:
break;
}
ImGui::EndPopup();
}
if (ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar();
}
if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
ImGui::SameLine();
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
ImGui::PopFont();
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
if (fileDialogOpen) {
fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
{"Nintendo 64 executable", "n64;z64;v64"},
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) {
panic("An error occured: {}", SDL_GetError());
}
if (!*filelist) {
warn("The user did not select any file.");
warn("Most likely, the dialog was canceled.");
return;
}
kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
},
this, window.getHandle(), filters, 3, nullptr, false);
}
if (minimized)
return; return;
}
kaizen->fileToLoad = *filelist; if (core.romLoaded) {
kaizen->shouldDisplaySpinner = true; core.parallel.UpdateScreen<true>();
return;
}
std::thread fileWorker(&KaizenGui::FileWorker, kaizen); core.parallel.UpdateScreen<false>();
fileWorker.detach();
}, this, window.getHandle(), filters, 3, nullptr, false);
}
if(minimized)
return;
if(core.romLoaded) {
core.parallel.UpdateScreen<true>();
return;
}
core.parallel.UpdateScreen<false>();
} }
void KaizenGui::LoadROM(const std::string &path) noexcept { void KaizenGui::LoadROM(const std::string &path) noexcept {
n64::Core& core = n64::Core::GetInstance(); n64::Core &core = n64::Core::GetInstance();
core.LoadROM(path); core.LoadROM(path);
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str()); SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
} }
void KaizenGui::run() { void KaizenGui::run() {
while(!quit) { while (!quit) {
SDL_Event e; SDL_Event e;
while (SDL_PollEvent(&e)) { while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e); ImGui_ImplSDL3_ProcessEvent(&e);
switch(e.type) { switch (e.type) {
case SDL_EVENT_QUIT: case SDL_EVENT_QUIT:
quit = true; quit = true;
emuThread.Stop(); emuThread.Stop();
break; break;
case SDL_EVENT_WINDOW_MINIMIZED: case SDL_EVENT_WINDOW_MINIMIZED:
minimized = true; minimized = true;
break; break;
case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_RESTORED:
minimized = false; minimized = false;
break; break;
default: break; default:
} break;
QueryDevices(e); }
HandleInput(e); 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 { void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
n64::Core::GetInstance().LoadTAS(fs::path(path));
}
+4 -3
View File
@@ -10,7 +10,7 @@ class KaizenGui final {
public: public:
explicit KaizenGui() noexcept; explicit KaizenGui() noexcept;
~KaizenGui(); ~KaizenGui();
double fpsCounter = -1.0; double fpsCounter = -1.0;
bool fastForward = false; bool fastForward = false;
bool unlockFramerate = false; bool unlockFramerate = false;
@@ -22,7 +22,7 @@ public:
Debugger debugger; Debugger debugger;
SDL_Gamepad* gamepad = nullptr; SDL_Gamepad* gamepad = nullptr;
void run(); void run();
static void LoadTAS(const std::string &path) noexcept; static void LoadTAS(const std::string &path) noexcept;
void LoadROM(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept;
@@ -37,12 +37,13 @@ private:
void HandleInput(const SDL_Event &event); void HandleInput(const SDL_Event &event);
void QueryDevices(const SDL_Event &event); void QueryDevices(const SDL_Event &event);
[[noreturn]] void FileWorker() { void FileWorker() {
while (true) { while (true) {
if (!fileToLoad.empty()) { if (!fileToLoad.empty()) {
LoadROM(fileToLoad); LoadROM(fileToLoad);
shouldDisplaySpinner = false; shouldDisplaySpinner = false;
fileToLoad = ""; fileToLoad = "";
return;
} }
} }
} }