2 // Virtual 63701 Emulator v1.0
5 // (C) 2014 Underground Software
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -----------------------------------------------------------
11 // JLH 06/15/2006 Added changelog ;-)
12 // JLH 06/15/2006 Scrubbed all BYTE, WORD & DWORD references from the code
13 // JLH 11/13/2006 Converted core to V65C02 macro style :-)
14 // JLH 11/13/2006 Converted flags to unpacked and separate flags
15 // JLH 07/21/2009 Converted clock from 32-bit to 64-bit value, added possible
16 // "don't branch" optimization
17 // JLH 09/21/2009 Fixed EA_ABS macros
18 // JLH 04/07/2014 Converted core to HD63701 type
19 // JLH 04/16/2014 Fixed a raft of bugs, added sane timer handling
22 // This is based on V6808, since it's in the same family apparently. The
23 // HD63701 has more opcodes, and apparently some built in timer facility as
26 // N.B.: There are some things missing from the emulation, such as the data
27 // ports, the Input Capture, RAM Control, sleep mode, processor modes,
28 // and probably some other stuff as well. What is here seems to work, and
29 // work well. It's a fair sight better than the one that's in MAME,
30 // that's for sure. :-D
32 // Some random thoughts: Could there be a performance gain by breaking
33 // out the flags in regs.cc into separate uint8_t variables (or bools)?
34 // You'd have to convert on entering and exiting the emulation loop, but I
35 // think the perfomance hit would be negligible compared to the gain in not
36 // having to mask and shift flags all the time. Investigate after the
37 // conversion to macro style opcodes is completed. :-)
38 // [DONE--remain to be seen if there is any performance increase]
41 #define TEST_DONT_BRANCH_OPTIMIZATION
44 //#include <stdio.h> // for printf()
49 #include <stdio.h> // for printf()
50 #define WriteLog printf
55 #define CLR_Z (flagZ = 0)
56 #define CLR_ZN (flagZ = flagN = 0)
57 #define CLR_ZNC (flagZ = flagN = flagC = 0)
58 #define CLR_NVC (flagN = flagV = flagC = 0)
59 #define CLR_VC (flagV = flagC = 0)
60 #define CLR_V (flagV = 0)
61 #define CLR_N (flagN = 0)
62 #define SET_Z(r) (flagZ = ((r) == 0 ? 1 : 0))
63 #define SET_N(r) (flagN = ((r) & 0x80) >> 7)
64 #define SET_V(a,b,r) (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x80) >> 7)
66 #define SET_C_CMP(a,b) (flagC = ((uint8_t)(b) < (uint8_t)(a) ? 1 : 0))
67 #define SET_ZN(r) SET_N(r); SET_Z(r)
68 #define SET_ZNC_ADD(a,b,r) SET_N(r); SET_Z(r); SET_C_ADD(a,b)
69 #define SET_ZNVC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b); SET_V(a,b,r)
71 #define SET_N16(r) (flagN = ((r) & 0x8000) >> 15)
72 #define SET_V16(a,b,r) (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x8000) >> 15)
73 #define SET_C_CMP16(a,b) (flagC = ((uint16_t)(b) < (uint16_t)(a) ? 1 : 0))
74 #define SET_ZNVC_CMP16(a,b,r) SET_N16(r); SET_Z(r); SET_C_CMP16(a,b); SET_V16(a,b,r)
76 #define EA_IMM regs.pc++
77 #define EA_ZP regs.RdMem(regs.pc++)
78 #define EA_ZP_X (regs.RdMem(regs.pc++) + regs.x)
79 #define EA_ABS FetchMemW(regs.pc)
81 #define READ_IMM regs.RdMem(EA_IMM)
82 #define READ_ZP regs.RdMem(EA_ZP)
83 #define READ_ZP_X regs.RdMem(EA_ZP_X)
84 #define READ_ABS regs.RdMem(EA_ABS)
86 #define READ_IMM16 FetchMemW(regs.pc);
87 #define READ_ZP16 RdMemW(EA_ZP)
88 #define READ_ZP_X16 RdMemW(EA_ZP_X)
89 #define READ_ABS16 RdMemW(EA_ABS)
91 #define READ_IMM_WB(v) uint16_t addr = EA_IMM; v = regs.RdMem(addr)
92 #define READ_ZP_WB(v) uint16_t addr = EA_ZP; v = regs.RdMem(addr)
93 #define READ_ZP_X_WB(v) uint16_t addr = EA_ZP_X; v = regs.RdMem(addr)
94 #define READ_ABS_WB(v) uint16_t addr = EA_ABS; v = regs.RdMem(addr)
96 #define WRITE_BACK(d) regs.WrMem(addr, (d))
98 // This is correct; PUSH writes the location *then* decrements the stack
99 // pointer, and PULL does the opposite. Verified from the data sheet.
100 #define PULL regs.RdMem(++regs.s)
101 #define PUSH(r) regs.WrMem(regs.s--, (r))
102 #define PULL16 RdMemW(++regs.s); ++regs.s;
103 #define PUSH16(r) regs.WrMem(regs.s--, (r) & 0xFF); regs.WrMem(regs.s--, (r) >> 8)
105 #define PACK_FLAGS ((regs.cc & 0xC0) | (flagH << 5) | (flagI << 4) | (flagN << 3) | (flagZ << 2) | (flagV << 1) | flagC)
106 #define UNPACK_FLAGS flagH = (regs.cc & FLAG_H) >> 5; \
107 flagI = (regs.cc & FLAG_I) >> 4; \
108 flagN = (regs.cc & FLAG_N) >> 3; \
109 flagZ = (regs.cc & FLAG_Z) >> 2; \
110 flagV = (regs.cc & FLAG_V) >> 1; \
111 flagC = (regs.cc & FLAG_C)
113 // Private global variables
115 static V63701REGS regs;
117 static V63701REGS * regsPointer;
118 //V63701REGS * regsPointer;
119 static uint8_t flagH, flagI, flagN, flagZ, flagV, flagC;
121 static uint8_t CPUCycles[256] = {
122 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
123 1, 1, 0, 0, 0, 0, 1, 1, 2, 2, 4, 1, 0, 0, 0, 0,
124 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
125 1, 1, 3, 3, 1, 1, 4, 4, 4, 5, 1, 10, 5, 7, 9, 12,
126 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
127 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
128 6, 7, 7, 6, 6, 7, 6, 6, 6, 6, 6, 5, 6, 4, 3, 5,
129 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 6, 4, 3, 5,
130 2, 2, 2, 3, 2, 2, 2, 0, 2, 2, 2, 2, 3, 5, 3, 0,
131 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 4, 4,
132 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
133 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 5, 6, 5, 5,
134 2, 2, 2, 3, 2, 2, 2, 0, 2, 2, 2, 2, 3, 0, 3, 0,
135 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
136 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5,
137 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5
140 // Private function prototypes
142 static uint16_t RdMemW(uint16_t);
143 static uint16_t FetchMemW(uint16_t);
144 static inline void HandleInterrupt(uint16_t, uint16_t flag = 0);
148 // Read a word out of 63701 memory (little endian format)
150 static inline uint16_t RdMemW(uint16_t address)
152 return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
157 // Fetch a word out of 63701 memory (little endian format). Increments PC
159 static inline uint16_t FetchMemW(uint16_t address)
162 return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
167 // 63701 OPCODE IMPLEMENTATION
169 // NOTE: Lots of macros are used here to save a LOT of typing. Also
170 // helps speed the debugging process. :-) Because of this, combining
171 // certain lines may look like a good idea but would end in disaster.
172 // You have been warned! ;-)
176 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
177 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
178 Add |ADDA |8B 2 2|9B 3 2|AB 5 2|BB 4 3| |A=A+M |T TTTT|
179 |ADDB |CB 2 2|DB 3 2|EB 5 2|FB 4 3| |B=B+M |T TTTT|
180 Add Accumulators |ABA | | | | |1B 2 1|A=A+B |T TTTT|
185 #define OP_ADD_HANDLER(m, acc) \
186 uint16_t sum = (uint16_t)(acc) + (m); \
188 flagH = (sum >> 4) & 0x01; \
189 SET_V(m, acc, sum); \
190 (acc) = sum & 0xFF; \
193 #define OP_ADD_HANDLER16(m, acc) \
194 uint32_t sum = (uint32_t)(acc) + (m); \
196 SET_V16(m, acc, sum); \
197 (acc) = sum & 0xFFFF; \
201 static void Op8B(void) // ADDA #
203 uint16_t m = READ_IMM;
204 OP_ADD_HANDLER(m, regs.d.acc.a);
208 static void Op9B(void) // ADDA ZP
210 uint16_t m = READ_ZP;
211 OP_ADD_HANDLER(m, regs.d.acc.a);
215 static void OpAB(void) // ADDA ZP, X
217 uint16_t m = READ_ZP_X;
218 OP_ADD_HANDLER(m, regs.d.acc.a);
222 static void OpBB(void) // ADDA ABS
224 uint16_t m = READ_ABS;
225 OP_ADD_HANDLER(m, regs.d.acc.a);
229 static void OpCB(void) // ADDB #
231 uint16_t m = READ_IMM;
232 OP_ADD_HANDLER(m, regs.d.acc.b);
236 static void OpDB(void) // ADDB ZP
238 uint16_t m = READ_ZP;
239 OP_ADD_HANDLER(m, regs.d.acc.b);
243 static void OpEB(void) // ADDB ZP, X
245 uint16_t m = READ_ZP_X;
246 OP_ADD_HANDLER(m, regs.d.acc.b);
250 static void OpFB(void) // ADDB ABS
252 uint16_t m = READ_ABS;
253 OP_ADD_HANDLER(m, regs.d.acc.b);
257 static void Op1B(void) // ABA
259 OP_ADD_HANDLER(regs.d.acc.b, regs.d.acc.a);
263 static void Op3A(void) // ABX
265 // Seems this one does *not* affect any flags...
266 regs.x += (uint16_t)regs.d.acc.b;
270 static void OpC3(void) // ADDD #
272 uint16_t m = READ_IMM16;
273 OP_ADD_HANDLER16(m, regs.d.word);
277 static void OpD3(void) // ADDD ZP
279 uint16_t m = READ_ZP16;
280 OP_ADD_HANDLER16(m, regs.d.word);
284 static void OpE3(void) // ADDD ZP, X
286 uint16_t m = READ_ZP_X16;
287 OP_ADD_HANDLER16(m, regs.d.word);
291 static void OpF3(void) // ADDD ABS
293 uint16_t m = READ_ABS16;
294 OP_ADD_HANDLER16(m, regs.d.word);
299 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
300 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
301 Add with Carry |ADCA |89 2 2|99 3 2|A9 5 2|B9 4 3| |A=A+M+C |T TTTT|
302 |ADCB |C9 2 2|D9 3 2|E9 5 2|F9 4 3| |B=B+M+C |T TTTT|
307 #define OP_ADC_HANDLER(m, acc) \
308 uint16_t sum = (uint16_t)acc + (m) + (uint16_t)flagC; \
310 flagH = (sum >> 4) & 0x01; \
311 SET_V(m, acc, sum); \
315 static void Op89(void) // ADCA #
317 uint16_t m = READ_IMM;
318 OP_ADC_HANDLER(m, regs.d.acc.a);
322 static void Op99(void) // ADCA ZP
324 uint16_t m = READ_ZP;
325 OP_ADC_HANDLER(m, regs.d.acc.a);
329 static void OpA9(void) // ADCA ZP, X
331 uint16_t m = READ_ZP_X;
332 OP_ADC_HANDLER(m, regs.d.acc.a);
336 static void OpB9(void) // ADCA ABS
338 uint16_t m = READ_ABS;
339 OP_ADC_HANDLER(m, regs.d.acc.a);
343 static void OpC9(void) // ADCB #
345 uint16_t m = READ_IMM;
346 OP_ADC_HANDLER(m, regs.d.acc.b);
350 static void OpD9(void) // ADCB ZP
352 uint16_t m = READ_ZP;
353 OP_ADC_HANDLER(m, regs.d.acc.b);
357 static void OpE9(void) // ADCB ZP, X
359 uint16_t m = READ_ZP_X;
360 OP_ADC_HANDLER(m, regs.d.acc.b);
364 static void OpF9(void) // ADCB ABS
366 uint16_t m = READ_ABS;
367 OP_ADC_HANDLER(m, regs.d.acc.b);
372 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
373 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
374 And |ANDA |84 2 2|94 3 2|A4 5 2|B4 4 3| |A=A+M | TTR |
375 |ANDB |C4 2 2|D4 3 2|E4 5 2|F4 4 3| |B=B+M | TTR |
380 #define OP_AND_HANDLER(m, acc) \
385 static void Op84(void) // ANDA #
387 uint8_t m = READ_IMM;
388 OP_AND_HANDLER(m, regs.d.acc.a);
392 static void Op94(void) // ANDA ZP
395 OP_AND_HANDLER(m, regs.d.acc.a);
399 static void OpA4(void) // ANDA ZP, X
401 uint16_t m = READ_ZP_X;
402 OP_AND_HANDLER(m, regs.d.acc.a);
406 static void OpB4(void) // ANDA ABS
408 uint16_t m = READ_ABS;
409 OP_AND_HANDLER(m, regs.d.acc.a);
413 static void OpC4(void) // ANDB #
415 uint8_t m = READ_IMM;
416 OP_AND_HANDLER(m, regs.d.acc.b);
420 static void OpD4(void) // ANDB ZP
423 OP_AND_HANDLER(m, regs.d.acc.b);
427 static void OpE4(void) // ANDB ZP, X
429 uint16_t m = READ_ZP_X;
430 OP_AND_HANDLER(m, regs.d.acc.b);
434 static void OpF4(void) // ANDB ABS
436 uint16_t m = READ_ABS;
437 OP_AND_HANDLER(m, regs.d.acc.b);
441 static void Op61(void) // AIM ZP, X (AND immediate with index)
444 uint8_t immValue = READ_IMM;
446 OP_AND_HANDLER(immValue, m);
451 static void Op71(void) // AIM ZP (AND immediate with zero page)
454 uint8_t immValue = READ_IMM;
456 OP_AND_HANDLER(immValue, m);
462 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
463 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
464 Bit Test |BITA |85 2 2|95 3 2|A5 5 2|B5 4 3| |A+M | TTR |
465 |BITB |C5 2 2|D5 3 2|E5 5 2|F5 4 3| |B+M | TTR |
470 #define OP_BIT_HANDLER(m, acc) \
471 int8_t result = acc & (m); \
474 static void Op85(void) // BITA #
476 uint8_t m = READ_IMM;
477 OP_BIT_HANDLER(m, regs.d.acc.a);
481 static void Op95(void) // BITA ZP
484 OP_BIT_HANDLER(m, regs.d.acc.a);
488 static void OpA5(void) // BITA ZP, X
490 uint8_t m = READ_ZP_X;
491 OP_BIT_HANDLER(m, regs.d.acc.a);
495 static void OpB5(void) // BITA ABS
497 uint8_t m = READ_ABS;
498 OP_BIT_HANDLER(m, regs.d.acc.a);
502 static void OpC5(void) // BITB #
504 uint8_t m = READ_IMM;
505 OP_BIT_HANDLER(m, regs.d.acc.b);
509 static void OpD5(void) // BITB ZP
512 OP_BIT_HANDLER(m, regs.d.acc.b);
516 static void OpE5(void) // BITB ZP, X
518 uint8_t m = READ_ZP_X;
519 OP_BIT_HANDLER(m, regs.d.acc.b);
523 static void OpF5(void) // BITB ABS
525 uint8_t m = READ_ABS;
526 OP_BIT_HANDLER(m, regs.d.acc.b);
530 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
531 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
532 Clear |CLR | | |6F 7 2|7F 6 3| |M=00 | RSRR|
533 |CLRA | | | | |4F 2 1|A=00 | RSRR|
534 |CLRB | | | | |5F 2 1|B=00 | RSRR|
539 static void Op6F(void) // CLR ZP, X
541 regs.WrMem(EA_ZP_X, 0);
547 static void Op7F(void) // CLR ABS
549 regs.WrMem(EA_ABS, 0);
555 static void Op4F(void) // CLRA
563 static void Op5F(void) // CLRB
571 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
572 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
573 Compare |CMPA |81 2 2|91 3 2|A1 5 2|B1 4 3| |A-M | TTTT|
574 |CMPB |C1 2 2|D1 3 2|E1 5 2|F1 4 3| |B-M | TTTT|
575 Compare Accumulators |CBA | | | | |11 2 1|A-B | TTTT|
581 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.
584 #define OP_CMP_HANDLER(m, acc) \
585 uint16_t result = acc - (m); \
586 SET_ZNVC_CMP(m, acc, result)
588 static void Op81(void) // CMPA #
590 uint8_t m = READ_IMM;
591 OP_CMP_HANDLER(m, regs.d.acc.a);
595 static void Op91(void) // CMPA ZP
598 OP_CMP_HANDLER(m, regs.d.acc.a);
602 static void OpA1(void) // CMPA ZP, X
604 uint8_t m = READ_ZP_X;
605 OP_CMP_HANDLER(m, regs.d.acc.a);
609 static void OpB1(void) // CMPA ABS
611 uint8_t m = READ_ABS;
612 OP_CMP_HANDLER(m, regs.d.acc.a);
616 static void OpC1(void) // CMPB #
618 uint8_t m = READ_IMM;
619 OP_CMP_HANDLER(m, regs.d.acc.b);
623 static void OpD1(void) // CMPB ZP
626 OP_CMP_HANDLER(m, regs.d.acc.b);
630 static void OpE1(void) // CMPB ZP, X
632 uint8_t m = READ_ZP_X;
633 OP_CMP_HANDLER(m, regs.d.acc.b);
637 static void OpF1(void) // CMPB ABS
639 uint8_t m = READ_ABS;
640 OP_CMP_HANDLER(m, regs.d.acc.b);
644 static void Op11(void) // CBA
646 OP_CMP_HANDLER(regs.d.acc.b, regs.d.acc.a);
650 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
651 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
652 Complement 1's |COM | | |63 7 2|73 6 3| |M=-M | TTRS|
653 |COMA | | | | |43 2 1|A=-A | TTRS|
654 |COMB | | | | |53 2 1|B=-B | TTRS|
659 #define OP_COM_HANDLER(m) \
665 static void Op63(void) // COM ZP, X
674 static void Op73(void) // COM ABS
683 static void Op43(void) // COMA
685 OP_COM_HANDLER(regs.d.acc.a);
689 static void Op53(void) // COMB
691 OP_COM_HANDLER(regs.d.acc.b);
695 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
696 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
697 Complement 2's |NEG | | |60 7 2|70 6 3| |M=00-M | TT12|
698 |NEGA | | | | |40 2 1|A=00-A | TT12|
699 |NEGB | | | | |50 2 1|B=00-B | TT12|
704 #define OP_NEG_HANDLER(m) \
707 flagV = (m == 0x80 ? 1 : 0); \
708 flagC = (m == 0x00 ? 1 : 0)
710 static void Op60(void) // NEG ZP, X
719 static void Op70(void) // NEG ABS
728 static void Op40(void) // NEGA
730 OP_NEG_HANDLER(regs.d.acc.a);
734 static void Op50(void) // NEGB
736 OP_NEG_HANDLER(regs.d.acc.b);
740 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
741 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
742 Decimal Adjust |DAA | | | | |19 2 1|* | TTT3|
745 static void Op19(void) // DAA
747 uint16_t result = (uint16_t)regs.d.acc.a;
749 if ((regs.d.acc.a & 0x0F) > 0x09 || flagH)
752 if ((regs.d.acc.a & 0xF0) > 0x90 || flagC || ((regs.d.acc.a & 0xF0) > 0x80 && (regs.d.acc.a & 0x0F) > 0x09))
755 regs.d.acc.a = (uint8_t)result;
757 CLR_V; // Not sure this is correct...
758 flagC |= (result & 0x100) >> 8; // Overwrite carry if it was 0, otherwise, ignore
762 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
763 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
764 Decrement |DEC | | |6A 7 2|7A 6 3| |M=M-1 | TT4 |
765 |DECA | | | | |4A 2 1|A=A-1 | TT4 |
766 |DECB | | | | |5A 2 1|B=B-1 | TT4 |
771 #define OP_DEC_HANDLER(m) \
774 flagV = (m == 0x7F ? 1 : 0)
776 static void Op6A(void) // DEC ZP, X
785 static void Op7A(void) // DEC ABS
794 static void Op4A(void) // DECA
796 OP_DEC_HANDLER(regs.d.acc.a);
800 static void Op5A(void) // DECB
802 OP_DEC_HANDLER(regs.d.acc.b);
806 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
807 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
808 Exclusive OR |EORA |88 2 2|98 3 2|A8 5 2|B8 4 3| |A=A(+)M | TTR |
809 |EORB |C8 2 2|D8 3 2|E8 5 2|F8 4 3| |B=B(+)M | TTR |
814 #define OP_EOR_HANDLER(m, acc) \
819 static void Op88(void) // EORA #
821 uint8_t m = READ_IMM;
822 OP_EOR_HANDLER(m, regs.d.acc.a);
826 static void Op98(void) // EORA ZP
829 OP_EOR_HANDLER(m, regs.d.acc.a);
833 static void OpA8(void) // EORA ZP, X
835 uint8_t m = READ_ZP_X;
836 OP_EOR_HANDLER(m, regs.d.acc.a);
840 static void OpB8(void) // EORA ABS
842 uint8_t m = READ_ABS;
843 OP_EOR_HANDLER(m, regs.d.acc.a);
847 static void OpC8(void) // EORB #
849 uint8_t m = READ_IMM;
850 OP_EOR_HANDLER(m, regs.d.acc.b);
854 static void OpD8(void) // EORB ZP
857 OP_EOR_HANDLER(m, regs.d.acc.b);
861 static void OpE8(void) // EORB ZP, X
863 uint8_t m = READ_ZP_X;
864 OP_EOR_HANDLER(m, regs.d.acc.b);
868 static void OpF8(void) // EORB ABS
870 uint8_t m = READ_ABS;
871 OP_EOR_HANDLER(m, regs.d.acc.b);
875 static void Op65(void) // EIM ZP, X (EOR immediate with index)
878 uint8_t immValue = READ_IMM;
880 OP_EOR_HANDLER(immValue, m);
885 static void Op75(void) // EIM ZP (EOR immediate with zero page)
888 uint8_t immValue = READ_IMM;
890 OP_EOR_HANDLER(immValue, m);
896 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
897 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
898 Increment |INC | | |6C 7 2|7C 6 3| |M=M+1 | TT5 |
899 |INCA | | | | |4C 2 1|A=A+1 | TT5 |
900 |INCB | | | | |5C 2 1|B=B+1 | TT5 |
905 #define OP_INC_HANDLER(m) \
908 flagV = (m == 0x80 ? 1 : 0)
910 static void Op6C(void) // INC ZP, X
919 static void Op7C(void) // INC ABS
928 static void Op4C(void) // INCA
930 OP_INC_HANDLER(regs.d.acc.a);
934 static void Op5C(void) // INCB
936 OP_INC_HANDLER(regs.d.acc.b);
940 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
941 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
942 Load Accumulator |LDAA |86 2 2|96 3 2|A6 5 2|B6 4 3| |A=M | TTR |
943 |LDAB |C6 2 2|D6 3 2|E6 5 2|F6 4 3| |B=M | TTR |
948 #define OP_LDA_HANDLER(m, acc) \
953 static void Op86(void) // LDAA #
955 uint8_t m = READ_IMM;
956 OP_LDA_HANDLER(m, regs.d.acc.a);
960 static void Op96(void) // LDAA ZP
963 OP_LDA_HANDLER(m, regs.d.acc.a);
967 static void OpA6(void) // LDAA ZP, X
969 uint8_t m = READ_ZP_X;
970 OP_LDA_HANDLER(m, regs.d.acc.a);
974 static void OpB6(void) // LDAA ABS
976 uint8_t m = READ_ABS;
977 OP_LDA_HANDLER(m, regs.d.acc.a);
981 static void OpC6(void) // LDAB #
983 uint8_t m = READ_IMM;
984 OP_LDA_HANDLER(m, regs.d.acc.b);
988 static void OpD6(void) // LDAB ZP
991 OP_LDA_HANDLER(m, regs.d.acc.b);
995 static void OpE6(void) // LDAB ZP, X
997 uint8_t m = READ_ZP_X;
998 OP_LDA_HANDLER(m, regs.d.acc.b);
1002 static void OpF6(void) // LDAB ABS
1004 uint8_t m = READ_ABS;
1005 OP_LDA_HANDLER(m, regs.d.acc.b);
1010 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1011 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1012 OR, Inclusive |ORAA |8A 2 2|9A 3 2|AA 5 2|BA 4 3| |A=A+M | TTR |
1013 |ORAB |CA 2 2|DA 3 2|EA 5 2|FA 4 3| |B=B+M | TTR |
1018 #define OP_ORA_HANDLER(m, acc) \
1023 static void Op8A(void) // ORAA #
1025 uint8_t m = READ_IMM;
1026 OP_ORA_HANDLER(m, regs.d.acc.a);
1030 static void Op9A(void) // ORAA ZP
1032 uint8_t m = READ_ZP;
1033 OP_ORA_HANDLER(m, regs.d.acc.a);
1037 static void OpAA(void) // ORAA ZP, X
1039 uint8_t m = READ_ZP_X;
1040 OP_ORA_HANDLER(m, regs.d.acc.a);
1044 static void OpBA(void) // ORAA ABS
1046 uint8_t m = READ_ABS;
1047 OP_ORA_HANDLER(m, regs.d.acc.a);
1051 static void OpCA(void) // ORAB #
1053 uint8_t m = READ_IMM;
1054 OP_ORA_HANDLER(m, regs.d.acc.b);
1058 static void OpDA(void) // ORAB ZP
1060 uint8_t m = READ_ZP;
1061 OP_ORA_HANDLER(m, regs.d.acc.b);
1065 static void OpEA(void) // ORAB ZP, X
1067 uint8_t m = READ_ZP_X;
1068 OP_ORA_HANDLER(m, regs.d.acc.b);
1072 static void OpFA(void) // ORAB ABS
1074 uint8_t m = READ_ABS;
1075 OP_ORA_HANDLER(m, regs.d.acc.b);
1079 static void Op62(void) // OIM ZP, X (ORA immediate with index)
1082 uint8_t immValue = READ_IMM;
1084 OP_ORA_HANDLER(immValue, m);
1089 static void Op72(void) // OIM ZP (ORA immediate with zero page)
1092 uint8_t immValue = READ_IMM;
1094 OP_ORA_HANDLER(immValue, m);
1100 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1101 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1102 Push Data |PSHA | | | | |36 4 1|Msp=A, *- | |
1103 |PSHB | | | | |37 4 1|Msp=B, *- | |
1104 Pull Data |PULA | | | | |32 4 1|A=Msp, *+ | |
1105 |PULB | | | | |33 4 1|B=Msp, *+ | |
1108 static void Op36(void) // PSHA
1114 static void Op37(void) // PSHB
1120 static void Op32(void) // PULA
1122 regs.d.acc.a = PULL;
1126 static void Op33(void) // PULB
1128 regs.d.acc.b = PULL;
1132 static void Op38(void) // PULX
1138 static void Op3C(void) // PSHX
1145 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1146 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1147 Rotate Left |ROL | | |69 7 2|79 6 3| |Memory *1| TT6T|
1148 |ROLA | | | | |49 2 1|Accum A *1| TT6T|
1149 |ROLB | | | | |59 2 1|Accum B *1| TT6T|
1154 #define OP_ROL_HANDLER(m) \
1155 uint8_t newCarry = (m & 0x80) >> 7; \
1156 m = (m << 1) | flagC; \
1159 flagV = flagN ^ flagC
1161 static void Op69(void) // ROL ZP, X
1170 static void Op79(void) // ROL ABS
1179 static void Op49(void) // ROLA
1181 OP_ROL_HANDLER(regs.d.acc.a);
1185 static void Op59(void) // ROLB
1187 OP_ROL_HANDLER(regs.d.acc.b);
1191 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1192 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1193 Rotate Right |ROR | | |66 7 2|76 6 3| |Memory *2| TT6T|
1194 |RORA | | | | |46 2 1|Accum A *2| TT6T|
1195 |RORB | | | | |56 2 1|Accum B *2| TT6T|
1200 #define OP_ROR_HANDLER(m) \
1201 uint8_t newCarry = m & 0x01; \
1202 m = (m >> 1) | (flagC << 7); \
1205 flagV = flagN ^ flagC
1207 static void Op66(void) // ROR ZP, X
1216 static void Op76(void) // ROR ABS
1225 static void Op46(void) // RORA
1227 OP_ROR_HANDLER(regs.d.acc.a);
1231 static void Op56(void) // RORB
1233 OP_ROR_HANDLER(regs.d.acc.b);
1237 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1238 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1239 Arithmetic Shift Left |ASL | | |68 7 2|78 6 3| |Memory *3| TT6T|
1240 |ASLA | | | | |48 2 1|Accum A *3| TT6T|
1241 |ASLB | | | | |58 2 1|Accum B *3| TT6T|
1246 #define OP_ASL_HANDLER(m) \
1247 uint8_t newCarry = (m & 0x80) >> 7; \
1251 flagV = flagN ^ flagC
1253 static void Op68(void) // ASL ZP, X
1262 static void Op78(void) // ASL ABS
1271 static void Op48(void) // ASLA
1273 OP_ASL_HANDLER(regs.d.acc.a);
1277 static void Op58(void) // ASLB
1279 OP_ASL_HANDLER(regs.d.acc.b);
1283 static void Op05(void) // ASLD
1285 uint8_t newCarry = (regs.d.word & 0x8000) >> 15;
1288 SET_N16(regs.d.word);
1290 flagV = flagN ^ flagC;
1295 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1296 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1297 Arithmetic Shift Right |ASR | | |67 7 2|77 6 3| |Memory *4| TT6T|
1298 |ASRA | | | | |47 2 1|Accum A *4| TT6T|
1299 |ASRB | | | | |57 2 1|Accum B *4| TT6T|
1304 #define OP_ASR_HANDLER(m) \
1305 uint8_t newCarry = m & 0x01; \
1306 m = (m >> 1) | (m & 0x80); \
1309 flagV = flagN ^ flagC
1311 static void Op67(void) // ASR ZP, X
1320 static void Op77(void) // ASR ABS
1329 static void Op47(void) // ASRA
1331 OP_ASR_HANDLER(regs.d.acc.a);
1335 static void Op57(void) // ASRB
1337 OP_ASR_HANDLER(regs.d.acc.b);
1342 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1343 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1344 Logic Shift Right |LSR | | |64 7 2|74 6 3| |Memory *5| TT6T|
1345 |LSRA | | | | |44 2 1|Accum A *5| TT6T|
1346 |LSRB | | | | |54 2 1|Accum B *5| TT6T|
1351 #define OP_LSR_HANDLER(m) \
1352 uint8_t newCarry = m & 0x01; \
1356 flagV = flagN ^ flagC
1358 static void Op64(void) // LSR ZP, X
1367 static void Op74(void) // LSR ABS
1376 static void Op44(void) // LSRA
1378 OP_LSR_HANDLER(regs.d.acc.a);
1382 static void Op54(void) // LSRB
1384 OP_LSR_HANDLER(regs.d.acc.b);
1388 static void Op04(void) // LSRD
1390 uint8_t newCarry = regs.d.word & 0x01;
1395 flagV = flagN ^ flagC;
1400 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1401 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1402 Store Accumulator |STAA | |97 4 2|A7 6 2|B7 5 3| |M=A | TTR |
1403 |STAB | |D7 4 2|E7 6 2|F7 5 3| |M=B | TTR |
1406 static void Op97(void) // STAA ZP
1408 regs.WrMem(EA_ZP, regs.d.acc.a);
1412 static void OpA7(void) // STAA ZP, X
1414 regs.WrMem(EA_ZP_X, regs.d.acc.a);
1418 static void OpB7(void) // STAA ABS
1420 regs.WrMem(EA_ABS, regs.d.acc.a);
1424 static void OpD7(void) // STAB ZP
1426 regs.WrMem(EA_ZP, regs.d.acc.b);
1430 static void OpE7(void) // STAB ZP, X
1432 regs.WrMem(EA_ZP_X, regs.d.acc.b);
1436 static void OpF7(void) // STAB ABS
1438 regs.WrMem(EA_ABS, regs.d.acc.b);
1442 // These are illegal instructions!
1444 static void Op87(void) // STA #
1446 // What does this even mean? Seems the M.A.M.E. guys think it's something
1448 uint16_t effectiveAddress = regs.pc;
1450 regs.WrMem(effectiveAddress, regs.d.acc.a);
1451 SET_ZN(regs.d.acc.a);
1453 // So, basically, what this does is change the immediate value in the
1454 // STA #. Seems like a completely useless thing to me.
1458 static void OpC7(void) // STB #
1460 uint16_t effectiveAddress = regs.pc;
1462 regs.WrMem(effectiveAddress, regs.d.acc.b);
1463 SET_ZN(regs.d.acc.b);
1470 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1471 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1472 Subtract |SUBA |80 2 2|90 3 2|A0 5 2|B0 4 3| |A=A-M | TTTT|
1473 |SUBB |C0 2 2|D0 3 2|E0 5 2|F0 4 3| |B=B-M | TTTT|
1474 Subtract Accumulators |SBA | | | | |10 2 1|A=A-B | TTTT|
1479 #define OP_SUB_HANDLER(m, acc) \
1480 uint16_t sum = (uint16_t)acc - (m); \
1481 flagC = sum >> 15; \
1482 SET_V(m, acc, sum); \
1483 acc = (uint8_t)sum; \
1486 #define OP_SUB_HANDLER16(m, acc) \
1487 uint32_t sum = (uint32_t)acc - (m); \
1488 flagC = sum >> 31; \
1489 SET_V16(m, acc, sum); \
1490 acc = (uint16_t)sum; \
1494 static void Op80(void) // SUBA #
1496 uint16_t m = READ_IMM;
1497 OP_SUB_HANDLER(m, regs.d.acc.a);
1501 static void Op90(void) // SUBA ZP
1503 uint16_t m = READ_ZP;
1504 OP_SUB_HANDLER(m, regs.d.acc.a);
1508 static void OpA0(void) // SUBA ZP, X
1510 uint16_t m = READ_ZP_X;
1511 OP_SUB_HANDLER(m, regs.d.acc.a);
1515 static void OpB0(void) // SUBA ABS
1517 uint16_t m = READ_ABS;
1518 OP_SUB_HANDLER(m, regs.d.acc.a);
1522 static void OpC0(void) // SUBB #
1524 uint16_t m = READ_IMM;
1525 OP_SUB_HANDLER(m, regs.d.acc.b);
1529 static void OpD0(void) // SUBB ZP
1531 uint16_t m = READ_ZP;
1532 OP_SUB_HANDLER(m, regs.d.acc.b);
1536 static void OpE0(void) // SUBB ZP, X
1538 uint16_t m = READ_ZP_X;
1539 OP_SUB_HANDLER(m, regs.d.acc.b);
1543 static void OpF0(void) // SUBB ABS
1545 uint16_t m = READ_ABS;
1546 OP_SUB_HANDLER(m, regs.d.acc.b);
1550 static void Op10(void) // SBA
1552 OP_SUB_HANDLER(regs.d.acc.b, regs.d.acc.a);
1556 static void Op83(void) // SUBD #
1558 uint16_t m = READ_IMM16;
1559 OP_SUB_HANDLER16(m, regs.d.word);
1563 static void Op93(void) // SUBD ZP
1565 uint16_t m = READ_ZP16;
1566 OP_SUB_HANDLER16(m, regs.d.word);
1570 static void OpA3(void) // SUBD ZP, X
1572 uint16_t m = READ_ZP_X16;
1573 OP_SUB_HANDLER16(m, regs.d.word);
1577 static void OpB3(void) // SUBD ABS
1579 uint16_t m = READ_ABS16;
1580 OP_SUB_HANDLER16(m, regs.d.word);
1585 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1586 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1587 Subtract with Carry |SBCA |82 2 2|92 3 2|A2 5 2|B2 4 3| |A=A-M-C | TTTT|
1588 |SBCB |C2 2 2|D2 3 2|E2 5 2|F2 4 3| |B=B-M-C | TTTT|
1593 #define OP_SBC_HANDLER(m, acc) \
1594 uint16_t sum = (uint16_t)acc - (m) - (uint16_t)flagC; \
1595 flagC = sum >> 15; \
1596 SET_V(m, acc, sum); \
1597 acc = (uint8_t)sum; \
1600 static void Op82(void) // SBCA #
1602 uint16_t m = READ_IMM;
1603 OP_SBC_HANDLER(m, regs.d.acc.a);
1607 static void Op92(void) // SBCA ZP
1609 uint16_t m = READ_ZP;
1610 OP_SBC_HANDLER(m, regs.d.acc.a);
1614 static void OpA2(void) // SBCA ZP, X
1616 uint16_t m = READ_ZP_X;
1617 OP_SBC_HANDLER(m, regs.d.acc.a);
1621 static void OpB2(void) // SBCA ABS
1623 uint16_t m = READ_ABS;
1624 OP_SBC_HANDLER(m, regs.d.acc.a);
1628 static void OpC2(void) // SBCB #
1630 uint16_t m = READ_IMM;
1631 OP_SBC_HANDLER(m, regs.d.acc.b);
1635 static void OpD2(void) // SBCB ZP
1637 uint16_t m = READ_ZP;
1638 OP_SBC_HANDLER(m, regs.d.acc.b);
1642 static void OpE2(void) // SBCB ZP, X
1644 uint16_t m = READ_ZP_X;
1645 OP_SBC_HANDLER(m, regs.d.acc.b);
1649 static void OpF2(void) // SBCB ABS
1651 uint16_t m = READ_ABS;
1652 OP_SBC_HANDLER(m, regs.d.acc.b);
1656 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1657 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1658 Transfer Accumulators |TAB | | | | |16 2 1|B=A | TTR |
1659 |TBA | | | | |17 2 1|A=B | TTR |
1662 static void Op16(void) // TAB
1664 regs.d.acc.b = regs.d.acc.a;
1665 SET_ZN(regs.d.acc.b);
1670 static void Op17(void) // TBA
1672 regs.d.acc.a = regs.d.acc.b;
1673 SET_ZN(regs.d.acc.a);
1678 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1679 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1680 Test, Zero/Minus |TST | | |6D 7 2|7D 6 3| |M-00 | TTRR|
1681 |TSTA | | | | |4D 2 1|A-00 | TTRR|
1682 |TSTB | | | | |5D 2 1|B-00 | TTRR|
1687 #define OP_TST_HANDLER(m) \
1688 uint8_t result = m; \
1692 #define OP_TST_IMM_HANDLER(m, acc) \
1693 uint8_t result = m & acc; \
1697 static void Op6D(void) // TST ZP, X
1705 OP_TST_HANDLER(READ_ZP_X);
1710 static void Op7D(void) // TST ABS
1718 OP_TST_HANDLER(READ_ABS);
1723 static void Op4D(void) // TSTA
1725 OP_TST_HANDLER(regs.d.acc.a);
1729 static void Op5D(void) // TSTB
1731 OP_TST_HANDLER(regs.d.acc.b);
1735 static void Op6B(void) // TIM ZP, X (TST immediate with index)
1738 uint8_t immValue = READ_IMM;
1740 OP_TST_IMM_HANDLER(immValue, READ_ZP_X);
1745 static void Op7B(void) // TIM ZP (TST immediate with zero page)
1748 uint8_t immValue = READ_IMM;
1750 OP_TST_IMM_HANDLER(immValue, READ_ZP);
1756 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1757 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1758 Compare Index Register |CPX |8C 3 3|9C 4 2|AC 6 2|BC 5 3| |Formula 1 | 7T8 |
1764 Compare sets flags as if a subtraction had been carried out. If the value in the X register 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. X>=$8000) of the X register.
1767 #define OP_CPX_HANDLER(m) \
1768 uint32_t result = regs.x - (m); \
1769 SET_ZNVC_CMP16(m, regs.x, result)
1771 static void Op8C(void) // CPX #
1773 uint16_t m = READ_IMM16;
1778 static void Op9C(void) // CPX ZP
1780 uint16_t m = READ_ZP16;
1785 static void OpAC(void) // CPX ZP, X
1787 uint16_t m = READ_ZP_X16;
1792 static void OpBC(void) // CPX ABS
1794 uint16_t m = READ_ABS16;
1799 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1800 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1801 Decrement Index Register|DEX | | | | |09 4 1|X=X-1 | T |
1802 Dec Stack Pointer |DES | | | | |34 4 1|SP=SP-1 | |
1803 Inc Index Regster |INX | | | | |08 4 1|X=X+1 | T |
1804 Inc Stack Pointer |INS | | | | |31 4 1|SP=SP+1 | |
1807 static void Op09(void) // DEX
1814 static void Op34(void) // DES
1820 static void Op08(void) // INX
1827 static void Op31(void) // INS
1833 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1834 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1835 Load Index Register |LDX |CE 3 3|DE 4 2|EE 6 2|FE 5 3| |Formula 2 | 9TR |
1836 Load Stack Pointer |LDS |8E 3 3|9E 4 2|AE 6 2|BE 5 3| |Formula 3 | 9TR |
1839 // LD* opcode handler
1841 #define OP_LD_HANDLER(acc) \
1846 static void OpCE(void) // LDX #
1848 regs.x = READ_IMM16;
1849 OP_LD_HANDLER(regs.x);
1853 static void OpDE(void) // LDX ZP
1856 OP_LD_HANDLER(regs.x);
1860 static void OpEE(void) // LDX ZP, X
1862 regs.x = READ_ZP_X16;
1863 OP_LD_HANDLER(regs.x);
1867 static void OpFE(void) // LDX ABS
1869 regs.x = READ_ABS16;
1870 OP_LD_HANDLER(regs.x);
1874 static void Op8E(void) // LDS #
1876 regs.s = READ_IMM16;
1877 OP_LD_HANDLER(regs.s);
1881 static void Op9E(void) // LDS ZP
1884 OP_LD_HANDLER(regs.s);
1888 static void OpAE(void) // LDS ZP, X
1890 regs.s = READ_ZP_X16;
1891 OP_LD_HANDLER(regs.s);
1895 static void OpBE(void) // LDS ABS
1897 regs.s = READ_ABS16;
1898 OP_LD_HANDLER(regs.s);
1902 static void OpCC(void) // LDD #
1904 regs.d.word = READ_IMM16;
1905 OP_LD_HANDLER(regs.d.word);
1909 static void OpDC(void) // LDD ZP
1911 regs.d.word = READ_ZP16;
1912 OP_LD_HANDLER(regs.d.word);
1916 static void OpEC(void) // LDD ZP, X
1918 regs.d.word = READ_ZP_X16;
1919 OP_LD_HANDLER(regs.d.word);
1923 static void OpFC(void) // LDD ABS
1925 regs.d.word = READ_ABS16;
1926 OP_LD_HANDLER(regs.d.word);
1931 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
1932 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
1933 Store Index Register |STX | |DF 5 2|EF 7 2|FF 6 3| |Formula 4 | 9TR |
1934 Store Stack Pointer |STS | |9F 5 2|AF 7 2|BF 6 3| |Formula 5 | 9TR |
1937 // ST* opcode handler
1939 #define OP_ST_HANDLER(m, acc) \
1940 regs.WrMem(m + 0, acc >> 8); \
1941 regs.WrMem(m + 1, acc & 0xFF); \
1946 static void OpDF(void) // STX ZP
1949 OP_ST_HANDLER(m, regs.x);
1953 static void OpEF(void) // STX ZP, X
1955 uint16_t m = EA_ZP_X;
1956 OP_ST_HANDLER(m, regs.x);
1960 static void OpFF(void) // STX ABS
1962 uint16_t m = EA_ABS;
1963 OP_ST_HANDLER(m, regs.x);
1967 static void Op9F(void) // STS ZP
1970 OP_ST_HANDLER(m, regs.s);
1974 static void OpAF(void) // STS ZP, X
1976 uint16_t m = EA_ZP_X;
1977 OP_ST_HANDLER(m, regs.s);
1981 static void OpBF(void) // STS ABS
1983 uint16_t m = EA_ABS;
1984 OP_ST_HANDLER(m, regs.s);
1988 // These are illegal instructions!
1990 // Store immediate--nonsensical opcodes :-P
1991 static void Op8F(void) // STS #
1993 uint16_t effectiveAddress = regs.pc;
1995 OP_ST_HANDLER(effectiveAddress, regs.s);
1999 static void OpCF(void) // STX #
2001 uint16_t effectiveAddress = regs.pc;
2003 OP_ST_HANDLER(effectiveAddress, regs.x);
2007 static void OpCD(void) // STD #
2009 uint16_t effectiveAddress = regs.pc;
2011 OP_ST_HANDLER(effectiveAddress, regs.d.word);
2016 static void OpDD(void) // STD ZP
2019 OP_ST_HANDLER(m, regs.d.word);
2023 static void OpED(void) // STD ZP, X
2025 uint16_t m = EA_ZP_X;
2026 OP_ST_HANDLER(m, regs.d.word);
2030 static void OpFD(void) // STD ABS
2032 uint16_t m = EA_ABS;
2033 OP_ST_HANDLER(m, regs.d.word);
2038 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
2039 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
2040 Index Reg > Stack Pnter |TXS | | | | |35 4 1|SP=X-1 | |
2041 Stack Ptr > Index Regtr |TSX | | | | |30 4 1|X=SP+1 | |
2044 static void Op35(void) // TXS
2046 regs.s = regs.x - 1;
2050 static void Op30(void) // TSX
2052 regs.x = regs.s + 1;
2056 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
2057 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
2058 Always |BRA | |20 4 2| | | |none | |
2059 Carry is Clear |BCC | |24 4 2| | | |C=0 | |
2060 Carry is Set |BCS | |25 4 2| | | |C=1 | |
2061 Equals Zero |BEQ | |27 4 2| | | |Z=1 | |
2062 Greater or Equal to Zero|BGE | |2C 4 2| | | |N(+)V=0 | |
2063 Greater than Zero |BGT | |2E 4 2| | | |Z+N(+)V=0 | |
2064 Higher |BHI | |22 4 2| | | |C+Z=0 | |
2065 Less or Equal than Zero |BLE | |2F 4 2| | | |Z+N(+)V=1 | |
2066 Lower or Same |BLS | |23 4 2| | | |C+Z=1 | |
2067 Less Than Zero |BLT | |2D 4 2| | | |N(+)V=1 | |
2068 Minus |BMI | |2B 4 2| | | |N=1 | |
2069 Not Zero |BNE | |26 4 2| | | |Z=0 | |
2070 Overflow Clear |BVC | |28 4 2| | | |V=0 | |
2071 Overflow Set |BVS | |29 4 2| | | |V=1 | |
2072 Plus |BPL | |2A 4 2| | | |N=0 | |
2075 static void Op20(void) // BRA
2077 int16_t m = (int16_t)(int8_t)READ_IMM;
2082 static void Op21(void) // BRN
2084 int16_t m = (int16_t)(int8_t)READ_IMM;
2089 static void Op24(void) // BCC
2091 // NOTE: We can optimize this by following the maxim: "Don't branch!" by
2092 // converting the boolean result into a multiplication. The only way to
2093 // know if this is a win is to do some profiling both with and without
2094 // the optimization.
2095 int16_t m = (int16_t)(int8_t)READ_IMM;
2097 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2098 //Note sure if the ! operator will do what we want, so we use ^ 1
2099 regs.pc += m * (flagC ^ 0x01);
2107 static void Op25(void) // BCS
2109 int16_t m = (int16_t)(int8_t)READ_IMM;
2111 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2112 regs.pc += m * (flagC);
2120 static void Op27(void) // BEQ
2122 int16_t m = (int16_t)(int8_t)READ_IMM;
2124 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2125 regs.pc += m * (flagZ);
2133 static void Op2C(void) // BGE
2135 int16_t m = (int16_t)(int8_t)READ_IMM;
2137 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2138 regs.pc += m * ((flagN ^ flagV) ^ 0x01);
2140 if (!(flagN ^ flagV))
2146 static void Op2E(void) // BGT
2148 int16_t m = (int16_t)(int8_t)READ_IMM;
2150 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2151 regs.pc += m * ((flagZ | (flagN ^ flagV)) ^ 0x01);
2153 if (!(flagZ | (flagN ^ flagV)))
2159 static void Op22(void) // BHI
2161 int16_t m = (int16_t)(int8_t)READ_IMM;
2163 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2164 regs.pc += m * ((flagZ | flagC) ^ 0x01);
2166 if (!(flagZ | flagC))
2172 static void Op2F(void) // BLE
2174 int16_t m = (int16_t)(int8_t)READ_IMM;
2176 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2177 regs.pc += m * (flagZ | (flagN ^ flagV));
2179 if (flagZ | (flagN ^ flagV))
2185 static void Op23(void) // BLS
2187 int16_t m = (int16_t)(int8_t)READ_IMM;
2189 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2190 regs.pc += m * (flagZ | flagC);
2198 static void Op2D(void) // BLT
2200 int16_t m = (int16_t)(int8_t)READ_IMM;
2202 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2203 regs.pc += m * (flagN ^ flagV);
2211 static void Op2B(void) // BMI
2213 int16_t m = (int16_t)(int8_t)READ_IMM;
2215 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2216 regs.pc += m * (flagN);
2224 static void Op26(void) // BNE
2226 int16_t m = (int16_t)(int8_t)READ_IMM;
2228 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2229 regs.pc += m * (flagZ ^ 0x01);
2237 static void Op28(void) // BVC
2239 int16_t m = (int16_t)(int8_t)READ_IMM;
2241 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2242 regs.pc += m * (flagV ^ 0x01);
2250 static void Op29(void) // BVS
2252 int16_t m = (int16_t)(int8_t)READ_IMM;
2254 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2255 regs.pc += m * (flagV);
2263 static void Op2A(void) // BPL
2265 int16_t m = (int16_t)(int8_t)READ_IMM;
2267 #ifdef TEST_DONT_BRANCH_OPTIMIZATION
2268 regs.pc += m * (flagN ^ 0x01);
2276 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
2277 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
2278 Branch to Subroutine |BSR | |8D 8 2| | | | | |
2279 Jump |JMP | | |6E 4 2|7E 3 3| | | |
2280 Jump to Subroutine |JSR | | |AD 8 2|BD 9 3| | | |
2283 static void Op8D(void) // BSR
2285 int16_t m = (int16_t)(int8_t)READ_IMM;
2291 static void Op6E(void) // JMP ZP, X
2293 uint16_t m = EA_ZP_X;
2298 static void Op7E(void) // JMP ABS
2304 static void Op9D(void) // JSR ZP
2306 uint16_t m = (uint16_t)EA_ZP;
2312 static void OpAD(void) // JSR ZP, X
2314 uint16_t m = EA_ZP_X;
2320 static void OpBD(void) // JSR ABS
2322 uint16_t m = EA_ABS;
2329 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
2330 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
2331 No Operation |NOP | | | | |01 2 1| | |
2332 Return from Interrupt |RTI | | | | |3B A 1| |AAAAAA|
2333 Return from Subroutine |RTS | | | | |39 5 1| | |
2334 Software Interrupt |SWI | | | | |3F C 1| | S |
2335 Wait For Interrupt |WAI | | | | |3E 9 1| | B |
2338 static void Op01(void) // NOP
2343 static void Op3B(void) // RTI
2346 regs.d.acc.b = PULL;
2347 regs.d.acc.a = PULL;
2354 static void Op39(void) // RTS
2360 static void Op3F(void) // SWI
2362 // It seems that the SWI is non-maskable, unlike the IRQ...
2364 regs.cc = PACK_FLAGS; // Mash flags back into the CC register
2365 PUSH16(regs.pc); // Save all regs...
2370 regs.pc = RdMemW(0xFFFA); // And do it!
2371 flagI = 1; // Also, set IRQ inhibit
2373 HandleInterrupt(0xFFFA);
2377 static void Op3E(void) // WAI
2380 WriteLog("*** WAI STATE ASSERTED ***\n");
2382 regs.cc = PACK_FLAGS; // Mash flags back into the CC register
2383 PUSH16(regs.pc); // Save all regs...
2388 regs.cpuFlags |= V63701_STATE_WAI; // And signal that we're in WAI mode
2394 static void Op3D(void) // MUL
2396 regs.d.word = (uint16_t)regs.d.acc.a * (uint16_t)regs.d.acc.b;
2397 flagC = regs.d.acc.b >> 7; // bug? No, this is how it really does it
2401 // Exchange X and D opcode
2403 static void Op18(void) // XGDX
2405 // This doesn't affect flags apparently
2406 uint16_t temp = regs.x;
2407 regs.x = regs.d.word;
2412 // Sleep opcode (similar to WAI)
2414 static void Op1A(void) // SLP
2416 // Apparently doesn't stack the state, so no need for RTI
2417 // It would seem that this opcode is intended for use in interrupt
2418 // routines, as it prevents stacking the state when one happens.
2419 // (Actually, this is used to put the MCU into a low power "sleep" mode,
2420 // where it stays until an interrupt or RESET occurs.)
2421 regs.cpuFlags |= V63701_STATE_WAI;
2425 // Undocumented opcode ($12 & $13)
2427 static void OpUN(void) // Undocumented
2429 regs.x += regs.RdMem(regs.s + 1);
2434 Operation |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
2435 | |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #| |HINZVC|
2436 Clear Carry |CLC | | | | |0C 2 1|C=0 | R|
2437 Clear Interrupt |CLI | | | | |0E 2 1|I=0 | R |
2438 Clear Overflow |CLV | | | | |0A 2 1|V=0 | R |
2439 Set Carry |SEC | | | | |0D 2 1|C=1 | S|
2440 Set Interrupt |SEI | | | | |0F 2 1|I=1 | S |
2441 Set Overflow |SEV | | | | |0B 2 1|V=1 | S |
2442 CCR=Accumulator A |TAP | | | | |06 2 1|CCR=A |CCCCCC|
2443 Accumlator A=CCR |TPA | | | | |07 2 1|A=CCR | |
2446 static void Op0C(void) // CLC
2452 static void Op0E(void) // CLI
2458 static void Op0A(void) // CLV
2464 static void Op0D(void) // SEC
2470 static void Op0F(void) // SEI
2476 static void Op0B(void) // SEV
2482 static void Op06(void) // TAP
2484 regs.cc = regs.d.acc.a;
2489 static void Op07(void) // TPA
2491 regs.d.acc.a = PACK_FLAGS;
2496 OP Operation Code, in Hexadecimal
2497 ~ Number of MPU cycles required
2498 # Number of program bytes required
2502 Msp Contents of Memory pointed to be Stack Pointer
2503 + Boolean Inclusive OR
2504 (+) Boolean Exclusive OR (XOR)
2505 * Converts Binary Addition of BCD Characters into BCD Format
2509 Condition Code Register Legend
2513 T Tests and sets if True, cleared otherise
2514 1 Test: Result=10000000?
2515 2 Test: Result=00000000?
2516 3 Test: Decimal value of most significant BCD character greater than nine?
2517 (Not cleared if previously set)
2518 4 Test: Operand=10000000 prior to execution?
2519 5 Test: Operand=01111111 prior to execution?
2520 6 Test: Set equal to result or N(+)C after shift has occurred.
2521 7 Test: Sign bit of most significant byte or result=1?
2522 8 Test: 2's compliment overflow from subtraction of least
2524 9 Test: Result less than zero? (Bit 15=1)
2525 A Load Condition Code Register from Stack.
2526 B Set when interrupt occurs. If previously set, a NMI is
2527 required to exit the wait state.
2528 C Set according to the contents of Accumulator A.
2530 *x SHIFT AND ROTATION DIAGRAMS
2531 *1 +-----------------+ C to LSB
2534 *2 +-----------------+
2537 *3 C <- 76543210 <- 0(Data)
2540 *4 �>76543210 -> C
2542 *5 (Data)0 -> 76543210 -> C
2552 static void Op__(void)
2554 // TRAP is non-maskable, unlike the IRQ... Also, highest priority after
2556 HandleInterrupt(0xFFEE);
2557 // regs.cpuFlags |= V63701_STATE_ILLEGAL_INST;
2562 // Ok, the exec_op[] array is globally defined here basically to save
2563 // a LOT of unnecessary typing. Sure it's ugly, but hey, it works!
2565 static void (* exec_op[256])() = {
2566 Op__, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op0B, Op0C, Op0D, Op0E, Op0F,
2567 Op10, Op11, OpUN, OpUN, Op__, Op__, Op16, Op17, Op18, Op19, Op1A, Op1B, Op__, Op__, Op__, Op__,
2568 Op20, Op21, Op22, Op23, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op2B, Op2C, Op2D, Op2E, Op2F,
2569 Op30, Op31, Op32, Op33, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op3B, Op3C, Op3D, Op3E, Op3F,
2570 Op40, Op__, Op__, Op43, Op44, Op__, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op__, Op4F,
2571 Op50, Op__, Op__, Op53, Op54, Op__, Op56, Op57, Op58, Op59, Op5A, Op__, Op5C, Op5D, Op__, Op5F,
2572 Op60, Op61, Op62, Op63, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op6B, Op6C, Op6D, Op6E, Op6F,
2573 Op70, Op71, Op72, Op73, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op7B, Op7C, Op7D, Op7E, Op7F,
2574 Op80, Op81, Op82, Op83, Op84, Op85, Op86, Op__, Op88, Op89, Op8A, Op8B, Op8C, Op8D, Op8E, Op__,
2575 Op90, Op91, Op92, Op93, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op9B, Op9C, Op9D, Op9E, Op9F,
2576 OpA0, OpA1, OpA2, OpA3, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, OpAB, OpAC, OpAD, OpAE, OpAF,
2577 OpB0, OpB1, OpB2, OpB3, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, OpBB, OpBC, OpBD, OpBE, OpBF,
2578 OpC0, OpC1, OpC2, OpC3, OpC4, OpC5, OpC6, Op__, OpC8, OpC9, OpCA, OpCB, OpCC, Op__, OpCE, Op__,
2579 OpD0, OpD1, OpD2, OpD3, OpD4, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, OpDB, OpDC, OpDD, OpDE, OpDF,
2580 OpE0, OpE1, OpE2, OpE3, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, OpEB, OpEC, OpED, OpEE, OpEF,
2581 OpF0, OpF1, OpF2, OpF3, OpF4, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, OpFB, OpFC, OpFD, OpFE, OpFF
2586 // Internal "memcpy" (so we don't have to link with any external libraries!)
2588 static void myMemcpy(void * dst, void * src, uint32_t size)
2590 uint8_t * d = (uint8_t *)dst, * s = (uint8_t *)src;
2592 for(uint32_t i=0; i<size; i++)
2598 //int instCount[256];
2599 bool V63701LogGo = false;
2600 //static bool V63701LogGo = true;
2602 extern uint8_t mcuMem[];
2603 uint8_t * memory = mcuMem;
2606 // Function to execute 63701 for "cycles" cycles
2608 void Execute63701(V63701REGS * context, uint32_t cycles)
2610 //#warning "V63701_STATE_WAI is not properly handled yet! !!! FIX !!!"
2611 //#warning "Need to convert from destructive clock to non-destructive. !!! FIX !!!"
2612 regsPointer = context;
2613 myMemcpy(®s, context, sizeof(V63701REGS));
2614 // Explode flags register into individual uint8_ts
2619 while (regs.clock < cycles)
2622 uint64_t endCycles = regs.clock + (uint64_t)cycles - regs.clockOverrun;
2624 while (regs.clock < endCycles)
2628 if (regs.pc == 0x8236)
2630 printf("V63701: $8236 called by $%04X...\n", RdMemW(regs.s));
2636 Decode63701(memory, regs.pc, instBuf);
2637 WriteLog("%s\n", instBuf);
2640 if (regs.cpuFlags & V63701_STATE_WAI)
2642 // Only bail out if no interrupts/resets are pending
2643 if (!(regs.cpuFlags & (V63701_ASSERT_LINE_IRQ | V63701_ASSERT_LINE_NMI | V63701_ASSERT_LINE_RESET | V63701_ASSERT_TIMER_OVERFLOW | V63701_ASSERT_OUTPUT_COMPARE | V63701_ASSERT_INPUT_CAPTURE)))
2644 // Burn any remaining cycles...
2645 regs.clock = endCycles;
2649 uint8_t opcode = regs.RdMem(regs.pc++);
2650 exec_op[opcode](); // Execute that opcode...
2651 regs.clock += CPUCycles[opcode];
2652 uint16_t oldCounter = regs.counter.word;
2653 regs.counter.word += CPUCycles[opcode];
2655 // We fake the free running counter above, by adding cycle counts
2656 // to it; this means we can never be sure that the counter will
2657 // be equal to the output compare register. So we have to check
2658 // for two cases: 1st, look for the previous counter value and see
2659 // if we went past it, and 2nd, do the same but check to see if we
2660 // overflowed in the meantime.
2661 if (((oldCounter < regs.outputCompare.word)
2662 && (regs.counter.word >= regs.outputCompare.word))
2663 || ((oldCounter > regs.counter.word)
2664 && (regs.counter.word >= regs.outputCompare.word)))
2666 // Set the output compare flag bit
2667 regs.tcsr.bit.ocf = 1;
2669 if (regs.tcsr.bit.eoci)
2671 regs.cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
2672 regsPointer->cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
2676 // Check for counter overflow
2677 if (regs.counter.word < oldCounter)
2679 // Set the timer overflow flag bit
2680 regs.tcsr.bit.tof = 1;
2682 if (regs.tcsr.bit.etoi)
2684 regs.cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
2685 regsPointer->cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
2690 // WriteLog(" [PC=%04X, S=%04X, X=%04X, A=%02X, B=%02X, CC=%s%s%s%s%s%s%s%s]\n", regs.pc, regs.s, regs.x, regs.d.acc.a, regs.d.acc.b, (regs.cc & FLAG_E ? "E" : " "), (regs.cc & FLAG_F ? "F" : " "), (regs.cc & FLAG_H ? "H" : " "), (regs.cc & FLAG_I ? "I" : " "), (regs.cc & FLAG_N ? "N" : " "), (regs.cc & FLAG_Z ? "Z" : " "), (regs.cc & FLAG_V ? "V" : " "), (regs.cc & FLAG_C ? "C" : " "));
2691 WriteLog(" [PC=%04X S=%04X X=%04X A=%02X B=%02X CC=%s%s%s%s%s%s TCSR=%s%s%s%s%s%s%s%s CT=%04X OC=%04X]\n", regs.pc, regs.s, regs.x, regs.d.acc.a, regs.d.acc.b, (flagH ? "H" : "."), (flagI ? "I" : "."), (flagN ? "N" : "."), (flagZ ? "Z" : "."), (flagV ? "V" : "."), (flagC ? "C" : "."), (regs.tcsr.bit.icf ? "I" :"."), (regs.tcsr.bit.ocf ? "O" :"."), (regs.tcsr.bit.tof ? "T" :"."), (regs.tcsr.bit.eici ? "i" :"."), (regs.tcsr.bit.eoci ? "o" :"."), (regs.tcsr.bit.etoi ? "t" :"."), (regs.tcsr.bit.iedg ? "E" :"."), (regs.tcsr.bit.olvl ? "O" :"."), regs.counter.word, regs.outputCompare.word);
2695 if (regs.cpuFlags & V63701_ASSERT_LINE_RESET)
2699 WriteLog("*** RESET LINE ASSERTED ***\n");
2702 regs.tcsrWasRead = false;
2703 regs.counter.word = 0;
2704 regs.outputCompare.word = 0xFFFF;
2706 regs.pc = RdMemW(0xFFFE); // And load PC with the RESET vector
2707 context->cpuFlags = 0; // Clear all lingering flags...
2710 else if (regs.cpuFlags & V63701_ASSERT_LINE_NMI)
2714 WriteLog("*** NMI LINE ASSERTED ***\n");
2716 HandleInterrupt(0xFFFC, V63701_ASSERT_LINE_NMI);
2718 else if (regs.cpuFlags & V63701_ASSERT_LINE_IRQ)
2722 WriteLog("*** IRQ LINE ASSERTED ***\n");
2724 // if (!(regs.cc & FLAG_I)) // Process an interrupt (I=0)?
2725 if (!flagI) // Process an interrupt (I=0)?
2729 WriteLog(" IRQ TAKEN!\n");
2730 //V63701LogGo = true;
2732 HandleInterrupt(0xFFF8, V63701_ASSERT_LINE_IRQ);
2735 else if (regs.cpuFlags & V63701_ASSERT_INPUT_CAPTURE)
2739 WriteLog("*** INPUT CAPTURE ASSERTED ***\n");
2741 // Process interrupt if no I inhibit set, & enable in TCSR is set
2742 if (!flagI && regs.tcsr.bit.eici)
2746 WriteLog(" IC TAKEN!\n");
2747 //V63701LogGo = true;
2749 HandleInterrupt(0xFFF6, V63701_ASSERT_INPUT_CAPTURE);
2752 else if (regs.cpuFlags & V63701_ASSERT_OUTPUT_COMPARE)
2756 WriteLog("*** OUTPUT COMPARE ASSERTED ***\n");
2758 // Process interrupt if no I inhibit set, & enable in TCSR is set
2759 if (!flagI && regs.tcsr.bit.eoci)
2763 WriteLog(" OC TAKEN!\n");
2764 //V63701LogGo = true;
2766 HandleInterrupt(0xFFF4, V63701_ASSERT_OUTPUT_COMPARE);
2769 else if (regs.cpuFlags & V63701_ASSERT_TIMER_OVERFLOW)
2773 WriteLog("*** TIMER OVER ASSERTED ***\n");
2775 // Process interrupt if no I inhibit set, & enable in TCSR is set
2776 if (!flagI && regs.tcsr.bit.etoi)
2780 WriteLog(" TO TAKEN!\n");
2781 //V63701LogGo = true;
2783 HandleInterrupt(0xFFF2, V63701_ASSERT_TIMER_OVERFLOW);
2788 // If we went longer than the passed in cycles, make a note of it so we can
2789 // subtract it out from a subsequent run. It's guaranteed to be positive,
2790 // because the condition that exits the main loop above is written such
2791 // that regs.clock has to be larger than endCycles to exit from it.
2792 regs.clockOverrun = regs.clock - endCycles;
2794 regs.cc = PACK_FLAGS; // Mash flags back into the CC register
2795 myMemcpy(context, ®s, sizeof(V63701REGS));
2800 // Get the clock of the currently executing CPU
2802 uint64_t GetCurrentV63701Clock(void)
2808 static inline void HandleInterrupt(uint16_t vector, uint16_t flag/*= 0*/)
2810 if (regs.cpuFlags & V63701_STATE_WAI)
2814 regs.cc = PACK_FLAGS; // Mash flags back into the CC register
2815 PUSH16(regs.pc); // Save all regs...
2823 regs.pc = RdMemW(vector); // And do it!
2826 // Clear the passed in flag + WAI state
2827 regs.cpuFlags &= ~(flag | V63701_STATE_WAI);
2828 regsPointer->cpuFlags &= ~(flag | V63701_STATE_WAI);
2832 uint8_t InternalRegisterRead(uint16_t address)
2834 switch (address & 0x1F)
2841 return (V63701ReadPort1() & ~regs.ddr1) | (regs.port1data & regs.ddr1);
2842 // return /*(regs.port1read & ~regs.ddr1) |*/ (regs.port1r & ~regs.ddr1);
2844 // Top 3 bits are MCU mode bits.
2845 return (V63701ReadPort2() & ~regs.ddr2) | (regs.port2data & regs.ddr2);
2846 // return /*(regs.port1read & ~regs.ddr1) |*/ (regs.port2r & ~regs.ddr2 & 0x1F);
2847 // Timer Control and Status Register
2849 regs.tcsrWasRead = true;
2850 return regs.tcsr.byte;
2851 // Counter high byte
2853 if (regs.tcsrWasRead)
2855 regs.tcsr.bit.tof = 0;
2856 regs.tcsrWasRead = false;
2859 regs.cReadLatch = regs.counter.byte.lo;
2860 return regs.counter.byte.hi;
2863 return regs.cReadLatch;
2864 // Output compare high byte
2866 return regs.outputCompare.byte.hi;
2867 // Output compare low byte
2869 return regs.outputCompare.byte.lo;
2870 // RAM Control register (only bits 6 & 7 are valid)
2872 return (regs.ramCtrl & 0xC0) | 0x3F;
2875 printf("V63701: Unhandled register read @ $%02X...\n", address);
2885 void InternalRegisterWrite(uint16_t address, uint8_t data)
2889 switch (address & 0x1F)
2898 regs.port1data = data;
2899 writeData = (V63701ReadPort1() & ~regs.ddr1) | (data & regs.ddr1);
2900 V63701WritePort1(writeData);
2903 // Port 2 only has 5 bits of output, top 3 are the MCU mode bits
2904 regs.port2data = ((regs.port2data & ~regs.ddr2) | (data & regs.ddr2)) & 0x1F;
2907 // Timer Control and Status Register
2909 // Top 3 bits are RO, so protect them
2910 regs.tcsr.byte = (data & 0x1F) | (regs.tcsr.byte & 0xE0);
2912 // Counter (High Byte)
2914 regs.cWriteLatch = data;
2915 regs.counter.word = 0xFFF8;
2917 // Counter (Low Byte)
2919 regs.counter.word = (regs.cWriteLatch << 8) | data;
2921 // Output Compare Register (High Byte)
2923 regs.outputCompare.byte.hi = data;
2925 if (regs.tcsrWasRead)
2927 regs.tcsr.bit.ocf = 0;
2928 regs.tcsrWasRead = false;
2932 // Output Compare Register (Low Byte)
2934 regs.outputCompare.byte.lo = data;
2936 if (regs.tcsrWasRead)
2938 regs.tcsr.bit.ocf = 0;
2939 regs.tcsrWasRead = false;
2943 // RAM Control register
2945 regs.ramCtrl = data;
2949 WriteLog("V63701: Unhandled register write @ $%02X...\n", address);