From 53873c75cc2226d579cd6828edcd93f206866416 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 20:48:13 +0200 Subject: [PATCH 01/10] Don't force users to load rom from terminal or by dragging and dropping onto executable file. Instead, open a blank window and use SDL's drag&drop feature --- include/emulator.hpp | 2 +- src/emulator.cpp | 296 +++++++++++++++++++++++-------------------- src/main.cpp | 14 +- 3 files changed, 169 insertions(+), 143 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index 3985c613..fca2240f 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -47,7 +47,7 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = true; + bool running = true, romLoaded = false; #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; diff --git a/src/emulator.cpp b/src/emulator.cpp index 1e30e073..3d3678df 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -90,18 +90,19 @@ void Emulator::run() { httpServer.startHttpServer(); #endif while (running) { + if(romLoaded) { #ifdef PANDA3DS_ENABLE_HTTP_SERVER - pollHttpServer(); + pollHttpServer(); #endif - runFrame(); // Run 1 frame of instructions - gpu.display(); // Display graphics + runFrame(); // Run 1 frame of instructions + gpu.display(); // Display graphics - ServiceManager& srv = kernel.getServiceManager(); - - // Send VBlank interrupts - srv.sendGPUInterrupt(GPUInterrupt::VBlank0); - srv.sendGPUInterrupt(GPUInterrupt::VBlank1); + ServiceManager& srv = kernel.getServiceManager(); + // Send VBlank interrupts + srv.sendGPUInterrupt(GPUInterrupt::VBlank0); + srv.sendGPUInterrupt(GPUInterrupt::VBlank1); + } SDL_Event event; while (SDL_PollEvent(&event)) { namespace Keys = HID::Keys; @@ -113,104 +114,111 @@ void Emulator::run() { return; case SDL_KEYDOWN: - switch (event.key.keysym.sym) { - case SDLK_l: srv.pressKey(Keys::A); break; - case SDLK_k: srv.pressKey(Keys::B); break; - case SDLK_o: srv.pressKey(Keys::X); break; - case SDLK_i: srv.pressKey(Keys::Y); break; + if(romLoaded) { + switch (event.key.keysym.sym) { + case SDLK_l: srv.pressKey(Keys::A); break; + case SDLK_k: srv.pressKey(Keys::B); break; + case SDLK_o: srv.pressKey(Keys::X); break; + case SDLK_i: srv.pressKey(Keys::Y); break; - case SDLK_q: srv.pressKey(Keys::L); break; - case SDLK_p: srv.pressKey(Keys::R); break; + case SDLK_q: srv.pressKey(Keys::L); break; + case SDLK_p: srv.pressKey(Keys::R); break; - case SDLK_RIGHT: srv.pressKey(Keys::Right); break; - case SDLK_LEFT: srv.pressKey(Keys::Left); break; - case SDLK_UP: srv.pressKey(Keys::Up); break; - case SDLK_DOWN: srv.pressKey(Keys::Down); break; + case SDLK_RIGHT: srv.pressKey(Keys::Right); break; + case SDLK_LEFT: srv.pressKey(Keys::Left); break; + case SDLK_UP: srv.pressKey(Keys::Up); break; + case SDLK_DOWN: srv.pressKey(Keys::Down); break; - case SDLK_w: - srv.setCirclepadY(0x9C); - keyboardAnalogY = true; - break; + case SDLK_w: + srv.setCirclepadY(0x9C); + keyboardAnalogY = true; + break; - case SDLK_a: - srv.setCirclepadX(-0x9C); - keyboardAnalogX = true; - break; + case SDLK_a: + srv.setCirclepadX(-0x9C); + keyboardAnalogX = true; + break; - case SDLK_s: - srv.setCirclepadY(-0x9C); - keyboardAnalogY = true; - break; + case SDLK_s: + srv.setCirclepadY(-0x9C); + keyboardAnalogY = true; + break; - case SDLK_d: - srv.setCirclepadX(0x9C); - keyboardAnalogX = true; - break; + case SDLK_d: + srv.setCirclepadX(0x9C); + keyboardAnalogX = true; + break; - case SDLK_RETURN: srv.pressKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; + case SDLK_RETURN: srv.pressKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; + } } break; case SDL_KEYUP: - switch (event.key.keysym.sym) { - case SDLK_l: srv.releaseKey(Keys::A); break; - case SDLK_k: srv.releaseKey(Keys::B); break; - case SDLK_o: srv.releaseKey(Keys::X); break; - case SDLK_i: srv.releaseKey(Keys::Y); break; + if(romLoaded) { + switch (event.key.keysym.sym) { + case SDLK_l: srv.releaseKey(Keys::A); break; + case SDLK_k: srv.releaseKey(Keys::B); break; + case SDLK_o: srv.releaseKey(Keys::X); break; + case SDLK_i: srv.releaseKey(Keys::Y); break; - case SDLK_q: srv.releaseKey(Keys::L); break; - case SDLK_p: srv.releaseKey(Keys::R); break; + case SDLK_q: srv.releaseKey(Keys::L); break; + case SDLK_p: srv.releaseKey(Keys::R); break; - case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; - case SDLK_LEFT: srv.releaseKey(Keys::Left); break; - case SDLK_UP: srv.releaseKey(Keys::Up); break; - case SDLK_DOWN: srv.releaseKey(Keys::Down); break; + case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; + case SDLK_LEFT: srv.releaseKey(Keys::Left); break; + case SDLK_UP: srv.releaseKey(Keys::Up); break; + case SDLK_DOWN: srv.releaseKey(Keys::Down); break; - // Err this is probably not ideal - case SDLK_w: - case SDLK_s: - srv.setCirclepadY(0); - keyboardAnalogY = false; - break; + // Err this is probably not ideal + case SDLK_w: + case SDLK_s: + srv.setCirclepadY(0); + keyboardAnalogY = false; + break; - case SDLK_a: - case SDLK_d: - srv.setCirclepadX(0); - keyboardAnalogX = false; - break; + case SDLK_a: + case SDLK_d: + srv.setCirclepadX(0); + keyboardAnalogX = false; + break; - case SDLK_RETURN: srv.releaseKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; - } - break; - - case SDL_MOUSEBUTTONDOWN: { - if (event.button.button == SDL_BUTTON_LEFT) { - const s32 x = event.button.x; - const s32 y = event.button.y; - - // Check if touch falls in the touch screen area - if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { - // Convert to 3DS coordinates - u16 x_converted = static_cast(x) - 40; - u16 y_converted = static_cast(y) - 240; - - srv.setTouchScreenPress(x_converted, y_converted); - } else { - srv.releaseTouchScreen(); + case SDLK_RETURN: srv.releaseKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; + } + } + break; + + case SDL_MOUSEBUTTONDOWN: + if(romLoaded) { + if (event.button.button == SDL_BUTTON_LEFT) { + const s32 x = event.button.x; + const s32 y = event.button.y; + + // Check if touch falls in the touch screen area + if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { + // Convert to 3DS coordinates + u16 x_converted = static_cast(x) - 40; + u16 y_converted = static_cast(y) - 240; + + srv.setTouchScreenPress(x_converted, y_converted); + } else { + srv.releaseTouchScreen(); + } + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = true; } - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = true; } break; - } case SDL_MOUSEBUTTONUP: - if (event.button.button == SDL_BUTTON_LEFT) { - srv.releaseTouchScreen(); - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = false; + if(romLoaded) { + if (event.button.button == SDL_BUTTON_LEFT) { + srv.releaseTouchScreen(); + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = false; + } } break; @@ -230,76 +238,90 @@ void Emulator::run() { break; case SDL_CONTROLLERBUTTONUP: - case SDL_CONTROLLERBUTTONDOWN: { - u32 key = 0; + case SDL_CONTROLLERBUTTONDOWN: + if(romLoaded) { + u32 key = 0; - switch (event.cbutton.button) { - case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; - case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; - case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; - case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; - case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; - case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; - case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; - case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; - } + switch (event.cbutton.button) { + case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; + case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; + case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; + case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; + case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; + case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; + } - if (key != 0) { - if (event.cbutton.state == SDL_PRESSED) { - srv.pressKey(key); - } else { - srv.releaseKey(key); + if (key != 0) { + if (event.cbutton.state == SDL_PRESSED) { + srv.pressKey(key); + } else { + srv.releaseKey(key); + } } } - } + break; // Detect mouse motion events for gyroscope emulation - case SDL_MOUSEMOTION: { - // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation - if (!holdingRightClick) break; + case SDL_MOUSEMOTION: + if(romLoaded) { + // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation + if (!holdingRightClick) break; - // Relative motion since last mouse motion event - const s32 motionX = event.motion.xrel; - const s32 motionY = event.motion.yrel; + // Relative motion since last mouse motion event + const s32 motionX = event.motion.xrel; + const s32 motionY = event.motion.yrel; - // The gyroscope involves lots of weird math I don't want to bother with atm - // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion - const s32 roll = motionX > 0 ? 0x7f : -0x7f; - const s32 pitch = motionY > 0 ? 0x7f : -0x7f; - srv.setRoll(roll); - srv.setPitch(pitch); + // The gyroscope involves lots of weird math I don't want to bother with atm + // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion + const s32 roll = motionX > 0 ? 0x7f : -0x7f; + const s32 pitch = motionY > 0 ? 0x7f : -0x7f; + srv.setRoll(roll); + srv.setPitch(pitch); + } break; + + case SDL_DROPFILE: { + char *droppedDir = event.drop.file; + if(droppedDir) { + loadROM(droppedDir); + free(droppedDir); + } } + break; } } - if (gameController != nullptr) { - const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); - const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); - constexpr s16 deadzone = 3276; - constexpr s16 maxValue = 0x9C; - constexpr s16 div = 0x8000 / maxValue; + if(romLoaded) { + if (gameController != nullptr) { + const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); + const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); + constexpr s16 deadzone = 3276; + constexpr s16 maxValue = 0x9C; + constexpr s16 div = 0x8000 / maxValue; - // Avoid overriding the keyboard's circlepad input - if (abs(stickX) < deadzone && !keyboardAnalogX) { - srv.setCirclepadX(0); - } else { - srv.setCirclepadX(stickX / div); - } + // Avoid overriding the keyboard's circlepad input + if (abs(stickX) < deadzone && !keyboardAnalogX) { + srv.setCirclepadX(0); + } else { + srv.setCirclepadX(stickX / div); + } - if (abs(stickY) < deadzone && !keyboardAnalogY) { - srv.setCirclepadY(0); - } else { - srv.setCirclepadY(-(stickY / div)); + if (abs(stickY) < deadzone && !keyboardAnalogY) { + srv.setCirclepadY(0); + } else { + srv.setCirclepadY(-(stickY / div)); + } } + srv.updateInputs(cpu.getTicks()); } // Update inputs in the HID module - srv.updateInputs(cpu.getTicks()); SDL_GL_SwapWindow(window); } } @@ -346,6 +368,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { romType = ROMType::None; } + romLoaded = success; return success; } @@ -390,6 +413,7 @@ bool Emulator::loadELF(std::ifstream& file) { if (entrypoint.value() & 1) { Helpers::panic("Misaligned ELF entrypoint. TODO: Check if ELFs can boot in thumb mode"); } + return true; } diff --git a/src/main.cpp b/src/main.cpp index 9637e83f..e0f803ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,11 +5,13 @@ int main (int argc, char *argv[]) { emu.initGraphicsContext(); - auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "teapot.elf"); - if (!emu.loadROM(romPath)) { - // For some reason just .c_str() doesn't show the proper path - Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); - } + if(argc > 1) { + auto romPath = std::filesystem::current_path() / argv[1]; + if (!emu.loadROM(romPath)) { + // For some reason just .c_str() doesn't show the proper path + Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); + } + } - emu.run(); + emu.run(); } \ No newline at end of file From 340c18b87bfa681efee9417f3ed1782970e48064 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 20:53:51 +0200 Subject: [PATCH 02/10] Oops small mistake --- src/emulator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 3d3678df..67510fea 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -90,6 +90,7 @@ void Emulator::run() { httpServer.startHttpServer(); #endif while (running) { + ServiceManager& srv = kernel.getServiceManager(); if(romLoaded) { #ifdef PANDA3DS_ENABLE_HTTP_SERVER pollHttpServer(); @@ -97,8 +98,6 @@ void Emulator::run() { runFrame(); // Run 1 frame of instructions gpu.display(); // Display graphics - ServiceManager& srv = kernel.getServiceManager(); - // Send VBlank interrupts srv.sendGPUInterrupt(GPUInterrupt::VBlank0); srv.sendGPUInterrupt(GPUInterrupt::VBlank1); From a7a908658ab2fc90478cf2621b28ed610fc68d74 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 21:20:02 +0200 Subject: [PATCH 03/10] Should use SDL_free for drag and drop char* --- src/emulator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 67510fea..4ce93ef1 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -289,7 +289,7 @@ void Emulator::run() { char *droppedDir = event.drop.file; if(droppedDir) { loadROM(droppedDir); - free(droppedDir); + SDL_free(droppedDir); } } break; From 7ad47875b6a8bcba1fa512bfcd9c44e106516e12 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 21:27:18 +0200 Subject: [PATCH 04/10] Should reset the state every time a rom is loaded so the user can keep dragging and dropping roms and it works --- include/emulator.hpp | 1 + src/emulator.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index fca2240f..e90a00a5 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -64,6 +64,7 @@ class Emulator { Emulator(); ~Emulator(); + void stop(); void step(); void render(); void reset(); diff --git a/src/emulator.cpp b/src/emulator.cpp index 4ce93ef1..7bd0a038 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -59,7 +59,7 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory Emulator::~Emulator() { config.save(std::filesystem::current_path() / "config.toml"); } -void Emulator::reset() { +void Emulator::stop() { cpu.reset(); gpu.reset(); memory.reset(); @@ -69,7 +69,9 @@ void Emulator::reset() { // Reloading r13 and r15 needs to happen after everything has been reset // Otherwise resetting the kernel or cpu might nuke them cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP +} +void Emulator::reset() { // If a ROM is active and we reset, reload it. This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again if (romType != ROMType::None && romPath.has_value()) { bool success = loadROM(romPath.value()); @@ -328,6 +330,7 @@ void Emulator::run() { void Emulator::runFrame() { cpu.runFrame(); } bool Emulator::loadROM(const std::filesystem::path& path) { + stop(); // Get path for saving files (AppData on Windows, /home/user/.local/share/ApplcationName on Linux, etc) // Inside that path, we be use a game-specific folder as well. Eg if we were loading a ROM called PenguinDemo.3ds, the savedata would be in // %APPDATA%/Alber/PenguinDemo/SaveData on Windows, and so on. We do this because games save data in their own filesystem on the cart From 49101037b8a8bccf89be1af57e312bd51dad0b4b Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 23:04:07 +0200 Subject: [PATCH 05/10] Should reset this flag in the GSP (needs better name) --- include/services/gsp_gpu.hpp | 1 + src/core/services/gsp_gpu.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index f687532e..214a001e 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -22,6 +22,7 @@ enum class GPUInterrupt : u8 { class Kernel; class GPUService { + bool registerInterruptRelayQueueBeenHere = false; Handle handle = KernelHandles::GPU; Memory& mem; GPU& gpu; diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index f27688a2..e3dea8f6 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -34,6 +34,7 @@ void GPUService::reset() { privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle interruptEvent = std::nullopt; sharedMem = nullptr; + registerInterruptRelayQueueBeenHere = false; } void GPUService::handleSyncRequest(u32 messagePointer) { @@ -77,9 +78,8 @@ void GPUService::acquireRight(u32 messagePointer) { // How does the shared memory handle thing work? void GPUService::registerInterruptRelayQueue(u32 messagePointer) { // Detect if this function is called a 2nd time because we'll likely need to impl threads properly for the GSP - static bool beenHere = false; - if (beenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); - beenHere = true; + if (registerInterruptRelayQueueBeenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); + registerInterruptRelayQueueBeenHere = true; const u32 flags = mem.read32(messagePointer + 4); const u32 eventHandle = mem.read32(messagePointer + 12); From 6ce861624df98e1cf0a27169d7ca2f5928dbb0b0 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 20:48:13 +0200 Subject: [PATCH 06/10] Don't force users to load rom from terminal or by dragging and dropping onto executable file. Instead, open a blank window and use SDL's drag&drop feature --- include/emulator.hpp | 3 +- include/services/gsp_gpu.hpp | 1 + src/core/services/gsp_gpu.cpp | 6 +- src/emulator.cpp | 304 ++++++++++++++++++---------------- src/main.cpp | 14 +- 5 files changed, 179 insertions(+), 149 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index 3985c613..e90a00a5 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -47,7 +47,7 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = true; + bool running = true, romLoaded = false; #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; @@ -64,6 +64,7 @@ class Emulator { Emulator(); ~Emulator(); + void stop(); void step(); void render(); void reset(); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index f687532e..214a001e 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -22,6 +22,7 @@ enum class GPUInterrupt : u8 { class Kernel; class GPUService { + bool registerInterruptRelayQueueBeenHere = false; Handle handle = KernelHandles::GPU; Memory& mem; GPU& gpu; diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index f27688a2..e3dea8f6 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -34,6 +34,7 @@ void GPUService::reset() { privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle interruptEvent = std::nullopt; sharedMem = nullptr; + registerInterruptRelayQueueBeenHere = false; } void GPUService::handleSyncRequest(u32 messagePointer) { @@ -77,9 +78,8 @@ void GPUService::acquireRight(u32 messagePointer) { // How does the shared memory handle thing work? void GPUService::registerInterruptRelayQueue(u32 messagePointer) { // Detect if this function is called a 2nd time because we'll likely need to impl threads properly for the GSP - static bool beenHere = false; - if (beenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); - beenHere = true; + if (registerInterruptRelayQueueBeenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); + registerInterruptRelayQueueBeenHere = true; const u32 flags = mem.read32(messagePointer + 4); const u32 eventHandle = mem.read32(messagePointer + 12); diff --git a/src/emulator.cpp b/src/emulator.cpp index 1e30e073..7bd0a038 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -59,7 +59,7 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory Emulator::~Emulator() { config.save(std::filesystem::current_path() / "config.toml"); } -void Emulator::reset() { +void Emulator::stop() { cpu.reset(); gpu.reset(); memory.reset(); @@ -69,7 +69,9 @@ void Emulator::reset() { // Reloading r13 and r15 needs to happen after everything has been reset // Otherwise resetting the kernel or cpu might nuke them cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP +} +void Emulator::reset() { // If a ROM is active and we reset, reload it. This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again if (romType != ROMType::None && romPath.has_value()) { bool success = loadROM(romPath.value()); @@ -90,18 +92,18 @@ void Emulator::run() { httpServer.startHttpServer(); #endif while (running) { -#ifdef PANDA3DS_ENABLE_HTTP_SERVER - pollHttpServer(); -#endif - runFrame(); // Run 1 frame of instructions - gpu.display(); // Display graphics - ServiceManager& srv = kernel.getServiceManager(); + if(romLoaded) { +#ifdef PANDA3DS_ENABLE_HTTP_SERVER + pollHttpServer(); +#endif + runFrame(); // Run 1 frame of instructions + gpu.display(); // Display graphics - // Send VBlank interrupts - srv.sendGPUInterrupt(GPUInterrupt::VBlank0); - srv.sendGPUInterrupt(GPUInterrupt::VBlank1); - + // Send VBlank interrupts + srv.sendGPUInterrupt(GPUInterrupt::VBlank0); + srv.sendGPUInterrupt(GPUInterrupt::VBlank1); + } SDL_Event event; while (SDL_PollEvent(&event)) { namespace Keys = HID::Keys; @@ -113,104 +115,111 @@ void Emulator::run() { return; case SDL_KEYDOWN: - switch (event.key.keysym.sym) { - case SDLK_l: srv.pressKey(Keys::A); break; - case SDLK_k: srv.pressKey(Keys::B); break; - case SDLK_o: srv.pressKey(Keys::X); break; - case SDLK_i: srv.pressKey(Keys::Y); break; + if(romLoaded) { + switch (event.key.keysym.sym) { + case SDLK_l: srv.pressKey(Keys::A); break; + case SDLK_k: srv.pressKey(Keys::B); break; + case SDLK_o: srv.pressKey(Keys::X); break; + case SDLK_i: srv.pressKey(Keys::Y); break; - case SDLK_q: srv.pressKey(Keys::L); break; - case SDLK_p: srv.pressKey(Keys::R); break; + case SDLK_q: srv.pressKey(Keys::L); break; + case SDLK_p: srv.pressKey(Keys::R); break; - case SDLK_RIGHT: srv.pressKey(Keys::Right); break; - case SDLK_LEFT: srv.pressKey(Keys::Left); break; - case SDLK_UP: srv.pressKey(Keys::Up); break; - case SDLK_DOWN: srv.pressKey(Keys::Down); break; + case SDLK_RIGHT: srv.pressKey(Keys::Right); break; + case SDLK_LEFT: srv.pressKey(Keys::Left); break; + case SDLK_UP: srv.pressKey(Keys::Up); break; + case SDLK_DOWN: srv.pressKey(Keys::Down); break; - case SDLK_w: - srv.setCirclepadY(0x9C); - keyboardAnalogY = true; - break; + case SDLK_w: + srv.setCirclepadY(0x9C); + keyboardAnalogY = true; + break; - case SDLK_a: - srv.setCirclepadX(-0x9C); - keyboardAnalogX = true; - break; + case SDLK_a: + srv.setCirclepadX(-0x9C); + keyboardAnalogX = true; + break; - case SDLK_s: - srv.setCirclepadY(-0x9C); - keyboardAnalogY = true; - break; + case SDLK_s: + srv.setCirclepadY(-0x9C); + keyboardAnalogY = true; + break; - case SDLK_d: - srv.setCirclepadX(0x9C); - keyboardAnalogX = true; - break; + case SDLK_d: + srv.setCirclepadX(0x9C); + keyboardAnalogX = true; + break; - case SDLK_RETURN: srv.pressKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; + case SDLK_RETURN: srv.pressKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; + } } break; case SDL_KEYUP: - switch (event.key.keysym.sym) { - case SDLK_l: srv.releaseKey(Keys::A); break; - case SDLK_k: srv.releaseKey(Keys::B); break; - case SDLK_o: srv.releaseKey(Keys::X); break; - case SDLK_i: srv.releaseKey(Keys::Y); break; + if(romLoaded) { + switch (event.key.keysym.sym) { + case SDLK_l: srv.releaseKey(Keys::A); break; + case SDLK_k: srv.releaseKey(Keys::B); break; + case SDLK_o: srv.releaseKey(Keys::X); break; + case SDLK_i: srv.releaseKey(Keys::Y); break; - case SDLK_q: srv.releaseKey(Keys::L); break; - case SDLK_p: srv.releaseKey(Keys::R); break; + case SDLK_q: srv.releaseKey(Keys::L); break; + case SDLK_p: srv.releaseKey(Keys::R); break; - case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; - case SDLK_LEFT: srv.releaseKey(Keys::Left); break; - case SDLK_UP: srv.releaseKey(Keys::Up); break; - case SDLK_DOWN: srv.releaseKey(Keys::Down); break; + case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; + case SDLK_LEFT: srv.releaseKey(Keys::Left); break; + case SDLK_UP: srv.releaseKey(Keys::Up); break; + case SDLK_DOWN: srv.releaseKey(Keys::Down); break; - // Err this is probably not ideal - case SDLK_w: - case SDLK_s: - srv.setCirclepadY(0); - keyboardAnalogY = false; - break; + // Err this is probably not ideal + case SDLK_w: + case SDLK_s: + srv.setCirclepadY(0); + keyboardAnalogY = false; + break; - case SDLK_a: - case SDLK_d: - srv.setCirclepadX(0); - keyboardAnalogX = false; - break; + case SDLK_a: + case SDLK_d: + srv.setCirclepadX(0); + keyboardAnalogX = false; + break; - case SDLK_RETURN: srv.releaseKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; - } - break; - - case SDL_MOUSEBUTTONDOWN: { - if (event.button.button == SDL_BUTTON_LEFT) { - const s32 x = event.button.x; - const s32 y = event.button.y; - - // Check if touch falls in the touch screen area - if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { - // Convert to 3DS coordinates - u16 x_converted = static_cast(x) - 40; - u16 y_converted = static_cast(y) - 240; - - srv.setTouchScreenPress(x_converted, y_converted); - } else { - srv.releaseTouchScreen(); + case SDLK_RETURN: srv.releaseKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; + } + } + break; + + case SDL_MOUSEBUTTONDOWN: + if(romLoaded) { + if (event.button.button == SDL_BUTTON_LEFT) { + const s32 x = event.button.x; + const s32 y = event.button.y; + + // Check if touch falls in the touch screen area + if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { + // Convert to 3DS coordinates + u16 x_converted = static_cast(x) - 40; + u16 y_converted = static_cast(y) - 240; + + srv.setTouchScreenPress(x_converted, y_converted); + } else { + srv.releaseTouchScreen(); + } + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = true; } - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = true; } break; - } case SDL_MOUSEBUTTONUP: - if (event.button.button == SDL_BUTTON_LEFT) { - srv.releaseTouchScreen(); - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = false; + if(romLoaded) { + if (event.button.button == SDL_BUTTON_LEFT) { + srv.releaseTouchScreen(); + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = false; + } } break; @@ -230,76 +239,90 @@ void Emulator::run() { break; case SDL_CONTROLLERBUTTONUP: - case SDL_CONTROLLERBUTTONDOWN: { - u32 key = 0; + case SDL_CONTROLLERBUTTONDOWN: + if(romLoaded) { + u32 key = 0; - switch (event.cbutton.button) { - case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; - case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; - case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; - case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; - case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; - case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; - case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; - case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; - } + switch (event.cbutton.button) { + case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; + case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; + case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; + case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; + case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; + case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; + } - if (key != 0) { - if (event.cbutton.state == SDL_PRESSED) { - srv.pressKey(key); - } else { - srv.releaseKey(key); + if (key != 0) { + if (event.cbutton.state == SDL_PRESSED) { + srv.pressKey(key); + } else { + srv.releaseKey(key); + } } } - } + break; // Detect mouse motion events for gyroscope emulation - case SDL_MOUSEMOTION: { - // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation - if (!holdingRightClick) break; + case SDL_MOUSEMOTION: + if(romLoaded) { + // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation + if (!holdingRightClick) break; - // Relative motion since last mouse motion event - const s32 motionX = event.motion.xrel; - const s32 motionY = event.motion.yrel; + // Relative motion since last mouse motion event + const s32 motionX = event.motion.xrel; + const s32 motionY = event.motion.yrel; - // The gyroscope involves lots of weird math I don't want to bother with atm - // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion - const s32 roll = motionX > 0 ? 0x7f : -0x7f; - const s32 pitch = motionY > 0 ? 0x7f : -0x7f; - srv.setRoll(roll); - srv.setPitch(pitch); + // The gyroscope involves lots of weird math I don't want to bother with atm + // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion + const s32 roll = motionX > 0 ? 0x7f : -0x7f; + const s32 pitch = motionY > 0 ? 0x7f : -0x7f; + srv.setRoll(roll); + srv.setPitch(pitch); + } break; + + case SDL_DROPFILE: { + char *droppedDir = event.drop.file; + if(droppedDir) { + loadROM(droppedDir); + SDL_free(droppedDir); + } } + break; } } - if (gameController != nullptr) { - const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); - const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); - constexpr s16 deadzone = 3276; - constexpr s16 maxValue = 0x9C; - constexpr s16 div = 0x8000 / maxValue; + if(romLoaded) { + if (gameController != nullptr) { + const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); + const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); + constexpr s16 deadzone = 3276; + constexpr s16 maxValue = 0x9C; + constexpr s16 div = 0x8000 / maxValue; - // Avoid overriding the keyboard's circlepad input - if (abs(stickX) < deadzone && !keyboardAnalogX) { - srv.setCirclepadX(0); - } else { - srv.setCirclepadX(stickX / div); - } + // Avoid overriding the keyboard's circlepad input + if (abs(stickX) < deadzone && !keyboardAnalogX) { + srv.setCirclepadX(0); + } else { + srv.setCirclepadX(stickX / div); + } - if (abs(stickY) < deadzone && !keyboardAnalogY) { - srv.setCirclepadY(0); - } else { - srv.setCirclepadY(-(stickY / div)); + if (abs(stickY) < deadzone && !keyboardAnalogY) { + srv.setCirclepadY(0); + } else { + srv.setCirclepadY(-(stickY / div)); + } } + srv.updateInputs(cpu.getTicks()); } // Update inputs in the HID module - srv.updateInputs(cpu.getTicks()); SDL_GL_SwapWindow(window); } } @@ -307,6 +330,7 @@ void Emulator::run() { void Emulator::runFrame() { cpu.runFrame(); } bool Emulator::loadROM(const std::filesystem::path& path) { + stop(); // Get path for saving files (AppData on Windows, /home/user/.local/share/ApplcationName on Linux, etc) // Inside that path, we be use a game-specific folder as well. Eg if we were loading a ROM called PenguinDemo.3ds, the savedata would be in // %APPDATA%/Alber/PenguinDemo/SaveData on Windows, and so on. We do this because games save data in their own filesystem on the cart @@ -346,6 +370,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { romType = ROMType::None; } + romLoaded = success; return success; } @@ -390,6 +415,7 @@ bool Emulator::loadELF(std::ifstream& file) { if (entrypoint.value() & 1) { Helpers::panic("Misaligned ELF entrypoint. TODO: Check if ELFs can boot in thumb mode"); } + return true; } diff --git a/src/main.cpp b/src/main.cpp index 9637e83f..e0f803ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,11 +5,13 @@ int main (int argc, char *argv[]) { emu.initGraphicsContext(); - auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "teapot.elf"); - if (!emu.loadROM(romPath)) { - // For some reason just .c_str() doesn't show the proper path - Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); - } + if(argc > 1) { + auto romPath = std::filesystem::current_path() / argv[1]; + if (!emu.loadROM(romPath)) { + // For some reason just .c_str() doesn't show the proper path + Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); + } + } - emu.run(); + emu.run(); } \ No newline at end of file From de2751fb5c7aa5e6b7a2d74bccd1bde6b96c59bd Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 11 Jul 2023 02:47:09 +0300 Subject: [PATCH 07/10] Bonk frontend changes --- include/emulator.hpp | 3 +- include/services/gsp_gpu.hpp | 4 +- src/core/services/gsp_gpu.cpp | 8 +- src/emulator.cpp | 270 +++++++++++++++++----------------- src/main.cpp | 4 +- 5 files changed, 149 insertions(+), 140 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index e90a00a5..3985c613 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -47,7 +47,7 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = true, romLoaded = false; + bool running = true; #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; @@ -64,7 +64,6 @@ class Emulator { Emulator(); ~Emulator(); - void stop(); void step(); void render(); void reset(); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 214a001e..7e69253a 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -22,7 +22,6 @@ enum class GPUInterrupt : u8 { class Kernel; class GPUService { - bool registerInterruptRelayQueueBeenHere = false; Handle handle = KernelHandles::GPU; Memory& mem; GPU& gpu; @@ -35,6 +34,9 @@ class GPUService { u32 privilegedProcess; std::optional interruptEvent; + // Number of threads registers via RegisterInterruptRelayQueue + u32 gspThreadCount = 0; + MAKE_LOG_FUNCTION(log, gspGPULogger) void processCommandBuffer(); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index e3dea8f6..5179aec8 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -33,8 +33,8 @@ namespace GXCommands { void GPUService::reset() { privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle interruptEvent = std::nullopt; + gspThreadCount = 0; sharedMem = nullptr; - registerInterruptRelayQueueBeenHere = false; } void GPUService::handleSyncRequest(u32 messagePointer) { @@ -78,8 +78,10 @@ void GPUService::acquireRight(u32 messagePointer) { // How does the shared memory handle thing work? void GPUService::registerInterruptRelayQueue(u32 messagePointer) { // Detect if this function is called a 2nd time because we'll likely need to impl threads properly for the GSP - if (registerInterruptRelayQueueBeenHere) Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); - registerInterruptRelayQueueBeenHere = true; + if (gspThreadCount >= 1) { + Helpers::panic("RegisterInterruptRelayQueue called a second time. Need to implement GSP threads properly"); + } + gspThreadCount += 1; const u32 flags = mem.read32(messagePointer + 4); const u32 eventHandle = mem.read32(messagePointer + 12); diff --git a/src/emulator.cpp b/src/emulator.cpp index 7bd0a038..58ef4c9c 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -53,13 +53,12 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory } config.load(std::filesystem::current_path() / "config.toml"); - reset(); } Emulator::~Emulator() { config.save(std::filesystem::current_path() / "config.toml"); } -void Emulator::stop() { +void Emulator::reset() { cpu.reset(); gpu.reset(); memory.reset(); @@ -69,9 +68,7 @@ void Emulator::stop() { // Reloading r13 and r15 needs to happen after everything has been reset // Otherwise resetting the kernel or cpu might nuke them cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP -} -void Emulator::reset() { // If a ROM is active and we reset, reload it. This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again if (romType != ROMType::None && romPath.has_value()) { bool success = loadROM(romPath.value()); @@ -91,9 +88,11 @@ void Emulator::run() { #ifdef PANDA3DS_ENABLE_HTTP_SERVER httpServer.startHttpServer(); #endif + while (running) { ServiceManager& srv = kernel.getServiceManager(); - if(romLoaded) { + + if (romType != ROMType::None) { #ifdef PANDA3DS_ENABLE_HTTP_SERVER pollHttpServer(); #endif @@ -104,6 +103,7 @@ void Emulator::run() { srv.sendGPUInterrupt(GPUInterrupt::VBlank0); srv.sendGPUInterrupt(GPUInterrupt::VBlank1); } + SDL_Event event; while (SDL_PollEvent(&event)) { namespace Keys = HID::Keys; @@ -115,111 +115,112 @@ void Emulator::run() { return; case SDL_KEYDOWN: - if(romLoaded) { - switch (event.key.keysym.sym) { - case SDLK_l: srv.pressKey(Keys::A); break; - case SDLK_k: srv.pressKey(Keys::B); break; - case SDLK_o: srv.pressKey(Keys::X); break; - case SDLK_i: srv.pressKey(Keys::Y); break; + if (romType == ROMType::None) break; - case SDLK_q: srv.pressKey(Keys::L); break; - case SDLK_p: srv.pressKey(Keys::R); break; + switch (event.key.keysym.sym) { + case SDLK_l: srv.pressKey(Keys::A); break; + case SDLK_k: srv.pressKey(Keys::B); break; + case SDLK_o: srv.pressKey(Keys::X); break; + case SDLK_i: srv.pressKey(Keys::Y); break; - case SDLK_RIGHT: srv.pressKey(Keys::Right); break; - case SDLK_LEFT: srv.pressKey(Keys::Left); break; - case SDLK_UP: srv.pressKey(Keys::Up); break; - case SDLK_DOWN: srv.pressKey(Keys::Down); break; + case SDLK_q: srv.pressKey(Keys::L); break; + case SDLK_p: srv.pressKey(Keys::R); break; - case SDLK_w: - srv.setCirclepadY(0x9C); - keyboardAnalogY = true; - break; + case SDLK_RIGHT: srv.pressKey(Keys::Right); break; + case SDLK_LEFT: srv.pressKey(Keys::Left); break; + case SDLK_UP: srv.pressKey(Keys::Up); break; + case SDLK_DOWN: srv.pressKey(Keys::Down); break; - case SDLK_a: - srv.setCirclepadX(-0x9C); - keyboardAnalogX = true; - break; + case SDLK_w: + srv.setCirclepadY(0x9C); + keyboardAnalogY = true; + break; - case SDLK_s: - srv.setCirclepadY(-0x9C); - keyboardAnalogY = true; - break; + case SDLK_a: + srv.setCirclepadX(-0x9C); + keyboardAnalogX = true; + break; - case SDLK_d: - srv.setCirclepadX(0x9C); - keyboardAnalogX = true; - break; + case SDLK_s: + srv.setCirclepadY(-0x9C); + keyboardAnalogY = true; + break; - case SDLK_RETURN: srv.pressKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; - } + case SDLK_d: + srv.setCirclepadX(0x9C); + keyboardAnalogX = true; + break; + + case SDLK_RETURN: srv.pressKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.pressKey(Keys::Select); break; } break; case SDL_KEYUP: - if(romLoaded) { - switch (event.key.keysym.sym) { - case SDLK_l: srv.releaseKey(Keys::A); break; - case SDLK_k: srv.releaseKey(Keys::B); break; - case SDLK_o: srv.releaseKey(Keys::X); break; - case SDLK_i: srv.releaseKey(Keys::Y); break; + if (romType == ROMType::None) break; - case SDLK_q: srv.releaseKey(Keys::L); break; - case SDLK_p: srv.releaseKey(Keys::R); break; + switch (event.key.keysym.sym) { + case SDLK_l: srv.releaseKey(Keys::A); break; + case SDLK_k: srv.releaseKey(Keys::B); break; + case SDLK_o: srv.releaseKey(Keys::X); break; + case SDLK_i: srv.releaseKey(Keys::Y); break; - case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; - case SDLK_LEFT: srv.releaseKey(Keys::Left); break; - case SDLK_UP: srv.releaseKey(Keys::Up); break; - case SDLK_DOWN: srv.releaseKey(Keys::Down); break; + case SDLK_q: srv.releaseKey(Keys::L); break; + case SDLK_p: srv.releaseKey(Keys::R); break; - // Err this is probably not ideal - case SDLK_w: - case SDLK_s: - srv.setCirclepadY(0); - keyboardAnalogY = false; - break; + case SDLK_RIGHT: srv.releaseKey(Keys::Right); break; + case SDLK_LEFT: srv.releaseKey(Keys::Left); break; + case SDLK_UP: srv.releaseKey(Keys::Up); break; + case SDLK_DOWN: srv.releaseKey(Keys::Down); break; - case SDLK_a: - case SDLK_d: - srv.setCirclepadX(0); - keyboardAnalogX = false; - break; + // Err this is probably not ideal + case SDLK_w: + case SDLK_s: + srv.setCirclepadY(0); + keyboardAnalogY = false; + break; - case SDLK_RETURN: srv.releaseKey(Keys::Start); break; - case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; - } + case SDLK_a: + case SDLK_d: + srv.setCirclepadX(0); + keyboardAnalogX = false; + break; + + case SDLK_RETURN: srv.releaseKey(Keys::Start); break; + case SDLK_BACKSPACE: srv.releaseKey(Keys::Select); break; } break; case SDL_MOUSEBUTTONDOWN: - if(romLoaded) { - if (event.button.button == SDL_BUTTON_LEFT) { - const s32 x = event.button.x; - const s32 y = event.button.y; + if (romType == ROMType::None) break; - // Check if touch falls in the touch screen area - if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { - // Convert to 3DS coordinates - u16 x_converted = static_cast(x) - 40; - u16 y_converted = static_cast(y) - 240; + if (event.button.button == SDL_BUTTON_LEFT) { + const s32 x = event.button.x; + const s32 y = event.button.y; - srv.setTouchScreenPress(x_converted, y_converted); - } else { - srv.releaseTouchScreen(); - } - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = true; + // Check if touch falls in the touch screen area + if (y >= 240 && y <= 480 && x >= 40 && x < 40 + 320) { + // Convert to 3DS coordinates + u16 x_converted = static_cast(x) - 40; + u16 y_converted = static_cast(y) - 240; + + srv.setTouchScreenPress(x_converted, y_converted); + } else { + srv.releaseTouchScreen(); } + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = true; } + break; case SDL_MOUSEBUTTONUP: - if(romLoaded) { - if (event.button.button == SDL_BUTTON_LEFT) { - srv.releaseTouchScreen(); - } else if (event.button.button == SDL_BUTTON_RIGHT) { - holdingRightClick = false; - } + if (romType == ROMType::None) break; + + if (event.button.button == SDL_BUTTON_LEFT) { + srv.releaseTouchScreen(); + } else if (event.button.button == SDL_BUTTON_RIGHT) { + holdingRightClick = false; } break; @@ -239,66 +240,67 @@ void Emulator::run() { break; case SDL_CONTROLLERBUTTONUP: - case SDL_CONTROLLERBUTTONDOWN: - if(romLoaded) { - u32 key = 0; + case SDL_CONTROLLERBUTTONDOWN: { + if (romType == ROMType::None) break; + u32 key = 0; - switch (event.cbutton.button) { - case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; - case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; - case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; - case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; - case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; - case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; - case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; - case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; - case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; - case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; - case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; - case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; - } + switch (event.cbutton.button) { + case SDL_CONTROLLER_BUTTON_A: key = Keys::B; break; + case SDL_CONTROLLER_BUTTON_B: key = Keys::A; break; + case SDL_CONTROLLER_BUTTON_X: key = Keys::Y; break; + case SDL_CONTROLLER_BUTTON_Y: key = Keys::X; break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: key = Keys::L; break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: key = Keys::R; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: key = Keys::Left; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: key = Keys::Right; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: key = Keys::Up; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: key = Keys::Down; break; + case SDL_CONTROLLER_BUTTON_BACK: key = Keys::Select; break; + case SDL_CONTROLLER_BUTTON_START: key = Keys::Start; break; + } - if (key != 0) { - if (event.cbutton.state == SDL_PRESSED) { - srv.pressKey(key); - } else { - srv.releaseKey(key); - } + if (key != 0) { + if (event.cbutton.state == SDL_PRESSED) { + srv.pressKey(key); + } else { + srv.releaseKey(key); } } break; + } // Detect mouse motion events for gyroscope emulation - case SDL_MOUSEMOTION: - if(romLoaded) { - // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation - if (!holdingRightClick) break; + case SDL_MOUSEMOTION: { + // We use right click to indicate we want to rotate the console. If right click is not held, then this is not a gyroscope rotation + if (romType == ROMType::None || !holdingRightClick) break; - // Relative motion since last mouse motion event - const s32 motionX = event.motion.xrel; - const s32 motionY = event.motion.yrel; + // Relative motion since last mouse motion event + const s32 motionX = event.motion.xrel; + const s32 motionY = event.motion.yrel; - // The gyroscope involves lots of weird math I don't want to bother with atm - // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion - const s32 roll = motionX > 0 ? 0x7f : -0x7f; - const s32 pitch = motionY > 0 ? 0x7f : -0x7f; - srv.setRoll(roll); - srv.setPitch(pitch); - } + // The gyroscope involves lots of weird math I don't want to bother with atm + // So up until then, we will set the gyroscope euler angles to fixed values based on the direction of the relative motion + const s32 roll = motionX > 0 ? 0x7f : -0x7f; + const s32 pitch = motionY > 0 ? 0x7f : -0x7f; + srv.setRoll(roll); + srv.setPitch(pitch); break; + } case SDL_DROPFILE: { - char *droppedDir = event.drop.file; - if(droppedDir) { + char* droppedDir = event.drop.file; + + if (droppedDir) { loadROM(droppedDir); SDL_free(droppedDir); } + break; } - break; } } - if(romLoaded) { + // Update controller analog sticks and HID service + if (romType != ROMType::None) { if (gameController != nullptr) { const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); @@ -319,6 +321,7 @@ void Emulator::run() { srv.setCirclepadY(-(stickY / div)); } } + srv.updateInputs(cpu.getTicks()); } @@ -330,7 +333,11 @@ void Emulator::run() { void Emulator::runFrame() { cpu.runFrame(); } bool Emulator::loadROM(const std::filesystem::path& path) { - stop(); + // Reset the emulator if we've already loaded a ROM + if (romType != ROMType::None) { + reset(); + } + // Get path for saving files (AppData on Windows, /home/user/.local/share/ApplcationName on Linux, etc) // Inside that path, we be use a game-specific folder as well. Eg if we were loading a ROM called PenguinDemo.3ds, the savedata would be in // %APPDATA%/Alber/PenguinDemo/SaveData on Windows, and so on. We do this because games save data in their own filesystem on the cart @@ -350,7 +357,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { kernel.initializeFS(); auto extension = path.extension(); - bool success; // Tracks if we loaded the ROM successfully + bool success; // Tracks if we loaded the ROM successfully if (extension == ".elf" || extension == ".axf") success = loadELF(path); @@ -370,7 +377,6 @@ bool Emulator::loadROM(const std::filesystem::path& path) { romType = ROMType::None; } - romLoaded = success; return success; } @@ -421,21 +427,19 @@ bool Emulator::loadELF(std::ifstream& file) { // Reset our graphics context and initialize the GPU's graphics context void Emulator::initGraphicsContext() { - gl.reset(); // TODO (For when we have multiple backends): Only do this if we are using OpenGL + gl.reset(); // TODO (For when we have multiple backends): Only do this if we are using OpenGL gpu.initGraphicsContext(); } #ifdef PANDA3DS_ENABLE_HTTP_SERVER void Emulator::pollHttpServer() { std::scoped_lock lock(httpServer.actionMutex); - + ServiceManager& srv = kernel.getServiceManager(); - + if (httpServer.pendingAction) { switch (httpServer.action) { - case HttpAction::Screenshot: - gpu.screenshot(HttpServer::httpServerScreenshotPath); - break; + case HttpAction::Screenshot: gpu.screenshot(HttpServer::httpServerScreenshotPath); break; case HttpAction::PressKey: if (httpServer.pendingKey != 0) { diff --git a/src/main.cpp b/src/main.cpp index e0f803ef..1559565a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,12 +5,14 @@ int main (int argc, char *argv[]) { emu.initGraphicsContext(); - if(argc > 1) { + if (argc > 1) { auto romPath = std::filesystem::current_path() / argv[1]; if (!emu.loadROM(romPath)) { // For some reason just .c_str() doesn't show the proper path Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str()); } + } else { + printf("No ROM inserted! Load a ROM by dragging and dropping it into the emulator window!\n"); } emu.run(); From 3063efaea16decb49ad330f4ff0dc569c18c19da Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 11 Jul 2023 02:50:40 +0300 Subject: [PATCH 08/10] Revert "Merge branch 'master' of https://github.com/SimoneN64/Panda3DS" This reverts commit 51fb4865ee715819ad6516dd37b248fe932b22c7, reversing changes made to de2751fb5c7aa5e6b7a2d74bccd1bde6b96c59bd. --- include/emulator.hpp | 3 +-- include/services/gsp_gpu.hpp | 1 - src/core/services/gsp_gpu.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index e90a00a5..3985c613 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -47,7 +47,7 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = true, romLoaded = false; + bool running = true; #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; @@ -64,7 +64,6 @@ class Emulator { Emulator(); ~Emulator(); - void stop(); void step(); void render(); void reset(); diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 00734458..7e69253a 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -22,7 +22,6 @@ enum class GPUInterrupt : u8 { class Kernel; class GPUService { - bool registerInterruptRelayQueueBeenHere = false; Handle handle = KernelHandles::GPU; Memory& mem; GPU& gpu; diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index fa18b761..5179aec8 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -35,7 +35,6 @@ void GPUService::reset() { interruptEvent = std::nullopt; gspThreadCount = 0; sharedMem = nullptr; - registerInterruptRelayQueueBeenHere = false; } void GPUService::handleSyncRequest(u32 messagePointer) { From effc9543b32bd1ad6e8f4fd9dbb90dd9e1001247 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 11 Jul 2023 03:08:12 +0300 Subject: [PATCH 09/10] Formatting --- include/services/gsp_gpu.hpp | 2 +- src/emulator.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp index 7e69253a..0757ea2d 100644 --- a/include/services/gsp_gpu.hpp +++ b/include/services/gsp_gpu.hpp @@ -34,7 +34,7 @@ class GPUService { u32 privilegedProcess; std::optional interruptEvent; - // Number of threads registers via RegisterInterruptRelayQueue + // Number of threads registered via RegisterInterruptRelayQueue u32 gspThreadCount = 0; MAKE_LOG_FUNCTION(log, gspGPULogger) diff --git a/src/emulator.cpp b/src/emulator.cpp index 58ef4c9c..c7508681 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -434,7 +434,6 @@ void Emulator::initGraphicsContext() { #ifdef PANDA3DS_ENABLE_HTTP_SERVER void Emulator::pollHttpServer() { std::scoped_lock lock(httpServer.actionMutex); - ServiceManager& srv = kernel.getServiceManager(); if (httpServer.pendingAction) { From b352309290087a719a21547f847c7ea53dd8be23 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:17:36 +0300 Subject: [PATCH 10/10] Introduce 2 methods of resetting the emulator; with and without reload --- include/emulator.hpp | 8 +++++++- src/emulator.cpp | 11 ++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/emulator.hpp b/include/emulator.hpp index 3985c613..83b832f6 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -61,12 +61,18 @@ class Emulator { std::optional romPath = std::nullopt; public: + // Decides whether to reload or not reload the ROM when resetting. We use enum class over a plain bool for clarity. + // If NoReload is selected, the emulator will not reload its selected ROM. This is useful for things like booting up the emulator, or resetting to + // change ROMs. If Reload is selected, the emulator will reload its selected ROM. This is useful for eg a "reset" button that keeps the current ROM + // and just resets the emu + enum class ReloadOption { NoReload, Reload }; + Emulator(); ~Emulator(); void step(); void render(); - void reset(); + void reset(ReloadOption reload); void run(); void runFrame(); diff --git a/src/emulator.cpp b/src/emulator.cpp index c7508681..502e7900 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -53,12 +53,12 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory } config.load(std::filesystem::current_path() / "config.toml"); - reset(); + reset(ReloadOption::NoReload); } Emulator::~Emulator() { config.save(std::filesystem::current_path() / "config.toml"); } -void Emulator::reset() { +void Emulator::reset(ReloadOption reload) { cpu.reset(); gpu.reset(); memory.reset(); @@ -69,8 +69,9 @@ void Emulator::reset() { // Otherwise resetting the kernel or cpu might nuke them cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP - // If a ROM is active and we reset, reload it. This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again - if (romType != ROMType::None && romPath.has_value()) { + // If a ROM is active and we reset, with the reload option enabled then reload it. + // This is necessary to set up stack, executable memory, .data/.rodata/.bss all over again + if (reload == ReloadOption::Reload && romType != ROMType::None && romPath.has_value()) { bool success = loadROM(romPath.value()); if (!success) { romType = ROMType::None; @@ -335,7 +336,7 @@ void Emulator::runFrame() { cpu.runFrame(); } bool Emulator::loadROM(const std::filesystem::path& path) { // Reset the emulator if we've already loaded a ROM if (romType != ROMType::None) { - reset(); + reset(ReloadOption::NoReload); } // Get path for saving files (AppData on Windows, /home/user/.local/share/ApplcationName on Linux, etc)