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