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 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1704 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1708 RTS Implied RTS 60 1 6
1711 static void Op60(void) // RTS
1713 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1714 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1715 regs.pc++; // Since it pushes return address - 1...
1716 //printf("*** RTS: PC = $%04X, SP= $1%02X\n", regs.pc, regs.sp);
1721 SBC Immediate SBC #Oper E9 2 2
1722 Zero Page SBC Zpg E5 2 3
1723 Zero Page,X SBC Zpg,X F5 2 4
1724 Absolute SBC Abs ED 3 4
1725 Absolute,X SBC Abs,X FD 3 4
1726 Absolute,Y SBC Abs,Y F9 3 4
1727 (Zero Page,X) SBC (Zpg,X) E1 2 6
1728 (Zero Page),Y SBC (Zpg),Y F1 2 5
1729 (Zero Page) SBC (Zpg) F2 2 5
1734 //This is non-optimal, but it works--optimize later. :-)
1735 //This is correct except for the BCD handling... !!! FIX !!! [Possibly DONE]
1736 #define OP_SBC_HANDLER(m) \
1737 uint16 sum = (uint16)regs.a - (m) - (uint16)((regs.cc & FLAG_C) ^ 0x01); \
1739 if (regs.cc & FLAG_D) \
1741 if ((sum & 0x0F) > 0x09) \
1744 if ((sum & 0xF0) > 0x90) \
1748 regs.cc = (regs.cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
1749 regs.cc = ((regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
1750 regs.a = sum & 0xFF; \
1754 D5AF: 38 SEC [PC=D5B0, SP=01F6, CC=---B-I-C, A=4C, X=00, Y=06]
1756 *** HERE'S where it sets the D flag on a subtract... Arg!
1758 D5B0: F1 9D SBC ($9D),Y [PC=D5B2, SP=01F6, CC=N--BDI--, A=FE, X=00, Y=06]
1763 //OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
1765 static void OpE9(void) // SBC #
1767 uint16 m = READ_IMM;
1771 static void OpE5(void) // SBC ZP
1777 static void OpF5(void) // SBC ZP, X
1779 uint16 m = READ_ZP_X;
1783 static void OpED(void) // SBC ABS
1785 uint16 m = READ_ABS;
1789 static void OpFD(void) // SBC ABS, X
1791 uint16 m = READ_ABS_X;
1795 static void OpF9(void) // SBC ABS, Y
1797 uint16 m = READ_ABS_Y;
1801 static void OpE1(void) // SBC (ZP, X)
1803 uint16 m = READ_IND_ZP_X;
1807 static void OpF1(void) // SBC (ZP), Y
1809 uint16 m = READ_IND_ZP_Y;
1813 static void OpF2(void) // SBC (ZP)
1815 uint16 m = READ_IND_ZP;
1820 SEC Implied SEC 38 1 2
1823 static void Op38(void) // SEC
1829 SED Implied SED F8 1 2
1832 static void OpF8(void) // SED
1838 SEI Implied SEI 78 1 2
1841 static void Op78(void) // SEI
1847 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.
1848 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1849 zp 07 17 27 37 47 57 67 77
1850 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1851 zp 87 97 A7 B7 C7 D7 E7 F7
1856 static void Op87(void) // SMB0 ZP
1864 static void Op97(void) // SMB1 ZP
1872 static void OpA7(void) // SMB2 ZP
1880 static void OpB7(void) // SMB3 ZP
1888 static void OpC7(void) // SMB4 ZP
1896 static void OpD7(void) // SMB5 ZP
1904 static void OpE7(void) // SMB6 ZP
1912 static void OpF7(void) // SMB7 ZP
1921 STA Zero Page STA Zpg 85 2 3
1922 Zero Page,X STA Zpg,X 95 2 4
1923 Absolute STA Abs 8D 3 4
1924 Absolute,X STA Abs,X 9D 3 5
1925 Absolute,Y STA Abs,Y 99 3 5
1926 (Zero Page,X) STA (Zpg,X) 81 2 6
1927 (Zero Page),Y STA (Zpg),Y 91 2 6
1928 (Zero Page) STA (Zpg) 92 2 5
1933 static void Op85(void)
1935 regs.WrMem(EA_ZP, regs.a);
1938 static void Op95(void)
1940 regs.WrMem(EA_ZP_X, regs.a);
1943 static void Op8D(void)
1945 regs.WrMem(EA_ABS, regs.a);
1948 static void Op9D(void)
1950 regs.WrMem(EA_ABS_X, regs.a);
1953 static void Op99(void)
1955 regs.WrMem(EA_ABS_Y, regs.a);
1958 static void Op81(void)
1960 regs.WrMem(EA_IND_ZP_X, regs.a);
1963 static void Op91(void)
1965 regs.WrMem(EA_IND_ZP_Y, regs.a);
1968 static void Op92(void)
1970 regs.WrMem(EA_IND_ZP, regs.a);
1974 STX Zero Page STX Zpg 86 2 3
1975 Zero Page,Y STX Zpg,Y 96 2 4
1976 Absolute STX Abs 8E 3 4
1981 static void Op86(void)
1983 regs.WrMem(EA_ZP, regs.x);
1986 static void Op96(void)
1988 regs.WrMem(EA_ZP_X, regs.x);
1991 static void Op8E(void)
1993 regs.WrMem(EA_ABS, regs.x);
1997 STY Zero Page STY Zpg 84 2 3
1998 Zero Page,X STY Zpg,X 94 2 4
1999 Absolute STY Abs 8C 3 4
2004 static void Op84(void)
2006 regs.WrMem(EA_ZP, regs.y);
2009 static void Op94(void)
2011 regs.WrMem(EA_ZP_X, regs.y);
2014 static void Op8C(void)
2016 regs.WrMem(EA_ABS, regs.y);
2020 STZ Zero Page STZ Zpg 64 2 3
2021 Zero Page,X STZ Zpg,X 74 2 4
2022 Absolute STZ Abs 9C 3 4
2023 Absolute,X STZ Abs,X 9E 3 5
2028 static void Op64(void)
2030 regs.WrMem(EA_ZP, 0x00);
2033 static void Op74(void)
2035 regs.WrMem(EA_ZP_X, 0x00);
2038 static void Op9C(void)
2040 regs.WrMem(EA_ABS, 0x00);
2043 static void Op9E(void)
2045 regs.WrMem(EA_ABS_X, 0x00);
2049 TAX Implied TAX AA 1 2
2052 static void OpAA(void) // TAX
2059 TAY Implied TAY A8 1 2
2062 static void OpA8(void) // TAY
2069 TRB Zero Page TRB Zpg 14 2 5
2070 Absolute TRB Abs 1C 3 6
2075 #define OP_TRB_HANDLER(m) \
2076 SET_Z(m & regs.a); \
2079 static void Op14(void) // TRB ZP
2087 static void Op1C(void) // TRB ABS
2096 TSB Zero Page TSB Zpg 04 2 5
2097 Absolute TSB Abs 0C 3 6
2102 #define OP_TSB_HANDLER(m) \
2103 SET_Z(m & regs.a); \
2106 static void Op04(void) // TSB ZP
2114 static void Op0C(void) // TSB ABS
2123 TSX Implied TSX BA 1 2
2126 static void OpBA(void) // TSX
2133 TXA Implied TXA 8A 1 2
2136 static void Op8A(void) // TXA
2143 TXS Implied TXS 9A 1 2
2146 static void Op9A(void) // TXS
2152 TYA Implied TYA 98 1 2
2154 static void Op98(void) // TYA
2160 static void Op__(void)
2162 regs.cpuFlags |= V65C02_STATE_ILLEGAL_INST;
2167 // Ok, the exec_op[] array is globally defined here basically to save
2168 // a LOT of unnecessary typing. Sure it's ugly, but hey, it works!
2170 void (* exec_op[256])() = {
2171 Op00, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op__, Op0C, Op0D, Op0E, Op0F,
2172 Op10, Op11, Op12, Op__, Op14, Op15, Op16, Op17, Op18, Op19, Op1A, Op__, Op1C, Op1D, Op1E, Op1F,
2173 Op20, Op21, Op__, Op__, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op__, Op2C, Op2D, Op2E, Op2F,
2174 Op30, Op31, Op32, Op__, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op__, Op3C, Op3D, Op3E, Op3F,
2175 Op40, Op41, Op__, Op__, Op__, Op45, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op4E, Op4F,
2176 Op50, Op51, Op52, Op__, Op__, Op55, Op56, Op57, Op58, Op59, Op5A, Op__, Op__, Op5D, Op5E, Op5F,
2177 Op60, Op61, Op__, Op__, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op__, Op6C, Op6D, Op6E, Op6F,
2178 Op70, Op71, Op72, Op__, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op__, Op7C, Op7D, Op7E, Op7F,
2179 Op80, Op81, Op__, Op__, Op84, Op85, Op86, Op87, Op88, Op89, Op8A, Op__, Op8C, Op8D, Op8E, Op8F,
2180 Op90, Op91, Op92, Op__, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op__, Op9C, Op9D, Op9E, Op9F,
2181 OpA0, OpA1, OpA2, Op__, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, Op__, OpAC, OpAD, OpAE, OpAF,
2182 OpB0, OpB1, OpB2, Op__, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, Op__, OpBC, OpBD, OpBE, OpBF,
2183 OpC0, OpC1, Op__, Op__, OpC4, OpC5, OpC6, OpC7, OpC8, OpC9, OpCA, Op__, OpCC, OpCD, OpCE, OpCF,
2184 OpD0, OpD1, OpD2, Op__, Op__, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, Op__, Op__, OpDD, OpDE, OpDF,
2185 OpE0, OpE1, Op__, Op__, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, Op__, OpEC, OpED, OpEE, OpEF,
2186 OpF0, OpF1, OpF2, Op__, Op__, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, Op__, Op__, OpFD, OpFE, OpFF
2190 // Internal "memcpy" (so we don't have to link with any external libraries!)
2192 static void myMemcpy(void * dst, void * src, uint32 size)
2194 uint8 * d = (uint8 *)dst, * s = (uint8 *)src;
2196 for(uint32 i=0; i<size; i++)
2201 FCA8: 38 698 WAIT SEC
2202 FCA9: 48 699 WAIT2 PHA
2203 FCAA: E9 01 700 WAIT3 SBC #$01
2204 FCAC: D0 FC 701 BNE WAIT3 ;1.0204 USEC
2205 FCAE: 68 702 PLA ;(13+27/2*A+5/2*A*A)
2206 FCAF: E9 01 703 SBC #$01
2207 FCB1: D0 F6 704 BNE WAIT2
2210 FBD9: C9 87 592 BELL1 CMP #$87 ;BELL CHAR? (CNTRL-G)
2211 FBDB: D0 12 593 BNE RTS2B ; NO, RETURN
2212 FBDD: A9 40 594 LDA #$40 ;DELAY .01 SECONDS
2213 FBDF: 20 A8 FC 595 JSR WAIT
2214 FBE2: A0 C0 596 LDY #$C0
2215 FBE4: A9 0C 597 BELL2 LDA #$0C ;TOGGLE SPEAKER AT
2216 FBE6: 20 A8 FC 598 JSR WAIT ; 1 KHZ FOR .1 SEC.
2217 FBE9: AD 30 C0 599 LDA SPKR
2219 FBED: D0 F5 601 BNE BELL2
2220 FBEF: 60 602 RTS2B RTS
2222 //int instCount[256];
2224 bool dumpDis = false;
2227 //Note: could enforce regs.clock to zero on starting the CPU with an Init() function...
2229 //static uint32 limit = 0;
2232 // Function to execute 65C02 for "cycles" cycles
2234 void Execute65C02(V65C02REGS * context, uint32 cycles)
2236 myMemcpy(®s, context, sizeof(V65C02REGS));
2239 // NOTE: There *must* be some way of doing this without requiring the caller to subtract out
2240 // the previous run's cycles. !!! FIX !!!
2242 // while (regs.clock < regs.clock + cycles) <-- won't work
2244 // This isn't as accurate as subtracting out cycles from regs.clock...
2245 // Unless limit is a static variable, adding cycles to it each time through...
2246 uint32 limit = regs.clock + cycles;
2247 while (regs.clock < limit)
2249 // but have wraparound to deal with. :-/
2253 if (regs.clock + cycles > 0xFFFFFFFF)
2257 while (regs.clock < cycles)
2260 /*if (regs.pc == 0x4007)
2264 if (regs.pc == 0x444B)
2266 WriteLog("\n*** End of wait...\n\n");
2269 if (regs.pc == 0x444E)
2271 WriteLog("\n*** Start of wait...\n\n");
2277 //WAIT is commented out here because it's called by BELL1...
2278 if (regs.pc == 0xFCA8)
2280 WriteLog("\n*** WAIT subroutine...\n\n");
2283 if (regs.pc == 0xFBD9)
2285 WriteLog("\n*** BELL1 subroutine...\n\n");
2288 if (regs.pc == 0xFC58)
2290 WriteLog("\n*** HOME subroutine...\n\n");
2293 if (regs.pc == 0xFDED)
2295 WriteLog("\n*** COUT subroutine...\n\n");
2302 Decode65C02(regs.pc);
2304 uint8 opcode = regs.RdMem(regs.pc++);
2306 //if (!(regs.cpuFlags & V65C02_STATE_ILLEGAL_INST))
2307 //instCount[opcode]++;
2309 exec_op[opcode](); // Execute that opcode...
2310 regs.clock += CPUCycles[opcode];
2313 WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
2314 regs.pc, 0x0100 + regs.sp,
2315 (regs.cc & FLAG_N ? "N" : "-"), (regs.cc & FLAG_V ? "V" : "-"),
2316 (regs.cc & FLAG_B ? "B" : "-"), (regs.cc & FLAG_D ? "D" : "-"),
2317 (regs.cc & FLAG_I ? "I" : "-"), (regs.cc & FLAG_Z ? "Z" : "-"),
2318 (regs.cc & FLAG_C ? "C" : "-"), regs.a, regs.x, regs.y);
2322 if (regs.pc == 0xFCB3) // WAIT exit point
2326 /*if (regs.pc == 0xFBEF) // BELL1 exit point
2330 /*if (regs.pc == 0xFC22) // HOME exit point
2334 if (regs.pc == 0xFDFF) // COUT exit point
2338 if (regs.pc == 0xFBD8)
2340 WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
2344 //These should be correct now...
2345 if (regs.cpuFlags & V65C02_ASSERT_LINE_RESET)
2348 WriteLog("\n*** RESET ***\n\n");
2350 // Not sure about this...
2352 regs.cc = FLAG_B | FLAG_I; // Reset the CC register
2353 regs.pc = RdMemW(0xFFFC); // And load PC with the RESET vector
2355 context->cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2356 regs.cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2358 else if (regs.cpuFlags & V65C02_ASSERT_LINE_NMI)
2361 WriteLog("\n*** NMI ***\n\n");
2363 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
2364 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
2365 regs.WrMem(0x0100 + regs.sp--, regs.cc);
2366 regs.cc |= FLAG_I; // Set I
2367 regs.cc &= ~FLAG_D; // & clear D
2368 regs.pc = RdMemW(0xFFFA); // And do it!
2371 context->cpuFlags &= ~V65C02_ASSERT_LINE_NMI;// Reset the asserted line (NMI)...
2372 regs.cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset the asserted line (NMI)...
2374 else if (regs.cpuFlags & V65C02_ASSERT_LINE_IRQ)
2376 if (!(regs.cc & FLAG_I)) // Process an interrupt (I=0)?
2379 WriteLog("\n*** IRQ ***\n\n");
2381 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
2382 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
2383 regs.WrMem(0x0100 + regs.sp--, regs.cc);
2384 regs.cc |= FLAG_I; // Set I
2385 regs.cc &= ~FLAG_D; // & clear D
2386 regs.pc = RdMemW(0xFFFE); // And do it!
2389 context->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2390 regs.cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2395 //This is a lame way of doing it, but in the end the simplest--however, it destroys any
2396 //record of elasped CPU time. Not sure that it's important to keep track, but there it is.
2397 regs.clock -= cycles;
2399 myMemcpy(context, ®s, sizeof(V65C02REGS));
2403 // Get the clock of the currently executing CPU
2405 uint32 GetCurrentV65C02Clock(void)