diff --git a/docs/debugger.html b/docs/debugger.html index 2e808cc45..332843089 100644 --- a/docs/debugger.html +++ b/docs/debugger.html @@ -316,9 +316,11 @@ used.

The larger button at the left top (labeled '<') performs the rewind operation, -which will undo the previous Step/Trace/Scan/Frame advance, the smaller button at +which will undo the previous Step/Trace/Scan/Frame... advance, the smaller button at the left bottom (labeled '>') performs the unwind operation, which will undo the -previous rewind operation. The rewind buffer is 100 levels deep by default.

+previous rewind operation. The rewind buffer is 100 levels deep by default, the +size can be configured e.g. in the +Developer Settings - States dialog.

The other operations are Step, Trace, Scan+1, Frame+1 and Exit (debugger).

@@ -347,11 +349,27 @@ previous rewind operation. The rewind buffer is 100 levels deep by default.

Control-r - Rewind + Rewind 1 - Shift-Control-r - Unwind + Control-Shift-r + Rewind 10 + + + Control-Alt-r + Rewind all + + + Control-y + Unwind 1 + + + Control-Shift-y + Unwind 10 + + + Control-Alt-y + Unwind all Backquote (`) diff --git a/docs/index.html b/docs/index.html index 8bb086b80..4c8ac198a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1622,11 +1622,31 @@ Alt + Left arrow Cmd + Left arrow + + Rewind by 10 states (pauses emulation) + Shift-Alt + Left arrow + Shift-Cmd + Left arrow + + + Rewind all states (pauses emulation) + Alt + Down arrow + Cmd + Down arrow + Unwind by one state (pauses emulation) Alt + Right arrow Cmd + Right arrow + + Unwind by 10 states (pauses emulation) + Shift-Alt + Right arrow + Shift-Cmd + Right arrow + + + Unwind all states (pauses emulation) + Alt + Up arrow + Cmd + Up arrow +

UI keys in Text Editing areas (cannot be remapped)

diff --git a/src/common/LinkedObjectPool.hxx b/src/common/LinkedObjectPool.hxx index d3484bc26..46205c6f1 100644 --- a/src/common/LinkedObjectPool.hxx +++ b/src/common/LinkedObjectPool.hxx @@ -175,26 +175,22 @@ class LinkedObjectPool moveToPrevious(); // if so, move to the previous node } -#if 0 /** - Convenience method to remove a single element from the active list at - position of the iterator +- the offset. + Remove a single element from the active list at position of the iterator. */ - void remove(const_iter i, Int32 offset = 0) { - myPool.splice(myPool.end(), myList, - offset >= 0 ? std::next(i, offset) : std::prev(i, -offset)); + void remove(const_iter i) { + myPool.splice(myPool.end(), myList, i); } -#endif + /** - Convenience method to remove a single element from the active list by - index, offset from the beginning of the list. (ie, '0' means first - element, '1' is second, and so on). + Remove a single element from the active list by index, offset from + the beginning of the list. (ie, '0' means first element, '1' is second, + and so on). */ void remove(uInt32 index) { myPool.splice(myPool.end(), myList, std::next(myList.begin(), index)); } - /** Remove range of elements from the beginning of the active list to the 'current' node. diff --git a/src/common/RewindManager.cxx b/src/common/RewindManager.cxx index f2314645c..7189e05c8 100644 --- a/src/common/RewindManager.cxx +++ b/src/common/RewindManager.cxx @@ -130,59 +130,90 @@ bool RewindManager::addState(const string& message, bool continuous) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool RewindManager::rewindState() +uInt32 RewindManager::rewindState(uInt32 numStates) { - if(!atFirst()) + uInt64 startCycles = myOSystem.console().tia().cycles(); + uInt32 i; + string message; + + for(i = 0; i < numStates; ++i) { - if (!myLastContinuousAdd) - // Set internal current iterator to previous state (back in time), - // since we will now processed this state - myStateList.moveToPrevious(); - myLastContinuousAdd = false; + if(!atFirst()) + { + if(!myLastContinuousAdd) + // Set internal current iterator to previous state (back in time), + // since we will now processed this state + myStateList.moveToPrevious(); + myLastContinuousAdd = false; - //RewindState& lastState = myStateList.current(); + RewindState& state = myStateList.current(); + Serializer& s = state.data; +//cerr << "rewind " << state.count << endl; + s.rewind(); // rewind Serializer internal buffers + } + else + break; + } + if(i) + { RewindState& state = myStateList.current(); Serializer& s = state.data; - string message = getMessage(state); -//cerr << "rewind " << state.count << endl; - s.rewind(); // rewind Serializer internal buffers myStateManager.loadState(s); myOSystem.console().tia().loadDisplay(s); - // Show message indicating the rewind state - myOSystem.frameBuffer().showMessage(message); - return true; + // Get message indicating the rewind state + message = getMessage(startCycles, i); } - myOSystem.frameBuffer().showMessage("Rewind not possible"); - return false; + else + message = "Rewind not possible"; + + myOSystem.frameBuffer().showMessage(message); + return i; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool RewindManager::unwindState() +uInt32 RewindManager::unwindState(uInt32 numStates) { - if(!atLast()) - { - // Set internal current iterator to nextCycles state (forward in time), - // since we've now processed this state - myStateList.moveToNext(); + uInt64 startCycles = myOSystem.console().tia().cycles(); + uInt32 i; + string message; + for(i = 0; i < numStates; ++i) + { + if(!atLast()) + { + // Set internal current iterator to nextCycles state (forward in time), + // since we've now processed this state + myStateList.moveToNext(); + + RewindState& state = myStateList.current(); + Serializer& s = state.data; +//cerr << "unwind " << state.count << endl; + s.rewind(); // rewind Serializer internal buffers + } + else + break; + } + + if(i) + { RewindState& state = myStateList.current(); Serializer& s = state.data; - string message = getMessage(state); -//cerr << "unwind " << state.count << endl; - s.rewind(); // rewind Serializer internal buffers myStateManager.loadState(s); myOSystem.console().tia().loadDisplay(s); - // Show message indicating the rewind state + // Get message indicating the rewind state + message = getMessage(startCycles, i); myOSystem.frameBuffer().showMessage(message); - return true; } - myOSystem.frameBuffer().showMessage("Unwind not possible"); - return false; + else + message = "Unwind not possible"; + + myOSystem.frameBuffer().showMessage(message); + return i; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -191,8 +222,8 @@ void RewindManager::compressStates() uInt64 currentCycles = myOSystem.console().tia().cycles(); double expectedCycles = myInterval * myFactor * (1 + myFactor); double maxError = 1; - uInt32 removeIdx = 0; uInt32 idx = myStateList.size() - 2; + Common::LinkedObjectPool::const_iter removeIter = myStateList.first(); //cerr << "idx: " << idx << endl; // iterate from last but one to first but one @@ -211,7 +242,7 @@ void RewindManager::compressStates() if(error > maxError) { maxError = error; - removeIdx = idx; + removeIter = it; } } --idx; @@ -224,22 +255,23 @@ void RewindManager::compressStates() } else { - myStateList.remove(removeIdx); // remove + myStateList.remove(removeIter); // remove //cerr << "remove " << removeIdx << endl; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string RewindManager::getMessage(RewindState& state) +string RewindManager::getMessage(Int64 startCycles, uInt32 numStates) { - Int64 diff = myOSystem.console().tia().cycles() - state.cycles; + RewindState& state = myStateList.current(); + Int64 diff = startCycles - state.cycles; stringstream message; message << (diff >= 0 ? "Rewind" : "Unwind") << " " << getUnitString(diff); - message << " [" << myStateList.currentIdx() << "/" << myStateList.size() << "]"; + // add optional message - if(!state.message.empty()) + if(numStates == 1 && !state.message.empty()) message << " (" << state.message << ")"; return message.str(); diff --git a/src/common/RewindManager.hxx b/src/common/RewindManager.hxx index f4e67debb..49c0bd1c4 100644 --- a/src/common/RewindManager.hxx +++ b/src/common/RewindManager.hxx @@ -99,6 +99,7 @@ class RewindManager }; /** + Initializes state list and calculates compression factor. */ void setup(); @@ -113,14 +114,20 @@ class RewindManager /** Rewind one level of the state list, and display the message associated with that state. + + @param numStates Number of states to rewind + @return Number of states to rewinded */ - bool rewindState(); + uInt32 rewindState(uInt32 numStates = 1); /** Unwind one level of the state list, and display the message associated with that state. + + @param numStates Number of states to unwind + @return Number of states to unwinded */ - bool unwindState(); + uInt32 unwindState(uInt32 numStates = 1); bool atFirst() const { return myStateList.atFirst(); } bool atLast() const { return myStateList.atLast(); } @@ -167,9 +174,17 @@ class RewindManager // frequent (de)-allocations) Common::LinkedObjectPool myStateList; + /** + Remove a save state from the list + */ void compressStates(); - string getMessage(RewindState& state); + /** + Get the message string for the rewind/unwind + + @return The message + */ + string getMessage(Int64 startCycles, uInt32 numStates); private: // Following constructors and assignment operators not supported diff --git a/src/common/StateManager.cxx b/src/common/StateManager.cxx index d31c56aaf..2ffcbbec6 100644 --- a/src/common/StateManager.cxx +++ b/src/common/StateManager.cxx @@ -142,17 +142,17 @@ void StateManager::toggleRewindMode() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool StateManager::rewindState() +bool StateManager::rewindState(uInt32 numStates) { RewindManager& r = myOSystem.state().rewindManager(); - return r.rewindState(); + return r.rewindState(numStates); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool StateManager::unwindState() +bool StateManager::unwindState(uInt32 numStates) { RewindManager& r = myOSystem.state().rewindManager(); - return r.unwindState(); + return r.unwindState(numStates); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/common/StateManager.hxx b/src/common/StateManager.hxx index 5e7b83c10..910b0e7a9 100644 --- a/src/common/StateManager.hxx +++ b/src/common/StateManager.hxx @@ -74,12 +74,12 @@ class StateManager /** Rewinds one state; this uses the RewindManager for its functionality. */ - bool rewindState(); + bool rewindState(uInt32 numStates = 1); /** Unwinds one state; this uses the RewindManager for its functionality. */ - bool unwindState(); + bool unwindState(uInt32 numStates = 1); /** Updates the state of the system based on the currently active mode. diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 38f462bbe..08a2c251f 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -526,7 +526,7 @@ void Debugger::updateRewindbuttons(const RewindManager& r) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Debugger::windStates(uInt16 states, bool unwind, string& message) +uInt16 Debugger::windStates(uInt16 numStates, bool unwind, string& message) { RewindManager& r = myOSystem.state().rewindManager(); @@ -534,13 +534,8 @@ uInt16 Debugger::windStates(uInt16 states, bool unwind, string& message) unlockBankswitchState(); - uInt16 winds = 0; uInt64 startCycles = myOSystem.console().tia().cycles(); - for(uInt16 i = 0; i < states; ++i) - if(unwind ? r.unwindState() : r.rewindState()) - winds++; - else - break; + uInt16 winds = unwind ? r.unwindState(numStates) : r.rewindState(numStates); message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles); lockBankswitchState(); @@ -550,15 +545,15 @@ uInt16 Debugger::windStates(uInt16 states, bool unwind, string& message) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Debugger::rewindStates(const uInt16 states, string& message) +uInt16 Debugger::rewindStates(const uInt16 numStates, string& message) { - return windStates(states, false, message); + return windStates(numStates, false, message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Debugger::unwindStates(const uInt16 states, string& message) +uInt16 Debugger::unwindStates(const uInt16 numStates, string& message) { - return windStates(states, true, message); + return windStates(numStates, true, message); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx index 01ba1f890..f3fdcd30c 100644 --- a/src/debugger/Debugger.hxx +++ b/src/debugger/Debugger.hxx @@ -262,8 +262,8 @@ class Debugger : public DialogContainer int trace(); void nextScanline(int lines); void nextFrame(int frames); - uInt16 rewindStates(const uInt16 states, string& message); - uInt16 unwindStates(const uInt16 states, string& message); + uInt16 rewindStates(const uInt16 numStates, string& message); + uInt16 unwindStates(const uInt16 numStates, string& message); void toggleBreakPoint(uInt16 bp); @@ -321,7 +321,7 @@ class Debugger : public DialogContainer private: // rewind/unwind n states - uInt16 windStates(uInt16 states, bool unwind, string& message); + uInt16 windStates(uInt16 numStates, bool unwind, string& message); // update the rewind/unwind button state void updateRewindbuttons(const RewindManager& r); diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index 58837e270..9f90d3570 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -2177,7 +2177,6 @@ void DebuggerParser::executeWatch() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // wrapper function for rewind/unwind commands -// TODO: return and output (formatted) cycles void DebuggerParser::executeWinds(bool unwind) { uInt16 states; diff --git a/src/debugger/gui/DebuggerDialog.cxx b/src/debugger/gui/DebuggerDialog.cxx index 8f30cfc06..8d6a07d95 100644 --- a/src/debugger/gui/DebuggerDialog.cxx +++ b/src/debugger/gui/DebuggerDialog.cxx @@ -97,8 +97,18 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod) switch(key) { case KBDK_R: - if(!instance().eventHandler().kbdShift(mod)) + if(instance().eventHandler().kbdAlt(mod)) + doRewindAll(); + else if(instance().eventHandler().kbdShift(mod)) + doRewind10(); + else doRewind(); + break; + case KBDK_Y: + if(instance().eventHandler().kbdAlt(mod)) + doUnwindAll(); + else if(instance().eventHandler().kbdShift(mod)) + doUnwind10(); else doUnwind(); break; @@ -212,6 +222,30 @@ void DebuggerDialog::doUnwind() instance().debugger().parser().run("unwind"); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DebuggerDialog::doRewind10() +{ + instance().debugger().parser().run("rewind #10"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DebuggerDialog::doUnwind10() +{ + instance().debugger().parser().run("unwind #10"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DebuggerDialog::doRewindAll() +{ + instance().debugger().parser().run("rewind #1000"); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void DebuggerDialog::doUnwindAll() +{ + instance().debugger().parser().run("unwind #1000"); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void DebuggerDialog::doExitDebugger() { diff --git a/src/debugger/gui/DebuggerDialog.hxx b/src/debugger/gui/DebuggerDialog.hxx index 0442870e1..996f198dc 100644 --- a/src/debugger/gui/DebuggerDialog.hxx +++ b/src/debugger/gui/DebuggerDialog.hxx @@ -84,6 +84,10 @@ class DebuggerDialog : public Dialog void doAdvance(); void doRewind(); void doUnwind(); + void doRewind10(); + void doUnwind10(); + void doRewindAll(); + void doUnwindAll(); void doExitDebugger(); void doExitRom(); diff --git a/src/emucore/EventHandler.cxx b/src/emucore/EventHandler.cxx index ac3957a8d..6d591b81d 100644 --- a/src/emucore/EventHandler.cxx +++ b/src/emucore/EventHandler.cxx @@ -298,16 +298,28 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state) { switch(key) { - case KBDK_LEFT: // Alt-left rewinds states + case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states myOSystem.frameBuffer().resetPauseDelay(); setEventState(S_PAUSE); - myOSystem.state().rewindState(); + myOSystem.state().rewindState((kbdShift(mod) && state) ? 10 : 1); break; - case KBDK_RIGHT: // Alt-right unwinds states + case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states myOSystem.frameBuffer().resetPauseDelay(); setEventState(S_PAUSE); - myOSystem.state().unwindState(); + myOSystem.state().unwindState((kbdShift(mod) && state) ? 10 : 1); + break; + + case KBDK_DOWN: // Alt-down rewinds to start of list + myOSystem.frameBuffer().resetPauseDelay(); + setEventState(S_PAUSE); + myOSystem.state().rewindState(1000); + break; + + case KBDK_UP: // Alt-up rewinds to end of list + myOSystem.frameBuffer().resetPauseDelay(); + setEventState(S_PAUSE); + myOSystem.state().unwindState(1000); break; default: