diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx new file mode 100644 index 000000000..00c7272cb --- /dev/null +++ b/src/common/RewindManager.cxx @@ -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; +} diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx new file mode 100644 index 000000000..88df9677e --- /dev/null +++ b/src/common/RewindManager.hxx @@ -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 diff --git a/src/emucore/StateManager.cxx b/src/common/StateManager.cxx similarity index 98% rename from src/emucore/StateManager.cxx rename to src/common/StateManager.cxx index 3a7b5ba0e..b22b34ce1 100644 --- a/src/emucore/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -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(myOSystem, *this); reset(); } diff --git a/src/emucore/StateManager.hxx b/src/common/StateManager.hxx similarity index 92% rename from src/emucore/StateManager.hxx rename to src/common/StateManager.hxx index 3e846841b..1256776a6 100644 --- a/src/emucore/StateManager.hxx +++ b/src/common/StateManager.hxx @@ -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 myRewindManager; + private: // Following constructors and assignment operators not supported StateManager() = delete; diff --git a/src/common/module.mk b/src/common/module.mk index 8c6238de5..37690f7dd 100644 --- a/src/common/module.mk +++ b/src/common/module.mk @@ -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 diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 32d018923..21e72f619 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -152,7 +152,6 @@ void Debugger::initialize() myDialog = new DebuggerDialog(myOSystem, *this, 0, 0, myWidth, myHeight); myBaseDialog = myDialog; - myRewindManager = make_unique(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); -} diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx index 049306aec..2a9bda8e5 100644 --- a/src/debugger/Debugger.hxx +++ b/src/debugger/Debugger.hxx @@ -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 myRewindManager; - private: // Following constructors and assignment operators not supported Debugger() = delete; diff --git a/src/emucore/module.mk b/src/emucore/module.mk index 1bd57fd4a..2bcbeaa7c 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -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 \