Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion core/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ static void cpu_inst_start(void) {
#endif
}

#ifdef DEBUG_SUPPORT
static void debug_break_before_ret(const uint32_t len) {
if (unlikely(debug.untilRet)) {
const uint32_t curSp = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
if (curSp >= debug.untilRetBase) {
const uint32_t start = cpu_mask_mode(cpu.registers.PC - (len + (cpu.SUFFIX ? 1u : 0u)), cpu.ADL);
cpu.registers.PC = start;
debug_open(DBG_STEP, cpu.registers.PC);
}
}
}
#endif

uint32_t cpu_address_mode(uint32_t address, bool mode) {
if (mode) {
return address & 0xFFFFFF;
Expand Down Expand Up @@ -1185,6 +1198,9 @@ void cpu_execute(void) {
cpu.cycles++;
if (cpu_read_cc(context.y)) {
r->R += 2;
#ifdef DEBUG_SUPPORT
debug_break_before_ret(1);
#endif
cpu_return();
}
break;
Expand All @@ -1204,6 +1220,9 @@ void cpu_execute(void) {
}
switch (context.p) {
case 0: /* RET */
#ifdef DEBUG_SUPPORT
debug_break_before_ret(1);
#endif
cpu_return();
break;
case 1: /* EXX */
Expand All @@ -1217,10 +1236,21 @@ void cpu_execute(void) {
REG_WRITE_EX(HL, r->HL, r->_HL);
REG_WRITE_EX(HLP, r->_HL, w);
break;
case 2: /* JP (rr) */
case 2: { /* JP (rr) */
uint32_t target = cpu_read_index();
cpu_prefetch_discard();
#ifdef DEBUG_SUPPORT
if (unlikely(debug.untilRet)) {
uint32_t curSp = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
/* if this indirect jp is a logical return for the frame
* where DBG_UNTIL_RET started, the hook rewinds PC and opens
* the debugger */
debug_until_ret_handle_indirect_jump(target, curSp);
}
#endif
cpu_jump(cpu_read_index(), cpu.L);
break;
}
case 3: /* LD SP, HL */
cpu_write_sp(cpu_read_index());
break;
Expand Down Expand Up @@ -1485,6 +1515,9 @@ void cpu_execute(void) {
/* This is actually identical to reti on the z80 */
case 1: /* RETI */
cpu.IEF1 = cpu.IEF2;
#ifdef DEBUG_SUPPORT
debug_break_before_ret(2);
#endif
cpu_return();
break;
case 2: /* LEA IY, IX + d */
Expand Down
32 changes: 32 additions & 0 deletions core/debug/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@ void debug_step(int mode, uint32_t addr) {
gui_debug_close();
debug.tempExec = addr;
break;
case DBG_UNTIL_RET:
gui_debug_close();
debug.untilRet = true;
debug.untilRetBase = cpu_address_mode(cpu.registers.stack[cpu.L].hl, cpu.L);
debug.untilRetIndex = debug.stackIndex;
break;
case DBG_BASIC_STEP_IN:
case DBG_BASIC_STEP_NEXT:
gui_debug_close();
Expand All @@ -328,6 +334,32 @@ void debug_step(int mode, uint32_t addr) {
void debug_clear_step(void) {
debug.step = debug.stepOver = false;
debug.tempExec = debug.stepOut = ~0u;
debug.untilRet = false;
debug.untilRetBase = 0;
debug.untilRetIndex = 0;
}

void debug_until_ret_handle_indirect_jump(const uint32_t target, const uint32_t currentSp) {
const debug_stack_entry_t *e = &debug.stack[debug.untilRetIndex];

if (!(e->mode == cpu.L &&
/* only consider frames above current SP baseline */
e->stack >= debug.untilRetBase &&
/* target must also match the frame's retAddr window */
(target - e->retAddr) <= e->range &&
/* SP restored to precall value, or frame popped */
(currentSp == e->stack || e->popped))) {
return;
}

const uint32_t len = 1 + (cpu.PREFIX != 0);
const uint32_t start = cpu_mask_mode(
cpu.registers.PC - (len + (cpu.SUFFIX ? 1u : 0u)),
cpu.ADL);

cpu.registers.PC = start;

debug_open(DBG_STEP, cpu.registers.PC);
}

void debug_clear_basic_step(void) {
Expand Down
5 changes: 5 additions & 0 deletions core/debug/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ typedef struct {
int64_t flashDelayCycles;
bool step, stepOver;
uint32_t tempExec, stepOut;
bool untilRet;
uint32_t untilRetBase; /* normalized 24bit stack pointer baseline */
uint32_t untilRetIndex; /* call-stack index when DBG_UNTIL_RET started */

uint32_t stackIndex, stackSize;
debug_stack_entry_t *stack;
Expand Down Expand Up @@ -179,6 +182,7 @@ enum {
DBG_STEP_OVER,
DBG_STEP_NEXT,
DBG_RUN_UNTIL,
DBG_UNTIL_RET,
DBG_BASIC_STEP_IN,
DBG_BASIC_STEP_NEXT,
};
Expand All @@ -187,6 +191,7 @@ enum {
void debug_step_switch(void);
void debug_clear_step(void);
void debug_clear_basic_step(void);
void debug_until_ret_handle_indirect_jump(uint32_t target, uint32_t currentSp);
#endif

/* register watchpoints */
Expand Down
15 changes: 15 additions & 0 deletions gui/qt/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ void MainWindow::debugEnable() {
void MainWindow::debugStep(int mode) {
if (mode == DBG_RUN_UNTIL) {
debug_step(mode, m_runUntilAddr);
} else if (mode == DBG_UNTIL_RET) {
// no address needed, cpu checks for returns internally
debug_step(mode, 0);
} else {
disasm.base = static_cast<int32_t>(cpu.registers.PC);
disasmGet(true);
Expand Down Expand Up @@ -679,6 +682,7 @@ void MainWindow::debugGuiState(bool state) const {
ui->buttonStepOver->setEnabled(state);
ui->buttonStepNext->setEnabled(state);
ui->buttonStepOut->setEnabled(state);
ui->buttonUntilRet->setEnabled(state);
ui->buttonCertID->setEnabled(state);
ui->groupCPU->setEnabled(state);
ui->groupFlags->setEnabled(state);
Expand Down Expand Up @@ -3003,6 +3007,17 @@ void MainWindow::stepOut() {
debugStep(DBG_STEP_OUT);
}

void MainWindow::stepUntilRet() {
if (!guiDebug) {
return;
}

disconnect(m_shortcutStepUntilRet, &QShortcut::activated, this, &MainWindow::stepUntilRet);

debugSync();
debugStep(DBG_UNTIL_RET);
}

//------------------------------------------------
// Other Functions
//------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions gui/qt/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@

// debug actions
connect(ui->buttonRun, &QPushButton::clicked, this, &MainWindow::debugToggle);
connect(ui->checkADLDisasm, &QCheckBox::stateChanged, this, &MainWindow::disasmUpdate);

Check warning on line 163 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 163 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 163 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 163 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
connect(ui->checkADLStack, &QCheckBox::stateChanged, this, &MainWindow::stackUpdate);

Check warning on line 164 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 164 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 164 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 164 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
connect(ui->checkADL, &QCheckBox::stateChanged, [this]{ disasmUpdate(); stackUpdate(); });

Check warning on line 165 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 165 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 165 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 165 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
connect(ui->buttonAddPort, &QPushButton::clicked, this, &MainWindow::portAddSlot);
connect(ui->buttonAddBreakpoint, &QPushButton::clicked, this, &MainWindow::breakAddSlot);
connect(ui->buttonAddWatchpoint, &QPushButton::clicked, this, &MainWindow::watchAddSlot);
Expand All @@ -170,6 +170,7 @@
connect(ui->buttonStepOver, &QPushButton::clicked, this, &MainWindow::stepOver);
connect(ui->buttonStepNext, &QPushButton::clicked, this, &MainWindow::stepNext);
connect(ui->buttonStepOut, &QPushButton::clicked, this, &MainWindow::stepOut);
connect(ui->buttonUntilRet, &QPushButton::clicked, this, &MainWindow::stepUntilRet);
connect(ui->buttonGoto, &QPushButton::clicked, this, &MainWindow::gotoPressed);
connect(ui->console, &QWidget::customContextMenuRequested, this, &MainWindow::contextConsole);
connect(m_disasm, &QWidget::customContextMenuRequested, this, &MainWindow::contextDisasm);
Expand Down Expand Up @@ -378,7 +379,7 @@
#ifdef PNG_WRITE_APNG_SUPPORTED
connect(ui->buttonRecordAnimated, &QPushButton::clicked, this, &MainWindow::recordAnimated);
connect(ui->apngSkip, &QSlider::valueChanged, this, &MainWindow::setFrameskip);
connect(ui->checkOptimizeRecording, &QCheckBox::stateChanged, this, &MainWindow::setOptimizeRecord);

Check warning on line 382 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 382 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 382 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 382 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
#else
ui->actionRecordAnimated->setEnabled(false);
ui->buttonRecordAnimated->setEnabled(false);
Expand All @@ -393,13 +394,13 @@
connect(ui->actionReportBug, &QAction::triggered, []{ QDesktopServices::openUrl(QUrl("https://github.com/CE-Programming/CEmu/issues")); });

// other gui actions
connect(ui->checkAllowGroupDrag, &QCheckBox::stateChanged, this, &MainWindow::setDockGroupDrag);

Check warning on line 397 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 397 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 397 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 397 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
connect(ui->buttonRunSetup, &QPushButton::clicked, this, &MainWindow::runSetup);
connect(ui->scaleLCD, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &MainWindow::setLcdScale);
connect(ui->upscaleLCD, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::setLcdUpscale);
connect(ui->fullscreenLCD, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::setLcdFullscreen);
connect(ui->guiSkip, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &MainWindow::setGuiSkip);
connect(ui->checkSkin, &QCheckBox::stateChanged, this, &MainWindow::setSkinToggle);

Check warning on line 403 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 403 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 403 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]

Check warning on line 403 in gui/qt/mainwindow.cpp

View workflow job for this annotation

GitHub Actions / Build: ubuntu-22.04 - x64-Dynamic

‘void QCheckBox::stateChanged(int)’ is deprecated: Use checkStateChanged() instead [-Wdeprecated-declarations]
connect(ui->comboBoxAsicRev, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &MainWindow::setAsicRevision);
connect(ui->checkPythonEdition, &QCheckBox::stateChanged, this, &MainWindow::setPythonEdition);
connect(ui->checkKeypadGhosting, &QCheckBox::stateChanged, this, &MainWindow::setKeypadGhosting);
Expand Down Expand Up @@ -521,6 +522,7 @@
m_shortcutStepOver = new QShortcut(QKeySequence(Qt::Key_F7), this);
m_shortcutStepNext = new QShortcut(QKeySequence(Qt::Key_F8), this);
m_shortcutStepOut = new QShortcut(QKeySequence(Qt::Key_F9), this);
m_shortcutStepUntilRet = new QShortcut(QKeySequence(Qt::SHIFT | Qt::Key_F9), this);
m_shortcutNavBack = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Left), this);
m_shortcutNavForward = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Right), this);

Expand All @@ -547,6 +549,7 @@
connect(m_shortcutStepOver, &QShortcut::activated, this, &MainWindow::stepOver);
connect(m_shortcutStepNext, &QShortcut::activated, this, &MainWindow::stepNext);
connect(m_shortcutStepOut, &QShortcut::activated, this, &MainWindow::stepOut);
connect(m_shortcutStepUntilRet, &QShortcut::activated, this, &MainWindow::stepUntilRet);

setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
Expand Down
2 changes: 2 additions & 0 deletions gui/qt/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ private slots:
void stepOver();
void stepNext();
void stepOut();
void stepUntilRet();

// os view
void osUpdate();
Expand Down Expand Up @@ -710,6 +711,7 @@ private slots:
QShortcut *m_shortcutStepOver;
QShortcut *m_shortcutStepNext;
QShortcut *m_shortcutStepOut;
QShortcut *m_shortcutStepUntilRet;
QShortcut *m_shortcutNavBack;
QShortcut *m_shortcutNavForward;
QShortcut *m_shortcutDebug;
Expand Down
29 changes: 26 additions & 3 deletions gui/qt/mainwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonStepOut">
<widget class="QPushButton" name="buttonStepOut">
<property name="enabled">
<bool>false</bool>
</property>
Expand All @@ -552,8 +552,31 @@
<iconset resource="resources.qrc">
<normaloff>:/icons/resources/icons/stepout.png</normaloff>:/icons/resources/icons/stepout.png</iconset>
</property>
</widget>
</item>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonUntilRet">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Until RET</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/resources/icons/untilret.png</normaloff>:/icons/resources/icons/untilret.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="buttonToggleBreakpoints">
<property name="enabled">
Expand Down
1 change: 1 addition & 0 deletions gui/qt/resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<file>resources/icons/stepout.png</file>
<file>resources/icons/stepover.png</file>
<file>resources/icons/stop.png</file>
<file>resources/icons/untilret.png</file>
<file>resources/icons/timers.png</file>
<file>resources/icons/toggle_console.png</file>
<file>resources/icons/ui_edit.png</file>
Expand Down
Binary file modified gui/qt/resources/icons/stepout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added gui/qt/resources/icons/untilret.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading