mirror of https://github.com/stella-emu/stella.git
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:
parent
628f981121
commit
aed2945a56
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue