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] 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();