First pass at continuous state saving in emulation mode, accessible by the debugger:

- Toggle this with Alt-r.  When enabled, state is saved each frame to memory (up to 100 slots)
- Upon entering the debugger, rewind is immediately available, allowing to rewind (for example) back past a breakpoint
- Testing is definitely required.
This commit is contained in:
Stephen Anthony 2017-09-18 20:29:52 -02:30
parent 628f981121
commit aed2945a56
5 changed files with 69 additions and 60 deletions

View File

@ -44,7 +44,7 @@ class RewindManager
@param message Message to display when rewinding to this state @param message Message to display when rewinding to this state
*/ */
bool addState(const string& message = ""); bool addState(const string& message);
/** /**
Rewind one level of the state list, and display the message associated Rewind one level of the state list, and display the message associated

View File

@ -35,16 +35,16 @@
StateManager::StateManager(OSystem& osystem) StateManager::StateManager(OSystem& osystem)
: myOSystem(osystem), : myOSystem(osystem),
myCurrentSlot(0), myCurrentSlot(0),
myActiveMode(kOffMode) myActiveMode(Mode::Off)
{ {
myRewindManager = make_unique<RewindManager>(myOSystem, *this); myRewindManager = make_unique<RewindManager>(myOSystem, *this);
reset(); reset();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::toggleRecordMode()
{
#if 0 #if 0
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::toggleRecordMode()
{
if(myActiveMode != kMovieRecordMode) // Turn on movie record mode if(myActiveMode != kMovieRecordMode) // Turn on movie record mode
{ {
myActiveMode = kOffMode; myActiveMode = kOffMode;
@ -81,15 +81,8 @@ bool StateManager::toggleRecordMode()
} }
return myActiveMode == kMovieRecordMode; return myActiveMode == kMovieRecordMode;
#endif ////////////////////////////////////////////////////////
return false; // FIXME - For now, I'm going to use this to activate movie playback
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::toggleRewindMode()
{
// FIXME - For now, I'm going to use this to activate movie playback
#if 0
// Close the writer, since we're about to re-open in read mode // Close the writer, since we're about to re-open in read mode
myMovieWriter.close(); myMovieWriter.close();
@ -128,34 +121,44 @@ bool StateManager::toggleRewindMode()
myMovieReader.close(); myMovieReader.close();
return false; return false;
} }
}
return myActiveMode == kMoviePlaybackMode;
#endif #endif
return false;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::toggleRewindMode()
{
myActiveMode = myActiveMode == Mode::Rewind ? Mode::Off : Mode::Rewind;
if(myActiveMode == Mode::Rewind)
myOSystem.frameBuffer().showMessage("Continuous rewind enabled");
else
myOSystem.frameBuffer().showMessage("Continuous rewind disabled");
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::update() void StateManager::update()
{ {
#if 0
switch(myActiveMode) switch(myActiveMode)
{ {
case kMovieRecordMode: case Mode::Rewind:
myRewindManager->addState("1 frame");
break;
#if 0
case Mode::MovieRecord:
myOSystem.console().controller(Controller::Left).save(myMovieWriter); myOSystem.console().controller(Controller::Left).save(myMovieWriter);
myOSystem.console().controller(Controller::Right).save(myMovieWriter); myOSystem.console().controller(Controller::Right).save(myMovieWriter);
myOSystem.console().switches().save(myMovieWriter); myOSystem.console().switches().save(myMovieWriter);
break; break;
case kMoviePlaybackMode: case Mode::MoviePlayback:
myOSystem.console().controller(Controller::Left).load(myMovieReader); myOSystem.console().controller(Controller::Left).load(myMovieReader);
myOSystem.console().controller(Controller::Right).load(myMovieReader); myOSystem.console().controller(Controller::Right).load(myMovieReader);
myOSystem.console().switches().load(myMovieReader); myOSystem.console().switches().load(myMovieReader);
break; break;
#endif
default: default:
break; break;
} }
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -33,41 +33,58 @@ class OSystem;
class StateManager class StateManager
{ {
public: public:
enum class Mode {
Off,
Rewind,
MovieRecord,
MoviePlayback
};
/** /**
Create a new statemananger class Create a new statemananger class.
*/ */
StateManager(OSystem& osystem); StateManager(OSystem& osystem);
public: public:
/** /**
Answers whether the manager is in record or playback mode Answers whether the manager is in record or playback mode.
*/ */
bool isActive() const { return myActiveMode != kOffMode; } Mode mode() const { return myActiveMode; }
bool toggleRecordMode(); #if 0
bool toggleRewindMode(); /**
Toggle movie recording mode (FIXME - currently disabled)
*/
void toggleRecordMode();
#endif
/** /**
Updates the state of the system based on the currently active mode Toggle state rewind recording mode; this uses the RewindManager
for its functionality.
*/
void toggleRewindMode();
/**
Updates the state of the system based on the currently active mode.
*/ */
void update(); void update();
/** /**
Load a state into the current system Load a state into the current system.
@param slot The state 'slot' to load state from @param slot The state 'slot' to load state from
*/ */
void loadState(int slot = -1); void loadState(int slot = -1);
/** /**
Save the current state from the system Save the current state from the system.
@param slot The state 'slot' to save into @param slot The state 'slot' to save into
*/ */
void saveState(int slot = -1); void saveState(int slot = -1);
/** /**
Switches to the next higher state slot (circular queue style) Switches to the next higher state slot (circular queue style).
*/ */
void changeState(); void changeState();
@ -92,7 +109,7 @@ class StateManager
bool saveState(Serializer& out); bool saveState(Serializer& out);
/** /**
Resets manager to defaults Resets manager to defaults.
*/ */
void reset(); void reset();
@ -102,14 +119,6 @@ class StateManager
RewindManager& rewindManager() const { return *myRewindManager; } RewindManager& rewindManager() const { return *myRewindManager; }
private: private:
enum Mode {
kOffMode,
kMoviePlaybackMode,
kMovieRecordMode,
kRewindPlaybackMode,
kRewindRecordMode
};
enum { enum {
kVersion = 001 kVersion = 001
}; };

View File

@ -505,12 +505,12 @@ void Debugger::setStartState()
// If rewinding is not enabled, always start the debugger with a clean list // If rewinding is not enabled, always start the debugger with a clean list
RewindManager& r = myOSystem.state().rewindManager(); RewindManager& r = myOSystem.state().rewindManager();
if(0) // FIXME if(myOSystem.state().mode() == StateManager::Mode::Off)
r.clear(); r.clear();
myDialog->rewindButton().setEnabled(!r.empty()); myDialog->rewindButton().setEnabled(!r.empty());
// Save initial state, but don't add it to the rewind list // Save initial state, but don't add it to the rewind list
saveOldState(); // FIXME - rework this saveOldState();
// Set the 're-disassemble' flag, but don't do it until the next scheduled time // Set the 're-disassemble' flag, but don't do it until the next scheduled time
myDialog->rom().invalidate(false); myDialog->rom().invalidate(false);

View File

@ -197,27 +197,20 @@ void EventHandler::poll(uInt64 time)
{ {
myOSystem.console().riot().update(); myOSystem.console().riot().update();
#if 0
// Now check if the StateManager should be saving or loading state // Now check if the StateManager should be saving or loading state
// Per-frame cheats are disabled if the StateManager is active, since // (for rewind and/or movies
// it would interfere with proper playback if(myOSystem.state().mode() != StateManager::Mode::Off)
if(myOSystem.state().isActive())
{
myOSystem.state().update(); myOSystem.state().update();
}
else
#endif
{
#ifdef CHEATCODE_SUPPORT
for(auto& cheat: myOSystem.cheat().perFrame())
cheat->evaluate();
#endif
// Handle continuous snapshots #ifdef CHEATCODE_SUPPORT
if(myContSnapshotInterval > 0 && for(auto& cheat: myOSystem.cheat().perFrame())
(++myContSnapshotCounter % myContSnapshotInterval == 0)) cheat->evaluate();
takeSnapshot(uInt32(time) >> 10); // not quite milliseconds, but close enough #endif
}
// Handle continuous snapshots
if(myContSnapshotInterval > 0 &&
(++myContSnapshotCounter % myContSnapshotInterval == 0))
takeSnapshot(uInt32(time) >> 10); // not quite milliseconds, but close enough
} }
else if(myOverlay) else if(myOverlay)
{ {
@ -441,6 +434,10 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state)
myOSystem.frameBuffer().toggleFrameStats(); myOSystem.frameBuffer().toggleFrameStats();
break; break;
case KBDK_R: // Alt-r toggles continuous store rewind states
myOSystem.state().toggleRewindMode();
break;
case KBDK_S: case KBDK_S:
if(myContSnapshotInterval == 0) if(myContSnapshotInterval == 0)
{ {