From 777c29623f7ff5983230e2de08fa5a079d57b39a Mon Sep 17 00:00:00 2001 From: Sightem Date: Wed, 15 Oct 2025 15:04:06 -0700 Subject: [PATCH 1/3] Implement execute until return --- core/cpu.c | 22 +++++++++++++++++++++ core/debug/debug.c | 7 +++++++ core/debug/debug.h | 3 +++ gui/qt/debugger.cpp | 15 ++++++++++++++ gui/qt/mainwindow.cpp | 3 +++ gui/qt/mainwindow.h | 2 ++ gui/qt/mainwindow.ui | 29 +++++++++++++++++++++++++--- gui/qt/resources.qrc | 1 + gui/qt/resources/icons/stepout.png | Bin 482 -> 3516 bytes gui/qt/resources/icons/untilret.png | Bin 0 -> 3664 bytes 10 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 gui/qt/resources/icons/untilret.png diff --git a/core/cpu.c b/core/cpu.c index e7139b5b3..f33d25d96 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -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.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; @@ -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; @@ -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 */ @@ -1485,6 +1504,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 */ diff --git a/core/debug/debug.c b/core/debug/debug.c index 204e6b244..df9beaa4d 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -312,6 +312,11 @@ 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); + break; case DBG_BASIC_STEP_IN: case DBG_BASIC_STEP_NEXT: gui_debug_close(); @@ -328,6 +333,8 @@ 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; } void debug_clear_basic_step(void) { diff --git a/core/debug/debug.h b/core/debug/debug.h index a3ad09991..f7711930c 100644 --- a/core/debug/debug.h +++ b/core/debug/debug.h @@ -145,6 +145,8 @@ typedef struct { int64_t flashDelayCycles; bool step, stepOver; uint32_t tempExec, stepOut; + bool untilRet; + uint32_t untilRetBase; /* normalized 24bit stack pointer baseline */ uint32_t stackIndex, stackSize; debug_stack_entry_t *stack; @@ -179,6 +181,7 @@ enum { DBG_STEP_OVER, DBG_STEP_NEXT, DBG_RUN_UNTIL, + DBG_UNTIL_RET, DBG_BASIC_STEP_IN, DBG_BASIC_STEP_NEXT, }; diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 7ded76e49..40f20b29a 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -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(cpu.registers.PC); disasmGet(true); @@ -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); @@ -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 //------------------------------------------------ diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index 8e44ae8d8..6fa28f417 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -170,6 +170,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U 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); @@ -521,6 +522,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U 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); @@ -547,6 +549,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U 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); diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index dc13adf45..99d94f567 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -354,6 +354,7 @@ private slots: void stepOver(); void stepNext(); void stepOut(); + void stepUntilRet(); // os view void osUpdate(); @@ -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; diff --git a/gui/qt/mainwindow.ui b/gui/qt/mainwindow.ui index 9f8b5b9fc..5e63aa886 100644 --- a/gui/qt/mainwindow.ui +++ b/gui/qt/mainwindow.ui @@ -532,7 +532,7 @@ - + false @@ -552,8 +552,31 @@ :/icons/resources/icons/stepout.png:/icons/resources/icons/stepout.png - - + + + + + + false + + + + 0 + 0 + + + + Qt::NoFocus + + + Until RET + + + + :/icons/resources/icons/untilret.png:/icons/resources/icons/untilret.png + + + diff --git a/gui/qt/resources.qrc b/gui/qt/resources.qrc index b3b8a4391..45cf3c48c 100644 --- a/gui/qt/resources.qrc +++ b/gui/qt/resources.qrc @@ -72,6 +72,7 @@ resources/icons/stepout.png resources/icons/stepover.png resources/icons/stop.png + resources/icons/untilret.png resources/icons/timers.png resources/icons/toggle_console.png resources/icons/ui_edit.png diff --git a/gui/qt/resources/icons/stepout.png b/gui/qt/resources/icons/stepout.png index bfb2f4d0e39a9ebe78e0f591fa35fee22271423a..e29e6f97f5d5544da3db1b3ae2f827e887b8a784 100755 GIT binary patch delta 3515 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 diff --git a/gui/qt/resources/icons/untilret.png b/gui/qt/resources/icons/untilret.png new file mode 100644 index 0000000000000000000000000000000000000000..0c955bfc789f0d03ef48e767fef483198db41c6f GIT binary patch literal 3664 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 iIK>#XLE85S5AY9rpH$!-ZA^mz0000 Date: Fri, 17 Oct 2025 14:02:55 -0700 Subject: [PATCH 2/3] support JP (HL/IX/IY) tail calls --- core/cpu.c | 15 +++++++++++++-- core/debug/debug.c | 40 ++++++++++++++++++++++++++++++++++++++++ core/debug/debug.h | 2 ++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/core/cpu.c b/core/cpu.c index f33d25d96..3a4eb9f97 100644 --- a/core/cpu.c +++ b/core/cpu.c @@ -60,7 +60,7 @@ 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.ADL); + 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); } @@ -1236,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; diff --git a/core/debug/debug.c b/core/debug/debug.c index df9beaa4d..4c186dd94 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -316,6 +316,7 @@ void debug_step(int mode, uint32_t addr) { 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: @@ -335,6 +336,45 @@ void debug_clear_step(void) { debug.tempExec = debug.stepOut = ~0u; debug.untilRet = false; debug.untilRetBase = 0; + debug.untilRetIndex = 0; +} + +void debug_until_ret_handle_indirect_jump(uint32_t target, uint32_t currentSp) { + /* stop if this JP(rr) is an actual return for a + * recorded call frame, where target matches its retAddr and + * SP has been restored to that frame's precall stack + * value (or frame was detected popped) */ + uint32_t idx = debug.stackIndex; + uint32_t sz = debug.stackSize; + debug_stack_entry_t *hit = NULL; + uint32_t hit_idx = ~0u; + + while (sz--) { + debug_stack_entry_t *entry = &debug.stack[idx]; + const uint32_t this_idx = idx; + idx = (idx - 1) & DBG_STACK_MASK; + if (entry->mode == cpu.L && + /* only consider frames above current SP baseline. target + * must also match the frame's retAddr window */ + entry->stack >= debug.untilRetBase && + (target - entry->retAddr) <= entry->range) { + hit = entry; + hit_idx = this_idx; + break; + } + } + + /* break if we are returning from the same frame + * where DBG_UNTIL_RET started */ + if (hit && hit_idx == debug.untilRetIndex && + (currentSp == hit->stack || hit->popped)) { + 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) { diff --git a/core/debug/debug.h b/core/debug/debug.h index f7711930c..5dfd6b406 100644 --- a/core/debug/debug.h +++ b/core/debug/debug.h @@ -147,6 +147,7 @@ typedef struct { 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; @@ -190,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 */ From b6b6774596da9275acf571aa54805cb55572f5f1 Mon Sep 17 00:00:00 2001 From: Sightem Date: Sun, 19 Oct 2025 12:11:09 -0700 Subject: [PATCH 3/3] simplify debug_until_ret_handle_indirect_jump --- core/debug/debug.c | 53 +++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 34 deletions(-) diff --git a/core/debug/debug.c b/core/debug/debug.c index 4c186dd94..b2e431bf1 100644 --- a/core/debug/debug.c +++ b/core/debug/debug.c @@ -339,42 +339,27 @@ void debug_clear_step(void) { debug.untilRetIndex = 0; } -void debug_until_ret_handle_indirect_jump(uint32_t target, uint32_t currentSp) { - /* stop if this JP(rr) is an actual return for a - * recorded call frame, where target matches its retAddr and - * SP has been restored to that frame's precall stack - * value (or frame was detected popped) */ - uint32_t idx = debug.stackIndex; - uint32_t sz = debug.stackSize; - debug_stack_entry_t *hit = NULL; - uint32_t hit_idx = ~0u; - - while (sz--) { - debug_stack_entry_t *entry = &debug.stack[idx]; - const uint32_t this_idx = idx; - idx = (idx - 1) & DBG_STACK_MASK; - if (entry->mode == cpu.L && - /* only consider frames above current SP baseline. target - * must also match the frame's retAddr window */ - entry->stack >= debug.untilRetBase && - (target - entry->retAddr) <= entry->range) { - hit = entry; - hit_idx = this_idx; - break; - } +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; } - /* break if we are returning from the same frame - * where DBG_UNTIL_RET started */ - if (hit && hit_idx == debug.untilRetIndex && - (currentSp == hit->stack || hit->popped)) { - 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); - } + 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) {