state compression improved

RewindManager class refactored for multiple rewinds/unwinds
additional keys for faster rewinding added
docs updated
This commit is contained in:
thrust26 2017-12-14 14:25:02 +01:00
parent 984e304f43
commit ba9d809109
13 changed files with 206 additions and 81 deletions

View File

@ -316,9 +316,11 @@ used.</p>
</ul> </ul>
<p>The larger button at the left top (labeled '&lt;') performs the rewind operation, <p>The larger button at the left top (labeled '&lt;') 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 '&gt;') performs the unwind operation, which will undo the the left bottom (labeled '&gt;') performs the unwind operation, which will undo the
previous rewind operation. The rewind buffer is 100 levels deep by default.<p> previous rewind operation. The rewind buffer is 100 levels deep by default, the
size can be configured e.g. in the
<b><a href="index.html#Debugger">Developer Settings</a> - States</b> dialog.<p>
<p>The other operations are Step, Trace, Scan+1, Frame+1 and Exit (debugger).</p> <p>The other operations are Step, Trace, Scan+1, Frame+1 and Exit (debugger).</p>
@ -347,11 +349,27 @@ previous rewind operation. The rewind buffer is 100 levels deep by default.<p>
</tr> </tr>
<tr> <tr>
<td>Control-r</td> <td>Control-r</td>
<td>Rewind</td> <td>Rewind 1</td>
</tr> </tr>
<tr> <tr>
<td>Shift-Control-r</td> <td>Control-Shift-r</td>
<td>Unwind</td> <td>Rewind 10</td>
</tr>
<tr>
<td>Control-Alt-r</td>
<td>Rewind all</td>
</tr>
<tr>
<td>Control-y</td>
<td>Unwind 1</td>
</tr>
<tr>
<td>Control-Shift-y</td>
<td>Unwind 10</td>
</tr>
<tr>
<td>Control-Alt-y</td>
<td>Unwind all</td>
</tr> </tr>
<tr> <tr>
<td>Backquote (`)</td> <td>Backquote (`)</td>

View File

@ -1622,11 +1622,31 @@
<td>Alt + Left arrow</td> <td>Alt + Left arrow</td>
<td>Cmd + Left arrow</td> <td>Cmd + Left arrow</td>
</tr> </tr>
<tr>
<td>Rewind by 10 states (pauses emulation)</td>
<td>Shift-Alt + Left arrow</td>
<td>Shift-Cmd + Left arrow</td>
</tr>
<tr>
<td>Rewind all states (pauses emulation)</td>
<td>Alt + Down arrow</td>
<td>Cmd + Down arrow</td>
</tr>
<tr> <tr>
<td>Unwind by one state (pauses emulation)</td> <td>Unwind by one state (pauses emulation)</td>
<td>Alt + Right arrow</td> <td>Alt + Right arrow</td>
<td>Cmd + Right arrow</td> <td>Cmd + Right arrow</td>
</tr> </tr>
<tr>
<td>Unwind by 10 states (pauses emulation)</td>
<td>Shift-Alt + Right arrow</td>
<td>Shift-Cmd + Right arrow</td>
</tr>
<tr>
<td>Unwind all states (pauses emulation)</td>
<td>Alt + Up arrow</td>
<td>Cmd + Up arrow</td>
</tr>
</table> </table>
<p><b>UI keys in Text Editing areas (cannot be remapped)</b></p> <p><b>UI keys in Text Editing areas (cannot be remapped)</b></p>

View File

@ -175,26 +175,22 @@ class LinkedObjectPool
moveToPrevious(); // if so, move to the previous node moveToPrevious(); // if so, move to the previous node
} }
#if 0
/** /**
Convenience method to remove a single element from the active list at Remove a single element from the active list at position of the iterator.
position of the iterator +- the offset.
*/ */
void remove(const_iter i, Int32 offset = 0) { void remove(const_iter i) {
myPool.splice(myPool.end(), myList, myPool.splice(myPool.end(), myList, i);
offset >= 0 ? std::next(i, offset) : std::prev(i, -offset));
} }
#endif
/** /**
Convenience method to remove a single element from the active list by Remove a single element from the active list by index, offset from
index, offset from the beginning of the list. (ie, '0' means first the beginning of the list. (ie, '0' means first element, '1' is second,
element, '1' is second, and so on). and so on).
*/ */
void remove(uInt32 index) { void remove(uInt32 index) {
myPool.splice(myPool.end(), myList, std::next(myList.begin(), index)); myPool.splice(myPool.end(), myList, std::next(myList.begin(), index));
} }
/** /**
Remove range of elements from the beginning of the active list to Remove range of elements from the beginning of the active list to
the 'current' node. the 'current' node.

View File

@ -130,38 +130,58 @@ bool RewindManager::addState(const string& message, bool continuous)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::rewindState() uInt32 RewindManager::rewindState(uInt32 numStates)
{ {
uInt64 startCycles = myOSystem.console().tia().cycles();
uInt32 i;
string message;
for(i = 0; i < numStates; ++i)
{
if(!atFirst()) if(!atFirst())
{ {
if (!myLastContinuousAdd) if(!myLastContinuousAdd)
// Set internal current iterator to previous state (back in time), // Set internal current iterator to previous state (back in time),
// since we will now processed this state // since we will now processed this state
myStateList.moveToPrevious(); myStateList.moveToPrevious();
myLastContinuousAdd = false; myLastContinuousAdd = false;
//RewindState& lastState = myStateList.current();
RewindState& state = myStateList.current(); RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state);
//cerr << "rewind " << state.count << endl; //cerr << "rewind " << state.count << endl;
s.rewind(); // rewind Serializer internal buffers s.rewind(); // rewind Serializer internal buffers
}
else
break;
}
if(i)
{
RewindState& state = myStateList.current();
Serializer& s = state.data;
myStateManager.loadState(s); myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(s); myOSystem.console().tia().loadDisplay(s);
// Show message indicating the rewind state // Get message indicating the rewind state
myOSystem.frameBuffer().showMessage(message); message = getMessage(startCycles, i);
return true;
} }
myOSystem.frameBuffer().showMessage("Rewind not possible"); else
return false; message = "Rewind not possible";
myOSystem.frameBuffer().showMessage(message);
return i;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RewindManager::unwindState() uInt32 RewindManager::unwindState(uInt32 numStates)
{ {
uInt64 startCycles = myOSystem.console().tia().cycles();
uInt32 i;
string message;
for(i = 0; i < numStates; ++i)
{
if(!atLast()) if(!atLast())
{ {
// Set internal current iterator to nextCycles state (forward in time), // Set internal current iterator to nextCycles state (forward in time),
@ -170,19 +190,30 @@ bool RewindManager::unwindState()
RewindState& state = myStateList.current(); RewindState& state = myStateList.current();
Serializer& s = state.data; Serializer& s = state.data;
string message = getMessage(state);
//cerr << "unwind " << state.count << endl; //cerr << "unwind " << state.count << endl;
s.rewind(); // rewind Serializer internal buffers s.rewind(); // rewind Serializer internal buffers
}
else
break;
}
if(i)
{
RewindState& state = myStateList.current();
Serializer& s = state.data;
myStateManager.loadState(s); myStateManager.loadState(s);
myOSystem.console().tia().loadDisplay(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); myOSystem.frameBuffer().showMessage(message);
return true;
} }
myOSystem.frameBuffer().showMessage("Unwind not possible"); else
return false; message = "Unwind not possible";
myOSystem.frameBuffer().showMessage(message);
return i;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -191,8 +222,8 @@ void RewindManager::compressStates()
uInt64 currentCycles = myOSystem.console().tia().cycles(); uInt64 currentCycles = myOSystem.console().tia().cycles();
double expectedCycles = myInterval * myFactor * (1 + myFactor); double expectedCycles = myInterval * myFactor * (1 + myFactor);
double maxError = 1; double maxError = 1;
uInt32 removeIdx = 0;
uInt32 idx = myStateList.size() - 2; uInt32 idx = myStateList.size() - 2;
Common::LinkedObjectPool<RewindState>::const_iter removeIter = myStateList.first();
//cerr << "idx: " << idx << endl; //cerr << "idx: " << idx << endl;
// iterate from last but one to first but one // iterate from last but one to first but one
@ -211,7 +242,7 @@ void RewindManager::compressStates()
if(error > maxError) if(error > maxError)
{ {
maxError = error; maxError = error;
removeIdx = idx; removeIter = it;
} }
} }
--idx; --idx;
@ -224,22 +255,23 @@ void RewindManager::compressStates()
} }
else else
{ {
myStateList.remove(removeIdx); // remove myStateList.remove(removeIter); // remove
//cerr << "remove " << removeIdx << endl; //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; stringstream message;
message << (diff >= 0 ? "Rewind" : "Unwind") << " " << getUnitString(diff); message << (diff >= 0 ? "Rewind" : "Unwind") << " " << getUnitString(diff);
message << " [" << myStateList.currentIdx() << "/" << myStateList.size() << "]"; message << " [" << myStateList.currentIdx() << "/" << myStateList.size() << "]";
// add optional message // add optional message
if(!state.message.empty()) if(numStates == 1 && !state.message.empty())
message << " (" << state.message << ")"; message << " (" << state.message << ")";
return message.str(); return message.str();

View File

@ -99,6 +99,7 @@ class RewindManager
}; };
/** /**
Initializes state list and calculates compression factor.
*/ */
void setup(); void setup();
@ -113,14 +114,20 @@ class RewindManager
/** /**
Rewind one level of the state list, and display the message associated Rewind one level of the state list, and display the message associated
with that state. 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 Unwind one level of the state list, and display the message associated
with that state. 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 atFirst() const { return myStateList.atFirst(); }
bool atLast() const { return myStateList.atLast(); } bool atLast() const { return myStateList.atLast(); }
@ -167,9 +174,17 @@ class RewindManager
// frequent (de)-allocations) // frequent (de)-allocations)
Common::LinkedObjectPool<RewindState, INITIAL_SIZE> myStateList; Common::LinkedObjectPool<RewindState, INITIAL_SIZE> myStateList;
/**
Remove a save state from the list
*/
void compressStates(); void compressStates();
string getMessage(RewindState& state); /**
Get the message string for the rewind/unwind
@return The message
*/
string getMessage(Int64 startCycles, uInt32 numStates);
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -142,17 +142,17 @@ void StateManager::toggleRewindMode()
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::rewindState() bool StateManager::rewindState(uInt32 numStates)
{ {
RewindManager& r = myOSystem.state().rewindManager(); 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(); RewindManager& r = myOSystem.state().rewindManager();
return r.unwindState(); return r.unwindState(numStates);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -74,12 +74,12 @@ class StateManager
/** /**
Rewinds one state; this uses the RewindManager for its functionality. 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. 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. Updates the state of the system based on the currently active mode.

View File

@ -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(); RewindManager& r = myOSystem.state().rewindManager();
@ -534,13 +534,8 @@ uInt16 Debugger::windStates(uInt16 states, bool unwind, string& message)
unlockBankswitchState(); unlockBankswitchState();
uInt16 winds = 0;
uInt64 startCycles = myOSystem.console().tia().cycles(); uInt64 startCycles = myOSystem.console().tia().cycles();
for(uInt16 i = 0; i < states; ++i) uInt16 winds = unwind ? r.unwindState(numStates) : r.rewindState(numStates);
if(unwind ? r.unwindState() : r.rewindState())
winds++;
else
break;
message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles); message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles);
lockBankswitchState(); 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);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -262,8 +262,8 @@ class Debugger : public DialogContainer
int trace(); int trace();
void nextScanline(int lines); void nextScanline(int lines);
void nextFrame(int frames); void nextFrame(int frames);
uInt16 rewindStates(const uInt16 states, string& message); uInt16 rewindStates(const uInt16 numStates, string& message);
uInt16 unwindStates(const uInt16 states, string& message); uInt16 unwindStates(const uInt16 numStates, string& message);
void toggleBreakPoint(uInt16 bp); void toggleBreakPoint(uInt16 bp);
@ -321,7 +321,7 @@ class Debugger : public DialogContainer
private: private:
// rewind/unwind n states // 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 // update the rewind/unwind button state
void updateRewindbuttons(const RewindManager& r); void updateRewindbuttons(const RewindManager& r);

View File

@ -2177,7 +2177,6 @@ void DebuggerParser::executeWatch()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// wrapper function for rewind/unwind commands // wrapper function for rewind/unwind commands
// TODO: return and output (formatted) cycles
void DebuggerParser::executeWinds(bool unwind) void DebuggerParser::executeWinds(bool unwind)
{ {
uInt16 states; uInt16 states;

View File

@ -97,8 +97,18 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
switch(key) switch(key)
{ {
case KBDK_R: case KBDK_R:
if(!instance().eventHandler().kbdShift(mod)) if(instance().eventHandler().kbdAlt(mod))
doRewindAll();
else if(instance().eventHandler().kbdShift(mod))
doRewind10();
else
doRewind(); doRewind();
break;
case KBDK_Y:
if(instance().eventHandler().kbdAlt(mod))
doUnwindAll();
else if(instance().eventHandler().kbdShift(mod))
doUnwind10();
else else
doUnwind(); doUnwind();
break; break;
@ -212,6 +222,30 @@ void DebuggerDialog::doUnwind()
instance().debugger().parser().run("unwind"); 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() void DebuggerDialog::doExitDebugger()
{ {

View File

@ -84,6 +84,10 @@ class DebuggerDialog : public Dialog
void doAdvance(); void doAdvance();
void doRewind(); void doRewind();
void doUnwind(); void doUnwind();
void doRewind10();
void doUnwind10();
void doRewindAll();
void doUnwindAll();
void doExitDebugger(); void doExitDebugger();
void doExitRom(); void doExitRom();

View File

@ -298,16 +298,28 @@ void EventHandler::handleKeyEvent(StellaKey key, StellaMod mod, bool state)
{ {
switch(key) switch(key)
{ {
case KBDK_LEFT: // Alt-left rewinds states case KBDK_LEFT: // Alt-left(-shift) rewinds 1(10) states
myOSystem.frameBuffer().resetPauseDelay(); myOSystem.frameBuffer().resetPauseDelay();
setEventState(S_PAUSE); setEventState(S_PAUSE);
myOSystem.state().rewindState(); myOSystem.state().rewindState((kbdShift(mod) && state) ? 10 : 1);
break; break;
case KBDK_RIGHT: // Alt-right unwinds states case KBDK_RIGHT: // Alt-right(-shift) unwinds 1(10) states
myOSystem.frameBuffer().resetPauseDelay(); myOSystem.frameBuffer().resetPauseDelay();
setEventState(S_PAUSE); 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; break;
default: default: