/*
 * The functions to execute for each opcode.
 *
 * If _65C12_ is defined then the extra Master 65C12 processor instructions
 * are added.
 *
 * If _R65C02_ is defined then the extra Rockwell R65C02 processor (as used
 * in the Master Turbo and 6502 2nd processors) instructions are added.
 *
 * Unimplemented instructions are considered as nops, but of the correct
 * length (ie not all invalid instructions are single byte).
 *
 * 4/12/93 jkb	creation
 */

#include "6502P.h"
#include "memory.h"
#include "unix_io.h"

/*
 * Some common things between opcodes - pushing an pulling the stack
 */
#define pushb(val) writebq(SP + 0x100, val); SP--
#define pullb()    (SP++, readbq(SP + 0x100))

#define pushw(val) pushb((val) / 0x100); pushb((val) & 0xff)
/* #define pullw() (pullb() + 0x100 * pullb()) */

/*
 * Defs for each of the valid opcode types
 */
#define LDA(mode, len, time, func) \
{ \
    A=read##func(mode()); \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define STA(mode, len, time, func) \
{ \
    write##func(mode(), A); \
    inc_PC(len); \
    inc_time(time); \
}

#define LDX(mode, len, time, func) \
{ \
    X=read##func(mode()); \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define STX(mode, len, time, func) \
{ \
    write##func(mode(), X); \
    inc_PC(len); \
    inc_time(time); \
}

#define LDY(mode, len, time, func) \
{ \
    Y=read##func(mode()); \
    reset_ZN(Y); \
    inc_PC(len); \
    inc_time(time); \
}

#define STY(mode, len, time, func) \
{ \
    write##func(mode(), Y); \
    inc_PC(len); \
    inc_time(time); \
}

#ifdef _65C12_
#define STZ(mode, len, time) \
{ \
    writebq(mode(), 0); \
    inc_PC(len); \
    inc_time(time); \
}
#endif

#define AND(mode, len, time, func) \
{ \
    A &= read##func(mode()); \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define EOR(mode, len, time, func) \
{ \
    A ^= read##func(mode()); \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define ORA(mode, len, time, func) \
{ \
    A |= read##func(mode()); \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define DEC(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte val = read##func(addr) - 1; \
\
    write##func(addr, val); \
    reset_ZN(val); \
    inc_PC(len); \
    inc_time(time); \
}

#define INC(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte tmp = read##func(addr) + 1; \
\
    write##func(addr, tmp); \
    reset_ZN(tmp); \
    inc_PC(len); \
    inc_time(time); \
}

#define NOP(len) \
{ \
    inc_PC(len); \
    inc_time(2); /* FIXME: true for 2/3 byte NOPs too? */ \
}

#define CLC(len, time) \
{ \
    clr_C(); \
    inc_PC(len); \
    inc_time(time); \
}

#define CLD(len, time) \
{ \
    clr_D(); \
    inc_PC(len); \
    inc_time(time); \
}

#define CLI(len, time) \
{ \
    clr_I(); \
    inc_PC(len); \
    inc_time(time); \
}

#define CLV(len, time) \
{ \
    clr_V(); \
    inc_PC(len); \
    inc_time(time); \
}

#define SEC(len, time) \
{ \
    set_C(); \
    inc_PC(len); \
    inc_time(time); \
}

#define SED(len, time) \
{ \
    set_D(); \
    inc_PC(len); \
    inc_time(time); \
}

#define SEI(len, time) \
{ \
    set_I(); \
    inc_PC(len); \
    inc_time(time); \
}

#define DEA(len, time) \
{ \
    A--; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define INA(len, time) \
{ \
    A++; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define DEX(len, time) \
{ \
    X--; \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define INX(len, time) \
{ \
    X++; \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define DEY(len, time) \
{ \
    Y--; \
    reset_ZN(Y); \
    inc_PC(len); \
    inc_time(time); \
}

#define INY(len, time) \
{ \
    Y++; \
    reset_ZN(Y); \
    inc_PC(len); \
    inc_time(time); \
}

/* FIXME: how does SED changes CMP? At all? It _does_ do a subtraction */
#define CMP(mode, len, time, func) \
{ \
    unsigned int tmp; \
\
    tmp = read##func(mode()); \
    reset_C(A >= tmp); \
/*    reset_ZN(A-tmp); */\
    reset_Z(A == tmp); \
    reset_N(((byte)(A - tmp)) & 0x80); \
    inc_PC(len); \
    inc_time(time); \
}

#define CPX(mode, len, time, func) \
{ \
    unsigned int tmp; \
\
    tmp = read##func(mode()); \
    reset_C(X >= tmp); \
    reset_Z(X == tmp); \
    reset_N(((byte)(X - tmp)) & 0x80); \
    inc_PC(len); \
    inc_time(time); \
}

#define CPY(mode, len, time, func) \
{ \
    unsigned int tmp; \
\
    tmp = read##func(mode()); \
    reset_C(Y >= tmp); \
    reset_Z(Y == tmp); \
    reset_N(((byte)(Y - tmp)) & 0x80); \
    inc_PC(len); \
    inc_time(time); \
}

#define TAX(len, time) \
{ \
    X = A; \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define TAY(len, time) \
{ \
    Y = A; \
    reset_ZN(Y); \
    inc_PC(len); \
    inc_time(time); \
}

#define TSX(len, time) \
{ \
    X = SP; \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define TXA(len, time) \
{ \
    A = X; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define TXS(len, time) \
{ \
    SP = X; \
    inc_PC(len); \
    inc_time(time); \
}

#define TYA(len, time) \
{ \
    A = Y; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

/*
 * By far the most yucky.
 * There's so very odd things here - especially with BCD.
 * Eg: BC + BC == BE (sets C, V, N). Apparently the simplest approach means
 * this is correctly, luckily :-)
 *
 * [ BCD in 6502 apparently leaves N, V and Z in an indeterminant state. The
 *   65C12 (and 65C02?) chips leaves these correct at the expense of one
 *   extra cycle when in BCD. ]
 */
#ifdef _65C12_ /* also R65C02? */
#    define ADC_KLUDGE get_D()
#else
#    define ADC_KLUDGE 0
#endif
#define ADC(mode, len, time, func) \
{ \
    byte M = read##func(mode()); \
    byte A_o = A; \
    signed int A2; \
\
    if (get_D()) { \
	int Alo = A & 0x0f; \
	int Ahi = A / 0x10; \
\
	Alo += (M & 0x0f) + get_C(); \
	if (Alo >= 10) { \
	    Alo -= 10; \
	    Ahi++; \
	} \
	Ahi += M / 0x10; \
	if (Ahi >= 10) { \
	    Ahi -= 10; \
	    set_C(); \
	} else { \
	    clr_C(); \
	} \
	A = Alo + 0x10 * Ahi; \
    } else { \
	A2 = A + M + get_C(); \
        A = A2 & 0xff; \
	reset_C(A2 >= 0x100); \
    } \
    /* Horrid logic: V is set if the sign of the answer is wrong. \
     * Eg: A is negative, M is negative, but A2 is positive. \
     */ \
    reset_ZN(A); \
    reset_V(((A_o & 0x80) == (M & 0x80)) && ((A_o & 0x80) != (A & 0x80)));\
/*    reset_V((A_o & 0x80) != (A & 0x80) && !get_Z()); */ \
\
    inc_PC(len); \
    inc_time(len); \
}

#define SBC(mode, len, time, func) \
{ \
    byte M = read##func(mode()); \
    byte A_o = A; \
    signed int A2; \
\
    if (get_D()) { \
	int Alo = A & 0x0f; \
	int Ahi = A / 0x10; \
\
	Alo -= (M & 0x0f) - !get_C(); \
	if (Alo <= 0) { \
	    Alo += 10; \
	    Ahi--; \
	} \
	Ahi -= M / 0x10; \
	if (Ahi <= 0) { \
	    Ahi += 10; \
	    clr_C(); \
	} else { \
	    set_C(); \
	} \
	A = Alo + 0x10 * Ahi; \
    } else { \
	A2 = A - M - (!get_C()); \
        A = A2 & 0xff; \
	reset_C(!(A2 < 0x00)); \
    } \
    /* Horrid logic: V is set if the sign of the answer is wrong. \
     * Eg: A is negative, M is negative, but A2 is positive. \
     */ \
    reset_ZN(A); \
    reset_V((A_o & 0x80) != (A & 0x80) && !get_Z()); \
\
    inc_PC(len); \
    inc_time(len); \
}

#define JMP(mode, len, time) \
{ \
    PC = mode(); \
    inc_time(time); \
}

#define PHA(len, time) \
{ \
    pushb(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define PHP(len, time) \
{ \
    pushb(P); \
    inc_PC(len); \
    inc_time(time); \
}

#define PLA(len, time) \
{ \
    A=pullb(); \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define PLP(len, time) \
{ \
    P=pullb() | P_U; \
    inc_PC(len); \
    inc_time(time); \
}

#ifdef _65C12_
#define PHX(len, time) \
{ \
    pushb(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define PHY(len, time) \
{ \
    pushb(Y); \
    inc_PC(len); \
    inc_time(time); \
}

#define PLX(len, time) \
{ \
    X=pullb(); \
    reset_ZN(X); \
    inc_PC(len); \
    inc_time(time); \
}

#define PLY(len, time) \
{ \
    Y=pullb(); \
    reset_ZN(Y); \
    inc_PC(len); \
    inc_time(time); \
}
#endif /* _65C12_ */

#define JSR(mode, len, time) \
{ \
    pushw(PC + len-1); \
    PC = mode(); \
    inc_time(time); \
}

#define RTS(len, time) \
{ \
    PC = readwq(SP + 0x101) + 1; SP += 2; \
    inc_time(time); \
}

#define RTI(len, time) \
{ \
    P = pullb(); \
    PC = readwq(SP + 0x101); SP += 2; \
    inc_time(time); \
}

#define ASL(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = read##func(addr); \
    reset_C(M & 0x80); \
    write##func(addr, M <<= 1); \
    reset_ZN(M); \
    inc_PC(len); \
    inc_time(time); \
}

#define ASLA(len, time) \
{ \
    reset_C(A & 0x80); \
    A <<= 1; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define LSR(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = read##func(addr); \
    reset_C(M & 0x01); \
    write##func(addr, M >>= 1); \
    reset_Z(M == 0); \
    clr_N(); \
    inc_PC(len); \
    inc_time(time); \
}

#define LSRA(len, time) \
{ \
    reset_C(A & 0x01); \
    A >>= 1; \
    reset_Z(A == 0); \
    clr_N(); \
    inc_PC(len); \
    inc_time(time); \
}

#define ROL(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = read##func(addr); \
    int tmp = get_C(); \
    reset_C(M & 0x80); \
    write##func(addr, M = (M << 1) + tmp); \
    reset_ZN(M); \
    inc_PC(len); \
    inc_time(time); \
}

#define ROLA(len, time) \
{ \
    int tmp = get_C(); \
    reset_C(A & 0x80); \
    A = (A << 1) + tmp; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define ROR(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = read##func(addr); \
    int tmp = get_C(); \
    reset_C(M & 0x01); \
    write##func(addr, M = (M >> 1) + 0x80 * tmp); \
    reset_ZN(M); \
    inc_PC(len); \
    inc_time(time); \
}

#define RORA(len, time) \
{ \
    int tmp = get_C(); \
    reset_C(A & 0x01); \
    A = (A >> 1) + 0x80 * tmp; \
    reset_ZN(A); \
    inc_PC(len); \
    inc_time(time); \
}

#define BCC(len, time) \
{ \
    if (!get_C()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BCS(len, time) \
{ \
    if (get_C()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BEQ(len, time) \
{ \
    if (get_Z()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BNE(len, time) \
{ \
    if (!get_Z()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BMI(len, time) \
{ \
    if (get_N()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BPL(len, time) \
{ \
    if (!get_N()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BVC(len, time) \
{ \
    if (get_V() == 0) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#define BVS(len, time) \
{ \
    if (get_V()) { \
	byte b = readb(imm()); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_time(time); \
    } \
}

#ifdef _65C12_
#define BRA(len, time) \
{ \
    byte b = readb(imm()); \
    word oPC = PC; \
    inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
    inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
}
#endif

#ifdef _R65C02_
#define BBR(bit, len, time) \
{ \
    byte b = readbq(zp); \
    if (!(b & bit)) { \
	byte b - readb(PC + 2); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_PC(time); \
    } \
}

#define BBS(bit, len, time) \
{ \
    byte b = readbq(zp); \
    if (b & bit) { \
	byte b - readb(PC + 2); \
	word oPC = PC; \
	inc_PC(len + (b < 0x80 ? b : b - 0x100)); \
	inc_time(time + 1 + ((PC & 0x100) != (oPC & 0x100))); \
    } else { \
	inc_PC(len); \
	inc_PC(time); \
    } \
}
#endif

#define BIT(mode, len, time, func) \
{ \
    byte M = read##func(mode()); \
    reset_Z((A & M) == 0); \
    reset_N(M & 0x80); \
    reset_V(M & 0x40); \
    inc_PC(len); \
    inc_time(time); \
}

/*
 * special hack for BIT in immediate mode. In this addressing mode the V and
 * N flags are not changed.
 */
#define BITx(mode, len, time) \
{ \
    byte M = readb(mode()); \
    reset_Z((A & M) == 0); \
    inc_PC(len); \
    inc_time(time); \
}

#ifdef _65C12_

#define TRB(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = ~A & read##func(addr); \
    write##func(addr, M); \
    reset_Z(M == 0); \
    inc_PC(len); \
    inc_PC(time); \
}

#define TSB(mode, len, time, func) \
{ \
    word addr = mode(); \
    byte M = A | read##func(addr); \
    write##func(addr, M); \
    reset_Z(M == 0); \
    inc_PC(len); \
    inc_PC(time); \
}
#endif

#define BRK(len, time) \
{ \
    pushw(PC+2); \
    set_B(); \
    pushb(P); \
    PC = readwq(0xFFFE); \
    inc_time(time); \
}

/*---------------------------------------------------------------------------*/

/*
 * Now the functions themselves.
 * We have one for each opcode value, even the unimplemented ones
 */

void op_0x00() { /* BRK */
    BRK(1, 7);
}

void op_0x01() { /* ORA (aa,X) */
    ORA(ind_x, 2, 6, b);
}

void op_0x02() { /* Unimplemented */
    NOP(1);
}

void op_0x03() { /* Unimplemented */
    NOP(1);
}

void op_0x04() { /* TSB aa */
#ifdef _65C12_
    TSB(zp, 2, 5, bq);
#else
    NOP(2);
#endif
}

void op_0x05() { /* ORA aa */
    ORA(zp, 2, 3, bq);
}

void op_0x06() { /* ASL aa */
    ASL(zp, 2, 5, bq);
}

void op_0x07() { /* Unimplemented */
    NOP(1);
}

void op_0x08() { /* PHP */
    PHP(1, 3);
}

void op_0x09() { /* ORA #dd */
    ORA(imm, 2, 2, bi);
}

void op_0x0a() { /* ASL A */
    ASLA(1, 2);
}

void op_0x0b() { /* Unimplemented */
    NOP(1);
}

void op_0x0c() { /* TSB aaaa */
#ifdef _65C12_
    TSB(abs, 3, 6, b);
#else
    NOP(3);
#endif
}

void op_0x0d() { /* ORA aaaa */
    ORA(abs, 3, 4, b);
}

void op_0x0e() { /* ASL aaaa */
    ASL(abs, 3, 6, b);
}

void op_0x0f() { /* BBR 0 aa rr */
#ifdef _R65C02_
    BBR(0x01, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x10() { /* BPL rr */
    BPL(2, 2);
}

void op_0x11() { /* ORA (aa),Y */
    ORA(ind_y, 2, 5 + page_crossed(), b);
}

void op_0x12() { /* ORA (aa) */
#ifdef _65C12_
    ORA(ind_zp, 2, 5, b);
#else
    NOP(2);
#endif
}

void op_0x13() { /* Unimplemented */
    NOP(1);
}

void op_0x14() { /* TRB aa */
#ifdef _65C12_
    TRB(zp, 2, 5, bq);
#else
    NOP(2);
#endif
}

void op_0x15() { /* ORA aa,X */
    ORA(zp_x, 2, 4, b);
}

void op_0x16() { /* ASL aa,X */
    ASL(zp_x, 2, 6, b);
}

void op_0x17() { /* Unimplemented */
    NOP(1);
}

void op_0x18() { /* CLC */
    CLC(1, 2);
}

void op_0x19() { /* ORA aaaa,Y */
    ORA(abs_y, 3, 4 + page_crossed(), b);
}

void op_0x1a() { /* INA */
#ifdef _65C12_
    INA(1, 2);
#else
    NOP(1);
#endif
}

void op_0x1b() { /* Unimplemented */
    NOP(1);
}

void op_0x1c() { /* TRB aaaa */
#ifdef _65C12_
    TRB(abs, 3, 6, b);
#else
    NOP(3);
#endif
}

void op_0x1d() { /* ORA aaaa,X */
    ORA(abs_x, 3, 4 + page_crossed(), b);
}

void op_0x1e() { /* ASL aaaa,X */
    ASL(abs_x, 3, 7, b);
}

void op_0x1f() { /* BBR 1 aa rr */
#ifdef _R65C02_
    BBR(0x02, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x20() { /* JSR aaaa */
    JSR(abs, 3, 6);
}

void op_0x21() { /* AND (aa,X) */
    AND(ind_x, 2, 6, b);
}

void op_0x22() { /* Unimplemented */
    NOP(1);
}

void op_0x23() { /* Unimplemented */
    NOP(1);
}

void op_0x24() { /* BIT aa */
    BIT(zp, 2, 3, bq);
}

void op_0x25() { /* AND aa */
    AND(zp, 2, 3, bq);
}

void op_0x26() { /* ROL aa */
    ROL(zp, 2, 5, bq);
}

void op_0x27() { /* Unimplemented */
    NOP(1);
}

void op_0x28() { /* PLP */
    PLP(1, 4);
}

void op_0x29() { /* AND #dd */
    AND(imm, 2, 2, bi);
}

void op_0x2a() { /* ROL A */
    ROLA(1, 2);
}

void op_0x2b() { /* Unimplemented */
    NOP(1);
}

void op_0x2c() { /* BIT aaaa */
    BIT(abs, 3, 4, b);
}

void op_0x2d() { /* AND aaaa */
    AND(abs, 3, 4, b);
}

void op_0x2e() { /* ROL aaaa */
    ROL(abs, 3, 6, b);
}

void op_0x2f() { /* BBR 2 aa rr */
#ifdef _R65C02_
    BBR(0x04, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x30() { /* BMI rr */
    BMI(2, 2);
}

void op_0x31() { /* AND (aa),Y */
    AND(ind_y, 2, 5 + page_crossed(), b);
}

void op_0x32() { /* AND (aa) */
#ifdef _65C12_
    AND(ind_zp, 2, 5, b);
#else
    NOP(2);
#endif
}

void op_0x33() { /* Unimplemented */
    NOP(1);
}

void op_0x34() { /* BIT aa,X */
#ifdef _65C12_
    BIT(zp_x, 2, 4, b);
#else
    NOP(2);
#endif
}

void op_0x35() { /* AND aa,X */
    AND(zp_x, 2, 4, b);
}

void op_0x36() { /* ROL aa,X */
    ROL(zp_x, 2, 6, b);
}

void op_0x37() { /* Unimplemented */
    NOP(1);
}

void op_0x38() { /* SEC */
    SEC(1, 2);
}

void op_0x39() { /* AND aaaa,Y */
    AND(abs_x, 3, 4 + page_crossed(), b);
}

void op_0x3a() { /* DEA */
#ifdef _65C12_
    DEA(1, 2);
#else
    NOP(1);
#endif
}

void op_0x3b() { /* Unimplemented */
    NOP(1);
}

void op_0x3c() { /* BIT aaaa,X */
#ifdef _65C12_
    BIT(abs_x, 3, 4, b);
#else
    NOP(3);
#endif
}

void op_0x3d() { /* AND aaaa,X */
    AND(abs_x, 3, 4 + page_crossed(), b);
}

void op_0x3e() { /* ROL aaaa,X */
    ROL(abs_x, 3, 7, b);
}

void op_0x3f() { /* BBR 3 aa rr */
#ifdef _R65C02_
    BBR(0x08, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x40() { /* RTI */
    RTI(1,6);
}

void op_0x41() { /* EOR (aa,X) */
    EOR(ind_x, 2, 6, b);
}

void op_0x42() { /* Unimplemented */
    NOP(1);
}

void op_0x43() { /* Unimplemented */
    NOP(1);
}

void op_0x44() { /* Unimplemented */
    NOP(1);
}

void op_0x45() { /* EOR aa */
    EOR(zp, 2, 3, bq);
}

void op_0x46() { /* LSR aa */
    LSR(zp, 2, 5, bq);
}

void op_0x47() { /* Unimplemented */
    NOP(1);
}

void op_0x48() { /* PHA */
    PHA(1, 3);
}

void op_0x49() { /* EOR #dd */
    EOR(imm, 2, 2, bi);
}

void op_0x4a() { /* LSR A */
    LSRA(1, 2);
}

void op_0x4b() { /* Unimplemented */
    NOP(1);
}

void op_0x4c() { /* JMP aaaa */
    JMP(abs, 3, 3);
}

void op_0x4d() { /* EOR aaaa */
    EOR(abs, 3, 4, b);
}

void op_0x4e() { /* LSR aaaa */
    LSR(abs, 3, 6, b);
}

void op_0x4f() { /* BBR 4 aa rr */
#ifdef _R65C02_
    BBR(0x10, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x50() { /* BVC rr */
    BVC(2, 2);
}

void op_0x51() { /* EOR (aa),Y */
    EOR(ind_y, 2, 5 + page_crossed(), b);
}

void op_0x52() { /* EOR (aa) */
#ifdef _65C12_
    EOR(ind_zp, 2, 5, b);
#else
    NOP(2);
#endif
}

void op_0x53() { /* Unimplemented */
    NOP(1);
}

void op_0x54() { /* Unimplemented */
    NOP(1);
}

void op_0x55() { /* EOR aa,X */
    EOR(zp_x, 2, 4, b);
}

void op_0x56() { /* LSR aa,X */
    LSR(zp_x, 2, 6, b);
}

void op_0x57() { /* Unimplemented */
    NOP(1);
}

void op_0x58() { /* CLI */
    CLI(1, 2);
}

void op_0x59() { /* EOR aaaa,Y */
    EOR(abs_y, 3, 4 + page_crossed(), b);
}

void op_0x5a() { /* PHY */
#ifdef _65C12_
    PHY(1, 2);
#else
    NOP(1);
#endif
}

void op_0x5b() { /* Unimplemented */
    NOP(1);
}

void op_0x5c() { /* Unimplemented */
    NOP(1);
}

void op_0x5d() { /* EOR aaaa,X */
    EOR(abs_x, 3, 4 + page_crossed(), b);
}

void op_0x5e() { /* LSR aaaa,X */
    LSR(abs_x, 3, 7 + page_crossed(), b);
}

void op_0x5f() { /* BBR 5 aa rr */
#ifdef _R65C02_
    BBR(0x20, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x60() { /* RTS */
    RTS(1, 6);
}

void op_0x61() { /* ADC (aa,X) */
    ADC(ind_x, 2, 6 + ADC_KLUDGE, b);
}

void op_0x62() { /* Unimplemented */
    NOP(1);
}

void op_0x63() { /* Unimplemented */
    NOP(1);
}

void op_0x64() { /* STZ aa */
#ifdef _65C12_
    STZ(zp, 2, 3);
#else
    NOP(2);
#endif
}

void op_0x65() { /* ADC aa */
    ADC(zp, 2, 3 + ADC_KLUDGE, bq);
}

void op_0x66() { /* ROR aa */
    ROR(zp, 2, 5, bq);
}

void op_0x67() { /* Unimplemented */
    NOP(1);
}

void op_0x68() { /* PLA */
    PLA(1, 4);
}

void op_0x69() { /* ADC #dd */
    ADC(imm, 2, 2 + ADC_KLUDGE, bi);
}

void op_0x6a() { /* ROR A */
    RORA(1, 2);
}

void op_0x6b() { /* Unimplemented */
    NOP(1);
}

void op_0x6c() { /* JMP (aaaa) */
    JMP(ind, 3, 5);
}

void op_0x6d() { /* ADC aaaa */
    ADC(abs, 3, 4 + ADC_KLUDGE, b);
}

void op_0x6e() { /* ROR aaaa */
    ROR(abs, 3, 6, b);
}

void op_0x6f() { /* BBR 6 aa rr */
#ifdef _R65C02_
    BBR(0x40, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x70() { /* BVS rr */
    BVS(2, 2);
}

void op_0x71() { /* ADC (aa),Y */
    ADC(ind_y, 2, 5 + page_crossed() + ADC_KLUDGE, b);
}

void op_0x72() { /* ADC (aa) */
#ifdef _65C12_
    ADC(ind_zp, 2, 5 + ADC_KLUDGE, b);
#else
    NOP(2);
#endif
}

void op_0x73() { /* Unimplemented */
    NOP(1);
}

void op_0x74() { /* STZ aa,X */
#ifdef _65C12_
    STZ(zp_x, 2, 4, b);
#else
    NOP(2);
#endif
}

void op_0x75() { /* ADC aa,X */
    ADC(zp_x, 2, 4 + ADC_KLUDGE, b);
}

void op_0x76() { /* ROR aa,X */
    ROR(zp_x, 2, 6, b);
}

void op_0x77() { /* Unimplemented */
    NOP(1);
}

void op_0x78() { /* SEI */
    SEI(1, 2);
}

void op_0x79() { /* ADC aaaa,Y */
    ADC(abs_y, 3, 4 + page_crossed() + ADC_KLUDGE, b);
}

void op_0x7a() { /* PLY */
#ifdef _65C12_
    PLY(1, 2);
#else
    NOP(1);
#endif
}

void op_0x7b() { /* Unimplemented */
    NOP(1);
}

void op_0x7c() { /* JMP (aaaa,X) */
#ifdef _65C12_
    JMP(absind_x, 3, 6);
#else
    NOP(3);
#endif
}

void op_0x7d() { /* ADC aaaa,X */
    ADC(abs_x, 3, 4 + page_crossed() + ADC_KLUDGE, b);
}

void op_0x7e() { /* ROR aaaa,X */
    ROR(abs_x, 3, 7, b);
}

void op_0x7f() { /* BBR 7 aa rr */
#ifdef _R65C02_
    BBR(0x80, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x80() { /* BRA */
#ifdef _65C12_
    BRA(2, 2);
#else
    NOP(2);
#endif
}

void op_0x81() { /* STA (aa,X) */
    STA(ind_x, 2, 6, b);
}

void op_0x82() { /* Unimplemented */
    NOP(1);
}

void op_0x83() { /* Unimplemented */
    NOP(1);
}

void op_0x84() { /* STY aa */
    STY(zp, 2, 3, bq);
}

void op_0x85() { /* STA aa */
    STA(zp, 2, 3, bq);
}

void op_0x86() { /* STX aa */
    STX(zp, 2, 3, bq);
}

void op_0x87() { /* Unimplemented */
    NOP(1);
}

void op_0x88() { /* DEY */
    DEY(1, 2);
}

void op_0x89() { /* BIT #dd */
#ifdef _65C12_
    BITx(imm, 2, 2, bi);
#else
    NOP(2);
#endif
}

void op_0x8a() { /* TXA */
    TXA(1, 2);
}

void op_0x8b() { /* Unimplemented */
    NOP(1);
}

void op_0x8c() { /* STY aaaa */
    STY(abs, 3, 4, b);
}

void op_0x8d() { /* STA aaaa */
    STA(abs, 3, 4, b);
}

void op_0x8e() { /* STX aaaa */
    STX(abs, 3, 4, b);
}

void op_0x8f() { /* BBS 0 aa rr */
#ifdef _R65C02_
    BBS(0x01, 3, 5);
#else
    NOP(3);
#endif
}

void op_0x90() { /* BCC rr */
    BCC(2, 2);
}

void op_0x91() { /* STA (aa),Y */
    STA(ind_y, 2, 6, b);
}

void op_0x92() { /* STA (aa) */
#ifdef _65C12_
    STA(ind_zp, 2, 6, b);
#else
    NOP(2);
#endif
}

void op_0x93() { /* Unimplemented */
    NOP(1);
}

void op_0x94() { /* STY aa,X */
    STY(zp_x, 2, 4, b);
}

void op_0x95() { /* STA aa,X */
    STA(zp_x, 2, 4, b);
}

void op_0x96() { /* STX aa,Y */
    STX(zp_y, 2, 4, b);
}

void op_0x97() { /* Unimplemented */
    NOP(1);
}

void op_0x98() { /* TYA */
    TYA(1, 2);
}

void op_0x99() { /* STA aaaa,Y */
    STA(abs_y, 3, 5, b);
}

void op_0x9a() { /* TXS */
    TXS(1, 2);
}

void op_0x9b() { /* Unimplemented */
    NOP(1);
}

void op_0x9c() { /* STZ aaaa */
#ifdef _65C12_
    STZ(abs, 3, 4);
#else
    NOP(2);
#endif
}

void op_0x9d() { /* STA aaaa,X */
    STA(abs_x, 3, 5, b);
}

void op_0x9e() { /* STZ aaaa,X */
#ifdef _65C12_
    STZ(abs_x, 3, 5, b);
#else
    NOP(2);
#endif
}

void op_0x9f() { /* BBS 1 aa rr */
#ifdef _R65C02_
    BBS(0x02, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xa0() { /* LDY #dd */
    LDY(imm, 2, 2, bi);
}

void op_0xa1() { /* LDA (aa,X) */
    LDA(ind_x, 2, 6, b);
}

void op_0xa2() { /* LDX #dd */
    LDX(imm, 2, 2, bi);
}

void op_0xa3() { /* Unimplemented */
    NOP(1);
}

void op_0xa4() { /* LDY aa */
    LDY(zp, 2, 3, bq);
}

void op_0xa5() { /* LDA aa */
    LDA(zp, 2, 3, bq);
}

void op_0xa6() { /* LDX aa */
    LDX(zp, 2, 3, bq);
}

void op_0xa7() { /* Unimplemented */
    NOP(1);
}

void op_0xa8() { /* TAY */
    TAY(1, 2);
}

void op_0xa9() { /* LDA #dd */
    LDA(imm, 2, 2, bi);
}

void op_0xaa() { /* TAX */
    TAX(1, 2);
}

void op_0xab() { /* Unimplemented */
    NOP(1);
}

void op_0xac() { /* LDY aaaa */
    LDY(abs, 3, 4, b);
}

void op_0xad() { /* LDA aaaa */
    LDA(abs, 3, 4, b);
}

void op_0xae() { /* LDX aaaa */
    LDX(abs, 3, 4, b);
}

void op_0xaf() { /* BBS 2 aa rr */
#ifdef _R65C02_
    BBS(0x04, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xb0() { /* BCS rr */
    BCS(2, 2);
}

void op_0xb1() { /* LDA (aa),Y */
    LDA(ind_y, 2, 5 + page_crossed(), b);
}

void op_0xb2() { /* LDA (aa) */
#ifdef _65C12_
    LDA(ind_zp, 2, 5, b);
#else
    NOP(2);
#endif
}

void op_0xb3() { /* Unimplemented */
    NOP(1);
}

void op_0xb4() { /* LDY aa,X */
    LDY(zp_x, 2, 4, b);
}

void op_0xb5() { /* LDA aa,X */
    LDA(zp_x, 2, 4, b);
}

void op_0xb6() { /* LDX aa,Y */
    LDX(zp_y, 2, 4, b);
}

void op_0xb7() { /* Unimplemented */
    NOP(1);
}

void op_0xb8() { /* CLV */
    CLV(1, 2);
}

void op_0xb9() { /* LDA aaaa,Y */
    LDA(abs_y, 3, 4 + page_crossed(), b);
}

void op_0xba() { /* TSX */
    TSX(1, 2);
}

void op_0xbb() { /* Unimplemented */
    NOP(1);
}

void op_0xbc() { /* LDY aaaa,X */
    LDY(abs_x, 3, 4 + page_crossed(), b);
}

void op_0xbd() { /* LDA aaaa,X */
    LDA(abs_x, 3, 4 + page_crossed(), b);
}

void op_0xbe() { /* LDX aaaa,Y */
    LDX(abs_y, 3, 4 + page_crossed(), b);
}

void op_0xbf() { /* BBS 3 aa rr */
#ifdef _R65C02_
    BBS(0x08, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xc0() { /* CPY #dd */
    CPY(imm, 2, 2, bi);
}

void op_0xc1() { /* CMP (aa,X) */
    CMP(ind_x, 2, 6, b);
}

void op_0xc2() { /* Unimplemented */
    NOP(1);
}

void op_0xc3() { /* Unimplemented */
    NOP(1);
}

void op_0xc4() { /* CPY aa */
    CPY(zp, 2, 3, bq);
}

void op_0xc5() { /* CMP aa */
    CMP(zp, 2, 3, bq);
}

void op_0xc6() { /* DEC aa */
    DEC(zp, 2, 5, bq);
}

void op_0xc7() { /* Unimplemented */
    NOP(1);
}

void op_0xc8() { /* INY */
    INY(1, 2);
}

void op_0xc9() { /* CMP #dd */
    CMP(imm, 2, 2, bi);
}

void op_0xca() { /* DEX */
    DEX(1, 2);
}

void op_0xcb() { /* Unimplemented */
    NOP(1);
}

void op_0xcc() { /* CPY aaaa */
    CPY(abs, 3, 4, b);
}

void op_0xcd() { /* CMP aaaa */
    CMP(abs, 3, 4, b);
}

void op_0xce() { /* DEC aaaa */
    DEC(abs, 3, 6, b);
}

void op_0xcf() { /* BBS 4 aa rr */
#ifdef _R65C02_
    BBS(0x10, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xd0() { /* BNE rr */
    BNE(2, 2);
}

void op_0xd1() { /* CMP (aa),y */
    CMP(ind_y, 2, 5 + page_crossed(), b);
}

void op_0xd2() { /* CMP (aa) */
#ifdef _65C12_
    CMP(ind_zp, 2, 5, b);
#else
    NOP(2);
#endif
}

void op_0xd3() { /* Unimplemented */
    NOP(1);
}

#ifdef FIXME
void op_0xd4() { /* Unimplemented */
    NOP(1);
}
#else
void op_0xd4() { /* UNX #dd */
    unix_call();
}
#endif

void op_0xd5() { /* CMP aa,X */
    CMP(zp_x, 2, 4, b);
}

void op_0xd6() { /* DEC aa,X */
    DEC(zp_x, 2, 6, b);
}

void op_0xd7() { /* Unimplemented */
    NOP(1);
}

void op_0xd8() { /* CLD */
    CLD(1, 2);
}

void op_0xd9() { /* CMP aaaa,Y */
    CMP(abs_y, 3, 4 + page_crossed(), b);
}

void op_0xda() { /* PHX */
#ifdef _65C12_
    PHX(1, 2);
#else
    NOP(1);
#endif
}

void op_0xdb() { /* Unimplemented */
    NOP(1);
}

void op_0xdc() { /* Unimplemented */
    NOP(1);
}

void op_0xdd() { /* CMP aaaa,X */
    CMP(abs_x, 3, 4 + page_crossed(), b);
}

void op_0xde() { /* DEC aaaa,X */
    DEC(abs_x, 3, 7, b);
}

void op_0xdf() { /* BBS 5 aa rr */
#ifdef _R65C02_
    BBS(0x20, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xe0() { /* CPX #dd */
    CPX(imm, 2, 2, bi);
}

void op_0xe1() { /* SBC (aa,X) */
    SBC(ind_x, 2, 6 + ADC_KLUDGE, b);
}

void op_0xe2() { /* Unimplemented */
    NOP(1);
}

void op_0xe3() { /* Unimplemented */
    NOP(1);
}

void op_0xe4() { /* CPX aa */
    CPX(zp, 2, 3, bq);
}

void op_0xe5() { /* SBC aa */
    SBC(zp, 2, 3 + ADC_KLUDGE, bq);
}

void op_0xe6() { /* INC aa */
    INC(zp, 2, 5, bq);
}

void op_0xe7() { /* Unimplemented */
    NOP(1);
}

void op_0xe8() { /* INX */
    INX(1, 2);
}

void op_0xe9() { /* SBC #dd */
    SBC(imm, 2, 2 + ADC_KLUDGE, bi);
}

void op_0xea() { /* NOP */
    NOP(1);
}

void op_0xeb() { /* Unimplemented */
    NOP(1);
}

void op_0xec() { /* CPX aaaa */
    CPX(abs, 3, 4, b);
}

void op_0xed() { /* SBC aaaa */
    SBC(abs, 3, 4 + ADC_KLUDGE, b);
}

void op_0xee() { /* INC aaaa */
    INC(abs, 3, 6, b);
}

void op_0xef() { /* BBS 6 aa rr */
#ifdef _R65C02_
    BBS(0x40, 3, 5);
#else
    NOP(3);
#endif
}

void op_0xf0() { /* BEQ rr */
    BEQ(2, 2);
}

void op_0xf1() { /* SBC (aa),Y */
    SBC(ind_y, 2, 5 + page_crossed() + ADC_KLUDGE, b);
}

void op_0xf2() { /* SBC (aa) */
#ifdef _65C12_
    SBC(zp, 2, 5 + ADC_KLUDGE, b);
#else
    NOP(2);
#endif
}

void op_0xf3() { /* Unimplemented */
    NOP(1);
}

void op_0xf4() { /* Unimplemented */
    NOP(1);
}

void op_0xf5() { /* SBC aa,x */
    SBC(zp_x, 2, 4 + ADC_KLUDGE, b);
}

void op_0xf6() { /* INC aa,X */
    INC(zp_x, 2, 6, b);
}

void op_0xf7() { /* Unimplemented */
    NOP(1);
}

void op_0xf8() { /* SED */
    SED(1, 2);
}

void op_0xf9() { /* SBC aaaa,Y */
    SBC(abs_y, 3, 4 + page_crossed() + ADC_KLUDGE, b);
}

void op_0xfa() { /* PLX */
#ifdef _65C12_
    PLX(1, 2);
#else
    NOP(1);
#endif
}

void op_0xfb() { /* Unimplemented */
    NOP(1);
}

void op_0xfc() { /* Unimplemented */
    NOP(1);
}

void op_0xfd() { /* SBC aaaa,X */
    SBC(abs_x, 3, 4 + page_crossed() + ADC_KLUDGE, b);
}

void op_0xfe() { /* INC aaaa,X */
    INC(abs_x, 3, 7, b);
}

void op_0xff() { /* BBS 7 aa rr */
#ifdef _R65C02_
    BBS(0x80, 3, 5);
#else
    NOP(3);
#endif
}

/*
 * Our opcode table of functions
 */
void (*opcode[256])() = {
    op_0x00, op_0x01, op_0x02, op_0x03, op_0x04, op_0x05, op_0x06, op_0x07, 
    op_0x08, op_0x09, op_0x0a, op_0x0b, op_0x0c, op_0x0d, op_0x0e, op_0x0f, 
    op_0x10, op_0x11, op_0x12, op_0x13, op_0x14, op_0x15, op_0x16, op_0x17, 
    op_0x18, op_0x19, op_0x1a, op_0x1b, op_0x1c, op_0x1d, op_0x1e, op_0x1f, 
    op_0x20, op_0x21, op_0x22, op_0x23, op_0x24, op_0x25, op_0x26, op_0x27, 
    op_0x28, op_0x29, op_0x2a, op_0x2b, op_0x2c, op_0x2d, op_0x2e, op_0x2f, 
    op_0x30, op_0x31, op_0x32, op_0x33, op_0x34, op_0x35, op_0x36, op_0x37, 
    op_0x38, op_0x39, op_0x3a, op_0x3b, op_0x3c, op_0x3d, op_0x3e, op_0x3f, 
    op_0x40, op_0x41, op_0x42, op_0x43, op_0x44, op_0x45, op_0x46, op_0x47, 
    op_0x48, op_0x49, op_0x4a, op_0x4b, op_0x4c, op_0x4d, op_0x4e, op_0x4f, 
    op_0x50, op_0x51, op_0x52, op_0x53, op_0x54, op_0x55, op_0x56, op_0x57, 
    op_0x58, op_0x59, op_0x5a, op_0x5b, op_0x5c, op_0x5d, op_0x5e, op_0x5f, 
    op_0x60, op_0x61, op_0x62, op_0x63, op_0x64, op_0x65, op_0x66, op_0x67, 
    op_0x68, op_0x69, op_0x6a, op_0x6b, op_0x6c, op_0x6d, op_0x6e, op_0x6f, 
    op_0x70, op_0x71, op_0x72, op_0x73, op_0x74, op_0x75, op_0x76, op_0x77, 
    op_0x78, op_0x79, op_0x7a, op_0x7b, op_0x7c, op_0x7d, op_0x7e, op_0x7f, 
    op_0x80, op_0x81, op_0x82, op_0x83, op_0x84, op_0x85, op_0x86, op_0x87, 
    op_0x88, op_0x89, op_0x8a, op_0x8b, op_0x8c, op_0x8d, op_0x8e, op_0x8f, 
    op_0x90, op_0x91, op_0x92, op_0x93, op_0x94, op_0x95, op_0x96, op_0x97, 
    op_0x98, op_0x99, op_0x9a, op_0x9b, op_0x9c, op_0x9d, op_0x9e, op_0x9f, 
    op_0xa0, op_0xa1, op_0xa2, op_0xa3, op_0xa4, op_0xa5, op_0xa6, op_0xa7, 
    op_0xa8, op_0xa9, op_0xaa, op_0xab, op_0xac, op_0xad, op_0xae, op_0xaf, 
    op_0xb0, op_0xb1, op_0xb2, op_0xb3, op_0xb4, op_0xb5, op_0xb6, op_0xb7, 
    op_0xb8, op_0xb9, op_0xba, op_0xbb, op_0xbc, op_0xbd, op_0xbe, op_0xbf, 
    op_0xc0, op_0xc1, op_0xc2, op_0xc3, op_0xc4, op_0xc5, op_0xc6, op_0xc7, 
    op_0xc8, op_0xc9, op_0xca, op_0xcb, op_0xcc, op_0xcd, op_0xce, op_0xcf, 
    op_0xd0, op_0xd1, op_0xd2, op_0xd3, op_0xd4, op_0xd5, op_0xd6, op_0xd7, 
    op_0xd8, op_0xd9, op_0xda, op_0xdb, op_0xdc, op_0xdd, op_0xde, op_0xdf, 
    op_0xe0, op_0xe1, op_0xe2, op_0xe3, op_0xe4, op_0xe5, op_0xe6, op_0xe7, 
    op_0xe8, op_0xe9, op_0xea, op_0xeb, op_0xec, op_0xed, op_0xee, op_0xef, 
    op_0xf0, op_0xf1, op_0xf2, op_0xf3, op_0xf4, op_0xf5, op_0xf6, op_0xf7, 
    op_0xf8, op_0xf9, op_0xfa, op_0xfb, op_0xfc, op_0xfd, op_0xfe, op_0xff
};
