40
40
#include < QtGui/QWindow>
41
41
#include < QtGui/QScreen>
42
42
#include < algorithm>
43
+ #include < array>
43
44
44
45
#ifdef _MSC_VER
45
46
#include < direct.h>
51
52
const QString MainWindow::DEBUG_UNSET_ADDR = QStringLiteral(" XXXXXX" );
52
53
const QString MainWindow::DEBUG_UNSET_PORT = QStringLiteral(" XXXX" );
53
54
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
+
54
112
// -----------------------------------------------
55
113
// Debugger Initialization
56
114
// -----------------------------------------------
@@ -95,6 +153,10 @@ void MainWindow::debugInit() {
95
153
// ------------------------------------------------
96
154
97
155
void MainWindow::debugDisable () {
156
+ if (m_suppressDebugCloseOnce) {
157
+ m_suppressDebugCloseOnce = false ;
158
+ return ;
159
+ }
98
160
guiDebug = false ;
99
161
debugGuiState (false );
100
162
}
@@ -110,6 +172,19 @@ void MainWindow::debugStep(int mode) {
110
172
} else {
111
173
disasm.base = static_cast <int32_t >(cpu.registers .PC );
112
174
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
+ }
113
188
debug_step (mode, static_cast <uint32_t >(disasm.next ));
114
189
}
115
190
emu.resume ();
@@ -392,6 +467,7 @@ void MainWindow::debugCommand(int reason, uint32_t data) {
392
467
393
468
if (reason == DBG_READY) {
394
469
guiReset = false ;
470
+ navDisasmClear ();
395
471
emu.resume ();
396
472
return ;
397
473
}
@@ -850,6 +926,17 @@ void MainWindow::debugPopulate() {
850
926
osUpdate ();
851
927
stackUpdate ();
852
928
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
+ }
853
940
854
941
memUpdate ();
855
942
@@ -1964,6 +2051,98 @@ void MainWindow::disasmUpdateAddr(int base, bool pane) {
1964
2051
connect (m_disasm->verticalScrollBar (), &QScrollBar::valueChanged, this , &MainWindow::disasmScroll);
1965
2052
}
1966
2053
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
+
1967
2146
// ------------------------------------------------
1968
2147
// Misc
1969
2148
// ------------------------------------------------
@@ -1993,7 +2172,8 @@ void MainWindow::gotoPressed() {
1993
2172
}
1994
2173
1995
2174
void MainWindow::gotoDisasmAddr (uint32_t address) {
1996
- disasmUpdateAddr (address, false );
2175
+ navDisasmEnsureSeeded ();
2176
+ navDisasmPush (address, false );
1997
2177
raiseContainingDock (ui->disasm );
1998
2178
ui->disasm ->setFocus ();
1999
2179
}
@@ -2108,6 +2288,16 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
2108
2288
return QMainWindow::eventFilter (obj, e);
2109
2289
}
2110
2290
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
+
2111
2301
if (e->type () == QEvent::MouseButtonPress) {
2112
2302
QString name = obj->objectName ();
2113
2303
0 commit comments