Skip to content

Commit 8ffe9a6

Browse files
authored
Forward and back navigation in disasm view (#529)
implements browser style back/forward navigation in the disassembly view, accessible via Alt+Left/Alt+Right and mouse back/forward buttons.
1 parent 01d7323 commit 8ffe9a6

File tree

3 files changed

+160
-2
lines changed

3 files changed

+160
-2
lines changed

gui/qt/debugger.cpp

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ void MainWindow::debugStep(int mode) {
110110
} else {
111111
disasm.base = static_cast<int32_t>(cpu.registers.PC);
112112
disasmGet(true);
113+
114+
m_stepCtx.active = true;
115+
m_stepCtx.seqNext = static_cast<uint32_t>(disasm.next);
113116
debug_step(mode, static_cast<uint32_t>(disasm.next));
114117
}
115118
emu.resume();
@@ -392,6 +395,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) {
392395

393396
if (reason == DBG_READY) {
394397
guiReset = false;
398+
navDisasmClear();
395399
emu.resume();
396400
return;
397401
}
@@ -850,6 +854,17 @@ void MainWindow::debugPopulate() {
850854
osUpdate();
851855
stackUpdate();
852856
disasmUpdateAddr(m_prevDisasmAddr = cpu.registers.PC, true);
857+
// Track step navigation: append on control-flow (branch taken),
858+
// replace on linear advance. Non-step stops do not modify history
859+
if (m_stepCtx.active) {
860+
bool tookBranch = (static_cast<uint32_t>(cpu.registers.PC) != m_stepCtx.seqNext);
861+
if (tookBranch) {
862+
navDisasmPush(m_prevDisasmAddr, true);
863+
} else {
864+
navDisasmReplace(m_prevDisasmAddr, true);
865+
}
866+
m_stepCtx.active = false;
867+
}
853868

854869
memUpdate();
855870

@@ -1964,6 +1979,98 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) {
19641979
connect(m_disasm->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::disasmScroll);
19651980
}
19661981

1982+
// ------------------------------------------------
1983+
// Disassembly navigation history helpers
1984+
// ------------------------------------------------
1985+
1986+
uint32_t MainWindow::currentDisasmAddress() const {
1987+
if (m_prevDisasmAddr) {
1988+
return m_prevDisasmAddr;
1989+
}
1990+
QString sel = m_disasm ? m_disasm->getSelectedAddr() : QString();
1991+
if (!sel.isEmpty()) {
1992+
return static_cast<uint32_t>(hex2int(sel));
1993+
}
1994+
return cpu.registers.PC;
1995+
}
1996+
1997+
void MainWindow::navDisasmEnsureSeeded() {
1998+
if (m_disasmNavIndex == -1) {
1999+
m_disasmNav.reserve(kMaxDisasmHistory);
2000+
// seed with the last PC location and current pane mode so that fully backing out returns to the same stop context
2001+
m_disasmNav.push_back({ currentDisasmAddress(), m_disasmPane });
2002+
m_disasmNavIndex = 0;
2003+
}
2004+
}
2005+
2006+
void MainWindow::navDisasmPush(uint32_t addr, bool pane) {
2007+
if (m_isApplyingDisasmNav) {
2008+
disasmUpdateAddr(static_cast<int>(addr), pane);
2009+
return;
2010+
}
2011+
if (m_disasmNavIndex >= 0 && m_disasmNavIndex < m_disasmNav.size()) {
2012+
const DisasmNavEntry &cur = m_disasmNav[m_disasmNavIndex];
2013+
if (cur.addr == addr && cur.pane == pane) {
2014+
disasmUpdateAddr(static_cast<int>(addr), pane);
2015+
return;
2016+
}
2017+
}
2018+
if (m_disasmNavIndex + 1 < m_disasmNav.size()) {
2019+
m_disasmNav.resize(m_disasmNavIndex + 1);
2020+
}
2021+
if (m_disasmNav.size() >= kMaxDisasmHistory) {
2022+
m_disasmNav.remove(0);
2023+
if (m_disasmNavIndex > 0) { --m_disasmNavIndex; }
2024+
}
2025+
m_disasmNav.push_back({addr, pane});
2026+
m_disasmNavIndex = m_disasmNav.size() - 1;
2027+
m_isApplyingDisasmNav = true;
2028+
disasmUpdateAddr(static_cast<int>(addr), pane);
2029+
m_isApplyingDisasmNav = false;
2030+
}
2031+
2032+
void MainWindow::navDisasmReplace(uint32_t addr, bool pane) {
2033+
if (m_isApplyingDisasmNav) {
2034+
return;
2035+
}
2036+
navDisasmEnsureSeeded();
2037+
if (m_disasmNavIndex < 0) {
2038+
m_disasmNav.push_back({addr, pane});
2039+
m_disasmNavIndex = m_disasmNav.size() - 1;
2040+
} else {
2041+
m_disasmNav[m_disasmNavIndex] = {addr, pane};
2042+
}
2043+
}
2044+
2045+
bool MainWindow::navDisasmBack() {
2046+
if (m_disasmNavIndex > 0) {
2047+
--m_disasmNavIndex;
2048+
const auto &e = m_disasmNav[m_disasmNavIndex];
2049+
m_isApplyingDisasmNav = true;
2050+
disasmUpdateAddr(static_cast<int>(e.addr), e.pane);
2051+
m_isApplyingDisasmNav = false;
2052+
return true;
2053+
}
2054+
return false;
2055+
}
2056+
2057+
bool MainWindow::navDisasmForward() {
2058+
if (m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size()) {
2059+
++m_disasmNavIndex;
2060+
const auto &e = m_disasmNav[m_disasmNavIndex];
2061+
m_isApplyingDisasmNav = true;
2062+
disasmUpdateAddr(static_cast<int>(e.addr), e.pane);
2063+
m_isApplyingDisasmNav = false;
2064+
return true;
2065+
}
2066+
return false;
2067+
}
2068+
2069+
void MainWindow::navDisasmClear() {
2070+
m_disasmNav.clear();
2071+
m_disasmNavIndex = -1;
2072+
}
2073+
19672074
// ------------------------------------------------
19682075
// Misc
19692076
// ------------------------------------------------
@@ -1993,7 +2100,8 @@ void MainWindow::gotoPressed() {
19932100
}
19942101

19952102
void MainWindow::gotoDisasmAddr(uint32_t address) {
1996-
disasmUpdateAddr(address, false);
2103+
navDisasmEnsureSeeded();
2104+
navDisasmPush(address, false);
19972105
raiseContainingDock(ui->disasm);
19982106
ui->disasm->setFocus();
19992107
}
@@ -2111,6 +2219,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
21112219
return QMainWindow::eventFilter(obj, e);
21122220
}
21132221

