1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #include "bspf.hxx" |
19 | #include "Debugger.hxx" |
20 | #include "DiStella.hxx" |
21 | using Common::Base; |
22 | |
23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
24 | DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list, |
25 | CartDebug::BankInfo& info, const DiStella::Settings& s, |
26 | CartDebug::AddrTypeArray& labels, |
27 | CartDebug::AddrTypeArray& directives, |
28 | CartDebug::ReservedEquates& reserved) |
29 | : myDbg(dbg), |
30 | myList(list), |
31 | mySettings(s), |
32 | myReserved(reserved), |
33 | myOffset(0), |
34 | myPC(0), |
35 | myPCEnd(0), |
36 | myLabels(labels), |
37 | myDirectives(directives) |
38 | { |
39 | bool resolve_code = mySettings.resolveCode; |
40 | CartDebug::AddressList& debuggerAddresses = info.addressList; |
41 | uInt16 start = *debuggerAddresses.cbegin(); |
42 | |
43 | myOffset = info.offset; |
44 | if (start & 0x1000) { |
45 | info.start = myAppData.start = 0x0000; |
46 | info.end = myAppData.end = static_cast<uInt16>(info.size - 1); |
47 | // Keep previous offset; it may be different between banks |
48 | if (info.offset == 0) |
49 | info.offset = myOffset = (start - (start % info.size)); |
50 | } else { // ZP RAM |
51 | // For now, we assume all accesses below $1000 are zero-page |
52 | info.start = myAppData.start = 0x0080; |
53 | info.end = myAppData.end = 0x00FF; |
54 | info.offset = myOffset = 0; |
55 | |
56 | // Resolve code is never used in ZP RAM mode |
57 | resolve_code = false; |
58 | } |
59 | myAppData.length = static_cast<uInt16>(info.size); |
60 | |
61 | myLabels.fill(0); |
62 | myDirectives.fill(0); |
63 | |
64 | // Process any directives first, as they override automatic code determination |
65 | processDirectives(info.directiveList); |
66 | |
67 | myReserved.breakFound = false; |
68 | |
69 | if (resolve_code) |
70 | // First pass |
71 | disasmPass1(info.addressList); |
72 | |
73 | // Second pass |
74 | disasm(myOffset, 2); |
75 | |
76 | // Add reserved line equates |
77 | ostringstream reservedLabel; |
78 | for (int k = 0; k <= myAppData.end; k++) { |
79 | if ((myLabels[k] & (CartDebug::REFERENCED | CartDebug::VALID_ENTRY)) == CartDebug::REFERENCED) { |
80 | // If we have a piece of code referenced somewhere else, but cannot |
81 | // locate the label in code (i.e because the address is inside of a |
82 | // multi-byte instruction, then we make note of that address for reference |
83 | // |
84 | // However, we only do this for labels pointing to ROM (above $1000) |
85 | if (myDbg.addressType(k + myOffset) == CartDebug::AddrType::ROM) { |
86 | reservedLabel.str("" ); |
87 | reservedLabel << "L" << Base::HEX4 << (k + myOffset); |
88 | myReserved.Label.emplace(k + myOffset, reservedLabel.str()); |
89 | } |
90 | } |
91 | } |
92 | |
93 | // Third pass |
94 | disasm(myOffset, 3); |
95 | } |
96 | |
97 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
98 | void DiStella::disasm(uInt32 distart, int pass) |
99 | /* |
100 | // Here we have 3 passes: |
101 | - pass 1 tries to detect code and data ranges and labels |
102 | - pass 2 marks valid code |
103 | - pass 3 generates output |
104 | */ |
105 | { |
106 | #define LABEL_A12_HIGH(address) labelA12High(nextLine, opcode, address, labelFound) |
107 | #define LABEL_A12_LOW(address) labelA12Low(nextLine, opcode, address, labelFound) |
108 | |
109 | uInt8 opcode, d1; |
110 | uInt16 ad; |
111 | Int32 cycles = 0; |
112 | AddressingMode addrMode; |
113 | int labelFound = 0; |
114 | stringstream nextLine, nextLineBytes; |
115 | |
116 | mySegType = CartDebug::NONE; // create extra lines between code and data |
117 | |
118 | myDisasmBuf.str("" ); |
119 | |
120 | /* pc=myAppData.start; */ |
121 | myPC = distart - myOffset; |
122 | while (myPC <= myAppData.end) { |
123 | |
124 | // since -1 is used in m6502.m4 for clearing the last peek |
125 | // and this results into an access at e.g. 0xffff, |
126 | // we have to fix the consequences here (ugly!). |
127 | if(myPC == myAppData.end) |
128 | goto FIX_LAST; |
129 | |
130 | if (checkBits(myPC, CartDebug::GFX | CartDebug::PGFX, |
131 | CartDebug::CODE)) { |
132 | if (pass == 2) |
133 | mark(myPC + myOffset, CartDebug::VALID_ENTRY); |
134 | if (pass == 3) |
135 | outputGraphics(); |
136 | ++myPC; |
137 | } else if (checkBits(myPC, CartDebug::DATA, |
138 | CartDebug::CODE | CartDebug::GFX | CartDebug::PGFX)) { |
139 | if (pass == 2) |
140 | mark(myPC + myOffset, CartDebug::VALID_ENTRY); |
141 | if (pass == 3) |
142 | outputBytes(CartDebug::DATA); |
143 | else |
144 | ++myPC; |
145 | } else if (checkBits(myPC, CartDebug::ROW, |
146 | CartDebug::CODE | CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) { |
147 | FIX_LAST: |
148 | if (pass == 2) |
149 | mark(myPC + myOffset, CartDebug::VALID_ENTRY); |
150 | |
151 | if (pass == 3) |
152 | outputBytes(CartDebug::ROW); |
153 | else |
154 | ++myPC; |
155 | } else { |
156 | // The following sections must be CODE |
157 | |
158 | // add extra spacing line when switching from non-code to code |
159 | if (pass == 3 && mySegType != CartDebug::CODE && mySegType != CartDebug::NONE) { |
160 | myDisasmBuf << " ' ' " ; |
161 | addEntry(CartDebug::NONE); |
162 | mark(myPC + myOffset, CartDebug::REFERENCED); // add label when switching |
163 | } |
164 | mySegType = CartDebug::CODE; |
165 | |
166 | /* version 2.1 bug fix */ |
167 | if (pass == 2) |
168 | mark(myPC + myOffset, CartDebug::VALID_ENTRY); |
169 | |
170 | // get opcode |
171 | opcode = Debugger::debugger().peek(myPC + myOffset); |
172 | // get address mode for opcode |
173 | addrMode = ourLookup[opcode].addr_mode; |
174 | |
175 | if (pass == 3) { |
176 | if (checkBit(myPC, CartDebug::REFERENCED)) |
177 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'" ; |
178 | else |
179 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '" ; |
180 | } |
181 | ++myPC; |
182 | |
183 | // detect labels inside instructions (e.g. BIT masks) |
184 | labelFound = false; |
185 | for (Uint8 i = 0; i < ourLookup[opcode].bytes - 1; i++) { |
186 | if (checkBit(myPC + i, CartDebug::REFERENCED)) { |
187 | labelFound = true; |
188 | break; |
189 | } |
190 | } |
191 | if (labelFound) { |
192 | if (myOffset >= 0x1000) { |
193 | // the opcode's operand address matches a label address |
194 | if (pass == 3) { |
195 | // output the byte of the opcode incl. cycles |
196 | Uint8 nextOpcode = Debugger::debugger().peek(myPC + myOffset); |
197 | |
198 | cycles += int(ourLookup[opcode].cycles) - int(ourLookup[nextOpcode].cycles); |
199 | nextLine << ".byte $" << Base::HEX2 << int(opcode) << " ;" ; |
200 | nextLine << ourLookup[opcode].mnemonic; |
201 | |
202 | myDisasmBuf << nextLine.str() << "'" << ";" |
203 | << std::dec << int(ourLookup[opcode].cycles) << "-" |
204 | << std::dec << int(ourLookup[nextOpcode].cycles) << " " |
205 | << "'= " << std::setw(3) << std::setfill(' ') << std::dec << cycles; |
206 | |
207 | nextLine.str("" ); |
208 | cycles = 0; |
209 | addEntry(CartDebug::CODE); // add the new found CODE entry |
210 | } |
211 | // continue with the label's opcode |
212 | continue; |
213 | } else { |
214 | if (pass == 3) { |
215 | // TODO |
216 | } |
217 | } |
218 | } |
219 | |
220 | // Undefined opcodes start with a '.' |
221 | // These are undefined wrt DASM |
222 | if (ourLookup[opcode].mnemonic[0] == '.' && pass == 3) { |
223 | nextLine << ".byte $" << Base::HEX2 << int(opcode) << " ;" ; |
224 | } |
225 | |
226 | if (pass == 3) { |
227 | nextLine << ourLookup[opcode].mnemonic; |
228 | nextLineBytes << Base::HEX2 << int(opcode) << " " ; |
229 | } |
230 | |
231 | // Add operand(s) for PC values outside the app data range |
232 | if (myPC >= myAppData.end) { |
233 | switch (addrMode) { |
234 | case AddressingMode::ABSOLUTE: |
235 | case AddressingMode::ABSOLUTE_X: |
236 | case AddressingMode::ABSOLUTE_Y: |
237 | case AddressingMode::INDIRECT_X: |
238 | case AddressingMode::INDIRECT_Y: |
239 | case AddressingMode::ABS_INDIRECT: |
240 | { |
241 | if (pass == 3) { |
242 | /* Line information is already printed; append .byte since last |
243 | instruction will put recompilable object larger that original |
244 | binary file */ |
245 | myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $" |
246 | << Base::HEX4 << myPC + myOffset << "'" |
247 | << Base::HEX2 << int(opcode); |
248 | addEntry(CartDebug::DATA); |
249 | |
250 | if (myPC == myAppData.end) { |
251 | if (checkBit(myPC, CartDebug::REFERENCED)) |
252 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'" ; |
253 | else |
254 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '" ; |
255 | |
256 | opcode = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
257 | myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode) << " $" |
258 | << Base::HEX4 << myPC + myOffset << "'" |
259 | << Base::HEX2 << int(opcode); |
260 | addEntry(CartDebug::DATA); |
261 | } |
262 | } |
263 | myPCEnd = myAppData.end + myOffset; |
264 | return; |
265 | } |
266 | |
267 | case AddressingMode::ZERO_PAGE: |
268 | case AddressingMode::IMMEDIATE: |
269 | case AddressingMode::ZERO_PAGE_X: |
270 | case AddressingMode::ZERO_PAGE_Y: |
271 | case AddressingMode::RELATIVE: |
272 | { |
273 | if (pass == 3) { |
274 | /* Line information is already printed, but we can remove the |
275 | Instruction (i.e. BMI) by simply clearing the buffer to print */ |
276 | myDisasmBuf << ".byte $" << Base::HEX2 << int(opcode); |
277 | addEntry(CartDebug::ROW); |
278 | nextLine.str("" ); |
279 | nextLineBytes.str("" ); |
280 | } |
281 | ++myPC; |
282 | myPCEnd = myAppData.end + myOffset; |
283 | return; |
284 | } |
285 | |
286 | default: |
287 | break; |
288 | } // end switch(addr_mode) |
289 | } |
290 | |
291 | // Add operand(s) |
292 | ad = d1 = 0; // not WSYNC by default! |
293 | /* Version 2.1 added the extensions to mnemonics */ |
294 | switch (addrMode) { |
295 | case AddressingMode::ACCUMULATOR: |
296 | { |
297 | if (pass == 3 && mySettings.aFlag) |
298 | nextLine << " A" ; |
299 | break; |
300 | } |
301 | |
302 | case AddressingMode::ABSOLUTE: |
303 | { |
304 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
305 | labelFound = mark(ad, CartDebug::REFERENCED); |
306 | if (pass == 3) { |
307 | if (ad < 0x100 && mySettings.fFlag) |
308 | nextLine << ".w " ; |
309 | else |
310 | nextLine << " " ; |
311 | |
312 | if (labelFound == 1) { |
313 | LABEL_A12_HIGH(ad); |
314 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
315 | } else if (labelFound == 4) { |
316 | if (mySettings.rFlag) { |
317 | int tmp = (ad & myAppData.end) + myOffset; |
318 | LABEL_A12_HIGH(tmp); |
319 | nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8); |
320 | } else { |
321 | nextLine << "$" << Base::HEX4 << ad; |
322 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
323 | } |
324 | } else { |
325 | LABEL_A12_LOW(ad); |
326 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
327 | } |
328 | } |
329 | break; |
330 | } |
331 | |
332 | case AddressingMode::ZERO_PAGE: |
333 | { |
334 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
335 | labelFound = mark(d1, CartDebug::REFERENCED); |
336 | if (pass == 3) { |
337 | nextLine << " " ; |
338 | LABEL_A12_LOW(int(d1)); |
339 | nextLineBytes << Base::HEX2 << int(d1); |
340 | } |
341 | break; |
342 | } |
343 | |
344 | case AddressingMode::IMMEDIATE: |
345 | { |
346 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
347 | if (pass == 3) { |
348 | nextLine << " #$" << Base::HEX2 << int(d1) << " " ; |
349 | nextLineBytes << Base::HEX2 << int(d1); |
350 | } |
351 | break; |
352 | } |
353 | |
354 | case AddressingMode::ABSOLUTE_X: |
355 | { |
356 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
357 | labelFound = mark(ad, CartDebug::REFERENCED); |
358 | if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) { |
359 | // Since we can't know what address is being accessed unless we also |
360 | // know the current X value, this is marked as ROW instead of DATA |
361 | // The processing is left here, however, in case future versions of |
362 | // the code can somehow track access to CPU registers |
363 | mark(ad, CartDebug::ROW); |
364 | } else if (pass == 3) { |
365 | if (ad < 0x100 && mySettings.fFlag) |
366 | nextLine << ".wx " ; |
367 | else |
368 | nextLine << " " ; |
369 | |
370 | if (labelFound == 1) { |
371 | LABEL_A12_HIGH(ad); |
372 | nextLine << ",x" ; |
373 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
374 | } else if (labelFound == 4) { |
375 | if (mySettings.rFlag) { |
376 | int tmp = (ad & myAppData.end) + myOffset; |
377 | LABEL_A12_HIGH(tmp); |
378 | nextLine << ",x" ; |
379 | nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8); |
380 | } else { |
381 | nextLine << "$" << Base::HEX4 << ad << ",x" ; |
382 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
383 | } |
384 | } else { |
385 | LABEL_A12_LOW(ad); |
386 | nextLine << ",x" ; |
387 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
388 | } |
389 | } |
390 | break; |
391 | } |
392 | |
393 | case AddressingMode::ABSOLUTE_Y: |
394 | { |
395 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
396 | labelFound = mark(ad, CartDebug::REFERENCED); |
397 | if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) { |
398 | // Since we can't know what address is being accessed unless we also |
399 | // know the current Y value, this is marked as ROW instead of DATA |
400 | // The processing is left here, however, in case future versions of |
401 | // the code can somehow track access to CPU registers |
402 | mark(ad, CartDebug::ROW); |
403 | } else if (pass == 3) { |
404 | if (ad < 0x100 && mySettings.fFlag) |
405 | nextLine << ".wy " ; |
406 | else |
407 | nextLine << " " ; |
408 | |
409 | if (labelFound == 1) { |
410 | LABEL_A12_HIGH(ad); |
411 | nextLine << ",y" ; |
412 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
413 | } else if (labelFound == 4) { |
414 | if (mySettings.rFlag) { |
415 | int tmp = (ad & myAppData.end) + myOffset; |
416 | LABEL_A12_HIGH(tmp); |
417 | nextLine << ",y" ; |
418 | nextLineBytes << Base::HEX2 << int(tmp & 0xff) << " " << Base::HEX2 << int(tmp >> 8); |
419 | } else { |
420 | nextLine << "$" << Base::HEX4 << ad << ",y" ; |
421 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
422 | } |
423 | } else { |
424 | LABEL_A12_LOW(ad); |
425 | nextLine << ",y" ; |
426 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
427 | } |
428 | } |
429 | break; |
430 | } |
431 | |
432 | case AddressingMode::INDIRECT_X: |
433 | { |
434 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
435 | if (pass == 3) { |
436 | labelFound = mark(d1, 0); // dummy call to get address type |
437 | nextLine << " (" ; |
438 | LABEL_A12_LOW(d1); |
439 | nextLine << ",x)" ; |
440 | nextLineBytes << Base::HEX2 << int(d1); |
441 | } |
442 | break; |
443 | } |
444 | |
445 | case AddressingMode::INDIRECT_Y: |
446 | { |
447 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
448 | if (pass == 3) { |
449 | labelFound = mark(d1, 0); // dummy call to get address type |
450 | nextLine << " (" ; |
451 | LABEL_A12_LOW(d1); |
452 | nextLine << "),y" ; |
453 | nextLineBytes << Base::HEX2 << int(d1); |
454 | } |
455 | break; |
456 | } |
457 | |
458 | case AddressingMode::ZERO_PAGE_X: |
459 | { |
460 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
461 | labelFound = mark(d1, CartDebug::REFERENCED); |
462 | if (pass == 3) { |
463 | nextLine << " " ; |
464 | LABEL_A12_LOW(d1); |
465 | nextLine << ",x" ; |
466 | } |
467 | nextLineBytes << Base::HEX2 << int(d1); |
468 | break; |
469 | } |
470 | |
471 | case AddressingMode::ZERO_PAGE_Y: |
472 | { |
473 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
474 | labelFound = mark(d1, CartDebug::REFERENCED); |
475 | if (pass == 3) { |
476 | nextLine << " " ; |
477 | LABEL_A12_LOW(d1); |
478 | nextLine << ",y" ; |
479 | } |
480 | nextLineBytes << Base::HEX2 << int(d1); |
481 | break; |
482 | } |
483 | |
484 | case AddressingMode::RELATIVE: |
485 | { |
486 | // SA - 04-06-2010: there seemed to be a bug in distella, |
487 | // where wraparound occurred on a 32-bit int, and subsequent |
488 | // indexing into the labels array caused a crash |
489 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
490 | ad = ((myPC + Int8(d1)) & 0xfff) + myOffset; |
491 | |
492 | labelFound = mark(ad, CartDebug::REFERENCED); |
493 | if (pass == 3) { |
494 | if (labelFound == 1) { |
495 | nextLine << " " ; |
496 | LABEL_A12_HIGH(ad); |
497 | } else |
498 | nextLine << " $" << Base::HEX4 << ad; |
499 | |
500 | nextLineBytes << Base::HEX2 << int(d1); |
501 | } |
502 | break; |
503 | } |
504 | |
505 | case AddressingMode::ABS_INDIRECT: |
506 | { |
507 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
508 | labelFound = mark(ad, CartDebug::REFERENCED); |
509 | if (pass == 2 && !checkBit(ad & myAppData.end, CartDebug::CODE)) { |
510 | // Since we can't know what address is being accessed unless we also |
511 | // know the current X value, this is marked as ROW instead of DATA |
512 | // The processing is left here, however, in case future versions of |
513 | // the code can somehow track access to CPU registers |
514 | mark(ad, CartDebug::ROW); |
515 | } else if (pass == 3) { |
516 | if (ad < 0x100 && mySettings.fFlag) |
517 | nextLine << ".ind " ; |
518 | else |
519 | nextLine << " " ; |
520 | } |
521 | if (labelFound == 1) { |
522 | nextLine << "(" ; |
523 | LABEL_A12_HIGH(ad); |
524 | nextLine << ")" ; |
525 | } |
526 | // TODO - should we consider case 4?? |
527 | else { |
528 | nextLine << "(" ; |
529 | LABEL_A12_LOW(ad); |
530 | nextLine << ")" ; |
531 | } |
532 | |
533 | nextLineBytes << Base::HEX2 << int(ad & 0xff) << " " << Base::HEX2 << int(ad >> 8); |
534 | break; |
535 | } |
536 | |
537 | default: |
538 | break; |
539 | } // end switch |
540 | |
541 | if (pass == 3) { |
542 | cycles += int(ourLookup[opcode].cycles); |
543 | // A complete line of disassembly (text, cycle count, and bytes) |
544 | myDisasmBuf << nextLine.str() << "'" |
545 | << ";" << std::dec << int(ourLookup[opcode].cycles) |
546 | << (addrMode == AddressingMode::RELATIVE ? (ad & 0xf00) != ((myPC + myOffset) & 0xf00) ? "/3!" : "/3 " : " " ); |
547 | if ((opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00 // code block end |
548 | || checkBit(myPC, CartDebug::REFERENCED) // referenced address |
549 | || (ourLookup[opcode].rw_mode == RWMode::WRITE && d1 == WSYNC)) // strobe WSYNC |
550 | && cycles > 0) { |
551 | // output cycles for previous code block |
552 | myDisasmBuf << "'= " << std::setw(3) << std::setfill(' ') << std::dec << cycles; |
553 | cycles = 0; |
554 | } else { |
555 | myDisasmBuf << "' " ; |
556 | } |
557 | myDisasmBuf << "'" << nextLineBytes.str(); |
558 | |
559 | addEntry(CartDebug::CODE); |
560 | if (opcode == 0x40 || opcode == 0x60 || opcode == 0x4c || opcode == 0x00) { |
561 | myDisasmBuf << " ' ' " ; |
562 | addEntry(CartDebug::NONE); |
563 | mySegType = CartDebug::NONE; // prevent extra lines if data follows |
564 | } |
565 | |
566 | nextLine.str("" ); |
567 | nextLineBytes.str("" ); |
568 | } |
569 | } |
570 | } /* while loop */ |
571 | |
572 | /* Just in case we are disassembling outside of the address range, force the myPCEnd to EOF */ |
573 | myPCEnd = myAppData.end + myOffset; |
574 | } |
575 | |
576 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
577 | void DiStella::disasmPass1(CartDebug::AddressList& debuggerAddresses) |
578 | { |
579 | auto it = debuggerAddresses.cbegin(); |
580 | uInt16 start = *it++; |
581 | |
582 | // After we've disassembled from all addresses in the address list, |
583 | // use all access points determined by Stella during emulation |
584 | int codeAccessPoint = 0; |
585 | |
586 | // Sometimes we get a circular reference, in that processing a certain |
587 | // PC address leads us to a sequence of addresses that end up trying |
588 | // to process the same address again. We detect such consecutive PC |
589 | // addresses and only process the first one |
590 | uInt16 lastPC = 0; |
591 | bool duplicateFound = false; |
592 | |
593 | while (!myAddressQueue.empty()) |
594 | myAddressQueue.pop(); |
595 | myAddressQueue.push(start); |
596 | |
597 | while (!(myAddressQueue.empty() || duplicateFound)) { |
598 | uInt16 pcBeg = myPC = lastPC = myAddressQueue.front(); |
599 | myAddressQueue.pop(); |
600 | |
601 | disasmFromAddress(myPC); |
602 | |
603 | if (pcBeg <= myPCEnd) { |
604 | // Tentatively mark all addresses in the range as CODE |
605 | // Note that this is a 'best-effort' approach, since |
606 | // Distella will normally keep going until the end of the |
607 | // range or branch is encountered |
608 | // However, addresses *specifically* marked as DATA/GFX/PGFX |
609 | // in the emulation core indicate that the CODE range has finished |
610 | // Therefore, we stop at the first such address encountered |
611 | for (uInt32 k = pcBeg; k <= myPCEnd; ++k) { |
612 | if (checkBits(k, CartDebug::CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX, |
613 | CartDebug::CODE)) { |
614 | //if (Debugger::debugger().getAccessFlags(k) & |
615 | // (CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX)) { |
616 | // TODO: this should never happen, remove when we are sure |
617 | // TODO: NOT USED: uInt8 flags = Debugger::debugger().getAccessFlags(k); |
618 | myPCEnd = k - 1; |
619 | break; |
620 | } |
621 | mark(k, CartDebug::CODE); |
622 | } |
623 | } |
624 | |
625 | // When we get to this point, all addresses have been processed |
626 | // starting from the initial one in the address list |
627 | // If so, process the next one in the list that hasn't already |
628 | // been marked as CODE |
629 | // If it *has* been marked, it can be removed from consideration |
630 | // in all subsequent passes |
631 | // |
632 | // Once the address list has been exhausted, we process all addresses |
633 | // determined during emulation to represent code, which *haven't* already |
634 | // been considered |
635 | // |
636 | // Note that we can't simply add all addresses right away, since |
637 | // the processing of a single address can cause others to be added in |
638 | // the ::disasm method |
639 | // All of these have to be exhausted before considering a new address |
640 | while (myAddressQueue.empty() && it != debuggerAddresses.end()) { |
641 | uInt16 addr = *it; |
642 | |
643 | if (!checkBit(addr - myOffset, CartDebug::CODE)) { |
644 | myAddressQueue.push(addr); |
645 | ++it; |
646 | } else // remove this address, it is redundant |
647 | it = debuggerAddresses.erase(it); |
648 | } |
649 | |
650 | // Stella itself can provide hints on whether an address has ever |
651 | // been referenced as CODE |
652 | while (myAddressQueue.empty() && codeAccessPoint <= myAppData.end) { |
653 | if ((Debugger::debugger().getAccessFlags(codeAccessPoint + myOffset) & CartDebug::CODE) |
654 | && !(myLabels[codeAccessPoint & myAppData.end] & CartDebug::CODE)) { |
655 | myAddressQueue.push(codeAccessPoint + myOffset); |
656 | ++codeAccessPoint; |
657 | break; |
658 | } |
659 | ++codeAccessPoint; |
660 | } |
661 | duplicateFound = !myAddressQueue.empty() && (myAddressQueue.front() == lastPC); // TODO: check! |
662 | } // while |
663 | |
664 | for (int k = 0; k <= myAppData.end; k++) { |
665 | // Let the emulation core know about tentative code |
666 | if (checkBit(k, CartDebug::CODE) && |
667 | !(Debugger::debugger().getAccessFlags(k + myOffset) & CartDebug::CODE) |
668 | && myOffset != 0) { |
669 | Debugger::debugger().setAccessFlags(k + myOffset, CartDebug::TCODE); |
670 | } |
671 | |
672 | // Must be ROW / unused bytes |
673 | if (!checkBit(k, CartDebug::CODE | CartDebug::GFX | |
674 | CartDebug::PGFX | CartDebug::DATA)) |
675 | mark(k + myOffset, CartDebug::ROW); |
676 | } |
677 | } |
678 | |
679 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
680 | void DiStella::disasmFromAddress(uInt32 distart) |
681 | { |
682 | uInt8 opcode, d1; |
683 | uInt16 ad; |
684 | AddressingMode addrMode; |
685 | |
686 | myPC = distart - myOffset; |
687 | |
688 | while (myPC <= myAppData.end) { |
689 | |
690 | // abort when we reach non-code areas |
691 | if (checkBits(myPC, CartDebug::CartDebug::DATA | CartDebug::GFX | CartDebug::PGFX, CartDebug::CODE)) { |
692 | myPCEnd = (myPC - 1) + myOffset; |
693 | return; |
694 | } |
695 | |
696 | // so this should be code now... |
697 | // get opcode |
698 | opcode = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
699 | // get address mode for opcode |
700 | addrMode = ourLookup[opcode].addr_mode; |
701 | |
702 | // Add operand(s) for PC values outside the app data range |
703 | if (myPC >= myAppData.end) { |
704 | switch (addrMode) { |
705 | case AddressingMode::ABSOLUTE: |
706 | case AddressingMode::ABSOLUTE_X: |
707 | case AddressingMode::ABSOLUTE_Y: |
708 | case AddressingMode::INDIRECT_X: |
709 | case AddressingMode::INDIRECT_Y: |
710 | case AddressingMode::ABS_INDIRECT: |
711 | myPCEnd = myAppData.end + myOffset; |
712 | return; |
713 | |
714 | case AddressingMode::ZERO_PAGE: |
715 | case AddressingMode::IMMEDIATE: |
716 | case AddressingMode::ZERO_PAGE_X: |
717 | case AddressingMode::ZERO_PAGE_Y: |
718 | case AddressingMode::RELATIVE: |
719 | if (myPC > myAppData.end) { |
720 | ++myPC; |
721 | myPCEnd = myAppData.end + myOffset; |
722 | return; |
723 | } |
724 | break; // TODO - is this the intent? |
725 | |
726 | default: |
727 | break; |
728 | } // end switch(addr_mode) |
729 | } // end if (myPC >= myAppData.end) |
730 | |
731 | // Add operand(s) |
732 | switch (addrMode) { |
733 | case AddressingMode::ABSOLUTE: |
734 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
735 | mark(ad, CartDebug::REFERENCED); |
736 | // handle JMP/JSR |
737 | if (ourLookup[opcode].source == AccessMode::ADDR) { |
738 | // do NOT use flags set by debugger, else known CODE will not analyzed statically. |
739 | if (!checkBit(ad & myAppData.end, CartDebug::CODE, false)) { |
740 | if (ad > 0xfff) |
741 | myAddressQueue.push((ad & myAppData.end) + myOffset); |
742 | mark(ad, CartDebug::CODE); |
743 | } |
744 | } else |
745 | mark(ad, CartDebug::DATA); |
746 | break; |
747 | |
748 | case AddressingMode::ZERO_PAGE: |
749 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
750 | mark(d1, CartDebug::REFERENCED); |
751 | break; |
752 | |
753 | case AddressingMode::IMMEDIATE: |
754 | ++myPC; |
755 | break; |
756 | |
757 | case AddressingMode::ABSOLUTE_X: |
758 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
759 | mark(ad, CartDebug::REFERENCED); |
760 | break; |
761 | |
762 | case AddressingMode::ABSOLUTE_Y: |
763 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
764 | mark(ad, CartDebug::REFERENCED); |
765 | break; |
766 | |
767 | case AddressingMode::INDIRECT_X: |
768 | ++myPC; |
769 | break; |
770 | |
771 | case AddressingMode::INDIRECT_Y: |
772 | ++myPC; |
773 | break; |
774 | |
775 | case AddressingMode::ZERO_PAGE_X: |
776 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
777 | mark(d1, CartDebug::REFERENCED); |
778 | break; |
779 | |
780 | case AddressingMode::ZERO_PAGE_Y: |
781 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
782 | mark(d1, CartDebug::REFERENCED); |
783 | break; |
784 | |
785 | case AddressingMode::RELATIVE: |
786 | // SA - 04-06-2010: there seemed to be a bug in distella, |
787 | // where wraparound occurred on a 32-bit int, and subsequent |
788 | // indexing into the labels array caused a crash |
789 | d1 = Debugger::debugger().peek(myPC + myOffset); ++myPC; |
790 | ad = ((myPC + Int8(d1)) & 0xfff) + myOffset; |
791 | mark(ad, CartDebug::REFERENCED); |
792 | // do NOT use flags set by debugger, else known CODE will not analyzed statically. |
793 | if (!checkBit(ad - myOffset, CartDebug::CODE, false)) { |
794 | myAddressQueue.push(ad); |
795 | mark(ad, CartDebug::CODE); |
796 | } |
797 | break; |
798 | |
799 | case AddressingMode::ABS_INDIRECT: |
800 | ad = Debugger::debugger().dpeek(myPC + myOffset); myPC += 2; |
801 | mark(ad, CartDebug::REFERENCED); |
802 | break; |
803 | |
804 | default: |
805 | break; |
806 | } // end switch |
807 | |
808 | // mark BRK vector |
809 | if (opcode == 0x00) { |
810 | ad = Debugger::debugger().dpeek(0xfffe, CartDebug::DATA); |
811 | if (!myReserved.breakFound) { |
812 | myAddressQueue.push(ad); |
813 | mark(ad, CartDebug::CODE); |
814 | myReserved.breakFound = true; |
815 | } |
816 | } |
817 | |
818 | // JMP/RTS/RTI always indicate the end of a block of CODE |
819 | if (opcode == 0x4c || opcode == 0x60 || opcode == 0x40) { |
820 | // code block end |
821 | myPCEnd = (myPC - 1) + myOffset; |
822 | return; |
823 | } |
824 | } // while |
825 | } |
826 | |
827 | |
828 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
829 | int DiStella::mark(uInt32 address, uInt8 mask, bool directive) |
830 | { |
831 | /*----------------------------------------------------------------------- |
832 | For any given offset and code range... |
833 | |
834 | If we're between the offset and the end of the code range, we mark |
835 | the bit in the labels array for that data. The labels array is an |
836 | array of label info for each code address. If this is the case, |
837 | return "1", else... |
838 | |
839 | We sweep for hardware/system equates, which are valid addresses, |
840 | outside the scope of the code/data range. For these, we mark its |
841 | corresponding hardware/system array element, and return "2" or "3" |
842 | (depending on which system/hardware element was accessed). |
843 | If this was not the case... |
844 | |
845 | Next we check if it is a code "mirror". For the 2600, address ranges |
846 | are limited with 13 bits, so other addresses can exist outside of the |
847 | standard code/data range. For these, we mark the element in the "labels" |
848 | array that corresponds to the mirrored address, and return "4" |
849 | |
850 | If all else fails, it's not a valid address, so return 0. |
851 | |
852 | A quick example breakdown for a 2600 4K cart: |
853 | =========================================================== |
854 | $00-$3d = system equates (WSYNC, etc...); return 2. |
855 | $80-$ff = zero-page RAM (ram_80, etc...); return 5. |
856 | $0280-$0297 = system equates (INPT0, etc...); mark the array's element |
857 | with the appropriate bit; return 3. |
858 | $1000-$1FFF = mark the code/data array for the mirrored address |
859 | with the appropriate bit; return 4. |
860 | $3000-$3FFF = mark the code/data array for the mirrored address |
861 | with the appropriate bit; return 4. |
862 | $5000-$5FFF = mark the code/data array for the mirrored address |
863 | with the appropriate bit; return 4. |
864 | $7000-$7FFF = mark the code/data array for the mirrored address |
865 | with the appropriate bit; return 4. |
866 | $9000-$9FFF = mark the code/data array for the mirrored address |
867 | with the appropriate bit; return 4. |
868 | $B000-$BFFF = mark the code/data array for the mirrored address |
869 | with the appropriate bit; return 4. |
870 | $D000-$DFFF = mark the code/data array for the mirrored address |
871 | with the appropriate bit; return 4. |
872 | $F000-$FFFF = mark the code/data array for the address |
873 | with the appropriate bit; return 1. |
874 | Anything else = invalid, return 0. |
875 | =========================================================== |
876 | -----------------------------------------------------------------------*/ |
877 | |
878 | // Check for equates before ROM/ZP-RAM accesses, because the original logic |
879 | // of Distella assumed either equates or ROM; it didn't take ZP-RAM into account |
880 | CartDebug::AddrType type = myDbg.addressType(address); |
881 | if (type == CartDebug::AddrType::TIA) { |
882 | return 2; |
883 | } else if (type == CartDebug::AddrType::IO) { |
884 | return 3; |
885 | } else if (type == CartDebug::AddrType::ZPRAM && myOffset != 0) { |
886 | return 5; |
887 | } else if (address >= uInt32(myOffset) && address <= uInt32(myAppData.end + myOffset)) { |
888 | myLabels[address - myOffset] = myLabels[address - myOffset] | mask; |
889 | if (directive) myDirectives[address - myOffset] = mask; |
890 | return 1; |
891 | } else if (address > 0x1000 && myOffset != 0) // Exclude zero-page accesses |
892 | { |
893 | /* 2K & 4K case */ |
894 | myLabels[address & myAppData.end] = myLabels[address & myAppData.end] | mask; |
895 | if (directive) myDirectives[address & myAppData.end] = mask; |
896 | return 4; |
897 | } else |
898 | return 0; |
899 | } |
900 | |
901 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
902 | bool DiStella::checkBit(uInt16 address, uInt8 mask, bool useDebugger) const |
903 | { |
904 | // The REFERENCED and VALID_ENTRY flags are needed for any inspection of |
905 | // an address |
906 | // Since they're set only in the labels array (as the lower two bits), |
907 | // they must be included in the other bitfields |
908 | uInt8 label = myLabels[address & myAppData.end], |
909 | lastbits = label & 0x03, |
910 | directive = myDirectives[address & myAppData.end] & 0xFC, |
911 | debugger = Debugger::debugger().getAccessFlags(address | myOffset) & 0xFC; |
912 | |
913 | // Any address marked by a manual directive always takes priority |
914 | if (directive) |
915 | return (directive | lastbits) & mask; |
916 | // Next, the results from a dynamic/runtime analysis are used (except for pass 1) |
917 | else if (useDebugger && ((debugger | lastbits) & mask)) |
918 | return true; |
919 | // Otherwise, default to static analysis from Distella |
920 | else |
921 | return label & mask; |
922 | } |
923 | |
924 | bool DiStella::checkBits(uInt16 address, uInt8 mask, uInt8 notMask, bool useDebugger) const |
925 | { |
926 | return checkBit(address, mask, useDebugger) && !checkBit(address, notMask, useDebugger); |
927 | } |
928 | |
929 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
930 | bool DiStella::check_range(uInt16 beg, uInt16 end) const |
931 | { |
932 | if (beg > end) { |
933 | cerr << "Beginning of range greater than end: start = " << std::hex << beg |
934 | << ", end = " << std::hex << end << endl; |
935 | return false; |
936 | } else if (beg > myAppData.end + myOffset) { |
937 | cerr << "Beginning of range out of range: start = " << std::hex << beg |
938 | << ", range = " << std::hex << (myAppData.end + myOffset) << endl; |
939 | return false; |
940 | } else if (beg < myOffset) { |
941 | cerr << "Beginning of range out of range: start = " << std::hex << beg |
942 | << ", offset = " << std::hex << myOffset << endl; |
943 | return false; |
944 | } |
945 | return true; |
946 | } |
947 | |
948 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
949 | void DiStella::addEntry(CartDebug::DisasmType type) |
950 | { |
951 | CartDebug::DisassemblyTag tag; |
952 | |
953 | // Type |
954 | tag.type = type; |
955 | |
956 | // Address |
957 | myDisasmBuf.seekg(0, std::ios::beg); |
958 | if (myDisasmBuf.peek() == ' ') |
959 | tag.address = 0; |
960 | else |
961 | myDisasmBuf >> std::setw(4) >> std::hex >> tag.address; |
962 | |
963 | // Only include addresses within the requested range |
964 | if (tag.address < myAppData.start) |
965 | goto DONE_WITH_ADD; |
966 | |
967 | // Label (a user-defined label always overrides any auto-generated one) |
968 | myDisasmBuf.seekg(5, std::ios::beg); |
969 | if (tag.address) { |
970 | tag.label = myDbg.getLabel(tag.address, true); |
971 | tag.hllabel = true; |
972 | if (tag.label == EmptyString) { |
973 | if (myDisasmBuf.peek() != ' ') |
974 | getline(myDisasmBuf, tag.label, '\''); |
975 | else if (mySettings.showAddresses && tag.type == CartDebug::CODE) { |
976 | // Have addresses indented, to differentiate from actual labels |
977 | tag.label = " " + Base::toString(tag.address, Base::F_16_4); |
978 | tag.hllabel = false; |
979 | } |
980 | } |
981 | } |
982 | |
983 | // Disassembly |
984 | // Up to this point the field sizes are fixed, until we get to |
985 | // variable length labels, cycle counts, etc |
986 | myDisasmBuf.seekg(11, std::ios::beg); |
987 | switch (tag.type) { |
988 | case CartDebug::CODE: |
989 | getline(myDisasmBuf, tag.disasm, '\''); |
990 | getline(myDisasmBuf, tag.ccount, '\''); |
991 | getline(myDisasmBuf, tag.ctotal, '\''); |
992 | getline(myDisasmBuf, tag.bytes); |
993 | |
994 | // Make note of when we override CODE sections from the debugger |
995 | // It could mean that the code hasn't been accessed up to this point, |
996 | // but it could also indicate that code will *never* be accessed |
997 | // Since it is impossible to tell the difference, marking the address |
998 | // in the disassembly at least tells the user about it |
999 | if (!(Debugger::debugger().getAccessFlags(tag.address) & CartDebug::CODE) |
1000 | && myOffset != 0) { |
1001 | tag.ccount += " *" ; |
1002 | Debugger::debugger().setAccessFlags(tag.address, CartDebug::TCODE); |
1003 | } |
1004 | break; |
1005 | case CartDebug::GFX: |
1006 | case CartDebug::PGFX: |
1007 | getline(myDisasmBuf, tag.disasm, '\''); |
1008 | getline(myDisasmBuf, tag.bytes); |
1009 | break; |
1010 | case CartDebug::DATA: |
1011 | getline(myDisasmBuf, tag.disasm, '\''); |
1012 | getline(myDisasmBuf, tag.bytes); |
1013 | break; |
1014 | case CartDebug::ROW: |
1015 | getline(myDisasmBuf, tag.disasm); |
1016 | break; |
1017 | case CartDebug::NONE: |
1018 | default: // should never happen |
1019 | tag.disasm = " " ; |
1020 | break; |
1021 | } |
1022 | myList.push_back(tag); |
1023 | |
1024 | DONE_WITH_ADD: |
1025 | myDisasmBuf.clear(); |
1026 | myDisasmBuf.str("" ); |
1027 | } |
1028 | |
1029 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1030 | void DiStella::outputGraphics() |
1031 | { |
1032 | bool isPGfx = checkBit(myPC, CartDebug::PGFX); |
1033 | const string& bitString = isPGfx ? "\x1f" : "\x1e" ; |
1034 | uInt8 byte = Debugger::debugger().peek(myPC + myOffset); |
1035 | |
1036 | // add extra spacing line when switching from non-graphics to graphics |
1037 | if (mySegType != CartDebug::GFX && mySegType != CartDebug::NONE) { |
1038 | myDisasmBuf << " ' ' " ; |
1039 | addEntry(CartDebug::NONE); |
1040 | } |
1041 | mySegType = CartDebug::GFX; |
1042 | |
1043 | if (checkBit(myPC, CartDebug::REFERENCED)) |
1044 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 << myPC + myOffset << "'" ; |
1045 | else |
1046 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '" ; |
1047 | myDisasmBuf << ".byte $" << Base::HEX2 << int(byte) << " |" ; |
1048 | for (uInt8 i = 0, c = byte; i < 8; ++i, c <<= 1) |
1049 | myDisasmBuf << ((c > 127) ? bitString : " " ); |
1050 | myDisasmBuf << "| $" << Base::HEX4 << myPC + myOffset << "'" ; |
1051 | if (mySettings.gfxFormat == Base::F_2) |
1052 | myDisasmBuf << Base::toString(byte, Base::F_2_8); |
1053 | else |
1054 | myDisasmBuf << Base::HEX2 << int(byte); |
1055 | |
1056 | addEntry(isPGfx ? CartDebug::PGFX : CartDebug::GFX); |
1057 | } |
1058 | |
1059 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1060 | void DiStella::outputBytes(CartDebug::DisasmType type) |
1061 | { |
1062 | bool isType = true; |
1063 | bool referenced = checkBit(myPC, CartDebug::REFERENCED); |
1064 | bool lineEmpty = true; |
1065 | int numBytes = 0; |
1066 | |
1067 | // add extra spacing line when switching from non-data to data |
1068 | if (mySegType != CartDebug::DATA && mySegType != CartDebug::NONE) { |
1069 | myDisasmBuf << " ' ' " ; |
1070 | addEntry(CartDebug::NONE); |
1071 | } |
1072 | mySegType = CartDebug::DATA; |
1073 | |
1074 | while (isType && myPC <= myAppData.end) { |
1075 | if (referenced) { |
1076 | // start a new line with a label |
1077 | if (!lineEmpty) |
1078 | addEntry(type); |
1079 | |
1080 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "'L" << Base::HEX4 |
1081 | << myPC + myOffset << "'.byte " << "$" << Base::HEX2 |
1082 | << int(Debugger::debugger().peek(myPC + myOffset)); |
1083 | ++myPC; |
1084 | numBytes = 1; |
1085 | lineEmpty = false; |
1086 | } else if (lineEmpty) { |
1087 | // start a new line without a label |
1088 | myDisasmBuf << Base::HEX4 << myPC + myOffset << "' '" |
1089 | << ".byte $" << Base::HEX2 << int(Debugger::debugger().peek(myPC + myOffset)); |
1090 | ++myPC; |
1091 | numBytes = 1; |
1092 | lineEmpty = false; |
1093 | } |
1094 | // Otherwise, append bytes to the current line, up until the maximum |
1095 | else if (++numBytes == mySettings.bytesWidth) { |
1096 | addEntry(type); |
1097 | lineEmpty = true; |
1098 | } else { |
1099 | myDisasmBuf << ",$" << Base::HEX2 << int(Debugger::debugger().peek(myPC + myOffset)); |
1100 | ++myPC; |
1101 | } |
1102 | isType = checkBits(myPC, type, |
1103 | CartDebug::CODE | (type != CartDebug::DATA ? CartDebug::DATA : 0) | CartDebug::GFX | CartDebug::PGFX); |
1104 | referenced = checkBit(myPC, CartDebug::REFERENCED); |
1105 | } |
1106 | if (!lineEmpty) |
1107 | addEntry(type); |
1108 | /*myDisasmBuf << " ' ' "; |
1109 | addEntry(CartDebug::NONE);*/ |
1110 | } |
1111 | |
1112 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1113 | void DiStella::processDirectives(const CartDebug::DirectiveList& directives) |
1114 | { |
1115 | for (const auto& tag : directives) { |
1116 | if (check_range(tag.start, tag.end)) |
1117 | for (uInt32 k = tag.start; k <= tag.end; ++k) |
1118 | mark(k, tag.type, true); |
1119 | } |
1120 | } |
1121 | |
1122 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1123 | DiStella::Settings DiStella::settings = { |
1124 | Base::F_2, // gfxFormat |
1125 | true, // resolveCode (opposite of -d in Distella) |
1126 | true, // showAddresses (not used externally; always off) |
1127 | false, // aFlag (-a in Distella) |
1128 | true, // fFlag (-f in Distella) |
1129 | false, // rFlag (-r in Distella) |
1130 | false, // bFlag (-b in Distella) |
1131 | 8+1 // number of bytes to use with .byte directive |
1132 | }; |
1133 | |
1134 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
1135 | const std::array<DiStella::Instruction_tag, 256> DiStella::ourLookup = { { |
1136 | /**** Positive ****/ |
1137 | |
1138 | /* 00 */{"brk" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 7, 1}, /* Pseudo Absolute */ |
1139 | /* 01 */{"ora" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect,X) */ |
1140 | /* 02 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1141 | /* 03 */{"SLO" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1142 | |
1143 | /* 04 */{"NOP" , AddressingMode::ZERO_PAGE, AccessMode::NONE, RWMode::NONE, 3, 2}, |
1144 | /* 05 */{"ora" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1145 | /* 06 */{"asl" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1146 | /* 07 */{"SLO" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1147 | |
1148 | /* 08 */{"php" , AddressingMode::IMPLIED, AccessMode::SR, RWMode::NONE, 3, 1}, |
1149 | /* 09 */{"ora" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1150 | /* 0a */{"asl" , AddressingMode::ACCUMULATOR, AccessMode::AC, RWMode::WRITE, 2, 1}, /* Accumulator */ |
1151 | /* 0b */{"ANC" , AddressingMode::IMMEDIATE, AccessMode::ACIM, RWMode::READ, 2, 2}, |
1152 | |
1153 | /* 0c */{"NOP" , AddressingMode::ABSOLUTE, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1154 | /* 0d */{"ora" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1155 | /* 0e */{"asl" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1156 | /* 0f */{"SLO" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1157 | |
1158 | /* 10 */{"bpl" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1159 | /* 11 */{"ora" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1160 | /* 12 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1161 | /* 13 */{"SLO" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1162 | |
1163 | /* 14 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1164 | /* 15 */{"ora" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1165 | /* 16 */{"asl" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1166 | /* 17 */{"SLO" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1167 | |
1168 | /* 18 */{"clc" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1169 | /* 19 */{"ora" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1170 | /* 1a */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1171 | /* 1b */{"SLO" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1172 | |
1173 | /* 1c */{"NOP" , AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1174 | /* 1d */{"ora" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1175 | /* 1e */{"asl" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1176 | /* 1f */{"SLO" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, |
1177 | |
1178 | /* 20 */{"jsr" , AddressingMode::ABSOLUTE, AccessMode::ADDR, RWMode::READ, 6, 3}, |
1179 | /* 21 */{"and" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect ,X) */ |
1180 | /* 22 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1181 | /* 23 */{"RLA" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1182 | |
1183 | /* 24 */{"bit" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1184 | /* 25 */{"and" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1185 | /* 26 */{"rol" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1186 | /* 27 */{"RLA" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1187 | |
1188 | /* 28 */{"plp" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 4, 1}, |
1189 | /* 29 */{"and" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1190 | /* 2a */{"rol" , AddressingMode::ACCUMULATOR, AccessMode::AC, RWMode::WRITE, 2, 1}, /* Accumulator */ |
1191 | /* 2b */{"ANC" , AddressingMode::IMMEDIATE, AccessMode::ACIM, RWMode::READ, 2, 2}, |
1192 | |
1193 | /* 2c */{"bit" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1194 | /* 2d */{"and" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1195 | /* 2e */{"rol" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1196 | /* 2f */{"RLA" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1197 | |
1198 | /* 30 */{"bmi" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1199 | /* 31 */{"and" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1200 | /* 32 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1201 | /* 33 */{"RLA" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1202 | |
1203 | /* 34 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1204 | /* 35 */{"and" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1205 | /* 36 */{"rol" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1206 | /* 37 */{"RLA" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1207 | |
1208 | /* 38 */{"sec" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1209 | /* 39 */{"and" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1210 | /* 3a */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1211 | /* 3b */{"RLA" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1212 | |
1213 | /* 3c */{"NOP" , AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1214 | /* 3d */{"and" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1215 | /* 3e */{"rol" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1216 | /* 3f */{"RLA" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, |
1217 | |
1218 | /* 40 */{"rti" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 6, 1}, |
1219 | /* 41 */{"eor" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect,X) */ |
1220 | /* 42 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1221 | /* 43 */{"SRE" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1222 | |
1223 | /* 44 */{"NOP" , AddressingMode::ZERO_PAGE, AccessMode::NONE, RWMode::NONE, 3, 2}, |
1224 | /* 45 */{"eor" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1225 | /* 46 */{"lsr" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1226 | /* 47 */{"SRE" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1227 | |
1228 | /* 48 */{"pha" , AddressingMode::IMPLIED, AccessMode::AC, RWMode::NONE, 3, 1}, |
1229 | /* 49 */{"eor" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1230 | /* 4a */{"lsr" , AddressingMode::ACCUMULATOR, AccessMode::AC, RWMode::WRITE, 2, 1}, /* Accumulator */ |
1231 | /* 4b */{"ASR" , AddressingMode::IMMEDIATE, AccessMode::ACIM, RWMode::READ, 2, 2}, /* (AC & IMM) >>1 */ |
1232 | |
1233 | /* 4c */{"jmp" , AddressingMode::ABSOLUTE, AccessMode::ADDR, RWMode::READ, 3, 3}, /* Absolute */ |
1234 | /* 4d */{"eor" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1235 | /* 4e */{"lsr" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1236 | /* 4f */{"SRE" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1237 | |
1238 | /* 50 */{"bvc" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1239 | /* 51 */{"eor" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1240 | /* 52 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1241 | /* 53 */{"SRE" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1242 | |
1243 | /* 54 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1244 | /* 55 */{"eor" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1245 | /* 56 */{"lsr" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1246 | /* 57 */{"SRE" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1247 | |
1248 | /* 58 */{"cli" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1249 | /* 59 */{"eor" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1250 | /* 5a */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1251 | /* 5b */{"SRE" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1252 | |
1253 | /* 5c */{"NOP" , AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1254 | /* 5d */{"eor" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1255 | /* 5e */{"lsr" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1256 | /* 5f */{"SRE" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, |
1257 | |
1258 | /* 60 */{"rts" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 6, 1}, |
1259 | /* 61 */{"adc" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect,X) */ |
1260 | /* 62 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1261 | /* 63 */{"RRA" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1262 | |
1263 | /* 64 */{"NOP" , AddressingMode::ZERO_PAGE, AccessMode::NONE, RWMode::NONE, 3, 2}, |
1264 | /* 65 */{"adc" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1265 | /* 66 */{"ror" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1266 | /* 67 */{"RRA" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1267 | |
1268 | /* 68 */{"pla" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 4, 1}, |
1269 | /* 69 */{"adc" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1270 | /* 6a */{"ror" , AddressingMode::ACCUMULATOR, AccessMode::AC, RWMode::WRITE, 2, 1}, /* Accumulator */ |
1271 | /* 6b */{"ARR" , AddressingMode::IMMEDIATE, AccessMode::ACIM, RWMode::READ, 2, 2}, /* ARR isn't typo */ |
1272 | |
1273 | /* 6c */{"jmp" , AddressingMode::ABS_INDIRECT,AccessMode::AIND, RWMode::READ, 5, 3}, /* Indirect */ |
1274 | /* 6d */{"adc" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1275 | /* 6e */{"ror" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1276 | /* 6f */{"RRA" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1277 | |
1278 | /* 70 */{"bvs" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1279 | /* 71 */{"adc" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1280 | /* 72 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT relative? */ |
1281 | /* 73 */{"RRA" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1282 | |
1283 | /* 74 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1284 | /* 75 */{"adc" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1285 | /* 76 */{"ror" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1286 | /* 77 */{"RRA" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1287 | |
1288 | /* 78 */{"sei" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1289 | /* 79 */{"adc" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1290 | /* 7a */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1291 | /* 7b */{"RRA" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1292 | |
1293 | /* 7c */{"NOP" , AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1294 | /* 7d */{"adc" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1295 | /* 7e */{"ror" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1296 | /* 7f */{"RRA" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, |
1297 | |
1298 | /**** Negative ****/ |
1299 | |
1300 | /* 80 */{"NOP" , AddressingMode::IMMEDIATE, AccessMode::NONE, RWMode::NONE, 2, 2}, |
1301 | /* 81 */{"sta" , AddressingMode::INDIRECT_X, AccessMode::AC, RWMode::WRITE, 6, 2}, /* (Indirect,X) */ |
1302 | /* 82 */{"NOP" , AddressingMode::IMMEDIATE, AccessMode::NONE, RWMode::NONE, 2, 2}, |
1303 | /* 83 */{"SAX" , AddressingMode::INDIRECT_X, AccessMode::ANXR, RWMode::WRITE, 6, 2}, |
1304 | |
1305 | /* 84 */{"sty" , AddressingMode::ZERO_PAGE, AccessMode::YR, RWMode::WRITE, 3, 2}, /* Zeropage */ |
1306 | /* 85 */{"sta" , AddressingMode::ZERO_PAGE, AccessMode::AC, RWMode::WRITE, 3, 2}, /* Zeropage */ |
1307 | /* 86 */{"stx" , AddressingMode::ZERO_PAGE, AccessMode::XR, RWMode::WRITE, 3, 2}, /* Zeropage */ |
1308 | /* 87 */{"SAX" , AddressingMode::ZERO_PAGE, AccessMode::ANXR, RWMode::WRITE, 3, 2}, |
1309 | |
1310 | /* 88 */{"dey" , AddressingMode::IMPLIED, AccessMode::YR, RWMode::NONE, 2, 1}, |
1311 | /* 89 */{"NOP" , AddressingMode::IMMEDIATE, AccessMode::NONE, RWMode::NONE, 2, 2}, |
1312 | /* 8a */{"txa" , AddressingMode::IMPLIED, AccessMode::XR, RWMode::NONE, 2, 1}, |
1313 | /**** very abnormal: usually AC = AC | #$EE & XR & #$oper ****/ |
1314 | /* 8b */{"ANE" , AddressingMode::IMMEDIATE, AccessMode::AXIM, RWMode::READ, 2, 2}, |
1315 | |
1316 | /* 8c */{"sty" , AddressingMode::ABSOLUTE, AccessMode::YR, RWMode::WRITE, 4, 3}, /* Absolute */ |
1317 | /* 8d */{"sta" , AddressingMode::ABSOLUTE, AccessMode::AC, RWMode::WRITE, 4, 3}, /* Absolute */ |
1318 | /* 8e */{"stx" , AddressingMode::ABSOLUTE, AccessMode::XR, RWMode::WRITE, 4, 3}, /* Absolute */ |
1319 | /* 8f */{"SAX" , AddressingMode::ABSOLUTE, AccessMode::ANXR, RWMode::WRITE, 4, 3}, |
1320 | |
1321 | /* 90 */{"bcc" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1322 | /* 91 */{"sta" , AddressingMode::INDIRECT_Y, AccessMode::AC, RWMode::WRITE, 6, 2}, /* (Indirect),Y */ |
1323 | /* 92 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT relative? */ |
1324 | /* 93 */{"SHA" , AddressingMode::INDIRECT_Y, AccessMode::ANXR, RWMode::WRITE, 6, 2}, |
1325 | |
1326 | /* 94 */{"sty" , AddressingMode::ZERO_PAGE_X, AccessMode::YR, RWMode::WRITE, 4, 2}, /* Zeropage,X */ |
1327 | /* 95 */{"sta" , AddressingMode::ZERO_PAGE_X, AccessMode::AC, RWMode::WRITE, 4, 2}, /* Zeropage,X */ |
1328 | /* 96 */{"stx" , AddressingMode::ZERO_PAGE_Y, AccessMode::XR, RWMode::WRITE, 4, 2}, /* Zeropage,Y */ |
1329 | /* 97 */{"SAX" , AddressingMode::ZERO_PAGE_Y, AccessMode::ANXR, RWMode::WRITE, 4, 2}, |
1330 | |
1331 | /* 98 */{"tya" , AddressingMode::IMPLIED, AccessMode::YR, RWMode::NONE, 2, 1}, |
1332 | /* 99 */{"sta" , AddressingMode::ABSOLUTE_Y, AccessMode::AC, RWMode::WRITE, 5, 3}, /* Absolute,Y */ |
1333 | /* 9a */{"txs" , AddressingMode::IMPLIED, AccessMode::XR, RWMode::NONE, 2, 1}, |
1334 | /*** This is very mysterious command ... */ |
1335 | /* 9b */{"SHS" , AddressingMode::ABSOLUTE_Y, AccessMode::ANXR, RWMode::WRITE, 5, 3}, |
1336 | |
1337 | /* 9c */{"SHY" , AddressingMode::ABSOLUTE_X, AccessMode::YR, RWMode::WRITE, 5, 3}, |
1338 | /* 9d */{"sta" , AddressingMode::ABSOLUTE_X, AccessMode::AC, RWMode::WRITE, 5, 3}, /* Absolute,X */ |
1339 | /* 9e */{"SHX" , AddressingMode::ABSOLUTE_Y, AccessMode::XR , RWMode::WRITE, 5, 3}, |
1340 | /* 9f */{"SHA" , AddressingMode::ABSOLUTE_Y, AccessMode::ANXR, RWMode::WRITE, 5, 3}, |
1341 | |
1342 | /* a0 */{"ldy" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1343 | /* a1 */{"lda" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (indirect,X) */ |
1344 | /* a2 */{"ldx" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1345 | /* a3 */{"LAX" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (indirect,X) */ |
1346 | |
1347 | /* a4 */{"ldy" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1348 | /* a5 */{"lda" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1349 | /* a6 */{"ldx" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1350 | /* a7 */{"LAX" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, |
1351 | |
1352 | /* a8 */{"tay" , AddressingMode::IMPLIED, AccessMode::AC, RWMode::NONE, 2, 1}, |
1353 | /* a9 */{"lda" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1354 | /* aa */{"tax" , AddressingMode::IMPLIED, AccessMode::AC, RWMode::NONE, 2, 1}, |
1355 | /* ab */{"LXA" , AddressingMode::IMMEDIATE, AccessMode::ACIM, RWMode::READ, 2, 2}, /* LXA isn't a typo */ |
1356 | |
1357 | /* ac */{"ldy" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1358 | /* ad */{"lda" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1359 | /* ae */{"ldx" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1360 | /* af */{"LAX" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, |
1361 | |
1362 | /* b0 */{"bcs" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1363 | /* b1 */{"lda" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (indirect),Y */ |
1364 | /* b2 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1365 | /* b3 */{"LAX" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, |
1366 | |
1367 | /* b4 */{"ldy" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1368 | /* b5 */{"lda" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1369 | /* b6 */{"ldx" , AddressingMode::ZERO_PAGE_Y, AccessMode::ZERY, RWMode::READ, 4, 2}, /* Zeropage,Y */ |
1370 | /* b7 */{"LAX" , AddressingMode::ZERO_PAGE_Y, AccessMode::ZERY, RWMode::READ, 4, 2}, |
1371 | |
1372 | /* b8 */{"clv" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1373 | /* b9 */{"lda" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1374 | /* ba */{"tsx" , AddressingMode::IMPLIED, AccessMode::SP, RWMode::NONE, 2, 1}, |
1375 | /* bb */{"LAS" , AddressingMode::ABSOLUTE_Y, AccessMode::SABY, RWMode::READ, 4, 3}, |
1376 | |
1377 | /* bc */{"ldy" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1378 | /* bd */{"lda" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1379 | /* be */{"ldx" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1380 | /* bf */{"LAX" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, |
1381 | |
1382 | /* c0 */{"cpy" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1383 | /* c1 */{"cmp" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect,X) */ |
1384 | /* c2 */{"NOP" , AddressingMode::IMMEDIATE, AccessMode::NONE, RWMode::NONE, 2, 2}, /* occasional TILT */ |
1385 | /* c3 */{"DCP" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1386 | |
1387 | /* c4 */{"cpy" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1388 | /* c5 */{"cmp" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1389 | /* c6 */{"dec" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1390 | /* c7 */{"DCP" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1391 | |
1392 | /* c8 */{"iny" , AddressingMode::IMPLIED, AccessMode::YR, RWMode::NONE, 2, 1}, |
1393 | /* c9 */{"cmp" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1394 | /* ca */{"dex" , AddressingMode::IMPLIED, AccessMode::XR, RWMode::NONE, 2, 1}, |
1395 | /* cb */{"SBX" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, |
1396 | |
1397 | /* cc */{"cpy" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1398 | /* cd */{"cmp" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1399 | /* ce */{"dec" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1400 | /* cf */{"DCP" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1401 | |
1402 | /* d0 */{"bne" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1403 | /* d1 */{"cmp" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1404 | /* d2 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1405 | /* d3 */{"DCP" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1406 | |
1407 | /* d4 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1408 | /* d5 */{"cmp" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1409 | /* d6 */{"dec" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1410 | /* d7 */{"DCP" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1411 | |
1412 | /* d8 */{"cld" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1413 | /* d9 */{"cmp" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1414 | /* da */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1415 | /* db */{"DCP" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1416 | |
1417 | /* dc */{"NOP" , AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1418 | /* dd */{"cmp" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1419 | /* de */{"dec" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1420 | /* df */{"DCP" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, |
1421 | |
1422 | /* e0 */{"cpx" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1423 | /* e1 */{"sbc" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::READ, 6, 2}, /* (Indirect,X) */ |
1424 | /* e2 */{"NOP" , AddressingMode::IMMEDIATE, AccessMode::NONE, RWMode::NONE, 2, 2}, |
1425 | /* e3 */{"ISB" , AddressingMode::INDIRECT_X, AccessMode::INDX, RWMode::WRITE, 8, 2}, |
1426 | |
1427 | /* e4 */{"cpx" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1428 | /* e5 */{"sbc" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::READ, 3, 2}, /* Zeropage */ |
1429 | /* e6 */{"inc" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, /* Zeropage */ |
1430 | /* e7 */{"ISB" , AddressingMode::ZERO_PAGE, AccessMode::ZERO, RWMode::WRITE, 5, 2}, |
1431 | |
1432 | /* e8 */{"inx" , AddressingMode::IMPLIED, AccessMode::XR, RWMode::NONE, 2, 1}, |
1433 | /* e9 */{"sbc" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* Immediate */ |
1434 | /* ea */{"nop" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1435 | /* eb */{"SBC" , AddressingMode::IMMEDIATE, AccessMode::IMM, RWMode::READ, 2, 2}, /* same as e9 */ |
1436 | |
1437 | /* ec */{"cpx" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1438 | /* ed */{"sbc" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::READ, 4, 3}, /* Absolute */ |
1439 | /* ee */{"inc" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, /* Absolute */ |
1440 | /* ef */{"ISB" , AddressingMode::ABSOLUTE, AccessMode::ABS, RWMode::WRITE, 6, 3}, |
1441 | |
1442 | /* f0 */{"beq" , AddressingMode::RELATIVE, AccessMode::REL, RWMode::READ, 2, 2}, |
1443 | /* f1 */{"sbc" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::READ, 5, 2}, /* (Indirect),Y */ |
1444 | /* f2 */{".JAM" ,AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 0, 1}, /* TILT */ |
1445 | /* f3 */{"ISB" , AddressingMode::INDIRECT_Y, AccessMode::INDY, RWMode::WRITE, 8, 2}, |
1446 | |
1447 | /* f4 */{"NOP" , AddressingMode::ZERO_PAGE_X, AccessMode::NONE, RWMode::NONE, 4, 2}, |
1448 | /* f5 */{"sbc" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::READ, 4, 2}, /* Zeropage,X */ |
1449 | /* f6 */{"inc" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, /* Zeropage,X */ |
1450 | /* f7 */{"ISB" , AddressingMode::ZERO_PAGE_X, AccessMode::ZERX, RWMode::WRITE, 6, 2}, |
1451 | |
1452 | /* f8 */{"sed" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1453 | /* f9 */{"sbc" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::READ, 4, 3}, /* Absolute,Y */ |
1454 | /* fa */{"NOP" , AddressingMode::IMPLIED, AccessMode::NONE, RWMode::NONE, 2, 1}, |
1455 | /* fb */{"ISB" , AddressingMode::ABSOLUTE_Y, AccessMode::ABSY, RWMode::WRITE, 7, 3}, |
1456 | |
1457 | /* fc */{"NOP" ,AddressingMode::ABSOLUTE_X, AccessMode::NONE, RWMode::NONE, 4, 3}, |
1458 | /* fd */{"sbc" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::READ, 4, 3}, /* Absolute,X */ |
1459 | /* fe */{"inc" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3}, /* Absolute,X */ |
1460 | /* ff */{"ISB" , AddressingMode::ABSOLUTE_X, AccessMode::ABSX, RWMode::WRITE, 7, 3} |
1461 | } }; |
1462 | |