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,104 @@ 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 static_cast <uint32_t >(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+ updateNavUiEnabledStates ();
2082+ }
2083+ }
2084+
2085+ void MainWindow::navDisasmPush (uint32_t addr, bool pane) {
2086+ if (m_isApplyingDisasmNav) {
2087+ disasmUpdateAddr (static_cast <int >(addr), pane);
2088+ return ;
2089+ }
2090+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex < m_disasmNav.size ()) {
2091+ const DisasmNavEntry &cur = m_disasmNav[m_disasmNavIndex];
2092+ if (cur.addr == addr && cur.pane == pane) {
2093+ disasmUpdateAddr (static_cast <int >(addr), pane);
2094+ return ;
2095+ }
2096+ }
2097+ if (m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2098+ m_disasmNav.resize (m_disasmNavIndex + 1 );
2099+ }
2100+ if (m_disasmNav.size () >= kMaxDisasmHistory ) {
2101+ m_disasmNav.remove (0 );
2102+ if (m_disasmNavIndex > 0 ) { --m_disasmNavIndex; }
2103+ }
2104+ m_disasmNav.push_back ({addr, pane});
2105+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2106+ m_isApplyingDisasmNav = true ;
2107+ disasmUpdateAddr (static_cast <int >(addr), pane);
2108+ m_isApplyingDisasmNav = false ;
2109+ updateNavUiEnabledStates ();
2110+ }
2111+
2112+ void MainWindow::navDisasmReplace (uint32_t addr, bool pane) {
2113+ if (m_isApplyingDisasmNav) {
2114+ return ;
2115+ }
2116+ navDisasmEnsureSeeded ();
2117+ if (m_disasmNavIndex < 0 ) {
2118+ m_disasmNav.push_back ({addr, pane});
2119+ m_disasmNavIndex = m_disasmNav.size () - 1 ;
2120+ } else {
2121+ m_disasmNav[m_disasmNavIndex] = {addr, pane};
2122+ }
2123+ updateNavUiEnabledStates ();
2124+ }
2125+
2126+ bool MainWindow::navDisasmBack () {
2127+ if (m_disasmNavIndex > 0 ) {
2128+ --m_disasmNavIndex;
2129+ const auto &e = m_disasmNav[m_disasmNavIndex];
2130+ m_isApplyingDisasmNav = true ;
2131+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2132+ m_isApplyingDisasmNav = false ;
2133+ updateNavUiEnabledStates ();
2134+ return true ;
2135+ }
2136+ return false ;
2137+ }
2138+
2139+ bool MainWindow::navDisasmForward () {
2140+ if (m_disasmNavIndex >= 0 && m_disasmNavIndex + 1 < m_disasmNav.size ()) {
2141+ ++m_disasmNavIndex;
2142+ const auto &e = m_disasmNav[m_disasmNavIndex];
2143+ m_isApplyingDisasmNav = true ;
2144+ disasmUpdateAddr (static_cast <int >(e.addr ), e.pane );
2145+ m_isApplyingDisasmNav = false ;
2146+ updateNavUiEnabledStates ();
2147+ return true ;
2148+ }
2149+ return false ;
2150+ }
2151+
2152+ void MainWindow::navDisasmClear () {
2153+ m_disasmNav.clear ();
2154+ m_disasmNavIndex = -1 ;
2155+ updateNavUiEnabledStates ();
2156+ }
2157+
19732158// ------------------------------------------------
19742159// Misc
19752160// ------------------------------------------------
@@ -1986,7 +2171,7 @@ void MainWindow::gotoPressed() {
19862171 QString resolved = resolveAddressOrEquate (typed, &ok);
19872172 if (ok) {
19882173 m_gotoAddr = typed;
1989- disasmUpdateAddr (static_cast <int >(hex2int (resolved)), false );
2174+ gotoDisasmAddr (static_cast <uint32_t >(hex2int (resolved)));
19902175
19912176 auto &hist = m_disasmGotoHistory;
19922177 hist.erase (std::remove_if (hist.begin (), hist.end (), [&](const QString &s){ return s.compare (typed, Qt::CaseInsensitive) == 0 ; }), hist.end ());
@@ -1999,7 +2184,8 @@ void MainWindow::gotoPressed() {
19992184}
20002185
20012186void MainWindow::gotoDisasmAddr (uint32_t address) {
2002- disasmUpdateAddr (address, false );
2187+ navDisasmEnsureSeeded ();
2188+ navDisasmPush (address, false );
20032189 raiseContainingDock (ui->disasm );
20042190 ui->disasm ->setFocus ();
20052191}
@@ -2114,6 +2300,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
21142300 return QMainWindow::eventFilter (obj, e);
21152301 }
21162302
2303+ // Mouse back/forward in Disassembly view
2304+ if (obj == m_disasm && e->type () == QEvent::MouseButtonPress) {
2305+ auto *me = static_cast <QMouseEvent*>(e);
2306+ if (me->button () == Qt::BackButton) {
2307+ if (navDisasmBack ()) { e->accept (); return true ; }
2308+ } else if (me->button () == Qt::ForwardButton) {
2309+ if (navDisasmForward ()) { e->accept (); return true ; }
2310+ }
2311+ }
2312+
21172313 if (e->type () == QEvent::MouseButtonPress) {
21182314 QString name = obj->objectName ();
21192315
0 commit comments