diff --git a/gui/qt/debugger.cpp b/gui/qt/debugger.cpp index 946a54da..655a33e2 100644 --- a/gui/qt/debugger.cpp +++ b/gui/qt/debugger.cpp @@ -110,6 +110,9 @@ void MainWindow::debugStep(int mode) { } else { disasm.base = static_cast(cpu.registers.PC); disasmGet(true); + + m_stepCtx.active = true; + m_stepCtx.seqNext = static_cast(disasm.next); debug_step(mode, static_cast(disasm.next)); } emu.resume(); @@ -392,6 +395,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) { if (reason == DBG_READY) { guiReset = false; + navDisasmClear(); emu.resume(); return; } @@ -850,6 +854,17 @@ void MainWindow::debugPopulate() { osUpdate(); stackUpdate(); disasmUpdateAddr(m_prevDisasmAddr = cpu.registers.PC, true); + // Track step navigation: append on control-flow (branch taken), + // replace on linear advance. Non-step stops do not modify history + if (m_stepCtx.active) { + bool tookBranch = (static_cast(cpu.registers.PC) != m_stepCtx.seqNext); + if (tookBranch) { + navDisasmPush(m_prevDisasmAddr, true); + } else { + navDisasmReplace(m_prevDisasmAddr, true); + } + m_stepCtx.active = false; + } memUpdate(); @@ -1964,6 +1979,98 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) { connect(m_disasm->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::disasmScroll); } +// ------------------------------------------------ +// Disassembly navigation history helpers +// ------------------------------------------------ + +uint32_t MainWindow::currentDisasmAddress() const { + if (m_prevDisasmAddr) { + return m_prevDisasmAddr; + } + QString sel = m_disasm ? m_disasm->getSelectedAddr() : QString(); + if (!sel.isEmpty()) { + return static_cast(hex2int(sel)); + } + return cpu.registers.PC; +} + +void MainWindow::navDisasmEnsureSeeded() { + if (m_disasmNavIndex == -1) { + m_disasmNav.reserve(kMaxDisasmHistory); + // seed with the last PC location and current pane mode so that fully backing out returns to the same stop context + m_disasmNav.push_back({ currentDisasmAddress(), m_disasmPane }); + m_disasmNavIndex = 0; + } +} + +void MainWindow::navDisasmPush(uint32_t addr, bool pane) { + if (m_isApplyingDisasmNav) { + disasmUpdateAddr(static_cast(addr), pane); + return; + } + if (m_disasmNavIndex >= 0 && m_disasmNavIndex < m_disasmNav.size()) { + const DisasmNavEntry &cur = m_disasmNav[m_disasmNavIndex]; + if (cur.addr == addr && cur.pane == pane) { + disasmUpdateAddr(static_cast(addr), pane); + return; + } + } + if (m_disasmNavIndex + 1 < m_disasmNav.size()) { + m_disasmNav.resize(m_disasmNavIndex + 1); + } + if (m_disasmNav.size() >= kMaxDisasmHistory) { + m_disasmNav.remove(0); + if (m_disasmNavIndex > 0) { --m_disasmNavIndex; } + } + m_disasmNav.push_back({addr, pane}); + m_disasmNavIndex = m_disasmNav.size() - 1; + m_isApplyingDisasmNav = true; + disasmUpdateAddr(static_cast(addr), pane); + m_isApplyingDisasmNav = false; +} + +void MainWindow::navDisasmReplace(uint32_t addr, bool pane) { + if (m_isApplyingDisasmNav) { + return; + } + navDisasmEnsureSeeded(); + if (m_disasmNavIndex < 0) { + m_disasmNav.push_back({addr, pane}); + m_disasmNavIndex = m_disasmNav.size() - 1; + } else { + m_disasmNav[m_disasmNavIndex] = {addr, pane}; + } +} + +bool MainWindow::navDisasmBack() { + if (m_disasmNavIndex > 0) { + --m_disasmNavIndex; + const auto &e = m_disasmNav[m_disasmNavIndex]; + m_isApplyingDisasmNav = true; + disasmUpdateAddr(static_cast(e.addr), e.pane); + m_isApplyingDisasmNav = false; + return true; + } + return false; +} + +bool MainWindow::navDisasmForward() { + if (m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size()) { + ++m_disasmNavIndex; + const auto &e = m_disasmNav[m_disasmNavIndex]; + m_isApplyingDisasmNav = true; + disasmUpdateAddr(static_cast(e.addr), e.pane); + m_isApplyingDisasmNav = false; + return true; + } + return false; +} + +void MainWindow::navDisasmClear() { + m_disasmNav.clear(); + m_disasmNavIndex = -1; +} + // ------------------------------------------------ // Misc // ------------------------------------------------ @@ -1993,7 +2100,8 @@ void MainWindow::gotoPressed() { } void MainWindow::gotoDisasmAddr(uint32_t address) { - disasmUpdateAddr(address, false); + navDisasmEnsureSeeded(); + navDisasmPush(address, false); raiseContainingDock(ui->disasm); ui->disasm->setFocus(); } @@ -2111,6 +2219,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) { return QMainWindow::eventFilter(obj, e); } + // Mouse back/forward in Disassembly view + if (obj == m_disasm && e->type() == QEvent::MouseButtonPress) { + auto *me = static_cast(e); + if (me->button() == Qt::BackButton) { + if (navDisasmBack()) { e->accept(); return true; } + } else if (me->button() == Qt::ForwardButton) { + if (navDisasmForward()) { e->accept(); return true; } + } + } + if (e->type() == QEvent::MouseButtonPress) { QString name = obj->objectName(); diff --git a/gui/qt/mainwindow.cpp b/gui/qt/mainwindow.cpp index c01248a7..6cdb83b0 100644 --- a/gui/qt/mainwindow.cpp +++ b/gui/qt/mainwindow.cpp @@ -97,6 +97,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U m_breakpoints = ui->breakpoints; m_ports = ui->ports; m_disasm = ui->disasm; + m_disasm->installEventFilter(this); ui->console->setMaximumBlockCount(1000); @@ -518,6 +519,11 @@ 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_shortcutNavBack = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Left), this); + m_shortcutNavForward = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Right), this); + + connect(m_shortcutNavBack, &QShortcut::activated, this, [this]{ navDisasmBack(); }); + connect(m_shortcutNavForward, &QShortcut::activated, this, [this]{ navDisasmForward(); }); m_shortcutDebug = new QShortcut(QKeySequence(Qt::Key_F10), this); m_shortcutFullscreen = new QShortcut(QKeySequence(Qt::Key_F11), this); m_shortcutAsm = new QShortcut(QKeySequence(Qt::Key_Pause), this); @@ -2695,6 +2701,11 @@ void MainWindow::contextDisasm(const QPoint &posa) { uint32_t addr = static_cast(hex2int(addrStr)); QMenu menu; + QAction *backAct = menu.addAction(tr("Back")); + QAction *fwdAct = menu.addAction(tr("Forward")); + backAct->setEnabled(m_disasmNavIndex > 0); + fwdAct->setEnabled(m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size()); + menu.addSeparator(); QAction *runUntil = menu.addAction(ACTION_RUN_UNTIL); menu.addSeparator(); QAction *toggleBreak = menu.addAction(ACTION_TOGGLE_BREAK); @@ -2706,7 +2717,11 @@ void MainWindow::contextDisasm(const QPoint &posa) { QAction *setPc = menu.addAction(tr("Set PC")); QAction *item = menu.exec(globalPos); - if (item == setPc) { + if (item == backAct) { + navDisasmBack(); + } else if (item == fwdAct) { + navDisasmForward(); + } else if (item == setPc) { ui->pcregView->setText(addrStr); debug_set_pc(addr); disasmUpdateAddr(static_cast(cpu.registers.PC), true); diff --git a/gui/qt/mainwindow.h b/gui/qt/mainwindow.h index 5dbb1064..affc1f01 100644 --- a/gui/qt/mainwindow.h +++ b/gui/qt/mainwindow.h @@ -706,6 +706,8 @@ private slots: QShortcut *m_shortcutStepOver; QShortcut *m_shortcutStepNext; QShortcut *m_shortcutStepOut; + QShortcut *m_shortcutNavBack; + QShortcut *m_shortcutNavForward; QShortcut *m_shortcutDebug; QShortcut *m_shortcutFullscreen; QShortcut *m_shortcutAsm; @@ -937,6 +939,29 @@ private slots: QTableWidget *m_ports = nullptr; DataWidget *m_disasm = nullptr; + struct DisasmNavEntry { + uint32_t addr; + bool pane; + }; + + QVector m_disasmNav; + int m_disasmNavIndex = -1; + bool m_isApplyingDisasmNav = false; + static constexpr int kMaxDisasmHistory = 200; + + [[nodiscard]] uint32_t currentDisasmAddress() const; + void navDisasmEnsureSeeded(); + void navDisasmPush(uint32_t addr, bool pane); + void navDisasmReplace(uint32_t addr, bool pane); + bool navDisasmBack(); + bool navDisasmForward(); + void navDisasmClear(); + + struct StepNavCtx { + bool active = false; + uint32_t seqNext = 0; // sequential next PC at step start + } m_stepCtx; + #ifdef LIBUSB_SUPPORT libusb_context *m_usbContext = nullptr; libusb_hotplug_callback_handle m_usbHotplugCallbackHandle{};