From 37188edc3b632df972f86e2c4c0898540d064843 Mon Sep 17 00:00:00 2001 From: offtkp Date: Mon, 10 Jul 2023 13:50:14 +0300 Subject: [PATCH 01/22] Add /status command in http server --- include/httpserver.hpp | 14 +++++++ src/httpserver.cpp | 83 ++++++++++++++++++++++++++++-------------- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 0526045a..8e01cce7 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -1,7 +1,9 @@ #ifdef PANDA3DS_ENABLE_HTTP_SERVER #pragma once +#include #include +#include #include #include "helpers.hpp" @@ -16,7 +18,19 @@ struct HttpServer { std::mutex actionMutex = {}; u32 pendingKey = 0; + HttpServer(); + void startHttpServer(); + std::string status(); + +private: + std::map> keyMap; + std::array pressedKeys = {}; + bool paused = false; + + u32 stringToKey(const std::string& key_name); + bool getKeyState(const std::string& key_name); + void setKeyState(const std::string& key_name, bool state); }; #endif // PANDA3DS_ENABLE_HTTP_SERVER \ No newline at end of file diff --git a/src/httpserver.cpp b/src/httpserver.cpp index fa7ad763..6ae7af66 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -2,41 +2,30 @@ #include "httpserver.hpp" #include -#include #include #include #include +#include #include "httplib.h" #include "services/hid.hpp" -u32 stringToKey(const std::string& key_name) { - namespace Keys = HID::Keys; - static std::map keyMap = { - {"A", Keys::A}, - {"B", Keys::B}, - {"Select", Keys::Select}, - {"Start", Keys::Start}, - {"Right", Keys::Right}, - {"Left", Keys::Left}, - {"Up", Keys::Up}, - {"Down", Keys::Down}, - {"R", Keys::R}, - {"L", Keys::L}, - {"X", Keys::X}, - {"Y", Keys::Y}, - {"CirclePadRight", Keys::CirclePadRight}, - {"CirclePadLeft", Keys::CirclePadLeft}, - {"CirclePadUp", Keys::CirclePadUp}, - {"CirclePadDown", Keys::CirclePadDown}, - }; - - if (keyMap.find(key_name) != keyMap.end()) { - return keyMap[key_name]; +HttpServer::HttpServer() : keyMap( + { + {"A", { HID::Keys::A, false } }, + {"B", { HID::Keys::B, false } }, + {"Select", { HID::Keys::Select, false } }, + {"Start", { HID::Keys::Start, false } }, + {"Right", { HID::Keys::Right, false } }, + {"Left", { HID::Keys::Left, false } }, + {"Up", { HID::Keys::Up, false } }, + {"Down", { HID::Keys::Down, false } }, + {"R", { HID::Keys::R, false } }, + {"L", { HID::Keys::L, false } }, + {"X", { HID::Keys::X, false } }, + {"Y", { HID::Keys::Y, false } }, } - - return 0; -} +) {} void HttpServer::startHttpServer() { std::thread http_thread([this]() { @@ -70,8 +59,10 @@ void HttpServer::startHttpServer() { ok = true; if (value == "1") { action = HttpAction::PressKey; + setKeyState(keyStr, true); } else if (value == "0") { action = HttpAction::ReleaseKey; + setKeyState(keyStr, false); } else { // Should not happen but just in case pendingAction = false; @@ -92,6 +83,10 @@ void HttpServer::startHttpServer() { response.set_content("ok", "text/plain"); }); + server.Get("/status", [this](const httplib::Request&, httplib::Response& response) { + response.set_content(status(), "text/plain"); + }); + // TODO: ability to specify host and port printf("Starting HTTP server on port 1234\n"); server.listen("localhost", 1234); @@ -100,4 +95,38 @@ void HttpServer::startHttpServer() { http_thread.detach(); } +std::string HttpServer::status() { + std::stringstream stringStream; + + stringStream << "Panda3DS\n"; + stringStream << "Status: " << (paused ? "Paused" : "Running") << "\n"; + for (auto& [keyStr, value] : keyMap) { + stringStream << keyStr << ": " << value.second << "\n"; + } + + return stringStream.str(); +} + +u32 HttpServer::stringToKey(const std::string& key_name) { + if (keyMap.find(key_name) != keyMap.end()) { + return keyMap[key_name].first; + } + + return 0; +} + +bool HttpServer::getKeyState(const std::string& key_name) { + if (keyMap.find(key_name) != keyMap.end()) { + return keyMap[key_name].second; + } + + return false; +} + +void HttpServer::setKeyState(const std::string& key_name, bool state) { + if (keyMap.find(key_name) != keyMap.end()) { + keyMap[key_name].second = state; + } +} + #endif // PANDA3DS_ENABLE_HTTP_SERVER \ No newline at end of file From 5372fe6e43ee3527f59f7b00fa4df1fa9c082a41 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 10 Jul 2023 10:46:47 -0700 Subject: [PATCH 02/22] config: Use `error_code` prototype of `filesystem::exists` The non-error-code version of these functions are susceptible to throwing an exception in the case of system errors like permission issues or underlying device errors. --- src/config.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 9964c1c7..7dfc2cb5 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -11,7 +11,8 @@ void EmulatorConfig::load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return - if (!std::filesystem::exists(path)) { + std::error_code error; + if (!std::filesystem::exists(path, error)) { save(path); return; } @@ -38,13 +39,19 @@ void EmulatorConfig::load(const std::filesystem::path& path) { void EmulatorConfig::save(const std::filesystem::path& path) { toml::basic_value data; - if (std::filesystem::exists(path)) { + std::error_code error; + if (std::filesystem::exists(path, error)) { try { data = toml::parse(path); } catch (std::exception& ex) { - Helpers::warn("Got exception trying to save config file. Exception: %s\n", ex.what()); + Helpers::warn("Exception trying to parse config file. Exception: %s\n", ex.what()); return; } + } else { + if (error) { + Helpers::warn("FileSystem error accessing %s %s\n", path.string().c_str(), error.message().c_str()); + } + Helpers::warn("Saving new configuration file %s \n", path.string().c_str()); } data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; From 53873c75cc2226d579cd6828edcd93f206866416 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 20:48:13 +0200 Subject: [PATCH 03/22] 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 04/22] 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 e4f3c3beda0e897f6319e66069db1075e0b93b02 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 10 Jul 2023 22:01:49 +0300 Subject: [PATCH 05/22] Bonk config.cpp --- src/config.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 7dfc2cb5..6c9a8450 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -49,9 +49,9 @@ void EmulatorConfig::save(const std::filesystem::path& path) { } } else { if (error) { - Helpers::warn("FileSystem error accessing %s %s\n", path.string().c_str(), error.message().c_str()); + Helpers::warn("Filesystem error accessing %s (error: %s)\n", path.string().c_str(), error.message().c_str()); } - Helpers::warn("Saving new configuration file %s \n", path.string().c_str()); + printf("Saving new configuration file %s\n", path.string().c_str()); } data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; @@ -59,4 +59,4 @@ void EmulatorConfig::save(const std::filesystem::path& path) { std::ofstream file(path, std::ios::out); file << data; file.close(); -} \ No newline at end of file +} From a7a908658ab2fc90478cf2621b28ed610fc68d74 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 21:20:02 +0200 Subject: [PATCH 06/22] 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 07/22] 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 1cb55137166e56a049983afd6874392898aa0ceb Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 10 Jul 2023 12:26:28 -0700 Subject: [PATCH 08/22] Disable compilation of SDL tests Speed up our compilation by about 20 files by removing the need to compile all of SDL's test suite. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f844a61..3b840368 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ add_compile_definitions(SDL_MAIN_HANDLED) set(SDL_STATIC ON CACHE BOOL "" FORCE) set(SDL_SHARED OFF CACHE BOOL "" FORCE) +set(SDL_TEST OFF CACHE BOOL "" FORCE) add_subdirectory(third_party/SDL2) add_subdirectory(third_party/glad) add_subdirectory(third_party/toml11) From 37b75f0928f0d0170538459a78100d7320ef8e78 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 19 Jun 2023 20:10:43 -0700 Subject: [PATCH 09/22] Fix `C4267`/`C4244` warnings Address warnings involving lossy conversions from larger integer-types into smaller integer-types --- include/PICA/gpu.hpp | 2 +- include/memory.hpp | 16 ++--- src/core/fs/archive_ncch.cpp | 8 +-- src/core/fs/archive_self_ncch.cpp | 12 ++-- src/core/kernel/file_operations.cpp | 8 +-- src/core/kernel/threads.cpp | 4 +- src/core/loader/lz77.cpp | 2 +- src/core/memory.cpp | 94 +++++++++++++--------------- src/core/renderer_gl/renderer_gl.cpp | 2 +- src/core/services/dsp.cpp | 4 +- src/core/services/fs.cpp | 8 +-- 11 files changed, 77 insertions(+), 83 deletions(-) diff --git a/include/PICA/gpu.hpp b/include/PICA/gpu.hpp index 2de48c01..a4adc816 100644 --- a/include/PICA/gpu.hpp +++ b/include/PICA/gpu.hpp @@ -26,7 +26,7 @@ class GPU { MAKE_LOG_FUNCTION(log, gpuLogger) static constexpr u32 maxAttribCount = 12; // Up to 12 vertex attributes - static constexpr u32 vramSize = 6_MB; + static constexpr u32 vramSize = u32(6_MB); Registers regs; // GPU internal registers std::array currentAttributes; // Vertex attributes before being passed to the shader diff --git a/include/memory.hpp b/include/memory.hpp index 40349b46..6f33d895 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -110,7 +110,7 @@ class Memory { std::vector memoryInfo; std::array sharedMemBlocks = { - SharedMemoryBlock(0, _shared_font_len, KernelHandles::FontSharedMemHandle), // Shared memory for the system font + SharedMemoryBlock(0, u32(_shared_font_len), KernelHandles::FontSharedMemHandle), // Shared memory for the system font SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory }; @@ -121,14 +121,14 @@ public: static constexpr u32 pageMask = pageSize - 1; static constexpr u32 totalPageCount = 1 << (32 - pageShift); - static constexpr u32 FCRAM_SIZE = 128_MB; - static constexpr u32 FCRAM_APPLICATION_SIZE = 64_MB; + static constexpr u32 FCRAM_SIZE = u32(128_MB); + static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB); static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize; static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize; - static constexpr u32 DSP_RAM_SIZE = 512_KB; - static constexpr u32 DSP_CODE_MEMORY_OFFSET = 0_KB; - static constexpr u32 DSP_DATA_MEMORY_OFFSET = 256_KB; + static constexpr u32 DSP_RAM_SIZE = u32(512_KB); + static constexpr u32 DSP_CODE_MEMORY_OFFSET = u32(0_KB); + static constexpr u32 DSP_DATA_MEMORY_OFFSET = u32(256_KB); private: std::bitset usedFCRAMPages; @@ -141,8 +141,8 @@ private: public: u16 kernelVersion = 0; - u32 usedUserMemory = 0_MB; // How much of the APPLICATION FCRAM range is used (allocated to the appcore) - u32 usedSystemMemory = 0_MB; // Similar for the SYSTEM range (reserved for the syscore) + u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore) + u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore) Memory(u64& cpuTicks); void reset(); diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 3a3330b8..6684ccf4 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -138,8 +138,8 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc switch (type) { case PathType::RomFS: { - const u32 romFSSize = cxi->romFS.size; - const u32 romFSOffset = cxi->romFS.offset; + const u64 romFSSize = cxi->romFS.size; + const u64 romFSOffset = cxi->romFS.offset; if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { Helpers::panic("Tried to read from NCCH with too big of an offset"); } @@ -161,8 +161,8 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size } for (u64 i = 0; i < bytesRead; i++) { - mem.write8(dataPointer + i, data[i]); + mem.write8(u32(dataPointer + i), data[i]); } - return bytesRead; + return u32(bytesRead); } \ No newline at end of file diff --git a/src/core/fs/archive_self_ncch.cpp b/src/core/fs/archive_self_ncch.cpp index fa141b03..9b3aff29 100644 --- a/src/core/fs/archive_self_ncch.cpp +++ b/src/core/fs/archive_self_ncch.cpp @@ -76,8 +76,8 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc switch (type) { case PathType::RomFS: { - const u32 romFSSize = cxi->romFS.size; - const u32 romFSOffset = cxi->romFS.offset; + const u64 romFSSize = cxi->romFS.size; + const u64 romFSOffset = cxi->romFS.offset; if ((offset >> 32) || (offset >= romFSSize) || (offset + size >= romFSSize)) { Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); } @@ -88,8 +88,8 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 } case PathType::ExeFS: { - const u32 exeFSSize = cxi->exeFS.size; - const u32 exeFSOffset = cxi->exeFS.offset; + const u64 exeFSSize = cxi->exeFS.size; + const u64 exeFSOffset = cxi->exeFS.offset; if ((offset >> 32) || (offset >= exeFSSize) || (offset + size >= exeFSSize)) { Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); } @@ -110,8 +110,8 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 } for (u64 i = 0; i < bytesRead; i++) { - mem.write8(dataPointer + i, data[i]); + mem.write8(u32(dataPointer + i), data[i]); } - return bytesRead; + return u32(bytesRead); } \ No newline at end of file diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index 455e9fc2..c7837100 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -97,11 +97,11 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) { } else { for (size_t i = 0; i < bytesRead; i++) { - mem.write8(dataPointer + i, data[i]); + mem.write8(u32(dataPointer + i), data[i]); } mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, bytesRead); + mem.write32(messagePointer + 8, u32(bytesRead)); } return; @@ -142,7 +142,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) { std::unique_ptr data(new u8[size]); for (size_t i = 0; i < size; i++) { - data[i] = mem.read8(dataPointer + i); + data[i] = mem.read8(u32(dataPointer + i)); } IOFile f(file->fd); @@ -153,7 +153,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) { Helpers::panic("Kernel::WriteFile failed"); } else { mem.write32(messagePointer + 4, Result::Success); - mem.write32(messagePointer + 8, bytesWritten); + mem.write32(messagePointer + 8, u32(bytesWritten)); } } diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index c8325031..587d5fc4 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -286,7 +286,7 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) { // Get the index of the event in the object's waitlist, write it to r1 for (size_t i = 0; i < t.waitList.size(); i++) { if (t.waitList[i] == handle) { - t.gprs[1] = i; + t.gprs[1] = u32(i); break; } } @@ -321,7 +321,7 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { // Get the index of the event in the object's waitlist, write it to r1 for (size_t i = 0; i < t.waitList.size(); i++) { if (t.waitList[i] == handle) { - t.gprs[1] = i; + t.gprs[1] = u32(i); break; } } diff --git a/src/core/loader/lz77.cpp b/src/core/loader/lz77.cpp index 254053fd..60021b13 100644 --- a/src/core/loader/lz77.cpp +++ b/src/core/loader/lz77.cpp @@ -12,7 +12,7 @@ u32 CartLZ77::decompressedSize(const u8* buffer, u32 compressedSize) { } bool CartLZ77::decompress(std::vector& output, const std::vector& input) { - u32 sizeCompressed = input.size() * sizeof(u8); + u32 sizeCompressed = u32(input.size() * sizeof(u8)); u32 sizeDecompressed = decompressedSize(input); output.resize(sizeDecompressed); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 00f28eba..1b14baaa 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -1,9 +1,11 @@ #include "memory.hpp" + +#include +#include // For time since epoch +#include + #include "config_mem.hpp" #include "resource_limits.hpp" -#include -#include // For time since epoch -#include using namespace KernelMemoryTypes; @@ -13,15 +15,15 @@ Memory::Memory(u64& cpuTicks) : cpuTicks(cpuTicks) { readTable.resize(totalPageCount, 0); writeTable.resize(totalPageCount, 0); - memoryInfo.reserve(32); // Pre-allocate some room for memory allocation info to avoid dynamic allocs + memoryInfo.reserve(32); // Pre-allocate some room for memory allocation info to avoid dynamic allocs } void Memory::reset() { // Unallocate all memory memoryInfo.clear(); usedFCRAMPages.reset(); - usedUserMemory = 0_MB; - usedSystemMemory = 0_MB; + usedUserMemory = u32(0_MB); + usedSystemMemory = u32(0_MB); for (u32 i = 0; i < totalPageCount; i++) { readTable[i] = 0; @@ -35,7 +37,7 @@ void Memory::reset() { } u32 basePaddrForTLS = tlsBaseOpt.value(); - for (int i = 0; i < appResourceLimits.maxThreads; i++) { + for (u32 i = 0; i < appResourceLimits.maxThreads; i++) { u32 vaddr = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize; allocateMemory(vaddr, basePaddrForTLS, VirtualAddrs::TLSSize, true); basePaddrForTLS += VirtualAddrs::TLSSize; @@ -48,8 +50,8 @@ void Memory::reset() { } // Map DSP RAM as R/W at [0x1FF00000, 0x1FF7FFFF] - constexpr u32 dspRamPages = DSP_RAM_SIZE / pageSize; // Number of DSP RAM pages - constexpr u32 initialPage = VirtualAddrs::DSPMemStart / pageSize; // First page of DSP RAM in the virtual address space + constexpr u32 dspRamPages = DSP_RAM_SIZE / pageSize; // Number of DSP RAM pages + constexpr u32 initialPage = VirtualAddrs::DSPMemStart / pageSize; // First page of DSP RAM in the virtual address space for (u32 i = 0; i < dspRamPages; i++) { auto pointer = uintptr_t(&dspRam[i * pageSize]); @@ -67,7 +69,7 @@ bool Memory::allocateMainThreadStack(u32 size) { } const u32 stackBottom = VirtualAddrs::StackTop - size; - std::optional result = allocateMemory(stackBottom, basePaddr.value(), size, true); // Should never be nullopt + std::optional result = allocateMemory(stackBottom, basePaddr.value(), size, true); // Should never be nullopt return result.has_value(); } @@ -78,18 +80,17 @@ u8 Memory::read8(u32 vaddr) { uintptr_t pointer = readTable[page]; if (pointer != 0) [[likely]] { return *(u8*)(pointer + offset); - } - else { + } else { switch (vaddr) { case ConfigMem::BatteryState: return getBatteryState(true, true, BatteryLevel::FourBars); case ConfigMem::EnvInfo: return envInfo; case ConfigMem::HardwareType: return ConfigMem::HardwareCodes::Product; case ConfigMem::KernelVersionMinor: return u8(kernelVersion & 0xff); case ConfigMem::KernelVersionMajor: return u8(kernelVersion >> 8); - case ConfigMem::LedState3D: return 1; // Report the 3D LED as always off (non-zero) for now - case ConfigMem::NetworkState: return 2; // Report that we've got an internet connection + case ConfigMem::LedState3D: return 1; // Report the 3D LED as always off (non-zero) for now + case ConfigMem::NetworkState: return 2; // Report that we've got an internet connection case ConfigMem::HeadphonesConnectedMaybe: return 0; - case ConfigMem::Unknown1086: return 1; // It's unknown what this is but some games want it to be 1 + case ConfigMem::Unknown1086: return 1; // It's unknown what this is but some games want it to be 1 default: Helpers::panic("Unimplemented 8-bit read, addr: %08X", vaddr); } } @@ -102,8 +103,7 @@ u16 Memory::read16(u32 vaddr) { uintptr_t pointer = readTable[page]; if (pointer != 0) [[likely]] { return *(u16*)(pointer + offset); - } - else { + } else { Helpers::panic("Unimplemented 16-bit read, addr: %08X", vaddr); } } @@ -117,18 +117,20 @@ u32 Memory::read32(u32 vaddr) { return *(u32*)(pointer + offset); } else { switch (vaddr) { - case ConfigMem::Datetime0: return u32(timeSince3DSEpoch()); // ms elapsed since Jan 1 1900, bottom 32 bits - case ConfigMem::Datetime0 + 4: return u32(timeSince3DSEpoch() >> 32); // top 32 bits + case ConfigMem::Datetime0: return u32(timeSince3DSEpoch()); // ms elapsed since Jan 1 1900, bottom 32 bits + case ConfigMem::Datetime0 + 4: + return u32(timeSince3DSEpoch() >> 32); // top 32 bits // Ticks since time was last updated. For now we return the current tick count case ConfigMem::Datetime0 + 8: return u32(cpuTicks); case ConfigMem::Datetime0 + 12: return u32(cpuTicks >> 32); - case ConfigMem::Datetime0 + 16: return 0xFFB0FF0; // Unknown, set by PTM - case ConfigMem::Datetime0 + 20: case ConfigMem::Datetime0 + 24: case ConfigMem::Datetime0 + 28: - return 0; // Set to 0 by PTM + case ConfigMem::Datetime0 + 16: return 0xFFB0FF0; // Unknown, set by PTM + case ConfigMem::Datetime0 + 20: + case ConfigMem::Datetime0 + 24: + case ConfigMem::Datetime0 + 28: return 0; // Set to 0 by PTM case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit; case ConfigMem::SyscoreVer: return 2; - case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does + case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does default: if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { Helpers::warn("VRAM read!\n"); @@ -154,13 +156,12 @@ void Memory::write8(u32 vaddr, u8 value) { uintptr_t pointer = writeTable[page]; if (pointer != 0) [[likely]] { *(u8*)(pointer + offset) = value; - } - else { + } else { // VRAM write if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { // TODO: Invalidate renderer caches here vram[vaddr - VirtualAddrs::VramStart] = value; - } + } else { Helpers::panic("Unimplemented 8-bit write, addr: %08X, val: %02X", vaddr, value); @@ -219,11 +220,10 @@ void* Memory::getWritePointer(u32 address) { std::string Memory::readString(u32 address, u32 maxSize) { std::string string; string.reserve(maxSize); - + for (std::size_t i = 0; i < maxSize; ++i) { char c = read8(address++); - if (c == '\0') - break; + if (c == '\0') break; string.push_back(c); } string.shrink_to_fit(); @@ -233,12 +233,9 @@ std::string Memory::readString(u32 address, u32 maxSize) { // Return a pointer to the linear heap vaddr based on the kernel ver, because it needed to be moved // thanks to the New 3DS having more FCRAM -u32 Memory::getLinearHeapVaddr() { - return (kernelVersion < 0x22C) ? VirtualAddrs::LinearHeapStartOld : VirtualAddrs::LinearHeapStartNew; -} +u32 Memory::getLinearHeapVaddr() { return (kernelVersion < 0x22C) ? VirtualAddrs::LinearHeapStartOld : VirtualAddrs::LinearHeapStartNew; } -std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r, bool w, bool x, - bool adjustAddrs, bool isMap) { +std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r, bool w, bool x, bool adjustAddrs, bool isMap) { // Kernel-allocated memory & size must always be aligned to a page boundary // Additionally assert we don't OoM and that we don't try to allocate physical FCRAM past what's available to userland // If we're mapping there's no fear of OoM, because we're not really allocating memory, just binding vaddrs to specific paddrs @@ -259,8 +256,7 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l // Non-linear allocation needs special handling if (paddr == 0 && adjustAddrs) { std::optional newPaddr = findPaddr(size); - if (!newPaddr.has_value()) - Helpers::panic("Failed to find paddr"); + if (!newPaddr.has_value()) Helpers::panic("Failed to find paddr"); paddr = newPaddr.value(); assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap); @@ -281,12 +277,11 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l } } - if (!isMap) - usedUserMemory += size; + if (!isMap) usedUserMemory += size; // Do linear mapping u32 virtualPage = vaddr >> pageShift; - u32 physPage = paddr >> pageShift; // TODO: Special handle when non-linear mapping is necessary + u32 physPage = paddr >> pageShift; // TODO: Special handle when non-linear mapping is necessary for (u32 i = 0; i < neededPageCount; i++) { if (r) { readTable[virtualPage] = uintptr_t(&fcram[physPage * pageSize]); @@ -320,11 +315,10 @@ std::optional Memory::findPaddr(u32 size) { u32 counter = 0; for (u32 i = 0; i < FCRAM_APPLICATION_PAGE_COUNT; i++) { - if (usedFCRAMPages[i]) { // Page is occupied already, go to new candidate + if (usedFCRAMPages[i]) { // Page is occupied already, go to new candidate candidatePage = i + 1; counter = 0; - } - else { // The paddr we're testing has 1 more free page + } else { // The paddr we're testing has 1 more free page counter++; // Check if there's enough free memory to use this page // We use == instead of >= because some software does 0-byte allocations @@ -351,12 +345,12 @@ u32 Memory::allocateSysMemory(u32 size) { Helpers::panic("Memory::allocateSysMemory: Overflowed OS FCRAM"); } - const u32 pageCount = size / pageSize; // Number of pages that will be used up - const u32 startIndex = sysFCRAMIndex() + usedSystemMemory; // Starting FCRAM index + const u32 pageCount = size / pageSize; // Number of pages that will be used up + const u32 startIndex = sysFCRAMIndex() + usedSystemMemory; // Starting FCRAM index const u32 startingPage = startIndex / pageSize; for (u32 i = 0; i < pageCount; i++) { - if (usedFCRAMPages[startingPage + i]) // Also a theoretically unreachable panic for safety + if (usedFCRAMPages[startingPage + i]) // Also a theoretically unreachable panic for safety Helpers::panic("Memory::reserveMemory: Trying to reserve already reserved memory"); usedFCRAMPages[startingPage + i] = true; } @@ -419,7 +413,7 @@ void Memory::mirrorMapping(u32 destAddress, u32 sourceAddress, u32 size) { // Should theoretically be unreachable, only here for safety purposes assert(isAligned(destAddress) && isAligned(sourceAddress) && isAligned(size)); - const u32 pageCount = size / pageSize; // How many pages we need to mirror + const u32 pageCount = size / pageSize; // How many pages we need to mirror for (u32 i = 0; i < pageCount; i++) { // Redo the shift here to "properly" handle wrapping around the address space instead of reading OoB const u32 sourcePage = sourceAddress / pageSize; @@ -437,16 +431,16 @@ void Memory::mirrorMapping(u32 destAddress, u32 sourceAddress, u32 size) { u64 Memory::timeSince3DSEpoch() { using namespace std::chrono; - std::time_t rawTime = std::time(nullptr); // Get current UTC time - auto localTime = std::localtime(&rawTime); // Convert to local time + std::time_t rawTime = std::time(nullptr); // Get current UTC time + auto localTime = std::localtime(&rawTime); // Convert to local time - bool daylightSavings = localTime->tm_isdst > 0; // Get if time includes DST + bool daylightSavings = localTime->tm_isdst > 0; // Get if time includes DST localTime = std::gmtime(&rawTime); // Use gmtime + mktime to calculate difference between local time and UTC auto timezoneDifference = rawTime - std::mktime(localTime); if (daylightSavings) { - timezoneDifference += 60ull * 60ull; // Add 1 hour (60 seconds * 60 minutes) + timezoneDifference += 60ull * 60ull; // Add 1 hour (60 seconds * 60 minutes) } // seconds between Jan 1 1900 and Jan 1 1970 diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 73fb0919..54d2485a 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -911,7 +911,7 @@ void Renderer::drawVertices(PICA::PrimType primType, std::span ver } vbo.bufferVertsSub(vertices); - OpenGL::draw(primitiveTopology, vertices.size()); + OpenGL::draw(primitiveTopology, GLsizei(vertices.size())); } constexpr u32 topScreenBuffer = 0x1f000000; diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp index 376a08a0..69eb9fb3 100644 --- a/src/core/services/dsp.cpp +++ b/src/core/services/dsp.cpp @@ -143,7 +143,7 @@ std::vector DSPService::readPipe(u32 pipe, u32 size) { } std::vector& data = pipeData[pipe]; - size = std::min(size, data.size()); // Clamp size to the maximum available data size + size = std::min(size, u32(data.size())); // Clamp size to the maximum available data size if (size == 0) return {}; @@ -168,7 +168,7 @@ void DSPService::readPipeIfPossible(u32 messagePointer) { } mem.write32(messagePointer + 4, Result::Success); - mem.write16(messagePointer + 8, data.size()); // Number of bytes read + mem.write16(messagePointer + 8, u16(data.size())); // Number of bytes read } void DSPService::recvData(u32 messagePointer) { diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 2fd3b8e6..7b64b234 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -219,7 +219,7 @@ void FSService::openArchive(u32 messagePointer) { } void FSService::openFile(u32 messagePointer) { - const Handle archiveHandle = mem.read64(messagePointer + 8); + const Handle archiveHandle = Handle(mem.read64(messagePointer + 8)); const u32 filePathType = mem.read32(messagePointer + 16); const u32 filePathSize = mem.read32(messagePointer + 20); const u32 openFlags = mem.read32(messagePointer + 24); @@ -342,7 +342,7 @@ void FSService::openFileDirectly(u32 messagePointer) { } void FSService::createFile(u32 messagePointer) { - const Handle archiveHandle = mem.read64(messagePointer + 8); + const Handle archiveHandle = Handle(mem.read64(messagePointer + 8)); const u32 filePathType = mem.read32(messagePointer + 16); const u32 filePathSize = mem.read32(messagePointer + 20); const u32 attributes = mem.read32(messagePointer + 24); @@ -367,7 +367,7 @@ void FSService::createFile(u32 messagePointer) { } void FSService::deleteFile(u32 messagePointer) { - const Handle archiveHandle = mem.read64(messagePointer + 8); + const Handle archiveHandle = Handle(mem.read64(messagePointer + 8)); const u32 filePathType = mem.read32(messagePointer + 16); const u32 filePathSize = mem.read32(messagePointer + 20); const u32 filePathPointer = mem.read32(messagePointer + 28); @@ -478,7 +478,7 @@ void FSService::formatThisUserSaveData(u32 messagePointer) { } void FSService::controlArchive(u32 messagePointer) { - const Handle archiveHandle = mem.read64(messagePointer + 4); + const Handle archiveHandle = Handle(mem.read64(messagePointer + 4)); const u32 action = mem.read32(messagePointer + 12); const u32 inputSize = mem.read32(messagePointer + 16); const u32 outputSize = mem.read32(messagePointer + 20); From 119c908aa70e305047ad7f9f47e2e1009df6d0e5 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 19 Jun 2023 21:18:51 -0700 Subject: [PATCH 10/22] Add explicit return-type overload for `getBits` Allows the return-type to be specified, allowing a concise final cast after extracting the bit-type. Addresses the remaining `C4244` warnings regarding `getBits`. --- include/helpers.hpp | 13 ++++++++++--- src/core/renderer_gl/etc1.cpp | 32 +++++++++++++++---------------- src/core/renderer_gl/textures.cpp | 20 +++++++++---------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/include/helpers.hpp b/include/helpers.hpp index 9830cc88..4162309e 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -113,9 +113,16 @@ namespace Helpers { } /// Extract bits from an integer-type - template - static constexpr T getBits(T value) { - return (value >> offset) & ones(); + template + static constexpr ReturnT getBits(ValueT value) { + static_assert((offset + bits) <= (CHAR_BIT * sizeof(ValueT)), "Invalid bit range"); + static_assert(bits > 0, "Invalid bit size"); + return ReturnT(ValueT(value >> offset) & ones()); + } + + template + static constexpr ValueT getBits(ValueT value) { + return getBits(value); } #ifdef HELPERS_APPLE_CLANG diff --git a/src/core/renderer_gl/etc1.cpp b/src/core/renderer_gl/etc1.cpp index ae5abb65..82f06724 100644 --- a/src/core/renderer_gl/etc1.cpp +++ b/src/core/renderer_gl/etc1.cpp @@ -60,14 +60,14 @@ u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) { }; // Parse colour data for 4x4 block - const u32 subindices = getBits<0, 16>(colourData); - const u32 negationFlags = getBits<16, 16>(colourData); + const u32 subindices = getBits<0, 16, u32>(colourData); + const u32 negationFlags = getBits<16, 16, u32>(colourData); const bool flip = getBit<32>(colourData); const bool diffMode = getBit<33>(colourData); // Note: index1 is indeed stored on the higher bits, with index2 in the lower bits - const u32 tableIndex1 = getBits<37, 3>(colourData); - const u32 tableIndex2 = getBits<34, 3>(colourData); + const u32 tableIndex1 = getBits<37, 3, u32>(colourData); + const u32 tableIndex2 = getBits<34, 3, u32>(colourData); const u32 texelIndex = u * 4 + v; // Index of the texel in the block if (flip) @@ -75,14 +75,14 @@ u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) { s32 r, g, b; if (diffMode) { - r = getBits<59, 5>(colourData); - g = getBits<51, 5>(colourData); - b = getBits<43, 5>(colourData); + r = getBits<59, 5, s32>(colourData); + g = getBits<51, 5, s32>(colourData); + b = getBits<43, 5, s32>(colourData); if (u >= 2) { - r += signExtend3To32(getBits<56, 3>(colourData)); - g += signExtend3To32(getBits<48, 3>(colourData)); - b += signExtend3To32(getBits<40, 3>(colourData)); + r += signExtend3To32(getBits<56, 3, u32>(colourData)); + g += signExtend3To32(getBits<48, 3, u32>(colourData)); + b += signExtend3To32(getBits<40, 3, u32>(colourData)); } // Expand from 5 to 8 bits per channel @@ -91,13 +91,13 @@ u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) { b = Colour::convert5To8Bit(b); } else { if (u < 2) { - r = getBits<60, 4>(colourData); - g = getBits<52, 4>(colourData); - b = getBits<44, 4>(colourData); + r = getBits<60, 4, s32>(colourData); + g = getBits<52, 4, s32>(colourData); + b = getBits<44, 4, s32>(colourData); } else { - r = getBits<56, 4>(colourData); - g = getBits<48, 4>(colourData); - b = getBits<40, 4>(colourData); + r = getBits<56, 4, s32>(colourData); + g = getBits<48, 4, s32>(colourData); + b = getBits<40, 4, s32>(colourData); } // Expand from 4 to 8 bits per channel diff --git a/src/core/renderer_gl/textures.cpp b/src/core/renderer_gl/textures.cpp index e4df36a0..819bf783 100644 --- a/src/core/renderer_gl/textures.cpp +++ b/src/core/renderer_gl/textures.cpp @@ -119,10 +119,10 @@ u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) { auto ptr = static_cast(data); u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8); - u8 alpha = Colour::convert4To8Bit(getBits<0, 4>(texel)); - u8 b = Colour::convert4To8Bit(getBits<4, 4>(texel)); - u8 g = Colour::convert4To8Bit(getBits<8, 4>(texel)); - u8 r = Colour::convert4To8Bit(getBits<12, 4>(texel)); + u8 alpha = Colour::convert4To8Bit(getBits<0, 4, u8>(texel)); + u8 b = Colour::convert4To8Bit(getBits<4, 4, u8>(texel)); + u8 g = Colour::convert4To8Bit(getBits<8, 4, u8>(texel)); + u8 r = Colour::convert4To8Bit(getBits<12, 4, u8>(texel)); return (alpha << 24) | (b << 16) | (g << 8) | r; } @@ -133,9 +133,9 @@ u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) { u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8); u8 alpha = getBit<0>(texel) ? 0xff : 0; - u8 b = Colour::convert5To8Bit(getBits<1, 5>(texel)); - u8 g = Colour::convert5To8Bit(getBits<6, 5>(texel)); - u8 r = Colour::convert5To8Bit(getBits<11, 5>(texel)); + u8 b = Colour::convert5To8Bit(getBits<1, 5, u8>(texel)); + u8 g = Colour::convert5To8Bit(getBits<6, 5, u8>(texel)); + u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel)); return (alpha << 24) | (b << 16) | (g << 8) | r; } @@ -145,9 +145,9 @@ u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) { auto ptr = static_cast(data); u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8); - u8 b = Colour::convert5To8Bit(getBits<0, 5>(texel)); - u8 g = Colour::convert6To8Bit(getBits<5, 6>(texel)); - u8 r = Colour::convert5To8Bit(getBits<11, 5>(texel)); + u8 b = Colour::convert5To8Bit(getBits<0, 5, u8>(texel)); + u8 g = Colour::convert6To8Bit(getBits<5, 6, u8>(texel)); + u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel)); return (0xff << 24) | (b << 16) | (g << 8) | r; } From d96289cc057d757418d4e6e2d1d3b00067a4ec29 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Mon, 19 Jun 2023 21:23:23 -0700 Subject: [PATCH 11/22] Address remaining `C4244` warnings Remaining warnings regarding an implicit cast from `double` to `float`. --- src/core/renderer_gl/renderer_gl.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 54d2485a..3a13b31d 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -888,8 +888,8 @@ void Renderer::drawVertices(PICA::PrimType primType, std::span ver } // TODO: Actually use this - float viewportWidth = f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0; - float viewportHeight = f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0; + GLsizei viewportWidth = GLsizei(f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0f); + GLsizei viewportHeight = GLsizei(f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0f); OpenGL::setViewport(viewportWidth, viewportHeight); // Note: The code below must execute after we've bound the colour buffer & its framebuffer @@ -929,10 +929,10 @@ void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 cont return; log("GPU: Clear buffer\nStart: %08X End: %08X\nValue: %08X Control: %08X\n", startAddress, endAddress, value, control); - const float r = float(getBits<24, 8>(value)) / 255.0; - const float g = float(getBits<16, 8>(value)) / 255.0; - const float b = float(getBits<8, 8>(value)) / 255.0; - const float a = float(value & 0xff) / 255.0; + const float r = float(getBits<24, 8>(value)) / 255.0f; + const float g = float(getBits<16, 8>(value)) / 255.0f; + const float b = float(getBits<8, 8>(value)) / 255.0f; + const float a = float(value & 0xff) / 255.0f; if (startAddress == topScreenBuffer) { log("GPU: Cleared top screen\n"); From 00f0af87717cb90ae2ee2cb346f254443d9dfe9a Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 10 Jul 2023 22:56:45 +0300 Subject: [PATCH 12/22] Sorry Wunk --- src/core/memory.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 1b14baaa..149b558c 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -223,7 +223,10 @@ std::string Memory::readString(u32 address, u32 maxSize) { for (std::size_t i = 0; i < maxSize; ++i) { char c = read8(address++); - if (c == '\0') break; + if (c == '\0') { + break; + } + string.push_back(c); } string.shrink_to_fit(); @@ -256,7 +259,9 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l // Non-linear allocation needs special handling if (paddr == 0 && adjustAddrs) { std::optional newPaddr = findPaddr(size); - if (!newPaddr.has_value()) Helpers::panic("Failed to find paddr"); + if (!newPaddr.has_value()) { + Helpers::panic("Failed to find paddr"); + } paddr = newPaddr.value(); assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap); @@ -277,7 +282,9 @@ std::optional Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool l } } - if (!isMap) usedUserMemory += size; + if (!isMap) { + usedUserMemory += size; + } // Do linear mapping u32 virtualPage = vaddr >> pageShift; From 49101037b8a8bccf89be1af57e312bd51dad0b4b Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 23:04:07 +0200 Subject: [PATCH 13/22] 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 57423e206f26f4c18554d6d188e098b04e86ea6b Mon Sep 17 00:00:00 2001 From: GPUCode Date: Tue, 11 Jul 2023 01:18:55 +0300 Subject: [PATCH 14/22] Fix typo --- src/core/loader/ncch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index bbc025cc..c1ce1b98 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -112,7 +112,7 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn // It seems like some decryption tools will decrypt the file, without actually setting the NoCrypto flag in the NCCH header // This is a nice and easy hack to see if a file is pretending to be encrypted, taken from 3DMoo and Citra if (u32(programID) == u32(jumpID) && encrypted) { - printf("NCSD is supposedly ecrypted but not actually encrypted\n"); + printf("NCSD is supposedly encrypted but not actually encrypted\n"); encrypted = false; // Cartridge is not actually encrypted, set all of our encryption info structures to nullopt @@ -312,4 +312,4 @@ std::pair NCCH::readFromFile(IOFile& file, const FSInfo &info } return { success, bytes}; -} \ No newline at end of file +} From 6ce861624df98e1cf0a27169d7ca2f5928dbb0b0 Mon Sep 17 00:00:00 2001 From: SimoneN64 Date: Mon, 10 Jul 2023 20:48:13 +0200 Subject: [PATCH 15/22] 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 16/22] 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 17/22] 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 18/22] 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 19/22] 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) From e4e8e7ce26f4dabc9adce3713b8f51b98bb02ba5 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 11 Jul 2023 10:43:17 -0700 Subject: [PATCH 20/22] Move `stb_image_write` into its own translation unit Rather than having the entire implementation within `emulator.cpp`, causing incremental builds to be much slower, give it its own translation unit `stb_image_write.c`. --- CMakeLists.txt | 2 +- src/emulator.cpp | 2 +- src/stb_image_write.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/stb_image_write.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b840368..d276af52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,7 @@ endif() set(SOURCE_FILES src/main.cpp src/emulator.cpp src/io_file.cpp src/gl_state.cpp src/config.cpp src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp src/core/memory.cpp - src/httpserver.cpp + src/httpserver.cpp src/stb_image_write.c ) set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp) set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp diff --git a/src/emulator.cpp b/src/emulator.cpp index 502e7900..0311f6e9 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,5 +1,5 @@ #include "emulator.hpp" -#define STB_IMAGE_WRITE_IMPLEMENTATION + #include #ifdef _WIN32 diff --git a/src/stb_image_write.c b/src/stb_image_write.c new file mode 100644 index 00000000..2f540c3c --- /dev/null +++ b/src/stb_image_write.c @@ -0,0 +1,2 @@ +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include \ No newline at end of file From 7f4746139d63f576c1eaf3e6d99ef4d3294161bb Mon Sep 17 00:00:00 2001 From: Caleb Yates Date: Tue, 11 Jul 2023 14:38:19 -0500 Subject: [PATCH 21/22] Add WIFI MAC to ConfigMem and stub reads --- include/kernel/config_mem.hpp | 12 ++++-------- src/core/memory.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/kernel/config_mem.hpp b/include/kernel/config_mem.hpp index dd4a3fd3..65163a52 100644 --- a/include/kernel/config_mem.hpp +++ b/include/kernel/config_mem.hpp @@ -11,20 +11,16 @@ namespace ConfigMem { AppMemAlloc = 0x1FF80040, HardwareType = 0x1FF81004, Datetime0 = 0x1FF81020, + WifiMac = 0x1FF81060, NetworkState = 0x1FF81067, LedState3D = 0x1FF81084, BatteryState = 0x1FF81085, Unknown1086 = 0x1FF81086, - HeadphonesConnectedMaybe = 0x1FF810C0 // TODO: What is actually stored here? + HeadphonesConnectedMaybe = 0x1FF810C0 // TODO: What is actually stored here? }; // Shows what type of hardware we're running on namespace HardwareCodes { - enum : u8 { - Product = 1, - Devboard = 2, - Debugger = 3, - Capture = 4 - }; + enum : u8 { Product = 1, Devboard = 2, Debugger = 3, Capture = 4 }; } -} \ No newline at end of file +} // namespace ConfigMem diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 149b558c..d3533620 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -104,7 +104,10 @@ u16 Memory::read16(u32 vaddr) { if (pointer != 0) [[likely]] { return *(u16*)(pointer + offset); } else { - Helpers::panic("Unimplemented 16-bit read, addr: %08X", vaddr); + switch (vaddr) { + case ConfigMem::WifiMac + 4: return 0xEEFF; // Wifi MAC: Last 2 bytes of MAC Address + default: Helpers::panic("Unimplemented 16-bit read, addr: %08X", vaddr); + } } } @@ -130,7 +133,8 @@ u32 Memory::read32(u32 vaddr) { case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit; case ConfigMem::SyscoreVer: return 2; - case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does + case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does + case ConfigMem::WifiMac: return 0xFF07F440; // Wifi MAC: First 4 bytes of MAC Address default: if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) { Helpers::warn("VRAM read!\n"); @@ -454,4 +458,4 @@ u64 Memory::timeSince3DSEpoch() { constexpr u64 offset = 2208988800ull; milliseconds ms = duration_cast(seconds(rawTime + timezoneDifference + offset)); return ms.count(); -} \ No newline at end of file +} From e958a360aef0754342cf3beef7862d1989a70814 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 12 Jul 2023 01:27:42 +0300 Subject: [PATCH 22/22] typo --- src/core/kernel/directory_operations.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/kernel/directory_operations.cpp b/src/core/kernel/directory_operations.cpp index fe4f58f4..2d5d7abc 100644 --- a/src/core/kernel/directory_operations.cpp +++ b/src/core/kernel/directory_operations.cpp @@ -21,7 +21,7 @@ void Kernel::closeDirectory(u32 messagePointer, Handle directory) { const auto p = getObject(directory, KernelObjectType::Directory); if (p == nullptr) [[unlikely]] { - Helpers::panic("Called CloseFile on non-existent file"); + Helpers::panic("Called CloseDirectory on non-existent directory"); } p->getData()->isOpen = false; @@ -37,4 +37,4 @@ void Kernel::readDirectory(u32 messagePointer, Handle directory) { mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, 0); -} \ No newline at end of file +}