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
@@ -1970,6 +2057,98 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) {
19702057 connect (m_disasm->verticalScrollBar (), &QScrollBar::valueChanged, this , &MainWindow::disasmScroll);
19712058}
19722059
2060+ // ------------------------------------------------
2061+ // Disassembly navigation history helpers
2062+ // ------------------------------------------------
2063+
2064+ uint32_t MainWindow::currentDisasmAddress () const {
2065+ if (m_prevDisasmAddr) {
2066+ return m_prevDisasmAddr;
2067+ }
2068+ QString sel = m_disasm ? m_disasm->getSelectedAddr () : QString ();
2069+ if (!sel.isEmpty ()) {
2070+ return static_cast <uint32_t >(hex2int (sel));
2071+ }
2072+ return cpu.registers .PC ;
2073+ }
2074+
2075+ void MainWindow::navDisasmEnsureSeeded () {
2076+ if (m_disasmNavIndex == -1 ) {
2077+ m_disasmNav.reserve (kMaxDisasmHistory );
2078+ // seed with the last PC location and current pane mode so that fully backing out returns to the same stop context
2079+ m_disasmNav.push_back ({ currentDisasmAddress (), m_disasmPane });
2080+ m_disasmNavIndex = 0 ;
2081+ }
2082+ }
2083+
2084+ void MainWindow::navDisasmPush (uint32_t addr, bool pane) {
2085+ if (m_isApplyingDisasmNav) {
2086+ disasmUpdateAddr (static_cast <int >(addr), pane);
2087+ return ;
2088+ }
2089+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex < m_disasmNav.size ()) {
2090+ const DisasmNavEntry &cur = m_disasmNav[m_disasmNavIndex];
2091+ if (cur.addr == addr && cur.pane == pane) {
2092+ disasmUpdateAddr (static_cast <int >(addr), pane);
2093+ return ;
2094+ }
2095+ }
2096+ if (m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2097+ m_disasmNav.resize (m_disasmNavIndex + 1 );
2098+ }
2099+ if (m_disasmNav.size () >= kMaxDisasmHistory ) {
2100+ m_disasmNav.remove (0 );
2101+ if (m_disasmNavIndex > 0 ) { --m_disasmNavIndex; }
2102+ }
2103+ m_disasmNav.push_back ({addr, pane});
2104+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2105+ m_isApplyingDisasmNav = true ;
2106+ disasmUpdateAddr (static_cast <int >(addr), pane);
2107+ m_isApplyingDisasmNav = false ;
2108+ }
2109+
2110+ void MainWindow::navDisasmReplace (uint32_t addr, bool pane) {
2111+ if (m_isApplyingDisasmNav) {
2112+ return ;
2113+ }
2114+ navDisasmEnsureSeeded ();
2115+ if (m_disasmNavIndex < 0 ) {
2116+ m_disasmNav.push_back ({addr, pane});
2117+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2118+ } else {
2119+ m_disasmNav[m_disasmNavIndex] = {addr, pane};
2120+ }
2121+ }
2122+
2123+ bool MainWindow::navDisasmBack () {
2124+ if (m_disasmNavIndex > 0 ) {
2125+ --m_disasmNavIndex;
2126+ const auto &e = m_disasmNav[m_disasmNavIndex];
2127+ m_isApplyingDisasmNav = true ;
2128+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2129+ m_isApplyingDisasmNav = false ;
2130+ return true ;
2131+ }
2132+ return false ;
2133+ }
2134+
2135+ bool MainWindow::navDisasmForward () {
2136+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2137+ ++m_disasmNavIndex;
2138+ const auto &e = m_disasmNav[m_disasmNavIndex];
2139+ m_isApplyingDisasmNav = true ;
2140+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2141+ m_isApplyingDisasmNav = false ;
2142+ return true ;
2143+ }
2144+ return false ;
2145+ }
2146+
2147+ void MainWindow::navDisasmClear () {
2148+ m_disasmNav.clear ();
2149+ m_disasmNavIndex = -1 ;
2150+ }
2151+
19732152// ------------------------------------------------
19742153// Misc
19752154// ------------------------------------------------
@@ -1999,7 +2178,8 @@ void MainWindow::gotoPressed() {
19992178}
20002179
20012180void MainWindow::gotoDisasmAddr (uint32_t address) {
2002- disasmUpdateAddr (address, false );
2181+ navDisasmEnsureSeeded ();
2182+ navDisasmPush (address, false );
20032183 raiseContainingDock (ui->disasm );
20042184 ui->disasm ->setFocus ();
20052185}
@@ -2114,6 +2294,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
21142294 return QMainWindow::eventFilter (obj, e);
21152295 }
21162296
2297+ // Mouse back/forward in Disassembly view
2298+ if (obj == m_disasm && e->type () == QEvent::MouseButtonPress) {
2299+ auto *me = static_cast <QMouseEvent*>(e);
2300+ if (me->button () == Qt::BackButton) {
2301+ if (navDisasmBack ()) { e->accept (); return true ; }
2302+ } else if (me->button () == Qt::ForwardButton) {
2303+ if (navDisasmForward ()) { e->accept (); return true ; }
2304+ }
2305+ }
2306+
21172307 if (e->type () == QEvent::MouseButtonPress) {
21182308 QString name = obj->objectName ();
21192309
0 commit comments