diff --git a/.gitignore b/.gitignore index defd838b..144b1da5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,5 @@ disasm.txt log.txt .vs/ CMakeSettings.json -out/ \ No newline at end of file +out/ +settings.json \ No newline at end of file diff --git a/src/frontend/App.cpp b/src/frontend/App.cpp index ae44149a..48fc3740 100644 --- a/src/frontend/App.cpp +++ b/src/frontend/App.cpp @@ -12,7 +12,7 @@ void App::Run() { SDL_EventState(SDL_DROPFILE, SDL_ENABLE); while (!core.done) { - core.Run(window, window.volumeL, window.volumeR); + core.Run(window, window.settings.GetVolumeL(), window.settings.GetVolumeL()); core.UpdateController(state); SDL_Event event; diff --git a/src/frontend/Settings.cpp b/src/frontend/Settings.cpp new file mode 100644 index 00000000..d9133ad1 --- /dev/null +++ b/src/frontend/Settings.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include + +using namespace std::filesystem; + +Settings::Settings(n64::Core& core) { + auto modes = std::fstream::in | std::fstream::out; + auto fileExists = exists("resources/settings.json"); + if(!fileExists) { + modes |= std::fstream::trunc; + } + std::fstream settingsFile{"resources/settings.json", modes}; + if(fileExists) { + settings = json::parse(settingsFile); + auto entryCpuType = settings["cpu"]["type"]; + if(!entryCpuType.empty()) { + cpuType = entryCpuType.get(); + if(cpuType == "dynarec") { + core.cpuType = n64::CpuType::Dynarec; + } else if(cpuType == "interpreter") { + core.cpuType = n64::CpuType::Interpreter; + } else { + util::panic("Unrecognized cpu type: {}\n", cpuType); + } + } else { + settingsFile.clear(); + settings["cpu"]["type"] = "dynarec"; + settingsFile << settings; + core.cpuType = n64::CpuType::Dynarec; + } + + auto volumeREntry = settings["audio"]["volumeR"]; + if(!volumeREntry.empty()) { + auto value = volumeREntry.get(); + volumeR = value; + } else { + settingsFile.clear(); + settings["audio"]["volumeR"] = 0.5; + settingsFile << settings; + volumeR = 0.5; + } + auto volumeLEntry = settings["audio"]["volumeL"]; + if(!volumeLEntry.empty()) { + auto value = volumeLEntry.get(); + volumeL = value; + } else { + settingsFile.clear(); + settings["audio"]["volumeL"] = 0.5; + settingsFile << settings; + volumeL = 0.5; + } + auto lockChannelsEntry = settings["audio"]["lockChannels"]; + if(!lockChannelsEntry.empty()) { + auto value = lockChannelsEntry.get(); + lockChannels = value; + } else { + settingsFile.clear(); + settings["audio"]["lockChannels"] = true; + settingsFile << settings; + lockChannels = true; + } + } else { + settings["cpu"]["type"] = "dynarec"; + settings["audio"]["volumeR"] = 0.5; + settings["audio"]["volumeL"] = 0.5; + settings["audio"]["lockChannels"] = true; + + core.cpuType = n64::CpuType::Dynarec; + volumeR = 0.5; + volumeL = 0.5; + lockChannels = true; + + settingsFile << settings; + } + + settingsFile.close(); +} + +Settings::~Settings() { + auto modes = std::fstream::out; + auto fileExists = exists("resources/settings.json"); + if(fileExists) { + modes |= std::fstream::trunc; + + std::fstream settingsFile{"resources/settings.json", modes}; + + settings["cpu"]["type"] = cpuType; + settings["audio"]["volumeR"] = volumeR; + settings["audio"]["volumeL"] = volumeL; + settings["audio"]["lockChannels"] = lockChannels; + settingsFile << settings; + settingsFile.close(); + } +} + +void Settings::RenderWidget(bool& show) { + if(show) { + ImGui::OpenPopup("Settings"); + if(ImGui::BeginPopupModal("Settings", &show)) { + static enum { CPU, Audio } category = CPU; + if(ImGui::Button("CPU")) { + category = CPU; + } + ImGui::SameLine(); + if(ImGui::Button("Audio")) { + category = Audio; + } + ImGui::Separator(); + if(category == Audio) { + ImGui::Checkbox("Lock channels", &lockChannels); + ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); + if (!lockChannels) { + ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); + } else { + volumeR = volumeL; + ImGui::BeginDisabled(); + ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); + ImGui::EndDisabled(); + } + } else if(category == CPU) { + const char* items[] = { "JIT", "Interpreter" }; + static int currentIndex = [this]() { + if(cpuType == "dynarec") return 0; + else if(cpuType == "interpreter") return 1; + return 0; + }(); + + if(ImGui::BeginCombo("CPU type", items[currentIndex])) { + for (int n = 0; n < 2; n++) { + const bool is_selected = (currentIndex == n); + if (ImGui::Selectable(items[n], is_selected)) { + currentIndex = n; + } + + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + } + + if(currentIndex == 0) { + cpuType = "dynarec"; + } + if(currentIndex == 1) { + cpuType = "interpreter"; + } + ImGui::EndCombo(); + } + } + ImGui::EndPopup(); + } + + SetLockChannels(lockChannels); + SetVolumeL(volumeL); + SetVolumeR(volumeR); + } +} \ No newline at end of file diff --git a/src/frontend/Settings.hpp b/src/frontend/Settings.hpp new file mode 100644 index 00000000..367023b6 --- /dev/null +++ b/src/frontend/Settings.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +using namespace nlohmann; + +struct Settings { + Settings(n64::Core&); + ~Settings(); + + float GetVolumeL() const { return volumeL; }; + float GetVolumeR() const { return volumeR; }; + bool GetLockChannels() const { return lockChannels; } + std::string GetCpuType() const { return cpuType; } + + void SetVolumeL(float v) { volumeL = v; }; + void SetVolumeR(float v) { volumeR = v; }; + void SetLockChannels(bool v) { lockChannels = v; } + void SetCpuType(std::string v) { cpuType = v; } + + void RenderWidget(bool& show); +private: + std::string cpuType = "interpreter"; + float volumeL = 0.0, volumeR = 0.0; + bool lockChannels = true; + json settings; +}; diff --git a/src/frontend/imgui/Window.cpp b/src/frontend/imgui/Window.cpp index 6a5be1e8..2a498f49 100644 --- a/src/frontend/imgui/Window.cpp +++ b/src/frontend/imgui/Window.cpp @@ -1,17 +1,14 @@ +#include #include #include #include #include -#include -#include #include VkInstance instance{}; -using json = nlohmann::json; -Window::Window(n64::Core& core) { +Window::Window(n64::Core& core) : settings(core) { InitSDL(); - LoadSettings(core); InitParallelRDP(core.mem.GetRDRAM(), window); InitImgui(); NFD::Init(); @@ -22,34 +19,6 @@ Window::Window(n64::Core& core) { && event.window.windowID == SDL_GetWindowID(window); } -void Window::LoadSettings(n64::Core &core) { - settingsFile.open("settings.json", std::fstream::in | std::fstream::out); - if(settingsFile.is_open()) { - settings = json::parse(settingsFile); - auto entryCpuType = settings["cpu_type"]; - if(!entryCpuType.empty()) { - auto cpuType = entryCpuType.get(); - if(cpuType == "dynarec") { - core.cpuType = n64::CpuType::Dynarec; - } else if(cpuType == "interpreter") { - core.cpuType = n64::CpuType::Interpreter; - } else { - util::panic("Unrecognized cpu type: {}\n", cpuType); - } - } else { - settings["cpu_type"] = "dynarec"; - settingsFile << settings; - core.cpuType = n64::CpuType::Dynarec; - } - } else { - settings["cpu_type"] = "dynarec"; - settingsFile << settings; - core.cpuType = n64::CpuType::Dynarec; - } - - settingsFile.close(); -} - void Window::InitSDL() { SDL_Init(SDL_INIT_EVERYTHING); n64::InitAudio(); @@ -217,6 +186,7 @@ void Window::Render(n64::Core& core) { SDL_SetWindowTitle(window, windowTitle.c_str()); windowTitle = shadowWindowTitle; } + static bool showSettings = false; bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus()); if(showMainMenuBar) { @@ -277,21 +247,7 @@ void Window::Render(n64::Core& core) { ImGui::EndMainMenuBar(); } - if(showSettings) { - ImGui::OpenPopup("Settings"); - if(ImGui::BeginPopupModal("Settings", &showSettings)) { - ImGui::Checkbox("Lock channels", &lockVolume); - ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); - if (!lockVolume) { - ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); - } else { - volumeR = volumeL; - ImGui::BeginDisabled(); - ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput); - ImGui::EndDisabled(); - } - ImGui::EndPopup(); - } - } + settings.RenderWidget(showSettings); + ImGui::PopFont(); } diff --git a/src/frontend/imgui/Window.hpp b/src/frontend/imgui/Window.hpp index b5162f9f..b9070620 100644 --- a/src/frontend/imgui/Window.hpp +++ b/src/frontend/imgui/Window.hpp @@ -6,9 +6,7 @@ #include #include #include -#include - -using namespace nlohmann; +#include struct Window { explicit Window(n64::Core& core); @@ -18,12 +16,9 @@ struct Window { [[nodiscard]] bool gotClosed(SDL_Event event); ImFont *uiFont{}, *codeFont{}; u32 windowID{}; - float volumeL = 0.0, volumeR = 0.0; + Settings settings; void LoadROM(n64::Core& core, const std::string& path); private: - json settings; - std::fstream settingsFile; - bool lockVolume = true; SDL_Window* window{}; std::string windowTitle{"Gadolinium"}; std::string shadowWindowTitle{windowTitle}; @@ -31,7 +26,6 @@ private: void InitSDL(); void InitImgui(); void Render(n64::Core& core); - void LoadSettings(n64::Core&); VkPhysicalDevice physicalDevice{}; VkDevice device{}; diff --git a/src/n64/Core.cpp b/src/n64/Core.cpp index 68abe404..c362e7e9 100644 --- a/src/n64/Core.cpp +++ b/src/n64/Core.cpp @@ -33,6 +33,7 @@ CartInfo Core::LoadROM(const std::string& rom_) { void Core::Run(Window& window, float volumeL, float volumeR) { MMIO& mmio = mem.mmio; Controller& controller = mmio.si.controller; + Registers& regs = CpuGetRegs(); for(int field = 0; field < mmio.vi.numFields; field++) { int frameCycles = 0; @@ -41,33 +42,33 @@ void Core::Run(Window& window, float volumeL, float volumeR) { mmio.vi.current = (i << 1) + field; if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { - InterruptRaise(mmio.mi, CpuGetRegs(), Interrupt::VI); + InterruptRaise(mmio.mi, regs, Interrupt::VI); } for(;cycles <= mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) { CpuStep(mem); if(!mmio.rsp.spStatus.halt) { - cpu.regs.steps++; - if(cpu.regs.steps > 2) { + regs.steps++; + if(regs.steps > 2) { mmio.rsp.steps += 2; - cpu.regs.steps -= 3; + regs.steps -= 3; } while(mmio.rsp.steps > 0) { mmio.rsp.steps--; - mmio.rsp.Step(CpuGetRegs(), mem); + mmio.rsp.Step(regs, mem); } } - mmio.ai.Step(mem, CpuGetRegs(), 1, volumeL, volumeR); - scheduler.tick(1, mem, CpuGetRegs()); + mmio.ai.Step(mem, regs, 1, volumeL, volumeR); + scheduler.tick(1, mem, regs); } cycles -= mmio.vi.cyclesPerHalfline; } if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { - InterruptRaise(mmio.mi, CpuGetRegs(), Interrupt::VI); + InterruptRaise(mmio.mi, regs, Interrupt::VI); } UpdateScreenParallelRdp(*this, window, GetVI()); diff --git a/src/n64/core/RSP.cpp b/src/n64/core/RSP.cpp index 255fd933..8f4994ab 100644 --- a/src/n64/core/RSP.cpp +++ b/src/n64/core/RSP.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include namespace n64 { RSP::RSP() { diff --git a/src/n64/core/interpreter/instructions.cpp b/src/n64/core/interpreter/instructions.cpp index e8ade609..f18592ee 100644 --- a/src/n64/core/interpreter/instructions.cpp +++ b/src/n64/core/interpreter/instructions.cpp @@ -778,28 +778,28 @@ void Interpreter::trap(bool cond) { } } -void Cpu::mtc2(u32 instr) { +void Interpreter::mtc2(u32 instr) { cop2Latch = regs.gpr[RT(instr)]; } -void Cpu::mfc2(u32 instr) { +void Interpreter::mfc2(u32 instr) { s32 value = cop2Latch; regs.gpr[RT(instr)] = value; } -void Cpu::dmtc2(u32 instr) { +void Interpreter::dmtc2(u32 instr) { cop2Latch = regs.gpr[RT(instr)]; } -void Cpu::dmfc2(u32 instr) { +void Interpreter::dmfc2(u32 instr) { regs.gpr[RT(instr)] = cop2Latch; } -void Cpu::ctc2(u32) { +void Interpreter::ctc2(u32) { } -void Cpu::cfc2(u32) { +void Interpreter::cfc2(u32) { }