2222+
// Mouse back/forward in Disassembly view
2223+
if (obj == m_disasm && e->type() == QEvent::MouseButtonPress) {
2224+
auto *me = static_cast<QMouseEvent*>(e);
2225+
if (me->button() == Qt::BackButton) {
2226+
if (navDisasmBack()) { e->accept(); return true; }
2227+
} else if (me->button() == Qt::ForwardButton) {
2228+
if (navDisasmForward()) { e->accept(); return true; }
2229+
}
2230+
}
2231+
21142232
if (e->type() == QEvent::MouseButtonPress) {
21152233
QString name = obj->objectName();
21162234

gui/qt/mainwindow.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U
9797
m_breakpoints = ui->breakpoints;
9898
m_ports = ui->ports;
9999
m_disasm = ui->disasm;
100+
m_disasm->installEventFilter(this);
100101

101102
ui->console->setMaximumBlockCount(1000);
102103

@@ -518,6 +519,11 @@ MainWindow::MainWindow(CEmuOpts &cliOpts, QWidget *p) : QMainWindow(p), ui(new U
518519
m_shortcutStepOver = new QShortcut(QKeySequence(Qt::Key_F7), this);
519520
m_shortcutStepNext = new QShortcut(QKeySequence(Qt::Key_F8), this);
520521
m_shortcutStepOut = new QShortcut(QKeySequence(Qt::Key_F9), this);
522+
m_shortcutNavBack = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Left), this);
523+
m_shortcutNavForward = new QShortcut(QKeySequence(Qt::ALT | Qt::Key_Right), this);
524+
525+
connect(m_shortcutNavBack, &QShortcut::activated, this, [this]{ navDisasmBack(); });
526+
connect(m_shortcutNavForward, &QShortcut::activated, this, [this]{ navDisasmForward(); });
521527
m_shortcutDebug = new QShortcut(QKeySequence(Qt::Key_F10), this);
522528
m_shortcutFullscreen = new QShortcut(QKeySequence(Qt::Key_F11), this);
523529
m_shortcutAsm = new QShortcut(QKeySequence(Qt::Key_Pause), this);
@@ -2695,6 +2701,11 @@ void MainWindow::contextDisasm(const QPoint &posa) {
26952701
uint32_t addr = static_cast<uint32_t>(hex2int(addrStr));
26962702

26972703
QMenu menu;
2704+
QAction *backAct = menu.addAction(tr("Back"));
2705+
QAction *fwdAct = menu.addAction(tr("Forward"));
2706+
backAct->setEnabled(m_disasmNavIndex > 0);
2707+
fwdAct->setEnabled(m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size());
2708+
menu.addSeparator();
26982709
QAction *runUntil = menu.addAction(ACTION_RUN_UNTIL);
26992710
menu.addSeparator();
27002711
QAction *toggleBreak = menu.addAction(ACTION_TOGGLE_BREAK);
@@ -2706,7 +2717,11 @@ void MainWindow::contextDisasm(const QPoint &posa) {
27062717
QAction *setPc = menu.addAction(tr("Set PC"));
27072718

27082719
QAction *item = menu.exec(globalPos);
2709-
if (item == setPc) {
2720+
if (item == backAct) {
2721+
navDisasmBack();
2722+
} else if (item == fwdAct) {
2723+
navDisasmForward();
2724+
} else if (item == setPc) {
27102725
ui->pcregView->setText(addrStr);
27112726
debug_set_pc(addr);
27122727
disasmUpdateAddr(static_cast<int>(cpu.registers.PC), true);

gui/qt/mainwindow.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,8 @@ private slots:
706706
QShortcut *m_shortcutStepOver;
707707
QShortcut *m_shortcutStepNext;
708708
QShortcut *m_shortcutStepOut;
709+
QShortcut *m_shortcutNavBack;
710+
QShortcut *m_shortcutNavForward;
709711
QShortcut *m_shortcutDebug;
710712
QShortcut *m_shortcutFullscreen;
711713
QShortcut *m_shortcutAsm;
@@ -937,6 +939,29 @@ private slots:
937939
QTableWidget *m_ports = nullptr;
938940
DataWidget *m_disasm = nullptr;
939941

942+
struct DisasmNavEntry {
943+
uint32_t addr;
944+
bool pane;
945+
};
946+
947+
QVector<DisasmNavEntry> m_disasmNav;
948+
int m_disasmNavIndex = -1;
949+
bool m_isApplyingDisasmNav = false;
950+
static constexpr int kMaxDisasmHistory = 200;
951+
952+
[[nodiscard]] uint32_t currentDisasmAddress() const;
953+
void navDisasmEnsureSeeded();
954+
void navDisasmPush(uint32_t addr, bool pane);
955+
void navDisasmReplace(uint32_t addr, bool pane);
956+
bool navDisasmBack();
957+
bool navDisasmForward();
958+
void navDisasmClear();
959+
960+
struct StepNavCtx {
961+
bool active = false;
962+
uint32_t seqNext = 0; // sequential next PC at step start
963+
} m_stepCtx;
964+
940965
#ifdef LIBUSB_SUPPORT
941966
libusb_context *m_usbContext = nullptr;
942967
libusb_hotplug_callback_handle m_usbHotplugCallbackHandle{};

0 commit comments

Comments
 (0)