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"
21using Common::Base;
22
23// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24DiStella::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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
98void 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)) {
147FIX_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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
577void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
680void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
829int 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
902bool 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
924bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
930bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
949void 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
1024DONE_WITH_ADD:
1025 myDisasmBuf.clear();
1026 myDisasmBuf.str("");
1027}
1028
1029// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1030void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1060void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1113void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1123DiStella::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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1135const 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