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 ;-)
14 //OK, the wraparound bug exists in both the Apple and Atari versions of Ultima II.
15 //However, the Atari version *does* occassionally pick strength while the Apple
16 //versions do not--which would seem to indicate a bug either in the RNG algorithm,
17 //the 65C02 core, or the Apple hardware. Need to investigate all three!
20 //#define __DEBUGMON__
31 #define CLR_Z (regs.cc &= ~FLAG_Z)
32 #define CLR_ZN (regs.cc &= ~(FLAG_Z | FLAG_N))
33 #define CLR_ZNC (regs.cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
34 #define CLR_V (regs.cc &= ~FLAG_V)
35 #define CLR_N (regs.cc &= ~FLAG_N)
36 #define SET_Z(r) (regs.cc = ((r) == 0 ? regs.cc | FLAG_Z : regs.cc & ~FLAG_Z))
37 #define SET_N(r) (regs.cc = ((r) & 0x80 ? regs.cc | FLAG_N : regs.cc & ~FLAG_N))
39 //Not sure that this code is computing the carry correctly... Investigate! [Seems to be]
40 #define SET_C_ADD(a,b) (regs.cc = ((uint8)(b) > (uint8)(~(a)) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
41 //#define SET_C_SUB(a,b) (regs.cc = ((uint8)(b) >= (uint8)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
42 #define SET_C_CMP(a,b) (regs.cc = ((uint8)(b) >= (uint8)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
43 #define SET_ZN(r) SET_N(r); SET_Z(r)
44 #define SET_ZNC_ADD(a,b,r) SET_N(r); SET_Z(r); SET_C_ADD(a,b)
45 //#define SET_ZNC_SUB(a,b,r) SET_N(r); SET_Z(r); SET_C_SUB(a,b)
46 #define SET_ZNC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b)
48 //Small problem with the EA_ macros: ABS macros don't increment the PC!!! !!! FIX !!!
49 #define EA_IMM regs.pc++
50 #define EA_ZP regs.RdMem(regs.pc++)
51 #define EA_ZP_X (regs.RdMem(regs.pc++) + regs.x) & 0xFF
52 #define EA_ZP_Y (regs.RdMem(regs.pc++) + regs.y) & 0xFF
53 #define EA_ABS RdMemW(regs.pc)
54 #define EA_ABS_X RdMemW(regs.pc) + regs.x
55 #define EA_ABS_Y RdMemW(regs.pc) + regs.y
56 #define EA_IND_ZP_X RdMemW((regs.RdMem(regs.pc++) + regs.x) & 0xFF)
57 #define EA_IND_ZP_Y RdMemW(regs.RdMem(regs.pc++)) + regs.y
58 #define EA_IND_ZP RdMemW(regs.RdMem(regs.pc++))
60 #define READ_IMM regs.RdMem(EA_IMM)
61 #define READ_ZP regs.RdMem(EA_ZP)
62 #define READ_ZP_X regs.RdMem(EA_ZP_X)
63 #define READ_ZP_Y regs.RdMem(EA_ZP_Y)
64 #define READ_ABS regs.RdMem(EA_ABS); regs.pc += 2
65 #define READ_ABS_X regs.RdMem(EA_ABS_X); regs.pc += 2
66 #define READ_ABS_Y regs.RdMem(EA_ABS_Y); regs.pc += 2
67 #define READ_IND_ZP_X regs.RdMem(EA_IND_ZP_X)
68 #define READ_IND_ZP_Y regs.RdMem(EA_IND_ZP_Y)
69 #define READ_IND_ZP regs.RdMem(EA_IND_ZP)
71 #define READ_IMM_WB(v) uint16 addr = EA_IMM; v = regs.RdMem(addr)
72 #define READ_ZP_WB(v) uint16 addr = EA_ZP; v = regs.RdMem(addr)
73 #define READ_ZP_X_WB(v) uint16 addr = EA_ZP_X; v = regs.RdMem(addr)
74 #define READ_ABS_WB(v) uint16 addr = EA_ABS; v = regs.RdMem(addr); regs.pc += 2
75 #define READ_ABS_X_WB(v) uint16 addr = EA_ABS_X; v = regs.RdMem(addr); regs.pc += 2
76 #define READ_ABS_Y_WB(v) uint16 addr = EA_ABS_Y; v = regs.RdMem(addr); regs.pc += 2
77 #define READ_IND_ZP_X_WB(v) uint16 addr = EA_IND_ZP_X; v = regs.RdMem(addr)
78 #define READ_IND_ZP_Y_WB(v) uint16 addr = EA_IND_ZP_Y; v = regs.RdMem(addr)
79 #define READ_IND_ZP_WB(v) uint16 addr = EA_IND_ZP; v = regs.RdMem(addr)
81 #define WRITE_BACK(d) regs.WrMem(addr, (d))
83 // Private global variables
85 static V65C02REGS regs;
87 //This is probably incorrect, at least WRT to the $x7 and $xF opcodes... !!! FIX !!!
88 //Also this doesn't take into account the extra cycle it takes when an indirect fetch
89 //(ABS, ABS X/Y, ZP) crosses a page boundary, or extra cycle for BCD add/subtract...
90 #warning Cycle counts are not accurate--!!! FIX !!!
91 static uint8 CPUCycles[256] = {
92 7, 6, 1, 1, 5, 3, 5, 1, 3, 2, 2, 1, 6, 4, 6, 1,
93 2, 5, 5, 1, 5, 4, 6, 1, 2, 4, 2, 1, 6, 4, 6, 1,
94 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 4, 4, 6, 1,
95 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 2, 1, 4, 4, 6, 1,
96 6, 6, 1, 1, 1, 3, 5, 1, 3, 2, 2, 1, 3, 4, 6, 1,
97 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
98 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 6, 4, 6, 1,
99 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 4, 1, 6, 4, 6, 1,
100 2, 6, 1, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
101 2, 6, 5, 1, 4, 4, 4, 1, 2, 5, 2, 1, 4, 5, 5, 1,
102 2, 6, 2, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
103 2, 5, 5, 1, 4, 4, 4, 1, 2, 4, 2, 1, 4, 4, 4, 1,
104 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
105 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
106 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
107 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 4, 1, 1, 4, 6, 1 };
109 // Private function prototypes
111 static uint16 RdMemW(uint16);
114 // Read a uint16 out of 65C02 memory (big endian format)
116 static uint16 RdMemW(uint16 address)
118 return (uint16)(regs.RdMem(address + 1) << 8) | regs.RdMem(address + 0);
123 // 65C02 OPCODE IMPLEMENTATION
125 // NOTE: Lots of macros are used here to save a LOT of typing. Also
126 // helps speed the debugging process. :-) Because of this, combining
127 // certain lines may look like a good idea but would end in disaster.
128 // You have been warned! ;-)
132 Mnemonic Addressing mode Form Opcode Size Timing
134 ADC Immediate ADC #Oper 69 2 2
135 Zero Page ADC Zpg 65 2 3
136 Zero Page,X ADC Zpg,X 75 2 4
137 Absolute ADC Abs 6D 3 4
138 Absolute,X ADC Abs,X 7D 3 4
139 Absolute,Y ADC Abs,Y 79 3 4
140 (Zero Page,X) ADC (Zpg,X) 61 2 6
141 (Zero Page),Y ADC (Zpg),Y 71 2 5
142 (Zero Page) ADC (Zpg) 72 2 5
147 //This is non-optimal, but it works--optimize later. :-)
148 #define OP_ADC_HANDLER(m) \
149 uint16 sum = (uint16)regs.a + (m) + (uint16)(regs.cc & FLAG_C); \
151 if (regs.cc & FLAG_D) \
153 if ((sum & 0x0F) > 0x09) \
156 if ((sum & 0xF0) > 0x90) \
160 regs.cc = (regs.cc & ~FLAG_C) | (sum >> 8); \
161 regs.cc = (~(regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
162 regs.a = sum & 0xFF; \
165 //OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
167 static void Op69(void) // ADC #
173 static void Op65(void) // ADC ZP
179 static void Op75(void) // ADC ZP, X
181 uint16 m = READ_ZP_X;
185 static void Op6D(void) // ADC ABS
191 static void Op7D(void) // ADC ABS, X
193 uint16 m = READ_ABS_X;
197 static void Op79(void) // ADC ABS, Y
199 uint16 m = READ_ABS_Y;
203 static void Op61(void) // ADC (ZP, X)
205 uint16 m = READ_IND_ZP_X;
209 static void Op71(void) // ADC (ZP), Y
211 uint16 m = READ_IND_ZP_Y;
215 static void Op72(void) // ADC (ZP)
217 uint16 m = READ_IND_ZP;
222 AND Immediate AND #Oper 29 2 2
223 Zero Page AND Zpg 25 2 3
224 Zero Page,X AND Zpg,X 35 2 4
225 Absolute AND Abs 2D 3 4
226 Absolute,X AND Abs,X 3D 3 4
227 Absolute,Y AND Abs,Y 39 3 4
228 (Zero Page,X) AND (Zpg,X) 21 2 6
229 (Zero Page),Y AND (Zpg),Y 31 2 5
230 (Zero Page) AND (Zpg) 32 2 5
235 #define OP_AND_HANDLER(m) \
239 static void Op29(void) // AND #
245 static void Op25(void) // AND ZP
251 static void Op35(void) // AND ZP, X
257 static void Op2D(void) // AND ABS
263 static void Op3D(void) // AND ABS, X
265 uint8 m = READ_ABS_X;
269 static void Op39(void) // AND ABS, Y
271 uint8 m = READ_ABS_Y;
275 static void Op21(void) // AND (ZP, X)
277 uint8 m = READ_IND_ZP_X;
281 static void Op31(void) // AND (ZP), Y
283 uint8 m = READ_IND_ZP_Y;
287 static void Op32(void) // AND (ZP)
289 uint8 m = READ_IND_ZP;
294 ASL Accumulator ASL A 0A 1 2
295 Zero Page ASL Zpg 06 2 5
296 Zero Page,X ASL Zpg,X 16 2 6
297 Absolute ASL Abs 0E 3 6
298 Absolute,X ASL Abs,X 1E 3 7
301 /*static void Op78(void) // LSL ABS
303 uint8 tmp; uint16 addr;
305 tmp = regs.RdMem(addr);
306 (tmp&0x80 ? regs.cc |= 0x01 : regs.cc &= 0xFE); // Shift hi bit into Carry
308 regs.WrMem(addr, tmp);
309 (tmp == 0 ? regs.cc |= 0x04 : regs.cc &= 0xFB); // Adjust Zero flag
310 (tmp&0x80 ? regs.cc |= 0x08 : regs.cc &= 0xF7); // Adjust Negative flag
315 #define OP_ASL_HANDLER(m) \
316 regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
320 static void Op0A(void) // ASL A
322 OP_ASL_HANDLER(regs.a);
325 static void Op06(void) // ASL ZP
333 static void Op16(void) // ASL ZP, X
341 static void Op0E(void) // ASL ABS
349 static void Op1E(void) // ASL ABS, X
358 BBR0 Relative BBR0 Oper 0F 2 2
359 BBR1 Relative BBR1 Oper 1F 2 2
360 BBR2 Relative BBR2 Oper 2F 2 2
361 BBR3 Relative BBR3 Oper 3F 2 2
362 BBR4 Relative BBR4 Oper 4F 2 2
363 BBR5 Relative BBR5 Oper 5F 2 2
364 BBR6 Relative BBR6 Oper 6F 2 2
365 BBR7 Relative BBR7 Oper 7F 2 2
366 BBS0 Relative BBS0 Oper 8F 2 2
367 BBS1 Relative BBS1 Oper 9F 2 2
368 BBS2 Relative BBS2 Oper AF 2 2
369 BBS3 Relative BBS3 Oper BF 2 2
370 BBS4 Relative BBS4 Oper CF 2 2
371 BBS5 Relative BBS5 Oper DF 2 2
372 BBS6 Relative BBS6 Oper EF 2 2
373 BBS7 Relative BBS7 Oper FF 2 2
378 static void Op0F(void) // BBR0
380 int16 m = (int16)(int8)READ_IMM;
382 if (!(regs.a & 0x01))
386 static void Op1F(void) // BBR1
388 int16 m = (int16)(int8)READ_IMM;
390 if (!(regs.a & 0x02))
394 static void Op2F(void) // BBR2
396 int16 m = (int16)(int8)READ_IMM;
398 if (!(regs.a & 0x04))
402 static void Op3F(void) // BBR3
404 int16 m = (int16)(int8)READ_IMM;
406 if (!(regs.a & 0x08))
410 static void Op4F(void) // BBR4
412 int16 m = (int16)(int8)READ_IMM;
414 if (!(regs.a & 0x10))
418 static void Op5F(void) // BBR5
420 int16 m = (int16)(int8)READ_IMM;
422 if (!(regs.a & 0x20))
426 static void Op6F(void) // BBR6
428 int16 m = (int16)(int8)READ_IMM;
430 if (!(regs.a & 0x40))
434 static void Op7F(void) // BBR7
436 int16 m = (int16)(int8)READ_IMM;
438 if (!(regs.a & 0x80))
442 static void Op8F(void) // BBS0
444 int16 m = (int16)(int8)READ_IMM;
450 static void Op9F(void) // BBS1
452 int16 m = (int16)(int8)READ_IMM;
458 static void OpAF(void) // BBS2
460 int16 m = (int16)(int8)READ_IMM;
466 static void OpBF(void) // BBS3
468 int16 m = (int16)(int8)READ_IMM;
474 static void OpCF(void) // BBS4
476 int16 m = (int16)(int8)READ_IMM;
482 static void OpDF(void) // BBS5
484 int16 m = (int16)(int8)READ_IMM;
490 static void OpEF(void) // BBS6
492 int16 m = (int16)(int8)READ_IMM;
498 static void OpFF(void) // BBS7
500 int16 m = (int16)(int8)READ_IMM;
507 BCC Relative BCC Oper 90 2 2
508 BCS Relative BCS Oper B0 2 2
509 BEQ Relative BEQ Oper F0 2 2
514 static void Op90(void) // BCC
516 int16 m = (int16)(int8)READ_IMM;
518 if (!(regs.cc & FLAG_C))
522 static void OpB0(void) // BCS
524 int16 m = (int16)(int8)READ_IMM;
526 if (regs.cc & FLAG_C)
530 static void OpF0(void) // BEQ
532 int16 m = (int16)(int8)READ_IMM;
534 if (regs.cc & FLAG_Z)
539 BIT Immediate BIT #Oper 89 2 2
540 Zero Page BIT Zpg 24 2 3
541 Zero Page,X BIT Zpg,X 34 2 4
542 Absolute BIT Abs 2C 3 4
543 Absolute,X BIT Abs,X 3C 3 4
548 /* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag (except in immediate
549 addressing mode where V & N are untouched.) The accumulator and the operand are ANDed and the
550 Z flag is set appropriately. */
552 #define OP_BIT_HANDLER(m) \
553 int8 result = regs.a & (m); \
554 regs.cc &= ~(FLAG_N | FLAG_V); \
555 regs.cc |= ((m) & 0xC0); \
558 static void Op89(void) // BIT #
561 int8 result = regs.a & m;
565 static void Op24(void) // BIT ZP
571 static void Op34(void) // BIT ZP, X
577 static void Op2C(void) // BIT ABS
583 static void Op3C(void) // BIT ABS, X
585 uint8 m = READ_ABS_X;
590 BMI Relative BMI Oper 30 2 2
591 BNE Relative BNE Oper D0 2 2
592 BPL Relative BPL Oper 10 2 2
593 BRA Relative BRA Oper 80 2 3
596 // More branch opcodes
598 static void Op30(void) // BMI
600 int16 m = (int16)(int8)READ_IMM;
602 if (regs.cc & FLAG_N)
606 static void OpD0(void) // BNE
608 int16 m = (int16)(int8)READ_IMM;
610 if (!(regs.cc & FLAG_Z))
614 static void Op10(void) // BPL
616 int16 m = (int16)(int8)READ_IMM;
618 if (!(regs.cc & FLAG_N))
622 static void Op80(void) // BRA
624 int16 m = (int16)(int8)READ_IMM;
629 BRK Implied BRK 00 1 7
632 static void Op00(void) // BRK
634 regs.cc |= FLAG_B; // Set B
635 regs.pc++; // RTI comes back to the instruction one byte after the BRK
636 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
637 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
638 regs.WrMem(0x0100 + regs.sp--, regs.cc);
639 regs.cc |= FLAG_I; // Set I
640 regs.cc &= ~FLAG_D; // & clear D
641 regs.pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
645 BVC Relative BVC Oper 50 2 2
646 BVS Relative BVS Oper 70 2 2
649 // Even more branch opcodes
651 static void Op50(void) // BVC
653 int16 m = (int16)(int8)READ_IMM;
655 if (!(regs.cc & FLAG_V))
659 static void Op70(void) // BVS
661 int16 m = (int16)(int8)READ_IMM;
663 if (regs.cc & FLAG_V)
668 CLC Implied CLC 18 1 2
671 static void Op18(void) // CLC
677 CLD Implied CLD D8 1 2
680 static void OpD8(void) // CLD
686 CLI Implied CLI 58 1 2
689 static void Op58(void) // CLI
695 CLV Implied CLV B8 1 2
698 static void OpB8(void) // CLV
704 CMP Immediate CMP #Oper C9 2 2
705 Zero Page CMP Zpg C5 2 3
706 Zero Page,X CMP Zpg D5 2 4
707 Absolute CMP Abs CD 3 4
708 Absolute,X CMP Abs,X DD 3 4
709 Absolute,Y CMP Abs,Y D9 3 4
710 (Zero Page,X) CMP (Zpg,X) C1 2 6
711 (Zero Page),Y CMP (Zpg),Y D1 2 5
712 (Zero Page) CMP (Zpg) D2 2 5
718 Here's the latest: The CMP is NOT generating the Z flag when A=$C0!
720 FABA: A0 07 LDY #$07 [PC=FABC, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
721 FABC: C6 01 DEC $01 [PC=FABE, SP=01FF, CC=N--B-I--, A=00, X=00, Y=07]
722 FABE: A5 01 LDA $01 [PC=FAC0, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
723 FAC0: C9 C0 CMP #$C0 [PC=FAC2, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
724 FAC2: F0 D7 BEQ $FA9B [PC=FAC4, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
725 FAC4: 8D F8 07 STA $07F8 [PC=FAC7, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
726 FAC7: B1 00 LDA ($00),Y
727 *** Read at I/O address C007
728 [PC=FAC9, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
729 FAC9: D9 01 FB CMP $FB01,Y [PC=FACC, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
730 FACC: D0 EC BNE $FABA [PC=FABA, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
732 Should be fixed now... (was adding instead of subtracting!)
734 Small problem here... First two should set the carry while the last one should clear it. !!! FIX !!! [DONE]
736 FDF0: C9 A0 CMP #$A0 [PC=FDF2, SP=01F1, CC=---B-IZ-, A=A0, X=02, Y=03]
737 FD7E: C9 E0 CMP #$E0 [PC=FD80, SP=01F4, CC=N--B-I--, A=A0, X=02, Y=03]
738 FD38: C9 9B CMP #$9B [PC=FD3A, SP=01F2, CC=---B-I-C, A=A0, X=02, Y=03]
740 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.
743 #define OP_CMP_HANDLER(m) \
744 uint8 result = regs.a - (m); \
745 SET_ZNC_CMP(m, regs.a, result)
747 static void OpC9(void) // CMP #
753 static void OpC5(void) // CMP ZP
759 static void OpD5(void) // CMP ZP, X
765 static void OpCD(void) // CMP ABS
771 static void OpDD(void) // CMP ABS, X
773 uint8 m = READ_ABS_X;
777 static void OpD9(void) // CMP ABS, Y
779 uint8 m = READ_ABS_Y;
783 static void OpC1(void) // CMP (ZP, X)
785 uint8 m = READ_IND_ZP_X;
789 static void OpD1(void) // CMP (ZP), Y
791 uint8 m = READ_IND_ZP_Y;
795 static void OpD2(void) // CMP (ZP)
797 uint8 m = READ_IND_ZP;
802 CPX Immediate CPX #Oper E0 2 2
803 Zero Page CPX Zpg E4 2 3
804 Absolute CPX Abs EC 3 4
809 #define OP_CPX_HANDLER(m) \
810 uint8 result = regs.x - (m); \
811 SET_ZNC_CMP(m, regs.x, result)
813 static void OpE0(void) // CPX #
819 static void OpE4(void) // CPX ZP
825 static void OpEC(void) // CPX ABS
832 CPY Immediate CPY #Oper C0 2 2
833 Zero Page CPY Zpg C4 2 3
834 Absolute CPY Abs CC 3 4
839 #define OP_CPY_HANDLER(m) \
840 uint8 result = regs.y - (m); \
841 SET_ZNC_CMP(m, regs.y, result)
843 static void OpC0(void) // CPY #
849 static void OpC4(void) // CPY ZP
855 static void OpCC(void) // CPY ABS
862 DEA Accumulator DEA 3A 1 2
865 static void Op3A(void) // DEA
872 DEC Zero Page DEC Zpg C6 2 5
873 Zero Page,X DEC Zpg,X D6 2 6
874 Absolute DEC Abs CE 3 6
875 Absolute,X DEC Abs,X DE 3 7
880 #define OP_DEC_HANDLER(m) \
884 static void OpC6(void) // DEC ZP
892 static void OpD6(void) // DEC ZP, X
900 static void OpCE(void) // DEC ABS
908 static void OpDE(void) // DEC ABS, X
917 Here's one problem: DEX is setting the N flag!
919 D3EE: A2 09 LDX #$09 [PC=D3F0, SP=01F7, CC=---B-I-C, A=01, X=09, Y=08]
920 D3F0: 98 TYA [PC=D3F1, SP=01F7, CC=N--B-I-C, A=08, X=09, Y=08]
921 D3F1: 48 PHA [PC=D3F2, SP=01F6, CC=N--B-I-C, A=08, X=09, Y=08]
922 D3F2: B5 93 LDA $93,X [PC=D3F4, SP=01F6, CC=---B-IZC, A=00, X=09, Y=08]
923 D3F4: CA DEX [PC=D3F5, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
924 D3F5: 10 FA BPL $D3F1 [PC=D3F7, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
925 D3F7: 20 84 E4 JSR $E484 [PC=E484, SP=01F4, CC=N--B-I-C, A=00, X=08, Y=08]
927 should be fixed now...
931 DEX Implied DEX CA 1 2
934 static void OpCA(void) // DEX
941 DEY Implied DEY 88 1 2
944 static void Op88(void) // DEY
951 EOR Immediate EOR #Oper 49 2 2
952 Zero Page EOR Zpg 45 2 3
953 Zero Page,X EOR Zpg,X 55 2 4
954 Absolute EOR Abs 4D 3 4
955 Absolute,X EOR Abs,X 5D 3 4
956 Absolute,Y EOR Abs,Y 59 3 4
957 (Zero Page,X) EOR (Zpg,X) 41 2 6
958 (Zero Page),Y EOR (Zpg),Y 51 2 5
959 (Zero Page) EOR (Zpg) 52 2 5
964 #define OP_EOR_HANDLER(m) \
968 static void Op49(void) // EOR #
974 static void Op45(void) // EOR ZP
980 static void Op55(void) // EOR ZP, X
986 static void Op4D(void) // EOR ABS
992 static void Op5D(void) // EOR ABS, X
994 uint8 m = READ_ABS_X;
998 static void Op59(void) // EOR ABS, Y
1000 uint8 m = READ_ABS_Y;
1004 static void Op41(void) // EOR (ZP, X)
1006 uint8 m = READ_IND_ZP_X;
1010 static void Op51(void) // EOR (ZP), Y
1012 uint8 m = READ_IND_ZP_Y;
1016 static void Op52(void) // EOR (ZP)
1018 uint8 m = READ_IND_ZP;
1023 INA Accumulator INA 1A 1 2
1026 static void Op1A(void) // INA
1033 INC Zero Page INC Zpg E6 2 5
1034 Zero Page,X INC Zpg,X F6 2 6
1035 Absolute INC Abs EE 3 6
1036 Absolute,X INC Abs,X FE 3 7
1041 #define OP_INC_HANDLER(m) \
1045 static void OpE6(void) // INC ZP
1053 static void OpF6(void) // INC ZP, X
1061 static void OpEE(void) // INC ABS
1069 static void OpFE(void) // INC ABS, X
1078 INX Implied INX E8 1 2
1081 static void OpE8(void) // INX
1088 INY Implied INY C8 1 2
1091 static void OpC8(void) // INY
1098 JMP Absolute JMP Abs 4C 3 3
1099 (Absolute) JMP (Abs) 6C 3 5
1100 (Absolute,X) JMP (Abs,X) 7C 3 6
1105 static void Op4C(void) // JMP ABS
1107 regs.pc = RdMemW(regs.pc);
1110 static void Op6C(void) // JMP (ABS)
1112 // uint16 addr = RdMemW(regs.pc);
1114 //WriteLog("\n[JMP ABS]: addr fetched = %04X, bytes at %04X = %02X %02X (RdMemw=%04X)\n",
1115 // addr, addr, regs.RdMem(addr), regs.RdMem(addr+1), RdMemW(addr));
1117 // addr = RdMemW(addr);
1118 regs.pc = RdMemW(RdMemW(regs.pc));
1121 static void Op7C(void) // JMP (ABS, X)
1123 regs.pc = RdMemW(RdMemW(regs.pc) + regs.x);
1127 JSR Absolute JSR Abs 20 3 6
1130 //This is not jumping to the correct address... !!! FIX !!! [DONE]
1131 static void Op20(void) // JSR
1133 uint16 addr = RdMemW(regs.pc);
1134 regs.pc++; // Since it pushes return address - 1...
1135 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8);
1136 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
1141 LDA Immediate LDA #Oper A9 2 2
1142 Zero Page LDA Zpg A5 2 3
1143 Zero Page,X LDA Zpg,X B5 2 4
1144 Absolute LDA Abs AD 3 4
1145 Absolute,X LDA Abs,X BD 3 4
1146 Absolute,Y LDA Abs,Y B9 3 4
1147 (Zero Page,X) LDA (Zpg,X) A1 2 6
1148 (Zero Page),Y LDA (Zpg),Y B1 2 5
1149 (Zero Page) LDA (Zpg) B2 2 5
1154 #define OP_LDA_HANDLER(m) \
1158 static void OpA9(void) // LDA #
1164 static void OpA5(void) // LDA ZP
1170 static void OpB5(void) // LDA ZP, X
1172 uint8 m = READ_ZP_X;
1176 static void OpAD(void) // LDA ABS
1182 static void OpBD(void) // LDA ABS, X
1184 uint8 m = READ_ABS_X;
1188 static void OpB9(void) // LDA ABS, Y
1190 uint8 m = READ_ABS_Y;
1194 static void OpA1(void) // LDA (ZP, X)
1196 uint8 m = READ_IND_ZP_X;
1200 static void OpB1(void) // LDA (ZP), Y
1202 uint8 m = READ_IND_ZP_Y;
1206 static void OpB2(void) // LDA (ZP)
1208 uint8 m = READ_IND_ZP;
1213 LDX Immediate LDX #Oper A2 2 2
1214 Zero Page LDX Zpg A6 2 3
1215 Zero Page,Y LDX Zpg,Y B6 2 4
1216 Absolute LDX Abs AE 3 4
1217 Absolute,Y LDX Abs,Y BE 3 4
1222 #define OP_LDX_HANDLER(m) \
1226 static void OpA2(void) // LDX #
1232 static void OpA6(void) // LDX ZP
1238 static void OpB6(void) // LDX ZP, Y
1240 uint8 m = READ_ZP_Y;
1244 static void OpAE(void) // LDX ABS
1250 static void OpBE(void) // LDX ABS, Y
1252 uint8 m = READ_ABS_Y;
1257 LDY Immediate LDY #Oper A0 2 2
1258 Zero Page LDY Zpg A4 2 3
1259 Zero Page,Y LDY Zpg,X B4 2 4
1260 Absolute LDY Abs AC 3 4
1261 Absolute,Y LDY Abs,X BC 3 4
1266 #define OP_LDY_HANDLER(m) \
1270 static void OpA0(void) // LDY #
1276 static void OpA4(void) // LDY ZP
1282 static void OpB4(void) // LDY ZP, X
1284 uint8 m = READ_ZP_X;
1288 static void OpAC(void) // LDY ABS
1294 static void OpBC(void) // LDY ABS, X
1296 uint8 m = READ_ABS_X;
1301 LSR Accumulator LSR A 4A 1 2
1302 Zero Page LSR Zpg 46 2 5
1303 Zero Page,X LSR Zpg,X 56 2 6
1304 Absolute LSR Abs 4E 3 6
1305 Absolute,X LSR Abs,X 5E 3 7
1310 #define OP_LSR_HANDLER(m) \
1311 regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1315 static void Op4A(void) // LSR A
1317 OP_LSR_HANDLER(regs.a);
1320 static void Op46(void) // LSR ZP
1328 static void Op56(void) // LSR ZP, X
1336 static void Op4E(void) // LSR ABS
1344 static void Op5E(void) // LSR ABS, X
1353 NOP Implied NOP EA 1 2
1356 static void OpEA(void) // NOP
1361 ORA Immediate ORA #Oper 09 2 2
1362 Zero Page ORA Zpg 05 2 3
1363 Zero Page,X ORA Zpg,X 15 2 4
1364 Absolute ORA Abs 0D 3 4
1365 Absolute,X ORA Abs,X 1D 3 4
1366 Absolute,Y ORA Abs,Y 19 3 4
1367 (Zero Page,X) ORA (Zpg,X) 01 2 6
1368 (Zero Page),Y ORA (Zpg),Y 11 2 5
1369 (Zero Page) ORA (Zpg) 12 2 5
1374 #define OP_ORA_HANDLER(m) \
1378 static void Op09(void) // ORA #
1384 static void Op05(void) // ORA ZP
1390 static void Op15(void) // ORA ZP, X
1392 uint8 m = READ_ZP_X;
1396 static void Op0D(void) // ORA ABS
1402 static void Op1D(void) // ORA ABS, X
1404 uint8 m = READ_ABS_X;
1408 static void Op19(void) // ORA ABS, Y
1410 uint8 m = READ_ABS_Y;
1414 static void Op01(void) // ORA (ZP, X)
1416 uint8 m = READ_IND_ZP_X;
1420 static void Op11(void) // ORA (ZP), Y
1422 uint8 m = READ_IND_ZP_Y;
1426 static void Op12(void) // ORA (ZP)
1428 uint8 m = READ_IND_ZP;
1433 PHA Implied PHA 48 1 3
1436 static void Op48(void) // PHA
1438 regs.WrMem(0x0100 + regs.sp--, regs.a);
1441 static void Op08(void) // PHP
1443 regs.cc |= FLAG_UNK; // Make sure that the unused bit is always set
1444 regs.WrMem(0x0100 + regs.sp--, regs.cc);
1448 PHX Implied PHX DA 1 3
1451 static void OpDA(void) // PHX
1453 regs.WrMem(0x0100 + regs.sp--, regs.x);
1457 PHY Implied PHY 5A 1 3
1460 static void Op5A(void) // PHY
1462 regs.WrMem(0x0100 + regs.sp--, regs.y);
1466 PLA Implied PLA 68 1 4
1469 static void Op68(void) // PLA
1471 regs.a = regs.RdMem(0x0100 + ++regs.sp);
1475 static void Op28(void) // PLP
1477 regs.cc = regs.RdMem(0x0100 + ++regs.sp);
1481 PLX Implied PLX FA 1 4
1484 static void OpFA(void) // PLX
1486 regs.x = regs.RdMem(0x0100 + ++regs.sp);
1491 PLY Implied PLY 7A 1 4
1494 static void Op7A(void) // PLY
1496 regs.y = regs.RdMem(0x0100 + ++regs.sp);
1501 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.
1502 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1503 zp 07 17 27 37 47 57 67 77
1504 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1505 zp 87 97 A7 B7 C7 D7 E7 F7
1510 static void Op07(void) // RMB0 ZP
1518 static void Op17(void) // RMB1 ZP
1526 static void Op27(void) // RMB2 ZP
1534 static void Op37(void) // RMB3 ZP
1542 static void Op47(void) // RMB4 ZP
1550 static void Op57(void) // RMB5 ZP
1558 static void Op67(void) // RMB6 ZP
1566 static void Op77(void) // RMB7 ZP
1575 ROL Accumulator ROL A 2A 1 2
1576 Zero Page ROL Zpg 26 2 5
1577 Zero Page,X ROL Zpg,X 36 2 6
1578 Absolute ROL Abs 2E 3 6
1579 Absolute,X ROL Abs,X 3E 3 7
1584 #define OP_ROL_HANDLER(m) \
1585 uint8 tmp = regs.cc & 0x01; \
1586 regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1587 (m) = ((m) << 1) | tmp; \
1590 static void Op2A(void) // ROL A
1592 OP_ROL_HANDLER(regs.a);
1595 static void Op26(void) // ROL ZP
1603 static void Op36(void) // ROL ZP, X
1611 static void Op2E(void) // ROL ABS
1619 static void Op3E(void) // ROL ABS, X
1628 ROR Accumulator ROR A 6A 1 2
1629 Zero Page ROR Zpg 66 2 5
1630 Zero Page,X ROR Zpg,X 76 2 6
1631 Absolute ROR Abs 6E 3 6
1632 Absolute,X ROR Abs,X 7E 3 7
1637 #define OP_ROR_HANDLER(m) \
1638 uint8 tmp = (regs.cc & 0x01) << 7; \
1639 regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
1640 (m) = ((m) >> 1) | tmp; \
1643 static void Op6A(void) // ROR A
1645 OP_ROR_HANDLER(regs.a);
1648 static void Op66(void) // ROR ZP
1656 static void Op76(void) // ROR ZP, X
1664 static void Op6E(void) // ROR ABS
1672 static void Op7E(void) // ROR ABS, X
1681 RTI Implied RTI 40 1 6
1684 static void Op40(void) // RTI
1686 regs.cc = regs.RdMem(0x0100 + ++regs.sp);
1687 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1688 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1692 RTS Implied RTS 60 1 6
1695 static void Op60(void) // RTS
1697 regs.pc = regs.RdMem(0x0100 + ++regs.sp);
1698 regs.pc |= (uint16)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
1699 regs.pc++; // Since it pushes return address - 1...
1700 //printf("*** RTS: PC = $%04X, SP= $1%02X\n", regs.pc, regs.sp);
1705 SBC Immediate SBC #Oper E9 2 2
1706 Zero Page SBC Zpg E5 2 3
1707 Zero Page,X SBC Zpg,X F5 2 4
1708 Absolute SBC Abs ED 3 4
1709 Absolute,X SBC Abs,X FD 3 4
1710 Absolute,Y SBC Abs,Y F9 3 4
1711 (Zero Page,X) SBC (Zpg,X) E1 2 6
1712 (Zero Page),Y SBC (Zpg),Y F1 2 5
1713 (Zero Page) SBC (Zpg) F2 2 5
1718 //This is non-optimal, but it works--optimize later. :-)
1719 //This is correct except for the BCD handling... !!! FIX !!! [Possibly DONE]
1720 #define OP_SBC_HANDLER(m) \
1721 uint16 sum = (uint16)regs.a - (m) - (uint16)((regs.cc & FLAG_C) ^ 0x01); \
1723 if (regs.cc & FLAG_D) \
1725 if ((sum & 0x0F) > 0x09) \
1728 if ((sum & 0xF0) > 0x90) \
1732 regs.cc = (regs.cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
1733 regs.cc = ((regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
1734 regs.a = sum & 0xFF; \
1738 D5AF: 38 SEC [PC=D5B0, SP=01F6, CC=---B-I-C, A=4C, X=00, Y=06]
1740 *** HERE'S where it sets the D flag on a subtract... Arg!
1742 D5B0: F1 9D SBC ($9D),Y [PC=D5B2, SP=01F6, CC=N--BDI--, A=FE, X=00, Y=06]
1747 //OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
1749 static void OpE9(void) // SBC #
1751 uint16 m = READ_IMM;
1755 static void OpE5(void) // SBC ZP
1761 static void OpF5(void) // SBC ZP, X
1763 uint16 m = READ_ZP_X;
1767 static void OpED(void) // SBC ABS
1769 uint16 m = READ_ABS;
1773 static void OpFD(void) // SBC ABS, X
1775 uint16 m = READ_ABS_X;
1779 static void OpF9(void) // SBC ABS, Y
1781 uint16 m = READ_ABS_Y;
1785 static void OpE1(void) // SBC (ZP, X)
1787 uint16 m = READ_IND_ZP_X;
1791 static void OpF1(void) // SBC (ZP), Y
1793 uint16 m = READ_IND_ZP_Y;
1797 static void OpF2(void) // SBC (ZP)
1799 uint16 m = READ_IND_ZP;
1804 SEC Implied SEC 38 1 2
1807 static void Op38(void) // SEC
1813 SED Implied SED F8 1 2
1816 static void OpF8(void) // SED
1822 SEI Implied SEI 78 1 2
1825 static void Op78(void) // SEI
1831 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.
1832 RMB0 RMB1 RMB2 RMB3 RMB4 RMB5 RMB6 RMB7
1833 zp 07 17 27 37 47 57 67 77
1834 SMB0 SMB1 SMB2 SMB3 SMB4 SMB5 SMB6 SMB7
1835 zp 87 97 A7 B7 C7 D7 E7 F7
1840 static void Op87(void) // SMB0 ZP
1848 static void Op97(void) // SMB1 ZP
1856 static void OpA7(void) // SMB2 ZP
1864 static void OpB7(void) // SMB3 ZP
1872 static void OpC7(void) // SMB4 ZP
1880 static void OpD7(void) // SMB5 ZP
1888 static void OpE7(void) // SMB6 ZP
1896 static void OpF7(void) // SMB7 ZP
1905 STA Zero Page STA Zpg 85 2 3
1906 Zero Page,X STA Zpg,X 95 2 4
1907 Absolute STA Abs 8D 3 4
1908 Absolute,X STA Abs,X 9D 3 5
1909 Absolute,Y STA Abs,Y 99 3 5
1910 (Zero Page,X) STA (Zpg,X) 81 2 6
1911 (Zero Page),Y STA (Zpg),Y 91 2 6
1912 (Zero Page) STA (Zpg) 92 2 5
1917 static void Op85(void)
1919 regs.WrMem(EA_ZP, regs.a);
1922 static void Op95(void)
1924 regs.WrMem(EA_ZP_X, regs.a);
1927 static void Op8D(void)
1929 regs.WrMem(EA_ABS, regs.a);
1933 static void Op9D(void)
1935 regs.WrMem(EA_ABS_X, regs.a);
1939 static void Op99(void)
1941 regs.WrMem(EA_ABS_Y, regs.a);
1945 static void Op81(void)
1947 regs.WrMem(EA_IND_ZP_X, regs.a);
1950 static void Op91(void)
1952 regs.WrMem(EA_IND_ZP_Y, regs.a);
1955 static void Op92(void)
1957 regs.WrMem(EA_IND_ZP, regs.a);
1961 STX Zero Page STX Zpg 86 2 3
1962 Zero Page,Y STX Zpg,Y 96 2 4
1963 Absolute STX Abs 8E 3 4
1968 static void Op86(void)
1970 regs.WrMem(EA_ZP, regs.x);
1973 static void Op96(void)
1975 regs.WrMem(EA_ZP_X, regs.x);
1978 static void Op8E(void)
1980 regs.WrMem(EA_ABS, regs.x);
1985 STY Zero Page STY Zpg 84 2 3
1986 Zero Page,X STY Zpg,X 94 2 4
1987 Absolute STY Abs 8C 3 4
1992 static void Op84(void)
1994 regs.WrMem(EA_ZP, regs.y);
1997 static void Op94(void)
1999 regs.WrMem(EA_ZP_X, regs.y);
2002 static void Op8C(void)
2004 regs.WrMem(EA_ABS, regs.y);
2009 STZ Zero Page STZ Zpg 64 2 3
2010 Zero Page,X STZ Zpg,X 74 2 4
2011 Absolute STZ Abs 9C 3 4
2012 Absolute,X STZ Abs,X 9E 3 5
2017 static void Op64(void)
2019 regs.WrMem(EA_ZP, 0x00);
2022 static void Op74(void)
2024 regs.WrMem(EA_ZP_X, 0x00);
2027 static void Op9C(void)
2029 regs.WrMem(EA_ABS, 0x00);
2033 static void Op9E(void)
2035 regs.WrMem(EA_ABS_X, 0x00);
2040 TAX Implied TAX AA 1 2
2043 static void OpAA(void) // TAX
2050 TAY Implied TAY A8 1 2
2053 static void OpA8(void) // TAY
2060 TRB Zero Page TRB Zpg 14 2 5
2061 Absolute TRB Abs 1C 3 6
2066 #define OP_TRB_HANDLER(m) \
2067 SET_Z(m & regs.a); \
2070 static void Op14(void) // TRB ZP
2078 static void Op1C(void) // TRB ABS
2087 TSB Zero Page TSB Zpg 04 2 5
2088 Absolute TSB Abs 0C 3 6
2093 #define OP_TSB_HANDLER(m) \
2094 SET_Z(m & regs.a); \
2097 static void Op04(void) // TSB ZP
2105 static void Op0C(void) // TSB ABS
2114 TSX Implied TSX BA 1 2
2117 static void OpBA(void) // TSX
2124 TXA Implied TXA 8A 1 2
2127 static void Op8A(void) // TXA
2134 TXS Implied TXS 9A 1 2
2137 static void Op9A(void) // TXS
2143 TYA Implied TYA 98 1 2
2145 static void Op98(void) // TYA
2151 static void Op__(void)
2153 regs.cpuFlags |= V65C02_STATE_ILLEGAL_INST;
2158 // Ok, the exec_op[] array is globally defined here basically to save
2159 // a LOT of unnecessary typing. Sure it's ugly, but hey, it works!
2161 void (* exec_op[256])() = {
2162 Op00, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op__, Op0C, Op0D, Op0E, Op0F,
2163 Op10, Op11, Op12, Op__, Op14, Op15, Op16, Op17, Op18, Op19, Op1A, Op__, Op1C, Op1D, Op1E, Op1F,
2164 Op20, Op21, Op__, Op__, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op__, Op2C, Op2D, Op2E, Op2F,
2165 Op30, Op31, Op32, Op__, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op__, Op3C, Op3D, Op3E, Op3F,
2166 Op40, Op41, Op__, Op__, Op__, Op45, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op4E, Op4F,
2167 Op50, Op51, Op52, Op__, Op__, Op55, Op56, Op57, Op58, Op59, Op5A, Op__, Op__, Op5D, Op5E, Op5F,
2168 Op60, Op61, Op__, Op__, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op__, Op6C, Op6D, Op6E, Op6F,
2169 Op70, Op71, Op72, Op__, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op__, Op7C, Op7D, Op7E, Op7F,
2170 Op80, Op81, Op__, Op__, Op84, Op85, Op86, Op87, Op88, Op89, Op8A, Op__, Op8C, Op8D, Op8E, Op8F,
2171 Op90, Op91, Op92, Op__, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op__, Op9C, Op9D, Op9E, Op9F,
2172 OpA0, OpA1, OpA2, Op__, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, Op__, OpAC, OpAD, OpAE, OpAF,
2173 OpB0, OpB1, OpB2, Op__, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, Op__, OpBC, OpBD, OpBE, OpBF,
2174 OpC0, OpC1, Op__, Op__, OpC4, OpC5, OpC6, OpC7, OpC8, OpC9, OpCA, Op__, OpCC, OpCD, OpCE, OpCF,
2175 OpD0, OpD1, OpD2, Op__, Op__, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, Op__, Op__, OpDD, OpDE, OpDF,
2176 OpE0, OpE1, Op__, Op__, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, Op__, OpEC, OpED, OpEE, OpEF,
2177 OpF0, OpF1, OpF2, Op__, Op__, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, Op__, Op__, OpFD, OpFE, OpFF
2181 // Internal "memcpy" (so we don't have to link with any external libraries!)
2183 static void myMemcpy(void * dst, void * src, uint32 size)
2185 uint8 * d = (uint8 *)dst, * s = (uint8 *)src;
2187 for(uint32 i=0; i<size; i++)
2192 FCA8: 38 698 WAIT SEC
2193 FCA9: 48 699 WAIT2 PHA
2194 FCAA: E9 01 700 WAIT3 SBC #$01
2195 FCAC: D0 FC 701 BNE WAIT3 ;1.0204 USEC
2196 FCAE: 68 702 PLA ;(13+27/2*A+5/2*A*A)
2197 FCAF: E9 01 703 SBC #$01
2198 FCB1: D0 F6 704 BNE WAIT2
2201 FBD9: C9 87 592 BELL1 CMP #$87 ;BELL CHAR? (CNTRL-G)
2202 FBDB: D0 12 593 BNE RTS2B ; NO, RETURN
2203 FBDD: A9 40 594 LDA #$40 ;DELAY .01 SECONDS
2204 FBDF: 20 A8 FC 595 JSR WAIT
2205 FBE2: A0 C0 596 LDY #$C0
2206 FBE4: A9 0C 597 BELL2 LDA #$0C ;TOGGLE SPEAKER AT
2207 FBE6: 20 A8 FC 598 JSR WAIT ; 1 KHZ FOR .1 SEC.
2208 FBE9: AD 30 C0 599 LDA SPKR
2210 FBED: D0 F5 601 BNE BELL2
2211 FBEF: 60 602 RTS2B RTS
2213 //int instCount[256];
2215 bool dumpDis = false;
2218 //Note: could enforce regs.clock to zero on starting the CPU with an Init() function...
2220 //static uint32 limit = 0;
2223 // Function to execute 65C02 for "cycles" cycles
2225 void Execute65C02(V65C02REGS * context, uint32 cycles)
2227 myMemcpy(®s, context, sizeof(V65C02REGS));
2230 // NOTE: There *must* be some way of doing this without requiring the caller to subtract out
2231 // the previous run's cycles. !!! FIX !!!
2233 // while (regs.clock < regs.clock + cycles) <-- won't work
2235 // This isn't as accurate as subtracting out cycles from regs.clock...
2236 // Unless limit is a static variable, adding cycles to it each time through...
2237 uint32 limit = regs.clock + cycles;
2238 while (regs.clock < limit)
2240 // but have wraparound to deal with. :-/
2244 if (regs.clock + cycles > 0xFFFFFFFF)
2248 while (regs.clock < cycles)
2251 /*if (regs.pc == 0x4007)
2255 if (regs.pc == 0x444B)
2257 WriteLog("\n*** End of wait...\n\n");
2260 if (regs.pc == 0x444E)
2262 WriteLog("\n*** Start of wait...\n\n");
2268 //WAIT is commented out here because it's called by BELL1...
2269 if (regs.pc == 0xFCA8)
2271 WriteLog("\n*** WAIT subroutine...\n\n");
2274 if (regs.pc == 0xFBD9)
2276 WriteLog("\n*** BELL1 subroutine...\n\n");
2279 if (regs.pc == 0xFC58)
2281 WriteLog("\n*** HOME subroutine...\n\n");
2284 if (regs.pc == 0xFDED)
2286 WriteLog("\n*** COUT subroutine...\n\n");
2293 Decode65C02(regs.pc);
2295 uint8 opcode = regs.RdMem(regs.pc++);
2297 //if (!(regs.cpuFlags & V65C02_STATE_ILLEGAL_INST))
2298 //instCount[opcode]++;
2300 exec_op[opcode](); // Execute that opcode...
2301 regs.clock += CPUCycles[opcode];
2304 WriteLog(" [PC=%04X, SP=%04X, CC=%s%s-%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
2305 regs.pc, 0x0100 + regs.sp,
2306 (regs.cc & FLAG_N ? "N" : "-"), (regs.cc & FLAG_V ? "V" : "-"),
2307 (regs.cc & FLAG_B ? "B" : "-"), (regs.cc & FLAG_D ? "D" : "-"),
2308 (regs.cc & FLAG_I ? "I" : "-"), (regs.cc & FLAG_Z ? "Z" : "-"),
2309 (regs.cc & FLAG_C ? "C" : "-"), regs.a, regs.x, regs.y);
2313 if (regs.pc == 0xFCB3) // WAIT exit point
2317 /*if (regs.pc == 0xFBEF) // BELL1 exit point
2321 /*if (regs.pc == 0xFC22) // HOME exit point
2325 if (regs.pc == 0xFDFF) // COUT exit point
2329 if (regs.pc == 0xFBD8)
2331 WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
2335 //These should be correct now...
2336 if (regs.cpuFlags & V65C02_ASSERT_LINE_RESET)
2339 WriteLog("\n*** RESET ***\n\n");
2341 // Not sure about this...
2343 regs.cc = FLAG_B | FLAG_I; // Reset the CC register
2344 regs.pc = RdMemW(0xFFFC); // And load PC with the RESET vector
2346 context->cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2347 regs.cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
2349 else if (regs.cpuFlags & V65C02_ASSERT_LINE_NMI)
2352 WriteLog("\n*** NMI ***\n\n");
2354 regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
2355 regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
2356 regs.WrMem(0x0100 + regs.sp--, regs.cc);
2357 regs.cc |= FLAG_I; // Set I
2358 regs.cc &= ~FLAG_D; // & clear D
2359 regs.pc = RdMemW(0xFFFA); // And do it!
2362 context->cpuFlags &= ~V65C02_ASSERT_LINE_NMI;// Reset the asserted line (NMI)...
2363 regs.cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset the asserted line (NMI)...
2365 else if (regs.cpuFlags & V65C02_ASSERT_LINE_IRQ)
2367 if (!(regs.cc & FLAG_I)) // Process an interrupt (I=0)?
2370 WriteLog("\n*** IRQ ***\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(0xFFFE); // And do it!
2380 context->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2381 regs.cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
2386 //This is a lame way of doing it, but in the end the simplest--however, it destroys any
2387 //record of elasped CPU time. Not sure that it's important to keep track, but there it is.
2388 regs.clock -= cycles;
2390 myMemcpy(context, ®s, sizeof(V65C02REGS));
2394 // Get the clock of the currently executing CPU
2396 uint32 GetCurrentV65C02Clock(void)