2 // Virtual 65C02 Emulator v1.0
5 // (c) 2005 Underground Software
7 // JLH = James L. 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 II.
16 //However, the Atari version *does* occassionally pick strength while the Apple
17 //versions do not--which would seem to indicate a bug either in the RNG algorithm,
18 //the 65C02 core, or the Apple hardware. Need to investigate all three!
21 //#define __DEBUGMON__
32 #define CLR_Z (regs.cc &= ~FLAG_Z)
33 #define CLR_ZN (regs.cc &= ~(FLAG_Z | FLAG_N))
34 #define CLR_ZNC (regs.cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
35 #define CLR_V (regs.cc &= ~FLAG_V)
36 #define CLR_N (regs.cc &= ~FLAG_N)
37 #define SET_Z(r) (regs.cc = ((r) == 0 ? regs.cc | FLAG_Z : regs.cc & ~FLAG_Z))
38 #define SET_N(r) (regs.cc = ((r) & 0x80 ? regs.cc | FLAG_N : regs.cc & ~FLAG_N))
40 //Not sure that this code is computing the carry correctly... Investigate! [Seems to be]
41 #define SET_C_ADD(a,b) (regs.cc = ((uint8)(b) > (uint8)(~(a)) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
42 //#define SET_C_SUB(a,b) (regs.cc = ((uint8)(b) >= (uint8)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
43 #define SET_C_CMP(a,b) (regs.cc = ((uint8)(b) >= (uint8)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
44 #define SET_ZN(r) SET_N(r); SET_Z(r)
45 #define SET_ZNC_ADD(a,b,r) SET_N(r); SET_Z(r); SET_C_ADD(a,b)
46 //#define SET_ZNC_SUB(a,b,r) SET_N(r); SET_Z(r); SET_C_SUB(a,b)
47 #define SET_ZNC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b)
49 //Small problem with the EA_ macros: ABS macros don't increment the PC!!! !!! FIX !!!
50 //NB: It's properly handled by everything that uses it, so it works, even if it's klunky
51 //Small problem with fixing it is that you can't do it in a single instruction, i.e.,
52 //you have to read the value THEN you have to increment the PC. Unless there's another
55 #define EA_IMM regs.pc++
56 #define EA_ZP regs.RdMem(regs.pc++)
57 #define EA_ZP_X (regs.RdMem(regs.pc++) + regs.x) & 0xFF
58 #define EA_ZP_Y (regs.RdMem(regs.pc++) + regs.y) & 0xFF
59 #define EA_ABS FetchMemW(regs.pc)
60 #define EA_ABS_X FetchMemW(regs.pc) + regs.x
61 #define EA_ABS_Y FetchMemW(regs.pc) + regs.y
62 #define EA_IND_ZP_X RdMemW((regs.RdMem(regs.pc++) + regs.x) & 0xFF)
63 #define EA_IND_ZP_Y RdMemW(regs.RdMem(regs.pc++)) + regs.y
64 #define EA_IND_ZP RdMemW(regs.RdMem(regs.pc++))
66 #define READ_IMM regs.RdMem(EA_IMM)
67 #define READ_ZP regs.RdMem(EA_ZP)
68 #define READ_ZP_X regs.RdMem(EA_ZP_X)
69 #define READ_ZP_Y regs.RdMem(EA_ZP_Y)
70 #define READ_ABS regs.RdMem(EA_ABS)
71 #define READ_ABS_X regs.RdMem(EA_ABS_X)
72 #define READ_ABS_Y regs.RdMem(EA_ABS_Y)
73 #define READ_IND_ZP_X regs.RdMem(EA_IND_ZP_X)
74 #define READ_IND_ZP_Y regs.RdMem(EA_IND_ZP_Y)
75 #define READ_IND_ZP regs.RdMem(EA_IND_ZP)
77 #define READ_IMM_WB(v) uint16 addr = EA_IMM; v = regs.RdMem(addr)
78 #define READ_ZP_WB(v) uint16 addr = EA_ZP; v = regs.RdMem(addr)
79 #define READ_ZP_X_WB(v) uint16 addr = EA_ZP_X; v = regs.RdMem(addr)
80 #define READ_ABS_WB(v) uint16 addr = EA_ABS; v = regs.RdMem(addr)
81 #define READ_ABS_X_WB(v) uint16 addr = EA_ABS_X; v = regs.RdMem(addr)
82 #define READ_ABS_Y_WB(v) uint16 addr = EA_ABS_Y; v = regs.RdMem(addr)
83 #define READ_IND_ZP_X_WB(v) uint16 addr = EA_IND_ZP_X; v = regs.RdMem(addr)
84 #define READ_IND_ZP_Y_WB(v) uint16 addr = EA_IND_ZP_Y; v = regs.RdMem(addr)
85 #define READ_IND_ZP_WB(v) uint16 addr = EA_IND_ZP; v = regs.RdMem(addr)
87 #define WRITE_BACK(d) regs.WrMem(addr, (d))
89 // Private global variables
91 static V65C02REGS regs;
93 //This is probably incorrect, at least WRT to the $x7 and $xF opcodes... !!! FIX !!!
94 //Also this doesn't take into account the extra cycle it takes when an indirect fetch
95 //(ABS, ABS X/Y, ZP) crosses a page boundary, or extra cycle for BCD add/subtract...
96 #warning "Cycle counts are not accurate--!!! FIX !!!"
97 static uint8 CPUCycles[256] = {
98 7, 6, 1, 1, 5, 3, 5, 1, 3, 2, 2, 1, 6, 4, 6, 1,
99 2, 5, 5, 1, 5, 4, 6, 1, 2, 4, 2, 1, 6, 4, 6, 1,
100 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 4, 4, 6, 1,
101 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 2, 1, 4, 4, 6, 1,
102 6, 6, 1, 1, 1, 3, 5, 1, 3, 2, 2, 1, 3, 4, 6, 1,
103 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
104 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 6, 4, 6, 1,
105 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 4, 1, 6, 4, 6, 1,
106 2, 6, 1, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
107 2, 6, 5, 1, 4, 4, 4, 1, 2, 5, 2, 1, 4, 5, 5, 1,
108 2, 6, 2, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
109 2, 5, 5, 1, 4, 4, 4, 1, 2, 4, 2, 1, 4, 4, 4, 1,
110 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
111 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
112 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
113 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 4, 1, 1, 4, 6, 1 };
115 // Private function prototypes
117 static uint16 RdMemW(uint16);
118 static uint16 FetchMemW(uint16 addr);
121 // Read a uint16 out of 65C02 memory (big endian format)
123 static inline uint16 RdMemW(uint16 address)
125 return (uint16)(regs.RdMem(address + 1) << 8) | regs.RdMem(address + 0);
129 // Read a uint16 out of 65C02 memory (big endian format) and increment PC
131 static inline uint16 FetchMemW(uint16 address)
134 return (uint16)(regs.RdMem(address + 1) << 8) | regs.RdMem(address + 0);
139 // 65C02 OPCODE IMPLEMENTATION
141 // NOTE: Lots of macros are used here to save a LOT of typing. Also
142 // helps speed the debugging process. :-) Because of this, combining
143 // certain lines may look like a good idea but would end in disaster.
144 // You have been warned! ;-)
148 Mnemonic Addressing mode Form Opcode Size Timing
150 ADC Immediate ADC #Oper 69 2 2
151 Zero Page ADC Zpg 65 2 3
152 Zero Page,X ADC Zpg,X 75 2 4
153 Absolute ADC Abs 6D 3 4
154 Absolute,X ADC Abs,X 7D 3 4
155 Absolute,Y ADC Abs,Y 79 3 4
156 (Zero Page,X) ADC (Zpg,X) 61 2 6
157 (Zero Page),Y ADC (Zpg),Y 71 2 5
158 (Zero Page) ADC (Zpg) 72 2 5
163 //This is non-optimal, but it works--optimize later. :-)
164 #define OP_ADC_HANDLER(m) \
165 uint16 sum = (uint16)regs.a + (m) + (uint16)(regs.cc & FLAG_C); \
167 if (regs.cc & FLAG_D) \
169 if ((sum & 0x0F) > 0x09) \
172 if ((sum & 0xF0) > 0x90) \
176 regs.cc = (regs.cc & ~FLAG_C) | (sum >> 8); \
177 regs.cc = (~(regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
178 regs.a = sum & 0xFF; \
181 //OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
183 static void Op69(void) // ADC #
189 static void Op65(void) // ADC ZP
195 static void Op75(void) // ADC ZP, X
197 uint16 m = READ_ZP_X;
201 static void Op6D(void) // ADC ABS
207 static void Op7D(void) // ADC ABS, X
209 uint16 m = READ_ABS_X;
213 static void Op79(void) // ADC ABS, Y
215 uint16 m = READ_ABS_Y;
219 static void Op61(void) // ADC (ZP, X)
221 uint16 m = READ_IND_ZP_X;
225 static void Op71(void) // ADC (ZP), Y
227 uint16 m = READ_IND_ZP_Y;
231 static void Op72(void) // ADC (ZP)
233 uint16 m = READ_IND_ZP;
238 AND Immediate AND #Oper 29 2 2
239 Zero Page AND Zpg 25 2 3
240 Zero Page,X AND Zpg,X 35 2 4
241 Absolute AND Abs 2D 3 4
242 Absolute,X AND Abs,X 3D 3 4
243 Absolute,Y AND Abs,Y 39 3 4
244 (Zero Page,X) AND (Zpg,X) 21 2 6
245 (Zero Page),Y AND (Zpg),Y 31 2 5
246 (Zero Page) AND (Zpg) 32 2 5
251 #define OP_AND_HANDLER(m) \
255 static void Op29(void) // AND #
261 static void Op25(void) // AND ZP
267 static void Op35(void) // AND ZP, X
273 static void Op2D(void) // AND ABS
279 static void Op3D(void) // AND ABS, X
281 uint8 m = READ_ABS_X;
285 static void Op39(void) // AND ABS, Y
287 uint8 m = READ_ABS_Y;
291 static void Op21(void) // AND (ZP, X)
293 uint8 m = READ_IND_ZP_X;
297 static void Op31(void) // AND (ZP), Y
299 uint8 m = READ_IND_ZP_Y;
303 static void Op32(void) // AND (ZP)
305 uint8 m = READ_IND_ZP;
310 ASL Accumulator ASL A 0A 1 2
311 Zero Page ASL Zpg 06 2 5
312 Zero Page,X ASL Zpg,X 16 2 6
313 Absolute ASL Abs 0E 3 6
314 Absolute,X ASL Abs,X 1E 3 7
317 /*static void Op78(void) // LSL ABS
319 uint8 tmp; uint16 addr;
321 tmp = regs.RdMem(addr);
322 (tmp&0x80 ? regs.cc |= 0x01 : regs.cc &= 0xFE); // Shift hi bit into Carry
324 regs.WrMem(addr, tmp);
325 (tmp == 0 ? regs.cc |= 0x04 : regs.cc &= 0xFB); // Adjust Zero flag
326 (tmp&0x80 ? regs.cc |= 0x08 : regs.cc &= 0xF7); // Adjust Negative flag
331 #define OP_ASL_HANDLER(m) \
332 regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
336 static void Op0A(void) // ASL A
338 OP_ASL_HANDLER(regs.a);
341 static void Op06(void) // ASL ZP
349 static void Op16(void) // ASL ZP, X
357 static void Op0E(void) // ASL ABS
365 static void Op1E(void) // ASL ABS, X
374 BBR0 Relative BBR0 Oper 0F 2 2
375 BBR1 Relative BBR1 Oper 1F 2 2
376 BBR2 Relative BBR2 Oper 2F 2 2
377 BBR3 Relative BBR3 Oper 3F 2 2
378 BBR4 Relative BBR4 Oper 4F 2 2
379 BBR5 Relative BBR5 Oper 5F 2 2
380 BBR6 Relative BBR6 Oper 6F 2 2
381 BBR7 Relative BBR7 Oper 7F 2 2
382 BBS0 Relative BBS0 Oper 8F 2 2
383 BBS1 Relative BBS1 Oper 9F 2 2
384 BBS2 Relative BBS2 Oper AF 2 2
385 BBS3 Relative BBS3 Oper BF 2 2
386 BBS4 Relative BBS4 Oper CF 2 2
387 BBS5 Relative BBS5 Oper DF 2 2
388 BBS6 Relative BBS6 Oper EF 2 2
389 BBS7 Relative BBS7 Oper FF 2 2
394 static void Op0F(void) // BBR0
396 int16 m = (int16)(int8)READ_IMM;
398 if (!(regs.a & 0x01))
402 static void Op1F(void) // BBR1
404 int16 m = (int16)(int8)READ_IMM;
406 if (!(regs.a & 0x02))
410 static void Op2F(void) // BBR2
412 int16 m = (int16)(int8)READ_IMM;
414 if (!(regs.a & 0x04))
418 static void Op3F(void) // BBR3
420 int16 m = (int16)(int8)READ_IMM;
422 if (!(regs.a & 0x08))
426 static void Op4F(void) // BBR4
428 int16 m = (int16)(int8)READ_IMM;
430 if (!(regs.a & 0x10))
434 static void Op5F(void) // BBR5
436 int16 m = (int16)(int8)READ_IMM;
438 if (!(regs.a & 0x20))
442 static void Op6F(void) // BBR6
444 int16 m = (int16)(int8)READ_IMM;
446 if (!(regs.a & 0x40))
450 static void Op7F(void) // BBR7
452 int16 m = (int16)(int8)READ_IMM;
454 if (!(regs.a & 0x80))
458 static void Op8F(void) // BBS0
460 int16 m = (int16)(int8)READ_IMM;
466 static void Op9F(void) // BBS1
468 int16 m = (int16)(int8)READ_IMM;
474 static void OpAF(void) // BBS2
476 int16 m = (int16)(int8)READ_IMM;
482 static void OpBF(void) // BBS3
484 int16 m = (int16)(int8)READ_IMM;
490 static void OpCF(void) // BBS4
492 int16 m = (int16)(int8)READ_IMM;
498 static void OpDF(void) // BBS5
500 int16 m = (int16)(int8)READ_IMM;
506 static void OpEF(void) // BBS6
508 int16 m = (int16)(int8)READ_IMM;
514 static void OpFF(void) // BBS7
516 int16 m = (int16)(int8)READ_IMM;
523 BCC Relative BCC Oper 90 2 2
524 BCS Relative BCS Oper B0 2 2
525 BEQ Relative BEQ Oper F0 2 2
530 static void Op90(void) // BCC
532 int16 m = (int16)(int8)READ_IMM;
534 if (!(regs.cc & FLAG_C))
538 static void OpB0(void) // BCS
540 int16 m = (int16)(int8)READ_IMM;
542 if (regs.cc & FLAG_C)
546 static void OpF0(void) // BEQ
548 int16 m = (int16)(int8)READ_IMM;
550 if (regs.cc & FLAG_Z)
555 BIT Immediate BIT #Oper 89 2 2
556 Zero Page BIT Zpg 24 2 3
557 Zero Page,X BIT Zpg,X 34 2 4
558 Absolute BIT Abs 2C 3 4
559 Absolute,X BIT Abs,X 3C 3 4
564 /* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag (except in immediate
565 addressing mode where V & N are untouched.) The accumulator and the operand are ANDed and the
566 Z flag is set appropriately. */
568 #define OP_BIT_HANDLER(m) \
569 int8 result = regs.a & (m); \
570 regs.cc &= ~(FLAG_N | FLAG_V); \
571 regs.cc |= ((m) & 0xC0); \
574 static void Op89(void) // BIT #
577 int8 result = regs.a & m;
581 static void Op24(void) // BIT ZP
587 static void Op34(void) // BIT ZP, X
593 static void Op2C(void) // BIT ABS
599 static void Op3C(void) // BIT ABS, X
601 uint8 m = READ_ABS_X;
606 BMI Relative BMI Oper 30 2 2
607 BNE Relative BNE Oper D0 2 2
608 BPL Relative BPL Oper 10 2 2
609 BRA Relative BRA Oper 80 2 3
612 // More branch opcodes
614 static void Op30(void) // BMI
616 int16 m = (int16)(int8)READ_IMM;
618 if (regs.cc & FLAG_N)
622 static void OpD0(void) // BNE
624 int16 m = (int16)(int8)READ_IMM;
626 if (!(regs.cc & FLAG_Z))
630 static void Op10(void) // BPL
632 int16 m = (int16)(int8)READ_IMM;
634 if (!(regs.cc & FLAG_N))
638 static void Op80(void) // BRA
640 int16 m = (int16)(int8)READ_IMM;
645 BRK Implied BRK 00 1 7
648 static void Op00(void) // BRK
650 regs.cc |= FLAG_B; // Set B
651 regs.pc++; // RTI comes back to the instruction one byte after the BRK
652 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
653 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
654 regs.WrMem(0x0100 + regs.sp--, regs.cc);
655 regs.cc |= FLAG_I; // Set I
656 regs.cc &= ~FLAG_D; // & clear D
657 regs.pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
661 BVC Relative BVC Oper 50 2 2
662 BVS Relative BVS Oper 70 2 2
665 // Even more branch opcodes
667 static void Op50(void) // BVC
669 int16 m = (int16)(int8)READ_IMM;
671 if (!(regs.cc & FLAG_V))
675 static void Op70(void) // BVS
677 int16 m = (int16)(int8)READ_IMM;
679 if (regs.cc & FLAG_V)
684 CLC Implied CLC 18 1 2
687 static void Op18(void) // CLC
693 CLD Implied CLD D8 1 2
696 static void OpD8(void) // CLD
702 CLI Implied CLI 58 1 2
705 static void Op58(void) // CLI
711 CLV Implied CLV B8 1 2
714 static void OpB8(void) // CLV
720 CMP Immediate CMP #Oper C9 2 2
721 Zero Page CMP Zpg C5 2 3
722 Zero Page,X CMP Zpg D5 2 4
723 Absolute CMP Abs CD 3 4
724 Absolute,X CMP Abs,X DD 3 4
725 Absolute,Y CMP Abs,Y D9 3 4
726 (Zero Page,X) CMP (Zpg,X) C1 2 6
727 (Zero Page),Y CMP (Zpg),Y D1 2 5
728 (Zero Page) CMP (Zpg) D2 2 5
734 Here's the latest: The CMP is NOT generating the Z flag when A=$C0!
736 FABA: A0 07 LDY #$07 [PC=FABC, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
737 FABC: C6 01 DEC $01 [PC=FABE, SP=01FF, CC=N--B-I--, A=00, X=00, Y=07]
738 FABE: A5 01 LDA $01 [PC=FAC0, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
739 FAC0: C9 C0 CMP #$C0 [PC=FAC2, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
740 FAC2: F0 D7 BEQ $FA9B [PC=FAC4, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
741 FAC4: 8D F8 07 STA $07F8 [PC=FAC7, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
742 FAC7: B1 00 LDA ($00),Y
743 *** Read at I/O address C007
744 [PC=FAC9, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
745 FAC9: D9 01 FB CMP $FB01,Y [PC=FACC, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
746 FACC: D0 EC BNE $FABA [PC=FABA, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
748 Should be fixed now... (was adding instead of subtracting!)
750 Small problem here... First two should set the carry while the last one should clear it. !!! FIX !!! [DONE]
752 FDF0: C9 A0 CMP #$A0 [PC=FDF2, SP=01F1, CC=---B-IZ-, A=A0, X=02, Y=03]
753 FD7E: C9 E0 CMP #$E0 [PC=FD80, SP=01F4, CC=N--B-I--, A=A0, X=02, Y=03]
754 FD38: C9 9B CMP #$9B [PC=FD3A, SP=01F2, CC=---B-I-C, A=A0, X=02, Y=03]
756 Compare sets flags as if a subtraction had been carried out. If the value in the accumulator is equal or greater than the compared value, the Carry will be set. The equal (Z) and sign (S) flags will be set based on equality or lack thereof and the sign (i.e. A>=$80) of the accumulator.
759 #define OP_CMP_HANDLER(m) \
760 uint8 result = regs.a - (m); \
761 SET_ZNC_CMP(m, regs.a, result)
763 static void OpC9(void) // CMP #
769 static void OpC5(void) // CMP ZP
775 static void OpD5(void) // CMP ZP, X
781 static void OpCD(void) // CMP ABS
787 static void OpDD(void) // CMP ABS, X
789 uint8 m = READ_ABS_X;
793 static void OpD9(void) // CMP ABS, Y
795 uint8 m = READ_ABS_Y;
799 static void OpC1(void) // CMP (ZP, X)
801 uint8 m = READ_IND_ZP_X;
805 static void OpD1(void) // CMP (ZP), Y
807 uint8 m = READ_IND_ZP_Y;
811 static void OpD2(void) // CMP (ZP)
813 uint8 m = READ_IND_ZP;
818 CPX Immediate CPX #Oper E0 2 2
819 Zero Page CPX Zpg E4 2 3
820 Absolute CPX Abs EC 3 4
825 #define OP_CPX_HANDLER(m) \
826 uint8 result = regs.x - (m); \
827 SET_ZNC_CMP(m, regs.x, result)
829 static void OpE0(void) // CPX #
835 static void OpE4(void) // CPX ZP
841 static void OpEC(void) // CPX ABS
848 CPY Immediate CPY #Oper C0 2 2
849 Zero Page CPY Zpg C4 2 3
850 Absolute CPY Abs CC 3 4
855 #define OP_CPY_HANDLER(m) \
856 uint8 result = regs.y - (m); \
857 SET_ZNC_CMP(m, regs.y, result)
859 static void OpC0(void) // CPY #
865 static void OpC4(void) // CPY ZP
871 static void OpCC(void) // CPY ABS
878 DEA Accumulator DEA 3A 1 2
881 static void Op3A(void) // DEA
888 DEC Zero Page DEC Zpg C6 2 5
889 Zero Page,X DEC Zpg,X D6 2 6
890 Absolute DEC Abs CE 3 6
891 Absolute,X DEC Abs,X DE 3 7
896 #define OP_DEC_HANDLER(m) \
900 static void OpC6(void) // DEC ZP
908 static void OpD6(void) // DEC ZP, X
916 static void OpCE(void) // DEC ABS
924 static void OpDE(void) // DEC ABS, X
933 Here's one problem: DEX is setting the N flag!
935 D3EE: A2 09 LDX #$09 [PC=D3F0, SP=01F7, CC=---B-I-C, A=01, X=09, Y=08]
936 D3F0: 98 TYA [PC=D3F1, SP=01F7, CC=N--B-I-C, A=08, X=09, Y=08]
937 D3F1: 48 PHA [PC=D3F2, SP=01F6, CC=N--B-I-C, A=08, X=09, Y=08]
938 D3F2: B5 93 LDA $93,X [PC=D3F4, SP=01F6, CC=---B-IZC, A=00, X=09, Y=08]
939 D3F4: CA DEX [PC=D3F5, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
940 D3F5: 10 FA BPL $D3F1 [PC=D3F7, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
941 D3F7: 20 84 E4 JSR $E484 [PC=E484, SP=01F4, CC=N--B-I-C, A=00, X=08, Y=08]
943 should be fixed now...
947 DEX Implied DEX CA 1 2
950 static void OpCA(void) // DEX
957 DEY Implied DEY 88 1 2
960 static void Op88(void) // DEY
967 EOR Immediate EOR #Oper 49 2 2
968 Zero Page EOR Zpg 45 2 3
969 Zero Page,X EOR Zpg,X 55 2 4
970 Absolute EOR Abs 4D 3 4
971 Absolute,X EOR Abs,X 5D 3 4
972 Absolute,Y EOR Abs,Y 59 3 4
973 (Zero Page,X) EOR (Zpg,X) 41 2 6
974 (Zero Page),Y EOR (Zpg),Y 51 2 5
975 (Zero Page) EOR (Zpg) 52 2 5
980 #define OP_EOR_HANDLER(m) \
984 static void Op49(void) // EOR #
990 static void Op45(void) // EOR ZP
996 static void Op55(void) // EOR ZP, X
1002 static void Op4D(void) // EOR ABS
1008 static void Op5D(void) // EOR ABS, X
1010 uint8 m = READ_ABS_X;
1014 static void Op59(void) // EOR ABS, Y
1016 uint8 m = READ_ABS_Y;
1020 static void Op41(void) // EOR (ZP, X)
1022 uint8 m = READ_IND_ZP_X;
1026 static void Op51(void) // EOR (ZP), Y
1028 uint8 m = READ_IND_ZP_Y;
1032 static void Op52(void) // EOR (ZP)
1034 uint8 m = READ_IND_ZP;
1039 INA Accumulator INA 1A 1 2
1042 static void Op1A(void) // INA
1049 INC Zero Page INC Zpg E6 2 5
1050 Zero Page,X INC Zpg,X F6 2 6
1051 Absolute INC Abs EE 3 6
1052 Absolute,X INC Abs,X FE 3 7
1057 #define OP_INC_HANDLER(m) \
1061 static void OpE6(void) // INC ZP
1069 static void OpF6(void) // INC ZP, X
1077 static void OpEE(void) // INC ABS
1085 static void OpFE(void) // INC ABS, X
1094 INX Implied INX E8 1 2
1097 static void OpE8(void) // INX
1104 INY Implied INY C8 1 2
1107 static void OpC8(void) // INY
1114 JMP Absolute JMP Abs 4C 3 3
1115 (Absolute) JMP (Abs) 6C 3 5
1116 (Absolute,X) JMP (Abs,X) 7C 3 6
1121 static void Op4C(void) // JMP ABS
1123 regs.pc = RdMemW(regs.pc);
1126 static void Op6C(void) // JMP (ABS)
1128 // uint16 addr = RdMemW(regs.pc);
1130 //WriteLog("\n[JMP ABS]: addr fetched = %04X, bytes at %04X = %02X %02X (RdMemw=%04X)\n",
1131 // addr, addr, regs.RdMem(addr), regs.RdMem(addr+1), RdMemW(addr));
1133 // addr = RdMemW(addr);
1134 regs.pc = RdMemW(RdMemW(regs.pc));
1137 static void Op7C(void) // JMP (ABS, X)
1139 regs.pc = RdMemW(RdMemW(regs.pc) + regs.x);
1143 JSR Absolute JSR Abs 20 3 6
1146 //This is not jumping to the correct address... !!! FIX !!! [DONE]
1147 static void Op20(void) // JSR
1149 uint16 addr = RdMemW(regs.pc);
1150 regs.pc++; // Since it pushes return address - 1...
1151 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8);
1152 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
1157 LDA Immediate LDA #Oper A9 2 2
1158 Zero Page LDA Zpg A5 2 3
1159 Zero Page,X LDA Zpg,X B5 2 4
1160 Absolute LDA Abs AD 3 4
1161 Absolute,X LDA Abs,X BD 3 4
1162 Absolute,Y LDA Abs,Y B9 3 4
1163 (Zero Page,X) LDA (Zpg,X) A1 2 6
1164 (Zero Page),Y LDA (Zpg),Y B1 2 5
1165 (Zero Page) LDA (Zpg) B2 2 5
1170 #define OP_LDA_HANDLER(m) \
1174 static void OpA9(void) // LDA #
1180 static void OpA5(void) // LDA ZP
1186 static void OpB5(void) // LDA ZP, X
1188 uint8 m = READ_ZP_X;
1192 static void OpAD(void) // LDA ABS
1198 static void OpBD(void) // LDA ABS, X
1200 uint8 m = READ_ABS_X;
1204 static void OpB9(void) // LDA ABS, Y
1206 uint8 m = READ_ABS_Y;
1210 static void OpA1(void) // LDA (ZP, X)
1212 uint8 m = READ_IND_ZP_X;
1216 static void OpB1(void) // LDA (ZP), Y
1218 uint8 m = READ_IND_ZP_Y;
1222 static void OpB2(void) // LDA (ZP)
1224 uint8 m = READ_IND_ZP;
1229 LDX Immediate LDX #Oper A2 2 2
1230 Zero Page LDX Zpg A6 2 3
1231 Zero Page,Y LDX Zpg,Y B6 2 4
1232 Absolute LDX Abs AE 3 4
1233 Absolute,Y LDX Abs,Y BE 3 4
1238 #define OP_LDX_HANDLER(m) \
1242 static void OpA2(void) // LDX #
1248 static void OpA6(void) // LDX ZP
1254 static void OpB6(void) // LDX ZP, Y
1256 uint8 m = READ_ZP_Y;
1260 static void OpAE(void) // LDX ABS
1266 static void OpBE(void) // LDX ABS, Y
1268 uint8 m = READ_ABS_Y;
1273 LDY Immediate LDY #Oper A0 2 2
1274 Zero Page LDY Zpg A4 2 3
1275 Zero Page,Y LDY Zpg,X B4 2 4
1276 Absolute LDY Abs AC 3 4
1277 Absolute,Y LDY Abs,X BC 3 4
1282 #define OP_LDY_HANDLER(m) \
1286 static void OpA0(void) // LDY #
1292 static void OpA4(void) // LDY ZP
1298 static void OpB4(void) // LDY ZP, X
1300 uint8 m = READ_ZP_X;
1304 static void OpAC(void) // LDY ABS
1310 static void OpBC(void) // LDY ABS, X
1312 uint8 m = READ_ABS_X;
1317 LSR Accumulator LSR A 4A 1 2
1318 Zero Page LSR Zpg 46 2 5
1319 Zero Page,X LSR Zpg,X 56 2 6
1320 Absolute LSR Abs 4E 3 6
1321 Absolute,X LSR Abs,X 5E 3 7
1326 #define OP_LSR_HANDLER(m) \
1327 regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1331 static void Op4A(void) // LSR A
1333 OP_LSR_HANDLER(regs.a);
1336 static void Op46(void) // LSR ZP
1344 static void Op56(void) // LSR ZP, X
1352 static void Op4E(void) // LSR ABS
1360 static void Op5E(void) // LSR ABS, X
1369 NOP Implied NOP EA 1 2
1372 static void OpEA(void) // NOP
1377 ORA Immediate ORA #Oper 09 2 2
1378 Zero Page ORA Zpg 05 2 3
1379 Zero Page,X ORA Zpg,X 15 2 4
1380 Absolute ORA Abs 0D 3 4
1381 Absolute,X ORA Abs,X 1D 3 4
1382 Absolute,Y ORA Abs,Y 19 3 4
1383 (Zero Page,X) ORA (Zpg,X) 01 2 6
1384 (Zero Page),Y ORA (Zpg),Y 11 2 5
1385 (Zero Page) ORA (Zpg) 12 2 5
1390 #define OP_ORA_HANDLER(m) \
1394 static void Op09(void) // ORA #
1400 static void Op05(void) // ORA ZP
1406 static void Op15(void) // ORA ZP, X
1408 uint8 m = READ_ZP_X;
1412 static void Op0D(void) // ORA ABS
1418 static void Op1D(void) // ORA ABS, X
1420 uint8 m = READ_ABS_X;
1424 static void Op19(void) // ORA ABS, Y
1426 uint8 m = READ_ABS_Y;
1430 static void Op01(void) // ORA (ZP, X)
1432 uint8 m = READ_IND_ZP_X;
1436 static void Op11(void) // ORA (ZP), Y
1438 uint8 m = READ_IND_ZP_Y;
1442 static void Op12(void) // ORA (ZP)
1444 uint8 m = READ_IND_ZP;
1449 PHA Implied PHA 48 1 3
1452 static void Op48(void) // PHA
1454 regs.WrMem(0x0100 + regs.sp--, regs.a);
1457 static void Op08(void) // PHP
1459 regs.cc |= FLAG_UNK; // Make sure that the unused bit is always set
1460 regs.WrMem(0x0100 + regs.sp--, regs.cc);
1464 PHX Implied PHX DA 1 3
1467 static void OpDA(void) // PHX
1469 regs.WrMem(0x0100 + regs.sp--, regs.x);
1473 PHY Implied PHY 5A 1 3
1476 static void Op5A(void) // PHY
1478 regs.WrMem(0x0100 + regs.sp--, regs.y);
1482 PLA Implied PLA 68 1 4
1485 static void Op68(void) // PLA
1487 regs.a = regs.RdMem(0x0100 + ++regs.sp);
1491 static void Op28(void) // PLP
1493 regs.cc = regs.RdMem(0x0100 + ++regs.sp);
1497 PLX Implied PLX FA 1 4
1500 static void OpFA(void) // PLX
1502 regs.x = regs.RdMem(0x0100 + ++regs.sp);
1507 PLY Implied PLY 7A 1 4
1510 static void Op7A(void) // PLY
1512 regs.y = regs.RdMem(0x0100 + ++regs.sp);
1517 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.
1518 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1519 zp 07 17 27 37 47 57 67 77
1520 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1521 zp 87 97 A7 B7 C7 D7 E7 F7
1526 static void Op07(void) // RMB0 ZP
1534 static void Op17(void) // RMB1 ZP
1542 static void Op27(void) // RMB2 ZP
1550 static void Op37(void) // RMB3 ZP
1558 static void Op47(void) // RMB4 ZP
1566 static void Op57(void) // RMB5 ZP
1574 static void Op67(void) // RMB6 ZP
1582 static void Op77(void) // RMB7 ZP
1591 ROL Accumulator ROL A 2A 1 2
1592 Zero Page ROL Zpg 26 2 5
1593 Zero Page,X ROL Zpg,X 36 2 6
1594 Absolute ROL Abs 2E 3 6
1595 Absolute,X ROL Abs,X 3E 3 7
1600 #define OP_ROL_HANDLER(m) \
1601 uint8 tmp = regs.cc & 0x01; \
1602 regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1603 (m) = ((m) << 1) | tmp; \
1606 static void Op2A(void) // ROL A
1608 OP_ROL_HANDLER(regs.a);
1611 static void Op26(void) // ROL ZP
1619 static void Op36(void) // ROL ZP, X
1627 static void Op2E(void) // ROL ABS
1635 static void Op3E(void) // ROL ABS, X
1644 ROR Accumulator ROR A 6A 1 2
1645 Zero Page ROR Zpg 66 2 5
1646 Zero Page,X ROR Zpg,X 76 2 6
1647 Absolute ROR Abs 6E 3 6
1648 Absolute,X ROR Abs,X 7E 3 7
1653 #define OP_ROR_HANDLER(m) \
1654 uint8 tmp = (regs.cc & 0x01) << 7; \
1655 regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1656 (m) = ((m) >> 1) | tmp; \
1659 static void Op6A(void) // ROR A
1661 OP_ROR_HANDLER(regs.a);
1664 static void Op66(void) // ROR ZP
1672 static void Op76(void) // ROR ZP, X
1680 static void Op6E(void) // ROR ABS
1688 static void Op7E(void) // ROR ABS, X
1697 RTI Implied RTI 40 1 6
1700 static void Op40(void) // RTI
1702 regs.cc = regs.RdMem(0x0100 + ++regs.sp);
1703 //clear I (seems to be the case, either that or clear it in the IRQ setup...)
1704 //I can't find *any* verification that this is the case.
1705 // regs.cc &= ~FLAG_I;
1706 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1707 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1711 RTS Implied RTS 60 1 6
1714 static void Op60(void) // RTS
1716 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1717 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1718 regs.pc++; // Since it pushes return address - 1...
1719 //printf("*** RTS: PC = $%04X, SP= $1%02X\n", regs.pc, regs.sp);
1724 SBC Immediate SBC #Oper E9 2 2
1725 Zero Page SBC Zpg E5 2 3
1726 Zero Page,X SBC Zpg,X F5 2 4
1727 Absolute SBC Abs ED 3 4
1728 Absolute,X SBC Abs,X FD 3 4
1729 Absolute,Y SBC Abs,Y F9 3 4
1730 (Zero Page,X) SBC (Zpg,X) E1 2 6
1731 (Zero Page),Y SBC (Zpg),Y F1 2 5
1732 (Zero Page) SBC (Zpg) F2 2 5
1737 //This is non-optimal, but it works--optimize later. :-)
1738 //This is correct except for the BCD handling... !!! FIX !!! [Possibly DONE]
1739 #define OP_SBC_HANDLER(m) \
1740 uint16 sum = (uint16)regs.a - (m) - (uint16)((regs.cc & FLAG_C) ^ 0x01); \
1742 if (regs.cc & FLAG_D) \
1744 if ((sum & 0x0F) > 0x09) \
1747 if ((sum & 0xF0) > 0x90) \
1751 regs.cc = (regs.cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
1752 regs.cc = ((regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
1753 regs.a = sum & 0xFF; \
1757 D5AF: 38 SEC [PC=D5B0, SP=01F6, CC=---B-I-C, A=4C, X=00, Y=06]
1759 *** HERE'S where it sets the D flag on a subtract... Arg!
1761 D5B0: F1 9D SBC ($9D),Y [PC=D5B2, SP=01F6, CC=N--BDI--, A=FE, X=00, Y=06]
1766 //OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
1768 static void OpE9(void) // SBC #
1770 uint16 m = READ_IMM;
1774 static void OpE5(void) // SBC ZP
1780 static void OpF5(void) // SBC ZP, X
1782 uint16 m = READ_ZP_X;
1786 static void OpED(void) // SBC ABS
1788 uint16 m = READ_ABS;
1792 static void OpFD(void) // SBC ABS, X
1794 uint16 m = READ_ABS_X;
1798 static void OpF9(void) // SBC ABS, Y
1800 uint16 m = READ_ABS_Y;
1804 static void OpE1(void) // SBC (ZP, X)
1806 uint16 m = READ_IND_ZP_X;
1810 static void OpF1(void) // SBC (ZP), Y
1812 uint16 m = READ_IND_ZP_Y;
1816 static void OpF2(void) // SBC (ZP)
1818 uint16 m = READ_IND_ZP;
1823 SEC Implied SEC 38 1 2
1826 static void Op38(void) // SEC
1832 SED Implied SED F8 1 2
1835 static void OpF8(void) // SED
1841 SEI Implied SEI 78 1 2
1844 static void Op78(void) // SEI
1850 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.
1851 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1852 zp 07 17 27 37 47 57 67 77
1853 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1854 zp 87 97 A7 B7 C7 D7 E7 F7
1859 static void Op87(void) // SMB0 ZP
1867 static void Op97(void) // SMB1 ZP
1875 static void OpA7(void) // SMB2 ZP
1883 static void OpB7(void) // SMB3 ZP
1891 static void OpC7(void) // SMB4 ZP
1899 static void OpD7(void) // SMB5 ZP
1907 static void OpE7(void) // SMB6 ZP
1915 static void OpF7(void) // SMB7 ZP
1924 STA Zero Page STA Zpg 85 2 3
1925 Zero Page,X STA Zpg,X 95 2 4
1926 Absolute STA Abs 8D 3 4
1927 Absolute,X STA Abs,X 9D 3 5
1928 Absolute,Y STA Abs,Y 99 3 5
1929 (Zero Page,X) STA (Zpg,X) 81 2 6
1930 (Zero Page),Y STA (Zpg),Y 91 2 6
1931 (Zero Page) STA (Zpg) 92 2 5
1936 static void Op85(void)
1938 regs.WrMem(EA_ZP, regs.a);
1941 static void Op95(void)
1943 regs.WrMem(EA_ZP_X, regs.a);
1946 static void Op8D(void)
1948 regs.WrMem(EA_ABS, regs.a);
1951 static void Op9D(void)
1953 regs.WrMem(EA_ABS_X, regs.a);
1956 static void Op99(void)
1958 regs.WrMem(EA_ABS_Y, regs.a);
1961 static void Op81(void)
1963 regs.WrMem(EA_IND_ZP_X, regs.a);
1966 static void Op91(void)
1968 regs.WrMem(EA_IND_ZP_Y, regs.a);
1971 static void Op92(void)
1973 regs.WrMem(EA_IND_ZP, regs.a);
1977 STX Zero Page STX Zpg 86 2 3
1978 Zero Page,Y STX Zpg,Y 96 2 4
1979 Absolute STX Abs 8E 3 4
1984 static void Op86(void)
1986 regs.WrMem(EA_ZP, regs.x);
1989 static void Op96(void)
1991 regs.WrMem(EA_ZP_X, regs.x);
1994 static void Op8E(void)
1996 regs.WrMem(EA_ABS, regs.x);
2000 STY Zero Page STY Zpg 84 2 3
2001 Zero Page,X STY Zpg,X 94 2 4
2002 Absolute STY Abs 8C 3 4
2007 static void Op84(void)
2009 regs.WrMem(EA_ZP, regs.y);
2012 static void Op94(void)
2014 regs.WrMem(EA_ZP_X, regs.y);
2017 static void Op8C(void)
2019 regs.WrMem(EA_ABS, regs.y);
2023 STZ Zero Page STZ Zpg 64 2 3
2024 Zero Page,X STZ Zpg,X 74 2 4
2025 Absolute STZ Abs 9C 3 4
2026 Absolute,X STZ Abs,X 9E 3 5
2031 static void Op64(void)
2033 regs.WrMem(EA_ZP, 0x00);
2036 static void Op74(void)
2038 regs.WrMem(EA_ZP_X, 0x00);
2041 static void Op9C(void)
2043 regs.WrMem(EA_ABS, 0x00);
2046 static void Op9E(void)
2048 regs.WrMem(EA_ABS_X, 0x00);
2052 TAX Implied TAX AA 1 2
2055 static void OpAA(void) // TAX
2062 TAY Implied TAY A8 1 2
2065 static void OpA8(void) // TAY
2072 TRB Zero Page TRB Zpg 14 2 5
2073 Absolute TRB Abs 1C 3 6
2078 #define OP_TRB_HANDLER(m) \
2079 SET_Z(m & regs.a); \
2082 static void Op14(void) // TRB ZP
2090 static void Op1C(void) // TRB ABS
2099 TSB Zero Page TSB Zpg 04 2 5
2100 Absolute TSB Abs 0C 3 6
2105 #define OP_TSB_HANDLER(m) \
2106 SET_Z(m & regs.a); \
2109 static void Op04(void) // TSB ZP
2117 static void Op0C(void) // TSB ABS
2126 TSX Implied TSX BA 1 2
2129 static void OpBA(void) // TSX
2136 TXA Implied TXA 8A 1 2
2139 static void Op8A(void) // TXA
2146 TXS Implied TXS 9A 1 2
2149 static void Op9A(void) // TXS
2155 TYA Implied TYA 98 1 2
2157 static void Op98(void) // TYA
2163 static void Op__(void)
2165 regs.cpuFlags |= V65C02_STATE_ILLEGAL_INST;
2170 // Ok, the exec_op[] array is globally defined here basically to save
2171 // a LOT of unnecessary typing. Sure it's ugly, but hey, it works!
2173 void (* exec_op[256])() = {
2174 Op00, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op__, Op0C, Op0D, Op0E, Op0F,
2175 Op10, Op11, Op12, Op__, Op14, Op15, Op16, Op17, Op18, Op19, Op1A, Op__, Op1C, Op1D, Op1E, Op1F,
2176 Op20, Op21, Op__, Op__, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op__, Op2C, Op2D, Op2E, Op2F,
2177 Op30, Op31, Op32, Op__, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op__, Op3C, Op3D, Op3E, Op3F,
2178 Op40, Op41, Op__, Op__, Op__, Op45, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op4E, Op4F,
2179 Op50, Op51, Op52, Op__, Op__, Op55, Op56, Op57, Op58, Op59, Op5A, Op__, Op__, Op5D, Op5E, Op5F,
2180 Op60, Op61, Op__, Op__, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op__, Op6C, Op6D, Op6E, Op6F,
2181 Op70, Op71, Op72, Op__, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op__, Op7C, Op7D, Op7E, Op7F,
2182 Op80, Op81, Op__, Op__, Op84, Op85, Op86, Op87, Op88, Op89, Op8A, Op__, Op8C, Op8D, Op8E, Op8F,
2183 Op90, Op91, Op92, Op__, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op__, Op9C, Op9D, Op9E, Op9F,
2184 OpA0, OpA1, OpA2, Op__, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, Op__, OpAC, OpAD, OpAE, OpAF,
2185 OpB0, OpB1, OpB2, Op__, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, Op__, OpBC, OpBD, OpBE, OpBF,
2186 OpC0, OpC1, Op__, Op__, OpC4, OpC5, OpC6, OpC7, OpC8, OpC9, OpCA, Op__, OpCC, OpCD, OpCE, OpCF,
2187 OpD0, OpD1, OpD2, Op__, Op__, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, Op__, Op__, OpDD, OpDE, OpDF,
2188 OpE0, OpE1, Op__, Op__, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, Op__, OpEC, OpED, OpEE, OpEF,
2189 OpF0, OpF1, OpF2, Op__, Op__, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, Op__, Op__, OpFD, OpFE, OpFF
2193 // Internal "memcpy" (so we don't have to link with any external libraries!)
2195 static void myMemcpy(void * dst, void * src, uint32 size)
2197 uint8 * d = (uint8 *)dst, * s = (uint8 *)src;
2199 for(uint32 i=0; i<size; i++)
2204 FCA8: 38 698 WAIT SEC
2205 FCA9: 48 699 WAIT2 PHA
2206 FCAA: E9 01 700 WAIT3 SBC #$01
2207 FCAC: D0 FC 701 BNE WAIT3 ;1.0204 USEC
2208 FCAE: 68 702 PLA ;(13+27/2*A+5/2*A*A)
2209 FCAF: E9 01 703 SBC #$01
2210 FCB1: D0 F6 704 BNE WAIT2
2213 FBD9: C9 87 592 BELL1 CMP #$87 ;BELL CHAR? (CNTRL-G)
2214 FBDB: D0 12 593 BNE RTS2B ; NO, RETURN
2215 FBDD: A9 40 594 LDA #$40 ;DELAY .01 SECONDS
2216 FBDF: 20 A8 FC 595 JSR WAIT
2217 FBE2: A0 C0 596 LDY #$C0
2218 FBE4: A9 0C 597 BELL2 LDA #$0C ;TOGGLE SPEAKER AT
2219 FBE6: 20 A8 FC 598 JSR WAIT ; 1 KHZ FOR .1 SEC.
2220 FBE9: AD 30 C0 599 LDA SPKR
2222 FBED: D0 F5 601 BNE BELL2
2223 FBEF: 60 602 RTS2B RTS
2225 //int instCount[256];
2227 bool dumpDis = false;
2230 //Note: could enforce regs.clock to zero on starting the CPU with an Init() function...
2232 //static uint32 limit = 0;
2235 // Function to execute 65C02 for "cycles" cycles
2237 void Execute65C02(V65C02REGS * context, uint32 cycles)
2239 myMemcpy(®s, context, sizeof(V65C02REGS));
2242 // NOTE: There *must* be some way of doing this without requiring the caller to subtract out
2243 // the previous run's cycles. !!! FIX !!!
2245 // while (regs.clock < regs.clock + cycles) <-- won't work
2247 // This isn't as accurate as subtracting out cycles from regs.clock...
2248 // Unless limit is a static variable, adding cycles to it each time through...
2249 uint32 limit = regs.clock + cycles;
2250 while (regs.clock < limit)
2252 // but have wraparound to deal with. :-/
2256 if (regs.clock + cycles > 0xFFFFFFFF)
2259 uint64 endCycles = regs.clock + (uint64)cycles;
2261 while (regs.clock < endCycles)
2264 /*if (regs.pc == 0x4007)
2268 if (regs.pc == 0x444B)
2270 WriteLog("\n*** End of wait...\n\n");
2273 if (regs.pc == 0x444E)
2275 WriteLog("\n*** Start of wait...\n\n");
2281 //WAIT is commented out here because it's called by BELL1...
2282 if (regs.pc == 0xFCA8)
2284 WriteLog("\n*** WAIT subroutine...\n\n");
2287 if (regs.pc == 0xFBD9)
2289 WriteLog("\n*** BELL1 subroutine...\n\n");
2292 if (regs.pc == 0xFC58)
2294 WriteLog("\n*** HOME subroutine...\n\n");
2297 if (regs.pc == 0xFDED)
2299 WriteLog("\n*** COUT subroutine...\n\n");
2305 if (regs.pc == 0x2000)
2311 Decode65C02(regs.pc);
2313 uint8 opcode = regs.RdMem(regs.pc++);
2315 //if (!(regs.cpuFlags & V65C02_STATE_ILLEGAL_INST))
2316 //instCount[opcode]++;
2318 exec_op[opcode](); // Execute that opcode...
2319 regs.clock += CPUCycles[opcode];
2322 WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
2323 regs.pc, 0x0100 + regs.sp,
2324 (regs.cc & FLAG_N ? "N" : "-"), (regs.cc & FLAG_V ? "V" : "-"),
2325 (regs.cc & FLAG_B ? "B" : "-"), (regs.cc & FLAG_D ? "D" : "-"),
2326 (regs.cc & FLAG_I ? "I" : "-"), (regs.cc & FLAG_Z ? "Z" : "-"),
2327 (regs.cc & FLAG_C ? "C" : "-"), regs.a, regs.x, regs.y);
2331 if (regs.pc == 0xFCB3) // WAIT exit point
2335 /*if (regs.pc == 0xFBEF) // BELL1 exit point
2339 /*if (regs.pc == 0xFC22) // HOME exit point
2343 if (regs.pc == 0xFDFF) // COUT exit point
2347 if (regs.pc == 0xFBD8)
2349 WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
2353 //These should be correct now...
2354 if (regs.cpuFlags & V65C02_ASSERT_LINE_RESET)
2357 WriteLog("\n*** RESET ***\n\n");
2359 // Not sure about this...
2361 regs.cc = FLAG_B | FLAG_I; // Reset the CC register
2362 regs.pc = RdMemW(0xFFFC); // And load PC with the RESET vector
2364 context->cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2365 regs.cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2367 else if (regs.cpuFlags & V65C02_ASSERT_LINE_NMI)
2370 WriteLog("\n*** NMI ***\n\n");
2372 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
2373 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
2374 regs.WrMem(0x0100 + regs.sp--, regs.cc);
2375 regs.cc |= FLAG_I; // Set I
2376 regs.cc &= ~FLAG_D; // & clear D
2377 regs.pc = RdMemW(0xFFFA); // And do it!
2380 context->cpuFlags &= ~V65C02_ASSERT_LINE_NMI;// Reset the asserted line (NMI)...
2381 regs.cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset the asserted line (NMI)...
2383 else if (regs.cpuFlags & V65C02_ASSERT_LINE_IRQ)
2385 if (!(regs.cc & FLAG_I)) // Process an interrupt (I=0)?
2388 WriteLog("\n*** IRQ ***\n\n");
2390 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
2391 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
2392 regs.WrMem(0x0100 + regs.sp--, regs.cc);
2393 regs.cc |= FLAG_I; // Set I
2394 regs.cc &= ~FLAG_D; // & clear D
2395 regs.pc = RdMemW(0xFFFE); // And do it!
2398 context->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2399 regs.cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2404 //This is a lame way of doing it, but in the end the simplest--however, it destroys any
2405 //record of elasped CPU time. Not sure that it's important to keep track, but there it is.
2406 // Now we use a 64-bit integer, so it won't wrap for about 500 millenia. ;-)
2407 // regs.clock -= cycles;
2409 myMemcpy(context, ®s, sizeof(V65C02REGS));
2413 // Get the clock of the currently executing CPU
2415 uint64 GetCurrentV65C02Clock(void)