2 // Virtual 65C02 Emulator v1.1
5 // (c) 2005-2018 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 01/04/2006 Added changelog ;-)
12 // JLH 01/18/2009 Fixed EA_ABS_* macros
15 //OK, the wraparound bug exists in both the Apple and Atari versions of Ultima
16 //II. However, the Atari version *does* occassionally pick strength while the
17 //Apple versions do not--which would seem to indicate a bug either in the RNG
18 //algorithm, the 65C02 core, or the Apple hardware. Need to investigate all
20 //N.B.: There were some lingering bugs in the BCD portions of the ADC and SBC
21 // opcodes; need to test to see if that clears up the problem.
24 //#define __DEBUGMON__
31 #include "floppydrive.h"
36 // Various helper macros
38 #define CLR_Z (regs->cc &= ~FLAG_Z)
39 #define CLR_ZN (regs->cc &= ~(FLAG_Z | FLAG_N))
40 #define CLR_ZNC (regs->cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
41 #define CLR_V (regs->cc &= ~FLAG_V)
42 #define CLR_N (regs->cc &= ~FLAG_N)
43 #define CLR_D (regs->cc &= ~FLAG_D)
44 #define SET_Z(r) (regs->cc = ((r) == 0 ? regs->cc | FLAG_Z : regs->cc & ~FLAG_Z))
45 #define SET_N(r) (regs->cc = ((r) & 0x80 ? regs->cc | FLAG_N : regs->cc & ~FLAG_N))
46 #define SET_I (regs->cc |= FLAG_I)
48 //Not sure that this code is computing the carry correctly... Investigate! [Seems to be]
50 Not 100% sure (for SET_C_CMP), when we have things like this:
51 D0BE: AC 6F D3 LDY $D36F [SP=01EC, CC=--.--IZ-, A=AA, X=60, Y=00]
52 D0C1: CC 5A D3 CPY $D35A [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
53 D0C4: F0 0F BEQ $D0D5 [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
54 D0D5: AD 6E D3 LDA $D36E [SP=01EC, CC=--.--I-C, A=0A, X=60, Y=00]
56 Which shows that $D35A has to be 0 since the Z flag is set. Why would the carry flag be set on a comparison where the compared items are equal?
58 #define SET_C_ADD(a,b) (regs->cc = ((uint8_t)(b) > (uint8_t)(~(a)) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
59 #define SET_C_CMP(a,b) (regs->cc = ((uint8_t)(b) >= (uint8_t)(a) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
60 #define SET_ZN(r) SET_N(r); SET_Z(r)
61 #define SET_ZNC_ADD(a,b,r) SET_N(r); SET_Z(r); SET_C_ADD(a,b)
62 #define SET_ZNC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b)
64 #define EA_IMM regs->pc++
65 #define EA_ZP regs->RdMem(regs->pc++)
66 #define EA_ZP_X (regs->RdMem(regs->pc++) + regs->x) & 0xFF
67 #define EA_ZP_Y (regs->RdMem(regs->pc++) + regs->y) & 0xFF
68 #define EA_ABS FetchMemW(regs->pc)
69 #define EA_ABS_X FetchMemW(regs->pc) + regs->x
70 #define EA_ABS_Y FetchMemW(regs->pc) + regs->y
71 #define EA_IND_ZP_X RdMemWZP((regs->RdMem(regs->pc++) + regs->x) & 0xFF)
72 #define EA_IND_ZP_Y RdMemWZP(regs->RdMem(regs->pc++)) + regs->y
73 #define EA_IND_ZP RdMemWZP(regs->RdMem(regs->pc++))
75 #define READ_IMM regs->RdMem(EA_IMM)
76 #define READ_ZP regs->RdMem(EA_ZP)
77 #define READ_ZP_X regs->RdMem(EA_ZP_X)
78 #define READ_ZP_Y regs->RdMem(EA_ZP_Y)
79 #define READ_ABS regs->RdMem(EA_ABS)
80 #define READ_ABS_X regs->RdMem(EA_ABS_X)
81 #define READ_ABS_Y regs->RdMem(EA_ABS_Y)
82 #define READ_IND_ZP_X regs->RdMem(EA_IND_ZP_X)
83 #define READ_IND_ZP_Y regs->RdMem(EA_IND_ZP_Y)
84 #define READ_IND_ZP regs->RdMem(EA_IND_ZP)
86 #define READ_IMM_WB(v) uint16_t addr = EA_IMM; v = regs->RdMem(addr)
87 #define READ_ZP_WB(v) uint16_t addr = EA_ZP; v = regs->RdMem(addr)
88 #define READ_ZP_X_WB(v) uint16_t addr = EA_ZP_X; v = regs->RdMem(addr)
89 #define READ_ABS_WB(v) uint16_t addr = EA_ABS; v = regs->RdMem(addr)
90 #define READ_ABS_X_WB(v) uint16_t addr = EA_ABS_X; v = regs->RdMem(addr)
91 #define READ_ABS_Y_WB(v) uint16_t addr = EA_ABS_Y; v = regs->RdMem(addr)
92 #define READ_IND_ZP_X_WB(v) uint16_t addr = EA_IND_ZP_X; v = regs->RdMem(addr)
93 #define READ_IND_ZP_Y_WB(v) uint16_t addr = EA_IND_ZP_Y; v = regs->RdMem(addr)
94 #define READ_IND_ZP_WB(v) uint16_t addr = EA_IND_ZP; v = regs->RdMem(addr)
96 #define WRITE_BACK(d) regs->WrMem(addr, (d))
99 // Private global variables
101 static V65C02REGS * regs;
103 // Cycle counts should be correct for the the Rockwell version of the 65C02.
104 // Extra cycles for page crossing or BCD mode are accounted for in their
105 // respective opcode handlers.
106 static uint8_t CPUCycles[256] = {
107 7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 5,
108 2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 6, 5,
109 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 2, 6, 5,
110 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 6, 5,
111 6, 6, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 5,
112 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 8, 4, 6, 5,
113 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 6, 4, 6, 5,
114 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 6, 5,
115 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
116 2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 5,
117 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
118 2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 5,
119 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 5, 5,
120 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 4, 4, 6, 5,
121 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 5,
122 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 4, 4, 6, 5 };
126 // Read a uint16_t out of 65C02 memory (big endian format)
128 static inline uint16_t RdMemW(uint16_t address)
130 return (uint16_t)(regs->RdMem(address + 1) << 8)
131 | regs->RdMem(address + 0);
136 // Read a uint16_t out of 65C02 memory (big endian format), wrapping on page 0
138 static inline uint16_t RdMemWZP(uint16_t address)
140 return (uint16_t)(regs->RdMem((address + 1) & 0xFF) << 8)
141 | regs->RdMem(address + 0);
146 // Read a uint16_t out of 65C02 memory (big endian format) and increment PC
148 static inline uint16_t FetchMemW(uint16_t address)
151 return (uint16_t)(regs->RdMem(address + 1) << 8)
152 | regs->RdMem(address + 0);
157 // 65C02 OPCODE IMPLEMENTATION
159 // NOTE: Lots of macros are used here to save a LOT of typing. Also
160 // helps speed the debugging process. :-) Because of this, combining
161 // certain lines may look like a good idea but would end in disaster.
162 // You have been warned! ;-)
165 // Page crossing macros. These catch the cases where access of a certain type
166 // will incur a one cycle penalty when crossing a page boundary.
168 #define HANDLE_PAGE_CROSSING_IND_Y \
169 uint16_t addressLo = regs->RdMem(regs->RdMem(regs->pc)); \
171 if ((addressLo + regs->y) > 0xFF) \
174 #define HANDLE_PAGE_CROSSING_ABS_X \
175 uint16_t addressLo = regs->RdMem(regs->pc); \
177 if ((addressLo + regs->x) > 0xFF) \
180 #define HANDLE_PAGE_CROSSING_ABS_Y \
181 uint16_t addressLo = regs->RdMem(regs->pc); \
183 if ((addressLo + regs->y) > 0xFF) \
186 // Branch taken adds a cycle, crossing page adds one more
188 #define HANDLE_BRANCH_TAKEN(m) \
190 uint16_t oldpc = regs->pc; \
194 if ((oldpc ^ regs->pc) & 0xFF00) \
199 Mnemonic Addressing mode Form Opcode Size Timing
201 ADC Immediate ADC #Oper 69 2 2
202 Zero Page ADC Zpg 65 2 3
203 Zero Page,X ADC Zpg,X 75 2 4
204 Absolute ADC Abs 6D 3 4
205 Absolute,X ADC Abs,X 7D 3 4
206 Absolute,Y ADC Abs,Y 79 3 4
207 (Zero Page,X) ADC (Zpg,X) 61 2 6
208 (Zero Page),Y ADC (Zpg),Y 71 2 5
209 (Zero Page) ADC (Zpg) 72 2 5
214 //This is non-optimal, but it works--optimize later. :-)
215 //N.B.: We have to pull the low nybble from each part of the sum in order to
216 // check BCD addition of the low nybble correctly. It doesn't work to
217 // look at the sum after summing the bytes. Also, Decimal mode incurs a
218 // one cycle penalty (for the decimal correction).
219 #define OP_ADC_HANDLER(m) \
220 uint16_t sum = (uint16_t)regs->a + (m) + (uint16_t)(regs->cc & FLAG_C); \
222 if (regs->cc & FLAG_D) \
224 uint8_t an = regs->a & 0x0F, mn = (m) & 0x0F, cn = (uint8_t)(regs->cc & FLAG_C); \
226 if ((an + mn + cn) > 9) \
229 if ((sum & 0x1F0) > 0x90) \
235 regs->cc = (regs->cc & ~FLAG_C) | (sum >> 8); \
236 regs->cc = (~(regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
237 regs->a = sum & 0xFF; \
240 static void Op69(void) // ADC #
242 uint16_t m = READ_IMM;
246 static void Op65(void) // ADC ZP
248 uint16_t m = READ_ZP;
252 static void Op75(void) // ADC ZP, X
254 uint16_t m = READ_ZP_X;
258 static void Op6D(void) // ADC ABS
260 uint16_t m = READ_ABS;
264 static void Op7D(void) // ADC ABS, X
266 HANDLE_PAGE_CROSSING_ABS_X;
267 uint16_t m = READ_ABS_X;
271 static void Op79(void) // ADC ABS, Y
273 HANDLE_PAGE_CROSSING_ABS_Y;
274 uint16_t m = READ_ABS_Y;
278 static void Op61(void) // ADC (ZP, X)
280 uint16_t m = READ_IND_ZP_X;
284 static void Op71(void) // ADC (ZP), Y
286 HANDLE_PAGE_CROSSING_IND_Y;
287 uint16_t m = READ_IND_ZP_Y;
291 static void Op72(void) // ADC (ZP)
293 uint16_t m = READ_IND_ZP;
298 AND Immediate AND #Oper 29 2 2
299 Zero Page AND Zpg 25 2 3
300 Zero Page,X AND Zpg,X 35 2 4
301 Absolute AND Abs 2D 3 4
302 Absolute,X AND Abs,X 3D 3 4
303 Absolute,Y AND Abs,Y 39 3 4
304 (Zero Page,X) AND (Zpg,X) 21 2 6
305 (Zero Page),Y AND (Zpg),Y 31 2 5
306 (Zero Page) AND (Zpg) 32 2 5
311 #define OP_AND_HANDLER(m) \
315 static void Op29(void) // AND #
317 uint8_t m = READ_IMM;
321 static void Op25(void) // AND ZP
327 static void Op35(void) // AND ZP, X
329 uint8_t m = READ_ZP_X;
333 static void Op2D(void) // AND ABS
335 uint8_t m = READ_ABS;
339 static void Op3D(void) // AND ABS, X
341 HANDLE_PAGE_CROSSING_ABS_X;
342 uint8_t m = READ_ABS_X;
346 static void Op39(void) // AND ABS, Y
348 HANDLE_PAGE_CROSSING_ABS_Y;
349 uint8_t m = READ_ABS_Y;
353 static void Op21(void) // AND (ZP, X)
355 uint8_t m = READ_IND_ZP_X;
359 static void Op31(void) // AND (ZP), Y
361 HANDLE_PAGE_CROSSING_IND_Y;
362 uint8_t m = READ_IND_ZP_Y;
366 static void Op32(void) // AND (ZP)
368 uint8_t m = READ_IND_ZP;
373 ASL Accumulator ASL A 0A 1 2
374 Zero Page ASL Zpg 06 2 5
375 Zero Page,X ASL Zpg,X 16 2 6
376 Absolute ASL Abs 0E 3 6
377 Absolute,X ASL Abs,X 1E 3 7
382 #define OP_ASL_HANDLER(m) \
383 regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
387 static void Op0A(void) // ASL A
389 OP_ASL_HANDLER(regs->a);
392 static void Op06(void) // ASL ZP
400 static void Op16(void) // ASL ZP, X
408 static void Op0E(void) // ASL ABS
416 static void Op1E(void) // ASL ABS, X
418 HANDLE_PAGE_CROSSING_ABS_X;
426 BBR0 ZP, Relative BBR0 Oper 0F 3 5
427 BBR1 ZP, Relative BBR1 Oper 1F 3 5
428 BBR2 ZP, Relative BBR2 Oper 2F 3 5
429 BBR3 ZP, Relative BBR3 Oper 3F 3 5
430 BBR4 ZP, Relative BBR4 Oper 4F 3 5
431 BBR5 ZP, Relative BBR5 Oper 5F 3 5
432 BBR6 ZP, Relative BBR6 Oper 6F 3 5
433 BBR7 ZP, Relative BBR7 Oper 7F 3 5
434 BBS0 ZP, Relative BBS0 Oper 8F 3 5
435 BBS1 ZP, Relative BBS1 Oper 9F 3 5
436 BBS2 ZP, Relative BBS2 Oper AF 3 5
437 BBS3 ZP, Relative BBS3 Oper BF 3 5
438 BBS4 ZP, Relative BBS4 Oper CF 3 5
439 BBS5 ZP, Relative BBS5 Oper DF 3 5
440 BBS6 ZP, Relative BBS6 Oper EF 3 5
441 BBS7 ZP, Relative BBS7 Oper FF 3 5
446 static void Op0F(void) // BBR0
449 int16_t m = (int16_t)(int8_t)READ_IMM;
452 HANDLE_BRANCH_TAKEN(m);
455 static void Op1F(void) // BBR1
458 int16_t m = (int16_t)(int8_t)READ_IMM;
461 HANDLE_BRANCH_TAKEN(m);
464 static void Op2F(void) // BBR2
467 int16_t m = (int16_t)(int8_t)READ_IMM;
470 HANDLE_BRANCH_TAKEN(m);
473 static void Op3F(void) // BBR3
476 int16_t m = (int16_t)(int8_t)READ_IMM;
479 HANDLE_BRANCH_TAKEN(m);
482 static void Op4F(void) // BBR4
485 int16_t m = (int16_t)(int8_t)READ_IMM;
488 HANDLE_BRANCH_TAKEN(m);
491 static void Op5F(void) // BBR5
494 int16_t m = (int16_t)(int8_t)READ_IMM;
497 HANDLE_BRANCH_TAKEN(m);
500 static void Op6F(void) // BBR6
503 int16_t m = (int16_t)(int8_t)READ_IMM;
506 HANDLE_BRANCH_TAKEN(m);
509 static void Op7F(void) // BBR7
512 int16_t m = (int16_t)(int8_t)READ_IMM;
515 HANDLE_BRANCH_TAKEN(m);
518 static void Op8F(void) // BBS0
521 int16_t m = (int16_t)(int8_t)READ_IMM;
524 HANDLE_BRANCH_TAKEN(m);
527 static void Op9F(void) // BBS1
530 int16_t m = (int16_t)(int8_t)READ_IMM;
533 HANDLE_BRANCH_TAKEN(m);
536 static void OpAF(void) // BBS2
539 int16_t m = (int16_t)(int8_t)READ_IMM;
542 HANDLE_BRANCH_TAKEN(m);
545 static void OpBF(void) // BBS3
548 int16_t m = (int16_t)(int8_t)READ_IMM;
551 HANDLE_BRANCH_TAKEN(m);
554 static void OpCF(void) // BBS4
557 int16_t m = (int16_t)(int8_t)READ_IMM;
560 HANDLE_BRANCH_TAKEN(m);
563 static void OpDF(void) // BBS5
566 int16_t m = (int16_t)(int8_t)READ_IMM;
569 HANDLE_BRANCH_TAKEN(m);
572 static void OpEF(void) // BBS6
575 int16_t m = (int16_t)(int8_t)READ_IMM;
578 HANDLE_BRANCH_TAKEN(m);
581 static void OpFF(void) // BBS7
584 int16_t m = (int16_t)(int8_t)READ_IMM;
587 HANDLE_BRANCH_TAKEN(m);
591 BCC Relative BCC Oper 90 2 2
592 BCS Relative BCS Oper B0 2 2
593 BEQ Relative BEQ Oper F0 2 2
598 static void Op90(void) // BCC
600 int16_t m = (int16_t)(int8_t)READ_IMM;
602 if (!(regs->cc & FLAG_C))
603 HANDLE_BRANCH_TAKEN(m)
606 static void OpB0(void) // BCS
608 int16_t m = (int16_t)(int8_t)READ_IMM;
610 if (regs->cc & FLAG_C)
611 HANDLE_BRANCH_TAKEN(m)
614 static void OpF0(void) // BEQ
616 int16_t m = (int16_t)(int8_t)READ_IMM;
618 if (regs->cc & FLAG_Z)
619 HANDLE_BRANCH_TAKEN(m)
623 BIT Immediate BIT #Oper 89 2 2
624 Zero Page BIT Zpg 24 2 3
625 Zero Page,X BIT Zpg,X 34 2 4
626 Absolute BIT Abs 2C 3 4
627 Absolute,X BIT Abs,X 3C 3 4
632 /* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag
633 (except in immediate addressing mode where V & N are untouched.) The
634 accumulator and the operand are ANDed and the Z flag is set
637 #define OP_BIT_HANDLER(m) \
638 int8_t result = regs->a & (m); \
639 regs->cc &= ~(FLAG_N | FLAG_V); \
640 regs->cc |= ((m) & 0xC0); \
643 static void Op89(void) // BIT #
646 int8_t result = regs->a & m;
650 static void Op24(void) // BIT ZP
656 static void Op34(void) // BIT ZP, X
658 uint8_t m = READ_ZP_X;
662 static void Op2C(void) // BIT ABS
664 uint8_t m = READ_ABS;
668 static void Op3C(void) // BIT ABS, X
670 HANDLE_PAGE_CROSSING_ABS_X;
671 uint8_t m = READ_ABS_X;
676 BMI Relative BMI Oper 30 2 2
677 BNE Relative BNE Oper D0 2 2
678 BPL Relative BPL Oper 10 2 2
679 BRA Relative BRA Oper 80 2 3
682 // More branch opcodes
684 static void Op30(void) // BMI
686 int16_t m = (int16_t)(int8_t)READ_IMM;
688 if (regs->cc & FLAG_N)
689 HANDLE_BRANCH_TAKEN(m)
692 static void OpD0(void) // BNE
694 int16_t m = (int16_t)(int8_t)READ_IMM;
696 if (!(regs->cc & FLAG_Z))
697 HANDLE_BRANCH_TAKEN(m)
700 static void Op10(void) // BPL
702 int16_t m = (int16_t)(int8_t)READ_IMM;
704 if (!(regs->cc & FLAG_N))
705 HANDLE_BRANCH_TAKEN(m)
708 static void Op80(void) // BRA
710 int16_t m = (int16_t)(int8_t)READ_IMM;
711 HANDLE_BRANCH_TAKEN(m)
715 BRK Implied BRK 00 1 7
718 static void Op00(void) // BRK
722 WriteLog("\n*** BRK ***\n\n");
723 WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
724 regs->pc, 0x0100 + regs->sp,
725 (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
726 (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
727 (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
728 (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y);
730 regs->cc |= FLAG_B; // Set B
731 regs->pc++; // RTI comes back to the instruction one byte after the BRK
732 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC and CC
733 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
734 regs->WrMem(0x0100 + regs->sp--, regs->cc);
735 regs->cc |= FLAG_I; // Set I
736 regs->cc &= ~FLAG_D; // & clear D
737 regs->pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
741 BVC Relative BVC Oper 50 2 2
742 BVS Relative BVS Oper 70 2 2
745 // Even more branch opcodes
747 static void Op50(void) // BVC
749 int16_t m = (int16_t)(int8_t)READ_IMM;
751 if (!(regs->cc & FLAG_V))
752 HANDLE_BRANCH_TAKEN(m)
755 static void Op70(void) // BVS
757 int16_t m = (int16_t)(int8_t)READ_IMM;
759 if (regs->cc & FLAG_V)
760 HANDLE_BRANCH_TAKEN(m)
764 CLC Implied CLC 18 1 2
767 static void Op18(void) // CLC
773 CLD Implied CLD D8 1 2
776 static void OpD8(void) // CLD
782 CLI Implied CLI 58 1 2
785 static void Op58(void) // CLI
791 CLV Implied CLV B8 1 2
794 static void OpB8(void) // CLV
800 CMP Immediate CMP #Oper C9 2 2
801 Zero Page CMP Zpg C5 2 3
802 Zero Page,X CMP Zpg D5 2 4
803 Absolute CMP Abs CD 3 4
804 Absolute,X CMP Abs,X DD 3 4
805 Absolute,Y CMP Abs,Y D9 3 4
806 (Zero Page,X) CMP (Zpg,X) C1 2 6
807 (Zero Page),Y CMP (Zpg),Y D1 2 5
808 (Zero Page) CMP (Zpg) D2 2 5
813 #define OP_CMP_HANDLER(m) \
814 uint8_t result = regs->a - (m); \
815 SET_ZNC_CMP(m, regs->a, result)
817 static void OpC9(void) // CMP #
819 uint8_t m = READ_IMM;
823 static void OpC5(void) // CMP ZP
829 static void OpD5(void) // CMP ZP, X
831 uint8_t m = READ_ZP_X;
835 static void OpCD(void) // CMP ABS
837 uint8_t m = READ_ABS;
841 static void OpDD(void) // CMP ABS, X
843 HANDLE_PAGE_CROSSING_ABS_X;
844 uint8_t m = READ_ABS_X;
848 static void OpD9(void) // CMP ABS, Y
850 HANDLE_PAGE_CROSSING_ABS_Y;
851 uint8_t m = READ_ABS_Y;
855 static void OpC1(void) // CMP (ZP, X)
857 uint8_t m = READ_IND_ZP_X;
861 static void OpD1(void) // CMP (ZP), Y
863 HANDLE_PAGE_CROSSING_IND_Y;
864 uint8_t m = READ_IND_ZP_Y;
868 static void OpD2(void) // CMP (ZP)
870 uint8_t m = READ_IND_ZP;
875 CPX Immediate CPX #Oper E0 2 2
876 Zero Page CPX Zpg E4 2 3
877 Absolute CPX Abs EC 3 4
882 #define OP_CPX_HANDLER(m) \
883 uint8_t result = regs->x - (m); \
884 SET_ZNC_CMP(m, regs->x, result)
886 static void OpE0(void) // CPX #
888 uint8_t m = READ_IMM;
892 static void OpE4(void) // CPX ZP
898 static void OpEC(void) // CPX ABS
900 uint8_t m = READ_ABS;
905 CPY Immediate CPY #Oper C0 2 2
906 Zero Page CPY Zpg C4 2 3
907 Absolute CPY Abs CC 3 4
912 #define OP_CPY_HANDLER(m) \
913 uint8_t result = regs->y - (m); \
914 SET_ZNC_CMP(m, regs->y, result)
916 static void OpC0(void) // CPY #
918 uint8_t m = READ_IMM;
922 static void OpC4(void) // CPY ZP
928 static void OpCC(void) // CPY ABS
930 uint8_t m = READ_ABS;
935 DEA Accumulator DEA 3A 1 2
938 static void Op3A(void) // DEA
945 DEC Zero Page DEC Zpg C6 2 5
946 Zero Page,X DEC Zpg,X D6 2 6
947 Absolute DEC Abs CE 3 6
948 Absolute,X DEC Abs,X DE 3 7
953 #define OP_DEC_HANDLER(m) \
957 static void OpC6(void) // DEC ZP
965 static void OpD6(void) // DEC ZP, X
973 static void OpCE(void) // DEC ABS
981 static void OpDE(void) // DEC ABS, X
983 HANDLE_PAGE_CROSSING_ABS_X;
991 DEX Implied DEX CA 1 2
994 static void OpCA(void) // DEX
1001 DEY Implied DEY 88 1 2
1004 static void Op88(void) // DEY
1011 EOR Immediate EOR #Oper 49 2 2
1012 Zero Page EOR Zpg 45 2 3
1013 Zero Page,X EOR Zpg,X 55 2 4
1014 Absolute EOR Abs 4D 3 4
1015 Absolute,X EOR Abs,X 5D 3 4
1016 Absolute,Y EOR Abs,Y 59 3 4
1017 (Zero Page,X) EOR (Zpg,X) 41 2 6
1018 (Zero Page),Y EOR (Zpg),Y 51 2 5
1019 (Zero Page) EOR (Zpg) 52 2 5
1024 #define OP_EOR_HANDLER(m) \
1028 static void Op49(void) // EOR #
1030 uint8_t m = READ_IMM;
1034 static void Op45(void) // EOR ZP
1036 uint8_t m = READ_ZP;
1040 static void Op55(void) // EOR ZP, X
1042 uint8_t m = READ_ZP_X;
1046 static void Op4D(void) // EOR ABS
1048 uint8_t m = READ_ABS;
1052 static void Op5D(void) // EOR ABS, X
1054 HANDLE_PAGE_CROSSING_ABS_X;
1055 uint8_t m = READ_ABS_X;
1059 static void Op59(void) // EOR ABS, Y
1061 HANDLE_PAGE_CROSSING_ABS_Y;
1062 uint8_t m = READ_ABS_Y;
1066 static void Op41(void) // EOR (ZP, X)
1068 uint8_t m = READ_IND_ZP_X;
1072 static void Op51(void) // EOR (ZP), Y
1074 HANDLE_PAGE_CROSSING_IND_Y;
1075 uint8_t m = READ_IND_ZP_Y;
1079 static void Op52(void) // EOR (ZP)
1081 uint8_t m = READ_IND_ZP;
1086 INA Accumulator INA 1A 1 2
1089 static void Op1A(void) // INA
1096 INC Zero Page INC Zpg E6 2 5
1097 Zero Page,X INC Zpg,X F6 2 6
1098 Absolute INC Abs EE 3 6
1099 Absolute,X INC Abs,X FE 3 7
1104 #define OP_INC_HANDLER(m) \
1108 static void OpE6(void) // INC ZP
1116 static void OpF6(void) // INC ZP, X
1124 static void OpEE(void) // INC ABS
1132 static void OpFE(void) // INC ABS, X
1134 HANDLE_PAGE_CROSSING_ABS_X;
1142 INX Implied INX E8 1 2
1145 static void OpE8(void) // INX
1152 INY Implied INY C8 1 2
1155 static void OpC8(void) // INY
1162 JMP Absolute JMP Abs 4C 3 3
1163 (Absolute) JMP (Abs) 6C 3 5
1164 (Absolute,X) JMP (Abs,X) 7C 3 6
1169 static void Op4C(void) // JMP ABS
1171 regs->pc = RdMemW(regs->pc);
1174 static void Op6C(void) // JMP (ABS)
1176 // Check for page crossing
1177 uint16_t addressLo = regs->RdMem(regs->pc);
1179 if (addressLo == 0xFF)
1182 regs->pc = RdMemW(RdMemW(regs->pc));
1185 static void Op7C(void) // JMP (ABS, X)
1187 regs->pc = RdMemW(RdMemW(regs->pc) + regs->x);
1191 JSR Absolute JSR Abs 20 3 6
1194 static void Op20(void) // JSR
1196 uint16_t addr = RdMemW(regs->pc);
1197 regs->pc++; // Since it pushes return address - 1...
1198 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8);
1199 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
1204 LDA Immediate LDA #Oper A9 2 2
1205 Zero Page LDA Zpg A5 2 3
1206 Zero Page,X LDA Zpg,X B5 2 4
1207 Absolute LDA Abs AD 3 4
1208 Absolute,X LDA Abs,X BD 3 4
1209 Absolute,Y LDA Abs,Y B9 3 4
1210 (Zero Page,X) LDA (Zpg,X) A1 2 6
1211 (Zero Page),Y LDA (Zpg),Y B1 2 5
1212 (Zero Page) LDA (Zpg) B2 2 5
1217 #define OP_LDA_HANDLER(m) \
1221 static void OpA9(void) // LDA #
1223 uint8_t m = READ_IMM;
1227 static void OpA5(void) // LDA ZP
1229 uint8_t m = READ_ZP;
1233 static void OpB5(void) // LDA ZP, X
1235 uint8_t m = READ_ZP_X;
1239 static void OpAD(void) // LDA ABS
1241 uint8_t m = READ_ABS;
1245 static void OpBD(void) // LDA ABS, X
1247 HANDLE_PAGE_CROSSING_ABS_X;
1248 uint8_t m = READ_ABS_X;
1252 static void OpB9(void) // LDA ABS, Y
1254 HANDLE_PAGE_CROSSING_ABS_Y;
1255 uint8_t m = READ_ABS_Y;
1259 static void OpA1(void) // LDA (ZP, X)
1261 uint8_t m = READ_IND_ZP_X;
1265 static void OpB1(void) // LDA (ZP), Y
1267 HANDLE_PAGE_CROSSING_IND_Y;
1268 uint8_t m = READ_IND_ZP_Y;
1272 static void OpB2(void) // LDA (ZP)
1274 uint8_t m = READ_IND_ZP;
1279 LDX Immediate LDX #Oper A2 2 2
1280 Zero Page LDX Zpg A6 2 3
1281 Zero Page,Y LDX Zpg,Y B6 2 4
1282 Absolute LDX Abs AE 3 4
1283 Absolute,Y LDX Abs,Y BE 3 4
1288 #define OP_LDX_HANDLER(m) \
1292 static void OpA2(void) // LDX #
1294 uint8_t m = READ_IMM;
1298 static void OpA6(void) // LDX ZP
1300 uint8_t m = READ_ZP;
1304 static void OpB6(void) // LDX ZP, Y
1306 uint8_t m = READ_ZP_Y;
1310 static void OpAE(void) // LDX ABS
1312 uint8_t m = READ_ABS;
1316 static void OpBE(void) // LDX ABS, Y
1318 HANDLE_PAGE_CROSSING_ABS_Y;
1319 uint8_t m = READ_ABS_Y;
1324 LDY Immediate LDY #Oper A0 2 2
1325 Zero Page LDY Zpg A4 2 3
1326 Zero Page,X LDY Zpg,X B4 2 4
1327 Absolute LDY Abs AC 3 4
1328 Absolute,X LDY Abs,X BC 3 4
1333 #define OP_LDY_HANDLER(m) \
1337 static void OpA0(void) // LDY #
1339 uint8_t m = READ_IMM;
1343 static void OpA4(void) // LDY ZP
1345 uint8_t m = READ_ZP;
1349 static void OpB4(void) // LDY ZP, X
1351 uint8_t m = READ_ZP_X;
1355 static void OpAC(void) // LDY ABS
1357 uint8_t m = READ_ABS;
1361 static void OpBC(void) // LDY ABS, X
1363 HANDLE_PAGE_CROSSING_ABS_X;
1364 uint8_t m = READ_ABS_X;
1369 LSR Accumulator LSR A 4A 1 2
1370 Zero Page LSR Zpg 46 2 5
1371 Zero Page,X LSR Zpg,X 56 2 6
1372 Absolute LSR Abs 4E 3 6
1373 Absolute,X LSR Abs,X 5E 3 7
1378 #define OP_LSR_HANDLER(m) \
1379 regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1383 static void Op4A(void) // LSR A
1385 OP_LSR_HANDLER(regs->a);
1388 static void Op46(void) // LSR ZP
1396 static void Op56(void) // LSR ZP, X
1404 static void Op4E(void) // LSR ABS
1412 static void Op5E(void) // LSR ABS, X
1414 HANDLE_PAGE_CROSSING_ABS_X;
1422 NOP Implied NOP EA 1 2
1425 static void OpEA(void) // NOP
1430 ORA Immediate ORA #Oper 09 2 2
1431 Zero Page ORA Zpg 05 2 3
1432 Zero Page,X ORA Zpg,X 15 2 4
1433 Absolute ORA Abs 0D 3 4
1434 Absolute,X ORA Abs,X 1D 3 4
1435 Absolute,Y ORA Abs,Y 19 3 4
1436 (Zero Page,X) ORA (Zpg,X) 01 2 6
1437 (Zero Page),Y ORA (Zpg),Y 11 2 5
1438 (Zero Page) ORA (Zpg) 12 2 5
1443 #define OP_ORA_HANDLER(m) \
1447 static void Op09(void) // ORA #
1449 uint8_t m = READ_IMM;
1453 static void Op05(void) // ORA ZP
1455 uint8_t m = READ_ZP;
1459 static void Op15(void) // ORA ZP, X
1461 uint8_t m = READ_ZP_X;
1465 static void Op0D(void) // ORA ABS
1467 uint8_t m = READ_ABS;
1471 static void Op1D(void) // ORA ABS, X
1473 HANDLE_PAGE_CROSSING_ABS_X;
1474 uint8_t m = READ_ABS_X;
1478 static void Op19(void) // ORA ABS, Y
1480 HANDLE_PAGE_CROSSING_ABS_Y;
1481 uint8_t m = READ_ABS_Y;
1485 static void Op01(void) // ORA (ZP, X)
1487 uint8_t m = READ_IND_ZP_X;
1491 static void Op11(void) // ORA (ZP), Y
1493 HANDLE_PAGE_CROSSING_IND_Y;
1494 uint8_t m = READ_IND_ZP_Y;
1498 static void Op12(void) // ORA (ZP)
1500 uint8_t m = READ_IND_ZP;
1505 PHA Implied PHA 48 1 3
1508 static void Op48(void) // PHA
1510 regs->WrMem(0x0100 + regs->sp--, regs->a);
1513 static void Op08(void) // PHP
1515 regs->cc |= FLAG_UNK; // Make sure that the unused bit is always set
1516 regs->WrMem(0x0100 + regs->sp--, regs->cc);
1520 PHX Implied PHX DA 1 3
1523 static void OpDA(void) // PHX
1525 regs->WrMem(0x0100 + regs->sp--, regs->x);
1529 PHY Implied PHY 5A 1 3
1532 static void Op5A(void) // PHY
1534 regs->WrMem(0x0100 + regs->sp--, regs->y);
1538 PLA Implied PLA 68 1 4
1541 static void Op68(void) // PLA
1543 regs->a = regs->RdMem(0x0100 + ++regs->sp);
1547 static void Op28(void) // PLP
1549 regs->cc = regs->RdMem(0x0100 + ++regs->sp);
1553 PLX Implied PLX FA 1 4
1556 static void OpFA(void) // PLX
1558 regs->x = regs->RdMem(0x0100 + ++regs->sp);
1563 PLY Implied PLY 7A 1 4
1566 static void Op7A(void) // PLY
1568 regs->y = regs->RdMem(0x0100 + ++regs->sp);
1573 The bit set and clear instructions have the form xyyy0111, where x is 0 to clear a bit or 1 to set it, and yyy is which bit at the memory location to set or clear.
1574 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1575 zp 07 17 27 37 47 57 67 77
1576 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1577 zp 87 97 A7 B7 C7 D7 E7 F7
1582 static void Op07(void) // RMB0 ZP
1590 static void Op17(void) // RMB1 ZP
1598 static void Op27(void) // RMB2 ZP
1606 static void Op37(void) // RMB3 ZP
1614 static void Op47(void) // RMB4 ZP
1622 static void Op57(void) // RMB5 ZP
1630 static void Op67(void) // RMB6 ZP
1638 static void Op77(void) // RMB7 ZP
1647 ROL Accumulator ROL A 2A 1 2
1648 Zero Page ROL Zpg 26 2 5
1649 Zero Page,X ROL Zpg,X 36 2 6
1650 Absolute ROL Abs 2E 3 6
1651 Absolute,X ROL Abs,X 3E 3 7
1656 #define OP_ROL_HANDLER(m) \
1657 uint8_t tmp = regs->cc & 0x01; \
1658 regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1659 (m) = ((m) << 1) | tmp; \
1662 static void Op2A(void) // ROL A
1664 OP_ROL_HANDLER(regs->a);
1667 static void Op26(void) // ROL ZP
1675 static void Op36(void) // ROL ZP, X
1683 static void Op2E(void) // ROL ABS
1691 static void Op3E(void) // ROL ABS, X
1693 HANDLE_PAGE_CROSSING_ABS_X;
1701 ROR Accumulator ROR A 6A 1 2
1702 Zero Page ROR Zpg 66 2 5
1703 Zero Page,X ROR Zpg,X 76 2 6
1704 Absolute ROR Abs 6E 3 6
1705 Absolute,X ROR Abs,X 7E 3 7
1710 #define OP_ROR_HANDLER(m) \
1711 uint8_t tmp = (regs->cc & 0x01) << 7; \
1712 regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1713 (m) = ((m) >> 1) | tmp; \
1716 static void Op6A(void) // ROR A
1718 OP_ROR_HANDLER(regs->a);
1721 static void Op66(void) // ROR ZP
1729 static void Op76(void) // ROR ZP, X
1737 static void Op6E(void) // ROR ABS
1745 static void Op7E(void) // ROR ABS, X
1747 HANDLE_PAGE_CROSSING_ABS_X;
1755 RTI Implied RTI 40 1 6
1758 static void Op40(void) // RTI
1760 regs->cc = regs->RdMem(0x0100 + ++regs->sp);
1761 regs->pc = regs->RdMem(0x0100 + ++regs->sp);
1762 regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
1766 RTS Implied RTS 60 1 6
1769 static void Op60(void) // RTS
1771 regs->pc = regs->RdMem(0x0100 + ++regs->sp);
1772 regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
1773 regs->pc++; // Since it pushes return address - 1...
1777 SBC Immediate SBC #Oper E9 2 2
1778 Zero Page SBC Zpg E5 2 3
1779 Zero Page,X SBC Zpg,X F5 2 4
1780 Absolute SBC Abs ED 3 4
1781 Absolute,X SBC Abs,X FD 3 4
1782 Absolute,Y SBC Abs,Y F9 3 4
1783 (Zero Page,X) SBC (Zpg,X) E1 2 6
1784 (Zero Page),Y SBC (Zpg),Y F1 2 5
1785 (Zero Page) SBC (Zpg) F2 2 5
1790 //This is non-optimal, but it works--optimize later. :-)
1791 // We do the BCD subtraction one nybble at a time to ensure a correct result.
1792 // 9 - m is a "Nine's Complement". We do the BCD subtraction as a 9s
1793 // complement addition because it's easier and it works. :-) Also, Decimal
1794 // mode incurs a once cycle penalty (for the decimal correction).
1795 #define OP_SBC_HANDLER(m) \
1796 uint16_t sum = (uint16_t)regs->a - (m) - (uint16_t)((regs->cc & FLAG_C) ^ 0x01); \
1798 if (regs->cc & FLAG_D) \
1800 sum = (regs->a & 0x0F) + (9 - ((m) & 0x0F)) + (uint16_t)(regs->cc & FLAG_C); \
1805 sum += (regs->a & 0xF0) + (0x90 - ((m) & 0xF0)); \
1810 sum ^= 0x100; /* Invert carry, for active low borrow */ \
1814 regs->cc = (regs->cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
1815 regs->cc = ((regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
1816 regs->a = sum & 0xFF; \
1819 static void OpE9(void) // SBC #
1821 uint16_t m = READ_IMM;
1825 static void OpE5(void) // SBC ZP
1827 uint16_t m = READ_ZP;
1831 static void OpF5(void) // SBC ZP, X
1833 uint16_t m = READ_ZP_X;
1837 static void OpED(void) // SBC ABS
1839 uint16_t m = READ_ABS;
1843 static void OpFD(void) // SBC ABS, X
1845 HANDLE_PAGE_CROSSING_ABS_X;
1846 uint16_t m = READ_ABS_X;
1850 static void OpF9(void) // SBC ABS, Y
1852 HANDLE_PAGE_CROSSING_ABS_Y;
1853 uint16_t m = READ_ABS_Y;
1857 static void OpE1(void) // SBC (ZP, X)
1859 uint16_t m = READ_IND_ZP_X;
1863 static void OpF1(void) // SBC (ZP), Y
1865 HANDLE_PAGE_CROSSING_IND_Y;
1866 uint16_t m = READ_IND_ZP_Y;
1870 static void OpF2(void) // SBC (ZP)
1872 uint16_t m = READ_IND_ZP;
1877 SEC Implied SEC 38 1 2
1880 static void Op38(void) // SEC
1886 SED Implied SED F8 1 2
1889 static void OpF8(void) // SED
1895 SEI Implied SEI 78 1 2
1898 static void Op78(void) // SEI
1904 The bit set and clear instructions have the form xyyy0111, where x is 0 to clear a bit or 1 to set it, and yyy is which bit at the memory location to set or clear.
1905 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1906 zp 07 17 27 37 47 57 67 77
1907 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1908 zp 87 97 A7 B7 C7 D7 E7 F7
1913 static void Op87(void) // SMB0 ZP
1921 static void Op97(void) // SMB1 ZP
1929 static void OpA7(void) // SMB2 ZP
1937 static void OpB7(void) // SMB3 ZP
1945 static void OpC7(void) // SMB4 ZP
1953 static void OpD7(void) // SMB5 ZP
1961 static void OpE7(void) // SMB6 ZP
1969 static void OpF7(void) // SMB7 ZP
1978 STA Zero Page STA Zpg 85 2 3
1979 Zero Page,X STA Zpg,X 95 2 4
1980 Absolute STA Abs 8D 3 4
1981 Absolute,X STA Abs,X 9D 3 5
1982 Absolute,Y STA Abs,Y 99 3 5
1983 (Zero Page,X) STA (Zpg,X) 81 2 6
1984 (Zero Page),Y STA (Zpg),Y 91 2 6
1985 (Zero Page) STA (Zpg) 92 2 5
1990 static void Op85(void)
1992 regs->WrMem(EA_ZP, regs->a);
1995 static void Op95(void)
1997 regs->WrMem(EA_ZP_X, regs->a);
2000 static void Op8D(void)
2002 regs->WrMem(EA_ABS, regs->a);
2005 static void Op9D(void)
2007 regs->WrMem(EA_ABS_X, regs->a);
2010 static void Op99(void)
2012 regs->WrMem(EA_ABS_Y, regs->a);
2015 static void Op81(void)
2017 regs->WrMem(EA_IND_ZP_X, regs->a);
2020 static void Op91(void)
2022 regs->WrMem(EA_IND_ZP_Y, regs->a);
2025 static void Op92(void)
2027 regs->WrMem(EA_IND_ZP, regs->a);
2031 STX Zero Page STX Zpg 86 2 3
2032 Zero Page,Y STX Zpg,Y 96 2 4
2033 Absolute STX Abs 8E 3 4
2038 static void Op86(void)
2040 regs->WrMem(EA_ZP, regs->x);
2043 static void Op96(void)
2045 regs->WrMem(EA_ZP_Y, regs->x);
2048 static void Op8E(void)
2050 regs->WrMem(EA_ABS, regs->x);
2054 STY Zero Page STY Zpg 84 2 3
2055 Zero Page,X STY Zpg,X 94 2 4
2056 Absolute STY Abs 8C 3 4
2061 static void Op84(void)
2063 regs->WrMem(EA_ZP, regs->y);
2066 static void Op94(void)
2068 regs->WrMem(EA_ZP_X, regs->y);
2071 static void Op8C(void)
2073 regs->WrMem(EA_ABS, regs->y);
2077 STZ Zero Page STZ Zpg 64 2 3
2078 Zero Page,X STZ Zpg,X 74 2 4
2079 Absolute STZ Abs 9C 3 4
2080 Absolute,X STZ Abs,X 9E 3 5
2085 static void Op64(void)
2087 regs->WrMem(EA_ZP, 0x00);
2090 static void Op74(void)
2092 regs->WrMem(EA_ZP_X, 0x00);
2095 static void Op9C(void)
2097 regs->WrMem(EA_ABS, 0x00);
2100 static void Op9E(void)
2102 regs->WrMem(EA_ABS_X, 0x00);
2106 TAX Implied TAX AA 1 2
2109 static void OpAA(void) // TAX
2116 TAY Implied TAY A8 1 2
2119 static void OpA8(void) // TAY
2126 TRB Zero Page TRB Zpg 14 2 5
2127 Absolute TRB Abs 1C 3 6
2132 #define OP_TRB_HANDLER(m) \
2133 SET_Z(m & regs->a); \
2136 static void Op14(void) // TRB ZP
2144 static void Op1C(void) // TRB ABS
2153 TSB Zero Page TSB Zpg 04 2 5
2154 Absolute TSB Abs 0C 3 6
2159 #define OP_TSB_HANDLER(m) \
2160 SET_Z(m & regs->a); \
2163 static void Op04(void) // TSB ZP
2171 static void Op0C(void) // TSB ABS
2180 TSX Implied TSX BA 1 2
2183 static void OpBA(void) // TSX
2190 TXA Implied TXA 8A 1 2
2193 static void Op8A(void) // TXA
2200 TXS Implied TXS 9A 1 2
2203 static void Op9A(void) // TXS
2209 TYA Implied TYA 98 1 2
2211 static void Op98(void) // TYA
2217 static void Op__(void)
2219 regs->cpuFlags |= V65C02_STATE_ILLEGAL_INST;
2223 // Ok, the exec_op[] array is globally defined here basically to save
2224 // a LOT of unnecessary typing. Sure it's ugly, but hey, it works!
2226 static void (* exec_op[256])() = {
2227 Op00, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op__, Op0C, Op0D, Op0E, Op0F,
2228 Op10, Op11, Op12, Op__, Op14, Op15, Op16, Op17, Op18, Op19, Op1A, Op__, Op1C, Op1D, Op1E, Op1F,
2229 Op20, Op21, Op__, Op__, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op__, Op2C, Op2D, Op2E, Op2F,
2230 Op30, Op31, Op32, Op__, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op__, Op3C, Op3D, Op3E, Op3F,
2231 Op40, Op41, Op__, Op__, Op__, Op45, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op4E, Op4F,
2232 Op50, Op51, Op52, Op__, Op__, Op55, Op56, Op57, Op58, Op59, Op5A, Op__, Op__, Op5D, Op5E, Op5F,
2233 Op60, Op61, Op__, Op__, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op__, Op6C, Op6D, Op6E, Op6F,
2234 Op70, Op71, Op72, Op__, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op__, Op7C, Op7D, Op7E, Op7F,
2235 Op80, Op81, Op__, Op__, Op84, Op85, Op86, Op87, Op88, Op89, Op8A, Op__, Op8C, Op8D, Op8E, Op8F,
2236 Op90, Op91, Op92, Op__, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op__, Op9C, Op9D, Op9E, Op9F,
2237 OpA0, OpA1, OpA2, Op__, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, Op__, OpAC, OpAD, OpAE, OpAF,
2238 OpB0, OpB1, OpB2, Op__, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, Op__, OpBC, OpBD, OpBE, OpBF,
2239 OpC0, OpC1, Op__, Op__, OpC4, OpC5, OpC6, OpC7, OpC8, OpC9, OpCA, Op__, OpCC, OpCD, OpCE, OpCF,
2240 OpD0, OpD1, OpD2, Op__, Op__, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, Op__, Op__, OpDD, OpDE, OpDF,
2241 OpE0, OpE1, Op__, Op__, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, Op__, OpEC, OpED, OpEE, OpEF,
2242 OpF0, OpF1, OpF2, Op__, Op__, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, Op__, Op__, OpFD, OpFE, OpFF
2247 FCA8: 38 698 WAIT SEC
2248 FCA9: 48 699 WAIT2 PHA
2249 FCAA: E9 01 700 WAIT3 SBC #$01
2250 FCAC: D0 FC 701 BNE WAIT3 ;1.0204 USEC
2251 FCAE: 68 702 PLA ;(13+27/2*A+5/2*A*A)
2252 FCAF: E9 01 703 SBC #$01
2253 FCB1: D0 F6 704 BNE WAIT2
2256 FBD9: C9 87 592 BELL1 CMP #$87 ;BELL CHAR? (CNTRL-G)
2257 FBDB: D0 12 593 BNE RTS2B ; NO, RETURN
2258 FBDD: A9 40 594 LDA #$40 ;DELAY .01 SECONDS
2259 FBDF: 20 A8 FC 595 JSR WAIT
2260 FBE2: A0 C0 596 LDY #$C0
2261 FBE4: A9 0C 597 BELL2 LDA #$0C ;TOGGLE SPEAKER AT
2262 FBE6: 20 A8 FC 598 JSR WAIT ; 1 KHZ FOR .1 SEC.
2263 FBE9: AD 30 C0 599 LDA SPKR
2265 FBED: D0 F5 601 BNE BELL2
2266 FBEF: 60 602 RTS2B RTS
2268 //int instCount[256];
2270 bool dumpDis = false;
2271 //bool dumpDis = true;
2275 On //e, $FCAA is the delay routine. (seems to not have changed from ][+)
2278 #define DO_BACKTRACE
2280 #define BACKTRACE_SIZE 16384
2281 uint32_t btQueuePtr = 0;
2282 V65C02REGS btQueue[BACKTRACE_SIZE];
2283 uint8_t btQueueInst[BACKTRACE_SIZE][4];
2287 // Function to execute 65C02 for "cycles" cycles
2289 //static bool first = true;
2290 void Execute65C02(V65C02REGS * context, uint32_t cycles)
2294 // Calculate number of clock cycles to run for
2295 uint64_t endCycles = regs->clock + (uint64_t)cycles - regs->overflow;
2297 while (regs->clock < endCycles)
2299 // Ultima I (WTF? This *used* to work! >:-U) [Now it does... :-P]
2300 // Turns out it was a problem with PAGE2 changing too much (it ignored the HIRES switch when switching memory, causing code at $2141 to be swapped out with zeroes).
2301 /*if (regs->pc == 0xC311)
2303 else if (regs->pc == 0x2141)
2308 static bool hitGo = false;
2310 if (regs->pc == 0xA000)
2316 // $FCA8 also needs to be silenced too ($FCB3 is exit point)
2317 if (regs->pc == 0xA181 && hitGo)
2320 WriteLog("*** BT2 DELAY\n");
2322 else if (regs->pc == 0xA18B && hitGo)
2326 else if (regs->pc == 0xFCA8 && hitGo)
2329 WriteLog("*** MONITOR DELAY ($FCA8)\n");
2331 else if (regs->pc == 0xFCB3 && hitGo)
2335 else if (regs->pc == 0xBD11 && hitGo)
2338 WriteLog("*** BT2 DELAY $BD11\n");
2340 else if (regs->pc == 0xDB1E && hitGo)
2344 else if (regs->pc == 0xA003 && hitGo)
2347 WriteLog("*** BT2 CHECK FOR $B7s\n");
2349 else if (regs->pc == 0xA063 && hitGo)
2353 else if (regs->pc == 0xA0FE && hitGo)
2356 WriteLog("*** BT2 CHECK FOR $D5 $AA $96 HEADER\n");
2358 else if (regs->pc == 0xA112 && hitGo)
2361 WriteLog("*** BT2 LOOK FOR HEADER FAILED\n");
2363 else if (regs->pc == 0xA14B && hitGo)
2367 else if (regs->pc == 0xA254 && hitGo)
2369 // This is where it hits a BRK and goes BOOM
2373 else if (regs->pc == 0xA155)
2375 WriteLog("*** $A2E2 is %02X...\n", regs->RdMem(0xA2E2));
2377 else if (regs->pc == 0xA1C2)
2379 static char bcName[13][5] = { "JMP", "JMPA", "BNE", "LDI", "JSR", "LDA", "SUB", "STA", "RTS", "JMPA", "INC", "CRSH", "ILDA" };
2380 static int bcLen[13] = { 2, 2, 2, 1, 2, 2, 1, 2, 0, 2, 2, 0, 2 };
2382 uint16_t addr = RdMemWZP(0x52) + regs->y;
2383 uint8_t bytecode = regs->RdMem(addr);
2384 uint16_t bcAddr = ((regs->RdMem(addr + 2) ^ 0xD9) << 8) | (regs->RdMem(addr + 1) ^ 0x03);
2385 uint8_t bcVal = regs->RdMem(addr + 1) ^ 0x4C;
2387 WriteLog("\n*** bc %04X: %s ", addr, bcName[bytecode]);
2389 if (bcLen[bytecode] == 1)
2390 WriteLog("$%02X", bcVal);
2391 else if (bcLen[bytecode] == 2)
2392 WriteLog("$%04X", bcAddr);
2399 // Border Zone timing...
2400 static bool inDelay1 = false;
2401 static bool inDelay2 = false;
2402 static bool inRead1 = false;
2403 static bool hitGo = false;
2404 if (regs->pc == 0xF0B1)
2405 WriteLog("*** $F09C ($6F,70) -> $%02X%02X\n", regs->RdMem(0x70), regs->RdMem(0x6F));
2407 if (regs->pc == 0xC8F2)
2410 // Delay is $D20D to $D21D...
2411 if (regs->pc == 0xD20D && hitGo && !inDelay1)
2415 WriteLog("*** DELAY\n");
2417 else if (regs->pc == 0xD21D && inDelay1)
2423 // Next delay starts @ $D356 - $D36A
2424 else if (regs->pc == 0xD356 && hitGo && !inDelay2)
2428 WriteLog("*** DELAY #2\n");
2430 else if (regs->pc == 0xD36A && inDelay2)
2435 else if (regs->pc == 0xD486 && hitGo && !inRead1)
2439 WriteLog("\n*** FAST READ ROUTINE (!!!)\n\n");
2441 else if (regs->pc == 0xD4B1 && inRead1)
2448 // 13-sector disk debugging
2449 // start with the slot ROM
2450 static bool inDelay = false;
2451 static bool inBell = false;
2452 static bool inReadSector = false;
2453 static bool inSlotROM = false;
2454 if (regs->pc == 0xFCA8)// && !inSlotROM)//!inBell && !inReadSector)
2458 WriteLog("*** DELAY\n");
2460 else if (regs->pc == 0xFCB3 && inDelay && inSlotROM)//&& !inBell && !inReadSector)
2465 if (regs->pc == 0xFBD9)
2469 WriteLog("*** BELL1\n");
2471 else if (regs->pc == 0xFBEF && inBell)
2476 //else if (regs->pc == 0xC664)
2477 else if (regs->pc == 0xC663)
2481 WriteLog("*** DISK @ $C600\n");
2483 else if (regs->pc == 0x801)
2485 WriteLog("*** DISK @ $801\n");
2489 // Hard disk debugging
2491 if (first && (regs->pc == 0x801))
2493 // regs->WrMem(0x42, 1); // v3.0 does this now...
2494 regs->WrMem(0x44, 0); // who writes non-zero to here??? (AHSSC does)
2497 //WriteLog("V65C02: Executing $801...\n");
2499 else if (regs->pc == 0x869)
2501 /* regs->WrMem(0x42, 1);
2503 /* static char disbuf[80];
2507 pc += Decode65C02(regs, disbuf, pc);
2508 WriteLog("%s\n", disbuf);
2511 WriteLog("\n>>> $42-7: %02X %02X %02X %02X %02X %02X\n\n", regs->RdMem(0x42), regs->RdMem(0x43), regs->RdMem(0x44), regs->RdMem(0x45), regs->RdMem(0x46), regs->RdMem(0x47));//*/
2516 if (regs->pc == 0x0518)
2520 else if (regs->pc == 0x051E)
2522 uint16_t c1 = regs->RdMem(0xFF);
2523 uint16_t c2 = regs->RdMem(0x00);
2524 WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
2525 WriteLog("--> $%02X\n", regs->RdMem((c2 << 8) | c1));
2527 else if (regs->pc == 0x0522)
2529 uint16_t c1 = regs->RdMem(0xFF);
2530 uint16_t c2 = regs->RdMem(0x00);
2531 WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
2532 WriteLog("--> $%02X\n", regs->RdMem(((c2 << 8) | c1) + 1));
2536 // Up N Down testing
2537 // Now Ankh testing...
2538 static bool inDelay = false;
2539 static bool inBell = false;
2540 static bool inReadSector = false;
2541 if (regs->pc == 0xFCA8 && !inBell && !inReadSector)
2545 WriteLog("*** DELAY\n");
2547 else if (regs->pc == 0xFCB3 && inDelay && !inBell && !inReadSector)
2552 if (regs->pc == 0xFBD9)
2556 WriteLog("*** BELL1\n");
2558 else if (regs->pc == 0xFBEF && inBell)
2563 else if (regs->pc == 0xC600)
2566 WriteLog("*** DISK @ $C600\n");
2568 else if (regs->pc == 0x801)
2570 WriteLog("*** DISK @ $801\n");
2573 else if (regs->pc == 0xC119)
2576 WriteLog("*** BIOS @ $C119\n");
2578 else if (regs->pc == 0xC117)
2582 else if (regs->pc == 0x843)
2585 inReadSector = true;
2586 uint16_t lo = regs->RdMem(0x26);
2587 uint16_t hi = regs->RdMem(0x27);
2588 WriteLog("\n*** DISK Read sector ($26=$%04X)...\n\n", (hi << 8) | lo);
2590 else if (regs->pc == 0x8FC)
2593 inReadSector = false;
2595 else if (regs->pc == 0xA8A8 || regs->pc == 0xC100)
2599 else if (regs->pc == 0x8FD)
2601 // regs->WrMem(0x827, 3);
2602 // regs->WrMem(0x82A, 0);
2603 //1 doesn't work, but 2 does (only with WOZ, not with DSK; DSK needs 4)...
2604 // regs->WrMem(0x0D, 4);
2609 static bool inDelay = false;
2610 static bool inMLI = false;
2611 static uint16_t mliReturnAddr = 0;
2612 static uint8_t mliCmd = 0;
2613 if (regs->pc == 0x160B && dumpDis)
2617 WriteLog("*** DELAY\n");
2619 else if (regs->pc == 0x1616 && inDelay)
2624 else if (regs->pc == 0xD385 && dumpDis)
2628 WriteLog("*** DELAY\n");
2630 else if (regs->pc == 0xD397 && inDelay)
2635 else if (regs->pc == 0xBF00 && dumpDis)
2637 uint16_t lo = regs->RdMem(regs->sp + 0x101);
2638 uint16_t hi = regs->RdMem(regs->sp + 0x102);
2639 mliReturnAddr = ((hi << 8) | lo) + 1;
2640 mliCmd = regs->RdMem(mliReturnAddr);
2641 WriteLog("*** Calling ProDOS MLI with params: %02X %04X\n", mliCmd, RdMemW(mliReturnAddr + 1));
2645 // We want to see what's going on in the WRITE BLOCK command... :-P
2646 // if (mliCmd != 0x81)
2649 else if (regs->pc == mliReturnAddr && inMLI)
2651 //extern bool stopWriting;
2652 //Stop writing to disk after the first block is done
2653 // if (mliCmd == 0x81)
2654 // stopWriting = true;
2659 else if (regs->pc == 0xAB3A && dumpDis && !inDelay)
2663 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2665 else if (regs->pc == 0xAB4A && inDelay)
2671 if (regs->pc == 0xA80B)
2676 static bool weGo = false;
2677 static bool inDelay = false;
2678 if (regs->pc == 0x92BA)
2683 else if (regs->pc == 0xAB3A && weGo && !inDelay)
2687 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2689 else if (regs->pc == 0xAB4A && weGo)
2694 else if (regs->pc == 0xA8B5 && weGo)
2696 WriteLog("\n$D4=%02X, $AC1F=%02X, $AC20=%02X\n\n", regs->RdMem(0xD4), regs->RdMem(0xAC1F), regs->RdMem(0xAC20));
2698 /*else if (regs->pc == 0xA8C4 && weGo)
2700 WriteLog("Cheating... (clearing Carry flag)\n");
2701 regs->cc &= ~FLAG_C;
2705 static bool weGo = false;
2706 if (regs->pc == 0x80AE)
2711 else if (regs->pc == 0xFCA8 && weGo)
2714 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2716 else if (regs->pc == 0xFCB3 && weGo)
2722 /*if (regs->pc == 0x4007)
2726 if (regs->pc == 0x444B)
2728 WriteLog("\n*** End of wait...\n\n");
2731 if (regs->pc == 0x444E)
2733 WriteLog("\n*** Start of wait...\n\n");
2737 /*if (regs->pc >= 0xC600 && regs->pc <=0xC6FF)
2742 dumpDis = false;//*/
2743 /*if (regs->pc == 0xE039)
2749 /*if (regs->pc == 0x0801)
2751 WriteLog("\n*** DISK BOOT subroutine...\n\n");
2754 if (regs->pc == 0xE000)
2757 WriteLog("\n*** Dump of $E000 routine ***\n\n");
2759 for(uint32_t addr=0xE000; addr<0xF000;)
2761 addr += Decode65C02(addr);
2765 WriteLog("\n*** DISK part II subroutine...\n\n");
2768 if (regs->pc == 0xD000)
2770 WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
2773 if (regs->pc == 0xD1BE)
2775 // WriteLog("\n*** DISK part II subroutine...\n\n");
2778 if (regs->pc == 0xD200)
2780 WriteLog("\n*** CUSTOM SCREEN subroutine...\n\n");
2783 if (regs->pc == 0xD269)
2785 // WriteLog("\n*** DISK part II subroutine...\n\n");
2789 //if (regs->pc == 0xE08E)
2790 /*if (regs->pc == 0xAD33)
2792 WriteLog("\n*** After loader ***\n\n");
2795 /*if (regs->pc == 0x0418)
2797 WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
2800 if (regs->pc == 0x0)
2805 //WAIT is commented out here because it's called by BELL1...
2806 if (regs->pc == 0xFCA8)
2808 WriteLog("\n*** WAIT subroutine...\n\n");
2811 if (regs->pc == 0xFBD9)
2813 WriteLog("\n*** BELL1 subroutine...\n\n");
2816 if (regs->pc == 0xFC58)
2818 WriteLog("\n*** HOME subroutine...\n\n");
2821 if (regs->pc == 0xFDED)
2823 WriteLog("\n*** COUT subroutine...\n\n");
2829 if (regs->pc == 0x2000)
2835 //uint32_t btQueuePtr = 0;
2836 //V65C02REGS btQueue[BACKTRACE_SIZE];
2837 //uint8_t btQueueInst[BACKTRACE_SIZE][4];
2838 memcpy(&btQueue[btQueuePtr], regs, sizeof(V65C02REGS));
2839 btQueuePtr = (btQueuePtr + 1) % BACKTRACE_SIZE;
2843 static uint16_t spc, ppc = 0;
2844 static bool seenHi = false;
2845 static uint64_t oldClock = 0;
2847 static char disbuf[80];
2850 Decode65C02(regs, disbuf, regs->pc);
2851 WriteLog("%s", disbuf);
2854 uint8_t opcode = regs->RdMem(regs->pc++);
2858 //if (regs->pc == 0xA255)
2859 //static bool seenBT = false;
2860 //if (hitGo && !seenBT)
2864 static char disbuf[80];
2865 // uint32_t btStart = btQueuePtr - 12 + (btQueuePtr < 12 ? BACKTRACE_SIZE : 0);
2866 uint32_t btStart = 0;
2868 for(uint32_t i=btStart; i<btQueuePtr; i++)
2870 Decode65C02(regs, disbuf, btQueue[i].pc);
2871 WriteLog("%s\n", disbuf);
2875 //if (!(regs->cpuFlags & V65C02_STATE_ILLEGAL_INST))
2876 //instCount[opcode]++;
2879 regs->Timer(CPUCycles[opcode]);
2881 uint64_t clockSave = regs->clock + CPUCycles[opcode];
2883 // We need this because the opcode function could add 1 or 2 cycles
2884 // to regs->clock which aren't accounted for in CPUCycles[].
2885 // uint64_t clockSave = regs->clock;
2887 // Execute that opcode...
2889 regs->clock += CPUCycles[opcode];
2891 // Tell the timer function (if any) how many PHI2s have elapsed...
2893 if (regs->Timer && (regs->clock - clockSave) > 0)
2894 regs->Timer(regs->clock - clockSave);
2899 WriteLog(" [SP=01%02X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X](%d)[%02X]\n",//<%s>\n",
2901 (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
2902 (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
2903 (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
2904 (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y, regs->clock - clockSave + CPUCycles[opcode], floppyDrive[0].dataRegister);//, sequence);
2907 if (((spc == 0xD4D1) || (spc == 0xD4E2)) && (floppyDrive[0].dataRegister & 0x80))
2910 if ((spc == 0xD4CE) || (spc == 0xD4DF))
2912 WriteLog(" (%d)\n", regs->clock - oldClock);
2914 if ((regs->y & 0x80) == 0 && seenHi && ((ppc == 0xD4D1) || (ppc == 0xD4E2)))
2915 WriteLog("\n***** MISS! *****\n\n");
2918 oldClock = regs->clock;
2926 if (regs->pc == 0xFCB3) // WAIT exit point
2930 /*if (regs->pc == 0xFBEF) // BELL1 exit point
2934 /*if (regs->pc == 0xFC22) // HOME exit point
2938 if (regs->pc == 0xFDFF) // COUT exit point
2942 if (regs->pc == 0xFBD8)
2944 WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
2948 //These should be correct now...
2949 if (regs->cpuFlags & V65C02_ASSERT_LINE_RESET)
2951 // Not sure about this...
2953 regs->cc = FLAG_I; // Reset the CC register
2954 regs->pc = RdMemW(0xFFFC); // And load PC with RESET vector
2956 regs->cpuFlags = 0; // Clear CPU flags...
2958 WriteLog("\n*** RESET *** (PC = $%04X)\n\n", regs->pc);
2961 else if (regs->cpuFlags & V65C02_ASSERT_LINE_NMI)
2964 WriteLog("\n*** NMI ***\n\n");
2966 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
2967 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
2968 regs->WrMem(0x0100 + regs->sp--, regs->cc);
2971 regs->pc = RdMemW(0xFFFA); // Jump to NMI vector
2974 regs->cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset NMI line
2976 else if ((regs->cpuFlags & V65C02_ASSERT_LINE_IRQ)
2977 // IRQs are maskable, so check if the I flag is clear
2978 && (!(regs->cc & FLAG_I)))
2981 WriteLog("\n*** IRQ ***\n\n");
2982 WriteLog("Clock=$%X\n", regs->clock);
2985 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
2986 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
2987 regs->WrMem(0x0100 + regs->sp--, regs->cc);
2990 regs->pc = RdMemW(0xFFFE); // Jump to IRQ vector
2993 regs->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset IRQ line
2997 // If we went longer than the passed in cycles, make a note of it so we can
2998 // subtract it out from a subsequent run. It's guaranteed to be non-
2999 // negative, because the condition that exits the main loop above is
3000 // written such that regs->clock has to be equal or larger than endCycles
3002 regs->overflow = regs->clock - endCycles;