diff --git a/src/emucore/OSystem.hxx b/src/emucore/OSystem.hxx index f54f59171..5d132920b 100644 --- a/src/emucore/OSystem.hxx +++ b/src/emucore/OSystem.hxx @@ -598,6 +598,9 @@ class OSystem // Indicates whether to stop the main loop bool myQuitLoop{false}; + static constexpr uInt32 FPS_METER_QUEUE_SIZE = 100; + FpsMeter myFpsMeter{FPS_METER_QUEUE_SIZE}; + private: FSNode myBaseDir, myStateDir, mySnapshotSaveDir, mySnapshotLoadDir, myNVRamDir, myCfgDir, myHomeDir, myUserDir, myBezelDir; @@ -607,9 +610,6 @@ class OSystem string myFeatures; string myBuildInfo; - static constexpr uInt32 FPS_METER_QUEUE_SIZE = 100; - FpsMeter myFpsMeter{FPS_METER_QUEUE_SIZE}; - // If not empty, a hint for derived classes to use this as the // base directory (where all settings are stored) // Derived classes are free to ignore it and use their own defaults diff --git a/src/os/rtstella/OSystemRTStella.cxx b/src/os/rtstella/OSystemRTStella.cxx index 0b95f3339..d674a3a64 100644 --- a/src/os/rtstella/OSystemRTStella.cxx +++ b/src/os/rtstella/OSystemRTStella.cxx @@ -5,6 +5,13 @@ #include #include "Logger.hxx" +#include "RTEmulationWorker.hxx" +#include "EventHandler.hxx" +#include "TimerManager.hxx" +#include "DispatchResult.hxx" +#include "Console.hxx" +#include "Debugger.hxx" +#include "TIA.hxx" namespace { void configureScheduler() { @@ -34,3 +41,73 @@ bool OSystemRTStella::initialize(const Settings::Options& options) return OSystemStandalone::initialize(options); } + +void OSystemRTStella::mainLoop() +{ + RTEmulationWorker worker; + DispatchResult dispatchResult; + + for (;;) { + TIA& tia = myConsole->tia(); + + if (worker.isRunning()) worker.suspend(); + + const EventHandlerState oldState = myEventHandler->state(); + const bool workerWasRunning = worker.isRunning(); + + if (oldState == EventHandlerState::EMULATION && worker.isRunning()) { + tia.renderToFrameBuffer(); + + switch (dispatchResult.getStatus()) { + case DispatchResult::Status::ok: + break; + + case DispatchResult::Status::debugger: + #ifdef DEBUGGER_SUPPORT + myDebugger->start( + dispatchResult.getMessage(), + dispatchResult.getAddress(), + dispatchResult.wasReadTrap(), + dispatchResult.getToolTip() + ); + #endif + + break; + + case DispatchResult::Status::fatal: + #ifdef DEBUGGER_SUPPORT + myDebugger->startWithFatalError(dispatchResult.getMessage()); + #else + cerr << dispatchResult.getMessage() << endl; + #endif + break; + + default: + cout << (int)dispatchResult.getStatus() << endl << std::flush; + throw runtime_error("invalid emulation dispatch result"); + } + } + + myEventHandler->poll(TimerManager::getTicks()); + if (myQuitLoop) break; + + if (dispatchResult.getStatus() == DispatchResult::Status::ok && myEventHandler->frying() && worker.isRunning()) + myConsole->fry(); + + const EventHandlerState newState = myEventHandler->state(); + + if (newState == EventHandlerState::EMULATION && !worker.isRunning()) { + worker.start(myConsole->emulationTiming().cyclesPerSecond(), &dispatchResult, &tia); + myFpsMeter.reset(); + } else if (newState != EventHandlerState::EMULATION && worker.isRunning()) { + worker.stop(); + } else if (newState == EventHandlerState::EMULATION && worker.isRunning()) { + worker.resume(); + } + + if (oldState == EventHandlerState::EMULATION && workerWasRunning) + myFrameBuffer->updateInEmulationMode(myFpsMeter.fps()); + else + myFrameBuffer->update(); + } +} diff --git a/src/os/rtstella/OSystemRTStella.hxx b/src/os/rtstella/OSystemRTStella.hxx index 72e1abe80..07b510da0 100644 --- a/src/os/rtstella/OSystemRTStella.hxx +++ b/src/os/rtstella/OSystemRTStella.hxx @@ -23,6 +23,8 @@ class OSystemRTStella: public OSystemStandalone { public: bool initialize(const Settings::Options& options) override; + + void mainLoop() override; }; #endif // OSYSTEM_RTSTELLA_HXX diff --git a/src/os/rtstella/RTEmulationWorker.cxx b/src/os/rtstella/RTEmulationWorker.cxx index 3a6cd22f8..5498b096f 100644 --- a/src/os/rtstella/RTEmulationWorker.cxx +++ b/src/os/rtstella/RTEmulationWorker.cxx @@ -15,12 +15,15 @@ #define TRACE_MSG(m) m " in " STRINGIFY(__FILE__) ":" STRINGIFY(__LINE__) namespace { - constexpr uint64_t TIMESLICE_NANOSECONDS = 100000; - constexpr uint64_t MAX_LAG_NANOSECONDS = 10 * TIMESLICE_NANOSECONDS; + constexpr uint64_t TIMESLICE_NANOSECONDS = 1000000; + constexpr uint64_t MAX_LAG_NANOSECONDS = 100000000; inline Int64 timeDifferenceNanoseconds(const struct timespec& from, const struct timespec& to) { - return (from.tv_sec - to.tv_sec) * 1000000000 + (from.tv_nsec - to.tv_nsec); + uInt64 deltaSec = from.tv_sec - to.tv_sec; + uInt64 deltaNsec = from.tv_nsec - to.tv_nsec; + + return deltaSec * 1000000000 + deltaNsec; } void configureScheduler() { @@ -119,6 +122,8 @@ void RTEmulationWorker::suspend() // the thread may transition to State::exception instead, so make sure that we rethrow while (myState != State::paused) rethrowPendingException(); + + __sync_synchronize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -133,6 +138,8 @@ void RTEmulationWorker::resume() // the thread may transition to State::exception instead, so make sure that we rethrow while (myState != State::running) rethrowPendingException(); + + __sync_synchronize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -207,6 +214,7 @@ void RTEmulationWorker::stop() } } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void RTEmulationWorker::threadMain() { configureScheduler(); @@ -260,6 +268,8 @@ void RTEmulationWorker::dispatchEmulation() clock_gettime(CLOCK_MONOTONIC, &timeOffset); + myDispatchResult->setOk(0); + myState = State::running; myPendingSignal = Signal::none; diff --git a/src/os/rtstella/RTEmulationWorker.hxx b/src/os/rtstella/RTEmulationWorker.hxx index 2c211861c..74533fb2d 100644 --- a/src/os/rtstella/RTEmulationWorker.hxx +++ b/src/os/rtstella/RTEmulationWorker.hxx @@ -64,6 +64,16 @@ class RTEmulationWorker { void stop(); + State state() + { + return myState; + } + + bool isRunning() + { + return myState == State::running || myState == State::paused; + } + private: void threadMain();