yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
disassembler.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <iomanip>
5#include <sstream>
6
7namespace yaze {
8namespace emu {
9namespace debug {
10
12
14 // Initialize all opcodes with their mnemonics and addressing modes
15 // Format: opcode_table_[opcode] = {mnemonic, addressing_mode, base_size}
16
17 using AM = AddressingMode65816;
18
19 // Row 0x00-0x0F
20 opcode_table_[0x00] = {"BRK", AM::kImmediate8, 2};
21 opcode_table_[0x01] = {"ORA", AM::kDirectPageIndexedIndirectX, 2};
22 opcode_table_[0x02] = {"COP", AM::kImmediate8, 2};
23 opcode_table_[0x03] = {"ORA", AM::kStackRelative, 2};
24 opcode_table_[0x04] = {"TSB", AM::kDirectPage, 2};
25 opcode_table_[0x05] = {"ORA", AM::kDirectPage, 2};
26 opcode_table_[0x06] = {"ASL", AM::kDirectPage, 2};
27 opcode_table_[0x07] = {"ORA", AM::kDirectPageIndirectLong, 2};
28 opcode_table_[0x08] = {"PHP", AM::kImplied, 1};
29 opcode_table_[0x09] = {"ORA", AM::kImmediateM, 2}; // Size depends on M flag
30 opcode_table_[0x0A] = {"ASL", AM::kAccumulator, 1};
31 opcode_table_[0x0B] = {"PHD", AM::kImplied, 1};
32 opcode_table_[0x0C] = {"TSB", AM::kAbsolute, 3};
33 opcode_table_[0x0D] = {"ORA", AM::kAbsolute, 3};
34 opcode_table_[0x0E] = {"ASL", AM::kAbsolute, 3};
35 opcode_table_[0x0F] = {"ORA", AM::kAbsoluteLong, 4};
36
37 // Row 0x10-0x1F
38 opcode_table_[0x10] = {"BPL", AM::kProgramCounterRelative, 2};
39 opcode_table_[0x11] = {"ORA", AM::kDirectPageIndirectIndexedY, 2};
40 opcode_table_[0x12] = {"ORA", AM::kDirectPageIndirect, 2};
41 opcode_table_[0x13] = {"ORA", AM::kStackRelativeIndirectIndexedY, 2};
42 opcode_table_[0x14] = {"TRB", AM::kDirectPage, 2};
43 opcode_table_[0x15] = {"ORA", AM::kDirectPageIndexedX, 2};
44 opcode_table_[0x16] = {"ASL", AM::kDirectPageIndexedX, 2};
45 opcode_table_[0x17] = {"ORA", AM::kDirectPageIndirectLongIndexedY, 2};
46 opcode_table_[0x18] = {"CLC", AM::kImplied, 1};
47 opcode_table_[0x19] = {"ORA", AM::kAbsoluteIndexedY, 3};
48 opcode_table_[0x1A] = {"INC", AM::kAccumulator, 1};
49 opcode_table_[0x1B] = {"TCS", AM::kImplied, 1};
50 opcode_table_[0x1C] = {"TRB", AM::kAbsolute, 3};
51 opcode_table_[0x1D] = {"ORA", AM::kAbsoluteIndexedX, 3};
52 opcode_table_[0x1E] = {"ASL", AM::kAbsoluteIndexedX, 3};
53 opcode_table_[0x1F] = {"ORA", AM::kAbsoluteLongIndexedX, 4};
54
55 // Row 0x20-0x2F
56 opcode_table_[0x20] = {"JSR", AM::kAbsolute, 3};
57 opcode_table_[0x21] = {"AND", AM::kDirectPageIndexedIndirectX, 2};
58 opcode_table_[0x22] = {"JSL", AM::kAbsoluteLong, 4};
59 opcode_table_[0x23] = {"AND", AM::kStackRelative, 2};
60 opcode_table_[0x24] = {"BIT", AM::kDirectPage, 2};
61 opcode_table_[0x25] = {"AND", AM::kDirectPage, 2};
62 opcode_table_[0x26] = {"ROL", AM::kDirectPage, 2};
63 opcode_table_[0x27] = {"AND", AM::kDirectPageIndirectLong, 2};
64 opcode_table_[0x28] = {"PLP", AM::kImplied, 1};
65 opcode_table_[0x29] = {"AND", AM::kImmediateM, 2};
66 opcode_table_[0x2A] = {"ROL", AM::kAccumulator, 1};
67 opcode_table_[0x2B] = {"PLD", AM::kImplied, 1};
68 opcode_table_[0x2C] = {"BIT", AM::kAbsolute, 3};
69 opcode_table_[0x2D] = {"AND", AM::kAbsolute, 3};
70 opcode_table_[0x2E] = {"ROL", AM::kAbsolute, 3};
71 opcode_table_[0x2F] = {"AND", AM::kAbsoluteLong, 4};
72
73 // Row 0x30-0x3F
74 opcode_table_[0x30] = {"BMI", AM::kProgramCounterRelative, 2};
75 opcode_table_[0x31] = {"AND", AM::kDirectPageIndirectIndexedY, 2};
76 opcode_table_[0x32] = {"AND", AM::kDirectPageIndirect, 2};
77 opcode_table_[0x33] = {"AND", AM::kStackRelativeIndirectIndexedY, 2};
78 opcode_table_[0x34] = {"BIT", AM::kDirectPageIndexedX, 2};
79 opcode_table_[0x35] = {"AND", AM::kDirectPageIndexedX, 2};
80 opcode_table_[0x36] = {"ROL", AM::kDirectPageIndexedX, 2};
81 opcode_table_[0x37] = {"AND", AM::kDirectPageIndirectLongIndexedY, 2};
82 opcode_table_[0x38] = {"SEC", AM::kImplied, 1};
83 opcode_table_[0x39] = {"AND", AM::kAbsoluteIndexedY, 3};
84 opcode_table_[0x3A] = {"DEC", AM::kAccumulator, 1};
85 opcode_table_[0x3B] = {"TSC", AM::kImplied, 1};
86 opcode_table_[0x3C] = {"BIT", AM::kAbsoluteIndexedX, 3};
87 opcode_table_[0x3D] = {"AND", AM::kAbsoluteIndexedX, 3};
88 opcode_table_[0x3E] = {"ROL", AM::kAbsoluteIndexedX, 3};
89 opcode_table_[0x3F] = {"AND", AM::kAbsoluteLongIndexedX, 4};
90
91 // Row 0x40-0x4F
92 opcode_table_[0x40] = {"RTI", AM::kImplied, 1};
93 opcode_table_[0x41] = {"EOR", AM::kDirectPageIndexedIndirectX, 2};
94 opcode_table_[0x42] = {"WDM", AM::kImmediate8, 2};
95 opcode_table_[0x43] = {"EOR", AM::kStackRelative, 2};
96 opcode_table_[0x44] = {"MVP", AM::kBlockMove, 3};
97 opcode_table_[0x45] = {"EOR", AM::kDirectPage, 2};
98 opcode_table_[0x46] = {"LSR", AM::kDirectPage, 2};
99 opcode_table_[0x47] = {"EOR", AM::kDirectPageIndirectLong, 2};
100 opcode_table_[0x48] = {"PHA", AM::kImplied, 1};
101 opcode_table_[0x49] = {"EOR", AM::kImmediateM, 2};
102 opcode_table_[0x4A] = {"LSR", AM::kAccumulator, 1};
103 opcode_table_[0x4B] = {"PHK", AM::kImplied, 1};
104 opcode_table_[0x4C] = {"JMP", AM::kAbsolute, 3};
105 opcode_table_[0x4D] = {"EOR", AM::kAbsolute, 3};
106 opcode_table_[0x4E] = {"LSR", AM::kAbsolute, 3};
107 opcode_table_[0x4F] = {"EOR", AM::kAbsoluteLong, 4};
108
109 // Row 0x50-0x5F
110 opcode_table_[0x50] = {"BVC", AM::kProgramCounterRelative, 2};
111 opcode_table_[0x51] = {"EOR", AM::kDirectPageIndirectIndexedY, 2};
112 opcode_table_[0x52] = {"EOR", AM::kDirectPageIndirect, 2};
113 opcode_table_[0x53] = {"EOR", AM::kStackRelativeIndirectIndexedY, 2};
114 opcode_table_[0x54] = {"MVN", AM::kBlockMove, 3};
115 opcode_table_[0x55] = {"EOR", AM::kDirectPageIndexedX, 2};
116 opcode_table_[0x56] = {"LSR", AM::kDirectPageIndexedX, 2};
117 opcode_table_[0x57] = {"EOR", AM::kDirectPageIndirectLongIndexedY, 2};
118 opcode_table_[0x58] = {"CLI", AM::kImplied, 1};
119 opcode_table_[0x59] = {"EOR", AM::kAbsoluteIndexedY, 3};
120 opcode_table_[0x5A] = {"PHY", AM::kImplied, 1};
121 opcode_table_[0x5B] = {"TCD", AM::kImplied, 1};
122 opcode_table_[0x5C] = {"JMP", AM::kAbsoluteLong, 4};
123 opcode_table_[0x5D] = {"EOR", AM::kAbsoluteIndexedX, 3};
124 opcode_table_[0x5E] = {"LSR", AM::kAbsoluteIndexedX, 3};
125 opcode_table_[0x5F] = {"EOR", AM::kAbsoluteLongIndexedX, 4};
126
127 // Row 0x60-0x6F
128 opcode_table_[0x60] = {"RTS", AM::kImplied, 1};
129 opcode_table_[0x61] = {"ADC", AM::kDirectPageIndexedIndirectX, 2};
130 opcode_table_[0x62] = {"PER", AM::kProgramCounterRelativeLong, 3};
131 opcode_table_[0x63] = {"ADC", AM::kStackRelative, 2};
132 opcode_table_[0x64] = {"STZ", AM::kDirectPage, 2};
133 opcode_table_[0x65] = {"ADC", AM::kDirectPage, 2};
134 opcode_table_[0x66] = {"ROR", AM::kDirectPage, 2};
135 opcode_table_[0x67] = {"ADC", AM::kDirectPageIndirectLong, 2};
136 opcode_table_[0x68] = {"PLA", AM::kImplied, 1};
137 opcode_table_[0x69] = {"ADC", AM::kImmediateM, 2};
138 opcode_table_[0x6A] = {"ROR", AM::kAccumulator, 1};
139 opcode_table_[0x6B] = {"RTL", AM::kImplied, 1};
140 opcode_table_[0x6C] = {"JMP", AM::kAbsoluteIndirect, 3};
141 opcode_table_[0x6D] = {"ADC", AM::kAbsolute, 3};
142 opcode_table_[0x6E] = {"ROR", AM::kAbsolute, 3};
143 opcode_table_[0x6F] = {"ADC", AM::kAbsoluteLong, 4};
144
145 // Row 0x70-0x7F
146 opcode_table_[0x70] = {"BVS", AM::kProgramCounterRelative, 2};
147 opcode_table_[0x71] = {"ADC", AM::kDirectPageIndirectIndexedY, 2};
148 opcode_table_[0x72] = {"ADC", AM::kDirectPageIndirect, 2};
149 opcode_table_[0x73] = {"ADC", AM::kStackRelativeIndirectIndexedY, 2};
150 opcode_table_[0x74] = {"STZ", AM::kDirectPageIndexedX, 2};
151 opcode_table_[0x75] = {"ADC", AM::kDirectPageIndexedX, 2};
152 opcode_table_[0x76] = {"ROR", AM::kDirectPageIndexedX, 2};
153 opcode_table_[0x77] = {"ADC", AM::kDirectPageIndirectLongIndexedY, 2};
154 opcode_table_[0x78] = {"SEI", AM::kImplied, 1};
155 opcode_table_[0x79] = {"ADC", AM::kAbsoluteIndexedY, 3};
156 opcode_table_[0x7A] = {"PLY", AM::kImplied, 1};
157 opcode_table_[0x7B] = {"TDC", AM::kImplied, 1};
158 opcode_table_[0x7C] = {"JMP", AM::kAbsoluteIndexedIndirect, 3};
159 opcode_table_[0x7D] = {"ADC", AM::kAbsoluteIndexedX, 3};
160 opcode_table_[0x7E] = {"ROR", AM::kAbsoluteIndexedX, 3};
161 opcode_table_[0x7F] = {"ADC", AM::kAbsoluteLongIndexedX, 4};
162
163 // Row 0x80-0x8F
164 opcode_table_[0x80] = {"BRA", AM::kProgramCounterRelative, 2};
165 opcode_table_[0x81] = {"STA", AM::kDirectPageIndexedIndirectX, 2};
166 opcode_table_[0x82] = {"BRL", AM::kProgramCounterRelativeLong, 3};
167 opcode_table_[0x83] = {"STA", AM::kStackRelative, 2};
168 opcode_table_[0x84] = {"STY", AM::kDirectPage, 2};
169 opcode_table_[0x85] = {"STA", AM::kDirectPage, 2};
170 opcode_table_[0x86] = {"STX", AM::kDirectPage, 2};
171 opcode_table_[0x87] = {"STA", AM::kDirectPageIndirectLong, 2};
172 opcode_table_[0x88] = {"DEY", AM::kImplied, 1};
173 opcode_table_[0x89] = {"BIT", AM::kImmediateM, 2};
174 opcode_table_[0x8A] = {"TXA", AM::kImplied, 1};
175 opcode_table_[0x8B] = {"PHB", AM::kImplied, 1};
176 opcode_table_[0x8C] = {"STY", AM::kAbsolute, 3};
177 opcode_table_[0x8D] = {"STA", AM::kAbsolute, 3};
178 opcode_table_[0x8E] = {"STX", AM::kAbsolute, 3};
179 opcode_table_[0x8F] = {"STA", AM::kAbsoluteLong, 4};
180
181 // Row 0x90-0x9F
182 opcode_table_[0x90] = {"BCC", AM::kProgramCounterRelative, 2};
183 opcode_table_[0x91] = {"STA", AM::kDirectPageIndirectIndexedY, 2};
184 opcode_table_[0x92] = {"STA", AM::kDirectPageIndirect, 2};
185 opcode_table_[0x93] = {"STA", AM::kStackRelativeIndirectIndexedY, 2};
186 opcode_table_[0x94] = {"STY", AM::kDirectPageIndexedX, 2};
187 opcode_table_[0x95] = {"STA", AM::kDirectPageIndexedX, 2};
188 opcode_table_[0x96] = {"STX", AM::kDirectPageIndexedY, 2};
189 opcode_table_[0x97] = {"STA", AM::kDirectPageIndirectLongIndexedY, 2};
190 opcode_table_[0x98] = {"TYA", AM::kImplied, 1};
191 opcode_table_[0x99] = {"STA", AM::kAbsoluteIndexedY, 3};
192 opcode_table_[0x9A] = {"TXS", AM::kImplied, 1};
193 opcode_table_[0x9B] = {"TXY", AM::kImplied, 1};
194 opcode_table_[0x9C] = {"STZ", AM::kAbsolute, 3};
195 opcode_table_[0x9D] = {"STA", AM::kAbsoluteIndexedX, 3};
196 opcode_table_[0x9E] = {"STZ", AM::kAbsoluteIndexedX, 3};
197 opcode_table_[0x9F] = {"STA", AM::kAbsoluteLongIndexedX, 4};
198
199 // Row 0xA0-0xAF
200 opcode_table_[0xA0] = {"LDY", AM::kImmediateX, 2};
201 opcode_table_[0xA1] = {"LDA", AM::kDirectPageIndexedIndirectX, 2};
202 opcode_table_[0xA2] = {"LDX", AM::kImmediateX, 2};
203 opcode_table_[0xA3] = {"LDA", AM::kStackRelative, 2};
204 opcode_table_[0xA4] = {"LDY", AM::kDirectPage, 2};
205 opcode_table_[0xA5] = {"LDA", AM::kDirectPage, 2};
206 opcode_table_[0xA6] = {"LDX", AM::kDirectPage, 2};
207 opcode_table_[0xA7] = {"LDA", AM::kDirectPageIndirectLong, 2};
208 opcode_table_[0xA8] = {"TAY", AM::kImplied, 1};
209 opcode_table_[0xA9] = {"LDA", AM::kImmediateM, 2};
210 opcode_table_[0xAA] = {"TAX", AM::kImplied, 1};
211 opcode_table_[0xAB] = {"PLB", AM::kImplied, 1};
212 opcode_table_[0xAC] = {"LDY", AM::kAbsolute, 3};
213 opcode_table_[0xAD] = {"LDA", AM::kAbsolute, 3};
214 opcode_table_[0xAE] = {"LDX", AM::kAbsolute, 3};
215 opcode_table_[0xAF] = {"LDA", AM::kAbsoluteLong, 4};
216
217 // Row 0xB0-0xBF
218 opcode_table_[0xB0] = {"BCS", AM::kProgramCounterRelative, 2};
219 opcode_table_[0xB1] = {"LDA", AM::kDirectPageIndirectIndexedY, 2};
220 opcode_table_[0xB2] = {"LDA", AM::kDirectPageIndirect, 2};
221 opcode_table_[0xB3] = {"LDA", AM::kStackRelativeIndirectIndexedY, 2};
222 opcode_table_[0xB4] = {"LDY", AM::kDirectPageIndexedX, 2};
223 opcode_table_[0xB5] = {"LDA", AM::kDirectPageIndexedX, 2};
224 opcode_table_[0xB6] = {"LDX", AM::kDirectPageIndexedY, 2};
225 opcode_table_[0xB7] = {"LDA", AM::kDirectPageIndirectLongIndexedY, 2};
226 opcode_table_[0xB8] = {"CLV", AM::kImplied, 1};
227 opcode_table_[0xB9] = {"LDA", AM::kAbsoluteIndexedY, 3};
228 opcode_table_[0xBA] = {"TSX", AM::kImplied, 1};
229 opcode_table_[0xBB] = {"TYX", AM::kImplied, 1};
230 opcode_table_[0xBC] = {"LDY", AM::kAbsoluteIndexedX, 3};
231 opcode_table_[0xBD] = {"LDA", AM::kAbsoluteIndexedX, 3};
232 opcode_table_[0xBE] = {"LDX", AM::kAbsoluteIndexedY, 3};
233 opcode_table_[0xBF] = {"LDA", AM::kAbsoluteLongIndexedX, 4};
234
235 // Row 0xC0-0xCF
236 opcode_table_[0xC0] = {"CPY", AM::kImmediateX, 2};
237 opcode_table_[0xC1] = {"CMP", AM::kDirectPageIndexedIndirectX, 2};
238 opcode_table_[0xC2] = {"REP", AM::kImmediate8, 2};
239 opcode_table_[0xC3] = {"CMP", AM::kStackRelative, 2};
240 opcode_table_[0xC4] = {"CPY", AM::kDirectPage, 2};
241 opcode_table_[0xC5] = {"CMP", AM::kDirectPage, 2};
242 opcode_table_[0xC6] = {"DEC", AM::kDirectPage, 2};
243 opcode_table_[0xC7] = {"CMP", AM::kDirectPageIndirectLong, 2};
244 opcode_table_[0xC8] = {"INY", AM::kImplied, 1};
245 opcode_table_[0xC9] = {"CMP", AM::kImmediateM, 2};
246 opcode_table_[0xCA] = {"DEX", AM::kImplied, 1};
247 opcode_table_[0xCB] = {"WAI", AM::kImplied, 1};
248 opcode_table_[0xCC] = {"CPY", AM::kAbsolute, 3};
249 opcode_table_[0xCD] = {"CMP", AM::kAbsolute, 3};
250 opcode_table_[0xCE] = {"DEC", AM::kAbsolute, 3};
251 opcode_table_[0xCF] = {"CMP", AM::kAbsoluteLong, 4};
252
253 // Row 0xD0-0xDF
254 opcode_table_[0xD0] = {"BNE", AM::kProgramCounterRelative, 2};
255 opcode_table_[0xD1] = {"CMP", AM::kDirectPageIndirectIndexedY, 2};
256 opcode_table_[0xD2] = {"CMP", AM::kDirectPageIndirect, 2};
257 opcode_table_[0xD3] = {"CMP", AM::kStackRelativeIndirectIndexedY, 2};
258 opcode_table_[0xD4] = {"PEI", AM::kDirectPageIndirect, 2};
259 opcode_table_[0xD5] = {"CMP", AM::kDirectPageIndexedX, 2};
260 opcode_table_[0xD6] = {"DEC", AM::kDirectPageIndexedX, 2};
261 opcode_table_[0xD7] = {"CMP", AM::kDirectPageIndirectLongIndexedY, 2};
262 opcode_table_[0xD8] = {"CLD", AM::kImplied, 1};
263 opcode_table_[0xD9] = {"CMP", AM::kAbsoluteIndexedY, 3};
264 opcode_table_[0xDA] = {"PHX", AM::kImplied, 1};
265 opcode_table_[0xDB] = {"STP", AM::kImplied, 1};
266 opcode_table_[0xDC] = {"JMP", AM::kAbsoluteIndirectLong, 3};
267 opcode_table_[0xDD] = {"CMP", AM::kAbsoluteIndexedX, 3};
268 opcode_table_[0xDE] = {"DEC", AM::kAbsoluteIndexedX, 3};
269 opcode_table_[0xDF] = {"CMP", AM::kAbsoluteLongIndexedX, 4};
270
271 // Row 0xE0-0xEF
272 opcode_table_[0xE0] = {"CPX", AM::kImmediateX, 2};
273 opcode_table_[0xE1] = {"SBC", AM::kDirectPageIndexedIndirectX, 2};
274 opcode_table_[0xE2] = {"SEP", AM::kImmediate8, 2};
275 opcode_table_[0xE3] = {"SBC", AM::kStackRelative, 2};
276 opcode_table_[0xE4] = {"CPX", AM::kDirectPage, 2};
277 opcode_table_[0xE5] = {"SBC", AM::kDirectPage, 2};
278 opcode_table_[0xE6] = {"INC", AM::kDirectPage, 2};
279 opcode_table_[0xE7] = {"SBC", AM::kDirectPageIndirectLong, 2};
280 opcode_table_[0xE8] = {"INX", AM::kImplied, 1};
281 opcode_table_[0xE9] = {"SBC", AM::kImmediateM, 2};
282 opcode_table_[0xEA] = {"NOP", AM::kImplied, 1};
283 opcode_table_[0xEB] = {"XBA", AM::kImplied, 1};
284 opcode_table_[0xEC] = {"CPX", AM::kAbsolute, 3};
285 opcode_table_[0xED] = {"SBC", AM::kAbsolute, 3};
286 opcode_table_[0xEE] = {"INC", AM::kAbsolute, 3};
287 opcode_table_[0xEF] = {"SBC", AM::kAbsoluteLong, 4};
288
289 // Row 0xF0-0xFF
290 opcode_table_[0xF0] = {"BEQ", AM::kProgramCounterRelative, 2};
291 opcode_table_[0xF1] = {"SBC", AM::kDirectPageIndirectIndexedY, 2};
292 opcode_table_[0xF2] = {"SBC", AM::kDirectPageIndirect, 2};
293 opcode_table_[0xF3] = {"SBC", AM::kStackRelativeIndirectIndexedY, 2};
294 opcode_table_[0xF4] = {"PEA", AM::kAbsolute, 3};
295 opcode_table_[0xF5] = {"SBC", AM::kDirectPageIndexedX, 2};
296 opcode_table_[0xF6] = {"INC", AM::kDirectPageIndexedX, 2};
297 opcode_table_[0xF7] = {"SBC", AM::kDirectPageIndirectLongIndexedY, 2};
298 opcode_table_[0xF8] = {"SED", AM::kImplied, 1};
299 opcode_table_[0xF9] = {"SBC", AM::kAbsoluteIndexedY, 3};
300 opcode_table_[0xFA] = {"PLX", AM::kImplied, 1};
301 opcode_table_[0xFB] = {"XCE", AM::kImplied, 1};
302 opcode_table_[0xFC] = {"JSR", AM::kAbsoluteIndexedIndirect, 3};
303 opcode_table_[0xFD] = {"SBC", AM::kAbsoluteIndexedX, 3};
304 opcode_table_[0xFE] = {"INC", AM::kAbsoluteIndexedX, 3};
305 opcode_table_[0xFF] = {"SBC", AM::kAbsoluteLongIndexedX, 4};
306}
307
309 uint8_t opcode) const {
310 return opcode_table_[opcode];
311}
312
313uint8_t Disassembler65816::GetInstructionSize(uint8_t opcode, bool m_flag,
314 bool x_flag) const {
315 const auto& info = opcode_table_[opcode];
316 uint8_t size = info.base_size;
317
318 // Adjust size for M-flag dependent immediate modes
319 if (info.mode == AddressingMode65816::kImmediateM && !m_flag) {
320 size++; // 16-bit accumulator mode adds 1 byte
321 }
322 // Adjust size for X-flag dependent immediate modes
323 if (info.mode == AddressingMode65816::kImmediateX && !x_flag) {
324 size++; // 16-bit index mode adds 1 byte
325 }
326
327 return size;
328}
329
331 uint32_t address, MemoryReader read_byte, bool m_flag, bool x_flag) const {
333 result.address = address;
334
335 // Read opcode
336 result.opcode = read_byte(address);
337 const auto& info = opcode_table_[result.opcode];
338 result.mnemonic = info.mnemonic;
339 result.size = GetInstructionSize(result.opcode, m_flag, x_flag);
340
341 // Read operand bytes
342 for (uint8_t i = 1; i < result.size; i++) {
343 result.operands.push_back(read_byte(address + i));
344 }
345
346 // Format operand string
347 result.operand_str =
348 FormatOperand(info.mode, result.operands, address, m_flag, x_flag);
349
350 // Determine instruction type
351 const std::string& mn = result.mnemonic;
352 result.is_branch = (mn == "BRA" || mn == "BRL" || mn == "BPL" ||
353 mn == "BMI" || mn == "BVC" || mn == "BVS" ||
354 mn == "BCC" || mn == "BCS" || mn == "BNE" ||
355 mn == "BEQ" || mn == "JMP");
356 result.is_call = (mn == "JSR" || mn == "JSL");
357 result.is_return = (mn == "RTS" || mn == "RTL" || mn == "RTI");
358
359 // Calculate branch target if applicable
360 if (result.is_branch || result.is_call) {
361 result.branch_target =
362 CalculateBranchTarget(address, result.operands, info.mode, result.size);
363 }
364
365 // Build full text representation
366 std::ostringstream ss;
367 ss << absl::StrFormat("$%06X: ", address);
368
369 // Hex dump of bytes
370 ss << absl::StrFormat("%02X ", result.opcode);
371 for (const auto& byte : result.operands) {
372 ss << absl::StrFormat("%02X ", byte);
373 }
374 // Pad to align mnemonics
375 for (int i = result.size; i < 4; i++) {
376 ss << " ";
377 }
378
379 ss << " " << result.mnemonic;
380 if (!result.operand_str.empty()) {
381 ss << " " << result.operand_str;
382 }
383
384 // Add branch target comment if applicable
385 if ((result.is_branch || result.is_call) &&
389 // Try to resolve symbol
390 if (symbol_resolver_) {
391 std::string symbol = symbol_resolver_(result.branch_target);
392 if (!symbol.empty()) {
393 ss << " ; -> " << symbol;
394 }
395 }
396 }
397
398 result.full_text = ss.str();
399 return result;
400}
401
402std::vector<DisassembledInstruction> Disassembler65816::DisassembleRange(
403 uint32_t start_address, size_t count, MemoryReader read_byte, bool m_flag,
404 bool x_flag) const {
405 std::vector<DisassembledInstruction> results;
406 results.reserve(count);
407
408 uint32_t current_address = start_address;
409 for (size_t i = 0; i < count; i++) {
410 auto instruction = Disassemble(current_address, read_byte, m_flag, x_flag);
411 results.push_back(instruction);
412 current_address += instruction.size;
413 }
414
415 return results;
416}
417
419 const std::vector<uint8_t>& ops,
420 uint32_t address, bool m_flag,
421 bool x_flag) const {
422 using AM = AddressingMode65816;
423
424 switch (mode) {
425 case AM::kImplied:
426 case AM::kAccumulator:
427 return "";
428
429 case AM::kImmediate8:
430 if (ops.size() >= 1) {
431 return absl::StrFormat("#$%02X", ops[0]);
432 }
433 break;
434
435 case AM::kImmediate16:
436 if (ops.size() >= 2) {
437 return absl::StrFormat("#$%04X", ops[0] | (ops[1] << 8));
438 }
439 break;
440
441 case AM::kImmediateM:
442 if (m_flag && ops.size() >= 1) {
443 return absl::StrFormat("#$%02X", ops[0]);
444 } else if (!m_flag && ops.size() >= 2) {
445 return absl::StrFormat("#$%04X", ops[0] | (ops[1] << 8));
446 }
447 break;
448
449 case AM::kImmediateX:
450 if (x_flag && ops.size() >= 1) {
451 return absl::StrFormat("#$%02X", ops[0]);
452 } else if (!x_flag && ops.size() >= 2) {
453 return absl::StrFormat("#$%04X", ops[0] | (ops[1] << 8));
454 }
455 break;
456
457 case AM::kDirectPage:
458 if (ops.size() >= 1) {
459 return absl::StrFormat("$%02X", ops[0]);
460 }
461 break;
462
463 case AM::kDirectPageIndexedX:
464 if (ops.size() >= 1) {
465 return absl::StrFormat("$%02X,X", ops[0]);
466 }
467 break;
468
469 case AM::kDirectPageIndexedY:
470 if (ops.size() >= 1) {
471 return absl::StrFormat("$%02X,Y", ops[0]);
472 }
473 break;
474
475 case AM::kDirectPageIndirect:
476 if (ops.size() >= 1) {
477 return absl::StrFormat("($%02X)", ops[0]);
478 }
479 break;
480
481 case AM::kDirectPageIndirectLong:
482 if (ops.size() >= 1) {
483 return absl::StrFormat("[$%02X]", ops[0]);
484 }
485 break;
486
487 case AM::kDirectPageIndexedIndirectX:
488 if (ops.size() >= 1) {
489 return absl::StrFormat("($%02X,X)", ops[0]);
490 }
491 break;
492
493 case AM::kDirectPageIndirectIndexedY:
494 if (ops.size() >= 1) {
495 return absl::StrFormat("($%02X),Y", ops[0]);
496 }
497 break;
498
499 case AM::kDirectPageIndirectLongIndexedY:
500 if (ops.size() >= 1) {
501 return absl::StrFormat("[$%02X],Y", ops[0]);
502 }
503 break;
504
505 case AM::kAbsolute:
506 if (ops.size() >= 2) {
507 uint16_t addr = ops[0] | (ops[1] << 8);
508 // Try symbol resolution
509 if (symbol_resolver_) {
510 std::string symbol = symbol_resolver_(addr);
511 if (!symbol.empty()) {
512 return symbol;
513 }
514 }
515 return absl::StrFormat("$%04X", addr);
516 }
517 break;
518
519 case AM::kAbsoluteIndexedX:
520 if (ops.size() >= 2) {
521 return absl::StrFormat("$%04X,X", ops[0] | (ops[1] << 8));
522 }
523 break;
524
525 case AM::kAbsoluteIndexedY:
526 if (ops.size() >= 2) {
527 return absl::StrFormat("$%04X,Y", ops[0] | (ops[1] << 8));
528 }
529 break;
530
531 case AM::kAbsoluteLong:
532 if (ops.size() >= 3) {
533 uint32_t addr = ops[0] | (ops[1] << 8) | (ops[2] << 16);
534 if (symbol_resolver_) {
535 std::string symbol = symbol_resolver_(addr);
536 if (!symbol.empty()) {
537 return symbol;
538 }
539 }
540 return absl::StrFormat("$%06X", addr);
541 }
542 break;
543
544 case AM::kAbsoluteLongIndexedX:
545 if (ops.size() >= 3) {
546 return absl::StrFormat("$%06X,X",
547 ops[0] | (ops[1] << 8) | (ops[2] << 16));
548 }
549 break;
550
551 case AM::kAbsoluteIndirect:
552 if (ops.size() >= 2) {
553 return absl::StrFormat("($%04X)", ops[0] | (ops[1] << 8));
554 }
555 break;
556
557 case AM::kAbsoluteIndirectLong:
558 if (ops.size() >= 2) {
559 return absl::StrFormat("[$%04X]", ops[0] | (ops[1] << 8));
560 }
561 break;
562
563 case AM::kAbsoluteIndexedIndirect:
564 if (ops.size() >= 2) {
565 return absl::StrFormat("($%04X,X)", ops[0] | (ops[1] << 8));
566 }
567 break;
568
569 case AM::kProgramCounterRelative:
570 if (ops.size() >= 1) {
571 // 8-bit signed offset
572 int8_t offset = static_cast<int8_t>(ops[0]);
573 uint32_t target = (address + 2 + offset) & 0xFFFF;
574 // Preserve bank
575 target |= (address & 0xFF0000);
576 if (symbol_resolver_) {
577 std::string symbol = symbol_resolver_(target);
578 if (!symbol.empty()) {
579 return symbol;
580 }
581 }
582 return absl::StrFormat("$%04X", target & 0xFFFF);
583 }
584 break;
585
586 case AM::kProgramCounterRelativeLong:
587 if (ops.size() >= 2) {
588 // 16-bit signed offset
589 int16_t offset = static_cast<int16_t>(ops[0] | (ops[1] << 8));
590 uint32_t target = (address + 3 + offset) & 0xFFFF;
591 target |= (address & 0xFF0000);
592 if (symbol_resolver_) {
593 std::string symbol = symbol_resolver_(target);
594 if (!symbol.empty()) {
595 return symbol;
596 }
597 }
598 return absl::StrFormat("$%04X", target & 0xFFFF);
599 }
600 break;
601
602 case AM::kStackRelative:
603 if (ops.size() >= 1) {
604 return absl::StrFormat("$%02X,S", ops[0]);
605 }
606 break;
607
608 case AM::kStackRelativeIndirectIndexedY:
609 if (ops.size() >= 1) {
610 return absl::StrFormat("($%02X,S),Y", ops[0]);
611 }
612 break;
613
614 case AM::kBlockMove:
615 if (ops.size() >= 2) {
616 // MVN/MVP: srcBank, dstBank
617 return absl::StrFormat("$%02X,$%02X", ops[0], ops[1]);
618 }
619 break;
620 }
621
622 return "???";
623}
624
626 uint32_t address, const std::vector<uint8_t>& operands,
627 AddressingMode65816 mode, uint8_t instruction_size) const {
628 using AM = AddressingMode65816;
629
630 switch (mode) {
631 case AM::kProgramCounterRelative:
632 if (operands.size() >= 1) {
633 int8_t offset = static_cast<int8_t>(operands[0]);
634 return (address + instruction_size + offset) & 0xFFFFFF;
635 }
636 break;
637
638 case AM::kProgramCounterRelativeLong:
639 if (operands.size() >= 2) {
640 int16_t offset =
641 static_cast<int16_t>(operands[0] | (operands[1] << 8));
642 return (address + instruction_size + offset) & 0xFFFFFF;
643 }
644 break;
645
646 case AM::kAbsolute:
647 if (operands.size() >= 2) {
648 // For JMP/JSR absolute, use current bank
649 return (address & 0xFF0000) | (operands[0] | (operands[1] << 8));
650 }
651 break;
652
653 case AM::kAbsoluteLong:
654 if (operands.size() >= 3) {
655 return operands[0] | (operands[1] << 8) | (operands[2] << 16);
656 }
657 break;
658
659 default:
660 break;
661 }
662
663 return 0;
664}
665
666} // namespace debug
667} // namespace emu
668} // namespace yaze
DisassembledInstruction Disassemble(uint32_t address, MemoryReader read_byte, bool m_flag=true, bool x_flag=true) const
Disassemble a single instruction.
uint8_t GetInstructionSize(uint8_t opcode, bool m_flag, bool x_flag) const
Calculate actual instruction size based on flags.
const InstructionInfo & GetInstructionInfo(uint8_t opcode) const
Get instruction info for an opcode.
std::vector< DisassembledInstruction > DisassembleRange(uint32_t start_address, size_t count, MemoryReader read_byte, bool m_flag=true, bool x_flag=true) const
Disassemble multiple instructions.
std::function< uint8_t(uint32_t)> MemoryReader
uint32_t CalculateBranchTarget(uint32_t address, const std::vector< uint8_t > &operands, AddressingMode65816 mode, uint8_t instruction_size) const
std::string FormatOperand(AddressingMode65816 mode, const std::vector< uint8_t > &operands, uint32_t address, bool m_flag, bool x_flag) const
AddressingMode65816
Addressing modes for the 65816 CPU.
Result of disassembling a single instruction.
Information about a single 65816 instruction.