diff --git a/include/emulator.hpp b/include/emulator.hpp index 8782fb1f..b508338e 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -56,7 +56,8 @@ 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 = false; // Is the emulator running a game? + bool programRunning = false; // Is the emulator program itself running? #ifdef PANDA3DS_ENABLE_HTTP_SERVER HttpServer httpServer; @@ -86,6 +87,10 @@ class Emulator { void run(); void runFrame(); + void resume(); // Resume the emulator + void pause(); // Pause the emulator + void togglePause(); + bool loadROM(const std::filesystem::path& path); bool loadNCSD(const std::filesystem::path& path, ROMType type); bool loadELF(const std::filesystem::path& path); diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 9d3cb818..7c70be14 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -12,7 +12,7 @@ #include "helpers.hpp" -enum class HttpActionType { None, Screenshot, Key }; +enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset }; class Emulator; namespace httplib { @@ -42,6 +42,8 @@ class HttpAction { static std::unique_ptr createScreenshotAction(DeferredResponseWrapper& response); static std::unique_ptr createKeyAction(uint32_t key, bool state); + static std::unique_ptr createTogglePauseAction(); + static std::unique_ptr createResetAction(); }; struct HttpServer { diff --git a/src/emulator.cpp b/src/emulator.cpp index 9d933bd7..abc66a53 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -75,6 +75,8 @@ Emulator::Emulator() } } + running = false; + programRunning = false; reset(ReloadOption::NoReload); } @@ -113,7 +115,9 @@ void Emulator::step() {} void Emulator::render() {} void Emulator::run() { - while (running) { + programRunning = true; + + while (programRunning) { runFrame(); HIDService& hid = kernel.getServiceManager().getHID(); @@ -124,7 +128,7 @@ void Emulator::run() { switch (event.type) { case SDL_QUIT: printf("Bye :(\n"); - running = false; + programRunning = false; return; case SDL_KEYDOWN: @@ -166,6 +170,19 @@ void Emulator::run() { case SDLK_RETURN: hid.pressKey(Keys::Start); break; case SDLK_BACKSPACE: hid.pressKey(Keys::Select); break; + + // Use the F4 button as a hot-key to pause or resume the emulator + // We can't use the audio play/pause buttons because it's annoying + case SDLK_F4: { + togglePause(); + break; + } + + // Use F5 as a reset button + case SDLK_F5: { + reset(ReloadOption::Reload); + break; + } } break; @@ -343,13 +360,18 @@ void Emulator::run() { } } +// Only resume if a ROM is properly loaded +void Emulator::resume() { running = (romType != ROMType::None); } +void Emulator::pause() { running = false; } +void Emulator::togglePause() { running ? pause() : resume(); } + void Emulator::runFrame() { - if (romType != ROMType::None) { + if (running) { #ifdef PANDA3DS_ENABLE_HTTP_SERVER httpServer.processActions(); #endif - cpu.runFrame(); // Run 1 frame of instructions - gpu.display(); // Display graphics + cpu.runFrame(); // Run 1 frame of instructions + gpu.display(); // Display graphics // Send VBlank interrupts ServiceManager& srv = kernel.getServiceManager(); @@ -360,6 +382,10 @@ void Emulator::runFrame() { if (cheats.haveCheats()) [[unlikely]] { cheats.run(); } + } else if (romType != ROMType::None) { + // If the emulator is not running and a game is loaded, we still want to display the framebuffer otherwise we will get weird + // double-buffering issues + gpu.display(); } } @@ -408,6 +434,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { romType = ROMType::None; } + resume(); // Start the emulator return success; } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index d8841807..d3dc7938 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -19,6 +19,16 @@ class HttpActionScreenshot : public HttpAction { DeferredResponseWrapper& getResponse() { return response; } }; +class HttpActionTogglePause : public HttpAction { + public: + HttpActionTogglePause() : HttpAction(HttpActionType::TogglePause) {} +}; + +class HttpActionReset : public HttpAction { + public: + HttpActionReset() : HttpAction(HttpActionType::Reset) {} +}; + class HttpActionKey : public HttpAction { u32 key; bool state; @@ -35,6 +45,8 @@ std::unique_ptr HttpAction::createScreenshotAction(DeferredResponseW } std::unique_ptr HttpAction::createKeyAction(u32 key, bool state) { return std::make_unique(key, state); } +std::unique_ptr HttpAction::createTogglePauseAction() { return std::make_unique(); } +std::unique_ptr HttpAction::createResetAction() { return std::make_unique(); } HttpServer::HttpServer(Emulator* emulator) : emulator(emulator), server(std::make_unique()), keyMap({ @@ -110,6 +122,16 @@ void HttpServer::startHttpServer() { server->Get("/status", [this](const httplib::Request&, httplib::Response& response) { response.set_content(status(), "text/plain"); }); + server->Get("/togglepause", [this](const httplib::Request&, httplib::Response& response) { + pushAction(HttpAction::createTogglePauseAction()); + response.set_content("ok", "text/plain"); + }); + + server->Get("/reset", [this](const httplib::Request&, httplib::Response& response) { + pushAction(HttpAction::createResetAction()); + response.set_content("ok", "text/plain"); + }); + // TODO: ability to specify host and port printf("Starting HTTP server on port 1234\n"); server->listen("localhost", 1234); @@ -165,6 +187,9 @@ void HttpServer::processActions() { break; } + case HttpActionType::TogglePause: emulator->togglePause(); break; + case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); break; + default: break; } }