mirror of https://github.com/stella-emu/stella.git
unwind logic started, TODO: adapt RewindManager
This commit is contained in:
parent
cf74aa8c4d
commit
3045a0ddf7
|
@ -15,6 +15,8 @@
|
||||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "OSystem.hxx"
|
#include "OSystem.hxx"
|
||||||
#include "Serializer.hxx"
|
#include "Serializer.hxx"
|
||||||
#include "StateManager.hxx"
|
#include "StateManager.hxx"
|
||||||
|
@ -25,15 +27,19 @@
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
|
RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
|
||||||
: myOSystem(system),
|
: myOSystem(system),
|
||||||
myStateManager(statemgr)
|
myStateManager(statemgr),
|
||||||
|
myIsNTSC(true) // TODO
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
bool RewindManager::addState(const string& message)
|
bool RewindManager::addState(const string& message)
|
||||||
{
|
{
|
||||||
|
// TODO: remove following (preceding???) (all invalid) states
|
||||||
|
// myStateList.removeToFront(); from current() + 1 (or - 1???)
|
||||||
|
|
||||||
if(myStateList.full())
|
if(myStateList.full())
|
||||||
myStateList.removeLast(); // remove the oldest state file
|
compressStates();
|
||||||
|
|
||||||
RewindState& state = myStateList.addFirst();
|
RewindState& state = myStateList.addFirst();
|
||||||
Serializer& s = state.data;
|
Serializer& s = state.data;
|
||||||
|
@ -41,7 +47,8 @@ bool RewindManager::addState(const string& message)
|
||||||
s.reset(); // rewind Serializer internal buffers
|
s.reset(); // rewind Serializer internal buffers
|
||||||
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
|
if(myStateManager.saveState(s) && myOSystem.console().tia().saveDisplay(s))
|
||||||
{
|
{
|
||||||
state.message = "Rewind " + message;
|
state.message = message;
|
||||||
|
state.cycle = myOSystem.console().tia().cycles();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -52,15 +59,19 @@ bool RewindManager::rewindState()
|
||||||
{
|
{
|
||||||
if(!myStateList.empty())
|
if(!myStateList.empty())
|
||||||
{
|
{
|
||||||
|
// TODO: get state previous to the current state instead of first()
|
||||||
RewindState& state = myStateList.first();
|
RewindState& state = myStateList.first();
|
||||||
Serializer& s = state.data;
|
Serializer& s = state.data;
|
||||||
|
string message = getMessage(state);
|
||||||
|
|
||||||
s.reset(); // rewind Serializer internal buffers
|
s.reset(); // rewind Serializer internal buffers
|
||||||
myStateManager.loadState(s);
|
myStateManager.loadState(s);
|
||||||
myOSystem.console().tia().loadDisplay(s);
|
myOSystem.console().tia().loadDisplay(s);
|
||||||
|
|
||||||
// Show message indicating the rewind state
|
// Show message indicating the rewind state
|
||||||
myOSystem.frameBuffer().showMessage(state.message);
|
myOSystem.frameBuffer().showMessage(message);
|
||||||
|
|
||||||
|
// TODO: Do NOT remove state (TODO later somewhere else: stop emulation)
|
||||||
myStateList.removeFirst();
|
myStateList.removeFirst();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -68,3 +79,80 @@ bool RewindManager::rewindState()
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool RewindManager::unwindState()
|
||||||
|
{
|
||||||
|
if(!atFirst()) // or last???
|
||||||
|
{
|
||||||
|
// TODO: get state next to the current state
|
||||||
|
/*RewindState& state = myStateList.???()
|
||||||
|
Serializer& s = state.data;
|
||||||
|
string message = getMessage(state);
|
||||||
|
|
||||||
|
s.reset(); // rewind Serializer internal buffers
|
||||||
|
myStateManager.loadState(s);
|
||||||
|
myOSystem.console().tia().loadDisplay(s);
|
||||||
|
|
||||||
|
// Show message indicating the rewind state
|
||||||
|
myOSystem.frameBuffer().showMessage(message);*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void RewindManager::compressStates()
|
||||||
|
{
|
||||||
|
myStateList.removeLast(); // remove the oldest state file
|
||||||
|
// TODO: add smart state removal
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
string RewindManager::getMessage(RewindState& state)
|
||||||
|
{
|
||||||
|
const Int64 NTSC_FREQ = 1193182;
|
||||||
|
const Int64 PAL_FREQ = 1182298;
|
||||||
|
Int64 diff = myOSystem.console().tia().cycles() - state.cycle,
|
||||||
|
freq = myIsNTSC ? NTSC_FREQ : PAL_FREQ,
|
||||||
|
diffUnit;
|
||||||
|
stringstream message;
|
||||||
|
string unit;
|
||||||
|
|
||||||
|
message << (diff >= 0 ? "Rewind" : "Unwind");
|
||||||
|
diff = abs(diff);
|
||||||
|
|
||||||
|
if(diff < 76 * 2)
|
||||||
|
{
|
||||||
|
unit = "cycle";
|
||||||
|
diffUnit = diff;
|
||||||
|
}
|
||||||
|
else if(diff < 76 * 262 * 2)
|
||||||
|
{
|
||||||
|
unit = "scanline";
|
||||||
|
diffUnit = diff / 76;
|
||||||
|
}
|
||||||
|
else if(diff < NTSC_FREQ * 2)
|
||||||
|
{
|
||||||
|
unit = "frame";
|
||||||
|
diffUnit = diff / (76 * 262);
|
||||||
|
}
|
||||||
|
else if(diff < NTSC_FREQ * 60 * 2)
|
||||||
|
{
|
||||||
|
unit = "second";
|
||||||
|
diffUnit = diff / NTSC_FREQ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unit = "minute";
|
||||||
|
diffUnit = diff / (NTSC_FREQ * 60);
|
||||||
|
}
|
||||||
|
message << " " << diffUnit << " " << unit;
|
||||||
|
if(diffUnit != 1)
|
||||||
|
message << "s";
|
||||||
|
|
||||||
|
// add optional message (TODO: when smart removal works, we have to do something smart with this part too)
|
||||||
|
if(!state.message.empty())
|
||||||
|
message << " (" << state.message << ")";
|
||||||
|
return message.str();
|
||||||
|
}
|
||||||
|
|
|
@ -49,7 +49,14 @@ class RewindManager
|
||||||
*/
|
*/
|
||||||
bool rewindState();
|
bool rewindState();
|
||||||
|
|
||||||
bool empty() const { return myStateList.empty(); }
|
/**
|
||||||
|
Unwind one level of the state list, and display the message associated
|
||||||
|
with that state.
|
||||||
|
*/
|
||||||
|
bool unwindState();
|
||||||
|
|
||||||
|
bool atLast() const { return myStateList.empty(); }
|
||||||
|
bool atFirst() const { return false; } // TODO
|
||||||
void clear() { myStateList.clear(); }
|
void clear() { myStateList.clear(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -62,6 +69,7 @@ class RewindManager
|
||||||
struct RewindState {
|
struct RewindState {
|
||||||
Serializer data;
|
Serializer data;
|
||||||
string message;
|
string message;
|
||||||
|
uInt64 cycle;
|
||||||
|
|
||||||
// We do nothing on object instantiation or copy
|
// We do nothing on object instantiation or copy
|
||||||
RewindState() { }
|
RewindState() { }
|
||||||
|
@ -70,6 +78,13 @@ class RewindManager
|
||||||
|
|
||||||
Common::LinkedObjectPool<RewindState, MAX_SIZE> myStateList;
|
Common::LinkedObjectPool<RewindState, MAX_SIZE> myStateList;
|
||||||
|
|
||||||
|
bool myIsNTSC;
|
||||||
|
|
||||||
|
void compressStates();
|
||||||
|
|
||||||
|
string getMessage(RewindState& state);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
RewindManager() = delete;
|
RewindManager() = delete;
|
||||||
|
|
|
@ -451,22 +451,34 @@ void Debugger::nextFrame(int frames)
|
||||||
lockBankswitchState();
|
lockBankswitchState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
bool Debugger::windState(bool unwind)
|
||||||
bool Debugger::rewindState()
|
|
||||||
{
|
{
|
||||||
RewindManager& r = myOSystem.state().rewindManager();
|
RewindManager& r = myOSystem.state().rewindManager();
|
||||||
|
|
||||||
mySystem.clearDirtyPages();
|
mySystem.clearDirtyPages();
|
||||||
|
|
||||||
unlockBankswitchState();
|
unlockBankswitchState();
|
||||||
bool result = r.rewindState();
|
bool result = unwind ? r.unwindState() : r.rewindState();
|
||||||
lockBankswitchState();
|
lockBankswitchState();
|
||||||
|
|
||||||
myDialog->rewindButton().setEnabled(!r.empty());
|
myDialog->rewindButton().setEnabled(!r.atLast());
|
||||||
|
myDialog->unwindButton().setEnabled(!r.atFirst());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool Debugger::rewindState()
|
||||||
|
{
|
||||||
|
return windState(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool Debugger::unwindState()
|
||||||
|
{
|
||||||
|
return windState(true);
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void Debugger::clearAllBreakPoints()
|
void Debugger::clearAllBreakPoints()
|
||||||
{
|
{
|
||||||
|
@ -505,7 +517,8 @@ void Debugger::saveOldState(string rewindMsg)
|
||||||
{
|
{
|
||||||
RewindManager& r = myOSystem.state().rewindManager();
|
RewindManager& r = myOSystem.state().rewindManager();
|
||||||
r.addState(rewindMsg);
|
r.addState(rewindMsg);
|
||||||
myDialog->rewindButton().setEnabled(!r.empty());
|
myDialog->rewindButton().setEnabled(!r.atLast());
|
||||||
|
myDialog->unwindButton().setEnabled(!r.atFirst());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,7 +532,8 @@ void Debugger::setStartState()
|
||||||
RewindManager& r = myOSystem.state().rewindManager();
|
RewindManager& r = myOSystem.state().rewindManager();
|
||||||
if(myOSystem.state().mode() == StateManager::Mode::Off)
|
if(myOSystem.state().mode() == StateManager::Mode::Off)
|
||||||
r.clear();
|
r.clear();
|
||||||
myDialog->rewindButton().setEnabled(!r.empty());
|
myDialog->rewindButton().setEnabled(!r.atLast());
|
||||||
|
myDialog->unwindButton().setEnabled(!r.atFirst());
|
||||||
|
|
||||||
// Save initial state, but don't add it to the rewind list
|
// Save initial state, but don't add it to the rewind list
|
||||||
saveOldState();
|
saveOldState();
|
||||||
|
|
|
@ -270,6 +270,7 @@ class Debugger : public DialogContainer
|
||||||
void nextScanline(int lines);
|
void nextScanline(int lines);
|
||||||
void nextFrame(int frames);
|
void nextFrame(int frames);
|
||||||
bool rewindState();
|
bool rewindState();
|
||||||
|
bool unwindState();
|
||||||
|
|
||||||
void toggleBreakPoint(uInt16 bp);
|
void toggleBreakPoint(uInt16 bp);
|
||||||
|
|
||||||
|
@ -326,6 +327,8 @@ class Debugger : public DialogContainer
|
||||||
static PseudoRegister ourPseudoRegisters[NUM_PSEUDO_REGS];
|
static PseudoRegister ourPseudoRegisters[NUM_PSEUDO_REGS];
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool windState(bool unwind);
|
||||||
|
|
||||||
// Following constructors and assignment operators not supported
|
// Following constructors and assignment operators not supported
|
||||||
Debugger() = delete;
|
Debugger() = delete;
|
||||||
Debugger(const Debugger&) = delete;
|
Debugger(const Debugger&) = delete;
|
||||||
|
|
|
@ -1837,6 +1837,19 @@ void DebuggerParser::executeUndef()
|
||||||
commandResult << red("no such label");
|
commandResult << red("no such label");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
// "unwind"
|
||||||
|
void DebuggerParser::executeUnwind()
|
||||||
|
{
|
||||||
|
if(debugger.unwindState())
|
||||||
|
{
|
||||||
|
debugger.rom().invalidate();
|
||||||
|
commandResult << "unwind by one level";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
commandResult << "no states left to rewind";
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// "v"
|
// "v"
|
||||||
void DebuggerParser::executeV()
|
void DebuggerParser::executeV()
|
||||||
|
@ -2611,6 +2624,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
|
||||||
std::mem_fn(&DebuggerParser::executeUndef)
|
std::mem_fn(&DebuggerParser::executeUndef)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"unwind",
|
||||||
|
"Unwind state to last step/trace/scanline/frame",
|
||||||
|
"Unwind currently only works in the debugger",
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
{ kARG_END_ARGS },
|
||||||
|
std::mem_fn(&DebuggerParser::executeUnwind)
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"v",
|
"v",
|
||||||
"Overflow Flag: set (0 or 1), or toggle (no arg)",
|
"Overflow Flag: set (0 or 1), or toggle (no arg)",
|
||||||
|
|
|
@ -68,7 +68,7 @@ class DebuggerParser
|
||||||
string saveScriptFile(string file);
|
string saveScriptFile(string file);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum { kNumCommands = 76 };
|
enum { kNumCommands = 77 };
|
||||||
|
|
||||||
// Constants for argument processing
|
// Constants for argument processing
|
||||||
enum {
|
enum {
|
||||||
|
@ -209,6 +209,7 @@ class DebuggerParser
|
||||||
void executeType();
|
void executeType();
|
||||||
void executeUHex();
|
void executeUHex();
|
||||||
void executeUndef();
|
void executeUndef();
|
||||||
|
void executeUnwind();
|
||||||
void executeV();
|
void executeV();
|
||||||
void executeWatch();
|
void executeWatch();
|
||||||
void executeX();
|
void executeX();
|
||||||
|
|
|
@ -92,7 +92,10 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
||||||
switch(key)
|
switch(key)
|
||||||
{
|
{
|
||||||
case KBDK_R:
|
case KBDK_R:
|
||||||
doRewind();
|
if(!instance().eventHandler().kbdShift(mod))
|
||||||
|
doRewind();
|
||||||
|
else
|
||||||
|
doUnwind();
|
||||||
break;
|
break;
|
||||||
case KBDK_S:
|
case KBDK_S:
|
||||||
doStep();
|
doStep();
|
||||||
|
@ -141,6 +144,10 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
doRewind();
|
doRewind();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case kDDUnwindCmd:
|
||||||
|
doUnwind();
|
||||||
|
break;
|
||||||
|
|
||||||
case kDDExitCmd:
|
case kDDExitCmd:
|
||||||
doExitDebugger();
|
doExitDebugger();
|
||||||
break;
|
break;
|
||||||
|
@ -189,6 +196,12 @@ void DebuggerDialog::doRewind()
|
||||||
instance().debugger().parser().run("rewind");
|
instance().debugger().parser().run("rewind");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void DebuggerDialog::doUnwind()
|
||||||
|
{
|
||||||
|
instance().debugger().parser().run("unwind");
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void DebuggerDialog::doExitDebugger()
|
void DebuggerDialog::doExitDebugger()
|
||||||
{
|
{
|
||||||
|
@ -375,7 +388,7 @@ void DebuggerDialog::addRomArea()
|
||||||
bwidth, bheight, "Exit", kDDExitCmd);
|
bwidth, bheight, "Exit", kDDExitCmd);
|
||||||
|
|
||||||
bwidth = myLFont->getStringWidth("< ") + 4;
|
bwidth = myLFont->getStringWidth("< ") + 4;
|
||||||
bheight = bheight * 5 + 4*4;
|
bheight = bheight * 3 + 4 * 2;
|
||||||
buttonX -= (bwidth + 5);
|
buttonX -= (bwidth + 5);
|
||||||
buttonY = r.top + 5;
|
buttonY = r.top + 5;
|
||||||
myRewindButton =
|
myRewindButton =
|
||||||
|
@ -383,6 +396,14 @@ void DebuggerDialog::addRomArea()
|
||||||
bwidth, bheight, "<", kDDRewindCmd);
|
bwidth, bheight, "<", kDDRewindCmd);
|
||||||
myRewindButton->clearFlags(WIDGET_ENABLED);
|
myRewindButton->clearFlags(WIDGET_ENABLED);
|
||||||
|
|
||||||
|
buttonY += bheight + 4;
|
||||||
|
bheight = (myLFont->getLineHeight() + 2) * 2 + 4 * 1;
|
||||||
|
myUnwindButton =
|
||||||
|
new ButtonWidget(this, *myLFont, buttonX, buttonY,
|
||||||
|
bwidth, bheight, ">", kDDUnwindCmd);
|
||||||
|
myUnwindButton->clearFlags(WIDGET_ENABLED);
|
||||||
|
|
||||||
|
|
||||||
int xpos = buttonX - 8*myLFont->getMaxCharWidth() - 20, ypos = 20;
|
int xpos = buttonX - 8*myLFont->getMaxCharWidth() - 20, ypos = 20;
|
||||||
DataGridOpsWidget* ops = new DataGridOpsWidget(this, *myLFont, xpos, ypos);
|
DataGridOpsWidget* ops = new DataGridOpsWidget(this, *myLFont, xpos, ypos);
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ class DebuggerDialog : public Dialog
|
||||||
CartRamWidget& cartRam() const { return *myCartRam; }
|
CartRamWidget& cartRam() const { return *myCartRam; }
|
||||||
EditTextWidget& message() const { return *myMessageBox; }
|
EditTextWidget& message() const { return *myMessageBox; }
|
||||||
ButtonWidget& rewindButton() const { return *myRewindButton; }
|
ButtonWidget& rewindButton() const { return *myRewindButton; }
|
||||||
|
ButtonWidget& unwindButton() const { return *myUnwindButton; }
|
||||||
|
|
||||||
void showFatalMessage(const string& msg);
|
void showFatalMessage(const string& msg);
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ class DebuggerDialog : public Dialog
|
||||||
void doScanlineAdvance();
|
void doScanlineAdvance();
|
||||||
void doAdvance();
|
void doAdvance();
|
||||||
void doRewind();
|
void doRewind();
|
||||||
|
void doUnwind();
|
||||||
void doExitDebugger();
|
void doExitDebugger();
|
||||||
void doExitRom();
|
void doExitRom();
|
||||||
|
|
||||||
|
@ -96,6 +98,7 @@ class DebuggerDialog : public Dialog
|
||||||
kDDAdvCmd = 'DDav',
|
kDDAdvCmd = 'DDav',
|
||||||
kDDSAdvCmd = 'DDsv',
|
kDDSAdvCmd = 'DDsv',
|
||||||
kDDRewindCmd = 'DDrw',
|
kDDRewindCmd = 'DDrw',
|
||||||
|
kDDUnwindCmd = 'DDuw',
|
||||||
kDDExitCmd = 'DDex',
|
kDDExitCmd = 'DDex',
|
||||||
kDDExitFatalCmd = 'DDer'
|
kDDExitFatalCmd = 'DDer'
|
||||||
};
|
};
|
||||||
|
@ -113,6 +116,7 @@ class DebuggerDialog : public Dialog
|
||||||
CartRamWidget* myCartRam;
|
CartRamWidget* myCartRam;
|
||||||
EditTextWidget* myMessageBox;
|
EditTextWidget* myMessageBox;
|
||||||
ButtonWidget* myRewindButton;
|
ButtonWidget* myRewindButton;
|
||||||
|
ButtonWidget* myUnwindButton;
|
||||||
unique_ptr<GUI::MessageBox> myFatalError;
|
unique_ptr<GUI::MessageBox> myFatalError;
|
||||||
|
|
||||||
unique_ptr<GUI::Font> myLFont; // used for labels
|
unique_ptr<GUI::Font> myLFont; // used for labels
|
||||||
|
|
Loading…
Reference in New Issue