//
-// Virtual 65C02 Emulator v1.0
+// Virtual 65C02 Emulator v1.1
//
-// by James L. Hammons
-// (c) 2005 Underground Software
+// by James Hammons
+// (c) 2005-2018 Underground Software
//
-// JLH = James L. Hammons <jlhamm@acm.org>
+// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
-// --- ---------- ------------------------------------------------------------
+// --- ---------- -----------------------------------------------------------
// JLH 01/04/2006 Added changelog ;-)
// JLH 01/18/2009 Fixed EA_ABS_* macros
//
-//OK, the wraparound bug exists in both the Apple and Atari versions of Ultima II.
-//However, the Atari version *does* occassionally pick strength while the Apple
-//versions do not--which would seem to indicate a bug either in the RNG algorithm,
-//the 65C02 core, or the Apple hardware. Need to investigate all three!
+//OK, the wraparound bug exists in both the Apple and Atari versions of Ultima
+//II. However, the Atari version *does* occassionally pick strength while the
+//Apple versions do not--which would seem to indicate a bug either in the RNG
+//algorithm, the 65C02 core, or the Apple hardware. Need to investigate all
+//three!
+//N.B.: There were some lingering bugs in the BCD portions of the ADC and SBC
+// opcodes; need to test to see if that clears up the problem.
#define __DEBUG__
//#define __DEBUGMON__
#include "v65c02.h"
#ifdef __DEBUG__
+#include <string.h>
#include "dis65c02.h"
#include "log.h"
#endif
-// Various macros
-#define CLR_Z (regs.cc &= ~FLAG_Z)
-#define CLR_ZN (regs.cc &= ~(FLAG_Z | FLAG_N))
-#define CLR_ZNC (regs.cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
-#define CLR_V (regs.cc &= ~FLAG_V)
-#define CLR_N (regs.cc &= ~FLAG_N)
-#define SET_Z(r) (regs.cc = ((r) == 0 ? regs.cc | FLAG_Z : regs.cc & ~FLAG_Z))
-#define SET_N(r) (regs.cc = ((r) & 0x80 ? regs.cc | FLAG_N : regs.cc & ~FLAG_N))
+// Various helper macros
+
+#define CLR_Z (regs->cc &= ~FLAG_Z)
+#define CLR_ZN (regs->cc &= ~(FLAG_Z | FLAG_N))
+#define CLR_ZNC (regs->cc &= ~(FLAG_Z | FLAG_N | FLAG_C))
+#define CLR_V (regs->cc &= ~FLAG_V)
+#define CLR_N (regs->cc &= ~FLAG_N)
+#define CLR_D (regs->cc &= ~FLAG_D)
+#define SET_Z(r) (regs->cc = ((r) == 0 ? regs->cc | FLAG_Z : regs->cc & ~FLAG_Z))
+#define SET_N(r) (regs->cc = ((r) & 0x80 ? regs->cc | FLAG_N : regs->cc & ~FLAG_N))
+#define SET_I (regs->cc |= FLAG_I)
//Not sure that this code is computing the carry correctly... Investigate! [Seems to be]
-#define SET_C_ADD(a,b) (regs.cc = ((uint8_t)(b) > (uint8_t)(~(a)) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
-//#define SET_C_SUB(a,b) (regs.cc = ((uint8_t)(b) >= (uint8_t)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
-#define SET_C_CMP(a,b) (regs.cc = ((uint8_t)(b) >= (uint8_t)(a) ? regs.cc | FLAG_C : regs.cc & ~FLAG_C))
+/*
+Not 100% sure (for SET_C_CMP), when we have things like this:
+D0BE: AC 6F D3 LDY $D36F [SP=01EC, CC=--.--IZ-, A=AA, X=60, Y=00]
+D0C1: CC 5A D3 CPY $D35A [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
+D0C4: F0 0F BEQ $D0D5 [SP=01EC, CC=--.--IZC, A=AA, X=60, Y=00]
+D0D5: AD 6E D3 LDA $D36E [SP=01EC, CC=--.--I-C, A=0A, X=60, Y=00]
+
+Which shows that $D35A has to be 0 since the Z flag is set. Why would the carry flag be set on a comparison where the compared items are equal?
+*/
+#define SET_C_ADD(a,b) (regs->cc = ((uint8_t)(b) > (uint8_t)(~(a)) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
+#define SET_C_CMP(a,b) (regs->cc = ((uint8_t)(b) >= (uint8_t)(a) ? regs->cc | FLAG_C : regs->cc & ~FLAG_C))
#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_ZNC_SUB(a,b,r) SET_N(r); SET_Z(r); SET_C_SUB(a,b)
#define SET_ZNC_CMP(a,b,r) SET_N(r); SET_Z(r); SET_C_CMP(a,b)
-//Small problem with the EA_ macros: ABS macros don't increment the PC!!! !!! FIX !!!
-//NB: It's properly handled by everything that uses it, so it works, even if it's klunky
-//Small problem with fixing it is that you can't do it in a single instruction, i.e.,
-//you have to read the value THEN you have to increment the PC. Unless there's another
-//way to do that
-//[DONE]
-#define EA_IMM regs.pc++
-#define EA_ZP regs.RdMem(regs.pc++)
-#define EA_ZP_X (regs.RdMem(regs.pc++) + regs.x) & 0xFF
-#define EA_ZP_Y (regs.RdMem(regs.pc++) + regs.y) & 0xFF
-#define EA_ABS FetchMemW(regs.pc)
-#define EA_ABS_X FetchMemW(regs.pc) + regs.x
-#define EA_ABS_Y FetchMemW(regs.pc) + regs.y
-#define EA_IND_ZP_X RdMemW((regs.RdMem(regs.pc++) + regs.x) & 0xFF)
-#define EA_IND_ZP_Y RdMemW(regs.RdMem(regs.pc++)) + regs.y
-#define EA_IND_ZP RdMemW(regs.RdMem(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_ZP_Y regs.RdMem(EA_ZP_Y)
-#define READ_ABS regs.RdMem(EA_ABS)
-#define READ_ABS_X regs.RdMem(EA_ABS_X)
-#define READ_ABS_Y regs.RdMem(EA_ABS_Y)
-#define READ_IND_ZP_X regs.RdMem(EA_IND_ZP_X)
-#define READ_IND_ZP_Y regs.RdMem(EA_IND_ZP_Y)
-#define READ_IND_ZP regs.RdMem(EA_IND_ZP)
-
-#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 READ_ABS_X_WB(v) uint16_t addr = EA_ABS_X; v = regs.RdMem(addr)
-#define READ_ABS_Y_WB(v) uint16_t addr = EA_ABS_Y; v = regs.RdMem(addr)
-#define READ_IND_ZP_X_WB(v) uint16_t addr = EA_IND_ZP_X; v = regs.RdMem(addr)
-#define READ_IND_ZP_Y_WB(v) uint16_t addr = EA_IND_ZP_Y; v = regs.RdMem(addr)
-#define READ_IND_ZP_WB(v) uint16_t addr = EA_IND_ZP; v = regs.RdMem(addr)
-
-#define WRITE_BACK(d) regs.WrMem(addr, (d))
+#define EA_IMM regs->pc++
+#define EA_ZP regs->RdMem(regs->pc++)
+#define EA_ZP_X (regs->RdMem(regs->pc++) + regs->x) & 0xFF
+#define EA_ZP_Y (regs->RdMem(regs->pc++) + regs->y) & 0xFF
+#define EA_ABS FetchMemW(regs->pc)
+#define EA_ABS_X FetchMemW(regs->pc) + regs->x
+#define EA_ABS_Y FetchMemW(regs->pc) + regs->y
+#define EA_IND_ZP_X RdMemWZP((regs->RdMem(regs->pc++) + regs->x) & 0xFF)
+#define EA_IND_ZP_Y RdMemWZP(regs->RdMem(regs->pc++)) + regs->y
+#define EA_IND_ZP RdMemWZP(regs->RdMem(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_ZP_Y regs->RdMem(EA_ZP_Y)
+#define READ_ABS regs->RdMem(EA_ABS)
+#define READ_ABS_X regs->RdMem(EA_ABS_X)
+#define READ_ABS_Y regs->RdMem(EA_ABS_Y)
+#define READ_IND_ZP_X regs->RdMem(EA_IND_ZP_X)
+#define READ_IND_ZP_Y regs->RdMem(EA_IND_ZP_Y)
+#define READ_IND_ZP regs->RdMem(EA_IND_ZP)
+
+#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 READ_ABS_X_WB(v) uint16_t addr = EA_ABS_X; v = regs->RdMem(addr)
+#define READ_ABS_Y_WB(v) uint16_t addr = EA_ABS_Y; v = regs->RdMem(addr)
+#define READ_IND_ZP_X_WB(v) uint16_t addr = EA_IND_ZP_X; v = regs->RdMem(addr)
+#define READ_IND_ZP_Y_WB(v) uint16_t addr = EA_IND_ZP_Y; v = regs->RdMem(addr)
+#define READ_IND_ZP_WB(v) uint16_t addr = EA_IND_ZP; v = regs->RdMem(addr)
+
+#define WRITE_BACK(d) regs->WrMem(addr, (d))
+
// Private global variables
-static V65C02REGS regs;
+static V65C02REGS * regs;
-//This is probably incorrect, at least WRT to the $x7 and $xF opcodes... !!! FIX !!!
-//Also this doesn't take into account the extra cycle it takes when an indirect fetch
-//(ABS, ABS X/Y, ZP) crosses a page boundary, or extra cycle for BCD add/subtract...
-#warning "Cycle counts are not accurate--!!! FIX !!!"
+// Cycle counts should be correct for the the Rockwell version of the 65C02.
+// Extra cycles for page crossing or BCD mode are accounted for in their
+// respective opcode handlers.
static uint8_t CPUCycles[256] = {
-#if 0
- 7, 6, 1, 1, 5, 3, 5, 1, 3, 2, 2, 1, 6, 4, 6, 1,
- 2, 5, 5, 1, 5, 4, 6, 1, 2, 4, 2, 1, 6, 4, 6, 1,
- 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 4, 4, 6, 1,
- 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 2, 1, 4, 4, 6, 1,
- 6, 6, 1, 1, 1, 3, 5, 1, 3, 2, 2, 1, 3, 4, 6, 1,
- 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
- 6, 6, 1, 1, 3, 3, 5, 1, 4, 2, 2, 1, 6, 4, 6, 1,
- 2, 5, 5, 1, 4, 4, 6, 1, 2, 4, 4, 1, 6, 4, 6, 1,
- 2, 6, 1, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
- 2, 6, 5, 1, 4, 4, 4, 1, 2, 5, 2, 1, 4, 5, 5, 1,
- 2, 6, 2, 1, 3, 3, 3, 1, 2, 2, 2, 1, 4, 4, 4, 1,
- 2, 5, 5, 1, 4, 4, 4, 1, 2, 4, 2, 1, 4, 4, 4, 1,
- 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
- 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 3, 1, 1, 4, 6, 1,
- 2, 6, 1, 1, 3, 3, 5, 1, 2, 2, 2, 1, 4, 4, 6, 1,
- 2, 5, 5, 1, 1, 4, 6, 1, 2, 4, 4, 1, 1, 4, 6, 1 };
-#else
- 7, 6, 2, 2, 5, 3, 5, 2, 3, 2, 2, 2, 6, 4, 6, 2,
- 2, 5, 5, 2, 5, 4, 6, 2, 2, 4, 2, 2, 6, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 4, 2, 2, 2, 4, 2, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 3, 2, 2, 2, 3, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 3, 2, 8, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 4, 2, 2, 2, 6, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 4, 2, 6, 4, 6, 2,
- 2, 6, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 2,
- 2, 6, 5, 2, 4, 4, 4, 2, 2, 5, 2, 2, 4, 5, 5, 2,
- 2, 6, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 2,
- 2, 5, 5, 2, 4, 4, 4, 2, 2, 4, 2, 2, 4, 4, 4, 2,
- 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 5, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 3, 2, 4, 4, 6, 2,
- 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 4, 2, 4, 4, 6, 2 };
-#endif
+ 7, 6, 2, 2, 5, 3, 5, 5, 3, 2, 2, 2, 6, 4, 6, 5,
+ 2, 5, 5, 2, 5, 4, 6, 5, 2, 4, 2, 2, 6, 4, 6, 5,
+ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 4, 2, 6, 5,
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 2, 2, 4, 4, 6, 5,
+ 6, 6, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 5,
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 8, 4, 6, 5,
+ 6, 6, 2, 2, 3, 3, 5, 5, 4, 2, 2, 2, 6, 4, 6, 5,
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 6, 4, 6, 5,
+ 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
+ 2, 6, 5, 2, 4, 4, 4, 5, 2, 5, 2, 2, 4, 5, 5, 5,
+ 2, 6, 2, 2, 3, 3, 3, 5, 2, 2, 2, 2, 4, 4, 4, 5,
+ 2, 5, 5, 2, 4, 4, 4, 5, 2, 4, 2, 2, 4, 4, 4, 5,
+ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 5, 5,
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 3, 2, 4, 4, 6, 5,
+ 2, 6, 2, 2, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 5,
+ 2, 5, 5, 2, 4, 4, 6, 5, 2, 4, 4, 2, 4, 4, 6, 5 };
-static uint8_t _6502Cycles[256] = {
- 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
- 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 2, 6, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
- 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
- 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 6, 4, 6, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
- 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
- 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5,
- 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
- 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4,
- 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 5, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7,
- 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
- 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 6, 7 };
-
-static uint8_t _65C02Cycles[256] = {
- 7, 6, 2, 2, 5, 3, 5, 2, 3, 2, 2, 2, 6, 4, 6, 2,
- 2, 5, 5, 2, 5, 4, 6, 2, 2, 4, 2, 2, 6, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 4, 2, 2, 2, 4, 2, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 3, 2, 2, 2, 3, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 3, 2, 8, 4, 6, 2,
- 6, 6, 2, 2, 3, 3, 5, 2, 4, 2, 2, 2, 6, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 4, 2, 6, 4, 6, 2,
- 2, 6, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 2,
- 2, 6, 5, 2, 4, 4, 4, 2, 2, 5, 2, 2, 4, 5, 5, 2,
- 2, 6, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 2,
- 2, 5, 5, 2, 4, 4, 4, 2, 2, 4, 2, 2, 4, 4, 4, 2,
- 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 5, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 3, 2, 4, 4, 6, 2,
- 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 6, 2,
- 2, 5, 5, 2, 4, 4, 6, 2, 2, 4, 4, 2, 4, 4, 6, 2 };
-#if 0
-// ExtraCycles:
-// +1 if branch taken
-// +1 if page boundary crossed
-#define BRANCH_TAKEN { \
- base = regs.pc; \
- regs.pc += addr; \
- if ((base ^ regs.pc) & 0xFF00) \
- uExtraCycles=2; \
- else \
- uExtraCycles=1; \
- }
-
-{
- oldpc = regs.pc;
- regs.pc += v;
- if ((oldc ^ regs.pc) & 0xFF00)
- regs.clock++;
- regs.clock++;
+//
+// Read a uint16_t out of 65C02 memory (big endian format)
+//
+static inline uint16_t RdMemW(uint16_t address)
+{
+ return (uint16_t)(regs->RdMem(address + 1) << 8)
+ | regs->RdMem(address + 0);
}
-#endif
-/*
-6502 cycles (includes illegal opcodes):
-
- case 0x00: BRK CYC(7) break;
- case 0x01: INDX ORA CYC(6) break;
- case 0x02: INV HLT CYC(2) break;
- case 0x03: INV INDX ASO CYC(8) break;
- case 0x04: INV ZPG NOP CYC(3) break;
- case 0x05: ZPG ORA CYC(3) break;
- case 0x06: ZPG ASL_NMOS CYC(5) break;
- case 0x07: INV ZPG ASO CYC(5) break;
- case 0x08: PHP CYC(3) break;
- case 0x09: IMM ORA CYC(2) break;
- case 0x0A: ASLA CYC(2) break;
- case 0x0B: INV IMM ANC CYC(2) break;
- case 0x0C: INV ABSX NOP CYC(4) break;
- case 0x0D: ABS ORA CYC(4) break;
- case 0x0E: ABS ASL_NMOS CYC(6) break;
- case 0x0F: INV ABS ASO CYC(6) break;
- case 0x10: REL BPL CYC(2) break;
- case 0x11: INDY ORA CYC(5) break;
- case 0x12: INV HLT CYC(2) break;
- case 0x13: INV INDY ASO CYC(8) break;
- case 0x14: INV ZPGX NOP CYC(4) break;
- case 0x15: ZPGX ORA CYC(4) break;
- case 0x16: ZPGX ASL_NMOS CYC(6) break;
- case 0x17: INV ZPGX ASO CYC(6) break;
- case 0x18: CLC CYC(2) break;
- case 0x19: ABSY ORA CYC(4) break;
- case 0x1A: INV NOP CYC(2) break;
- case 0x1B: INV ABSY ASO CYC(7) break;
- case 0x1C: INV ABSX NOP CYC(4) break;
- case 0x1D: ABSX ORA CYC(4) break;
- case 0x1E: ABSX ASL_NMOS CYC(6) break;
- case 0x1F: INV ABSX ASO CYC(7) break;
- case 0x20: ABS JSR CYC(6) break;
- case 0x21: INDX AND CYC(6) break;
- case 0x22: INV HLT CYC(2) break;
- case 0x23: INV INDX RLA CYC(8) break;
- case 0x24: ZPG BIT CYC(3) break;
- case 0x25: ZPG AND CYC(3) break;
- case 0x26: ZPG ROL_NMOS CYC(5) break;
- case 0x27: INV ZPG RLA CYC(5) break;
- case 0x28: PLP CYC(4) break;
- case 0x29: IMM AND CYC(2) break;
- case 0x2A: ROLA CYC(2) break;
- case 0x2B: INV IMM ANC CYC(2) break;
- case 0x2C: ABS BIT CYC(4) break;
- case 0x2D: ABS AND CYC(2) break;
- case 0x2E: ABS ROL_NMOS CYC(6) break;
- case 0x2F: INV ABS RLA CYC(6) break;
- case 0x30: REL BMI CYC(2) break;
- case 0x31: INDY AND CYC(5) break;
- case 0x32: INV HLT CYC(2) break;
- case 0x33: INV INDY RLA CYC(8) break;
- case 0x34: INV ZPGX NOP CYC(4) break;
- case 0x35: ZPGX AND CYC(4) break;
- case 0x36: ZPGX ROL_NMOS CYC(6) break;
- case 0x37: INV ZPGX RLA CYC(6) break;
- case 0x38: SEC CYC(2) break;
- case 0x39: ABSY AND CYC(4) break;
- case 0x3A: INV NOP CYC(2) break;
- case 0x3B: INV ABSY RLA CYC(7) break;
- case 0x3C: INV ABSX NOP CYC(4) break;
- case 0x3D: ABSX AND CYC(4) break;
- case 0x3E: ABSX ROL_NMOS CYC(6) break;
- case 0x3F: INV ABSX RLA CYC(7) break;
- case 0x40: RTI CYC(6) DoIrqProfiling(uExecutedCycles); break;
- case 0x41: INDX EOR CYC(6) break;
- case 0x42: INV HLT CYC(2) break;
- case 0x43: INV INDX LSE CYC(8) break;
- case 0x44: INV ZPG NOP CYC(3) break;
- case 0x45: ZPG EOR CYC(3) break;
- case 0x46: ZPG LSR_NMOS CYC(5) break;
- case 0x47: INV ZPG LSE CYC(5) break;
- case 0x48: PHA CYC(3) break;
- case 0x49: IMM EOR CYC(2) break;
- case 0x4A: LSRA CYC(2) break;
- case 0x4B: INV IMM ALR CYC(2) break;
- case 0x4C: ABS JMP CYC(3) break;
- case 0x4D: ABS EOR CYC(4) break;
- case 0x4E: ABS LSR_NMOS CYC(6) break;
- case 0x4F: INV ABS LSE CYC(6) break;
- case 0x50: REL BVC CYC(2) break;
- case 0x51: INDY EOR CYC(5) break;
- case 0x52: INV HLT CYC(2) break;
- case 0x53: INV INDY LSE CYC(8) break;
- case 0x54: INV ZPGX NOP CYC(4) break;
- case 0x55: ZPGX EOR CYC(4) break;
- case 0x56: ZPGX LSR_NMOS CYC(6) break;
- case 0x57: INV ZPGX LSE CYC(6) break;
- case 0x58: CLI CYC(2) break;
- case 0x59: ABSY EOR CYC(4) break;
- case 0x5A: INV NOP CYC(2) break;
- case 0x5B: INV ABSY LSE CYC(7) break;
- case 0x5C: INV ABSX NOP CYC(4) break;
- case 0x5D: ABSX EOR CYC(4) break;
- case 0x5E: ABSX LSR_NMOS CYC(6) break;
- case 0x5F: INV ABSX LSE CYC(7) break;
- case 0x60: RTS CYC(6) break;
- case 0x61: INDX ADC_NMOS CYC(6) break;
- case 0x62: INV HLT CYC(2) break;
- case 0x63: INV INDX RRA CYC(8) break;
- case 0x64: INV ZPG NOP CYC(3) break;
- case 0x65: ZPG ADC_NMOS CYC(3) break;
- case 0x66: ZPG ROR_NMOS CYC(5) break;
- case 0x67: INV ZPG RRA CYC(5) break;
- case 0x68: PLA CYC(4) break;
- case 0x69: IMM ADC_NMOS CYC(2) break;
- case 0x6A: RORA CYC(2) break;
- case 0x6B: INV IMM ARR CYC(2) break;
- case 0x6C: IABSNMOS JMP CYC(6) break;
- case 0x6D: ABS ADC_NMOS CYC(4) break;
- case 0x6E: ABS ROR_NMOS CYC(6) break;
- case 0x6F: INV ABS RRA CYC(6) break;
- case 0x70: REL BVS CYC(2) break;
- case 0x71: INDY ADC_NMOS CYC(5) break;
- case 0x72: INV HLT CYC(2) break;
- case 0x73: INV INDY RRA CYC(8) break;
- case 0x74: INV ZPGX NOP CYC(4) break;
- case 0x75: ZPGX ADC_NMOS CYC(4) break;
- case 0x76: ZPGX ROR_NMOS CYC(6) break;
- case 0x77: INV ZPGX RRA CYC(6) break;
- case 0x78: SEI CYC(2) break;
- case 0x79: ABSY ADC_NMOS CYC(4) break;
- case 0x7A: INV NOP CYC(2) break;
- case 0x7B: INV ABSY RRA CYC(7) break;
- case 0x7C: INV ABSX NOP CYC(4) break;
- case 0x7D: ABSX ADC_NMOS CYC(4) break;
- case 0x7E: ABSX ROR_NMOS CYC(6) break;
- case 0x7F: INV ABSX RRA CYC(7) break;
- case 0x80: INV IMM NOP CYC(2) break;
- case 0x81: INDX STA CYC(6) break;
- case 0x82: INV IMM NOP CYC(2) break;
- case 0x83: INV INDX AXS CYC(6) break;
- case 0x84: ZPG STY CYC(3) break;
- case 0x85: ZPG STA CYC(3) break;
- case 0x86: ZPG STX CYC(3) break;
- case 0x87: INV ZPG AXS CYC(3) break;
- case 0x88: DEY CYC(2) break;
- case 0x89: INV IMM NOP CYC(2) break;
- case 0x8A: TXA CYC(2) break;
- case 0x8B: INV IMM XAA CYC(2) break;
- case 0x8C: ABS STY CYC(4) break;
- case 0x8D: ABS STA CYC(4) break;
- case 0x8E: ABS STX CYC(4) break;
- case 0x8F: INV ABS AXS CYC(4) break;
- case 0x90: REL BCC CYC(2) break;
- case 0x91: INDY STA CYC(6) break;
- case 0x92: INV HLT CYC(2) break;
- case 0x93: INV INDY AXA CYC(6) break;
- case 0x94: ZPGX STY CYC(4) break;
- case 0x95: ZPGX STA CYC(4) break;
- case 0x96: ZPGY STX CYC(4) break;
- case 0x97: INV ZPGY AXS CYC(4) break;
- case 0x98: TYA CYC(2) break;
- case 0x99: ABSY STA CYC(5) break;
- case 0x9A: TXS CYC(2) break;
- case 0x9B: INV ABSY TAS CYC(5) break;
- case 0x9C: INV ABSX SAY CYC(5) break;
- case 0x9D: ABSX STA CYC(5) break;
- case 0x9E: INV ABSY XAS CYC(5) break;
- case 0x9F: INV ABSY AXA CYC(5) break;
- case 0xA0: IMM LDY CYC(2) break;
- case 0xA1: INDX LDA CYC(6) break;
- case 0xA2: IMM LDX CYC(2) break;
- case 0xA3: INV INDX LAX CYC(6) break;
- case 0xA4: ZPG LDY CYC(3) break;
- case 0xA5: ZPG LDA CYC(3) break;
- case 0xA6: ZPG LDX CYC(3) break;
- case 0xA7: INV ZPG LAX CYC(3) break;
- case 0xA8: TAY CYC(2) break;
- case 0xA9: IMM LDA CYC(2) break;
- case 0xAA: TAX CYC(2) break;
- case 0xAB: INV IMM OAL CYC(2) break;
- case 0xAC: ABS LDY CYC(4) break;
- case 0xAD: ABS LDA CYC(4) break;
- case 0xAE: ABS LDX CYC(4) break;
- case 0xAF: INV ABS LAX CYC(4) break;
- case 0xB0: REL BCS CYC(2) break;
- case 0xB1: INDY LDA CYC(5) break;
- case 0xB2: INV HLT CYC(2) break;
- case 0xB3: INV INDY LAX CYC(5) break;
- case 0xB4: ZPGX LDY CYC(4) break;
- case 0xB5: ZPGX LDA CYC(4) break;
- case 0xB6: ZPGY LDX CYC(4) break;
- case 0xB7: INV ZPGY LAX CYC(4) break;
- case 0xB8: CLV CYC(2) break;
- case 0xB9: ABSY LDA CYC(4) break;
- case 0xBA: TSX CYC(2) break;
- case 0xBB: INV ABSY LAS CYC(4) break;
- case 0xBC: ABSX LDY CYC(4) break;
- case 0xBD: ABSX LDA CYC(4) break;
- case 0xBE: ABSY LDX CYC(4) break;
- case 0xBF: INV ABSY LAX CYC(4) break;
- case 0xC0: IMM CPY CYC(2) break;
- case 0xC1: INDX CMP CYC(6) break;
- case 0xC2: INV IMM NOP CYC(2) break;
- case 0xC3: INV INDX DCM CYC(8) break;
- case 0xC4: ZPG CPY CYC(3) break;
- case 0xC5: ZPG CMP CYC(3) break;
- case 0xC6: ZPG DEC_NMOS CYC(5) break;
- case 0xC7: INV ZPG DCM CYC(5) break;
- case 0xC8: INY CYC(2) break;
- case 0xC9: IMM CMP CYC(2) break;
- case 0xCA: DEX CYC(2) break;
- case 0xCB: INV IMM SAX CYC(2) break;
- case 0xCC: ABS CPY CYC(4) break;
- case 0xCD: ABS CMP CYC(4) break;
- case 0xCE: ABS DEC_NMOS CYC(5) break;
- case 0xCF: INV ABS DCM CYC(6) break;
- case 0xD0: REL BNE CYC(2) break;
- case 0xD1: INDY CMP CYC(5) break;
- case 0xD2: INV HLT CYC(2) break;
- case 0xD3: INV INDY DCM CYC(8) break;
- case 0xD4: INV ZPGX NOP CYC(4) break;
- case 0xD5: ZPGX CMP CYC(4) break;
- case 0xD6: ZPGX DEC_NMOS CYC(6) break;
- case 0xD7: INV ZPGX DCM CYC(6) break;
- case 0xD8: CLD CYC(2) break;
- case 0xD9: ABSY CMP CYC(4) break;
- case 0xDA: INV NOP CYC(2) break;
- case 0xDB: INV ABSY DCM CYC(7) break;
- case 0xDC: INV ABSX NOP CYC(4) break;
- case 0xDD: ABSX CMP CYC(4) break;
- case 0xDE: ABSX DEC_NMOS CYC(6) break;
- case 0xDF: INV ABSX DCM CYC(7) break;
- case 0xE0: IMM CPX CYC(2) break;
- case 0xE1: INDX SBC_NMOS CYC(6) break;
- case 0xE2: INV IMM NOP CYC(2) break;
- case 0xE3: INV INDX INS CYC(8) break;
- case 0xE4: ZPG CPX CYC(3) break;
- case 0xE5: ZPG SBC_NMOS CYC(3) break;
- case 0xE6: ZPG INC_NMOS CYC(5) break;
- case 0xE7: INV ZPG INS CYC(5) break;
- case 0xE8: INX CYC(2) break;
- case 0xE9: IMM SBC_NMOS CYC(2) break;
- case 0xEA: NOP CYC(2) break;
- case 0xEB: INV IMM SBC_NMOS CYC(2) break;
- case 0xEC: ABS CPX CYC(4) break;
- case 0xED: ABS SBC_NMOS CYC(4) break;
- case 0xEE: ABS INC_NMOS CYC(6) break;
- case 0xEF: INV ABS INS CYC(6) break;
- case 0xF0: REL BEQ CYC(2) break;
- case 0xF1: INDY SBC_NMOS CYC(5) break;
- case 0xF2: INV HLT CYC(2) break;
- case 0xF3: INV INDY INS CYC(8) break;
- case 0xF4: INV ZPGX NOP CYC(4) break;
- case 0xF5: ZPGX SBC_NMOS CYC(4) break;
- case 0xF6: ZPGX INC_NMOS CYC(6) break;
- case 0xF7: INV ZPGX INS CYC(6) break;
- case 0xF8: SED CYC(2) break;
- case 0xF9: ABSY SBC_NMOS CYC(4) break;
- case 0xFA: INV NOP CYC(2) break;
- case 0xFB: INV ABSY INS CYC(7) break;
- case 0xFC: INV ABSX NOP CYC(4) break;
- case 0xFD: ABSX SBC_NMOS CYC(4) break;
- case 0xFE: ABSX INC_NMOS CYC(6) break;
- case 0xFF: INV ABSX INS CYC(7) break;
-
-
-65C02 opcodes: (all illegal are NOP, but have cycle counts)
-
- case 0x00: BRK CYC(7) break;
- case 0x01: INDX ORA CYC(6) break;
- case 0x02: INV IMM NOP CYC(2) break;
- case 0x03: INV NOP CYC(2) break;
- case 0x04: ZPG TSB CYC(5) break;
- case 0x05: ZPG ORA CYC(3) break;
- case 0x06: ZPG ASL_CMOS CYC(5) break;
- case 0x07: INV NOP CYC(2) break;
- case 0x08: PHP CYC(3) break;
- case 0x09: IMM ORA CYC(2) break;
- case 0x0A: ASLA CYC(2) break;
- case 0x0B: INV NOP CYC(2) break;
- case 0x0C: ABS TSB CYC(6) break;
- case 0x0D: ABS ORA CYC(4) break;
- case 0x0E: ABS ASL_CMOS CYC(6) break;
- case 0x0F: INV NOP CYC(2) break;
- case 0x10: REL BPL CYC(2) break;
- case 0x11: INDY ORA CYC(5) break;
- case 0x12: IZPG ORA CYC(5) break;
- case 0x13: INV NOP CYC(2) break;
- case 0x14: ZPG TRB CYC(5) break;
- case 0x15: ZPGX ORA CYC(4) break;
- case 0x16: ZPGX ASL_CMOS CYC(6) break;
- case 0x17: INV NOP CYC(2) break;
- case 0x18: CLC CYC(2) break;
- case 0x19: ABSY ORA CYC(4) break;
- case 0x1A: INA CYC(2) break;
- case 0x1B: INV NOP CYC(2) break;
- case 0x1C: ABS TRB CYC(6) break;
- case 0x1D: ABSX ORA CYC(4) break;
- case 0x1E: ABSX ASL_CMOS CYC(6) break;
- case 0x1F: INV NOP CYC(2) break;
- case 0x20: ABS JSR CYC(6) break;
- case 0x21: INDX AND CYC(6) break;
- case 0x22: INV IMM NOP CYC(2) break;
- case 0x23: INV NOP CYC(2) break;
- case 0x24: ZPG BIT CYC(3) break;
- case 0x25: ZPG AND CYC(3) break;
- case 0x26: ZPG ROL_CMOS CYC(5) break;
- case 0x27: INV NOP CYC(2) break;
- case 0x28: PLP CYC(4) break;
- case 0x29: IMM AND CYC(2) break;
- case 0x2A: ROLA CYC(2) break;
- case 0x2B: INV NOP CYC(2) break;
- case 0x2C: ABS BIT CYC(4) break;
- case 0x2D: ABS AND CYC(2) break;
- case 0x2E: ABS ROL_CMOS CYC(6) break;
- case 0x2F: INV NOP CYC(2) break;
- case 0x30: REL BMI CYC(2) break;
- case 0x31: INDY AND CYC(5) break;
- case 0x32: IZPG AND CYC(5) break;
- case 0x33: INV NOP CYC(2) break;
- case 0x34: ZPGX BIT CYC(4) break;
- case 0x35: ZPGX AND CYC(4) break;
- case 0x36: ZPGX ROL_CMOS CYC(6) break;
- case 0x37: INV NOP CYC(2) break;
- case 0x38: SEC CYC(2) break;
- case 0x39: ABSY AND CYC(4) break;
- case 0x3A: DEA CYC(2) break;
- case 0x3B: INV NOP CYC(2) break;
- case 0x3C: ABSX BIT CYC(4) break;
- case 0x3D: ABSX AND CYC(4) break;
- case 0x3E: ABSX ROL_CMOS CYC(6) break;
- case 0x3F: INV NOP CYC(2) break;
- case 0x40: RTI CYC(6) DoIrqProfiling(uExecutedCycles); break;
- case 0x41: INDX EOR CYC(6) break;
- case 0x42: INV IMM NOP CYC(2) break;
- case 0x43: INV NOP CYC(2) break;
- case 0x44: INV ZPG NOP CYC(3) break;
- case 0x45: ZPG EOR CYC(3) break;
- case 0x46: ZPG LSR_CMOS CYC(5) break;
- case 0x47: INV NOP CYC(2) break;
- case 0x48: PHA CYC(3) break;
- case 0x49: IMM EOR CYC(2) break;
- case 0x4A: LSRA CYC(2) break;
- case 0x4B: INV NOP CYC(2) break;
- case 0x4C: ABS JMP CYC(3) break;
- case 0x4D: ABS EOR CYC(4) break;
- case 0x4E: ABS LSR_CMOS CYC(6) break;
- case 0x4F: INV NOP CYC(2) break;
- case 0x50: REL BVC CYC(2) break;
- case 0x51: INDY EOR CYC(5) break;
- case 0x52: IZPG EOR CYC(5) break;
- case 0x53: INV NOP CYC(2) break;
- case 0x54: INV ZPGX NOP CYC(4) break;
- case 0x55: ZPGX EOR CYC(4) break;
- case 0x56: ZPGX LSR_CMOS CYC(6) break;
- case 0x57: INV NOP CYC(2) break;
- case 0x58: CLI CYC(2) break;
- case 0x59: ABSY EOR CYC(4) break;
- case 0x5A: PHY CYC(3) break;
- case 0x5B: INV NOP CYC(2) break;
- case 0x5C: INV ABSX NOP CYC(8) break;
- case 0x5D: ABSX EOR CYC(4) break;
- case 0x5E: ABSX LSR_CMOS CYC(6) break;
- case 0x5F: INV NOP CYC(2) break;
- case 0x60: RTS CYC(6) break;
- case 0x61: INDX ADC_CMOS CYC(6) break;
- case 0x62: INV IMM NOP CYC(2) break;
- case 0x63: INV NOP CYC(2) break;
- case 0x64: ZPG STZ CYC(3) break;
- case 0x65: ZPG ADC_CMOS CYC(3) break;
- case 0x66: ZPG ROR_CMOS CYC(5) break;
- case 0x67: INV NOP CYC(2) break;
- case 0x68: PLA CYC(4) break;
- case 0x69: IMM ADC_CMOS CYC(2) break;
- case 0x6A: RORA CYC(2) break;
- case 0x6B: INV NOP CYC(2) break;
- case 0x6C: IABSCMOS JMP CYC(6) break;
- case 0x6D: ABS ADC_CMOS CYC(4) break;
- case 0x6E: ABS ROR_CMOS CYC(6) break;
- case 0x6F: INV NOP CYC(2) break;
- case 0x70: REL BVS CYC(2) break;
- case 0x71: INDY ADC_CMOS CYC(5) break;
- case 0x72: IZPG ADC_CMOS CYC(5) break;
- case 0x73: INV NOP CYC(2) break;
- case 0x74: ZPGX STZ CYC(4) break;
- case 0x75: ZPGX ADC_CMOS CYC(4) break;
- case 0x76: ZPGX ROR_CMOS CYC(6) break;
- case 0x77: INV NOP CYC(2) break;
- case 0x78: SEI CYC(2) break;
- case 0x79: ABSY ADC_CMOS CYC(4) break;
- case 0x7A: PLY CYC(4) break;
- case 0x7B: INV NOP CYC(2) break;
- case 0x7C: IABSX JMP CYC(6) break;
- case 0x7D: ABSX ADC_CMOS CYC(4) break;
- case 0x7E: ABSX ROR_CMOS CYC(6) break;
- case 0x7F: INV NOP CYC(2) break;
- case 0x80: REL BRA CYC(2) break;
- case 0x81: INDX STA CYC(6) break;
- case 0x82: INV IMM NOP CYC(2) break;
- case 0x83: INV NOP CYC(2) break;
- case 0x84: ZPG STY CYC(3) break;
- case 0x85: ZPG STA CYC(3) break;
- case 0x86: ZPG STX CYC(3) break;
- case 0x87: INV NOP CYC(2) break;
- case 0x88: DEY CYC(2) break;
- case 0x89: IMM BITI CYC(2) break;
- case 0x8A: TXA CYC(2) break;
- case 0x8B: INV NOP CYC(2) break;
- case 0x8C: ABS STY CYC(4) break;
- case 0x8D: ABS STA CYC(4) break;
- case 0x8E: ABS STX CYC(4) break;
- case 0x8F: INV NOP CYC(2) break;
- case 0x90: REL BCC CYC(2) break;
- case 0x91: INDY STA CYC(6) break;
- case 0x92: IZPG STA CYC(5) break;
- case 0x93: INV NOP CYC(2) break;
- case 0x94: ZPGX STY CYC(4) break;
- case 0x95: ZPGX STA CYC(4) break;
- case 0x96: ZPGY STX CYC(4) break;
- case 0x97: INV NOP CYC(2) break;
- case 0x98: TYA CYC(2) break;
- case 0x99: ABSY STA CYC(5) break;
- case 0x9A: TXS CYC(2) break;
- case 0x9B: INV NOP CYC(2) break;
- case 0x9C: ABS STZ CYC(4) break;
- case 0x9D: ABSX STA CYC(5) break;
- case 0x9E: ABSX STZ CYC(5) break;
- case 0x9F: INV NOP CYC(2) break;
- case 0xA0: IMM LDY CYC(2) break;
- case 0xA1: INDX LDA CYC(6) break;
- case 0xA2: IMM LDX CYC(2) break;
- case 0xA3: INV NOP CYC(2) break;
- case 0xA4: ZPG LDY CYC(3) break;
- case 0xA5: ZPG LDA CYC(3) break;
- case 0xA6: ZPG LDX CYC(3) break;
- case 0xA7: INV NOP CYC(2) break;
- case 0xA8: TAY CYC(2) break;
- case 0xA9: IMM LDA CYC(2) break;
- case 0xAA: TAX CYC(2) break;
- case 0xAB: INV NOP CYC(2) break;
- case 0xAC: ABS LDY CYC(4) break;
- case 0xAD: ABS LDA CYC(4) break;
- case 0xAE: ABS LDX CYC(4) break;
- case 0xAF: INV NOP CYC(2) break;
- case 0xB0: REL BCS CYC(2) break;
- case 0xB1: INDY LDA CYC(5) break;
- case 0xB2: IZPG LDA CYC(5) break;
- case 0xB3: INV NOP CYC(2) break;
- case 0xB4: ZPGX LDY CYC(4) break;
- case 0xB5: ZPGX LDA CYC(4) break;
- case 0xB6: ZPGY LDX CYC(4) break;
- case 0xB7: INV NOP CYC(2) break;
- case 0xB8: CLV CYC(2) break;
- case 0xB9: ABSY LDA CYC(4) break;
- case 0xBA: TSX CYC(2) break;
- case 0xBB: INV NOP CYC(2) break;
- case 0xBC: ABSX LDY CYC(4) break;
- case 0xBD: ABSX LDA CYC(4) break;
- case 0xBE: ABSY LDX CYC(4) break;
- case 0xBF: INV NOP CYC(2) break;
- case 0xC0: IMM CPY CYC(2) break;
- case 0xC1: INDX CMP CYC(6) break;
- case 0xC2: INV IMM NOP CYC(2) break;
- case 0xC3: INV NOP CYC(2) break;
- case 0xC4: ZPG CPY CYC(3) break;
- case 0xC5: ZPG CMP CYC(3) break;
- case 0xC6: ZPG DEC_CMOS CYC(5) break;
- case 0xC7: INV NOP CYC(2) break;
- case 0xC8: INY CYC(2) break;
- case 0xC9: IMM CMP CYC(2) break;
- case 0xCA: DEX CYC(2) break;
- case 0xCB: INV NOP CYC(2) break;
- case 0xCC: ABS CPY CYC(4) break;
- case 0xCD: ABS CMP CYC(4) break;
- case 0xCE: ABS DEC_CMOS CYC(5) break;
- case 0xCF: INV NOP CYC(2) break;
- case 0xD0: REL BNE CYC(2) break;
- case 0xD1: INDY CMP CYC(5) break;
- case 0xD2: IZPG CMP CYC(5) break;
- case 0xD3: INV NOP CYC(2) break;
- case 0xD4: INV ZPGX NOP CYC(4) break;
- case 0xD5: ZPGX CMP CYC(4) break;
- case 0xD6: ZPGX DEC_CMOS CYC(6) break;
- case 0xD7: INV NOP CYC(2) break;
- case 0xD8: CLD CYC(2) break;
- case 0xD9: ABSY CMP CYC(4) break;
- case 0xDA: PHX CYC(3) break;
- case 0xDB: INV NOP CYC(2) break;
- case 0xDC: INV ABSX NOP CYC(4) break;
- case 0xDD: ABSX CMP CYC(4) break;
- case 0xDE: ABSX DEC_CMOS CYC(6) break;
- case 0xDF: INV NOP CYC(2) break;
- case 0xE0: IMM CPX CYC(2) break;
- case 0xE1: INDX SBC_CMOS CYC(6) break;
- case 0xE2: INV IMM NOP CYC(2) break;
- case 0xE3: INV NOP CYC(2) break;
- case 0xE4: ZPG CPX CYC(3) break;
- case 0xE5: ZPG SBC_CMOS CYC(3) break;
- case 0xE6: ZPG INC_CMOS CYC(5) break;
- case 0xE7: INV NOP CYC(2) break;
- case 0xE8: INX CYC(2) break;
- case 0xE9: IMM SBC_CMOS CYC(2) break;
- case 0xEA: NOP CYC(2) break;
- case 0xEB: INV NOP CYC(2) break;
- case 0xEC: ABS CPX CYC(4) break;
- case 0xED: ABS SBC_CMOS CYC(4) break;
- case 0xEE: ABS INC_CMOS CYC(6) break;
- case 0xEF: INV NOP CYC(2) break;
- case 0xF0: REL BEQ CYC(2) break;
- case 0xF1: INDY SBC_CMOS CYC(5) break;
- case 0xF2: IZPG SBC_CMOS CYC(5) break;
- case 0xF3: INV NOP CYC(2) break;
- case 0xF4: INV ZPGX NOP CYC(4) break;
- case 0xF5: ZPGX SBC_CMOS CYC(4) break;
- case 0xF6: ZPGX INC_CMOS CYC(6) break;
- case 0xF7: INV NOP CYC(2) break;
- case 0xF8: SED CYC(2) break;
- case 0xF9: ABSY SBC_CMOS CYC(4) break;
- case 0xFA: PLX CYC(4) break;
- case 0xFB: INV NOP CYC(2) break;
- case 0xFC: INV ABSX NOP CYC(4) break;
- case 0xFD: ABSX SBC_CMOS CYC(4) break;
- case 0xFE: ABSX INC_CMOS CYC(6) break;
- case 0xFF: INV NOP CYC(2) break;
-*/
-
-// Private function prototypes
-
-static uint16_t RdMemW(uint16_t);
-static uint16_t FetchMemW(uint16_t addr);
//
-// Read a uint16_t out of 65C02 memory (big endian format)
+// Read a uint16_t out of 65C02 memory (big endian format), wrapping on page 0
//
-static inline uint16_t RdMemW(uint16_t address)
+static inline uint16_t RdMemWZP(uint16_t address)
{
- return (uint16_t)(regs.RdMem(address + 1) << 8) | regs.RdMem(address + 0);
+ return (uint16_t)(regs->RdMem((address + 1) & 0xFF) << 8)
+ | regs->RdMem(address + 0);
}
+
//
// Read a uint16_t out of 65C02 memory (big endian format) and increment PC
//
static inline uint16_t FetchMemW(uint16_t address)
{
- regs.pc += 2;
- return (uint16_t)(regs.RdMem(address + 1) << 8) | regs.RdMem(address + 0);
+ regs->pc += 2;
+ return (uint16_t)(regs->RdMem(address + 1) << 8)
+ | regs->RdMem(address + 0);
}
//
// 65C02 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
+// 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! ;-)
+// You have been warned! ;-)
//
+// Page crossing macros. These catch the cases where access of a certain type
+// will incur a one cycle penalty when crossing a page boundary.
+
+#define HANDLE_PAGE_CROSSING_IND_Y \
+ uint16_t addressLo = regs->RdMem(regs->RdMem(regs->pc)); \
+\
+ if ((addressLo + regs->y) > 0xFF) \
+ regs->clock++;
+
+#define HANDLE_PAGE_CROSSING_ABS_X \
+ uint16_t addressLo = regs->RdMem(regs->pc); \
+\
+ if ((addressLo + regs->x) > 0xFF) \
+ regs->clock++;
+
+#define HANDLE_PAGE_CROSSING_ABS_Y \
+ uint16_t addressLo = regs->RdMem(regs->pc); \
+\
+ if ((addressLo + regs->y) > 0xFF) \
+ regs->clock++;
+
+// Branch taken adds a cycle, crossing page adds one more
+
+#define HANDLE_BRANCH_TAKEN(m) \
+{ \
+ uint16_t oldpc = regs->pc; \
+ regs->pc += m; \
+ regs->clock++; \
+ \
+ if ((oldpc ^ regs->pc) & 0xFF00) \
+ regs->clock++; \
+}
+
/*
Mnemonic Addressing mode Form Opcode Size Timing
// ADC opcodes
//This is non-optimal, but it works--optimize later. :-)
+//N.B.: We have to pull the low nybble from each part of the sum in order to
+// check BCD addition of the low nybble correctly. It doesn't work to
+// look at the sum after summing the bytes. Also, Decimal mode incurs a
+// one cycle penalty (for the decimal correction).
#define OP_ADC_HANDLER(m) \
- uint16_t sum = (uint16_t)regs.a + (m) + (uint16_t)(regs.cc & FLAG_C); \
+ uint16_t sum = (uint16_t)regs->a + (m) + (uint16_t)(regs->cc & FLAG_C); \
\
- if (regs.cc & FLAG_D) \
+ if (regs->cc & FLAG_D) \
{ \
- if ((sum & 0x0F) > 0x09) \
+ uint8_t an = regs->a & 0x0F, mn = (m) & 0x0F, cn = (uint8_t)(regs->cc & FLAG_C); \
+\
+ if ((an + mn + cn) > 9) \
sum += 0x06; \
\
- if ((sum & 0xF0) > 0x90) \
+ if ((sum & 0x1F0) > 0x90) \
sum += 0x60; \
+\
+ regs->clock++;\
} \
\
- regs.cc = (regs.cc & ~FLAG_C) | (sum >> 8); \
- regs.cc = (~(regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
- regs.a = sum & 0xFF; \
- SET_ZN(regs.a)
-
-//OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
+ regs->cc = (regs->cc & ~FLAG_C) | (sum >> 8); \
+ regs->cc = (~(regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
+ regs->a = sum & 0xFF; \
+ SET_ZN(regs->a)
static void Op69(void) // ADC #
{
static void Op7D(void) // ADC ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint16_t m = READ_ABS_X;
OP_ADC_HANDLER(m);
}
static void Op79(void) // ADC ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint16_t m = READ_ABS_Y;
OP_ADC_HANDLER(m);
}
static void Op71(void) // ADC (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint16_t m = READ_IND_ZP_Y;
OP_ADC_HANDLER(m);
}
// AND opcodes
#define OP_AND_HANDLER(m) \
- regs.a &= m; \
- SET_ZN(regs.a)
+ regs->a &= m; \
+ SET_ZN(regs->a)
static void Op29(void) // AND #
{
static void Op3D(void) // AND ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_AND_HANDLER(m);
}
static void Op39(void) // AND ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_AND_HANDLER(m);
}
static void Op31(void) // AND (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint8_t m = READ_IND_ZP_Y;
OP_AND_HANDLER(m);
}
Absolute,X ASL Abs,X 1E 3 7
*/
-/*static void Op78(void) // LSL ABS
-{
- uint8_t tmp; uint16_t addr;
- addr = FetchW();
- tmp = regs.RdMem(addr);
- (tmp&0x80 ? regs.cc |= 0x01 : regs.cc &= 0xFE); // Shift hi bit into Carry
- tmp <<= 1;
- regs.WrMem(addr, tmp);
- (tmp == 0 ? regs.cc |= 0x04 : regs.cc &= 0xFB); // Adjust Zero flag
- (tmp&0x80 ? regs.cc |= 0x08 : regs.cc &= 0xF7); // Adjust Negative flag
-}*/
-
// ASL opcodes
#define OP_ASL_HANDLER(m) \
- regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
+ regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
(m) <<= 1; \
SET_ZN((m))
static void Op0A(void) // ASL A
{
- OP_ASL_HANDLER(regs.a);
+ OP_ASL_HANDLER(regs->a);
}
static void Op06(void) // ASL ZP
static void Op1E(void) // ASL ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_ASL_HANDLER(m);
}
/*
-BBR0 Relative BBR0 Oper 0F 2 2
-BBR1 Relative BBR1 Oper 1F 2 2
-BBR2 Relative BBR2 Oper 2F 2 2
-BBR3 Relative BBR3 Oper 3F 2 2
-BBR4 Relative BBR4 Oper 4F 2 2
-BBR5 Relative BBR5 Oper 5F 2 2
-BBR6 Relative BBR6 Oper 6F 2 2
-BBR7 Relative BBR7 Oper 7F 2 2
-BBS0 Relative BBS0 Oper 8F 2 2
-BBS1 Relative BBS1 Oper 9F 2 2
-BBS2 Relative BBS2 Oper AF 2 2
-BBS3 Relative BBS3 Oper BF 2 2
-BBS4 Relative BBS4 Oper CF 2 2
-BBS5 Relative BBS5 Oper DF 2 2
-BBS6 Relative BBS6 Oper EF 2 2
-BBS7 Relative BBS7 Oper FF 2 2
+BBR0 ZP, Relative BBR0 Oper 0F 3 5
+BBR1 ZP, Relative BBR1 Oper 1F 3 5
+BBR2 ZP, Relative BBR2 Oper 2F 3 5
+BBR3 ZP, Relative BBR3 Oper 3F 3 5
+BBR4 ZP, Relative BBR4 Oper 4F 3 5
+BBR5 ZP, Relative BBR5 Oper 5F 3 5
+BBR6 ZP, Relative BBR6 Oper 6F 3 5
+BBR7 ZP, Relative BBR7 Oper 7F 3 5
+BBS0 ZP, Relative BBS0 Oper 8F 3 5
+BBS1 ZP, Relative BBS1 Oper 9F 3 5
+BBS2 ZP, Relative BBS2 Oper AF 3 5
+BBS3 ZP, Relative BBS3 Oper BF 3 5
+BBS4 ZP, Relative BBS4 Oper CF 3 5
+BBS5 ZP, Relative BBS5 Oper DF 3 5
+BBS6 ZP, Relative BBS6 Oper EF 3 5
+BBS7 ZP, Relative BBS7 Oper FF 3 5
*/
// BBR/Sn opcodes
static void Op0F(void) // BBR0
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x01))
- regs.pc += m;
+ if (!(b & 0x01))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op1F(void) // BBR1
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x02))
- regs.pc += m;
+ if (!(b & 0x02))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op2F(void) // BBR2
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x04))
- regs.pc += m;
+ if (!(b & 0x04))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op3F(void) // BBR3
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x08))
- regs.pc += m;
+ if (!(b & 0x08))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op4F(void) // BBR4
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x10))
- regs.pc += m;
+ if (!(b & 0x10))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op5F(void) // BBR5
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x20))
- regs.pc += m;
+ if (!(b & 0x20))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op6F(void) // BBR6
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x40))
- regs.pc += m;
+ if (!(b & 0x40))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op7F(void) // BBR7
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.a & 0x80))
- regs.pc += m;
+ if (!(b & 0x80))
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op8F(void) // BBS0
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x01)
- regs.pc += m;
+ if (b & 0x01)
+ HANDLE_BRANCH_TAKEN(m);
}
static void Op9F(void) // BBS1
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x02)
- regs.pc += m;
+ if (b & 0x02)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpAF(void) // BBS2
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x04)
- regs.pc += m;
+ if (b & 0x04)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpBF(void) // BBS3
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x08)
- regs.pc += m;
+ if (b & 0x08)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpCF(void) // BBS4
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x10)
- regs.pc += m;
+ if (b & 0x10)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpDF(void) // BBS5
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x20)
- regs.pc += m;
+ if (b & 0x20)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpEF(void) // BBS6
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x40)
- regs.pc += m;
+ if (b & 0x40)
+ HANDLE_BRANCH_TAKEN(m);
}
static void OpFF(void) // BBS7
{
+ uint8_t b = READ_ZP;
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.a & 0x80)
- regs.pc += m;
+ if (b & 0x80)
+ HANDLE_BRANCH_TAKEN(m);
}
/*
BEQ Relative BEQ Oper F0 2 2
*/
-// Branch taken adds a cycle, crossing page adds one more
-
-#define HANDLE_BRANCH_TAKEN(m) \
-{ \
- uint16_t oldpc = regs.pc; \
- regs.pc += m; \
- regs.clock++; \
- if ((oldpc ^ regs.pc) & 0xFF00) \
- regs.clock++; \
-}
-
// Branch opcodes
static void Op90(void) // BCC
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.cc & FLAG_C))
+ if (!(regs->cc & FLAG_C))
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void OpB0(void) // BCS
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.cc & FLAG_C)
+ if (regs->cc & FLAG_C)
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void OpF0(void) // BEQ
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.cc & FLAG_Z)
+ if (regs->cc & FLAG_Z)
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
/*
// BIT opcodes
-/* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag (except in immediate
- addressing mode where V & N are untouched.) The accumulator and the operand are ANDed and the
- Z flag is set appropriately. */
+/* 1. The BIT instruction copies bit 6 to the V flag, and bit 7 to the N flag
+ (except in immediate addressing mode where V & N are untouched.) The
+ accumulator and the operand are ANDed and the Z flag is set
+ appropriately. */
#define OP_BIT_HANDLER(m) \
- int8_t result = regs.a & (m); \
- regs.cc &= ~(FLAG_N | FLAG_V); \
- regs.cc |= ((m) & 0xC0); \
+ int8_t result = regs->a & (m); \
+ regs->cc &= ~(FLAG_N | FLAG_V); \
+ regs->cc |= ((m) & 0xC0); \
SET_Z(result)
static void Op89(void) // BIT #
{
int8_t m = READ_IMM;
- int8_t result = regs.a & m;
+ int8_t result = regs->a & m;
SET_Z(result);
}
static void Op3C(void) // BIT ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_BIT_HANDLER(m);
}
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.cc & FLAG_N)
+ if (regs->cc & FLAG_N)
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void OpD0(void) // BNE
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.cc & FLAG_Z))
+ if (!(regs->cc & FLAG_Z))
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void Op10(void) // BPL
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.cc & FLAG_N))
+ if (!(regs->cc & FLAG_N))
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void Op80(void) // BRA
{
int16_t m = (int16_t)(int8_t)READ_IMM;
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
/*
static void Op00(void) // BRK
{
- regs.cc |= FLAG_B; // Set B
- regs.pc++; // RTI comes back to the instruction one byte after the BRK
- regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
- regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
- regs.WrMem(0x0100 + regs.sp--, regs.cc);
- regs.cc |= FLAG_I; // Set I
- regs.cc &= ~FLAG_D; // & clear D
- regs.pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
+//#ifdef __DEBUG__
+#if 1
+WriteLog("\n*** BRK ***\n\n");
+WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
+ regs->pc, 0x0100 + regs->sp,
+ (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
+ (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
+ (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
+ (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y);
+#endif
+ regs->cc |= FLAG_B; // Set B
+ regs->pc++; // RTI comes back to the instruction one byte after the BRK
+ regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC and CC
+ regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
+ regs->WrMem(0x0100 + regs->sp--, regs->cc);
+ regs->cc |= FLAG_I; // Set I
+ regs->cc &= ~FLAG_D; // & clear D
+ regs->pc = RdMemW(0xFFFE); // Grab the IRQ vector & go...
}
/*
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (!(regs.cc & FLAG_V))
+ if (!(regs->cc & FLAG_V))
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
static void Op70(void) // BVS
{
int16_t m = (int16_t)(int8_t)READ_IMM;
- if (regs.cc & FLAG_V)
+ if (regs->cc & FLAG_V)
HANDLE_BRANCH_TAKEN(m)
-// regs.pc += m;
}
/*
static void Op18(void) // CLC
{
- regs.cc &= ~FLAG_C;
+ regs->cc &= ~FLAG_C;
}
/*
static void OpD8(void) // CLD
{
- regs.cc &= ~FLAG_D;
+ CLR_D;
}
/*
static void Op58(void) // CLI
{
- regs.cc &= ~FLAG_I;
+ regs->cc &= ~FLAG_I;
}
/*
static void OpB8(void) // CLV
{
- regs.cc &= ~FLAG_V;
+ regs->cc &= ~FLAG_V;
}
/*
// CMP opcodes
-/*
-Here's the latest: The CMP is NOT generating the Z flag when A=$C0!
-
-FABA: A0 07 LDY #$07 [PC=FABC, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
-FABC: C6 01 DEC $01 [PC=FABE, SP=01FF, CC=N--B-I--, A=00, X=00, Y=07]
-FABE: A5 01 LDA $01 [PC=FAC0, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
-FAC0: C9 C0 CMP #$C0 [PC=FAC2, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
-FAC2: F0 D7 BEQ $FA9B [PC=FAC4, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
-FAC4: 8D F8 07 STA $07F8 [PC=FAC7, SP=01FF, CC=N--B-I--, A=C0, X=00, Y=07]
-FAC7: B1 00 LDA ($00),Y
-*** Read at I/O address C007
- [PC=FAC9, SP=01FF, CC=---B-IZ-, A=00, X=00, Y=07]
-FAC9: D9 01 FB CMP $FB01,Y [PC=FACC, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
-FACC: D0 EC BNE $FABA [PC=FABA, SP=01FF, CC=---B-I--, A=00, X=00, Y=07]
-
-Should be fixed now... (was adding instead of subtracting!)
-
-Small problem here... First two should set the carry while the last one should clear it. !!! FIX !!! [DONE]
-
-FDF0: C9 A0 CMP #$A0 [PC=FDF2, SP=01F1, CC=---B-IZ-, A=A0, X=02, Y=03]
-FD7E: C9 E0 CMP #$E0 [PC=FD80, SP=01F4, CC=N--B-I--, A=A0, X=02, Y=03]
-FD38: C9 9B CMP #$9B [PC=FD3A, SP=01F2, CC=---B-I-C, A=A0, X=02, Y=03]
-
-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) \
- uint8_t result = regs.a - (m); \
- SET_ZNC_CMP(m, regs.a, result)
+ uint8_t result = regs->a - (m); \
+ SET_ZNC_CMP(m, regs->a, result)
static void OpC9(void) // CMP #
{
static void OpDD(void) // CMP ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_CMP_HANDLER(m);
}
static void OpD9(void) // CMP ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_CMP_HANDLER(m);
}
static void OpD1(void) // CMP (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint8_t m = READ_IND_ZP_Y;
OP_CMP_HANDLER(m);
}
// CPX opcodes
#define OP_CPX_HANDLER(m) \
- uint8_t result = regs.x - (m); \
- SET_ZNC_CMP(m, regs.x, result)
+ uint8_t result = regs->x - (m); \
+ SET_ZNC_CMP(m, regs->x, result)
static void OpE0(void) // CPX #
{
// CPY opcodes
#define OP_CPY_HANDLER(m) \
- uint8_t result = regs.y - (m); \
- SET_ZNC_CMP(m, regs.y, result)
+ uint8_t result = regs->y - (m); \
+ SET_ZNC_CMP(m, regs->y, result)
static void OpC0(void) // CPY #
{
static void Op3A(void) // DEA
{
- regs.a--;
- SET_ZN(regs.a);
+ regs->a--;
+ SET_ZN(regs->a);
}
/*
static void OpDE(void) // DEC ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_DEC_HANDLER(m);
WRITE_BACK(m);
}
-/*
-Here's one problem: DEX is setting the N flag!
-
-D3EE: A2 09 LDX #$09 [PC=D3F0, SP=01F7, CC=---B-I-C, A=01, X=09, Y=08]
-D3F0: 98 TYA [PC=D3F1, SP=01F7, CC=N--B-I-C, A=08, X=09, Y=08]
-D3F1: 48 PHA [PC=D3F2, SP=01F6, CC=N--B-I-C, A=08, X=09, Y=08]
-D3F2: B5 93 LDA $93,X [PC=D3F4, SP=01F6, CC=---B-IZC, A=00, X=09, Y=08]
-D3F4: CA DEX [PC=D3F5, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
-D3F5: 10 FA BPL $D3F1 [PC=D3F7, SP=01F6, CC=N--B-I-C, A=00, X=08, Y=08]
-D3F7: 20 84 E4 JSR $E484 [PC=E484, SP=01F4, CC=N--B-I-C, A=00, X=08, Y=08]
-
-should be fixed now...
-*/
-
/*
DEX Implied DEX CA 1 2
*/
static void OpCA(void) // DEX
{
- regs.x--;
- SET_ZN(regs.x);
+ regs->x--;
+ SET_ZN(regs->x);
}
/*
static void Op88(void) // DEY
{
- regs.y--;
- SET_ZN(regs.y);
+ regs->y--;
+ SET_ZN(regs->y);
}
/*
// EOR opcodes
#define OP_EOR_HANDLER(m) \
- regs.a ^= m; \
- SET_ZN(regs.a)
+ regs->a ^= m; \
+ SET_ZN(regs->a)
static void Op49(void) // EOR #
{
static void Op5D(void) // EOR ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_EOR_HANDLER(m);
}
static void Op59(void) // EOR ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_EOR_HANDLER(m);
}
static void Op51(void) // EOR (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint8_t m = READ_IND_ZP_Y;
OP_EOR_HANDLER(m);
}
static void Op1A(void) // INA
{
- regs.a++;
- SET_ZN(regs.a);
+ regs->a++;
+ SET_ZN(regs->a);
}
/*
static void OpFE(void) // INC ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_INC_HANDLER(m);
static void OpE8(void) // INX
{
- regs.x++;
- SET_ZN(regs.x);
+ regs->x++;
+ SET_ZN(regs->x);
}
/*
static void OpC8(void) // INY
{
- regs.y++;
- SET_ZN(regs.y);
+ regs->y++;
+ SET_ZN(regs->y);
}
/*
static void Op4C(void) // JMP ABS
{
- regs.pc = RdMemW(regs.pc);
+ regs->pc = RdMemW(regs->pc);
}
static void Op6C(void) // JMP (ABS)
{
-// uint16_t addr = RdMemW(regs.pc);
-//#ifdef __DEBUG__
-//WriteLog("\n[JMP ABS]: addr fetched = %04X, bytes at %04X = %02X %02X (RdMemw=%04X)\n",
-// addr, addr, regs.RdMem(addr), regs.RdMem(addr+1), RdMemW(addr));
-//#endif
-// addr = RdMemW(addr);
- regs.pc = RdMemW(RdMemW(regs.pc));
+ // Check for page crossing
+ uint16_t addressLo = regs->RdMem(regs->pc);
+
+ if (addressLo == 0xFF)
+ regs->clock++;
+
+ regs->pc = RdMemW(RdMemW(regs->pc));
}
static void Op7C(void) // JMP (ABS, X)
{
- regs.pc = RdMemW(RdMemW(regs.pc) + regs.x);
+ regs->pc = RdMemW(RdMemW(regs->pc) + regs->x);
}
/*
JSR Absolute JSR Abs 20 3 6
*/
-//This is not jumping to the correct address... !!! FIX !!! [DONE]
static void Op20(void) // JSR
{
- uint16_t addr = RdMemW(regs.pc);
- regs.pc++; // Since it pushes return address - 1...
- regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8);
- regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
- regs.pc = addr;
+ uint16_t addr = RdMemW(regs->pc);
+ regs->pc++; // Since it pushes return address - 1...
+ regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8);
+ regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
+ regs->pc = addr;
}
/*
// LDA opcodes
#define OP_LDA_HANDLER(m) \
- regs.a = m; \
- SET_ZN(regs.a)
+ regs->a = m; \
+ SET_ZN(regs->a)
static void OpA9(void) // LDA #
{
static void OpBD(void) // LDA ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_LDA_HANDLER(m);
}
static void OpB9(void) // LDA ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_LDA_HANDLER(m);
}
static void OpB1(void) // LDA (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint8_t m = READ_IND_ZP_Y;
OP_LDA_HANDLER(m);
}
// LDX opcodes
#define OP_LDX_HANDLER(m) \
- regs.x = m; \
- SET_ZN(regs.x)
+ regs->x = m; \
+ SET_ZN(regs->x)
static void OpA2(void) // LDX #
{
static void OpBE(void) // LDX ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_LDX_HANDLER(m);
}
/*
LDY Immediate LDY #Oper A0 2 2
Zero Page LDY Zpg A4 2 3
-Zero Page,Y LDY Zpg,X B4 2 4
+Zero Page,X LDY Zpg,X B4 2 4
Absolute LDY Abs AC 3 4
-Absolute,Y LDY Abs,X BC 3 4
+Absolute,X LDY Abs,X BC 3 4
*/
// LDY opcodes
#define OP_LDY_HANDLER(m) \
- regs.y = m; \
- SET_ZN(regs.y)
+ regs->y = m; \
+ SET_ZN(regs->y)
static void OpA0(void) // LDY #
{
static void OpBC(void) // LDY ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_LDY_HANDLER(m);
}
// LSR opcodes
#define OP_LSR_HANDLER(m) \
- regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
+ regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
(m) >>= 1; \
CLR_N; SET_Z((m))
static void Op4A(void) // LSR A
{
- OP_LSR_HANDLER(regs.a);
+ OP_LSR_HANDLER(regs->a);
}
static void Op46(void) // LSR ZP
static void Op5E(void) // LSR ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_LSR_HANDLER(m);
// ORA opcodes
#define OP_ORA_HANDLER(m) \
- regs.a |= m; \
- SET_ZN(regs.a)
+ regs->a |= m; \
+ SET_ZN(regs->a)
static void Op09(void) // ORA #
{
static void Op1D(void) // ORA ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m = READ_ABS_X;
OP_ORA_HANDLER(m);
}
static void Op19(void) // ORA ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint8_t m = READ_ABS_Y;
OP_ORA_HANDLER(m);
}
static void Op11(void) // ORA (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint8_t m = READ_IND_ZP_Y;
OP_ORA_HANDLER(m);
}
static void Op48(void) // PHA
{
- regs.WrMem(0x0100 + regs.sp--, regs.a);
+ regs->WrMem(0x0100 + regs->sp--, regs->a);
}
static void Op08(void) // PHP
{
- regs.cc |= FLAG_UNK; // Make sure that the unused bit is always set
- regs.WrMem(0x0100 + regs.sp--, regs.cc);
+ regs->cc |= FLAG_UNK; // Make sure that the unused bit is always set
+ regs->WrMem(0x0100 + regs->sp--, regs->cc);
}
/*
static void OpDA(void) // PHX
{
- regs.WrMem(0x0100 + regs.sp--, regs.x);
+ regs->WrMem(0x0100 + regs->sp--, regs->x);
}
/*
static void Op5A(void) // PHY
{
- regs.WrMem(0x0100 + regs.sp--, regs.y);
+ regs->WrMem(0x0100 + regs->sp--, regs->y);
}
/*
static void Op68(void) // PLA
{
- regs.a = regs.RdMem(0x0100 + ++regs.sp);
- SET_ZN(regs.a);
+ regs->a = regs->RdMem(0x0100 + ++regs->sp);
+ SET_ZN(regs->a);
}
static void Op28(void) // PLP
{
- regs.cc = regs.RdMem(0x0100 + ++regs.sp);
+ regs->cc = regs->RdMem(0x0100 + ++regs->sp);
}
/*
static void OpFA(void) // PLX
{
- regs.x = regs.RdMem(0x0100 + ++regs.sp);
- SET_ZN(regs.x);
+ regs->x = regs->RdMem(0x0100 + ++regs->sp);
+ SET_ZN(regs->x);
}
/*
static void Op7A(void) // PLY
{
- regs.y = regs.RdMem(0x0100 + ++regs.sp);
- SET_ZN(regs.y);
+ regs->y = regs->RdMem(0x0100 + ++regs->sp);
+ SET_ZN(regs->y);
}
/*
// ROL opcodes
#define OP_ROL_HANDLER(m) \
- uint8_t tmp = regs.cc & 0x01; \
- regs.cc = ((m) & 0x80 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
+ uint8_t tmp = regs->cc & 0x01; \
+ regs->cc = ((m) & 0x80 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
(m) = ((m) << 1) | tmp; \
SET_ZN((m))
static void Op2A(void) // ROL A
{
- OP_ROL_HANDLER(regs.a);
+ OP_ROL_HANDLER(regs->a);
}
static void Op26(void) // ROL ZP
static void Op3E(void) // ROL ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_ROL_HANDLER(m);
// ROR opcodes
#define OP_ROR_HANDLER(m) \
- uint8_t tmp = (regs.cc & 0x01) << 7; \
- regs.cc = ((m) & 0x01 ? regs.cc | FLAG_C : regs.cc & ~FLAG_C); \
+ uint8_t tmp = (regs->cc & 0x01) << 7; \
+ regs->cc = ((m) & 0x01 ? regs->cc | FLAG_C : regs->cc & ~FLAG_C); \
(m) = ((m) >> 1) | tmp; \
SET_ZN((m))
static void Op6A(void) // ROR A
{
- OP_ROR_HANDLER(regs.a);
+ OP_ROR_HANDLER(regs->a);
}
static void Op66(void) // ROR ZP
static void Op7E(void) // ROR ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint8_t m;
READ_ABS_X_WB(m);
OP_ROR_HANDLER(m);
static void Op40(void) // RTI
{
- regs.cc = regs.RdMem(0x0100 + ++regs.sp);
-//clear I (seems to be the case, either that or clear it in the IRQ setup...)
-//I can't find *any* verification that this is the case.
-// regs.cc &= ~FLAG_I;
- regs.pc = regs.RdMem(0x0100 + ++regs.sp);
- regs.pc |= (uint16_t)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
+ regs->cc = regs->RdMem(0x0100 + ++regs->sp);
+ regs->pc = regs->RdMem(0x0100 + ++regs->sp);
+ regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
}
/*
static void Op60(void) // RTS
{
- regs.pc = regs.RdMem(0x0100 + ++regs.sp);
- regs.pc |= (uint16_t)(regs.RdMem(0x0100 + ++regs.sp)) << 8;
- regs.pc++; // Since it pushes return address - 1...
-//printf("*** RTS: PC = $%04X, SP= $1%02X\n", regs.pc, regs.sp);
-//fflush(stdout);
+ regs->pc = regs->RdMem(0x0100 + ++regs->sp);
+ regs->pc |= (uint16_t)(regs->RdMem(0x0100 + ++regs->sp)) << 8;
+ regs->pc++; // Since it pushes return address - 1...
}
/*
// SBC opcodes
//This is non-optimal, but it works--optimize later. :-)
-//This is correct except for the BCD handling... !!! FIX !!! [Possibly DONE]
+// We do the BCD subtraction one nybble at a time to ensure a correct result.
+// 9 - m is a "Nine's Complement". We do the BCD subtraction as a 9s
+// complement addition because it's easier and it works. :-) Also, Decimal
+// mode incurs a once cycle penalty (for the decimal correction).
#define OP_SBC_HANDLER(m) \
- uint16_t sum = (uint16_t)regs.a - (m) - (uint16_t)((regs.cc & FLAG_C) ^ 0x01); \
+ uint16_t sum = (uint16_t)regs->a - (m) - (uint16_t)((regs->cc & FLAG_C) ^ 0x01); \
\
- if (regs.cc & FLAG_D) \
+ if (regs->cc & FLAG_D) \
{ \
- if ((sum & 0x0F) > 0x09) \
- sum -= 0x06; \
+ sum = (regs->a & 0x0F) + (9 - ((m) & 0x0F)) + (uint16_t)(regs->cc & FLAG_C); \
\
- if ((sum & 0xF0) > 0x90) \
- sum -= 0x60; \
+ if (sum > 0x09) \
+ sum += 0x06; \
+\
+ sum += (regs->a & 0xF0) + (0x90 - ((m) & 0xF0)); \
+\
+ if (sum > 0x99) \
+ sum += 0x60; \
+\
+ sum ^= 0x100; /* Invert carry, for active low borrow */ \
+ regs->clock++;\
} \
\
- regs.cc = (regs.cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
- regs.cc = ((regs.a ^ (m)) & (regs.a ^ sum) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V); \
- regs.a = sum & 0xFF; \
- SET_ZN(regs.a)
-
-/*
-D5AF: 38 SEC [PC=D5B0, SP=01F6, CC=---B-I-C, A=4C, X=00, Y=06]
-
-*** HERE'S where it sets the D flag on a subtract... Arg!
-
-D5B0: F1 9D SBC ($9D),Y [PC=D5B2, SP=01F6, CC=N--BDI--, A=FE, X=00, Y=06]
-
-Fixed. :-)
-*/
-
-//OLD V detection: regs.cc = ((regs.a ^ (m) ^ sum ^ (regs.cc << 7)) & 0x80 ? regs.cc | FLAG_V : regs.cc & ~FLAG_V);
+ regs->cc = (regs->cc & ~FLAG_C) | (((sum >> 8) ^ 0x01) & FLAG_C); \
+ regs->cc = ((regs->a ^ (m)) & (regs->a ^ sum) & 0x80 ? regs->cc | FLAG_V : regs->cc & ~FLAG_V); \
+ regs->a = sum & 0xFF; \
+ SET_ZN(regs->a)
static void OpE9(void) // SBC #
{
static void OpFD(void) // SBC ABS, X
{
+ HANDLE_PAGE_CROSSING_ABS_X;
uint16_t m = READ_ABS_X;
OP_SBC_HANDLER(m);
}
static void OpF9(void) // SBC ABS, Y
{
+ HANDLE_PAGE_CROSSING_ABS_Y;
uint16_t m = READ_ABS_Y;
OP_SBC_HANDLER(m);
}
static void OpF1(void) // SBC (ZP), Y
{
+ HANDLE_PAGE_CROSSING_IND_Y;
uint16_t m = READ_IND_ZP_Y;
OP_SBC_HANDLER(m);
}
static void Op38(void) // SEC
{
- regs.cc |= FLAG_C;
+ regs->cc |= FLAG_C;
}
/*
static void OpF8(void) // SED
{
- regs.cc |= FLAG_D;
+ regs->cc |= FLAG_D;
}
/*
static void Op78(void) // SEI
{
- regs.cc |= FLAG_I;
+ SET_I;
}
/*
static void Op85(void)
{
- regs.WrMem(EA_ZP, regs.a);
+ regs->WrMem(EA_ZP, regs->a);
}
static void Op95(void)
{
- regs.WrMem(EA_ZP_X, regs.a);
+ regs->WrMem(EA_ZP_X, regs->a);
}
static void Op8D(void)
{
- regs.WrMem(EA_ABS, regs.a);
+ regs->WrMem(EA_ABS, regs->a);
}
static void Op9D(void)
{
- regs.WrMem(EA_ABS_X, regs.a);
+ regs->WrMem(EA_ABS_X, regs->a);
}
static void Op99(void)
{
- regs.WrMem(EA_ABS_Y, regs.a);
+ regs->WrMem(EA_ABS_Y, regs->a);
}
static void Op81(void)
{
- regs.WrMem(EA_IND_ZP_X, regs.a);
+ regs->WrMem(EA_IND_ZP_X, regs->a);
}
static void Op91(void)
{
- regs.WrMem(EA_IND_ZP_Y, regs.a);
+ regs->WrMem(EA_IND_ZP_Y, regs->a);
}
static void Op92(void)
{
- regs.WrMem(EA_IND_ZP, regs.a);
+ regs->WrMem(EA_IND_ZP, regs->a);
}
/*
static void Op86(void)
{
- regs.WrMem(EA_ZP, regs.x);
+ regs->WrMem(EA_ZP, regs->x);
}
static void Op96(void)
{
- regs.WrMem(EA_ZP_X, regs.x);
+ regs->WrMem(EA_ZP_Y, regs->x);
}
static void Op8E(void)
{
- regs.WrMem(EA_ABS, regs.x);
+ regs->WrMem(EA_ABS, regs->x);
}
/*
static void Op84(void)
{
- regs.WrMem(EA_ZP, regs.y);
+ regs->WrMem(EA_ZP, regs->y);
}
static void Op94(void)
{
- regs.WrMem(EA_ZP_X, regs.y);
+ regs->WrMem(EA_ZP_X, regs->y);
}
static void Op8C(void)
{
- regs.WrMem(EA_ABS, regs.y);
+ regs->WrMem(EA_ABS, regs->y);
}
/*
static void Op64(void)
{
- regs.WrMem(EA_ZP, 0x00);
+ regs->WrMem(EA_ZP, 0x00);
}
static void Op74(void)
{
- regs.WrMem(EA_ZP_X, 0x00);
+ regs->WrMem(EA_ZP_X, 0x00);
}
static void Op9C(void)
{
- regs.WrMem(EA_ABS, 0x00);
+ regs->WrMem(EA_ABS, 0x00);
}
static void Op9E(void)
{
- regs.WrMem(EA_ABS_X, 0x00);
+ regs->WrMem(EA_ABS_X, 0x00);
}
/*
static void OpAA(void) // TAX
{
- regs.x = regs.a;
- SET_ZN(regs.x);
+ regs->x = regs->a;
+ SET_ZN(regs->x);
}
/*
static void OpA8(void) // TAY
{
- regs.y = regs.a;
- SET_ZN(regs.y);
+ regs->y = regs->a;
+ SET_ZN(regs->y);
}
/*
// TRB opcodes
#define OP_TRB_HANDLER(m) \
- SET_Z(m & regs.a); \
- m &= ~regs.a
+ SET_Z(m & regs->a); \
+ m &= ~regs->a
static void Op14(void) // TRB ZP
{
// TSB opcodes
#define OP_TSB_HANDLER(m) \
- SET_Z(m & regs.a); \
- m |= regs.a
+ SET_Z(m & regs->a); \
+ m |= regs->a
static void Op04(void) // TSB ZP
{
static void OpBA(void) // TSX
{
- regs.x = regs.sp;
- SET_ZN(regs.x);
+ regs->x = regs->sp;
+ SET_ZN(regs->x);
}
/*
static void Op8A(void) // TXA
{
- regs.a = regs.x;
- SET_ZN(regs.a);
+ regs->a = regs->x;
+ SET_ZN(regs->a);
}
/*
static void Op9A(void) // TXS
{
- regs.sp = regs.x;
+ regs->sp = regs->x;
}
/*
*/
static void Op98(void) // TYA
{
- regs.a = regs.y;
- SET_ZN(regs.a);
+ regs->a = regs->y;
+ SET_ZN(regs->a);
}
static void Op__(void)
{
- regs.cpuFlags |= V65C02_STATE_ILLEGAL_INST;
+ regs->cpuFlags |= V65C02_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])() = {
+static void (* exec_op[256])() = {
Op00, Op01, Op__, Op__, Op04, Op05, Op06, Op07, Op08, Op09, Op0A, Op__, Op0C, Op0D, Op0E, Op0F,
Op10, Op11, Op12, Op__, Op14, Op15, Op16, Op17, Op18, Op19, Op1A, Op__, Op1C, Op1D, Op1E, Op1F,
Op20, Op21, Op__, Op__, Op24, Op25, Op26, Op27, Op28, Op29, Op2A, Op__, Op2C, Op2D, Op2E, Op2F,
};
-//
-// 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];
-}
-
/*
FCA8: 38 698 WAIT SEC
FCA9: 48 699 WAIT2 PHA
//int instCount[256];
#ifdef __DEBUG__
bool dumpDis = false;
+//bool dumpDis = true;
+#endif
+
+/*
+On //e, $FCAA is the delay routine. (seems to not have changed from ][+)
+*/
+
+#define DO_BACKTRACE
+#ifdef DO_BACKTRACE
+#define BACKTRACE_SIZE 16384
+uint32_t btQueuePtr = 0;
+V65C02REGS btQueue[BACKTRACE_SIZE];
+uint8_t btQueueInst[BACKTRACE_SIZE][4];
#endif
-//Note: could enforce regs.clock to zero on starting the CPU with an Init() function...
-//bleh.
-//static uint32_t limit = 0;
//
// Function to execute 65C02 for "cycles" cycles
//
+//static bool first = true;
void Execute65C02(V65C02REGS * context, uint32_t cycles)
{
- myMemcpy(®s, context, sizeof(V65C02REGS));
+ regs = context;
- // Execute here...
-// NOTE: There *must* be some way of doing this without requiring the caller to subtract out
-// the previous run's cycles. !!! FIX !!!
-// Could try:
-// while (regs.clock < regs.clock + cycles) <-- won't work
-/*
- // This isn't as accurate as subtracting out cycles from regs.clock...
- // Unless limit is a static variable, adding cycles to it each time through...
- uint32_t limit = regs.clock + cycles;
- while (regs.clock < limit)
-*/
-// but have wraparound to deal with. :-/
-/*
-Let's see...
-
- if (regs.clock + cycles > 0xFFFFFFFF)
- wraparound = true;
-*/
- uint64_t endCycles = regs.clock + (uint64_t)cycles;
+ // Calculate number of clock cycles to run for
+ uint64_t endCycles = regs->clock + (uint64_t)cycles - regs->overflow;
- while (regs.clock < endCycles)
+ while (regs->clock < endCycles)
+ {
+// Hard disk debugging
+#if 0
+if (first && (regs->pc == 0x801))
+{
+// regs->WrMem(0x42, 1); // v3.0 does this now...
+ regs->WrMem(0x44, 0); // who writes non-zero to here??? (AHSSC does)
+ first = false;
+// dumpDis = true;
+//WriteLog("V65C02: Executing $801...\n");
+}
+else if (regs->pc == 0x869)
+{
+/* regs->WrMem(0x42, 1);
+ first = false;//*/
+/* static char disbuf[80];
+ uint16_t pc=0x801;
+ while (pc < 0xA00)
{
+ pc += Decode65C02(regs, disbuf, pc);
+ WriteLog("%s\n", disbuf);
+ }*/
+/* dumpDis = true;
+ WriteLog("\n>>> $42-7: %02X %02X %02X %02X %02X %02X\n\n", regs->RdMem(0x42), regs->RdMem(0x43), regs->RdMem(0x44), regs->RdMem(0x45), regs->RdMem(0x46), regs->RdMem(0x47));//*/
+}
+#endif
+#if 0
+//Epoch
+if (regs->pc == 0x0518)
+{
+ dumpDis = true;
+}
+else if (regs->pc == 0x051E)
+{
+ uint16_t c1 = regs->RdMem(0xFF);
+ uint16_t c2 = regs->RdMem(0x00);
+ WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
+ WriteLog("--> $%02X\n", regs->RdMem((c2 << 8) | c1));
+}
+else if (regs->pc == 0x0522)
+{
+ uint16_t c1 = regs->RdMem(0xFF);
+ uint16_t c2 = regs->RdMem(0x00);
+ WriteLog("$FF/$00 = $%02X $%02X\n", c1, c2);
+ WriteLog("--> $%02X\n", regs->RdMem(((c2 << 8) | c1) + 1));
+}
+#endif
+#if 0
+// Up N Down testing
+// Now Ankh testing...
+static bool inDelay = false;
+static bool inBell = false;
+static bool inReadSector = false;
+if (regs->pc == 0xFCA8 && !inBell && !inReadSector)
+{
+ dumpDis = false;
+ inDelay = true;
+ WriteLog("*** DELAY\n");
+}
+else if (regs->pc == 0xFCB3 && inDelay && !inBell && !inReadSector)
+{
+ dumpDis = true;
+ inDelay = false;
+}
+if (regs->pc == 0xFBD9)
+{
+ dumpDis = false;
+ inBell = true;
+ WriteLog("*** BELL1\n");
+}
+else if (regs->pc == 0xFBEF && inBell)
+{
+ dumpDis = true;
+ inBell = false;
+}
+else if (regs->pc == 0xC600)
+{
+ dumpDis = false;
+ WriteLog("*** DISK @ $C600\n");
+}
+else if (regs->pc == 0x801)
+{
+ WriteLog("*** DISK @ $801\n");
+ dumpDis = true;
+}
+else if (regs->pc == 0xC119)
+{
+ dumpDis = false;
+ WriteLog("*** BIOS @ $C119\n");
+}
+else if (regs->pc == 0xC117)
+{
+ dumpDis = true;
+}
+else if (regs->pc == 0x843)
+{
+ dumpDis = false;
+ inReadSector = true;
+ uint16_t lo = regs->RdMem(0x26);
+ uint16_t hi = regs->RdMem(0x27);
+ WriteLog("\n*** DISK Read sector ($26=$%04X)...\n\n", (hi << 8) | lo);
+}
+else if (regs->pc == 0x8FC)
+{
+ dumpDis = true;
+ inReadSector = false;
+}
+else if (regs->pc == 0xA8A8 || regs->pc == 0xC100)
+{
+ dumpDis = false;
+}
+else if (regs->pc == 0x8FD)
+{
+// regs->WrMem(0x827, 3);
+// regs->WrMem(0x82A, 0);
+//1 doesn't work, but 2 does (only with WOZ, not with DSK; DSK needs 4)...
+// regs->WrMem(0x0D, 4);
+}
+
+#endif
+#if 0
+static bool inDelay = false;
+static bool inMLI = false;
+static uint16_t mliReturnAddr = 0;
+static uint8_t mliCmd = 0;
+if (regs->pc == 0x160B && dumpDis)
+{
+ inDelay = true;
+ dumpDis = false;
+ WriteLog("*** DELAY\n");
+}
+else if (regs->pc == 0x1616 && inDelay)
+{
+ inDelay = false;
+ dumpDis = true;
+}
+else if (regs->pc == 0xD385 && dumpDis)
+{
+ inDelay = true;
+ dumpDis = false;
+ WriteLog("*** DELAY\n");
+}
+else if (regs->pc == 0xD397 && inDelay)
+{
+ inDelay = false;
+ dumpDis = true;
+}
+else if (regs->pc == 0xBF00 && dumpDis)
+{
+ uint16_t lo = regs->RdMem(regs->sp + 0x101);
+ uint16_t hi = regs->RdMem(regs->sp + 0x102);
+ mliReturnAddr = ((hi << 8) | lo) + 1;
+ mliCmd = regs->RdMem(mliReturnAddr);
+ WriteLog("*** Calling ProDOS MLI with params: %02X %04X\n", mliCmd, RdMemW(mliReturnAddr + 1));
+ mliReturnAddr += 3;
+ inMLI = true;
+
+ // We want to see what's going on in the WRITE BLOCK command... :-P
+// if (mliCmd != 0x81)
+// dumpDis = false;
+}
+else if (regs->pc == mliReturnAddr && inMLI)
+{
+//extern bool stopWriting;
+//Stop writing to disk after the first block is done
+// if (mliCmd == 0x81)
+// stopWriting = true;
+
+ inMLI = false;
+ dumpDis = true;
+}
+else if (regs->pc == 0xAB3A && dumpDis && !inDelay)
+{
+ dumpDis = false;
+ inDelay = true;
+ WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
+}
+else if (regs->pc == 0xAB4A && inDelay)
+{
+ dumpDis = true;
+ inDelay = false;
+}
+
+if (regs->pc == 0xA80B)
+ dumpDis = true;
+
+#endif
+#if 0
+static bool weGo = false;
+static bool inDelay = false;
+if (regs->pc == 0x92BA)
+{
+ dumpDis = true;
+ weGo = true;
+}
+else if (regs->pc == 0xAB3A && weGo && !inDelay)
+{
+ dumpDis = false;
+ inDelay = true;
+ WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
+}
+else if (regs->pc == 0xAB4A && weGo)
+{
+ dumpDis = true;
+ inDelay = false;
+}
+else if (regs->pc == 0xA8B5 && weGo)
+{
+ WriteLog("\n$D4=%02X, $AC1F=%02X, $AC20=%02X\n\n", regs->RdMem(0xD4), regs->RdMem(0xAC1F), regs->RdMem(0xAC20));
+}
+/*else if (regs->pc == 0xA8C4 && weGo)
+{
+ WriteLog("Cheating... (clearing Carry flag)\n");
+ regs->cc &= ~FLAG_C;
+}*/
+#endif
+#if 0
+static bool weGo = false;
+if (regs->pc == 0x80AE)
+{
+ dumpDis = true;
+ weGo = true;
+}
+else if (regs->pc == 0xFCA8 && weGo)
+{
+ dumpDis = false;
+ WriteLog("\n*** DELAY (A=$%02X)\n\n", regs->a);
+}
+else if (regs->pc == 0xFCB3 && weGo)
+{
+ dumpDis = true;
+}
+#endif
#if 0
-/*if (regs.pc == 0x4007)
+/*if (regs->pc == 0x4007)
{
dumpDis = true;
}//*/
-if (regs.pc == 0x444B)
+if (regs->pc == 0x444B)
{
WriteLog("\n*** End of wait...\n\n");
dumpDis = true;
}//*/
-if (regs.pc == 0x444E)
+if (regs->pc == 0x444E)
{
WriteLog("\n*** Start of wait...\n\n");
dumpDis = false;
}//*/
#endif
+/*if (regs->pc >= 0xC600 && regs->pc <=0xC6FF)
+{
+ dumpDis = true;
+}
+else
+ dumpDis = false;//*/
+/*if (regs->pc == 0xE039)
+{
+ dumpDis = true;
+}//*/
+#if 0
+/*if (regs->pc == 0x0801)
+{
+ WriteLog("\n*** DISK BOOT subroutine...\n\n");
+ dumpDis = true;
+}//*/
+if (regs->pc == 0xE000)
+{
+#if 0
+ WriteLog("\n*** Dump of $E000 routine ***\n\n");
+
+ for(uint32_t addr=0xE000; addr<0xF000;)
+ {
+ addr += Decode65C02(addr);
+ WriteLog("\n");
+ }
+#endif
+ WriteLog("\n*** DISK part II subroutine...\n\n");
+ dumpDis = true;
+}//*/
+if (regs->pc == 0xD000)
+{
+ WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
+ dumpDis = false;
+}//*/
+if (regs->pc == 0xD1BE)
+{
+// WriteLog("\n*** DISK part II subroutine...\n\n");
+ dumpDis = true;
+}//*/
+if (regs->pc == 0xD200)
+{
+ WriteLog("\n*** CUSTOM SCREEN subroutine...\n\n");
+ dumpDis = false;
+}//*/
+if (regs->pc == 0xD269)
+{
+// WriteLog("\n*** DISK part II subroutine...\n\n");
+ dumpDis = true;
+}//*/
+#endif
+//if (regs->pc == 0xE08E)
+/*if (regs->pc == 0xAD33)
+{
+ WriteLog("\n*** After loader ***\n\n");
+ dumpDis = true;
+}//*/
+/*if (regs->pc == 0x0418)
+{
+ WriteLog("\n*** CUSTOM DISK READ subroutine...\n\n");
+ dumpDis = false;
+}
+if (regs->pc == 0x0)
+{
+ dumpDis = true;
+}//*/
#ifdef __DEBUGMON__
//WAIT is commented out here because it's called by BELL1...
-if (regs.pc == 0xFCA8)
+if (regs->pc == 0xFCA8)
{
WriteLog("\n*** WAIT subroutine...\n\n");
dumpDis = false;
}//*/
-if (regs.pc == 0xFBD9)
+if (regs->pc == 0xFBD9)
{
WriteLog("\n*** BELL1 subroutine...\n\n");
// dumpDis = false;
}//*/
-if (regs.pc == 0xFC58)
+if (regs->pc == 0xFC58)
{
WriteLog("\n*** HOME subroutine...\n\n");
// dumpDis = false;
}//*/
-if (regs.pc == 0xFDED)
+if (regs->pc == 0xFDED)
{
WriteLog("\n*** COUT subroutine...\n\n");
dumpDis = false;
#endif
#if 0
// ProDOS debugging
-if (regs.pc == 0x2000)
+if (regs->pc == 0x2000)
dumpDis = true;
#endif
#ifdef __DEBUG__
+#ifdef DO_BACKTRACE
+//uint32_t btQueuePtr = 0;
+//V65C02REGS btQueue[BACKTRACE_SIZE];
+//uint8_t btQueueInst[BACKTRACE_SIZE][4];
+memcpy(&btQueue[btQueuePtr], regs, sizeof(V65C02REGS));
+btQueuePtr = (btQueuePtr + 1) % BACKTRACE_SIZE;
+#endif
+#endif
+#ifdef __DEBUG__
+static char disbuf[80];
if (dumpDis)
- Decode65C02(regs.pc);
+{
+ Decode65C02(regs, disbuf, regs->pc);
+ WriteLog("%s", disbuf);
+}
#endif
- uint8_t opcode = regs.RdMem(regs.pc++);
+ uint8_t opcode = regs->RdMem(regs->pc++);
-//if (!(regs.cpuFlags & V65C02_STATE_ILLEGAL_INST))
+#if 0
+if (opcode == 0)
+{
+ static char disbuf[80];
+ uint32_t btStart = btQueuePtr - 12 + (btQueuePtr < 12 ? BACKTRACE_SIZE : 0);
+
+ for(uint32_t i=btStart; i<btQueuePtr; i++)
+ {
+ Decode65C02(regs, disbuf, btQueue[i].pc);
+ WriteLog("%s\n", disbuf);
+ }
+}
+#endif
+//if (!(regs->cpuFlags & V65C02_STATE_ILLEGAL_INST))
//instCount[opcode]++;
- exec_op[opcode](); // Execute that opcode...
- regs.clock += CPUCycles[opcode];
+ // We need this because the opcode function could add 1 or 2 cycles
+ // which aren't accounted for in CPUCycles[].
+ uint64_t clockSave = regs->clock;
+
+ // Execute that opcode...
+ exec_op[opcode]();
+ regs->clock += CPUCycles[opcode];
+
+ // Tell the timer function (if any) how many PHI2s have elapsed...
+ if (regs->Timer)
+ regs->Timer(regs->clock - clockSave);
+
#ifdef __DEBUG__
if (dumpDis)
- WriteLog(" [PC=%04X, SP=%04X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X]\n",
- regs.pc, 0x0100 + regs.sp,
- (regs.cc & FLAG_N ? "N" : "-"), (regs.cc & FLAG_V ? "V" : "-"),
- (regs.cc & FLAG_B ? "B" : "-"), (regs.cc & FLAG_D ? "D" : "-"),
- (regs.cc & FLAG_I ? "I" : "-"), (regs.cc & FLAG_Z ? "Z" : "-"),
- (regs.cc & FLAG_C ? "C" : "-"), regs.a, regs.x, regs.y);
+ WriteLog(" [SP=01%02X, CC=%s%s.%s%s%s%s%s, A=%02X, X=%02X, Y=%02X](%d)\n",
+ regs->sp,
+ (regs->cc & FLAG_N ? "N" : "-"), (regs->cc & FLAG_V ? "V" : "-"),
+ (regs->cc & FLAG_B ? "B" : "-"), (regs->cc & FLAG_D ? "D" : "-"),
+ (regs->cc & FLAG_I ? "I" : "-"), (regs->cc & FLAG_Z ? "Z" : "-"),
+ (regs->cc & FLAG_C ? "C" : "-"), regs->a, regs->x, regs->y, regs->clock - clockSave);
#endif
#ifdef __DEBUGMON__
-if (regs.pc == 0xFCB3) // WAIT exit point
+if (regs->pc == 0xFCB3) // WAIT exit point
{
dumpDis = true;
}//*/
-/*if (regs.pc == 0xFBEF) // BELL1 exit point
+/*if (regs->pc == 0xFBEF) // BELL1 exit point
{
dumpDis = true;
}//*/
-/*if (regs.pc == 0xFC22) // HOME exit point
+/*if (regs->pc == 0xFC22) // HOME exit point
{
dumpDis = true;
}//*/
-if (regs.pc == 0xFDFF) // COUT exit point
+if (regs->pc == 0xFDFF) // COUT exit point
{
dumpDis = true;
}
-if (regs.pc == 0xFBD8)
+if (regs->pc == 0xFBD8)
{
WriteLog("\n*** BASCALC set BASL/H = $%04X\n\n", RdMemW(0x0028));
}//*/
#endif
//These should be correct now...
- if (regs.cpuFlags & V65C02_ASSERT_LINE_RESET)
+ if (regs->cpuFlags & V65C02_ASSERT_LINE_RESET)
{
-#ifdef __DEBUG__
-WriteLog("\n*** RESET ***\n\n");
-#endif
// Not sure about this...
- regs.sp = 0xFF;
- regs.cc = FLAG_B | FLAG_I; // Reset the CC register
- regs.pc = RdMemW(0xFFFC); // And load PC with the RESET vector
+ regs->sp = 0xFF;
+ regs->cc = FLAG_I; // Reset the CC register
+ regs->pc = RdMemW(0xFFFC); // And load PC with RESET vector
- context->cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
- regs.cpuFlags &= ~V65C02_ASSERT_LINE_RESET;
+ regs->cpuFlags = 0; // Clear CPU flags...
+#ifdef __DEBUG__
+WriteLog("\n*** RESET *** (PC = $%04X)\n\n", regs->pc);
+#endif
}
- else if (regs.cpuFlags & V65C02_ASSERT_LINE_NMI)
+ else if (regs->cpuFlags & V65C02_ASSERT_LINE_NMI)
{
#ifdef __DEBUG__
WriteLog("\n*** NMI ***\n\n");
#endif
- regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
- regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
- regs.WrMem(0x0100 + regs.sp--, regs.cc);
- regs.cc |= FLAG_I; // Set I
- regs.cc &= ~FLAG_D; // & clear D
- regs.pc = RdMemW(0xFFFA); // And do it!
-
- regs.clock += 7;
- context->cpuFlags &= ~V65C02_ASSERT_LINE_NMI;// Reset the asserted line (NMI)...
- regs.cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset the asserted line (NMI)...
+ regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
+ regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
+ regs->WrMem(0x0100 + regs->sp--, regs->cc);
+ SET_I;
+ CLR_D;
+ regs->pc = RdMemW(0xFFFA); // Jump to NMI vector
+
+ regs->clock += 7;
+ regs->cpuFlags &= ~V65C02_ASSERT_LINE_NMI; // Reset NMI line
}
- else if (regs.cpuFlags & V65C02_ASSERT_LINE_IRQ)
+ else if ((regs->cpuFlags & V65C02_ASSERT_LINE_IRQ)
+ // IRQs are maskable, so check if the I flag is clear
+ && (!(regs->cc & FLAG_I)))
{
- if (!(regs.cc & FLAG_I)) // Process an interrupt (I=0)?
- {
#ifdef __DEBUG__
WriteLog("\n*** IRQ ***\n\n");
+WriteLog("Clock=$%X\n", regs->clock);
+//dumpDis = true;
#endif
- regs.WrMem(0x0100 + regs.sp--, regs.pc >> 8); // Save PC and CC
- regs.WrMem(0x0100 + regs.sp--, regs.pc & 0xFF);
- regs.WrMem(0x0100 + regs.sp--, regs.cc);
- regs.cc |= FLAG_I; // Set I
- regs.cc &= ~FLAG_D; // & clear D
- regs.pc = RdMemW(0xFFFE); // And do it!
-
- regs.clock += 7;
- context->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
- regs.cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset the asserted line (IRQ)...
- }
+ regs->WrMem(0x0100 + regs->sp--, regs->pc >> 8); // Save PC & CC
+ regs->WrMem(0x0100 + regs->sp--, regs->pc & 0xFF);
+ regs->WrMem(0x0100 + regs->sp--, regs->cc);
+ SET_I;
+ CLR_D;
+ regs->pc = RdMemW(0xFFFE); // Jump to IRQ vector
+
+ regs->clock += 7;
+ regs->cpuFlags &= ~V65C02_ASSERT_LINE_IRQ; // Reset IRQ line
}
}
- myMemcpy(context, ®s, sizeof(V65C02REGS));
-}
-
-
-//
-// Get the clock of the currently executing CPU
-//
-uint64_t GetCurrentV65C02Clock(void)
-{
- return regs.clock;
+ // 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 non-
+ // negative, because the condition that exits the main loop above is
+ // written such that regs->clock has to be equal or larger than endCycles
+ // to exit from it.
+ regs->overflow = regs->clock - endCycles;
}