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__
35 // Various helper macros
37 #define CLR_Z (regs->cc &= ~FLAG_Z)
38 #define CLR_ZN (regs->cc &= ~(FLAG_Z | FLAG_N))
39 #define CLR_ZNC (regs->cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
40 #define CLR_V (regs->cc &= ~FLAG_V)
41 #define CLR_N (regs->cc &= ~FLAG_N)
42 #define CLR_D (regs->cc &= ~FLAG_D)
43 #define SET_Z(r) (regs->cc = ((r) == 0 ? regs->cc | FLAG_Z : regs->cc & ~FLAG_Z))
44 #define SET_N(r) (regs->cc = ((r) & 0x80 ? regs->cc | FLAG_N : regs->cc & ~FLAG_N))
45 #define SET_I (regs->cc |= FLAG_I)
47 //Not sure that this code is computing the carry correctly... Investigate! [Seems to be]
49 Not 100% sure (for SET_C_CMP), when we have things like this:
50 D0BE: AC 6F D3 LDY $D36F [SP=01EC, CC=--.--IZ-, A=AA, X=60, Y=00]
51 D0C1: CC 5A D3 CPY $D35A [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
52 D0C4: F0 0F BEQ $D0D5 [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
53 D0D5: AD 6E D3 LDA $D36E [SP=01EC, CC=--.--I-C, A=0A, X=60, Y=00]
55 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?
57 #define SET_C_ADD(a,b) (regs->cc = ((uint8_t)(b) > (uint8_t)(~(a)) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
58 #define SET_C_CMP(a,b) (regs->cc = ((uint8_t)(b) >= (uint8_t)(a) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
59 #define SET_ZN(r) SET_N(r); SET_Z(r)
60 #define SET_ZNC_ADD(a,b,r) SET_N(r); SET_Z(r); SET_C_ADD(a,b)
61 #define SET_ZNC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b)
63 #define EA_IMM regs->pc++
64 #define EA_ZP regs->RdMem(regs->pc++)
65 #define EA_ZP_X (regs->RdMem(regs->pc++) + regs->x) & 0xFF
66 #define EA_ZP_Y (regs->RdMem(regs->pc++) + regs->y) & 0xFF
67 #define EA_ABS FetchMemW(regs->pc)
68 #define EA_ABS_X FetchMemW(regs->pc) + regs->x
69 #define EA_ABS_Y FetchMemW(regs->pc) + regs->y
70 #define EA_IND_ZP_X RdMemWZP((regs->RdMem(regs->pc++) + regs->x) & 0xFF)
71 #define EA_IND_ZP_Y RdMemWZP(regs->RdMem(regs->pc++)) + regs->y
72 #define EA_IND_ZP RdMemWZP(regs->RdMem(regs->pc++))
74 #define READ_IMM regs->RdMem(EA_IMM)
75 #define READ_ZP regs->RdMem(EA_ZP)
76 #define READ_ZP_X regs->RdMem(EA_ZP_X)
77 #define READ_ZP_Y regs->RdMem(EA_ZP_Y)
78 #define READ_ABS regs->RdMem(EA_ABS)
79 #define READ_ABS_X regs->RdMem(EA_ABS_X)
80 #define READ_ABS_Y regs->RdMem(EA_ABS_Y)
81 #define READ_IND_ZP_X regs->RdMem(EA_IND_ZP_X)
82 #define READ_IND_ZP_Y regs->RdMem(EA_IND_ZP_Y)
83 #define READ_IND_ZP regs->RdMem(EA_IND_ZP)
85 #define READ_IMM_WB(v) uint16_t addr = EA_IMM; v = regs->RdMem(addr)
86 #define READ_ZP_WB(v) uint16_t addr = EA_ZP; v = regs->RdMem(addr)
87 #define READ_ZP_X_WB(v) uint16_t addr = EA_ZP_X; v = regs->RdMem(addr)
88 #define READ_ABS_WB(v) uint16_t addr = EA_ABS; v = regs->RdMem(addr)
89 #define READ_ABS_X_WB(v) uint16_t addr = EA_ABS_X; v = regs->RdMem(addr)
90 #define READ_ABS_Y_WB(v) uint16_t addr = EA_ABS_Y; v = regs->RdMem(addr)
91 #define READ_IND_ZP_X_WB(v) uint16_t addr = EA_IND_ZP_X; v = regs->RdMem(addr)
92 #define READ_IND_ZP_Y_WB(v) uint16_t addr = EA_IND_ZP_Y; v = regs->RdMem(addr)
93 #define READ_IND_ZP_WB(v) uint16_t addr = EA_IND_ZP; v = regs->RdMem(addr)
95 #define WRITE_BACK(d) regs->WrMem(addr, (d))
98 // Private global variables
100 static V65C02REGS * regs;
102 // Cycle counts should be correct for the the Rockwell version of the 65C02.
103 // Extra cycles for page crossing or BCD mode are accounted for in their
104 // respective opcode handlers.
105 static uint8_t CPUCycles[256] = {
106 7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 5,
107 2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 6, 5,
108 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 2, 6, 5,
109 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 6, 5,
110 6, 6, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 5,
111 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 8, 4, 6, 5,
112 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 6, 4, 6, 5,
113 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 6, 5,
114 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
115 2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 5,
116 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
117 2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 5,
118 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 5, 5,
119 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 4, 4, 6, 5,
120 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 5,
121 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 4, 4, 6, 5 };
125 // Read a uint16_t out of 65C02 memory (big endian format)
127 static inline uint16_t RdMemW(uint16_t address)
129 return (uint16_t)(regs->RdMem(address + 1) << 8)
130 | regs->RdMem(address + 0);
135 // Read a uint16_t out of 65C02 memory (big endian format), wrapping on page 0
137 static inline uint16_t RdMemWZP(uint16_t address)
139 return (uint16_t)(regs->RdMem((address + 1) & 0xFF) << 8)
140 | regs->RdMem(address + 0);
145 // Read a uint16_t out of 65C02 memory (big endian format) and increment PC
147 static inline uint16_t FetchMemW(uint16_t address)
150 return (uint16_t)(regs->RdMem(address + 1) << 8)
151 | regs->RdMem(address + 0);
156 // 65C02 OPCODE IMPLEMENTATION
158 // NOTE: Lots of macros are used here to save a LOT of typing. Also
159 // helps speed the debugging process. :-) Because of this, combining
160 // certain lines may look like a good idea but would end in disaster.
161 // You have been warned! ;-)
164 // Page crossing macros. These catch the cases where access of a certain type
165 // will incur a one cycle penalty when crossing a page boundary.
167 #define HANDLE_PAGE_CROSSING_IND_Y \
168 uint16_t addressLo = regs->RdMem(regs->RdMem(regs->pc)); \
170 if ((addressLo + regs->y) > 0xFF) \
173 #define HANDLE_PAGE_CROSSING_ABS_X \
174 uint16_t addressLo = regs->RdMem(regs->pc); \
176 if ((addressLo + regs->x) > 0xFF) \
179 #define HANDLE_PAGE_CROSSING_ABS_Y \
180 uint16_t addressLo = regs->RdMem(regs->pc); \
182 if ((addressLo + regs->y) > 0xFF) \
185 // Branch taken adds a cycle, crossing page adds one more
187 #define HANDLE_BRANCH_TAKEN(m) \
189 uint16_t oldpc = regs->pc; \
193 if ((oldpc ^ regs->pc) & 0xFF00) \
198 Mnemonic Addressing mode Form Opcode Size Timing
200 ADC Immediate ADC #Oper 69 2 2
201 Zero Page ADC Zpg 65 2 3
202 Zero Page,X ADC Zpg,X 75 2 4
203 Absolute ADC Abs 6D 3 4
204 Absolute,X ADC Abs,X 7D 3 4
205 Absolute,Y ADC Abs,Y 79 3 4
206 (Zero Page,X) ADC (Zpg,X) 61 2 6
207 (Zero Page),Y ADC (Zpg),Y 71 2 5
208 (Zero Page) ADC (Zpg) 72 2 5
213 //This is non-optimal, but it works--optimize later. :-)
214 //N.B.: We have to pull the low nybble from each part of the sum in order to
215 // check BCD addition of the low nybble correctly. It doesn't work to
216 // look at the sum after summing the bytes. Also, Decimal mode incurs a
217 // one cycle penalty (for the decimal correction).
218 #define OP_ADC_HANDLER(m) \
219 uint16_t sum = (uint16_t)regs->a + (m) + (uint16_t)(regs->cc & FLAG_C); \
221 if (regs->cc & FLAG_D) \
223 uint8_t an = regs->a & 0x0F, mn = (m) & 0x0F, cn = (uint8_t)(regs->cc & FLAG_C); \
225 if ((an + mn + cn) > 9) \
228 if ((sum & 0x1F0) > 0x90) \
234 regs->cc = (regs->cc & ~FLAG_C) | (sum >> 8); \
235 regs->cc = (~(regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
236 regs->a = sum & 0xFF; \
239 static void Op69(void) // ADC #
241 uint16_t m = READ_IMM;
245 static void Op65(void) // ADC ZP
247 uint16_t m = READ_ZP;
251 static void Op75(void) // ADC ZP, X
253 uint16_t m = READ_ZP_X;
257 static void Op6D(void) // ADC ABS
259 uint16_t m = READ_ABS;
263 static void Op7D(void) // ADC ABS, X
265 HANDLE_PAGE_CROSSING_ABS_X;
266 uint16_t m = READ_ABS_X;
270 static void Op79(void) // ADC ABS, Y
272 HANDLE_PAGE_CROSSING_ABS_Y;
273 uint16_t m = READ_ABS_Y;
277 static void Op61(void) // ADC (ZP, X)
279 uint16_t m = READ_IND_ZP_X;
283 static void Op71(void) // ADC (ZP), Y
285 HANDLE_PAGE_CROSSING_IND_Y;
286 uint16_t m = READ_IND_ZP_Y;
290 static void Op72(void) // ADC (ZP)
292 uint16_t m = READ_IND_ZP;
297 AND Immediate AND #Oper 29 2 2
298 Zero Page AND Zpg 25 2 3
299 Zero Page,X AND Zpg,X 35 2 4
300 Absolute AND Abs 2D 3 4
301 Absolute,X AND Abs,X 3D 3 4
302 Absolute,Y AND Abs,Y 39 3 4
303 (Zero Page,X) AND (Zpg,X) 21 2 6
304 (Zero Page),Y AND (Zpg),Y 31 2 5
305 (Zero Page) AND (Zpg) 32 2 5
310 #define OP_AND_HANDLER(m) \
314 static void Op29(void) // AND #
316 uint8_t m = READ_IMM;
320 static void Op25(void) // AND ZP
326 static void Op35(void) // AND ZP, X
328 uint8_t m = READ_ZP_X;
332 static void Op2D(void) // AND ABS
334 uint8_t m = READ_ABS;
338 static void Op3D(void) // AND ABS, X
340 HANDLE_PAGE_CROSSING_ABS_X;
341 uint8_t m = READ_ABS_X;
345 static void Op39(void) // AND ABS, Y
347 HANDLE_PAGE_CROSSING_ABS_Y;
348 uint8_t m = READ_ABS_Y;
352 static void Op21(void) // AND (ZP, X)
354 uint8_t m = READ_IND_ZP_X;
358 static void Op31(void) // AND (ZP), Y
360 HANDLE_PAGE_CROSSING_IND_Y;
361 uint8_t m = READ_IND_ZP_Y;
365 static void Op32(void) // AND (ZP)
367 uint8_t m = READ_IND_ZP;
372 ASL Accumulator ASL A 0A 1 2
373 Zero Page ASL Zpg 06 2 5
374 Zero Page,X ASL Zpg,X 16 2 6
375 Absolute ASL Abs 0E 3 6
376 Absolute,X ASL Abs,X 1E 3 7
381 #define OP_ASL_HANDLER(m) \
382 regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
386 static void Op0A(void) // ASL A
388 OP_ASL_HANDLER(regs->a);
391 static void Op06(void) // ASL ZP
399 static void Op16(void) // ASL ZP, X
407 static void Op0E(void) // ASL ABS
415 static void Op1E(void) // ASL ABS, X
417 HANDLE_PAGE_CROSSING_ABS_X;
425 BBR0 ZP, Relative BBR0 Oper 0F 3 5
426 BBR1 ZP, Relative BBR1 Oper 1F 3 5
427 BBR2 ZP, Relative BBR2 Oper 2F 3 5
428 BBR3 ZP, Relative BBR3 Oper 3F 3 5
429 BBR4 ZP, Relative BBR4 Oper 4F 3 5
430 BBR5 ZP, Relative BBR5 Oper 5F 3 5
431 BBR6 ZP, Relative BBR6 Oper 6F 3 5
432 BBR7 ZP, Relative BBR7 Oper 7F 3 5
433 BBS0 ZP, Relative BBS0 Oper 8F 3 5
434 BBS1 ZP, Relative BBS1 Oper 9F 3 5
435 BBS2 ZP, Relative BBS2 Oper AF 3 5
436 BBS3 ZP, Relative BBS3 Oper BF 3 5
437 BBS4 ZP, Relative BBS4 Oper CF 3 5
438 BBS5 ZP, Relative BBS5 Oper DF 3 5
439 BBS6 ZP, Relative BBS6 Oper EF 3 5
440 BBS7 ZP, Relative BBS7 Oper FF 3 5
445 static void Op0F(void) // BBR0
448 int16_t m = (int16_t)(int8_t)READ_IMM;
451 HANDLE_BRANCH_TAKEN(m);
454 static void Op1F(void) // BBR1
457 int16_t m = (int16_t)(int8_t)READ_IMM;
460 HANDLE_BRANCH_TAKEN(m);
463 static void Op2F(void) // BBR2
466 int16_t m = (int16_t)(int8_t)READ_IMM;
469 HANDLE_BRANCH_TAKEN(m);
472 static void Op3F(void) // BBR3
475 int16_t m = (int16_t)(int8_t)READ_IMM;
478 HANDLE_BRANCH_TAKEN(m);
481 static void Op4F(void) // BBR4
484 int16_t m = (int16_t)(int8_t)READ_IMM;
487 HANDLE_BRANCH_TAKEN(m);
490 static void Op5F(void) // BBR5
493 int16_t m = (int16_t)(int8_t)READ_IMM;
496 HANDLE_BRANCH_TAKEN(m);
499 static void Op6F(void) // BBR6
502 int16_t m = (int16_t)(int8_t)READ_IMM;
505 HANDLE_BRANCH_TAKEN(m);
508 static void Op7F(void) // BBR7
511 int16_t m = (int16_t)(int8_t)READ_IMM;
514 HANDLE_BRANCH_TAKEN(m);
517 static void Op8F(void) // BBS0
520 int16_t m = (int16_t)(int8_t)READ_IMM;
523 HANDLE_BRANCH_TAKEN(m);
526 static void Op9F(void) // BBS1
529 int16_t m = (int16_t)(int8_t)READ_IMM;
532 HANDLE_BRANCH_TAKEN(m);
535 static void OpAF(void) // BBS2
538 int16_t m = (int16_t)(int8_t)READ_IMM;
541 HANDLE_BRANCH_TAKEN(m);
544 static void OpBF(void) // BBS3
547 int16_t m = (int16_t)(int8_t)READ_IMM;
550 HANDLE_BRANCH_TAKEN(m);
553 static void OpCF(void) // BBS4
556 int16_t m = (int16_t)(int8_t)READ_IMM;
559 HANDLE_BRANCH_TAKEN(m);
562 static void OpDF(void) // BBS5
565 int16_t m = (int16_t)(int8_t)READ_IMM;
568 HANDLE_BRANCH_TAKEN(m);
571 static void OpEF(void) // BBS6
574 int16_t m = (int16_t)(int8_t)READ_IMM;
577 HANDLE_BRANCH_TAKEN(m);
580 static void OpFF(void) // BBS7
583 int16_t m = (int16_t)(int8_t)READ_IMM;
586 HANDLE_BRANCH_TAKEN(m);
590 BCC Relative BCC Oper 90 2 2
591 BCS Relative BCS Oper B0 2 2
592 BEQ Relative BEQ Oper F0 2 2
597 static void Op90(void) // BCC
599 int16_t m = (int16_t)(int8_t)READ_IMM;
601 if (!(regs->cc & FLAG_C))
602 HANDLE_BRANCH_TAKEN(m)
605 static void OpB0(void) // BCS
607 int16_t m = (int16_t)(int8_t)READ_IMM;
609 if (regs->cc & FLAG_C)
610 HANDLE_BRANCH_TAKEN(m)
613 static void OpF0(void) // BEQ
615 int16_t m = (int16_t)(int8_t)READ_IMM;
617 if (regs->cc & FLAG_Z)
618 HANDLE_BRANCH_TAKEN(m)
622 BIT Immediate BIT #Oper 89 2 2
623 Zero Page BIT Zpg 24 2 3
624 Zero Page,X BIT Zpg,X 34 2 4
625 Absolute BIT Abs 2C 3 4
626 Absolute,X BIT Abs,X 3C 3 4
631 /* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag
632 (except in immediate addressing mode where V & N are untouched.) The
633 accumulator and the operand are ANDed and the Z flag is set
636 #define OP_BIT_HANDLER(m) \
637 int8_t result = regs->a & (m); \
638 regs->cc &= ~(FLAG_N | FLAG_V); \
639 regs->cc |= ((m) & 0xC0); \
642 static void Op89(void) // BIT #
645 int8_t result = regs->a & m;
649 static void Op24(void) // BIT ZP
655 static void Op34(void) // BIT ZP, X
657 uint8_t m = READ_ZP_X;
661 static void Op2C(void) // BIT ABS
663 uint8_t m = READ_ABS;
667 static void Op3C(void) // BIT ABS, X
669 HANDLE_PAGE_CROSSING_ABS_X;
670 uint8_t m = READ_ABS_X;
675 BMI Relative BMI Oper 30 2 2
676 BNE Relative BNE Oper D0 2 2
677 BPL Relative BPL Oper 10 2 2
678 BRA Relative BRA Oper 80 2 3
681 // More branch opcodes
683 static void Op30(void) // BMI
685 int16_t m = (int16_t)(int8_t)READ_IMM;
687 if (regs->cc & FLAG_N)
688 HANDLE_BRANCH_TAKEN(m)
691 static void OpD0(void) // BNE
693 int16_t m = (int16_t)(int8_t)READ_IMM;
695 if (!(regs->cc & FLAG_Z))
696 HANDLE_BRANCH_TAKEN(m)
699 static void Op10(void) // BPL
701 int16_t m = (int16_t)(int8_t)READ_IMM;
703 if (!(regs->cc & FLAG_N))
704 HANDLE_BRANCH_TAKEN(m)
707 static void Op80(void) // BRA
709 int16_t m = (int16_t)(int8_t)READ_IMM;
710 HANDLE_BRANCH_TAKEN(m)
714 BRK Implied BRK 00 1 7
717 static void Op00(void) // BRK
721 WriteLog("\n*** BRK ***\n\n");
722 WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
723 regs->pc, 0x0100 + regs->sp,
724 (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
725 (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
726 (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
727 (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y);
729 regs->cc |= FLAG_B; // Set B
730 regs->pc++; // RTI comes back to the instruction one byte after the BRK
731 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC and CC
732 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
733 regs->WrMem(0x0100 + regs->sp--, regs->cc);
734 regs->cc |= FLAG_I; // Set I
735 regs->cc &= ~FLAG_D; // & clear D
736 regs->pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
740 BVC Relative BVC Oper 50 2 2
741 BVS Relative BVS Oper 70 2 2
744 // Even more branch opcodes
746 static void Op50(void) // BVC
748 int16_t m = (int16_t)(int8_t)READ_IMM;
750 if (!(regs->cc & FLAG_V))
751 HANDLE_BRANCH_TAKEN(m)
754 static void Op70(void) // BVS
756 int16_t m = (int16_t)(int8_t)READ_IMM;
758 if (regs->cc & FLAG_V)
759 HANDLE_BRANCH_TAKEN(m)
763 CLC Implied CLC 18 1 2
766 static void Op18(void) // CLC
772 CLD Implied CLD D8 1 2
775 static void OpD8(void) // CLD
781 CLI Implied CLI 58 1 2
784 static void Op58(void) // CLI
790 CLV Implied CLV B8 1 2
793 static void OpB8(void) // CLV
799 CMP Immediate CMP #Oper C9 2 2
800 Zero Page CMP Zpg C5 2 3
801 Zero Page,X CMP Zpg D5 2 4
802 Absolute CMP Abs CD 3 4
803 Absolute,X CMP Abs,X DD 3 4
804 Absolute,Y CMP Abs,Y D9 3 4
805 (Zero Page,X) CMP (Zpg,X) C1 2 6
806 (Zero Page),Y CMP (Zpg),Y D1 2 5
807 (Zero Page) CMP (Zpg) D2 2 5
812 #define OP_CMP_HANDLER(m) \
813 uint8_t result = regs->a - (m); \
814 SET_ZNC_CMP(m, regs->a, result)
816 static void OpC9(void) // CMP #
818 uint8_t m = READ_IMM;
822 static void OpC5(void) // CMP ZP
828 static void OpD5(void) // CMP ZP, X
830 uint8_t m = READ_ZP_X;
834 static void OpCD(void) // CMP ABS
836 uint8_t m = READ_ABS;
840 static void OpDD(void) // CMP ABS, X
842 HANDLE_PAGE_CROSSING_ABS_X;
843 uint8_t m = READ_ABS_X;
847 static void OpD9(void) // CMP ABS, Y
849 HANDLE_PAGE_CROSSING_ABS_Y;
850 uint8_t m = READ_ABS_Y;
854 static void OpC1(void) // CMP (ZP, X)
856 uint8_t m = READ_IND_ZP_X;
860 static void OpD1(void) // CMP (ZP), Y
862 HANDLE_PAGE_CROSSING_IND_Y;
863 uint8_t m = READ_IND_ZP_Y;
867 static void OpD2(void) // CMP (ZP)
869 uint8_t m = READ_IND_ZP;
874 CPX Immediate CPX #Oper E0 2 2
875 Zero Page CPX Zpg E4 2 3
876 Absolute CPX Abs EC 3 4
881 #define OP_CPX_HANDLER(m) \
882 uint8_t result = regs->x - (m); \
883 SET_ZNC_CMP(m, regs->x, result)
885 static void OpE0(void) // CPX #
887 uint8_t m = READ_IMM;
891 static void OpE4(void) // CPX ZP
897 static void OpEC(void) // CPX ABS
899 uint8_t m = READ_ABS;
904 CPY Immediate CPY #Oper C0 2 2
905 Zero Page CPY Zpg C4 2 3
906 Absolute CPY Abs CC 3 4
911 #define OP_CPY_HANDLER(m) \
912 uint8_t result = regs->y - (m); \
913 SET_ZNC_CMP(m, regs->y, result)
915 static void OpC0(void) // CPY #
917 uint8_t m = READ_IMM;
921 static void OpC4(void) // CPY ZP
927 static void OpCC(void) // CPY ABS
929 uint8_t m = READ_ABS;
934 DEA Accumulator DEA 3A 1 2
937 static void Op3A(void) // DEA
944 DEC Zero Page DEC Zpg C6 2 5
945 Zero Page,X DEC Zpg,X D6 2 6
946 Absolute DEC Abs CE 3 6
947 Absolute,X DEC Abs,X DE 3 7
952 #define OP_DEC_HANDLER(m) \
956 static void OpC6(void) // DEC ZP
964 static void OpD6(void) // DEC ZP, X
972 static void OpCE(void) // DEC ABS
980 static void OpDE(void) // DEC ABS, X
982 HANDLE_PAGE_CROSSING_ABS_X;
990 DEX Implied DEX CA 1 2
993 static void OpCA(void) // DEX
1000 DEY Implied DEY 88 1 2
1003 static void Op88(void) // DEY
1010 EOR Immediate EOR #Oper 49 2 2
1011 Zero Page EOR Zpg 45 2 3
1012 Zero Page,X EOR Zpg,X 55 2 4
1013 Absolute EOR Abs 4D 3 4
1014 Absolute,X EOR Abs,X 5D 3 4
1015 Absolute,Y EOR Abs,Y 59 3 4
1016 (Zero Page,X) EOR (Zpg,X) 41 2 6
1017 (Zero Page),Y EOR (Zpg),Y 51 2 5
1018 (Zero Page) EOR (Zpg) 52 2 5
1023 #define OP_EOR_HANDLER(m) \
1027 static void Op49(void) // EOR #
1029 uint8_t m = READ_IMM;
1033 static void Op45(void) // EOR ZP
1035 uint8_t m = READ_ZP;
1039 static void Op55(void) // EOR ZP, X
1041 uint8_t m = READ_ZP_X;
1045 static void Op4D(void) // EOR ABS
1047 uint8_t m = READ_ABS;
1051 static void Op5D(void) // EOR ABS, X
1053 HANDLE_PAGE_CROSSING_ABS_X;
1054 uint8_t m = READ_ABS_X;
1058 static void Op59(void) // EOR ABS, Y
1060 HANDLE_PAGE_CROSSING_ABS_Y;
1061 uint8_t m = READ_ABS_Y;
1065 static void Op41(void) // EOR (ZP, X)
1067 uint8_t m = READ_IND_ZP_X;
1071 static void Op51(void) // EOR (ZP), Y
1073 HANDLE_PAGE_CROSSING_IND_Y;
1074 uint8_t m = READ_IND_ZP_Y;
1078 static void Op52(void) // EOR (ZP)
1080 uint8_t m = READ_IND_ZP;
1085 INA Accumulator INA 1A 1 2
1088 static void Op1A(void) // INA
1095 INC Zero Page INC Zpg E6 2 5
1096 Zero Page,X INC Zpg,X F6 2 6
1097 Absolute INC Abs EE 3 6
1098 Absolute,X INC Abs,X FE 3 7
1103 #define OP_INC_HANDLER(m) \
1107 static void OpE6(void) // INC ZP
1115 static void OpF6(void) // INC ZP, X
1123 static void OpEE(void) // INC ABS
1131 static void OpFE(void) // INC ABS, X
1133 HANDLE_PAGE_CROSSING_ABS_X;
1141 INX Implied INX E8 1 2
1144 static void OpE8(void) // INX
1151 INY Implied INY C8 1 2
1154 static void OpC8(void) // INY
1161 JMP Absolute JMP Abs 4C 3 3
1162 (Absolute) JMP (Abs) 6C 3 5
1163 (Absolute,X) JMP (Abs,X) 7C 3 6
1168 static void Op4C(void) // JMP ABS
1170 regs->pc = RdMemW(regs->pc);
1173 static void Op6C(void) // JMP (ABS)
1175 // Check for page crossing
1176 uint16_t addressLo = regs->RdMem(regs->pc);
1178 if (addressLo == 0xFF)
1181 regs->pc = RdMemW(RdMemW(regs->pc));
1184 static void Op7C(void) // JMP (ABS, X)
1186 regs->pc = RdMemW(RdMemW(regs->pc) + regs->x);
1190 JSR Absolute JSR Abs 20 3 6
1193 static void Op20(void) // JSR
1195 uint16_t addr = RdMemW(regs->pc);
1196 regs->pc++; // Since it pushes return address - 1...
1197 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8);
1198 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
1203 LDA Immediate LDA #Oper A9 2 2
1204 Zero Page LDA Zpg A5 2 3
1205 Zero Page,X LDA Zpg,X B5 2 4
1206 Absolute LDA Abs AD 3 4
1207 Absolute,X LDA Abs,X BD 3 4
1208 Absolute,Y LDA Abs,Y B9 3 4
1209 (Zero Page,X) LDA (Zpg,X) A1 2 6
1210 (Zero Page),Y LDA (Zpg),Y B1 2 5
1211 (Zero Page) LDA (Zpg) B2 2 5
1216 #define OP_LDA_HANDLER(m) \
1220 static void OpA9(void) // LDA #
1222 uint8_t m = READ_IMM;
1226 static void OpA5(void) // LDA ZP
1228 uint8_t m = READ_ZP;
1232 static void OpB5(void) // LDA ZP, X
1234 uint8_t m = READ_ZP_X;
1238 static void OpAD(void) // LDA ABS
1240 uint8_t m = READ_ABS;
1244 static void OpBD(void) // LDA ABS, X
1246 HANDLE_PAGE_CROSSING_ABS_X;
1247 uint8_t m = READ_ABS_X;
1251 static void OpB9(void) // LDA ABS, Y
1253 HANDLE_PAGE_CROSSING_ABS_Y;
1254 uint8_t m = READ_ABS_Y;
1258 static void OpA1(void) // LDA (ZP, X)
1260 uint8_t m = READ_IND_ZP_X;
1264 static void OpB1(void) // LDA (ZP), Y
1266 HANDLE_PAGE_CROSSING_IND_Y;
1267 uint8_t m = READ_IND_ZP_Y;
1271 static void OpB2(void) // LDA (ZP)
1273 uint8_t m = READ_IND_ZP;
1278 LDX Immediate LDX #Oper A2 2 2
1279 Zero Page LDX Zpg A6 2 3
1280 Zero Page,Y LDX Zpg,Y B6 2 4
1281 Absolute LDX Abs AE 3 4
1282 Absolute,Y LDX Abs,Y BE 3 4
1287 #define OP_LDX_HANDLER(m) \
1291 static void OpA2(void) // LDX #
1293 uint8_t m = READ_IMM;
1297 static void OpA6(void) // LDX ZP
1299 uint8_t m = READ_ZP;
1303 static void OpB6(void) // LDX ZP, Y
1305 uint8_t m = READ_ZP_Y;
1309 static void OpAE(void) // LDX ABS
1311 uint8_t m = READ_ABS;
1315 static void OpBE(void) // LDX ABS, Y
1317 HANDLE_PAGE_CROSSING_ABS_Y;
1318 uint8_t m = READ_ABS_Y;
1323 LDY Immediate LDY #Oper A0 2 2
1324 Zero Page LDY Zpg A4 2 3
1325 Zero Page,X LDY Zpg,X B4 2 4
1326 Absolute LDY Abs AC 3 4
1327 Absolute,X LDY Abs,X BC 3 4
1332 #define OP_LDY_HANDLER(m) \
1336 static void OpA0(void) // LDY #
1338 uint8_t m = READ_IMM;
1342 static void OpA4(void) // LDY ZP
1344 uint8_t m = READ_ZP;
1348 static void OpB4(void) // LDY ZP, X
1350 uint8_t m = READ_ZP_X;
1354 static void OpAC(void) // LDY ABS
1356 uint8_t m = READ_ABS;
1360 static void OpBC(void) // LDY ABS, X
1362 HANDLE_PAGE_CROSSING_ABS_X;
1363 uint8_t m = READ_ABS_X;
1368 LSR Accumulator LSR A 4A 1 2
1369 Zero Page LSR Zpg 46 2 5
1370 Zero Page,X LSR Zpg,X 56 2 6
1371 Absolute LSR Abs 4E 3 6
1372 Absolute,X LSR Abs,X 5E 3 7
1377 #define OP_LSR_HANDLER(m) \
1378 regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1382 static void Op4A(void) // LSR A
1384 OP_LSR_HANDLER(regs->a);
1387 static void Op46(void) // LSR ZP
1395 static void Op56(void) // LSR ZP, X
1403 static void Op4E(void) // LSR ABS
1411 static void Op5E(void) // LSR ABS, X
1413 HANDLE_PAGE_CROSSING_ABS_X;
1421 NOP Implied NOP EA 1 2
1424 static void OpEA(void) // NOP
1429 ORA Immediate ORA #Oper 09 2 2
1430 Zero Page ORA Zpg 05 2 3
1431 Zero Page,X ORA Zpg,X 15 2 4
1432 Absolute ORA Abs 0D 3 4
1433 Absolute,X ORA Abs,X 1D 3 4
1434 Absolute,Y ORA Abs,Y 19 3 4
1435 (Zero Page,X) ORA (Zpg,X) 01 2 6
1436 (Zero Page),Y ORA (Zpg),Y 11 2 5
1437 (Zero Page) ORA (Zpg) 12 2 5
1442 #define OP_ORA_HANDLER(m) \
1446 static void Op09(void) // ORA #
1448 uint8_t m = READ_IMM;
1452 static void Op05(void) // ORA ZP
1454 uint8_t m = READ_ZP;
1458 static void Op15(void) // ORA ZP, X
1460 uint8_t m = READ_ZP_X;
1464 static void Op0D(void) // ORA ABS
1466 uint8_t m = READ_ABS;
1470 static void Op1D(void) // ORA ABS, X
1472 HANDLE_PAGE_CROSSING_ABS_X;
1473 uint8_t m = READ_ABS_X;
1477 static void Op19(void) // ORA ABS, Y
1479 HANDLE_PAGE_CROSSING_ABS_Y;
1480 uint8_t m = READ_ABS_Y;
1484 static void Op01(void) // ORA (ZP, X)
1486 uint8_t m = READ_IND_ZP_X;
1490 static void Op11(void) // ORA (ZP), Y
1492 HANDLE_PAGE_CROSSING_IND_Y;
1493 uint8_t m = READ_IND_ZP_Y;
1497 static void Op12(void) // ORA (ZP)
1499 uint8_t m = READ_IND_ZP;
1504 PHA Implied PHA 48 1 3
1507 static void Op48(void) // PHA
1509 regs->WrMem(0x0100 + regs->sp--, regs->a);
1512 static void Op08(void) // PHP
1514 regs->cc |= FLAG_UNK; // Make sure that the unused bit is always set
1515 regs->WrMem(0x0100 + regs->sp--, regs->cc);
1519 PHX Implied PHX DA 1 3
1522 static void OpDA(void) // PHX
1524 regs->WrMem(0x0100 + regs->sp--, regs->x);
1528 PHY Implied PHY 5A 1 3
1531 static void Op5A(void) // PHY
1533 regs->WrMem(0x0100 + regs->sp--, regs->y);
1537 PLA Implied PLA 68 1 4
1540 static void Op68(void) // PLA
1542 regs->a = regs->RdMem(0x0100 + ++regs->sp);
1546 static void Op28(void) // PLP
1548 regs->cc = regs->RdMem(0x0100 + ++regs->sp);
1552 PLX Implied PLX FA 1 4
1555 static void OpFA(void) // PLX
1557 regs->x = regs->RdMem(0x0100 + ++regs->sp);
1562 PLY Implied PLY 7A 1 4
1565 static void Op7A(void) // PLY
1567 regs->y = regs->RdMem(0x0100 + ++regs->sp);
1572 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.
1573 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1574 zp 07 17 27 37 47 57 67 77
1575 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1576 zp 87 97 A7 B7 C7 D7 E7 F7
1581 static void Op07(void) // RMB0 ZP
1589 static void Op17(void) // RMB1 ZP
1597 static void Op27(void) // RMB2 ZP
1605 static void Op37(void) // RMB3 ZP
1613 static void Op47(void) // RMB4 ZP
1621 static void Op57(void) // RMB5 ZP
1629 static void Op67(void) // RMB6 ZP
1637 static void Op77(void) // RMB7 ZP
1646 ROL Accumulator ROL A 2A 1 2
1647 Zero Page ROL Zpg 26 2 5
1648 Zero Page,X ROL Zpg,X 36 2 6
1649 Absolute ROL Abs 2E 3 6
1650 Absolute,X ROL Abs,X 3E 3 7
1655 #define OP_ROL_HANDLER(m) \
1656 uint8_t tmp = regs->cc & 0x01; \
1657 regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1658 (m) = ((m) << 1) | tmp; \
1661 static void Op2A(void) // ROL A
1663 OP_ROL_HANDLER(regs->a);
1666 static void Op26(void) // ROL ZP
1674 static void Op36(void) // ROL ZP, X
1682 static void Op2E(void) // ROL ABS
1690 static void Op3E(void) // ROL ABS, X
1692 HANDLE_PAGE_CROSSING_ABS_X;
1700 ROR Accumulator ROR A 6A 1 2
1701 Zero Page ROR Zpg 66 2 5
1702 Zero Page,X ROR Zpg,X 76 2 6
1703 Absolute ROR Abs 6E 3 6
1704 Absolute,X ROR Abs,X 7E 3 7
1709 #define OP_ROR_HANDLER(m) \
1710 uint8_t tmp = (regs->cc & 0x01) << 7; \
1711 regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
1712 (m) = ((m) >> 1) | tmp; \
1715 static void Op6A(void) // ROR A
1717 OP_ROR_HANDLER(regs->a);
1720 static void Op66(void) // ROR ZP
1728 static void Op76(void) // ROR ZP, X
1736 static void Op6E(void) // ROR ABS
1744 static void Op7E(void) // ROR ABS, X
1746 HANDLE_PAGE_CROSSING_ABS_X;
1754 RTI Implied RTI 40 1 6
1757 static void Op40(void) // RTI
1759 regs->cc = regs->RdMem(0x0100 + ++regs->sp);
1760 regs->pc = regs->RdMem(0x0100 + ++regs->sp);
1761 regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
1765 RTS Implied RTS 60 1 6
1768 static void Op60(void) // RTS
1770 regs->pc = regs->RdMem(0x0100 + ++regs->sp);
1771 regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
1772 regs->pc++; // Since it pushes return address - 1...
1776 SBC Immediate SBC #Oper E9 2 2
1777 Zero Page SBC Zpg E5 2 3
1778 Zero Page,X SBC Zpg,X F5 2 4
1779 Absolute SBC Abs ED 3 4
1780 Absolute,X SBC Abs,X FD 3 4
1781 Absolute,Y SBC Abs,Y F9 3 4
1782 (Zero Page,X) SBC (Zpg,X) E1 2 6
1783 (Zero Page),Y SBC (Zpg),Y F1 2 5
1784 (Zero Page) SBC (Zpg) F2 2 5
1789 //This is non-optimal, but it works--optimize later. :-)
1790 // We do the BCD subtraction one nybble at a time to ensure a correct result.
1791 // 9 - m is a "Nine's Complement". We do the BCD subtraction as a 9s
1792 // complement addition because it's easier and it works. :-) Also, Decimal
1793 // mode incurs a once cycle penalty (for the decimal correction).
1794 #define OP_SBC_HANDLER(m) \
1795 uint16_t sum = (uint16_t)regs->a - (m) - (uint16_t)((regs->cc & FLAG_C) ^ 0x01); \
1797 if (regs->cc & FLAG_D) \
1799 sum = (regs->a & 0x0F) + (9 - ((m) & 0x0F)) + (uint16_t)(regs->cc & FLAG_C); \
1804 sum += (regs->a & 0xF0) + (0x90 - ((m) & 0xF0)); \
1809 sum ^= 0x100; /* Invert carry, for active low borrow */ \
1813 regs->cc = (regs->cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
1814 regs->cc = ((regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
1815 regs->a = sum & 0xFF; \
1818 static void OpE9(void) // SBC #
1820 uint16_t m = READ_IMM;
1824 static void OpE5(void) // SBC ZP
1826 uint16_t m = READ_ZP;
1830 static void OpF5(void) // SBC ZP, X
1832 uint16_t m = READ_ZP_X;
1836 static void OpED(void) // SBC ABS
1838 uint16_t m = READ_ABS;
1842 static void OpFD(void) // SBC ABS, X
1844 HANDLE_PAGE_CROSSING_ABS_X;
1845 uint16_t m = READ_ABS_X;
1849 static void OpF9(void) // SBC ABS, Y
1851 HANDLE_PAGE_CROSSING_ABS_Y;
1852 uint16_t m = READ_ABS_Y;
1856 static void OpE1(void) // SBC (ZP, X)
1858 uint16_t m = READ_IND_ZP_X;
1862 static void OpF1(void) // SBC (ZP), Y
1864 HANDLE_PAGE_CROSSING_IND_Y;
1865 uint16_t m = READ_IND_ZP_Y;
1869 static void OpF2(void) // SBC (ZP)
1871 uint16_t m = READ_IND_ZP;
1876 SEC Implied SEC 38 1 2
1879 static void Op38(void) // SEC
1885 SED Implied SED F8 1 2
1888 static void OpF8(void) // SED
1894 SEI Implied SEI 78 1 2
1897 static void Op78(void) // SEI
1903 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.
1904 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1905 zp 07 17 27 37 47 57 67 77
1906 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1907 zp 87 97 A7 B7 C7 D7 E7 F7
1912 static void Op87(void) // SMB0 ZP
1920 static void Op97(void) // SMB1 ZP
1928 static void OpA7(void) // SMB2 ZP
1936 static void OpB7(void) // SMB3 ZP
1944 static void OpC7(void) // SMB4 ZP
1952 static void OpD7(void) // SMB5 ZP
1960 static void OpE7(void) // SMB6 ZP
1968 static void OpF7(void) // SMB7 ZP
1977 STA Zero Page STA Zpg 85 2 3
1978 Zero Page,X STA Zpg,X 95 2 4
1979 Absolute STA Abs 8D 3 4
1980 Absolute,X STA Abs,X 9D 3 5
1981 Absolute,Y STA Abs,Y 99 3 5
1982 (Zero Page,X) STA (Zpg,X) 81 2 6
1983 (Zero Page),Y STA (Zpg),Y 91 2 6
1984 (Zero Page) STA (Zpg) 92 2 5
1989 static void Op85(void)
1991 regs->WrMem(EA_ZP, regs->a);
1994 static void Op95(void)
1996 regs->WrMem(EA_ZP_X, regs->a);
1999 static void Op8D(void)
2001 regs->WrMem(EA_ABS, regs->a);
2004 static void Op9D(void)
2006 regs->WrMem(EA_ABS_X, regs->a);
2009 static void Op99(void)
2011 regs->WrMem(EA_ABS_Y, regs->a);
2014 static void Op81(void)
2016 regs->WrMem(EA_IND_ZP_X, regs->a);
2019 static void Op91(void)
2021 regs->WrMem(EA_IND_ZP_Y, regs->a);
2024 static void Op92(void)
2026 regs->WrMem(EA_IND_ZP, regs->a);
2030 STX Zero Page STX Zpg 86 2 3
2031 Zero Page,Y STX Zpg,Y 96 2 4
2032 Absolute STX Abs 8E 3 4
2037 static void Op86(void)
2039 regs->WrMem(EA_ZP, regs->x);
2042 static void Op96(void)
2044 regs->WrMem(EA_ZP_Y, regs->x);
2047 static void Op8E(void)
2049 regs->WrMem(EA_ABS, regs->x);
2053 STY Zero Page STY Zpg 84 2 3
2054 Zero Page,X STY Zpg,X 94 2 4
2055 Absolute STY Abs 8C 3 4
2060 static void Op84(void)
2062 regs->WrMem(EA_ZP, regs->y);
2065 static void Op94(void)
2067 regs->WrMem(EA_ZP_X, regs->y);
2070 static void Op8C(void)
2072 regs->WrMem(EA_ABS, regs->y);
2076 STZ Zero Page STZ Zpg 64 2 3
2077 Zero Page,X STZ Zpg,X 74 2 4
2078 Absolute STZ Abs 9C 3 4
2079 Absolute,X STZ Abs,X 9E 3 5
2084 static void Op64(void)
2086 regs->WrMem(EA_ZP, 0x00);
2089 static void Op74(void)
2091 regs->WrMem(EA_ZP_X, 0x00);
2094 static void Op9C(void)
2096 regs->WrMem(EA_ABS, 0x00);
2099 static void Op9E(void)
2101 regs->WrMem(EA_ABS_X, 0x00);
2105 TAX Implied TAX AA 1 2
2108 static void OpAA(void) // TAX
2115 TAY Implied TAY A8 1 2
2118 static void OpA8(void) // TAY
2125 TRB Zero Page TRB Zpg 14 2 5
2126 Absolute TRB Abs 1C 3 6
2131 #define OP_TRB_HANDLER(m) \
2132 SET_Z(m & regs->a); \
2135 static void Op14(void) // TRB ZP
2143 static void Op1C(void) // TRB ABS
2152 TSB Zero Page TSB Zpg 04 2 5
2153 Absolute TSB Abs 0C 3 6
2158 #define OP_TSB_HANDLER(m) \
2159 SET_Z(m & regs->a); \
2162 static void Op04(void) // TSB ZP
2170 static void Op0C(void) // TSB ABS
2179 TSX Implied TSX BA 1 2
2182 static void OpBA(void) // TSX
2189 TXA Implied TXA 8A 1 2
2192 static void Op8A(void) // TXA
2199 TXS Implied TXS 9A 1 2
2202 static void Op9A(void) // TXS
2208 TYA Implied TYA 98 1 2
2210 static void Op98(void) // TYA
2216 static void Op__(void)
2218 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];
2288 // Function to execute 65C02 for "cycles" cycles
2290 //static bool first = true;
2291 void Execute65C02(V65C02REGS * context, uint32_t cycles)
2295 // Calculate number of clock cycles to run for
2296 uint64_t endCycles = regs->clock + (uint64_t)cycles - regs->overflow;
2298 while (regs->clock < endCycles)
2300 // Hard disk debugging
2302 if (first && (regs->pc == 0x801))
2304 regs->WrMem(0x42, 1);
2305 regs->WrMem(0x44, 0);
2308 else if (regs->pc == 0x869)
2310 /* regs->WrMem(0x42, 1);
2312 /* static char disbuf[80];
2316 pc += Decode65C02(regs, disbuf, pc);
2317 WriteLog("%s\n", disbuf);
2320 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));//*/
2325 if (regs->pc == 0x0518)
2329 else if (regs->pc == 0x051E)
2331 uint16_t c1 = regs->RdMem(0xFF);
2332 uint16_t c2 = regs->RdMem(0x00);
2333 WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
2334 WriteLog("--> $%02X\n", regs->RdMem((c2 << 8) | c1));
2336 else if (regs->pc == 0x0522)
2338 uint16_t c1 = regs->RdMem(0xFF);
2339 uint16_t c2 = regs->RdMem(0x00);
2340 WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
2341 WriteLog("--> $%02X\n", regs->RdMem(((c2 << 8) | c1) + 1));
2345 // Up N Down testing
2346 // Now Ankh testing...
2347 static bool inDelay = false;
2348 static bool inBell = false;
2349 static bool inReadSector = false;
2350 if (regs->pc == 0xFCA8 && !inBell && !inReadSector)
2354 WriteLog("*** DELAY\n");
2356 else if (regs->pc == 0xFCB3 && inDelay && !inBell && !inReadSector)
2361 if (regs->pc == 0xFBD9)
2365 WriteLog("*** BELL1\n");
2367 else if (regs->pc == 0xFBEF && inBell)
2372 else if (regs->pc == 0xC600)
2375 WriteLog("*** DISK @ $C600\n");
2377 else if (regs->pc == 0x801)
2379 WriteLog("*** DISK @ $801\n");
2382 else if (regs->pc == 0xC119)
2385 WriteLog("*** BIOS @ $C119\n");
2387 else if (regs->pc == 0xC117)
2391 else if (regs->pc == 0x843)
2394 inReadSector = true;
2395 uint16_t lo = regs->RdMem(0x26);
2396 uint16_t hi = regs->RdMem(0x27);
2397 WriteLog("\n*** DISK Read sector ($26=$%04X)...\n\n", (hi << 8) | lo);
2399 else if (regs->pc == 0x8FC)
2402 inReadSector = false;
2404 else if (regs->pc == 0xA8A8 || regs->pc == 0xC100)
2408 else if (regs->pc == 0x8FD)
2410 // regs->WrMem(0x827, 3);
2411 // regs->WrMem(0x82A, 0);
2412 //1 doesn't work, but 2 does (only with WOZ, not with DSK; DSK needs 4)...
2413 // regs->WrMem(0x0D, 4);
2418 static bool inDelay = false;
2419 static bool inMLI = false;
2420 static uint16_t mliReturnAddr = 0;
2421 static uint8_t mliCmd = 0;
2422 if (regs->pc == 0x160B && dumpDis)
2426 WriteLog("*** DELAY\n");
2428 else if (regs->pc == 0x1616 && inDelay)
2433 else if (regs->pc == 0xD385 && dumpDis)
2437 WriteLog("*** DELAY\n");
2439 else if (regs->pc == 0xD397 && inDelay)
2444 else if (regs->pc == 0xBF00 && dumpDis)
2446 uint16_t lo = regs->RdMem(regs->sp + 0x101);
2447 uint16_t hi = regs->RdMem(regs->sp + 0x102);
2448 mliReturnAddr = ((hi << 8) | lo) + 1;
2449 mliCmd = regs->RdMem(mliReturnAddr);
2450 WriteLog("*** Calling ProDOS MLI with params: %02X %04X\n", mliCmd, RdMemW(mliReturnAddr + 1));
2454 // We want to see what's going on in the WRITE BLOCK command... :-P
2455 // if (mliCmd != 0x81)
2458 else if (regs->pc == mliReturnAddr && inMLI)
2460 //extern bool stopWriting;
2461 //Stop writing to disk after the first block is done
2462 // if (mliCmd == 0x81)
2463 // stopWriting = true;
2468 else if (regs->pc == 0xAB3A && dumpDis && !inDelay)
2472 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2474 else if (regs->pc == 0xAB4A && inDelay)
2480 if (regs->pc == 0xA80B)
2485 static bool weGo = false;
2486 static bool inDelay = false;
2487 if (regs->pc == 0x92BA)
2492 else if (regs->pc == 0xAB3A && weGo && !inDelay)
2496 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2498 else if (regs->pc == 0xAB4A && weGo)
2503 else if (regs->pc == 0xA8B5 && weGo)
2505 WriteLog("\n$D4=%02X, $AC1F=%02X, $AC20=%02X\n\n", regs->RdMem(0xD4), regs->RdMem(0xAC1F), regs->RdMem(0xAC20));
2507 /*else if (regs->pc == 0xA8C4 && weGo)
2509 WriteLog("Cheating... (clearing Carry flag)\n");
2510 regs->cc &= ~FLAG_C;
2514 static bool weGo = false;
2515 if (regs->pc == 0x80AE)
2520 else if (regs->pc == 0xFCA8 && weGo)
2523 WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
2525 else if (regs->pc == 0xFCB3 && weGo)
2531 /*if (regs->pc == 0x4007)
2535 if (regs->pc == 0x444B)
2537 WriteLog("\n*** End of wait...\n\n");
2540 if (regs->pc == 0x444E)
2542 WriteLog("\n*** Start of wait...\n\n");
2546 /*if (regs->pc >= 0xC600 && regs->pc <=0xC6FF)
2551 dumpDis = false;//*/
2552 /*if (regs->pc == 0xE039)
2558 /*if (regs->pc == 0x0801)
2560 WriteLog("\n*** DISK BOOT subroutine...\n\n");
2563 if (regs->pc == 0xE000)
2566 WriteLog("\n*** Dump of $E000 routine ***\n\n");
2568 for(uint32_t addr=0xE000; addr<0xF000;)
2570 addr += Decode65C02(addr);
2574 WriteLog("\n*** DISK part II subroutine...\n\n");
2577 if (regs->pc == 0xD000)
2579 WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
2582 if (regs->pc == 0xD1BE)
2584 // WriteLog("\n*** DISK part II subroutine...\n\n");
2587 if (regs->pc == 0xD200)
2589 WriteLog("\n*** CUSTOM SCREEN subroutine...\n\n");
2592 if (regs->pc == 0xD269)
2594 // WriteLog("\n*** DISK part II subroutine...\n\n");
2598 //if (regs->pc == 0xE08E)
2599 /*if (regs->pc == 0xAD33)
2601 WriteLog("\n*** After loader ***\n\n");
2604 /*if (regs->pc == 0x0418)
2606 WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
2609 if (regs->pc == 0x0)
2614 //WAIT is commented out here because it's called by BELL1...
2615 if (regs->pc == 0xFCA8)
2617 WriteLog("\n*** WAIT subroutine...\n\n");
2620 if (regs->pc == 0xFBD9)
2622 WriteLog("\n*** BELL1 subroutine...\n\n");
2625 if (regs->pc == 0xFC58)
2627 WriteLog("\n*** HOME subroutine...\n\n");
2630 if (regs->pc == 0xFDED)
2632 WriteLog("\n*** COUT subroutine...\n\n");
2638 if (regs->pc == 0x2000)
2644 //uint32_t btQueuePtr = 0;
2645 //V65C02REGS btQueue[BACKTRACE_SIZE];
2646 //uint8_t btQueueInst[BACKTRACE_SIZE][4];
2647 memcpy(&btQueue[btQueuePtr], regs, sizeof(V65C02REGS));
2648 btQueuePtr = (btQueuePtr + 1) % BACKTRACE_SIZE;
2652 static char disbuf[80];
2655 Decode65C02(regs, disbuf, regs->pc);
2656 WriteLog("%s", disbuf);
2659 uint8_t opcode = regs->RdMem(regs->pc++);
2664 static char disbuf[80];
2665 uint32_t btStart = btQueuePtr - 12 + (btQueuePtr < 12 ? BACKTRACE_SIZE : 0);
2667 for(uint32_t i=btStart; i<btQueuePtr; i++)
2669 Decode65C02(regs, disbuf, btQueue[i].pc);
2670 WriteLog("%s\n", disbuf);
2674 //if (!(regs->cpuFlags & V65C02_STATE_ILLEGAL_INST))
2675 //instCount[opcode]++;
2677 // We need this because the opcode function could add 1 or 2 cycles
2678 // which aren't accounted for in CPUCycles[].
2679 uint64_t clockSave = regs->clock;
2681 // Execute that opcode...
2683 regs->clock += CPUCycles[opcode];
2685 // Tell the timer function (if any) how many PHI2s have elapsed...
2687 regs->Timer(regs->clock - clockSave);
2691 WriteLog(" [SP=01%02X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X](%d)\n",
2693 (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
2694 (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
2695 (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
2696 (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y, regs->clock - clockSave);
2700 if (regs->pc == 0xFCB3) // WAIT exit point
2704 /*if (regs->pc == 0xFBEF) // BELL1 exit point
2708 /*if (regs->pc == 0xFC22) // HOME exit point
2712 if (regs->pc == 0xFDFF) // COUT exit point
2716 if (regs->pc == 0xFBD8)
2718 WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
2722 //These should be correct now...
2723 if (regs->cpuFlags & V65C02_ASSERT_LINE_RESET)
2725 // Not sure about this...
2727 regs->cc = FLAG_I; // Reset the CC register
2728 regs->pc = RdMemW(0xFFFC); // And load PC with RESET vector
2730 regs->cpuFlags = 0; // Clear CPU flags...
2732 WriteLog("\n*** RESET *** (PC = $%04X)\n\n", regs->pc);
2735 else if (regs->cpuFlags & V65C02_ASSERT_LINE_NMI)
2738 WriteLog("\n*** NMI ***\n\n");
2740 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
2741 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
2742 regs->WrMem(0x0100 + regs->sp--, regs->cc);
2745 regs->pc = RdMemW(0xFFFA); // Jump to NMI vector
2748 regs->cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset NMI line
2750 else if ((regs->cpuFlags & V65C02_ASSERT_LINE_IRQ)
2751 // IRQs are maskable, so check if the I flag is clear
2752 && (!(regs->cc & FLAG_I)))
2755 WriteLog("\n*** IRQ ***\n\n");
2756 WriteLog("Clock=$%X\n", regs->clock);
2759 regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
2760 regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
2761 regs->WrMem(0x0100 + regs->sp--, regs->cc);
2764 regs->pc = RdMemW(0xFFFE); // Jump to IRQ vector
2767 regs->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset IRQ line
2771 // If we went longer than the passed in cycles, make a note of it so we can
2772 // subtract it out from a subsequent run. It's guaranteed to be non-
2773 // negative, because the condition that exits the main loop above is
2774 // written such that regs->clock has to be equal or larger than endCycles
2776 regs->overflow = regs->clock - endCycles;