Sanitize halt and hardware state handling after CPU dispatch.

This commit is contained in:
Christian Speckner 2018-01-06 23:33:25 +01:00
parent f9d243e503
commit c0edcaf0c9
6 changed files with 68 additions and 22 deletions

View File

@ -25,6 +25,7 @@
"string": "cpp", "string": "cpp",
"string_view": "cpp", "string_view": "cpp",
"system_error": "cpp", "system_error": "cpp",
"vector": "cpp" "vector": "cpp",
"stdexcept": "cpp"
} }
} }

View File

@ -781,6 +781,19 @@ void Debugger::unlockBankswitchState()
myConsole.cartridge().unlockBank(); myConsole.cartridge().unlockBank();
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Debugger::update()
{
if (myDialog) {
myDialog->setDirty();
// loadConfig is redeclared static in DebuggerDialog, hence the cast
static_cast<Dialog*>(myDialog)->loadConfig();
myDialog->drawDialog();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Debugger::BuiltinFunction Debugger::ourBuiltinFunctions[NUM_BUILTIN_FUNCS] = { Debugger::BuiltinFunction Debugger::ourBuiltinFunctions[NUM_BUILTIN_FUNCS] = {
// left joystick: // left joystick:

View File

@ -240,6 +240,11 @@ class Debugger : public DialogContainer
void lockBankswitchState(); void lockBankswitchState();
void unlockBankswitchState(); void unlockBankswitchState();
/**
Update debugger status.
*/
void update();
private: private:
/** /**
Save state of each debugger subsystem and, by default, mark all Save state of each debugger subsystem and, by default, mark all

View File

@ -215,6 +215,30 @@ void M6502::updateStepStateByInstruction()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::execute(uInt32 number) bool M6502::execute(uInt32 number)
{
const ExecuteResult result = _execute(number);
// Debugger hack: this ensures that stepping a "STA WSYNC" will actually end at the
// beginning of the next line (otherwise, the next instruction would be stepped in order for
// the halt to take effect). This is safe because as we know that the next cycle will be a read
// cycle anyway.
handleHalt();
// Make sure that the hardware state matches the current system clock. This is necessary
// to ensure that the state is displayed correctly in the debugger. The performance impact
// on emulation is negligible as M6502::execute is called only once per frame.
mySystem->tia().updateEmulation();
mySystem->m6532().updateEmulation();
#ifdef DEBUGGER_SUPPORT
if (result == ExecuteResult::debuggerTrap && myDebugger) myDebugger->update();
#endif
return result != ExecuteResult::failure;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline M6502::ExecuteResult M6502::_execute(uInt32 number)
{ {
// Clear all of the execution status bits except for the fatal error bit // Clear all of the execution status bits except for the fatal error bit
myExecutionStatus &= FatalErrorBit; myExecutionStatus &= FatalErrorBit;
@ -236,13 +260,13 @@ bool M6502::execute(uInt32 number)
myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false; myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address, read)) if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address, read))
{ {
return true; return ExecuteResult::debuggerTrap;
} }
} }
if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC)) if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC))
if(myDebugger && myDebugger->start("BP: ", PC)) if(myDebugger && myDebugger->start("BP: ", PC))
return true; return ExecuteResult::debuggerTrap;
int cond = evalCondBreaks(); int cond = evalCondBreaks();
if(cond > -1) if(cond > -1)
@ -250,7 +274,7 @@ bool M6502::execute(uInt32 number)
stringstream msg; stringstream msg;
msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond]; msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];
if(myDebugger && myDebugger->start(msg.str())) if(myDebugger && myDebugger->start(msg.str()))
return true; return ExecuteResult::debuggerTrap;
} }
cond = evalCondSaveStates(); cond = evalCondSaveStates();
@ -303,31 +327,22 @@ bool M6502::execute(uInt32 number)
// See if execution has been stopped // See if execution has been stopped
if(myExecutionStatus & StopExecutionBit) if(myExecutionStatus & StopExecutionBit)
{ {
// Debugger hack: this ensures that stepping a "STA WSYNC" will actually end at the
// beginning of the next line (otherwise, the next instruction would be stepped in order for
// the halt to take effect). This is safe because as we know that the next cycle will be a read
// cycle anyway.
handleHalt();
// Yes, so answer that everything finished fine // Yes, so answer that everything finished fine
return true; return ExecuteResult::success;
} }
// See if a fatal error has occured // See if a fatal error has occured
if(myExecutionStatus & FatalErrorBit) if(myExecutionStatus & FatalErrorBit)
{ {
// Yes, so answer that something when wrong // Yes, so answer that something when wrong
return false; return ExecuteResult::failure;
} }
// See if we've executed the specified number of instructions // See if we've executed the specified number of instructions
if(number == 0) if(number == 0)
{ {
// See above
handleHalt();
// Yes, so answer that everything finished fine // Yes, so answer that everything finished fine
return true; return ExecuteResult::success;
} }
} }
} }

View File

@ -320,6 +320,21 @@ class M6502 : public Serializable
*/ */
void updateStepStateByInstruction(); void updateStepStateByInstruction();
/**
The dispatch result.
*/
enum ExecuteResult {
success,
failure,
debuggerTrap
};
/**
This is the actual dispatch function that does the grunt work. M6502::execute
wraps it and makes sure that any pending halt is processed before returning.
*/
ExecuteResult _execute(uInt32 number);
private: private:
/** /**
Bit fields used to indicate that certain conditions need to be Bit fields used to indicate that certain conditions need to be

View File

@ -1064,8 +1064,7 @@ TIA& TIA::updateScanline()
{ {
// Update frame by one scanline at a time // Update frame by one scanline at a time
uInt32 line = scanlines(); uInt32 line = scanlines();
while (line == scanlines() && mySystem->m6502().execute(1)) while (line == scanlines() && mySystem->m6502().execute(1));
updateEmulation();
return *this; return *this;
} }
@ -1074,8 +1073,7 @@ TIA& TIA::updateScanline()
TIA& TIA::updateScanlineByStep() TIA& TIA::updateScanlineByStep()
{ {
// Update frame by one CPU instruction/color clock // Update frame by one CPU instruction/color clock
if (mySystem->m6502().execute(1)) mySystem->m6502().execute(1);
updateEmulation();
return *this; return *this;
} }
@ -1085,8 +1083,7 @@ TIA& TIA::updateScanlineByTrace(int target)
{ {
uInt32 count = 100; // only try up to 100 steps uInt32 count = 100; // only try up to 100 steps
while (mySystem->m6502().getPC() != target && count-- && while (mySystem->m6502().getPC() != target && count-- &&
mySystem->m6502().execute(1)) mySystem->m6502().execute(1));
updateEmulation();
return *this; return *this;
} }