]> Shamusworld >> Repos - thunder/blobdiff - src/v63701.cpp
Added V63701, YM2151 emulation, more SDL 2 fixes.
[thunder] / src / v63701.cpp
diff --git a/src/v63701.cpp b/src/v63701.cpp
new file mode 100644 (file)
index 0000000..3f01921
--- /dev/null
@@ -0,0 +1,2920 @@
+//
+// Virtual 63701 Emulator v1.0
+//
+// by James Hammons
+// (C) 2014 Underground Software
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO  WHEN        WHAT
+// ---  ----------  -----------------------------------------------------------
+// JLH  06/15/2006  Added changelog ;-)
+// JLH  06/15/2006  Scrubbed all BYTE, WORD & DWORD references from the code
+// JLH  11/13/2006  Converted core to V65C02 macro style :-)
+// JLH  11/13/2006  Converted flags to unpacked and separate flags
+// JLH  07/21/2009  Converted clock from 32-bit to 64-bit value, added possible
+//                  "don't branch" optimization
+// JLH  09/21/2009  Fixed EA_ABS macros
+// JLH  04/07/2014  Converted core to HD63701 type
+// JLH  04/16/2014  Fixed a raft of bugs, added sane timer handling
+//
+
+// This is based on V6808, since it's in the same family apparently. The
+// HD63701 has more opcodes, and apparently some built in timer facility as
+// well.
+
+// N.B.: There are some things missing from the emulation, such as the data
+//       ports, the Input Capture, RAM Control, sleep mode, processor modes,
+//       and probably some other stuff as well. What is here seems to work, and
+//       work well. It's a fair sight better than the one that's in MAME,
+//       that's for sure. :-D
+
+// Some random thoughts: Could there be a performance gain by breaking
+// out the flags in regs.cc into separate uint8_t variables (or bools)?
+// You'd have to convert on entering and exiting the emulation loop, but I
+// think the perfomance hit would be negligible compared to the gain in not
+// having to mask and shift flags all the time. Investigate after the
+// conversion to macro style opcodes is completed. :-)
+// [DONE--remain to be seen if there is any performance increase]
+
+//#define __DEBUG__
+#define TEST_DONT_BRANCH_OPTIMIZATION
+
+#include "v63701.h"
+//#include <stdio.h>           // for printf()
+
+#ifdef __DEBUG__
+#include "dis63701.h"
+//#include "log.h"
+#define WriteLog printf
+#endif
+
+// Various macros
+
+#define CLR_Z                                  (flagZ = 0)
+#define CLR_ZN                                 (flagZ = flagN = 0)
+#define CLR_ZNC                                        (flagZ = flagN = flagC = 0)
+#define CLR_NVC                                        (flagN = flagV = flagC = 0)
+#define CLR_VC                                 (flagV = flagC = 0)
+#define CLR_V                                  (flagV = 0)
+#define CLR_N                                  (flagN = 0)
+#define SET_Z(r)                               (flagZ = ((r) == 0 ? 1 : 0))
+#define SET_N(r)                               (flagN = ((r) & 0x80) >> 7)
+#define SET_V(a,b,r)                   (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x80) >> 7)
+
+#define SET_C_CMP(a,b)                 (flagC = ((uint8_t)(b) < (uint8_t)(a) ? 1 : 0))
+#define SET_ZN(r)                              SET_N(r); SET_Z(r)
+#define SET_ZNC_ADD(a,b,r)             SET_N(r); SET_Z(r); SET_C_ADD(a,b)
+#define SET_ZNVC_CMP(a,b,r)            SET_N(r); SET_Z(r); SET_C_CMP(a,b); SET_V(a,b,r)
+
+#define SET_N16(r)                             (flagN = ((r) & 0x8000) >> 15)
+#define SET_V16(a,b,r)                 (flagV = (((b) ^ (a) ^ (r) ^ ((r) >> 1)) & 0x8000) >> 15)
+#define SET_C_CMP16(a,b)               (flagC = ((uint16_t)(b) < (uint16_t)(a) ? 1 : 0))
+#define SET_ZNVC_CMP16(a,b,r)  SET_N16(r); SET_Z(r); SET_C_CMP16(a,b); SET_V16(a,b,r)
+
+#define EA_IMM                         regs.pc++
+#define EA_ZP                          regs.RdMem(regs.pc++)
+#define EA_ZP_X                                (regs.RdMem(regs.pc++) + regs.x)
+#define EA_ABS                         FetchMemW(regs.pc)
+
+#define READ_IMM                       regs.RdMem(EA_IMM)
+#define READ_ZP                                regs.RdMem(EA_ZP)
+#define READ_ZP_X                      regs.RdMem(EA_ZP_X)
+#define READ_ABS                       regs.RdMem(EA_ABS)
+
+#define READ_IMM16                     FetchMemW(regs.pc);
+#define READ_ZP16                      RdMemW(EA_ZP)
+#define READ_ZP_X16                    RdMemW(EA_ZP_X)
+#define READ_ABS16                     RdMemW(EA_ABS)
+
+#define READ_IMM_WB(v)         uint16_t addr = EA_IMM;         v = regs.RdMem(addr)
+#define READ_ZP_WB(v)          uint16_t addr = EA_ZP;          v = regs.RdMem(addr)
+#define READ_ZP_X_WB(v)                uint16_t addr = EA_ZP_X;        v = regs.RdMem(addr)
+#define READ_ABS_WB(v)         uint16_t addr = EA_ABS;         v = regs.RdMem(addr)
+
+#define WRITE_BACK(d)          regs.WrMem(addr, (d))
+
+#define PULL                           regs.RdMem(regs.s++)
+#define PUSH(r)                                regs.WrMem(--regs.s, (r))
+#define PULL16                         RdMemW(regs.s);         regs.s += 2
+#define PUSH16(r)                      regs.WrMem(--regs.s, (r) & 0xFF); regs.WrMem(--regs.s, (r) >> 8)
+
+#define PACK_FLAGS                     ((regs.cc & 0xC0) | (flagH << 5) | (flagI << 4) | (flagN << 3) | (flagZ << 2) | (flagV << 1) | flagC)
+#define UNPACK_FLAGS           flagH = (regs.cc & FLAG_H) >> 5; \
+       flagI = (regs.cc & FLAG_I) >> 4; \
+       flagN = (regs.cc & FLAG_N) >> 3; \
+       flagZ = (regs.cc & FLAG_Z) >> 2; \
+       flagV = (regs.cc & FLAG_V) >> 1; \
+       flagC = (regs.cc & FLAG_C)
+
+// Private global variables
+
+static V63701REGS regs;
+static V63701REGS * regsPointer;
+static uint8_t flagH, flagI, flagN, flagZ, flagV, flagC;
+
+static uint8_t CPUCycles[256] = {
+       0,  1,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
+       1,  1,  0,  0,  0,  0,  1,  1,  2,  2,  4,  1,  0,  0,  0,  0,
+       3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
+       1,  1,  3,  3,  1,  1,  4,  4,  4,  5,  1, 10,  5,  7,  9, 12,
+       1,  0,  0,  1,  1,  0,  1,  1,  1,  1,  1,  0,  1,  1,  0,  1,
+       1,  0,  0,  1,  1,  0,  1,  1,  1,  1,  1,  0,  1,  1,  0,  1,
+       6,  7,  7,  6,  6,  7,  6,  6,  6,  6,  6,  5,  6,  4,  3,  5,
+       6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  6,  4,  6,  4,  3,  5,
+       2,  2,  2,  3,  2,  2,  2,  0,  2,  2,  2,  2,  3,  5,  3,  0,
+       3,  3,  3,  4,  3,  3,  3,  3,  3,  3,  3,  3,  4,  5,  4,  4,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  6,  5,  5,
+       2,  2,  2,  3,  2,  2,  2,  0,  2,  2,  2,  2,  3,  0,  3,  0,
+       3,  3,  3,  4,  3,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5,
+       4,  4,  4,  5,  4,  4,  4,  4,  4,  4,  4,  4,  5,  5,  5,  5
+};
+
+// Private function prototypes
+
+static uint16_t RdMemW(uint16_t);
+static uint16_t FetchMemW(uint16_t);
+static inline void HandleInterrupt(uint16_t, uint16_t flag = 0);
+
+
+//
+// Read a word out of 63701 memory (little endian format)
+//
+static inline uint16_t RdMemW(uint16_t address)
+{
+       return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
+}
+
+
+//
+// Fetch a word out of 63701 memory (little endian format). Increments PC
+//
+static inline uint16_t FetchMemW(uint16_t address)
+{
+       regs.pc += 2;
+       return (uint16_t)(regs.RdMem(address) << 8) | regs.RdMem(address + 1);
+}
+
+
+//
+// 63701 OPCODE IMPLEMENTATION
+//
+// NOTE: Lots of macros are used here to save a LOT of typing. Also
+//       helps speed the debugging process. :-) Because of this, combining
+//       certain lines may look like a good idea but would end in disaster.
+//       You have been warned! ;-)
+//
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Add                     |ADDA |8B 2 2|9B 3 2|AB 5 2|BB 4 3|      |A=A+M     |T TTTT|
+                        |ADDB |CB 2 2|DB 3 2|EB 5 2|FB 4 3|      |B=B+M     |T TTTT|
+Add Accumulators        |ABA  |      |      |      |      |1B 2 1|A=A+B     |T TTTT|
+*/
+
+// ADD opcodes
+
+#define OP_ADD_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)(acc) + (m); \
+       flagC = sum >> 8; \
+       flagH = (sum >> 4) & 0x01; \
+       SET_V(m, acc, sum); \
+       (acc) = sum & 0xFF; \
+       SET_ZN(acc)
+
+#define OP_ADD_HANDLER16(m, acc) \
+       uint32_t sum = (uint32_t)(acc) + (m); \
+       flagC = sum >> 16; \
+       SET_V16(m, acc, sum); \
+       (acc) = sum & 0xFFFF; \
+       SET_Z(acc); \
+       SET_N16(acc)
+
+static void Op8B(void)         // ADDA #
+{
+       uint16_t m = READ_IMM;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op9B(void)         // ADDA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpAB(void)         // ADDA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpBB(void)         // ADDA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADD_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpCB(void)         // ADDB #
+{
+       uint16_t m = READ_IMM;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpDB(void)         // ADDB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpEB(void)         // ADDB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpFB(void)         // ADDB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADD_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op1B(void)         // ABA
+{
+       OP_ADD_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+
+static void Op3A(void)         // ABX
+{
+       // Seems this one does *not* affect any flags...
+       regs.x += (uint16_t)regs.d.acc.b;
+}
+
+
+static void OpC3(void)         // ADDD #
+{
+       uint16_t m = READ_IMM16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpD3(void)         // ADDD ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpE3(void)         // ADDD ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpF3(void)         // ADDD ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_ADD_HANDLER16(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Add with Carry          |ADCA |89 2 2|99 3 2|A9 5 2|B9 4 3|      |A=A+M+C   |T TTTT|
+                        |ADCB |C9 2 2|D9 3 2|E9 5 2|F9 4 3|      |B=B+M+C   |T TTTT|
+*/
+
+// ADC opcodes
+
+#define OP_ADC_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc + (m) + (uint16_t)flagC; \
+       flagC = sum >> 8; \
+       flagH = (sum >> 4) & 0x01; \
+       SET_V(m, acc, sum); \
+       acc = sum & 0xFF; \
+       SET_ZN(acc)
+
+static void Op89(void)                                                 // ADCA #
+{
+       uint16_t m = READ_IMM;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op99(void)                                                 // ADCA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA9(void)                                                 // ADCA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB9(void)                                                 // ADCA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC9(void)                                                 // ADCB #
+{
+       uint16_t m = READ_IMM;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD9(void)                                                 // ADCB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE9(void)                                                 // ADCB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF9(void)                                                 // ADCB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_ADC_HANDLER(m, regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+And                     |ANDA |84 2 2|94 3 2|A4 5 2|B4 4 3|      |A=A+M     |  TTR |
+                        |ANDB |C4 2 2|D4 3 2|E4 5 2|F4 4 3|      |B=B+M     |  TTR |
+*/
+
+// AND opcodes
+
+#define OP_AND_HANDLER(m, acc) \
+       acc &= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op84(void)         // ANDA #
+{
+       uint8_t m = READ_IMM;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op94(void)         // ANDA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA4(void)         // ANDA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB4(void)         // ANDA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_AND_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC4(void)         // ANDB #
+{
+       uint8_t m = READ_IMM;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD4(void)         // ANDB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE4(void)         // ANDB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF4(void)         // ANDB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_AND_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op61(void)         // AIM ZP, X (AND immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_AND_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op71(void)         // AIM ZP (AND immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_AND_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Bit Test                |BITA |85 2 2|95 3 2|A5 5 2|B5 4 3|      |A+M       |  TTR |
+                        |BITB |C5 2 2|D5 3 2|E5 5 2|F5 4 3|      |B+M       |  TTR |
+*/
+
+// BIT opcodes
+
+#define OP_BIT_HANDLER(m, acc) \
+       int8_t result = acc & (m); \
+       SET_ZN(result)
+
+static void Op85(void)                                                 // BITA #
+{
+       uint8_t m = READ_IMM;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op95(void)                                                 // BITA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA5(void)                                                 // BITA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB5(void)                                                 // BITA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_BIT_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC5(void)                                                 // BITB #
+{
+       uint8_t m = READ_IMM;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD5(void)                                                 // BITB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE5(void)                                                 // BITB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF5(void)                                                 // BITB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_BIT_HANDLER(m, regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Clear                   |CLR  |      |      |6F 7 2|7F 6 3|      |M=00      |  RSRR|
+                        |CLRA |      |      |      |      |4F 2 1|A=00      |  RSRR|
+                        |CLRB |      |      |      |      |5F 2 1|B=00      |  RSRR|
+*/
+
+// CLR opcodes
+
+static void Op6F(void)                                                 // CLR ZP, X
+{
+       regs.WrMem(EA_ZP_X, 0);
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op7F(void)                                                 // CLR ABS
+{
+       regs.WrMem(EA_ABS, 0);
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op4F(void)                                                 // CLRA
+{
+       regs.d.acc.a = 0;
+       CLR_NVC;
+       SET_Z(0);
+}
+
+
+static void Op5F(void)                                                 // CLRB
+{
+       regs.d.acc.b = 0;
+       CLR_NVC;
+       SET_Z(0);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Compare                 |CMPA |81 2 2|91 3 2|A1 5 2|B1 4 3|      |A-M       |  TTTT|
+                        |CMPB |C1 2 2|D1 3 2|E1 5 2|F1 4 3|      |B-M       |  TTTT|
+Compare Accumulators    |CBA  |      |      |      |      |11 2 1|A-B       |  TTTT|
+*/
+
+// CMP opcodes
+
+/*
+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.
+*/
+
+#define OP_CMP_HANDLER(m, acc) \
+       uint16_t result = acc - (m); \
+       SET_ZNVC_CMP(m, acc, result)
+
+static void Op81(void)                                                 // CMPA #
+{
+       uint8_t m = READ_IMM;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op91(void)                                                 // CMPA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA1(void)                                                 // CMPA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB1(void)                                                 // CMPA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_CMP_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC1(void)                                                 // CMPB #
+{
+       uint8_t m = READ_IMM;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD1(void)                                                 // CMPB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE1(void)                                                 // CMPB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF1(void)                                                 // CMPB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_CMP_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op11(void)                                                 // CBA
+{
+       OP_CMP_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Complement 1's          |COM  |      |      |63 7 2|73 6 3|      |M=-M      |  TTRS|
+                        |COMA |      |      |      |      |43 2 1|A=-A      |  TTRS|
+                        |COMB |      |      |      |      |53 2 1|B=-B      |  TTRS|
+*/
+
+// COM opcodes
+
+#define OP_COM_HANDLER(m) \
+       m = m ^ 0xFF; \
+       SET_ZN(m); \
+       CLR_V; \
+       flagC = 1
+
+static void Op63(void)                                                 // COM ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_COM_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op73(void)                                                 // COM ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_COM_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op43(void)                                                 // COMA
+{
+       OP_COM_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op53(void)                                                 // COMB
+{
+       OP_COM_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Complement 2's          |NEG  |      |      |60 7 2|70 6 3|      |M=00-M    |  TT12|
+                        |NEGA |      |      |      |      |40 2 1|A=00-A    |  TT12|
+                        |NEGB |      |      |      |      |50 2 1|B=00-B    |  TT12|
+*/
+
+// NEG opcodes
+
+#define OP_NEG_HANDLER(m) \
+       m = -m; \
+       SET_ZN(m); \
+       flagV = (m == 0x80 ? 1 : 0); \
+       flagC = (m == 0x00 ? 1 : 0)
+
+static void Op60(void)                                                 // NEG ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_NEG_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op70(void)                                                 // NEG ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_NEG_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op40(void)                                                 // NEGA
+{
+       OP_NEG_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op50(void)                                                 // NEGB
+{
+       OP_NEG_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decimal Adjust          |DAA  |      |      |      |      |19 2 1|*         |  TTT3|
+*/
+
+static void Op19(void)                                                 // DAA
+{
+       uint16_t result = (uint16_t)regs.d.acc.a;
+
+       if ((regs.d.acc.a & 0x0F) > 0x09 || flagH)
+               result += 0x06;
+
+       if ((regs.d.acc.a & 0xF0) > 0x90 || flagC || ((regs.d.acc.a & 0xF0) > 0x80 && (regs.d.acc.a & 0x0F) > 0x09))
+               result += 0x60;
+
+       regs.d.acc.a = (uint8_t)result;
+       SET_ZN(result);
+       CLR_V;                                                                          // Not sure this is correct...
+       flagC |= (result & 0x100) >> 8;                         // Overwrite carry if it was 0, otherwise, ignore
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decrement               |DEC  |      |      |6A 7 2|7A 6 3|      |M=M-1     |  TT4 |
+                        |DECA |      |      |      |      |4A 2 1|A=A-1     |  TT4 |
+                        |DECB |      |      |      |      |5A 2 1|B=B-1     |  TT4 |
+*/
+
+// DEC opcodes
+
+#define OP_DEC_HANDLER(m) \
+       m--; \
+       SET_ZN(m); \
+       flagV = (m == 0x7F ? 1 : 0)
+
+static void Op6A(void)                                                 // DEC ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_DEC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op7A(void)                                                 // DEC ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_DEC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op4A(void)                                                 // DECA
+{
+       OP_DEC_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5A(void)                                                 // DECB
+{
+       OP_DEC_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Exclusive OR            |EORA |88 2 2|98 3 2|A8 5 2|B8 4 3|      |A=A(+)M   |  TTR |
+                        |EORB |C8 2 2|D8 3 2|E8 5 2|F8 4 3|      |B=B(+)M   |  TTR |
+*/
+
+// EOR opcodes
+
+#define OP_EOR_HANDLER(m, acc) \
+       acc ^= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op88(void)         // EORA #
+{
+       uint8_t m = READ_IMM;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op98(void)         // EORA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA8(void)         // EORA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB8(void)         // EORA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_EOR_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC8(void)         // EORB #
+{
+       uint8_t m = READ_IMM;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD8(void)         // EORB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE8(void)         // EORB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF8(void)         // EORB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_EOR_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op65(void)         // EIM ZP, X (EOR immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_EOR_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op75(void)         // EIM ZP (EOR immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_EOR_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Increment               |INC  |      |      |6C 7 2|7C 6 3|      |M=M+1     |  TT5 |
+                        |INCA |      |      |      |      |4C 2 1|A=A+1     |  TT5 |
+                        |INCB |      |      |      |      |5C 2 1|B=B+1     |  TT5 |
+*/
+
+// INC opcodes
+
+#define OP_INC_HANDLER(m) \
+       m++; \
+       SET_ZN(m); \
+       flagV = (m == 0x80 ? 1 : 0)
+
+static void Op6C(void)                                                 // INC ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_INC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op7C(void)                                                 // INC ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_INC_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op4C(void)                                                 // INCA
+{
+       OP_INC_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5C(void)                                                 // INCB
+{
+       OP_INC_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Load Accumulator        |LDAA |86 2 2|96 3 2|A6 5 2|B6 4 3|      |A=M       |  TTR |
+                        |LDAB |C6 2 2|D6 3 2|E6 5 2|F6 4 3|      |B=M       |  TTR |
+*/
+
+// LDA opcodes
+
+#define OP_LDA_HANDLER(m, acc) \
+       acc = m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op86(void)                                                 // LDAA #
+{
+       uint8_t m = READ_IMM;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op96(void)                                                 // LDAA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA6(void)                                                 // LDAA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB6(void)                                                 // LDAA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_LDA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC6(void)                                                 // LDAB #
+{
+       uint8_t m = READ_IMM;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD6(void)                                                 // LDAB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE6(void)                                                 // LDAB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF6(void)                                                 // LDAB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_LDA_HANDLER(m, regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+OR, Inclusive           |ORAA |8A 2 2|9A 3 2|AA 5 2|BA 4 3|      |A=A+M     |  TTR |
+                        |ORAB |CA 2 2|DA 3 2|EA 5 2|FA 4 3|      |B=B+M     |  TTR |
+*/
+
+// ORA opcodes
+
+#define OP_ORA_HANDLER(m, acc) \
+       acc |= m; \
+       SET_ZN(acc); \
+       CLR_V
+
+static void Op8A(void)         // ORAA #
+{
+       uint8_t m = READ_IMM;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op9A(void)         // ORAA ZP
+{
+       uint8_t m = READ_ZP;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpAA(void)         // ORAA ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpBA(void)         // ORAA ABS
+{
+       uint8_t m = READ_ABS;
+       OP_ORA_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpCA(void)         // ORAB #
+{
+       uint8_t m = READ_IMM;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpDA(void)         // ORAB ZP
+{
+       uint8_t m = READ_ZP;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpEA(void)         // ORAB ZP, X
+{
+       uint8_t m = READ_ZP_X;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpFA(void)         // ORAB ABS
+{
+       uint8_t m = READ_ABS;
+       OP_ORA_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op62(void)         // OIM ZP, X (ORA immediate with index)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_X_WB(m);
+       OP_ORA_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+static void Op72(void)         // OIM ZP (ORA immediate with zero page)
+{
+       uint8_t m;
+       uint8_t immValue = READ_IMM;
+       READ_ZP_WB(m);
+       OP_ORA_HANDLER(immValue, m);
+       WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Push Data               |PSHA |      |      |      |      |36 4 1|Msp=A, *- |      |
+                        |PSHB |      |      |      |      |37 4 1|Msp=B, *- |      |
+Pull Data               |PULA |      |      |      |      |32 4 1|A=Msp, *+ |      |
+                        |PULB |      |      |      |      |33 4 1|B=Msp, *+ |      |
+*/
+
+static void Op36(void)                                                 // PSHA
+{
+       PUSH(regs.d.acc.a);
+}
+
+
+static void Op37(void)                                                 // PSHB
+{
+       PUSH(regs.d.acc.b);
+}
+
+
+static void Op32(void)                                                 // PULA
+{
+       regs.d.acc.a = PULL;
+}
+
+
+static void Op33(void)                                                 // PULB
+{
+       regs.d.acc.b = PULL;
+}
+
+
+static void Op38(void)                                                 // PULX
+{
+       regs.x = PULL16;
+}
+
+
+static void Op3C(void)                                                 // PSHX
+{
+       PUSH16(regs.x);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Rotate Left             |ROL  |      |      |69 7 2|79 6 3|      |Memory  *1|  TT6T|
+                        |ROLA |      |      |      |      |49 2 1|Accum A *1|  TT6T|
+                        |ROLB |      |      |      |      |59 2 1|Accum B *1|  TT6T|
+*/
+
+// ROL opcodes
+
+#define OP_ROL_HANDLER(m) \
+       uint8_t newCarry = (m & 0x80) >> 7; \
+       m = (m << 1) | flagC; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op69(void)                                                 // ROL ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ROL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op79(void)                                                 // ROL ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ROL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op49(void)                                                 // ROLA
+{
+       OP_ROL_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op59(void)                                                 // ROLB
+{
+       OP_ROL_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Rotate Right            |ROR  |      |      |66 7 2|76 6 3|      |Memory  *2|  TT6T|
+                        |RORA |      |      |      |      |46 2 1|Accum A *2|  TT6T|
+                        |RORB |      |      |      |      |56 2 1|Accum B *2|  TT6T|
+*/
+
+// ROR opcodes
+
+#define OP_ROR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m = (m >> 1) | (flagC << 7); \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op66(void)                                                 // ROR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ROR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op76(void)                                                 // ROR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ROR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op46(void)                                                 // RORA
+{
+       OP_ROR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op56(void)                                                 // RORB
+{
+       OP_ROR_HANDLER(regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Arithmetic Shift Left   |ASL  |      |      |68 7 2|78 6 3|      |Memory  *3|  TT6T|
+                        |ASLA |      |      |      |      |48 2 1|Accum A *3|  TT6T|
+                        |ASLB |      |      |      |      |58 2 1|Accum B *3|  TT6T|
+*/
+
+// ASL opcodes
+
+#define OP_ASL_HANDLER(m) \
+       uint8_t newCarry = (m & 0x80) >> 7; \
+       m <<= 1; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op68(void)         // ASL ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ASL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op78(void)         // ASL ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ASL_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op48(void)         // ASLA
+{
+       OP_ASL_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op58(void)         // ASLB
+{
+       OP_ASL_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op05(void)         // ASLD
+{
+       uint8_t newCarry = (regs.d.word & 0x8000) >> 15;
+       regs.d.word <<= 1;
+       SET_Z(regs.d.word);
+       SET_N16(regs.d.word);
+       flagC = newCarry;
+       flagV = flagN ^ flagC;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Arithmetic Shift Right  |ASR  |      |      |67 7 2|77 6 3|      |Memory  *4|  TT6T|
+                        |ASRA |      |      |      |      |47 2 1|Accum A *4|  TT6T|
+                        |ASRB |      |      |      |      |57 2 1|Accum B *4|  TT6T|
+*/
+
+// ASR opcodes
+
+#define OP_ASR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m = (m >> 1) | (m & 0x80); \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op67(void)         // ASR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_ASR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op77(void)         // ASR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_ASR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op47(void)         // ASRA
+{
+       OP_ASR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op57(void)         // ASRB
+{
+       OP_ASR_HANDLER(regs.d.acc.b);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Logic Shift Right       |LSR  |      |      |64 7 2|74 6 3|      |Memory  *5|  TT6T|
+                        |LSRA |      |      |      |      |44 2 1|Accum A *5|  TT6T|
+                        |LSRB |      |      |      |      |54 2 1|Accum B *5|  TT6T|
+*/
+
+// LSR opcodes
+
+#define OP_LSR_HANDLER(m) \
+       uint8_t newCarry = m & 0x01; \
+       m >>= 1; \
+       SET_ZN(m); \
+       flagC = newCarry; \
+       flagV = flagN ^ flagC
+
+static void Op64(void)         // LSR ZP, X
+{
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_LSR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op74(void)         // LSR ABS
+{
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_LSR_HANDLER(m);
+       WRITE_BACK(m);
+}
+
+
+static void Op44(void)         // LSRA
+{
+       OP_LSR_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op54(void)         // LSRB
+{
+       OP_LSR_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op04(void)         // LSRD
+{
+       uint8_t newCarry = regs.d.word & 0x01;
+       regs.d.word >>= 1;
+       SET_Z(regs.d.word);
+       flagN = 0;
+       flagC = newCarry;
+       flagV = flagN ^ flagC;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Store Accumulator       |STAA |      |97 4 2|A7 6 2|B7 5 3|      |M=A       |  TTR |
+                        |STAB |      |D7 4 2|E7 6 2|F7 5 3|      |M=B       |  TTR |
+*/
+
+static void Op97(void)         // STAA ZP
+{
+       regs.WrMem(EA_ZP, regs.d.acc.a);
+}
+
+
+static void OpA7(void)         // STAA ZP, X
+{
+       regs.WrMem(EA_ZP_X, regs.d.acc.a);
+}
+
+
+static void OpB7(void)         // STAA ABS
+{
+       regs.WrMem(EA_ABS, regs.d.acc.a);
+}
+
+
+static void OpD7(void)         // STAB ZP
+{
+       regs.WrMem(EA_ZP, regs.d.acc.b);
+}
+
+
+static void OpE7(void)         // STAB ZP, X
+{
+       regs.WrMem(EA_ZP_X, regs.d.acc.b);
+}
+
+
+static void OpF7(void)         // STAB ABS
+{
+       regs.WrMem(EA_ABS, regs.d.acc.b);
+}
+
+
+// These are illegal instructions!
+#if 0
+static void Op87(void)         // STA #
+{
+       // What does this even mean? Seems the M.A.M.E. guys think it's something
+       // like so:
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc++;
+       regs.WrMem(effectiveAddress, regs.d.acc.a);
+       SET_ZN(regs.d.acc.a);
+       flagV = 0;
+       // So, basically, what this does is change the immediate value in the
+       // STA #. Seems like a completely useless thing to me.
+}
+
+
+static void OpC7(void)         // STB #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc++;
+       regs.WrMem(effectiveAddress, regs.d.acc.b);
+       SET_ZN(regs.d.acc.b);
+       flagV = 0;
+}
+#endif
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Subtract                |SUBA |80 2 2|90 3 2|A0 5 2|B0 4 3|      |A=A-M     |  TTTT|
+                        |SUBB |C0 2 2|D0 3 2|E0 5 2|F0 4 3|      |B=B-M     |  TTTT|
+Subtract Accumulators   |SBA  |      |      |      |      |10 2 1|A=A-B     |  TTTT|
+*/
+
+// SUB opcodes
+
+#define OP_SUB_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc - (m); \
+       flagC = sum >> 15; \
+       SET_V(m, acc, sum); \
+       acc = (uint8_t)sum; \
+       SET_ZN(acc)
+
+#define OP_SUB_HANDLER16(m, acc) \
+       uint32_t sum = (uint32_t)acc - (m); \
+       flagC = sum >> 31; \
+       SET_V16(m, acc, sum); \
+       acc = (uint16_t)sum; \
+       SET_Z(acc); \
+       SET_N16(acc)
+
+static void Op80(void)         // SUBA #
+{
+       uint16_t m = READ_IMM;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op90(void)         // SUBA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA0(void)         // SUBA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB0(void)         // SUBA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SUB_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC0(void)         // SUBB #
+{
+       uint16_t m = READ_IMM;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD0(void)         // SUBB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE0(void)         // SUBB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF0(void)         // SUBB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SUB_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void Op10(void)         // SBA
+{
+       OP_SUB_HANDLER(regs.d.acc.b, regs.d.acc.a);
+}
+
+
+static void Op83(void)         // SUBD #
+{
+       uint16_t m = READ_IMM16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void Op93(void)         // SUBD ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpA3(void)         // SUBD ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+static void OpB3(void)         // SUBD ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_SUB_HANDLER16(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Subtract with Carry     |SBCA |82 2 2|92 3 2|A2 5 2|B2 4 3|      |A=A-M-C   |  TTTT|
+                        |SBCB |C2 2 2|D2 3 2|E2 5 2|F2 4 3|      |B=B-M-C   |  TTTT|
+*/
+
+// SBC opcodes
+
+#define OP_SBC_HANDLER(m, acc) \
+       uint16_t sum = (uint16_t)acc - (m) - (uint16_t)flagC; \
+       flagC = sum >> 15; \
+       SET_V(m, acc, sum); \
+       acc = (uint8_t)sum; \
+       SET_ZN(acc)
+
+static void Op82(void)                                                 // SBCA #
+{
+       uint16_t m = READ_IMM;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void Op92(void)                                                 // SBCA ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpA2(void)                                                 // SBCA ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpB2(void)                                                 // SBCA ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SBC_HANDLER(m, regs.d.acc.a);
+}
+
+
+static void OpC2(void)                                                 // SBCB #
+{
+       uint16_t m = READ_IMM;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpD2(void)                                                 // SBCB ZP
+{
+       uint16_t m = READ_ZP;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpE2(void)                                                 // SBCB ZP, X
+{
+       uint16_t m = READ_ZP_X;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+
+static void OpF2(void)                                                 // SBCB ABS
+{
+       uint16_t m = READ_ABS;
+       OP_SBC_HANDLER(m, regs.d.acc.b);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Transfer Accumulators   |TAB  |      |      |      |      |16 2 1|B=A       |  TTR |
+                        |TBA  |      |      |      |      |17 2 1|A=B       |  TTR |
+*/
+
+static void Op16(void)                                                 // TAB
+{
+       regs.d.acc.b = regs.d.acc.a;
+       SET_ZN(regs.d.acc.b);
+       CLR_V;
+}
+
+
+static void Op17(void)                                                 // TBA
+{
+       regs.d.acc.a = regs.d.acc.b;
+       SET_ZN(regs.d.acc.a);
+       CLR_V;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Test, Zero/Minus        |TST  |      |      |6D 7 2|7D 6 3|      |M-00      |  TTRR|
+                        |TSTA |      |      |      |      |4D 2 1|A-00      |  TTRR|
+                        |TSTB |      |      |      |      |5D 2 1|B-00      |  TTRR|
+*/
+
+// TST opcodes
+
+#define OP_TST_HANDLER(m) \
+       uint8_t result = m; \
+       SET_ZN(result); \
+       CLR_VC
+
+#define OP_TST_IMM_HANDLER(m, acc) \
+       uint8_t result = m & acc; \
+       SET_ZN(result); \
+       CLR_VC
+
+static void Op6D(void)                                                 // TST ZP, X
+{
+#if 0
+       uint8_t m;
+       READ_ZP_X_WB(m);
+       OP_TST_HANDLER(m);
+       WRITE_BACK(m);
+#else
+       OP_TST_HANDLER(READ_ZP_X);
+#endif
+}
+
+
+static void Op7D(void)                                                 // TST ABS
+{
+#if 0
+       uint8_t m;
+       READ_ABS_WB(m);
+       OP_TST_HANDLER(m);
+       WRITE_BACK(m);
+#else
+       OP_TST_HANDLER(READ_ABS);
+#endif
+}
+
+
+static void Op4D(void)                                                 // TSTA
+{
+       OP_TST_HANDLER(regs.d.acc.a);
+}
+
+
+static void Op5D(void)                                                 // TSTB
+{
+       OP_TST_HANDLER(regs.d.acc.b);
+}
+
+
+static void Op6B(void)         // TIM ZP, X (TST immediate with index)
+{
+//     uint8_t m;
+       uint8_t immValue = READ_IMM;
+//     READ_ZP_X_WB(m);
+       OP_TST_IMM_HANDLER(immValue, READ_ZP_X);
+//     WRITE_BACK(m);
+}
+
+
+static void Op7B(void)         // TIM ZP (TST immediate with zero page)
+{
+//     uint8_t m;
+       uint8_t immValue = READ_IMM;
+//     READ_ZP_WB(m);
+       OP_TST_IMM_HANDLER(immValue, READ_ZP);
+//     WRITE_BACK(m);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Compare Index Register  |CPX  |8C 3 3|9C 4 2|AC 6 2|BC 5 3|      |Formula 1 |  7T8 |
+*/
+
+// CPX opcodes
+
+/*
+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.
+*/
+
+#define OP_CPX_HANDLER(m) \
+       uint32_t result = regs.x - (m); \
+       SET_ZNVC_CMP16(m, regs.x, result)
+
+static void Op8C(void)                                                 // CPX #
+{
+       uint16_t m = READ_IMM16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void Op9C(void)                                                 // CPX ZP
+{
+       uint16_t m = READ_ZP16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void OpAC(void)                                                 // CPX ZP, X
+{
+       uint16_t m = READ_ZP_X16;
+       OP_CPX_HANDLER(m);
+}
+
+
+static void OpBC(void)                                                 // CPX ABS
+{
+       uint16_t m = READ_ABS16;
+       OP_CPX_HANDLER(m);
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Decrement Index Register|DEX  |      |      |      |      |09 4 1|X=X-1     |   T  |
+Dec Stack Pointer       |DES  |      |      |      |      |34 4 1|SP=SP-1   |      |
+Inc Index Regster       |INX  |      |      |      |      |08 4 1|X=X+1     |   T  |
+Inc Stack Pointer       |INS  |      |      |      |      |31 4 1|SP=SP+1   |      |
+*/
+
+static void Op09(void)                                                 // DEX
+{
+       regs.x--;
+       SET_Z(regs.x);
+}
+
+
+static void Op34(void)                                                 // DES
+{
+       regs.s--;
+}
+
+
+static void Op08(void)                                                 // INX
+{
+       regs.x++;
+       SET_Z(regs.x);
+}
+
+
+static void Op31(void)                                                 // INS
+{
+       regs.s++;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Load Index Register     |LDX  |CE 3 3|DE 4 2|EE 6 2|FE 5 3|      |Formula 2 |  9TR |
+Load Stack Pointer      |LDS  |8E 3 3|9E 4 2|AE 6 2|BE 5 3|      |Formula 3 |  9TR |
+*/
+
+// LD* opcode handler
+
+#define OP_LD_HANDLER(acc) \
+       SET_N16(acc); \
+       SET_Z(acc); \
+       CLR_V
+
+static void OpCE(void)         // LDX #
+{
+       regs.x = READ_IMM16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpDE(void)         // LDX ZP
+{
+       regs.x = READ_ZP16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpEE(void)         // LDX ZP, X
+{
+       regs.x = READ_ZP_X16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void OpFE(void)         // LDX ABS
+{
+       regs.x = READ_ABS16;
+       OP_LD_HANDLER(regs.x);
+}
+
+
+static void Op8E(void)         // LDS #
+{
+       regs.s = READ_IMM16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void Op9E(void)         // LDS ZP
+{
+       regs.s = READ_ZP16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpAE(void)         // LDS ZP, X
+{
+       regs.s = READ_ZP_X16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpBE(void)         // LDS ABS
+{
+       regs.s = READ_ABS16;
+       OP_LD_HANDLER(regs.s);
+}
+
+
+static void OpCC(void)         // LDD #
+{
+       regs.d.word = READ_IMM16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpDC(void)         // LDD ZP
+{
+       regs.d.word = READ_ZP16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpEC(void)         // LDD ZP, X
+{
+       regs.d.word = READ_ZP_X16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+static void OpFC(void)         // LDD ABS
+{
+       regs.d.word = READ_ABS16;
+       OP_LD_HANDLER(regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Store Index Register    |STX  |      |DF 5 2|EF 7 2|FF 6 3|      |Formula 4 |  9TR |
+Store Stack Pointer     |STS  |      |9F 5 2|AF 7 2|BF 6 3|      |Formula 5 |  9TR |
+*/
+
+// ST* opcode handler
+
+#define OP_ST_HANDLER(m, acc) \
+       regs.WrMem(m + 0, acc >> 8); \
+       regs.WrMem(m + 1, acc & 0xFF); \
+       SET_N16(acc); \
+       SET_Z(acc); \
+       CLR_V
+
+static void OpDF(void)         // STX ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void OpEF(void)         // STX ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void OpFF(void)         // STX ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.x);
+}
+
+
+static void Op9F(void)         // STS ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+static void OpAF(void)         // STS ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+static void OpBF(void)         // STS ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.s);
+}
+
+
+// These are illegal instructions!
+#if 0
+// Store immediate--nonsensical opcodes :-P
+static void Op8F(void)         // STS #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.s);
+}
+
+
+static void OpCF(void)         // STX #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.x);
+}
+
+
+static void OpCD(void)         // STD #
+{
+       uint16_t effectiveAddress = regs.pc;
+       regs.pc += 2;
+       OP_ST_HANDLER(effectiveAddress, regs.d.word);
+}
+#endif
+
+
+static void OpDD(void)         // STD ZP
+{
+       uint16_t m = EA_ZP;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+static void OpED(void)         // STD ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+static void OpFD(void)         // STD ABS
+{
+       uint16_t m = EA_ABS;
+       OP_ST_HANDLER(m, regs.d.word);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Index Reg > Stack Pnter |TXS  |      |      |      |      |35 4 1|SP=X-1    |      |
+Stack Ptr > Index Regtr |TSX  |      |      |      |      |30 4 1|X=SP+1    |      |
+*/
+
+static void Op35(void)                                                 // TXS
+{
+       regs.s = regs.x - 1;
+}
+
+
+static void Op30(void)                                                 // TSX
+{
+       regs.x = regs.s + 1;
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Always                  |BRA  |      |20 4 2|      |      |      |none      |      |
+Carry is Clear          |BCC  |      |24 4 2|      |      |      |C=0       |      |
+Carry is Set            |BCS  |      |25 4 2|      |      |      |C=1       |      |
+Equals Zero             |BEQ  |      |27 4 2|      |      |      |Z=1       |      |
+Greater or Equal to Zero|BGE  |      |2C 4 2|      |      |      |N(+)V=0   |      |
+Greater than Zero       |BGT  |      |2E 4 2|      |      |      |Z+N(+)V=0 |      |
+Higher                  |BHI  |      |22 4 2|      |      |      |C+Z=0     |      |
+Less or Equal than Zero |BLE  |      |2F 4 2|      |      |      |Z+N(+)V=1 |      |
+Lower or Same           |BLS  |      |23 4 2|      |      |      |C+Z=1     |      |
+Less Than Zero          |BLT  |      |2D 4 2|      |      |      |N(+)V=1   |      |
+Minus                   |BMI  |      |2B 4 2|      |      |      |N=1       |      |
+Not Zero                |BNE  |      |26 4 2|      |      |      |Z=0       |      |
+Overflow Clear          |BVC  |      |28 4 2|      |      |      |V=0       |      |
+Overflow Set            |BVS  |      |29 4 2|      |      |      |V=1       |      |
+Plus                    |BPL  |      |2A 4 2|      |      |      |N=0       |      |
+*/
+
+static void Op20(void)         // BRA
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+       regs.pc += m;
+}
+
+
+static void Op21(void)         // BRN
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+//     regs.pc += m;
+}
+
+
+static void Op24(void)         // BCC
+{
+// NOTE: We can optimize this by following the maxim: "Don't branch!" by
+//       converting the boolean result into a multiplication. The only way to
+//       know if this is a win is to do some profiling both with and without
+//       the optimization.
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+//Note sure if the ! operator will do what we want, so we use ^ 1
+       regs.pc += m * (flagC ^ 0x01);
+#else
+       if (!flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op25(void)         // BCS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagC);
+#else
+       if (flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op27(void)         // BEQ
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ);
+#else
+       if (flagZ)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2C(void)         // BGE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagN ^ flagV) ^ 0x01);
+#else
+       if (!(flagN ^ flagV))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2E(void)         // BGT
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagZ | (flagN ^ flagV)) ^ 0x01);
+#else
+       if (!(flagZ | (flagN ^ flagV)))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op22(void)         // BHI
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * ((flagZ | flagC) ^ 0x01);
+#else
+       if (!(flagZ | flagC))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2F(void)         // BLE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ | (flagN ^ flagV));
+#else
+       if (flagZ | (flagN ^ flagV))
+               regs.pc += m;
+#endif
+}
+
+
+static void Op23(void)         // BLS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ | flagC);
+#else
+       if (flagZ | flagC)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2D(void)         // BLT
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN ^ flagV);
+#else
+       if (flagN ^ flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2B(void)         // BMI
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN);
+#else
+       if (flagN)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op26(void)         // BNE
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagZ ^ 0x01);
+#else
+       if (!flagZ)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op28(void)         // BVC
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagV ^ 0x01);
+#else
+       if (!flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op29(void)         // BVS
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagV);
+#else
+       if (flagV)
+               regs.pc += m;
+#endif
+}
+
+
+static void Op2A(void)         // BPL
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+
+#ifdef TEST_DONT_BRANCH_OPTIMIZATION
+       regs.pc += m * (flagN ^ 0x01);
+#else
+       if (!flagN)
+               regs.pc += m;
+#endif
+}
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Branch to Subroutine    |BSR  |      |8D 8 2|      |      |      |          |      |
+Jump                    |JMP  |      |      |6E 4 2|7E 3 3|      |          |      |
+Jump to Subroutine      |JSR  |      |      |AD 8 2|BD 9 3|      |          |      |
+*/
+
+static void Op8D(void)         // BSR
+{
+       int16_t m = (int16_t)(int8_t)READ_IMM;
+       PUSH16(regs.pc);
+       regs.pc += m;
+}
+
+
+static void Op6E(void)         // JMP ZP, X
+{
+       regs.pc = EA_ZP_X;
+}
+
+
+static void Op7E(void)         // JMP ABS
+{
+       regs.pc = EA_ABS;
+}
+
+
+static void Op9D(void)         // JSR ZP
+{
+       uint16_t m = (uint16_t)EA_ZP;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+static void OpAD(void)         // JSR ZP, X
+{
+       uint16_t m = EA_ZP_X;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+static void OpBD(void)         // JSR ABS
+{
+       uint16_t m = EA_ABS;
+       PUSH16(regs.pc);
+       regs.pc = m;
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+No Operation            |NOP  |      |      |      |      |01 2 1|          |      |
+Return from Interrupt   |RTI  |      |      |      |      |3B A 1|          |AAAAAA|
+Return from Subroutine  |RTS  |      |      |      |      |39 5 1|          |      |
+Software Interrupt      |SWI  |      |      |      |      |3F C 1|          | S    |
+Wait For Interrupt      |WAI  |      |      |      |      |3E 9 1|          | B    |
+*/
+
+static void Op01(void)         // NOP
+{
+}
+
+
+static void Op3B(void)         // RTI
+{
+       regs.cc = PULL;
+       regs.d.acc.b  = PULL;
+       regs.d.acc.a  = PULL;
+       regs.x  = PULL16;
+       regs.pc = PULL16;
+       UNPACK_FLAGS;
+}
+
+
+static void Op39(void)         // RTS
+{
+       regs.pc = PULL16;
+}
+
+
+static void Op3F(void)         // SWI
+{
+       // It seems that the SWI is non-maskable, unlike the IRQ...
+#if 0
+       regs.cc = PACK_FLAGS;                   // Mash flags back into the CC register
+       PUSH16(regs.pc);                                // Save all regs...
+       PUSH16(regs.x);
+       PUSH(regs.d.acc.b);
+       PUSH(regs.d.acc.a);
+       PUSH(regs.cc);
+       regs.pc = RdMemW(0xFFFA);               // And do it!
+       flagI = 1;                                              // Also, set IRQ inhibit
+#endif
+       HandleInterrupt(0xFFFA);
+}
+
+
+static void Op3E(void)         // WAI
+{
+#ifdef __DEBUG__
+WriteLog("*** WAI STATE ASSERTED ***\n");
+#endif
+       regs.cc = PACK_FLAGS;                   // Mash flags back into the CC register
+       PUSH16(regs.pc);                                // Save all regs...
+       PUSH16(regs.x);
+       PUSH(regs.d.acc.b);
+       PUSH(regs.d.acc.a);
+       PUSH(regs.cc);
+       regs.cpuFlags |= V63701_STATE_WAI;      // And signal that we're in WAI mode
+}
+
+
+// Multiply opcode
+
+static void Op3D(void)         // MUL
+{
+       regs.d.word = (uint16_t)regs.d.acc.a * (uint16_t)regs.d.acc.b;
+       flagC = regs.d.acc.b >> 7;      // bug?  No, this is how it really does it
+}
+
+
+// Exchange X and D opcode
+
+static void Op18(void)         // XGDX
+{
+       // This doesn't affect flags apparently
+       uint16_t temp = regs.x;
+       regs.x = regs.d.word;
+       regs.d.word = temp;
+}
+
+
+// Sleep opcode (similar to WAI)
+
+static void Op1A(void)         // SLP
+{
+       // Apparently doesn't stack the state, so no need for RTI
+       // It would seem that this opcode is intended for use in interrupt
+       // routines, as it prevents stacking the state when one happens.
+       // (Actually, this is used to put the MCU into a low power "sleep" mode,
+       // where it stays until an interrupt or RESET occurs.)
+       regs.cpuFlags |= V63701_STATE_WAI;
+}
+
+
+// Undocumented opcode ($12 & $13)
+
+static void OpUN(void)         // Undocumented
+{
+       regs.x += regs.RdMem(regs.s + 1);
+}
+
+
+/*
+Operation               |Mnem.|Immed.|Direct|Index |Extend|Inher.|Operation |CC Reg|
+                        |     |OP ~ #|OP ~ #|OP ~ #|OP ~ #|OP ~ #|          |HINZVC|
+Clear Carry             |CLC  |      |      |      |      |0C 2 1|C=0       |     R|
+Clear Interrupt         |CLI  |      |      |      |      |0E 2 1|I=0       | R    |
+Clear Overflow          |CLV  |      |      |      |      |0A 2 1|V=0       |    R |
+Set Carry               |SEC  |      |      |      |      |0D 2 1|C=1       |     S|
+Set Interrupt           |SEI  |      |      |      |      |0F 2 1|I=1       | S    |
+Set Overflow            |SEV  |      |      |      |      |0B 2 1|V=1       |    S |
+CCR=Accumulator A       |TAP  |      |      |      |      |06 2 1|CCR=A     |CCCCCC|
+Accumlator A=CCR        |TPA  |      |      |      |      |07 2 1|A=CCR     |      |
+*/
+
+static void Op0C(void)                                                 // CLC
+{
+       flagC = 0;
+}
+
+
+static void Op0E(void)                                                 // CLI
+{
+       flagI = 0;
+}
+
+
+static void Op0A(void)                                                 // CLV
+{
+       flagV = 0;
+}
+
+
+static void Op0D(void)                                                 // SEC
+{
+       flagC = 1;
+}
+
+
+static void Op0F(void)                                                 // SEI
+{
+       flagI = 1;
+}
+
+
+static void Op0B(void)                                                 // SEV
+{
+       flagV = 1;
+}
+
+
+static void Op06(void)                                                 // TAP
+{
+       regs.cc = regs.d.acc.a;
+       UNPACK_FLAGS;
+}
+
+
+static void Op07(void)                                                 // TPA
+{
+       regs.d.acc.a = PACK_FLAGS;
+}
+
+
+/*
+  OP  Operation Code, in Hexadecimal
+  ~   Number of MPU cycles required
+  #   Number of program bytes required
+  +   Arithmetic Plus
+  -   Arithmetic Minus
+  +   Boolean AND
+  Msp Contents of Memory pointed to be Stack Pointer
+  +   Boolean Inclusive OR
+  (+) Boolean Exclusive OR (XOR)
+  *   Converts Binary Addition of BCD Characters into BCD Format
+  *-  SP=SP-1
+  *+  SP=SP+1
+
+  Condition Code Register Legend
+     Not Affected
+   R Reset (0, Low)
+   S Set   (1, High)
+   T Tests and sets if True, cleared otherise
+   1 Test: Result=10000000?
+   2 Test: Result=00000000?
+   3 Test: Decimal value of most significant BCD character greater than nine?
+           (Not cleared if previously set)
+   4 Test: Operand=10000000 prior to execution?
+   5 Test: Operand=01111111 prior to execution?
+   6 Test: Set equal to result or N(+)C after shift has occurred.
+   7 Test: Sign bit of most significant byte or result=1?
+   8 Test: 2's compliment overflow from subtraction of least
+           significant bytes?
+   9 Test: Result less than zero? (Bit 15=1)
+   A Load Condition Code Register from Stack.
+   B Set when interrupt occurs.  If previously set, a NMI is
+        required to exit the wait state.
+   C Set according to the contents of Accumulator A.
+
+  *x SHIFT AND ROTATION DIAGRAMS
+   *1     +-----------------+       C to LSB
+          - C <- 76543210 <-+
+
+   *2    +-----------------+
+         +> C -> 76543210 -+
+
+   *3       C <- 76543210 <- 0(Data)
+               +-+
+
+   *4          �>76543210 -> C
+
+   *5 (Data)0 -> 76543210 -> C
+
+  FORMULAS
+   1: (Xh/Xl)-(M/M+1)
+   2: Xh=M, Xl=M+1
+   3: SPh=M, SPl=M+1
+   4: M=Xh, M+1=Xl
+*/
+
+
+static void Op__(void)
+{
+       // TRAP is non-maskable, unlike the IRQ... Also, highest priority after
+       // RESET
+       HandleInterrupt(0xFFEE);
+//     regs.cpuFlags |= V63701_STATE_ILLEGAL_INST;
+}
+
+
+//
+// Ok, the exec_op[] array is globally defined here basically to save
+// a LOT of unnecessary typing.  Sure it's ugly, but hey, it works!
+//
+void (* exec_op[256])() = {
+       Op__, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op0B, Op0C, Op0D, Op0E, Op0F,
+       Op10, Op11, OpUN, OpUN, Op__, Op__, Op16, Op17, Op18, Op19, Op1A, Op1B, Op__, Op__, Op__, Op__,
+       Op20, Op21, Op22, Op23, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op2B, Op2C, Op2D, Op2E, Op2F,
+       Op30, Op31, Op32, Op33, Op34, Op35, Op36, Op37, Op38, Op39, Op3A, Op3B, Op3C, Op3D, Op3E, Op3F,
+       Op40, Op__, Op__, Op43, Op44, Op__, Op46, Op47, Op48, Op49, Op4A, Op__, Op4C, Op4D, Op__, Op4F,
+       Op50, Op__, Op__, Op53, Op54, Op__, Op56, Op57, Op58, Op59, Op5A, Op__, Op5C, Op5D, Op__, Op5F,
+       Op60, Op61, Op62, Op63, Op64, Op65, Op66, Op67, Op68, Op69, Op6A, Op6B, Op6C, Op6D, Op6E, Op6F,
+       Op70, Op71, Op72, Op73, Op74, Op75, Op76, Op77, Op78, Op79, Op7A, Op7B, Op7C, Op7D, Op7E, Op7F,
+       Op80, Op81, Op82, Op83, Op84, Op85, Op86, Op__, Op88, Op89, Op8A, Op8B, Op8C, Op8D, Op8E, Op__,
+       Op90, Op91, Op92, Op93, Op94, Op95, Op96, Op97, Op98, Op99, Op9A, Op9B, Op9C, Op9D, Op9E, Op9F,
+       OpA0, OpA1, OpA2, OpA3, OpA4, OpA5, OpA6, OpA7, OpA8, OpA9, OpAA, OpAB, OpAC, OpAD, OpAE, OpAF,
+       OpB0, OpB1, OpB2, OpB3, OpB4, OpB5, OpB6, OpB7, OpB8, OpB9, OpBA, OpBB, OpBC, OpBD, OpBE, OpBF,
+       OpC0, OpC1, OpC2, OpC3, OpC4, OpC5, OpC6, Op__, OpC8, OpC9, OpCA, OpCB, OpCC, Op__, OpCE, Op__,
+       OpD0, OpD1, OpD2, OpD3, OpD4, OpD5, OpD6, OpD7, OpD8, OpD9, OpDA, OpDB, OpDC, OpDD, OpDE, OpDF,
+       OpE0, OpE1, OpE2, OpE3, OpE4, OpE5, OpE6, OpE7, OpE8, OpE9, OpEA, OpEB, OpEC, OpED, OpEE, OpEF,
+       OpF0, OpF1, OpF2, OpF3, OpF4, OpF5, OpF6, OpF7, OpF8, OpF9, OpFA, OpFB, OpFC, OpFD, OpFE, OpFF
+};
+
+
+//
+// Internal "memcpy" (so we don't have to link with any external libraries!)
+//
+static void myMemcpy(void * dst, void * src, uint32_t size)
+{
+       uint8_t * d = (uint8_t *)dst, * s = (uint8_t *)src;
+
+       for(uint32_t i=0; i<size; i++)
+               d[i] = s[i];
+}
+
+
+#ifdef __DEBUG__
+//int instCount[256];
+//static bool logGo = false;
+static bool logGo = true;
+char instBuf[256];
+extern uint8_t memory[];
+#endif
+//
+// Function to execute 63701 for "cycles" cycles
+//
+void Execute63701(V63701REGS * context, uint32_t cycles)
+{
+//#warning "V63701_STATE_WAI is not properly handled yet! !!! FIX !!!"
+//#warning "Need to convert from destructive clock to non-destructive. !!! FIX !!!"
+       regsPointer = context;
+       myMemcpy(&regs, context, sizeof(V63701REGS));
+       // Explode flags register into individual uint8_ts
+       UNPACK_FLAGS;
+
+#if 0
+       // Execute here...
+       while (regs.clock < cycles)
+#else
+       // Execute here...
+       uint64_t endCycles = regs.clock + (uint64_t)cycles - regs.clockOverrun;
+
+       while (regs.clock < endCycles)
+#endif
+       {
+#ifdef __DEBUG__
+if (logGo)
+{
+       Decode63701(memory, regs.pc, instBuf);
+       WriteLog("%s\n", instBuf);
+}
+#endif
+               if (regs.cpuFlags & V63701_STATE_WAI)
+               {
+                       // Only bail out if no interrupts/resets are pending
+                       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)))
+                               // Burn any remaining cycles...
+                               regs.clock = endCycles;
+               }
+               else
+               {
+                       uint8_t opcode = regs.RdMem(regs.pc++);
+                       exec_op[opcode]();                                              // Execute that opcode...
+                       regs.clock += CPUCycles[opcode];
+                       uint16_t oldCounter = regs.counter.word;
+                       regs.counter.word += CPUCycles[opcode];
+
+                       // We fake the free running counter above, by adding cycle counts
+                       // to it; this means we can never be sure that the counter will
+                       // be equal to the output compare register. So we have to check
+                       // for two cases: 1st, look for the previous counter value and see
+                       // if we went past it, and 2nd, do the same but check to see if we
+                       // overflowed in the meantime.
+                       if (((oldCounter < regs.outputCompare.word)
+                               && (regs.counter.word >= regs.outputCompare.word))
+                               || ((oldCounter > regs.counter.word)
+                               && (regs.counter.word >= regs.outputCompare.word)))
+                       {
+                               // Set the output compare flag bit
+                               regs.tcsr.bit.ocf = 1;
+
+                               if (regs.tcsr.bit.eoci)
+                               {
+                                       regs.cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
+                                       regsPointer->cpuFlags |= V63701_ASSERT_OUTPUT_COMPARE;
+                               }
+                       }
+
+                       // Check for counter overflow
+                       if (regs.counter.word < oldCounter)
+                       {
+                               // Set the timer overflow flag bit
+                               regs.tcsr.bit.tof = 1;
+
+                               if (regs.tcsr.bit.etoi)
+                               {
+                                       regs.cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
+                                       regsPointer->cpuFlags |= V63701_ASSERT_TIMER_OVERFLOW;
+                               }
+                       }
+#ifdef __DEBUG__
+if (logGo)
+//     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" : " "));
+       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);
+#endif
+               }
+
+               if (regs.cpuFlags & V63701_ASSERT_LINE_RESET)
+               {
+#ifdef __DEBUG__
+WriteLog("*** RESET LINE ASSERTED ***\n");
+#endif
+                       regs.tcsr.byte = 0;
+                       regs.tcsrWasRead = false;
+                       regs.counter.word = 0;
+                       regs.outputCompare.word = 0xFFFF;
+                       flagI = 1;                                      // Set I
+                       regs.pc = RdMemW(0xFFFE);       // And load PC with the RESET vector
+                       context->cpuFlags = 0;          // Clear all lingering flags...
+                       regs.cpuFlags = 0;
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_LINE_NMI)
+               {
+#ifdef __DEBUG__
+WriteLog("*** NMI LINE ASSERTED ***\n");
+#endif
+                       HandleInterrupt(0xFFFC, V63701_ASSERT_LINE_NMI);
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_LINE_IRQ)
+               {
+#ifdef __DEBUG__
+WriteLog("*** IRQ LINE ASSERTED ***\n");
+#endif
+//                     if (!(regs.cc & FLAG_I))                        // Process an interrupt (I=0)?
+                       if (!flagI)                                                     // Process an interrupt (I=0)?
+                       {
+#ifdef __DEBUG__
+WriteLog("    IRQ TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF8, V63701_ASSERT_LINE_IRQ);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_INPUT_CAPTURE)
+               {
+#ifdef __DEBUG__
+WriteLog("*** INPUT CAPTURE ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.eici)
+                       {
+#ifdef __DEBUG__
+WriteLog("    IC TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF6, V63701_ASSERT_INPUT_CAPTURE);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_OUTPUT_COMPARE)
+               {
+#ifdef __DEBUG__
+WriteLog("*** OUTPUT COMPARE ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.eoci)
+                       {
+#ifdef __DEBUG__
+WriteLog("    OC TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF4, V63701_ASSERT_OUTPUT_COMPARE);
+                       }
+               }
+               else if (regs.cpuFlags & V63701_ASSERT_TIMER_OVERFLOW)
+               {
+#ifdef __DEBUG__
+WriteLog("*** TIMER OVER ASSERTED ***\n");
+#endif
+                       // Process interrupt if no I inhibit set, & enable in TCSR is set
+                       if (!flagI && regs.tcsr.bit.etoi)
+                       {
+#ifdef __DEBUG__
+WriteLog("    TO TAKEN!\n");
+logGo = true;
+#endif
+                               HandleInterrupt(0xFFF2, V63701_ASSERT_TIMER_OVERFLOW);
+                       }
+               }
+       }
+
+       // If we went longer than the passed in cycles, make a note of it so we can
+       // subtract it out from a subsequent run. It's guaranteed to be positive,
+       // because the condition that exits the main loop above is written such
+       // that regs.clock has to be larger than endCycles to exit from it.
+       regs.clockOverrun = regs.clock - endCycles;
+
+       regs.cc = PACK_FLAGS;                                           // Mash flags back into the CC register
+       myMemcpy(context, &regs, sizeof(V63701REGS));
+}
+
+
+//
+// Get the clock of the currently executing CPU
+//
+uint64_t GetCurrentV63701Clock(void)
+{
+       return regs.clock;
+}
+
+
+static inline void HandleInterrupt(uint16_t vector, uint16_t flag/*= 0*/)
+{
+       if (regs.cpuFlags & V63701_STATE_WAI)
+               regs.clock += 4;
+       else
+       {
+               regs.cc = PACK_FLAGS;   // Mash flags back into the CC register
+               PUSH16(regs.pc);                // Save all regs...
+               PUSH16(regs.x);
+               PUSH(regs.d.acc.a);
+               PUSH(regs.d.acc.b);
+               PUSH(regs.cc);
+               regs.clock += 12;
+       }
+
+       regs.pc = RdMemW(vector);       // And do it!
+       flagI = 1;
+
+       // Clear the passed in flag + WAI state
+       regs.cpuFlags &= ~(flag | V63701_STATE_WAI);
+       regsPointer->cpuFlags &= ~(flag | V63701_STATE_WAI);
+}
+
+
+uint8_t InternalRegisterRead(uint16_t address)
+{
+       switch (address & 0x1F)
+       {
+       case 0x00:
+               return regs.ddr1;
+       case 0x02:
+               return /*(regs.port1read & ~regs.ddr1) |*/ (regs.port1 & regs.ddr1);
+       // Timer Control and Status Register
+       case 0x08:
+               regs.tcsrWasRead = true;
+               return regs.tcsr.byte;
+       // Counter high byte
+       case 0x09:
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.tof = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               regs.cReadLatch = regs.counter.byte.lo;
+               return regs.counter.byte.hi;
+       // Counter low byte
+       case 0x0A:
+               return regs.cReadLatch;
+       // Output compare high byte
+       case 0x0B:
+               return regs.outputCompare.byte.hi;
+       // Output compare low byte
+       case 0x0C:
+               return regs.outputCompare.byte.lo;
+       // RAM Control register (only bits 6 & 7 are valid)
+       case 0x14:
+               return (regs.ramCtrl & 0xC0) | 0x3F;
+       default:
+#ifdef __DEBUG__
+               printf("V63701: Unhandled register read @ $%02X...\n", address);
+#else
+               ;
+#endif
+       }
+
+       return 0x00;
+}
+
+
+void InternalRegisterWrite(uint16_t address, uint8_t data)
+{
+       switch (address & 0x1F)
+       {
+       case 0x00:
+               regs.ddr1 = data;
+               break;
+       case 0x01:
+               regs.ddr2 = data;
+               break;
+       case 0x02:
+               regs.port1 = data;
+               break;
+       case 0x03:
+               regs.port2 = data;
+               break;
+//     case 0x05:
+       // Timer Control and Status Register
+       case 0x08:
+               // Top 3 bits are RO, so protect them
+               regs.tcsr.byte = (data & 0x1F) | (regs.tcsr.byte & 0xE0);
+               break;
+       // Counter (High Byte)
+       case 0x09:
+               regs.cWriteLatch = data;
+               regs.counter.word = 0xFFF8;
+               break;
+       // Counter (Low Byte)
+       case 0x0A:
+               regs.counter.word = (regs.cWriteLatch << 8) | data;
+               break;
+       // Output Compare Register (High Byte)
+       case 0x0B:
+               regs.outputCompare.byte.hi = data;
+
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.ocf = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               break;
+       // Output Compare Register (Low Byte)
+       case 0x0C:
+               regs.outputCompare.byte.lo = data;
+
+               if (regs.tcsrWasRead)
+               {
+                       regs.tcsr.bit.ocf = 0;
+                       regs.tcsrWasRead = false;
+               }
+
+               break;
+       // RAM Control register
+       case 0x14:
+               regs.ramCtrl = data;
+               break;
+       default:
+#ifdef __DEBUG__
+               WriteLog("V63701: Unhandled register write @ $%02X...\n", address);
+#else
+               ;
+#endif
+       }
+}
+