unwind logic started, TODO: adapt RewindManager

This commit is contained in:
thrust26 2017-10-12 17:43:41 +02:00
parent cf74aa8c4d
commit 3045a0ddf7
8 changed files with 183 additions and 14 deletions

View File

@ -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();
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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)",

View File

@ -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();

View File

@ -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);

View File

@ -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