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.
|
||||
//============================================================================
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "OSystem.hxx"
|
||||
#include "Serializer.hxx"
|
||||
#include "StateManager.hxx"
|
||||
|
@ -25,15 +27,19 @@
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
RewindManager::RewindManager(OSystem& system, StateManager& statemgr)
|
||||
: myOSystem(system),
|
||||
myStateManager(statemgr)
|
||||
myStateManager(statemgr),
|
||||
myIsNTSC(true) // TODO
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool RewindManager::addState(const string& message)
|
||||
{
|
||||
// TODO: remove following (preceding???) (all invalid) states
|
||||
// myStateList.removeToFront(); from current() + 1 (or - 1???)
|
||||
|
||||
if(myStateList.full())
|
||||
myStateList.removeLast(); // remove the oldest state file
|
||||
compressStates();
|
||||
|
||||
RewindState& state = myStateList.addFirst();
|
||||
Serializer& s = state.data;
|
||||
|
@ -41,7 +47,8 @@ bool RewindManager::addState(const string& message)
|
|||
s.reset(); // rewind Serializer internal buffers
|
||||
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 false;
|
||||
|
@ -52,15 +59,19 @@ bool RewindManager::rewindState()
|
|||
{
|
||||
if(!myStateList.empty())
|
||||
{
|
||||
// TODO: get state previous to the current state instead of first()
|
||||
RewindState& state = myStateList.first();
|
||||
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(state.message);
|
||||
myOSystem.frameBuffer().showMessage(message);
|
||||
|
||||
// TODO: Do NOT remove state (TODO later somewhere else: stop emulation)
|
||||
myStateList.removeFirst();
|
||||
|
||||
return true;
|
||||
|
@ -68,3 +79,80 @@ bool RewindManager::rewindState()
|
|||
else
|
||||
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 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(); }
|
||||
|
||||
private:
|
||||
|
@ -62,6 +69,7 @@ class RewindManager
|
|||
struct RewindState {
|
||||
Serializer data;
|
||||
string message;
|
||||
uInt64 cycle;
|
||||
|
||||
// We do nothing on object instantiation or copy
|
||||
RewindState() { }
|
||||
|
@ -70,6 +78,13 @@ class RewindManager
|
|||
|
||||
Common::LinkedObjectPool<RewindState, MAX_SIZE> myStateList;
|
||||
|
||||
bool myIsNTSC;
|
||||
|
||||
void compressStates();
|
||||
|
||||
string getMessage(RewindState& state);
|
||||
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
RewindManager() = delete;
|
||||
|
|
|
@ -451,22 +451,34 @@ void Debugger::nextFrame(int frames)
|
|||
lockBankswitchState();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Debugger::rewindState()
|
||||
bool Debugger::windState(bool unwind)
|
||||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
|
||||
mySystem.clearDirtyPages();
|
||||
|
||||
unlockBankswitchState();
|
||||
bool result = r.rewindState();
|
||||
bool result = unwind ? r.unwindState() : r.rewindState();
|
||||
lockBankswitchState();
|
||||
|
||||
myDialog->rewindButton().setEnabled(!r.empty());
|
||||
myDialog->rewindButton().setEnabled(!r.atLast());
|
||||
myDialog->unwindButton().setEnabled(!r.atFirst());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Debugger::rewindState()
|
||||
{
|
||||
return windState(false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Debugger::unwindState()
|
||||
{
|
||||
return windState(true);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Debugger::clearAllBreakPoints()
|
||||
{
|
||||
|
@ -505,7 +517,8 @@ void Debugger::saveOldState(string rewindMsg)
|
|||
{
|
||||
RewindManager& r = myOSystem.state().rewindManager();
|
||||
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();
|
||||
if(myOSystem.state().mode() == StateManager::Mode::Off)
|
||||
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
|
||||
saveOldState();
|
||||
|
|
|
@ -270,6 +270,7 @@ class Debugger : public DialogContainer
|
|||
void nextScanline(int lines);
|
||||
void nextFrame(int frames);
|
||||
bool rewindState();
|
||||
bool unwindState();
|
||||
|
||||
void toggleBreakPoint(uInt16 bp);
|
||||
|
||||
|
@ -326,6 +327,8 @@ class Debugger : public DialogContainer
|
|||
static PseudoRegister ourPseudoRegisters[NUM_PSEUDO_REGS];
|
||||
|
||||
private:
|
||||
bool windState(bool unwind);
|
||||
|
||||
// Following constructors and assignment operators not supported
|
||||
Debugger() = delete;
|
||||
Debugger(const Debugger&) = delete;
|
||||
|
|
|
@ -1837,6 +1837,19 @@ void DebuggerParser::executeUndef()
|
|||
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"
|
||||
void DebuggerParser::executeV()
|
||||
|
@ -2611,6 +2624,16 @@ DebuggerParser::Command DebuggerParser::commands[kNumCommands] = {
|
|||
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",
|
||||
"Overflow Flag: set (0 or 1), or toggle (no arg)",
|
||||
|
|
|
@ -68,7 +68,7 @@ class DebuggerParser
|
|||
string saveScriptFile(string file);
|
||||
|
||||
private:
|
||||
enum { kNumCommands = 76 };
|
||||
enum { kNumCommands = 77 };
|
||||
|
||||
// Constants for argument processing
|
||||
enum {
|
||||
|
@ -209,6 +209,7 @@ class DebuggerParser
|
|||
void executeType();
|
||||
void executeUHex();
|
||||
void executeUndef();
|
||||
void executeUnwind();
|
||||
void executeV();
|
||||
void executeWatch();
|
||||
void executeX();
|
||||
|
|
|
@ -92,7 +92,10 @@ void DebuggerDialog::handleKeyDown(StellaKey key, StellaMod mod)
|
|||
switch(key)
|
||||
{
|
||||
case KBDK_R:
|
||||
doRewind();
|
||||
if(!instance().eventHandler().kbdShift(mod))
|
||||
doRewind();
|
||||
else
|
||||
doUnwind();
|
||||
break;
|
||||
case KBDK_S:
|
||||
doStep();
|
||||
|
@ -141,6 +144,10 @@ void DebuggerDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
doRewind();
|
||||
break;
|
||||
|
||||
case kDDUnwindCmd:
|
||||
doUnwind();
|
||||
break;
|
||||
|
||||
case kDDExitCmd:
|
||||
doExitDebugger();
|
||||
break;
|
||||
|
@ -189,6 +196,12 @@ void DebuggerDialog::doRewind()
|
|||
instance().debugger().parser().run("rewind");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::doUnwind()
|
||||
{
|
||||
instance().debugger().parser().run("unwind");
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DebuggerDialog::doExitDebugger()
|
||||
{
|
||||
|
@ -375,7 +388,7 @@ void DebuggerDialog::addRomArea()
|
|||
bwidth, bheight, "Exit", kDDExitCmd);
|
||||
|
||||
bwidth = myLFont->getStringWidth("< ") + 4;
|
||||
bheight = bheight * 5 + 4*4;
|
||||
bheight = bheight * 3 + 4 * 2;
|
||||
buttonX -= (bwidth + 5);
|
||||
buttonY = r.top + 5;
|
||||
myRewindButton =
|
||||
|
@ -383,6 +396,14 @@ void DebuggerDialog::addRomArea()
|
|||
bwidth, bheight, "<", kDDRewindCmd);
|
||||
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;
|
||||
DataGridOpsWidget* ops = new DataGridOpsWidget(this, *myLFont, xpos, ypos);
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ class DebuggerDialog : public Dialog
|
|||
CartRamWidget& cartRam() const { return *myCartRam; }
|
||||
EditTextWidget& message() const { return *myMessageBox; }
|
||||
ButtonWidget& rewindButton() const { return *myRewindButton; }
|
||||
ButtonWidget& unwindButton() const { return *myUnwindButton; }
|
||||
|
||||
void showFatalMessage(const string& msg);
|
||||
|
||||
|
@ -75,6 +76,7 @@ class DebuggerDialog : public Dialog
|
|||
void doScanlineAdvance();
|
||||
void doAdvance();
|
||||
void doRewind();
|
||||
void doUnwind();
|
||||
void doExitDebugger();
|
||||
void doExitRom();
|
||||
|
||||
|
@ -96,6 +98,7 @@ class DebuggerDialog : public Dialog
|
|||
kDDAdvCmd = 'DDav',
|
||||
kDDSAdvCmd = 'DDsv',
|
||||
kDDRewindCmd = 'DDrw',
|
||||
kDDUnwindCmd = 'DDuw',
|
||||
kDDExitCmd = 'DDex',
|
||||
kDDExitFatalCmd = 'DDer'
|
||||
};
|
||||
|
@ -113,6 +116,7 @@ class DebuggerDialog : public Dialog
|
|||
CartRamWidget* myCartRam;
|
||||
EditTextWidget* myMessageBox;
|
||||
ButtonWidget* myRewindButton;
|
||||
ButtonWidget* myUnwindButton;
|
||||
unique_ptr<GUI::MessageBox> myFatalError;
|
||||
|
||||
unique_ptr<GUI::Font> myLFont; // used for labels
|
||||
|
|
Loading…
Reference in New Issue