mirror of https://github.com/stella-emu/stella.git
state compression improved
RewindManager class refactored for multiple rewinds/unwinds additional keys for faster rewinding added docs updated
This commit is contained in:
parent
984e304f43
commit
ba9d809109
|
@ -316,9 +316,11 @@ used.</p>
|
|||
</ul>
|
||||
|
||||
<p>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.<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>
|
||||
|
||||
|
@ -347,11 +349,27 @@ previous rewind operation. The rewind buffer is 100 levels deep by default.<p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Control-r</td>
|
||||
<td>Rewind</td>
|
||||
<td>Rewind 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shift-Control-r</td>
|
||||
<td>Unwind</td>
|
||||
<td>Control-Shift-r</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>
|
||||
<td>Backquote (`)</td>
|
||||
|
|
|
@ -1622,11 +1622,31 @@
|
|||
<td>Alt + Left arrow</td>
|
||||
<td>Cmd + Left arrow</td>
|
||||
</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>
|
||||
<td>Unwind by one state (pauses emulation)</td>
|
||||
<td>Alt + Right arrow</td>
|
||||
<td>Cmd + Right arrow</td>
|
||||
</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>
|
||||
|
||||
<p><b>UI keys in Text Editing areas (cannot be remapped)</b></p>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -130,7 +130,13 @@ 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())
|
||||
{
|
||||
|
@ -140,27 +146,41 @@ bool RewindManager::rewindState()
|
|||
myStateList.moveToPrevious();
|
||||
myLastContinuousAdd = false;
|
||||
|
||||
//RewindState& lastState = myStateList.current();
|
||||
|
||||
RewindState& state = myStateList.current();
|
||||
Serializer& s = state.data;
|
||||
string message = getMessage(state);
|
||||
//cerr << "rewind " << state.count << endl;
|
||||
|
||||
s.rewind(); // rewind Serializer internal buffers
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
RewindState& state = myStateList.current();
|
||||
Serializer& s = state.data;
|
||||
|
||||
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)
|
||||
{
|
||||
uInt64 startCycles = myOSystem.console().tia().cycles();
|
||||
uInt32 i;
|
||||
string message;
|
||||
|
||||
for(i = 0; i < numStates; ++i)
|
||||
{
|
||||
if(!atLast())
|
||||
{
|
||||
|
@ -170,19 +190,30 @@ bool RewindManager::unwindState()
|
|||
|
||||
RewindState& state = myStateList.current();
|
||||
Serializer& s = state.data;
|
||||
string message = getMessage(state);
|
||||
//cerr << "unwind " << state.count << endl;
|
||||
|
||||
s.rewind(); // rewind Serializer internal buffers
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
RewindState& state = myStateList.current();
|
||||
Serializer& s = state.data;
|
||||
|
||||
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<RewindState>::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();
|
||||
|
|
|
@ -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<RewindState, INITIAL_SIZE> 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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue