Refactor StateManager and RewindManager for eventual use outside debugger.

This commit is contained in:
Stephen Anthony 2017-09-03 23:02:15 -02:30
parent 5ca9b7912b
commit eafe102daa
8 changed files with 200 additions and 128 deletions

View File

@ -0,0 +1,93 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "OSystem.hxx"
#include "Serializer.hxx"
#include "StateManager.hxx"
#include "TIA.hxx"
#include "RewindManager.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
: myOSystem(system),
myStateManager(statemgr),
mySize(0),
myTop(0)
{
for(int i = 0; i < MAX_SIZE; ++i)
myStateList[i] = nullptr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RewindManager::~RewindManager()
{
for(int i = 0; i < MAX_SIZE; ++i)
delete myStateList[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::addState(const string& message)
{
// Create a new Serializer object if we need one
if(myStateList[myTop] == nullptr)
myStateList[myTop] = new Serializer();
Serializer& s = *(myStateList[myTop]);
if(s)
{
s.reset();
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
{
// Are we still within the allowable size, or are we overwriting an item?
mySize++; if(mySize > MAX_SIZE) mySize = MAX_SIZE;
myTop = (myTop + 1) % MAX_SIZE;
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::rewindState()
{
if(mySize > 0)
{
mySize--;
myTop = myTop == 0 ? MAX_SIZE - 1 : myTop - 1;
Serializer& s = *(myStateList[myTop]);
s.reset();
myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s);
return true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RewindManager::clear()
{
for(int i = 0; i < MAX_SIZE; ++i)
if(myStateList[i] != nullptr)
myStateList[i]->reset();
myTop = mySize = 0;
}

View File

@ -0,0 +1,77 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef REWIND_MANAGER_HXX
#define REWIND_MANAGER_HXX
class OSystem;
class StateManager;
/**
This class is used to save (and later 'rewind') system save states.
Essentially, it's a modified circular array-based stack that cleverly deals
with allocation/deallocation of memory.
Since the stack is circular, the oldest states are automatically overwritten
by new ones (up to MAX_SIZE, defined below).
@author Stephen Anthony
*/
class RewindManager
{
public:
RewindManager(OSystem& system, StateManager& statemgr);
virtual ~RewindManager();
public:
/**
Add a new state file with the given message; this message will be
displayed when the state is rewound.
@param message Message to display when rewinding to this state
*/
bool addState(const string& message = "");
/**
Rewind one level of the state list, and display the message associated
with that state.
*/
bool rewindState();
bool empty() const { return mySize == 0; }
void clear();
private:
// Maximum number of states to save
enum { MAX_SIZE = 100 };
OSystem& myOSystem;
StateManager& myStateManager;
Serializer* myStateList[MAX_SIZE];
uInt32 mySize, myTop;
private:
// Following constructors and assignment operators not supported
RewindManager() = delete;
RewindManager(const RewindManager&) = delete;
RewindManager(RewindManager&&) = delete;
RewindManager& operator=(const RewindManager&) = delete;
RewindManager& operator=(RewindManager&&) = delete;
};
#endif

View File

@ -28,7 +28,7 @@
#include "StateManager.hxx"
#define STATE_HEADER "05000200state"
#define STATE_HEADER "05000300state"
#define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -37,6 +37,7 @@ StateManager::StateManager(OSystem& osystem)
myCurrentSlot(0),
myActiveMode(kOffMode)
{
myRewindManager = make_unique<RewindManager>(myOSystem, *this);
reset();
}

View File

@ -20,6 +20,7 @@
class OSystem;
#include "RewindManager.hxx"
#include "Serializer.hxx"
/**
@ -95,6 +96,11 @@ class StateManager
*/
void reset();
/**
The rewind facility for the state manager
*/
RewindManager& rewindManager() const { return *myRewindManager; }
private:
enum Mode {
kOffMode,
@ -124,6 +130,9 @@ class StateManager
Serializer myMovieWriter;
Serializer myMovieReader;
// Stored savestates to be later rewound
unique_ptr<RewindManager> myRewindManager;
private:
// Following constructors and assignment operators not supported
StateManager() = delete;

View File

@ -10,10 +10,12 @@ MODULE_OBJS := \
src/common/FSNodeZIP.o \
src/common/PNGLibrary.o \
src/common/MouseControl.o \
src/common/RewindManager.o \
src/common/StateManager.o \
src/common/ZipHandler.o
MODULE_DIRS += \
src/common
# Include common rules
# Include common rules
include $(srcdir)/common.rules

View File

@ -152,7 +152,6 @@ void Debugger::initialize()
myDialog = new DebuggerDialog(myOSystem, *this, 0, 0, myWidth, myHeight);
myBaseDialog = myDialog;
myRewindManager = make_unique<RewindManager>(myOSystem, myDialog->rewindButton());
myCartDebug->setDebugWidget(&(myDialog->cartDebug()));
}
@ -433,12 +432,16 @@ void Debugger::nextFrame(int frames)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::rewindState()
{
RewindManager& r = myOSystem.state().rewindManager();
mySystem.clearDirtyPages();
unlockBankswitchState();
bool result = myRewindManager->rewindState();
bool result = r.rewindState();
lockBankswitchState();
myDialog->rewindButton().setEnabled(!r.empty());
return result;
}
@ -476,7 +479,12 @@ void Debugger::saveOldState(bool addrewind)
myTiaDebug->saveOldState();
// Add another rewind level to the Undo list
if(addrewind) myRewindManager->addState();
if(addrewind)
{
RewindManager& r = myOSystem.state().rewindManager();
r.addState();
myDialog->rewindButton().setEnabled(!r.empty());
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -485,8 +493,11 @@ void Debugger::setStartState()
// Lock the bus each time the debugger is entered, so we don't disturb anything
lockBankswitchState();
// Start a new rewind list
myRewindManager->clear();
// If rewinding is not enabled, always start the debugger with a clean list
RewindManager& r = myOSystem.state().rewindManager();
if(0) // FIXME
r.clear();
myDialog->rewindButton().setEnabled(!r.empty());
// Save initial state, but don't add it to the rewind list
saveOldState(false);
@ -634,90 +645,3 @@ void Debugger::unlockBankswitchState()
mySystem.unlockDataBus();
myConsole.cartridge().unlockBank();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::RewindManager::RewindManager(OSystem& system, ButtonWidget& button)
: myOSystem(system),
myRewindButton(button),
mySize(0),
myTop(0)
{
for(int i = 0; i < MAX_SIZE; ++i)
myStateList[i] = nullptr;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::RewindManager::~RewindManager()
{
for(int i = 0; i < MAX_SIZE; ++i)
delete myStateList[i];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::addState()
{
// Create a new Serializer object if we need one
if(myStateList[myTop] == nullptr)
myStateList[myTop] = new Serializer();
Serializer& s = *(myStateList[myTop]);
if(s)
{
s.reset();
if(myOSystem.state().saveState(s) && myOSystem.console().tia().saveDisplay(s))
{
// Are we still within the allowable size, or are we overwriting an item?
mySize++; if(mySize > MAX_SIZE) mySize = MAX_SIZE;
myTop = (myTop + 1) % MAX_SIZE;
myRewindButton.setEnabled(true);
return true;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::rewindState()
{
if(mySize > 0)
{
mySize--;
myTop = myTop == 0 ? MAX_SIZE - 1 : myTop - 1;
Serializer& s = *(myStateList[myTop]);
s.reset();
myOSystem.state().loadState(s);
myOSystem.console().tia().loadDisplay(s);
if(mySize == 0)
myRewindButton.setEnabled(false);
return true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Debugger::RewindManager::empty()
{
return mySize == 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::RewindManager::clear()
{
for(int i = 0; i < MAX_SIZE; ++i)
if(myStateList[i] != nullptr)
myStateList[i]->reset();
myTop = mySize = 0;
// We use Widget::clearFlags here instead of Widget::setEnabled(),
// since the latter implies an immediate draw/update, but this method
// might be called before any UI exists
// TODO - fix this deficiency in the UI core; we shouldn't have to worry
// about such things at this level
myRewindButton.clearFlags(WIDGET_ENABLED);
}

View File

@ -26,7 +26,6 @@ class TiaZoomWidget;
class EditTextWidget;
class RomWidget;
class Expression;
class Serializer;
class PackedBitArray;
class PromptWidget;
class ButtonWidget;
@ -295,38 +294,6 @@ class Debugger : public DialogContainer
uInt32 myWidth;
uInt32 myHeight;
// Class holding all rewind state functionality in the debugger
// Essentially, it's a modified circular array-based stack
// that cleverly deals with allocation/deallocation of memory
class RewindManager
{
public:
RewindManager(OSystem& system, ButtonWidget& button);
virtual ~RewindManager();
public:
bool addState();
bool rewindState();
bool empty();
void clear();
private:
enum { MAX_SIZE = 100 };
OSystem& myOSystem;
ButtonWidget& myRewindButton;
Serializer* myStateList[MAX_SIZE];
uInt32 mySize, myTop;
private:
// Following constructors and assignment operators not supported
RewindManager() = delete;
RewindManager(const RewindManager&) = delete;
RewindManager(RewindManager&&) = delete;
RewindManager& operator=(const RewindManager&) = delete;
RewindManager& operator=(RewindManager&&) = delete;
};
unique_ptr<RewindManager> myRewindManager;
private:
// Following constructors and assignment operators not supported
Debugger() = delete;

View File

@ -73,7 +73,6 @@ MODULE_OBJS := \
src/emucore/Serializer.o \
src/emucore/Settings.o \
src/emucore/Switches.o \
src/emucore/StateManager.o \
src/emucore/System.o \
src/emucore/TIASnd.o \
src/emucore/TIASurface.o \