diff --git a/.gitignore b/.gitignore index d9bda124..7a47fd50 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ roms/ .cache/ .vscode/ vgcore.* -*.txt \ No newline at end of file +*.txt +*.data \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c4fad0b..97f57e1a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED true) add_compile_definitions(VULKAN_DEBUG) +file(COPY ${CMAKE_SOURCE_DIR}/../resources DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(n64) add_subdirectory(frontend) diff --git a/src/frontend/App.cpp b/src/frontend/App.cpp index b46a407d..78c07f53 100644 --- a/src/frontend/App.cpp +++ b/src/frontend/App.cpp @@ -5,6 +5,7 @@ void App::Run() { // Main loop bool done = false; + const u8* state = SDL_GetKeyboardState(nullptr); while (!done) { SDL_Event event; while (SDL_PollEvent(&event)) { @@ -14,6 +15,15 @@ void App::Run() { case SDL_WINDOWEVENT: done = window.gotClosed(event); break; + case SDL_CONTROLLERDEVICEADDED: { + const int index = event.cdevice.which; + core.gamepad = SDL_GameControllerOpen(index); + core.gamepadConnected = true; + } break; + case SDL_CONTROLLERDEVICEREMOVED: + SDL_GameControllerClose(core.gamepad); + core.gamepadConnected = false; + break; case SDL_KEYDOWN: switch(event.key.keysym.sym) { case SDLK_o: { @@ -28,7 +38,7 @@ void App::Run() { } break; } - core.PollInputs(event); + core.UpdateController(state); } core.Run(window); diff --git a/src/frontend/App.hpp b/src/frontend/App.hpp index eb2c3774..bd05d841 100644 --- a/src/frontend/App.hpp +++ b/src/frontend/App.hpp @@ -3,6 +3,7 @@ struct App { App() : window(core) {}; + ~App() = default; void Run(); inline void LoadROM(const std::string& path) { core.LoadROM(path); diff --git a/src/frontend/imgui/Window.cpp b/src/frontend/imgui/Window.cpp index 3bf1a2c9..83043994 100644 --- a/src/frontend/imgui/Window.cpp +++ b/src/frontend/imgui/Window.cpp @@ -174,6 +174,9 @@ Window::~Window() { vkDestroyDescriptorPool(device, descriptorPool, nullptr); vkDestroyDevice(device, nullptr); vkDestroyInstance(instance, nullptr); + SDL_DestroyWindow(window); + SDL_DestroyWindow(g_Window); + SDL_Quit(); } ImDrawData* Window::Present(n64::Core& core) { diff --git a/src/n64/Core.cpp b/src/n64/Core.cpp index dade90b6..09ccad8b 100644 --- a/src/n64/Core.cpp +++ b/src/n64/Core.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include namespace n64 { Core::Core() { @@ -21,14 +23,24 @@ void Core::LoadROM(const std::string& rom) { void Core::Run(Window& window) { MMIO& mmio = mem.mmio; - for(mmio.vi.current = 0; mmio.vi.current < 262; mmio.vi.current++) { + int cycles = 0; + for(int field = 0; field < mmio.vi.numFields; field++) { if(romLoaded) { - for (int i = 0; i < 6000; i++) { - cpu.Step(mem); - mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); - mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); - mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); - mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); + //timerInstructions.Start(); + for (int i = 0; i < mmio.vi.numHalflines; i++) { + if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { + InterruptRaise(mmio.mi, cpu.regs, Interrupt::VI); + } + + for(;cycles <= mmio.vi.cyclesPerHalfline; cycles++) { + cpu.Step(mem); + mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); + mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); + mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); + mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp); + } + + cycles -= mmio.vi.cyclesPerHalfline; } if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { @@ -42,7 +54,85 @@ void Core::Run(Window& window) { } } -void Core::PollInputs(SDL_Event e) { +#define GET_BUTTON(gamepad, i) SDL_GameControllerGetButton(gamepad, i) +#define GET_AXIS(gamepad, axis) SDL_GameControllerGetAxis(gamepad, axis) +void Core::UpdateController(const u8* state) { + Controller& controller = mem.mmio.si.controller; + if(gamepadConnected) { + controller.b1 = 0; + controller.b2 = 0; + controller.b3 = 0; + controller.b4 = 0; + + bool A = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_A); + bool B = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_X); + bool Z = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == 32767; + bool START = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_START); + bool DUP = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_UP); + bool DDOWN = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_DOWN); + bool DLEFT = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_LEFT); + bool DRIGHT = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); + bool L = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); + bool R = GET_BUTTON(gamepad, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); + bool CUP = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTY) == 32767; + bool CDOWN = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTY) == -32768; + bool CLEFT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) == -32768; + bool CRIGHT = GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_RIGHTX) == 32767; + + controller.b1 = (A << 7) | (B << 6) | (Z << 5) | (START << 4) | + (DUP << 3) | (DDOWN << 2) | (DLEFT << 1) | DRIGHT; + + controller.b2 = ((START && L && R) << 7) | (0 << 6) | (L << 5) | (R << 4) | + (CUP << 3) | (CDOWN << 2) | (CLEFT << 1) | CRIGHT; + + s8 xaxis = (s8)std::clamp((GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTX) >> 8), -127, 127); + s8 yaxis = (s8)std::clamp(-(GET_AXIS(gamepad, SDL_CONTROLLER_AXIS_LEFTY) >> 8), -127, 127); + + controller.b3 = xaxis; + controller.b4 = yaxis; + + if((controller.b2 >> 7) & 1) { + controller.b1 &= ~0x10; + controller.b3 = 0; + controller.b4 = 0; + } + } else { + controller.b1 = 0; + controller.b2 = 0; + controller.b3 = 0; + controller.b4 = 0; + + controller.b1 = + (state[SDL_SCANCODE_X] << 7) | + (state[SDL_SCANCODE_C] << 6) | + (state[SDL_SCANCODE_Z] << 5) | + (state[SDL_SCANCODE_RETURN] << 4) | + (state[SDL_SCANCODE_KP_8] << 3) | + (state[SDL_SCANCODE_KP_5] << 2) | + (state[SDL_SCANCODE_KP_4] << 1) | + (state[SDL_SCANCODE_KP_6]); + controller.b2 = + ((state[SDL_SCANCODE_RETURN] && state[SDL_SCANCODE_A] && state[SDL_SCANCODE_S]) << 7) | + (0 << 6) | + (state[SDL_SCANCODE_A] << 5) | + (state[SDL_SCANCODE_S] << 4) | + (state[SDL_SCANCODE_I] << 3) | + (state[SDL_SCANCODE_J] << 2) | + (state[SDL_SCANCODE_K] << 1) | + (state[SDL_SCANCODE_L]); + + s8 xaxis = state[SDL_SCANCODE_LEFT] ? -128 : (state[SDL_SCANCODE_RIGHT] ? 127 : 0); + s8 yaxis = state[SDL_SCANCODE_DOWN] ? -128 : (state[SDL_SCANCODE_UP] ? 127 : 0); + + controller.b3 = xaxis; + controller.b4 = yaxis; + + if((controller.b2 >> 7) & 1) { + controller.b1 &= ~0x10; + controller.b3 = 0; + controller.b4 = 0; + } + } } } diff --git a/src/n64/Core.hpp b/src/n64/Core.hpp index 0f4db52d..96f27601 100644 --- a/src/n64/Core.hpp +++ b/src/n64/Core.hpp @@ -12,9 +12,11 @@ struct Core { void Reset(); void LoadROM(const std::string&); void Run(Window&); - void PollInputs(SDL_Event); + void UpdateController(const u8*); VI& GetVI() { return mem.mmio.vi; } bool romLoaded = false; + SDL_GameController* gamepad; + bool gamepadConnected = false; private: friend struct ::Window; Mem mem; diff --git a/src/n64/core/Cpu.cpp b/src/n64/core/Cpu.cpp index ff9b3e84..fb890337 100644 --- a/src/n64/core/Cpu.cpp +++ b/src/n64/core/Cpu.cpp @@ -99,7 +99,9 @@ inline void HandleInterrupt(Registers& regs) { } void Cpu::LogInstruction(u32 instruction) { +#ifndef NDEBUG count = cs_disasm(handle, reinterpret_cast(&instruction), 4, regs.pc, 0, &insn); +#endif if (count > 0) { for(auto j = 0; j < count; j++) { @@ -113,13 +115,14 @@ void Cpu::LogInstruction(u32 instruction) { void Cpu::Step(Mem& mem) { regs.gpr[0] = 0; + regs.prevDelaySlot = regs.delaySlot; regs.delaySlot = false; CheckCompareInterrupt(mem.mmio.mi, regs); u32 instruction = mem.Read(regs, regs.pc, regs.pc); - LogInstruction(instruction); + //LogInstruction(instruction); HandleInterrupt(regs); diff --git a/src/n64/core/rsp/decode.cpp b/src/n64/core/rsp/decode.cpp index f1783f59..7cce97b2 100644 --- a/src/n64/core/rsp/decode.cpp +++ b/src/n64/core/rsp/decode.cpp @@ -20,7 +20,7 @@ inline void special(RSP& rsp, u32 instr) { //case 0x24: rsp.and_(instr); break; //case 0x25: rsp.or_(instr); break; //case 0x27: rsp.nor(instr); break; - default: util::panic("Unhandled RSP special instruction %d %d\n", (mask >> 3) & 7, mask & 7); + default: util::panic("Unhandled RSP special instruction {} {}\n", (mask >> 3) & 7, mask & 7); } } @@ -29,7 +29,7 @@ inline void regimm(RSP& rsp, u32 instr) { switch(mask) { //case 0x00: rsp.b(instr, (s32)rsp.gpr[RS(instr)] < 0); break; //case 0x01: rsp.b(instr, (s32)rsp.gpr[RS(instr)] >= 0); break; - default: util::panic("Unhandled RSP regimm instruction %d %d\n", (mask >> 3) & 3, mask & 7); + default: util::panic("Unhandled RSP regimm instruction {} {}\n", (mask >> 3) & 3, mask & 7); } } @@ -37,7 +37,7 @@ inline void lwc2(RSP& rsp, u32 instr) { u8 mask = (instr >> 11) & 0x1F; switch(mask) { //case 0x04: rsp.lqv(instr); break; - default: util::panic("Unhandled RSP LWC2 %d %d\n", (mask >> 3) & 3, mask & 7); + default: util::panic("Unhandled RSP LWC2 {} {}\n", (mask >> 3) & 3, mask & 7); } } @@ -45,7 +45,7 @@ inline void swc2(RSP& rsp, u32 instr) { u8 mask = (instr >> 11) & 0x1F; switch(mask) { //case 0x04: rsp.sqv(instr); break; - default: util::panic("Unhandled RSP SWC2 %d %d\n", (mask >> 3) & 3, mask & 7); + default: util::panic("Unhandled RSP SWC2 {} {}\n", (mask >> 3) & 3, mask & 7); } } @@ -56,7 +56,7 @@ inline void cop2(RSP& rsp, u32 instr) { case 0x00: switch(mask_sub) { //case 0x02: rsp.cfc2(instr); break; - default: util::panic("Unhandled RSP COP2 sub %d %d\n", (mask_sub >> 3) & 3, mask_sub & 3); + default: util::panic("Unhandled RSP COP2 sub {} {}\n", (mask_sub >> 3) & 3, mask_sub & 3); } break; //case 0x13: rsp.vabs(instr); break; @@ -64,7 +64,7 @@ inline void cop2(RSP& rsp, u32 instr) { //case 0x21: rsp.veq(instr); break; //case 0x22: rsp.vne(instr); break; //case 0x33: rsp.vmov(instr); break; - default: util::panic("Unhandled RSP COP2 %d %d\n", (mask >> 3) & 7, mask & 7); + default: util::panic("Unhandled RSP COP2 {} {}\n", (mask >> 3) & 7, mask & 7); } } @@ -73,7 +73,7 @@ inline void cop0(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u32 instr) { switch(mask) { //case 0x00: rsp.mfc0(rdp, instr); break; //case 0x04: rsp.mtc0(mi, regs, rdp, instr); break; - default: util::panic("Unhandled RSP COP0 %d %d\n", (mask >> 3) & 3, mask & 7); + default: util::panic("Unhandled RSP COP0 {} {}\n", (mask >> 3) & 3, mask & 7); } } @@ -100,7 +100,7 @@ void RSP::Exec(MI &mi, Registers ®s, RDP &rdp, u32 instr) { //case 0x2B: sw(instr); break; //case 0x32: lwc2(*this, instr); break; //case 0x3A: swc2(*this, instr); break; - default: util::panic("Unhandled RSP instruction %d %d\n", (mask >> 3) & 7, mask & 7); + default: util::panic("Unhandled RSP instruction {} {}\n", (mask >> 3) & 7, mask & 7); } } } \ No newline at end of file diff --git a/src/util.hpp b/src/util.hpp index c5a3dc1e..eaab66a0 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -6,8 +6,13 @@ #include #include #include +#include namespace util { +using SteadyClock = std::chrono::steady_clock; +using DurationMillis = std::chrono::duration; +using TimePoint = std::chrono::time_point; + enum MessageType : u8 { Info, Warn, Error }; @@ -46,6 +51,25 @@ constexpr void logdebug(const std::string& fmt, Args... args) { #endif } +#define TIMER(name) \ + struct Timer##name { \ + util::TimePoint start, end; \ + Timer##name() {} \ + ~Timer##name() {} \ + void Start() { \ + start = util::SteadyClock::now(); \ + } \ + \ + void End() { \ + end = util::SteadyClock::now(); \ + } \ + \ + void PrintDuration() { \ + auto diff = end - start; \ + util::info(#name + std::string(" took {:.3f}ms to run\n"), diff.count()); \ + } \ + } + template [[maybe_unused]] auto GetSwapFunc(T num) -> T { static_assert(sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "GetSwapFunc used with invalid size!");