4040#include < QtGui/QWindow>
4141#include < QtGui/QScreen>
4242#include < algorithm>
43+ #include < array>
4344
4445#ifdef _MSC_VER
4546 #include < direct.h>
5152const QString MainWindow::DEBUG_UNSET_ADDR = QStringLiteral(" XXXXXX" );
5253const QString MainWindow::DEBUG_UNSET_PORT = QStringLiteral(" XXXX" );
5354
55+ namespace {
56+ constexpr uint8_t OP_CALL = 0xCD ;
57+ constexpr uint8_t OP_RET = 0xC9 ;
58+ constexpr uint8_t OP_JP_HL = 0xE9 ;
59+ constexpr uint8_t OP_PREFIX_ED = 0xED ;
60+ constexpr uint8_t OP_PREFIX_DD = 0xDD ;
61+ constexpr uint8_t OP_PREFIX_FD = 0xFD ;
62+ constexpr uint8_t OP_DJNZ = 0x10 ;
63+ constexpr uint8_t OP_JR = 0x18 ;
64+ constexpr uint8_t OP_JR_NZ = 0x20 ;
65+ constexpr uint8_t OP_JR_Z = 0x28 ;
66+ constexpr uint8_t OP_JR_NC = 0x30 ;
67+ constexpr uint8_t OP_JR_C = 0x38 ;
68+ constexpr uint8_t OP_JP = 0xC3 ;
69+
70+ constexpr uint8_t OP_RETN_ED = 0x45 ;
71+ constexpr uint8_t OP_RETI_ED = 0x4D ;
72+
73+ constexpr uint8_t CF_NONE = 0 ;
74+ constexpr uint8_t CF_CALL = 1u << 0 ;
75+ constexpr uint8_t CF_RET = 1u << 1 ;
76+ constexpr uint8_t CF_JUMP = 1u << 2 ;
77+ constexpr uint8_t CF_RST = 1u << 3 ;
78+
79+ constexpr auto kCtrlLut = []{
80+ std::array<uint8_t , 256 > lut{};
81+ // CALL nn and CALL cc,nn
82+ lut[OP_CALL] |= CF_CALL; lut[0xC4 ] |= CF_CALL; lut[0xCC ] |= CF_CALL; lut[0xD4 ] |= CF_CALL; lut[0xDC ] |= CF_CALL;
83+ lut[0xE4 ] |= CF_CALL; lut[0xEC ] |= CF_CALL; lut[0xF4 ] |= CF_CALL; lut[0xFC ] |= CF_CALL;
84+ // RET
85+ lut[OP_RET] |= CF_RET;
86+ // JP nn and JP cc, nn
87+ lut[OP_JP] |= CF_JUMP;
88+ lut[0xC2 ] |= CF_JUMP; lut[0xCA ] |= CF_JUMP; lut[0xD2 ] |= CF_JUMP; lut[0xDA ] |= CF_JUMP;
89+ lut[0xE2 ] |= CF_JUMP; lut[0xEA ] |= CF_JUMP; lut[0xF2 ] |= CF_JUMP; lut[0xFA ] |= CF_JUMP;
90+ // JR e and JR cc,e
91+ lut[OP_JR] |= CF_JUMP; lut[OP_JR_NZ] |= CF_JUMP; lut[OP_JR_Z] |= CF_JUMP; lut[OP_JR_NC] |= CF_JUMP; lut[OP_JR_C] |= CF_JUMP;
92+ // DJNZ
93+ lut[OP_DJNZ] |= CF_JUMP;
94+ // JP (HL)
95+ lut[OP_JP_HL] |= CF_JUMP;
96+ // RST t (C7, CF, D7, DF, E7, EF, F7, FF)
97+ lut[0xC7 ] |= CF_RST; lut[0xCF ] |= CF_RST; lut[0xD7 ] |= CF_RST; lut[0xDF ] |= CF_RST;
98+ lut[0xE7 ] |= CF_RST; lut[0xEF ] |= CF_RST; lut[0xF7 ] |= CF_RST; lut[0xFF ] |= CF_RST;
99+ return lut;
100+ }();
101+
102+ bool isCtrlFlowOpcode (uint8_t b0, uint8_t b1) {
103+ if (kCtrlLut [b0] != CF_NONE) { return true ; }
104+ // RETN/RETI
105+ if (b0 == OP_PREFIX_ED && (b1 == OP_RETN_ED || b1 == OP_RETI_ED)) { return true ; }
106+ // JP (IX)/(IY)
107+ if ((b0 == OP_PREFIX_DD || b0 == OP_PREFIX_FD) && b1 == OP_JP_HL) { return true ; }
108+ return false ;
109+ }
110+ }
111+
54112// -----------------------------------------------
55113// Debugger Initialization
56114// -----------------------------------------------
@@ -95,6 +153,10 @@ void MainWindow::debugInit() {
95153// ------------------------------------------------
96154
97155void MainWindow::debugDisable () {
156+ if (m_suppressDebugCloseOnce) {
157+ m_suppressDebugCloseOnce = false ;
158+ return ;
159+ }
98160 guiDebug = false ;
99161 debugGuiState (false );
100162}
@@ -110,6 +172,19 @@ void MainWindow::debugStep(int mode) {
110172 } else {
111173 disasm.base = static_cast <int32_t >(cpu.registers .PC );
112174 disasmGet (true );
175+
176+ m_stepCtx.active = true ;
177+ m_stepCtx.seqNext = static_cast <uint32_t >(disasm.next );
178+
179+ if (mode == DBG_STEP_OVER) {
180+ const uint32_t pc0 = cpu.registers .PC ;
181+ const uint8_t b0 = mem_peek_byte (pc0);
182+ const uint8_t b1 = mem_peek_byte (pc0 + 1 );
183+ if (!isCtrlFlowOpcode (b0, b1)) {
184+ mode = DBG_STEP_NEXT;
185+ m_suppressDebugCloseOnce = true ;
186+ }
187+ }
113188 debug_step (mode, static_cast <uint32_t >(disasm.next ));
114189 }
115190 emu.resume ();
@@ -392,6 +467,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) {
392467
393468 if (reason == DBG_READY) {
394469 guiReset = false ;
470+ navDisasmClear ();
395471 emu.resume ();
396472 return ;
397473 }
@@ -850,6 +926,17 @@ void MainWindow::debugPopulate() {
850926 osUpdate ();
851927 stackUpdate ();
852928 disasmUpdateAddr (m_prevDisasmAddr = cpu.registers .PC , true );
929+ // Track step navigation: append on control-flow (branch taken),
930+ // replace on linear advance. Non-step stops do not modify history
931+ if (m_stepCtx.active ) {
932+ bool tookBranch = (static_cast <uint32_t >(cpu.registers .PC ) != m_stepCtx.seqNext );
933+ if (tookBranch) {
934+ navDisasmPush (m_prevDisasmAddr, true );
935+ } else {
936+ navDisasmReplace (m_prevDisasmAddr, true );
937+ }
938+ m_stepCtx.active = false ;
939+ }
853940
854941 memUpdate ();
855942
@@ -1964,6 +2051,98 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) {
19642051 connect (m_disasm->verticalScrollBar (), &QScrollBar::valueChanged, this , &MainWindow::disasmScroll);
19652052}
19662053
2054+ // ------------------------------------------------
2055+ // Disassembly navigation history helpers
2056+ // ------------------------------------------------
2057+
2058+ uint32_t MainWindow::currentDisasmAddress () const {
2059+ if (m_prevDisasmAddr) {
2060+ return m_prevDisasmAddr;
2061+ }
2062+ QString sel = m_disasm ? m_disasm->getSelectedAddr () : QString ();
2063+ if (!sel.isEmpty ()) {
2064+ return static_cast <uint32_t >(hex2int (sel));
2065+ }
2066+ return cpu.registers .PC ;
2067+ }
2068+
2069+ void MainWindow::navDisasmEnsureSeeded () {
2070+ if (m_disasmNavIndex == -1 ) {
2071+ m_disasmNav.reserve (kMaxDisasmHistory );
2072+ // seed with the last PC location and current pane mode so that fully backing out returns to the same stop context
2073+ m_disasmNav.push_back ({ currentDisasmAddress (), m_disasmPane });
2074+ m_disasmNavIndex = 0 ;
2075+ }
2076+ }
2077+
2078+ void MainWindow::navDisasmPush (uint32_t addr, bool pane) {
2079+ if (m_isApplyingDisasmNav) {
2080+ disasmUpdateAddr (static_cast <int >(addr), pane);
2081+ return ;
2082+ }
2083+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex < m_disasmNav.size ()) {
2084+ const DisasmNavEntry &cur = m_disasmNav[m_disasmNavIndex];
2085+ if (cur.addr == addr && cur.pane == pane) {
2086+ disasmUpdateAddr (static_cast <int >(addr), pane);
2087+ return ;
2088+ }
2089+ }
2090+ if (m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2091+ m_disasmNav.resize (m_disasmNavIndex + 1 );
2092+ }
2093+ if (m_disasmNav.size () >= kMaxDisasmHistory ) {
2094+ m_disasmNav.remove (0 );
2095+ if (m_disasmNavIndex > 0 ) { --m_disasmNavIndex; }
2096+ }
2097+ m_disasmNav.push_back ({addr, pane});
2098+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2099+ m_isApplyingDisasmNav = true ;
2100+ disasmUpdateAddr (static_cast <int >(addr), pane);
2101+ m_isApplyingDisasmNav = false ;
2102+ }
2103+
2104+ void MainWindow::navDisasmReplace (uint32_t addr, bool pane) {
2105+ if (m_isApplyingDisasmNav) {
2106+ return ;
2107+ }
2108+ navDisasmEnsureSeeded ();
2109+ if (m_disasmNavIndex < 0 ) {
2110+ m_disasmNav.push_back ({addr, pane});
2111+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2112+ } else {
2113+ m_disasmNav[m_disasmNavIndex] = {addr, pane};
2114+ }
2115+ }
2116+
2117+ bool MainWindow::navDisasmBack () {
2118+ if (m_disasmNavIndex > 0 ) {
2119+ --m_disasmNavIndex;
2120+ const auto &e = m_disasmNav[m_disasmNavIndex];
2121+ m_isApplyingDisasmNav = true ;
2122+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2123+ m_isApplyingDisasmNav = false ;
2124+ return true ;
2125+ }
2126+ return false ;
2127+ }
2128+
2129+ bool MainWindow::navDisasmForward () {
2130+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2131+ ++m_disasmNavIndex;
2132+ const auto &e = m_disasmNav[m_disasmNavIndex];
2133+ m_isApplyingDisasmNav = true ;
2134+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2135+ m_isApplyingDisasmNav = false ;
2136+ return true ;
2137+ }
2138+ return false ;
2139+ }
2140+
2141+ void MainWindow::navDisasmClear () {
2142+ m_disasmNav.clear ();
2143+ m_disasmNavIndex = -1 ;
2144+ }
2145+
19672146// ------------------------------------------------
19682147// Misc
19692148// ------------------------------------------------
@@ -1993,7 +2172,8 @@ void MainWindow::gotoPressed() {
19932172}
19942173
19952174void MainWindow::gotoDisasmAddr (uint32_t address) {
1996- disasmUpdateAddr (address, false );
2175+ navDisasmEnsureSeeded ();
2176+ navDisasmPush (address, false );
19972177 raiseContainingDock (ui->disasm );
19982178 ui->disasm ->setFocus ();
19992179}
@@ -2108,6 +2288,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
21082288 return QMainWindow::eventFilter (obj, e);
21092289 }
21102290
2291+ // Mouse back/forward in Disassembly view
2292+ if (obj == m_disasm && e->type () == QEvent::MouseButtonPress) {
2293+ auto *me = static_cast <QMouseEvent*>(e);
2294+ if (me->button () == Qt::BackButton) {
2295+ if (navDisasmBack ()) { e->accept (); return true ; }
2296+ } else if (me->button () == Qt::ForwardButton) {
2297+ if (navDisasmForward ()) { e->accept (); return true ; }
2298+ }
2299+ }
2300+
21112301 if (e->type () == QEvent::MouseButtonPress) {
21122302 QString name = obj->objectName ();
21132303
0 commit comments