#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <stdio.h>

#include <6502/6502.h>

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// 6502 simulator
// --------------
//
// 6502 simulator with hopefully cycle-accurate bus behaviour and
// interrupt timings.
//
// How the 6502 works
// ------------------
//
// The timing runs off a clock input, phi2 (or phi0 as the Synertek data
// sheet has it - see p42). There are two stages to every cycle, phi2=0, then
// phi2=1; internally the 6502 translates these into phi1=1 (when phi2=0) and
// phi2=1 (when phi2=1). There are phi1 and phi2 outputs that presumably
// reflect these states.
//
// When phi1=1, an address is put on the address bus; when phi2=1, the data
// put on the data bus, either by memory or whatever (read) or by the CPU
// (write).
//
// For convenience, the state where phi1=1 is referred to simply as "phi1",
// and the state where phi2=1 is "phi2". These are also referred to as "phase
// 1" and "phase 2" respectively, in some comments - will fix.
//
// Here's some output from visual 6502.
//
// <pre>
// cycle ab   db rw Fetch pc   a  x  y  s  p        Execute State irq D1x1
// ----- ---- -- -- ----- ---- -- -- -- -- -------- ------- ----- --- ----
// 0     0010 58 1  CLI   0010 aa 00 00 fd nv-BdIZc BRK     T1    0   1
// 0     0010 58 1  CLI   0010 aa 00 00 fd nv-BdIZc BRK     T1    0   1
// 1     0011 78 1        0011 aa 00 00 fd nv-BdIZc CLI     T0+T2 0   1
// 1     0011 78 1        0011 aa 00 00 fd nv-BdIZc CLI     T0+T2 0   1
// 2     0011 78 1  SEI   0011 aa 00 00 fd nv-BdiZc CLI     T1    0   1
// 2     0011 78 1  SEI   0011 aa 00 00 fd nv-BdiZc CLI     T1    0   1
// 3     0012 58 1        0012 aa 00 00 fd nv-BdiZc SEI     T0+T2 0   1
// 3     0012 58 1        0012 aa 00 00 fd nv-bdiZc SEI     T0+T2 0   0
// 4     0012 58 1  CLI   0012 aa 00 00 fd nv-bdIZc SEI     T1    0   0
// 4     0012 58 1  CLI   0012 aa 00 00 fd nv-bdIZc SEI     T1    0   0
// 5     0012 58 1        0012 aa 00 00 fd nv-bdIZc BRK     T2    0   0
// 5     0012 58 1        0012 aa 00 00 fd nv-bdIZc BRK     T2    0   0
// 6     01fd 58 0        0012 aa 00 00 fd nv-bdIZc BRK     T3    0   0
// 6     01fd 00 0        0012 aa 00 00 fd nv-bdIZc BRK     T3    0   0
// </pre>
//
// Each cycle has two rows - phi1, then phi2. (I guess the
// various signals are as they would be at the end of that phase, when
// things are settled, but the voltages haven't started to ramp
// down... e.g., for phi2, between the end of the TMDS period and
// before the start of the TH period in fig 1.8 in the SY6502 data
// sheet.)
//
// How the simulator operates
// --------------------------
//
// The M6502 object has a function pointer called tfn, holding a
// pointer to the function that handles the current state. It's a
// standard C-style OO-type function, taking 1 argument, a pointer to
// the M6502 of interest, to be called like "(*s->tfn)(s)" or
// "(*s.tfn)(&s)".
//
// Call it to advance from the end of phi1 of the current cycle to
// the end of phi1 of the next (see above).
//
// At the end of phi1, abus/rw/dbus are set up according to the
// access required. The simulator doesn't access memory itself - so at
// this point, it returns to its caller. The caller then needs to
// perform the write (by setting the value at address `abus.w' to the
// value `dbus') or the read (by setting `dbus' to the value at
// address `abus.w'). Cycle stretching can be handled
// straightforwardly.
//
// (When comparing simulator output to a Visual 6502 log: at this
// point, with phi1 complete and the memory read performed, even
// though phi2 hasn't properly run yet, ab/db/rw should have the
// values they'd have at the end of the visual 6502 phi2. The
// externally visible part of phi2 involves the data bus being held
// at the appropriate levels for some period, but this part happens
// instantaneously in the simulator, so it's all done before phi2
// starts. The simulator's phi2 just consists of doing the internal
// gubbins that the 6502 would do at the same time as the bus access.)
//
// On the next call, the simulator then runs phi2 of that cycle,
// and then follows with phi1 of the next cycle. So on each update,
// the simulated 6502 atomically runs phi2 of the current cycle,
// then phi1 of the next.
//
// States
// ------
//
// Most states are handled by one of the autogenerated functions in
// 6502_internal.inl called Tn_XXX, where n is the cycle number and
// XXX the type of operation (IRQ, RMW absolute indexed instruction,
// immediate instruction, etc.). Each function performs phi2 of
// cycle n, and then phi1 of cycle n+1, and sets tfn to point to
// the next function in the chain.
//
// Then at the end of the last function in the chain,
// M6502_NextInstruction is called. This performs phi1 of T0, by
// putting PC on the address bus and setting in motion an IRQ, or the
// next instruction, as appropriate.
//
// After calling M6502_NextInstruction, when executing an instruction,
// the next tfn is always T0_All. This function reads the opcode from
// the data bus, finds the tfn for T0 for that opcode, and calls it
// directly. Most operation-specific T0_xxx functions are called this
// way, rather than via tfn.
//
// The IRQ-related T0 functions are an exception, and when performing
// an interrupt, M6502_NextInstruction sets tfn to &T0_IRQ.
//
// ifn
// ---
//
// Special-purpose operations are implemented directly as a sequence
// of Tn_XXX functions. (IRQs, RTI, BRK, JSR, etc.)
//
// Read, RMW, Write, conditional branch and (some) Implied
// instructions, on the other hand, have a set of Tn_XXX functions to
// handle fetching the operands and the data, and then call a second
// callback, ifn, to perform the actual operation. (ifn comes from the
// same opcode table that provides the initial tfn for each opcode.)
//
// Each category of instruction interfaces with its ifn in a specific
// way, as described in the code.
//
// IRQs/NMIs
// ---------
//
// The simulated 6502 can accept IRQs and NMIs from up to N devices
// (according to the width of the types M6502_DeviceIRQFlags and
// M6502_DeviceNMIFlags). There are private fields inside the 6502
// struct, one for IRQs and one for NMIs, with 1 bit per device,
// allowing devices to independently assert or unassert the given type
// of interrupt.
//
// (Use the M6502_SetDeviceIRQ function to request or unrequest IRQs,
// and the M6502_SetDeviceNMI function for NMIs.)
//
// (The bit levels in the IRQ flags are indeed not the same as the
// 6502's irq pin.)
//
// Interrupt response
// ------------------
//
// Interrupts are checked for on the penultimate cycle of each
// instruction. If there's an IRQ or NMI that needs servicing, the
// d1x1 flag is cleared. This is then checked in
// M6502_NextInstruction: d1x1==0 means do an interrupt, and d1x1==1
// means do normal opcode fetch. Then d1x1 is checked at various
// points during the interrupt sequence to determine what sort of
// interrupt this is.
//
// In a real 6502, d1x1 incorporates the IRQ/NMI line, which I guess
// is how the IRQ-eats-BRK bug occurs: BRK starts, then d1x1 is
// checked, it's low, the 6502 decides it was an IRQ, and carries on.
//
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// CMOS info (http://www.6502.org/tutorials/65c02opcodes.html)
// -----------------------------------------------------------
//
// Standard CMOS stuff:
//
// - Extra stack ops: PHX, PHY, PLX, PLY
// - STZ
// - TRB, TSB
// - BRA
// - (ZP) for: ADC, AND, CMP, EOR, LDA, ORA, SBC, STA
// - Immediate for: BIT (sets zero flag only)
// - ZP,X for: BIT
// - Abs,X for: BIT
// - Accumulator for: DEC, INC
// - (Abs,X) for: JMP
// - Fix for JMP ($xxFF) page crossing bug
// - Different BCD behaviour, with +1 cycle penalty
// - Various extra NOPs
// - Last 2 cycles of RMW instructions are RW not WW
// - Abs,X skips last cycle if no page crossing for: ASL, LSR, ROL, ROR
// - BRK/IRQ/NMI/RESET clear D flag
// - IRQ during BRK processing does BRK then IRQ
//
// Rockwell stuff:
//
// - All the standard CMOS stuff
// - BBR/BBS
// - RMB/SMB
//
// WDC stuff:
//
// - All the Rockwell stuff
// - WAI
// - STP
//
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// Other notes:
//
// - the Tn_ prefix bears no relation at all to the Tn state names in
//   Visual6502. This is a bad naming convention and it needs to change

#if M6502_DBUS_OFFSET

#define SET_DBUS(F) (s->dbus_offset = offsetof(M6502, F), (void)0)
#define DBUS_IS(F) (s->dbus_offset == offsetof(M6502, F))

#else

#define SET_DBUS(F) (s->dbus = &s->F, (void)0)
#define DBUS_IS(F) (s->dbus == &s->F)

#endif

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//static inline void FixupP(M6502 *s) {
//    s->p.bits.b=s->d1x1;
//    s->p.bits._=1;
//}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static inline M6502P GetP(const M6502 *s) {
    M6502P p;
    p.value = s->p.value;

    p.bits.b = s->d1x1;
    p.bits._ = 1;

    return p;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static inline void CheckForInterrupts(M6502 *s) {
    if (s->irq_flags != 0 && !s->p.bits.i) {
        s->d1x1 = 0;
    } else if (s->nmi_flags != 0) {
        s->d1x1 = 0;

        // nmi_flags is reset in T4_Interrupt.
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static inline void SetNZ(M6502 *s, uint8_t v) {
    s->p.bits.n = v >> 7;
    s->p.bits.z = v == 0;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* ASL mem/ASL A */
static inline void DoASL(M6502 *s, uint8_t *p) {
    s->p.bits.c = *p >> 7;
    *p <<= 1;
    SetNZ(s, *p);
}

/* LSR mem/LSR A */
static inline void DoLSR(M6502 *s, uint8_t *p) {
    s->p.bits.c = *p & 1;
    *p >>= 1;
    SetNZ(s, *p);
}

/* ROL mem/ROL A */
static inline void DoROL(M6502 *s, uint8_t *p) {
    uint8_t c = *p >> 7;

    *p <<= 1;
    *p |= s->p.bits.c;
    s->p.bits.c = c;

    SetNZ(s, *p);
}

/* ROR mem/ROR A */
static inline void DoROR(M6502 *s, uint8_t *p) {
    uint8_t c = *p & 1;

    *p >>= 1;
    *p |= s->p.bits.c << 7;
    s->p.bits.c = c;

    SetNZ(s, *p);
}

/* DEC mem/DEX/DEY */
static inline void DoDEC(M6502 *s, uint8_t *p) {
    --*p;
    SetNZ(s, *p);
}

/* INC mem/INX/INY */
static inline void DoINC(M6502 *s, uint8_t *p) {
    ++*p;
    SetNZ(s, *p);
}

/* CMP/CPX/CPY */
static inline void DoCMP(M6502 *s, uint8_t reg) {
    s->p.bits.c = reg >= s->data;
    s->p.bits.z = reg == s->data;
    s->p.bits.n = (reg - s->data) >> 7;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Implied instructions. Update the 6502 state. */

static void CLD(M6502 *s) {
    s->p.bits.d = 0;
}

static void SED(M6502 *s) {
    s->p.bits.d = 1;
}

static void CLI(M6502 *s) {
    s->p.bits.i = 0;
}

static void SEI(M6502 *s) {
    s->p.bits.i = 1;
}

static void CLV(M6502 *s) {
    s->p.bits.v = 0;
}

static void CLC(M6502 *s) {
    s->p.bits.c = 0;
}

static void SEC(M6502 *s) {
    s->p.bits.c = 1;
}

static void TXA(M6502 *s) {
    s->a = s->x;
    SetNZ(s, s->a);
}

static void TYA(M6502 *s) {
    s->a = s->y;
    SetNZ(s, s->a);
}

static void TXS(M6502 *s) {
    s->s.b.l = s->x;
}

static void TAX(M6502 *s) {
    s->x = s->a;
    SetNZ(s, s->x);
}

static void TSX(M6502 *s) {
    s->x = s->s.b.l;
    SetNZ(s, s->x);
}

static void TAY(M6502 *s) {
    s->y = s->a;
    SetNZ(s, s->y);
}

static void INX(M6502 *s) {
    DoINC(s, &s->x);
}

static void INY(M6502 *s) {
    DoINC(s, &s->y);
}

static void DEX(M6502 *s) {
    DoDEC(s, &s->x);
}

static void DEY(M6502 *s) {
    DoDEC(s, &s->y);
}

static void ILL(M6502 *s) {
    if (s->ill_fn) {
        (*s->ill_fn)(s, s->ill_context);
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Read instructions. Treat s->data as the operand and update the 6502
 * state. */

static void ADC(M6502 *s) {
    if (s->p.bits.d) {
        unsigned tmp = (s->a & 0xfu) + (s->data & 0xfu) + (s->p.bits.c);
        if (tmp > 9) {
            tmp += 6;
        }
        if (tmp <= 0xf) {
            tmp = (tmp & 0xf) + (s->a & 0xf0) + (s->data & 0xf0);
        } else {
            tmp = (tmp & 0xf) + (s->a & 0xf0) + (s->data & 0xf0) + 0x10;
        }

        s->p.bits.z = 0;
        s->p.bits.v = 0;
        s->p.bits.n = 0;

        if (((s->a + s->data + (s->p.bits.c)) & 0xff) == 0) {
            s->p.bits.z = 1;
        }

        if (tmp & 0x80) {
            s->p.bits.n = 1;
        }

        if (((s->a ^ tmp) & 0x80) && !((s->a ^ s->data) & 0x80)) {
            s->p.bits.v = 1;
        }

        if ((tmp & 0x1f0) > 0x90) {
            tmp += 0x60;
        }

        s->p.bits.c = (tmp & 0xff0) > 0xf0;

        s->a = (uint8_t)tmp;
    } else {
        M6502Word result;

        result.w = s->a + s->data + s->p.bits.c;

        SetNZ(s, result.b.l);
        s->p.bits.c = result.b.h > 0;
        s->p.bits.v = (~(s->a ^ s->data) & (s->a ^ result.b.l) & 0x80) != 0;

        s->a = result.b.l;
    }
}

static void ADC_CMOS(M6502 *s) {
    if (s->p.bits.d) {
        unsigned tmp = (s->a & 0xfu) + (s->data & 0xfu) + s->p.bits.c;
        if (tmp > 9) {
            tmp += 6;
        }

        if (tmp <= 0xf) {
            tmp = (tmp & 0xf) + (s->a & 0xf0) + (s->data & 0xf0);
        } else {
            tmp = (tmp & 0xf) + (s->a & 0xf0) + (s->data & 0xf0) + 0x10;
        }

        s->p.bits.v = ((uint8_t)(s->a ^ tmp) & (~s->a ^ s->data) & 0x80) != 0;

        s->p.bits.c = 0;
        if ((tmp & 0x1f0) > 0x90) {
            tmp += 0x60;
            s->p.bits.c = 1;
        }

        s->a = (uint8_t)tmp;
        SetNZ(s, s->a);
    } else {
        M6502Word result;

        result.w = s->a + s->data + s->p.bits.c;

        SetNZ(s, result.b.l);
        s->p.bits.c = result.b.h > 0;
        s->p.bits.v = (~(s->a ^ s->data) & (s->a ^ result.b.l) & 0x80) != 0;

        s->a = result.b.l;
    }
}

static void SBC(M6502 *s) {
    M6502Word result;
    result.w = s->a + (uint8_t)~s->data + s->p.bits.c;

    if (s->p.bits.d) {
        unsigned tmp = (s->a & 0xfu) - (s->data & 0xfu) - (!s->p.bits.c);

        if (tmp & 0x10) {
            tmp = ((tmp - 6u) & 0xfu) | ((s->a & 0xf0u) - (s->data & 0xf0u) - 0x10u);
        } else {
            tmp = (tmp & 0xfu) | ((s->a & 0xf0u) - (s->data & 0xf0u));
        }

        if (tmp & 0x100) {
            tmp -= 0x60;
        }

        SetNZ(s, result.b.l);

        s->p.bits.c = result.b.h > 0;
        s->p.bits.v = (((s->a ^ result.b.l) & 0x80) && ((s->a ^ s->data) & 0x80)) != 0;
        s->a = (uint8_t)tmp;
    } else {
        SetNZ(s, result.b.l);
        s->p.bits.c = result.b.h > 0;
        s->p.bits.v = ((s->a ^ s->data) & (s->a ^ result.b.l) & 0x80) != 0;
        s->a = result.b.l;
    }
}

static void SBC_CMOS(M6502 *s) {
    if (s->p.bits.d) {
        int al = (s->a & 0xf) - (s->data & 0xf) - !s->p.bits.c;
        int result = s->a - s->data - !s->p.bits.c;

        s->p.bits.v = ((s->a ^ s->data) & (s->a ^ (uint8_t)result) & 128) != 0;
        s->p.bits.c = (result & 0x100) == 0;

        if (result < 0) {
            result -= 0x60;
        }

        if (al < 0) {
            result -= 0x06;
        }

        s->a = (uint8_t)result;
        SetNZ(s, s->a);
    } else {
        M6502Word result;
        result.w = s->a + (uint8_t)~s->data + s->p.bits.c;

        SetNZ(s, result.b.l);
        s->p.bits.c = result.b.h > 0;
        s->p.bits.v = ((s->a ^ s->data) & (s->a ^ result.b.l) & 0x80) != 0;
        s->a = result.b.l;
    }
}

static void AND(M6502 *s) {
    s->a &= s->data;
    SetNZ(s, s->a);
}

static void BIT(M6502 *s) {
    uint8_t result = s->a & s->data;
    s->p.bits.z = result == 0;

    s->p.value &= ~0xc0u;
    s->p.value |= s->data & 0xc0;
}

// This is just BIT immediate.
static void BIT_CMOS(M6502 *s) {
    uint8_t result = s->a & s->data;
    s->p.bits.z = result == 0;
}

static void CMP(M6502 *s) {
    DoCMP(s, s->a);
}

static void CPX(M6502 *s) {
    DoCMP(s, s->x);
}

static void CPY(M6502 *s) {
    DoCMP(s, s->y);
}

static void EOR(M6502 *s) {
    s->a ^= s->data;
    SetNZ(s, s->a);
}

static void LDA(M6502 *s) {
    s->a = s->data;
    SetNZ(s, s->a);
}

static void LDX(M6502 *s) {
    s->x = s->data;
    SetNZ(s, s->x);
}

static void LDY(M6502 *s) {
    s->y = s->data;
    SetNZ(s, s->y);
}

static void ORA(M6502 *s) {
    s->a |= s->data;
    SetNZ(s, s->a);
}

static void NOP(M6502 *s) {
    (void)s;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Read-modify-write instructions. Modify s->data and update 6502
 * state.
 *
 * Instructions that can operate on the accumulator also have an xxxA
 * version.
 */

static void ROL(M6502 *s) {
    DoROL(s, &s->data);
}

static void ROLA(M6502 *s) {
    DoROL(s, &s->a);
}

static void ROR(M6502 *s) {
    DoROR(s, &s->data);
}

static void RORA(M6502 *s) {
    DoROR(s, &s->a);
}

static void ASL(M6502 *s) {
    DoASL(s, &s->data);
}

static void ASLA(M6502 *s) {
    DoASL(s, &s->a);
}

static void LSR(M6502 *s) {
    DoLSR(s, &s->data);
}

static void LSRA(M6502 *s) {
    DoLSR(s, &s->a);
}

static void INC(M6502 *s) {
    DoINC(s, &s->data);
}

static void INCA(M6502 *s) {
    DoINC(s, &s->a);
}

static void DEC(M6502 *s) {
    DoDEC(s, &s->data);
}

static void DECA(M6502 *s) {
    DoDEC(s, &s->a);
}

static void TRB(M6502 *s) {
    s->p.bits.z = (s->data & s->a) == 0;
    s->data &= ~s->a;
}

static void TSB(M6502 *s) {
    s->p.bits.z = (s->data & s->a) == 0;
    s->data |= s->a;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Write instructions. Set s->data to the value to be
 * written. */

static void STA(M6502 *s) {
    s->data = s->a;
}

static void STX(M6502 *s) {
    s->data = s->x;
}

static void STY(M6502 *s) {
    s->data = s->y;
}

static void STZ(M6502 *s) {
    s->data = 0;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Branch instructions. These set s->data to indicate that the branch
 * should be taken (s->data true) or not taken (s->data false). */

static void BCC(M6502 *s) {
    s->data = !s->p.bits.c;
}

static void BCS(M6502 *s) {
    s->data = s->p.bits.c;
}

static void BEQ(M6502 *s) {
    s->data = s->p.bits.z;
}

static void BMI(M6502 *s) {
    s->data = s->p.bits.n;
}

static void BNE(M6502 *s) {
    s->data = !s->p.bits.z;
}

static void BPL(M6502 *s) {
    s->data = !s->p.bits.n;
}

static void BVC(M6502 *s) {
    s->data = !s->p.bits.v;
}

static void BVS(M6502 *s) {
    s->data = s->p.bits.v;
}

static void BRA(M6502 *s) {
    s->data = 1;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Push instructions. Set s->data to the value to be pushed. */

static void PHA(M6502 *s) {
    s->data = s->a;
}

static void PHP(M6502 *s) {
    s->data = GetP(s).value;
}

static void PHX(M6502 *s) {
    s->data = s->x;
}

static void PHY(M6502 *s) {
    s->data = s->y;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Pop instructions. Read the popped value from s->data. */

static void PLP(M6502 *s) {
    s->p.value = s->data;
}

static void PLA(M6502 *s) {
    s->a = s->data;
    SetNZ(s, s->a);
}

static void PLX(M6502 *s) {
    s->x = s->data;
    SetNZ(s, s->x);
}

static void PLY(M6502 *s) {
    s->y = s->data;
    SetNZ(s, s->y);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Undocumented instructions, of one type or another. */

/* RMW */
static void SLO(M6502 *s) {
    ASL(s);
    ORA(s);
}

/* RMW */
static void RLA(M6502 *s) {
    ROL(s);
    AND(s);
}

/* RMW */
static void SRE(M6502 *s) {
    LSR(s);
    EOR(s);
}

/* RMW */
static void RRA(M6502 *s) {
    ROR(s);
    ADC(s);
}

/* RMW */
static void DCP(M6502 *s) {
    DEC(s);
    CMP(s);
}

/* RMW */
static void ISB(M6502 *s) {
    INC(s);
    SBC(s);
}

/* Read */
static void LAX(M6502 *s) {
    LDA(s);
    LDX(s);
}

/* Write */
static void SAX(M6502 *s) {
    s->data = s->a & s->x;
}

/* Read */
static void ASR(M6502 *s) {
    AND(s);
    LSRA(s);
}

/* Read */
static void ARR(M6502 *s) {
    /* Copied from 64doc. Passes the Lorenz test suite, and frankly
     * I'm disinclined to argue. */
    if (s->p.bits.d) {
        uint8_t t = s->a & s->data; /* Perform the AND */
        uint8_t ah = t >> 4;
        uint8_t al = t & 15;

        s->a = (t >> 1) | (s->p.bits.c << 7); /* Perform the ROR */
        SetNZ(s, s->a);

        s->p.bits.v = (s->a ^ t) >> 6; /* set the V flag in a weird way */

        if (al + (al & 1) > 5) {
            s->a = (s->a & 0xf0) | ((s->a + 6) & 0x0f);
        }

        s->p.bits.c = ah + (ah & 1) > 5;
        if (s->p.bits.c) {
            s->a += 0x60;
        }
    } else {
        AND(s);
        RORA(s);

        s->p.bits.c = s->a >> 6;
        s->p.bits.v = (s->a >> 6) ^ (s->a >> 5);
    }
}

/* Read */

/* See
 * https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/6510core.c#l651
 * for some C64 notes.
 *
 * See
 * http://visual6502.org/wiki/index.php?title=6502_Opcode_8B_(XAA,_ANE)
 * for general notes.
 *
 * I haven't done any testing on a real BBC Micro with this; the
 * constant here is 0xEE, which makes it pass the Lorenz test suite.
 * Perhaps this ought to be configurable.
 */
static void ANE(M6502 *s) {
    s->a = (s->a | s->config->xaa_magic) & s->x & s->data;
    SetNZ(s, s->a);
}

/* Read */
/* Some documents have this as a special form of LAX... but there's
 * already an LAX. So this is LXA, the 64doc name. */
static void LXA(M6502 *s) {
    s->x = s->a = (s->a | s->config->xaa_magic) & s->data;
    SetNZ(s, s->a);
}

/* Read */
static void SBX(M6502 *s) {
    uint8_t lhs = s->a & s->x;
    s->x = lhs - s->data;
    DoCMP(s, lhs);
}

/* Write */
static void SHA(M6502 *s) {
    s->data = s->a & s->x & (s->ad.b.h + 1);
}

/* Write */
static void SHX(M6502 *s) {
    s->data = s->x & (s->ad.b.h + 1);
}

/* Write */
static void SHY(M6502 *s) {
    s->data = s->y & (s->ad.b.h + 1);
}

/* Write */
static void SHS(M6502 *s) {
    s->s.b.l = s->a & s->x;
    s->data = s->a & s->x & (s->ad.b.h + 1);
}

/* Read */
static void ANC(M6502 *s) {
    AND(s);
    s->p.bits.c = s->p.bits.n;
}

static void LDS(M6502 *s) {
    s->s.b.l = s->x = s->a = s->s.b.l & s->data;
    SetNZ(s, s->a);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// Rockwell instructions.

/* RMW */
static void RMB(M6502 *s) {
    uint8_t bit = s->opcode >> 4 & 7;
    s->data &= ~(1 << bit);
}

/* RMW */
static void SMB(M6502 *s) {
    uint8_t bit = s->opcode >> 4 & 7;
    s->data |= 1 << bit;
}

/* Weird branch. Data set on entry as if a read instruction. Set data for exit
 * as if a branch instruction.
 */
static void BBR(M6502 *s) {
    uint8_t bit = s->opcode >> 4 & 7;
    s->data = ~s->data & 1 << bit;
}

static void BBS(M6502 *s) {
    uint8_t bit = s->opcode >> 4 & 7;
    s->data &= 1 << bit;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle0_All(M6502 *s);

void M6502_NextInstruction(M6502 *s) {
    if (!s->d1x1) {
        s->abus.w = s->pc.w;
        s->read = M6502ReadType_Interrupt;
        s->tfn = s->interrupt_tfn;
    } else {
        s->abus.w = s->pc.w++;
        s->opcode_pc = s->abus;
        s->read = M6502ReadType_Opcode;
        s->tfn = &Cycle0_All;
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle0_All(M6502 *s) {
    /* T0 phase 2 */
    s->opcode = s->dbus;
    //++s->pc.w;

    const M6502Fns *fns = &s->fns[s->opcode];

    s->ifn = fns->ifn;

    (*fns->t0fn)(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_Branch(M6502 *s);
static void Cycle2_Branch(M6502 *s);
static void Cycle3_Branch(M6502 *s);

void Cycle0_Branch(M6502 *s) {
    /* T0 phase 2 */
    /* (decode - already done) */
    CheckForInterrupts(s); //TODO - CheckForInterrupts in phi2

    /* T1 phase 1 */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_Branch;
}

static void Cycle1_Branch(M6502 *s) {
    /* T1 phase 2 */
    s->ad.b.l = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    //CheckForInterrupts(s);

    if (!s->data) {
        /* Branch not taken - done. */

        /* T0 phase 1 */
        M6502_NextInstruction(s);
        return;
    }

    s->ad.w = (uint16_t)(s->pc.w + (int8_t)s->ad.b.l);

    /* T2 phase 1 */
    s->abus = s->pc;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_Branch;
}

static void Cycle2_Branch(M6502 *s) {
    /* T2 phase 2 */
    if (s->ad.b.h == s->pc.b.h) {
        /* Branch taken, no carry - done. */
        s->pc = s->ad;

        /* T0 phase 1 */
        M6502_NextInstruction(s);

        //        /* Do this after the NextInstruction stuff. Any IRQ that's
        //         * spotted here wants to delay for one more instruction.
        //         */i
        //        CheckForInterrupts(s);

        return;
    }

    /* T3 phase 1 */
    s->abus.b.l = s->ad.b.l;
    s->abus.b.h = s->pc.b.h;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle3_Branch;
}

static void Cycle3_Branch(M6502 *s) {
    /* T3 phase 2 */
    s->pc = s->ad;

    /* T0 phase 1 */
    CheckForInterrupts(s);
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_Branch_ZPG_REL_ROCKWELL(M6502 *s);
static void Cycle2_Branch_ZPG_REL_ROCKWELL(M6502 *s);
static void Cycle3_Branch_ZPG_REL_ROCKWELL(M6502 *s);

void Cycle0_Branch_ZPG_REL_ROCKWELL(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_Branch_ZPG_REL_ROCKWELL;
}

static void Cycle1_Branch_ZPG_REL_ROCKWELL(M6502 *s) {
    s->abus.w = s->dbus; //Zero page address
    s->ia.b.l = s->dbus; //Save it somewhere the trace can find it
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle2_Branch_ZPG_REL_ROCKWELL;
}

static void Cycle2_Branch_ZPG_REL_ROCKWELL(M6502 *s) {
    s->data = s->dbus; //Save zero page data
    // Hmm. What actually happens here?
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_Branch_ZPG_REL_ROCKWELL;
}

static void Cycle3_Branch_ZPG_REL_ROCKWELL(M6502 *s) {
    // Hmm. What actually happens here?
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_Branch;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle0_R_ABX_CMOS(M6502 *s);
static void Cycle1_R_ABX_CMOS(M6502 *s);
static void Cycle2_R_ABX_CMOS(M6502 *s);
static void Cycle3_R_ABX_CMOS_AC0(M6502 *s);
static void Cycle3_R_ABX_CMOS_AC1(M6502 *s);
static void Cycle4_R_ABX_CMOS_AC1(M6502 *s);

static void Cycle0_R_ABX_CMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_R_ABX_CMOS;
}

static void Cycle1_R_ABX_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_R_ABX_CMOS;
}

static void Cycle2_R_ABX_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h != s->ad.b.h) {
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABX_CMOS_AC1;
    } else {
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABX_CMOS_AC0;
        CheckForInterrupts(s);
    }
}

static void Cycle3_R_ABX_CMOS_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502_NextInstruction(s);
}

static void Cycle3_R_ABX_CMOS_AC1(M6502 *s) {
    s->abus.w = s->ad.w + s->x;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABX_CMOS_AC1;
}

static void Cycle4_R_ABX_CMOS_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle0_R_ABY_CMOS(M6502 *s);
static void Cycle1_R_ABY_CMOS(M6502 *s);
static void Cycle2_R_ABY_CMOS(M6502 *s);
static void Cycle3_R_ABY_CMOS_AC0(M6502 *s);
static void Cycle3_R_ABY_CMOS_AC1(M6502 *s);
static void Cycle4_R_ABY_CMOS_AC1(M6502 *s);

static void Cycle0_R_ABY_CMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_R_ABY_CMOS;
}

static void Cycle1_R_ABY_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_R_ABY_CMOS;
}

static void Cycle2_R_ABY_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h != s->ad.b.h) {
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABY_CMOS_AC1;
    } else {
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABY_CMOS_AC0;
        CheckForInterrupts(s);
    }
}

static void Cycle3_R_ABY_CMOS_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502_NextInstruction(s);
}

static void Cycle3_R_ABY_CMOS_AC1(M6502 *s) {
    s->abus.w = s->ad.w + s->y;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABY_CMOS_AC1;
}

static void Cycle4_R_ABY_CMOS_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_RMW_ABX_CMOS(M6502 *s);
static void Cycle2_RMW_ABX_CMOS(M6502 *s);
static void Cycle3_RMW_ABX_CMOS(M6502 *s);
static void Cycle4_RMW_ABX_CMOS(M6502 *s);
static void Cycle5_RMW_ABX_CMOS(M6502 *s);
static void Cycle6_RMW_ABX_CMOS(M6502 *s);

// INC/DEC
static void Cycle0_RMW_ABX_CMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_RMW_ABX_CMOS;
}

static void Cycle1_RMW_ABX_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;

    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_RMW_ABX_CMOS;
}

static void Cycle2_RMW_ABX_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;

    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    }

    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_RMW_ABX_CMOS;
}

static void Cycle3_RMW_ABX_CMOS(M6502 *s) {
    s->abus.w = s->ad.w + s->x;
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle4_RMW_ABX_CMOS;
}

static void Cycle4_RMW_ABX_CMOS(M6502 *s) {
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle5_RMW_ABX_CMOS;
}

static void Cycle5_RMW_ABX_CMOS(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
    s->read = 0;
    s->dbus = s->data;
    s->tfn = &Cycle6_RMW_ABX_CMOS;
    CheckForInterrupts(s);
}

static void Cycle6_RMW_ABX_CMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// This slightly unusual case was making the code generator a bit
// fiddly - so here it is, added by hand.

static void Cycle1_RMW_ABX2_CMOS(M6502 *);
static void Cycle2_RMW_ABX2_CMOS(M6502 *);
static void Cycle3_RMW_ABX2_CMOS_AC0(M6502 *);
static void Cycle3_RMW_ABX2_CMOS_AC1(M6502 *);
static void Cycle4_RMW_ABX2_CMOS_AC0(M6502 *);
static void Cycle4_RMW_ABX2_CMOS_AC1(M6502 *);
static void Cycle5_RMW_ABX2_CMOS_AC0(M6502 *);
static void Cycle5_RMW_ABX2_CMOS_AC1(M6502 *);
static void Cycle6_RMW_ABX2_CMOS_AC1(M6502 *);

static void Cycle0_RMW_ABX2_CMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_RMW_ABX2_CMOS;
}

static void Cycle1_RMW_ABX2_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;

    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_RMW_ABX2_CMOS;
}

static void Cycle2_RMW_ABX2_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;

    s->read = M6502ReadType_Uninteresting;
    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
        s->tfn = &Cycle3_RMW_ABX2_CMOS_AC0;
    } else {
        s->tfn = &Cycle3_RMW_ABX2_CMOS_AC1;
    }
}

static void Cycle3_RMW_ABX2_CMOS_AC0(M6502 *s) {
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_RMW_ABX2_CMOS_AC0;
}

static void Cycle4_RMW_ABX2_CMOS_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    CheckForInterrupts(s);
    s->read = 0;
    s->dbus = s->data;
    s->tfn = &Cycle5_RMW_ABX2_CMOS_AC0;
}

static void Cycle5_RMW_ABX2_CMOS_AC0(M6502 *s) {
    M6502_NextInstruction(s);
}

static void Cycle3_RMW_ABX2_CMOS_AC1(M6502 *s) {
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle4_RMW_ABX2_CMOS_AC1;
}

static void Cycle4_RMW_ABX2_CMOS_AC1(M6502 *s) {
    s->abus.w = s->ad.w + s->x;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle5_RMW_ABX2_CMOS_AC1;
}

static void Cycle5_RMW_ABX2_CMOS_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    s->read = 0;
    s->dbus = s->data;
    s->tfn = &Cycle6_RMW_ABX2_CMOS_AC1;
    CheckForInterrupts(s);
}

static void Cycle6_RMW_ABX2_CMOS_AC1(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_W_ABX_CMOS(M6502 *);
static void Cycle2_W_ABX_CMOS(M6502 *);
static void Cycle3_W_ABX_CMOS(M6502 *);
static void Cycle4_W_ABX_CMOS(M6502 *);

static void Cycle0_W_ABX_CMOS(M6502 *s) {
    /* (called from Cycle0_All) */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_W_ABX_CMOS;
}

static void Cycle1_W_ABX_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_W_ABX_CMOS;
}

static void Cycle2_W_ABX_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    }
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_W_ABX_CMOS;
}

static void Cycle3_W_ABX_CMOS(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    s->abus.w = s->ad.w + s->x;
    s->dbus = s->data;
    s->read = 0;
    s->tfn = &Cycle4_W_ABX_CMOS;
    CheckForInterrupts(s);
}

static void Cycle4_W_ABX_CMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_W_ABY_CMOS(M6502 *);
static void Cycle2_W_ABY_CMOS(M6502 *);
static void Cycle3_W_ABY_CMOS(M6502 *);
static void Cycle4_W_ABY_CMOS(M6502 *);

static void Cycle0_W_ABY_CMOS(M6502 *s) {
    /* (called from Cycle0_All) */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_W_ABY_CMOS;
}

static void Cycle1_W_ABY_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle2_W_ABY_CMOS;
}

static void Cycle2_W_ABY_CMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    }
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_W_ABY_CMOS;
}

static void Cycle3_W_ABY_CMOS(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    s->abus.w = s->ad.w + s->y;
    s->dbus = s->data;
    s->read = 0;
    s->tfn = &Cycle4_W_ABY_CMOS;
    CheckForInterrupts(s);
}

static void Cycle4_W_ABY_CMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/* Complete guesswork based on playing about with Visual6502. */

static void Cycle1_HLT(M6502 *s);

static void Cycle0_HLT(M6502 *s) {
    /* T0 phase 2 */

    /* T1 phase 1 */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_HLT;
}

static void Cycle1_HLT(M6502 *s) {
    /* T1 phase 2 */

    /* T2 phase 1 */
    s->abus.w = 0xffff;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_HLT;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_Interrupt(M6502 *);
static void Cycle2_Interrupt(M6502 *);
static void Cycle3_Interrupt(M6502 *);
static void Cycle4_Interrupt(M6502 *);
static void Cycle5_Interrupt(M6502 *);
static void Cycle6_Interrupt(M6502 *);

static void Cycle0_Interrupt(M6502 *s) {
    /* T0 phase 2 */
    /* (decode - already done) */

    /* T1 phase 1 */
    s->abus.w = s->pc.w;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_Interrupt;

    if (s->d1x1) {
        // This is BRK. Increment PC.
        ++s->pc.w;
    }
}

static void Cycle1_Interrupt(M6502 *s) {
    /* T1 phase 2 */
    s->data = s->dbus;

    /* T2 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.h;
    s->read = 0;
    s->tfn = &Cycle2_Interrupt;
}

static void Cycle2_Interrupt(M6502 *s) {
    /* T2 phase 2 */

    /* T3 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.l;
    s->read = 0;
    s->tfn = &Cycle3_Interrupt;
}

static void Cycle3_Interrupt(M6502 *s) {
    /* T3 phase 2 */

    /* T4 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = GetP(s).value;
    s->read = 0;
    s->tfn = &Cycle4_Interrupt;
}

static void Cycle4_Interrupt(M6502 *s) {
    /* T4 phase 2 */
    //s->p.bits.b=1;

    /* T5 phase 1 */
    if (s->nmi_flags != 0) {
        // NMI
        s->abus.w = 0xfffa;
        s->nmi_flags = 0;
    } else if (s->d1x1 || s->irq_flags != 0) {
        // BRK/IRQ
        s->abus.w = 0xfffe;

        // TODO: Is it safe to do this when it's a BRK? Need some more
        // time with Visual6502 again...
        s->irq_flags = s->device_irq_flags;
    } else {
        // If D1x1 is low, but there's no obvious source of
        // interrupts, the CPU does an IRQ.
        s->abus.w = 0xfffe;
    }

    s->p.bits.i = 1;

    s->read = M6502ReadType_Address;
    s->tfn = &Cycle5_Interrupt;

    // Can forget about this now.
    s->d1x1 = 1;
}

static void Cycle5_Interrupt(M6502 *s) {
    /* T5 phase 2 */
    s->pc.b.l = s->dbus;

    /* T6 phase 1 */
    assert(s->abus.w == 0xfffa || s->abus.w == 0xfffe);
    ++s->abus.w;
    s->read = M6502ReadType_Address;
    s->tfn = &Cycle6_Interrupt;
}

static void Cycle6_Interrupt(M6502 *s) {
    /* T6 phase 2 */
    s->pc.b.h = s->dbus;

    /* T0 phase 1 */
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// I basically just guessed this whole thing...
//
// Since it makes all the decisions in the first cycle, there's no way
// one type of interrupt can turn into another - and CMOS BRK is
// handled separately.

static void Cycle1_InterruptCMOS(M6502 *);
static void Cycle2_InterruptCMOS(M6502 *);
static void Cycle3_InterruptCMOS(M6502 *);
static void Cycle4_InterruptCMOS(M6502 *);
static void Cycle5_InterruptCMOS(M6502 *);
static void Cycle6_InterruptCMOS(M6502 *);

static void Cycle0_InterruptCMOS(M6502 *s) {
    /* T0 phase 2 */
    /* (decode - already done) */

    // Decide on the interrupt type straight away.
    if (s->nmi_flags != 0) {
        // NMI
        s->data = 1;
        s->nmi_flags = 0;
    } else if (s->irq_flags != 0) {
        //IRQ
        s->data = 0;
        s->irq_flags = s->device_irq_flags;
    } else {
        // ??? - assume NMI.
        s->data = 1;
    }

    // Cancel d1x1.
    s->d1x1 = 1;

    /* T1 phase 1 */
    s->abus.w = s->pc.w;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_InterruptCMOS;
}

static void Cycle1_InterruptCMOS(M6502 *s) {
    /* T1 phase 2 */
    //s->data=s->dbus;

    /* T2 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.h;
    s->read = 0;
    s->tfn = &Cycle2_InterruptCMOS;
}

static void Cycle2_InterruptCMOS(M6502 *s) {
    /* T2 phase 2 */

    /* T3 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.l;
    s->read = 0;
    s->tfn = &Cycle3_InterruptCMOS;
}

static void Cycle3_InterruptCMOS(M6502 *s) {
    /* T3 phase 2 */

    /* T4 phase 1 */
    s->abus = s->s;
    --s->s.b.l;

    M6502P p;
    p.value = s->p.value;
    p.bits._ = 1;
    p.bits.b = 0;
    s->dbus = p.value;
    s->read = 0;
    s->tfn = &Cycle4_InterruptCMOS;
}

static void Cycle4_InterruptCMOS(M6502 *s) {
    /* T4 phase 2 */
    //s->p.bits.b=1;

    /* T5 phase 1 */
    if (s->data) {
        s->abus.w = 0xfffa;
    } else {
        s->abus.w = 0xfffe;
    }

    s->p.bits.i = 1;
    s->p.bits.d = 0;

    s->read = M6502ReadType_Address;
    s->tfn = &Cycle5_InterruptCMOS;
}

static void Cycle5_InterruptCMOS(M6502 *s) {
    /* T5 phase 2 */
    s->pc.b.l = s->dbus;

    /* T6 phase 1 */
    assert(s->abus.w == 0xfffa || s->abus.w == 0xfffe);
    ++s->abus.w;
    s->read = M6502ReadType_Address;
    s->tfn = &Cycle6_InterruptCMOS;
}

static void Cycle6_InterruptCMOS(M6502 *s) {
    /* T6 phase 2 */
    s->pc.b.h = s->dbus;

    /* T0 phase 1 */
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_BRK_CMOS(M6502 *);
static void Cycle2_BRK_CMOS(M6502 *);
static void Cycle3_BRK_CMOS(M6502 *);
static void Cycle4_BRK_CMOS(M6502 *);
static void Cycle5_BRK_CMOS(M6502 *);
static void Cycle6_BRK_CMOS(M6502 *);

static void Cycle0_BRK_CMOS(M6502 *s) {
    /* T0 phase 2 */
    /* (decode - already done) */

    /* T1 phase 1 */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_BRK_CMOS;
}

static void Cycle1_BRK_CMOS(M6502 *s) {
    /* T1 phase 2 */
    s->data = s->dbus;

    /* T2 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.h;
    s->read = 0;
    s->tfn = &Cycle2_BRK_CMOS;
}

static void Cycle2_BRK_CMOS(M6502 *s) {
    /* T2 phase 2 */

    /* T3 phase 1 */
    s->abus = s->s;
    --s->s.b.l;
    s->dbus = s->pc.b.l;
    s->read = 0;
    s->tfn = &Cycle3_BRK_CMOS;
}

static void Cycle3_BRK_CMOS(M6502 *s) {
    /* T3 phase 2 */

    /* T4 phase 1 */
    s->abus = s->s;
    --s->s.b.l;

    M6502P p;
    p.value = s->p.value;
    p.bits.b = 1;
    p.bits._ = 1;
    s->dbus = p.value;

    s->read = 0;
    s->tfn = &Cycle4_BRK_CMOS;
}

static void Cycle4_BRK_CMOS(M6502 *s) {
    /* T4 phase 2 */
    s->p.bits.i = 1;
    s->p.bits.d = 0;

    /* T5 phase 1 */
    s->abus.w = 0xfffe;

    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle5_BRK_CMOS;
}

static void Cycle5_BRK_CMOS(M6502 *s) {
    /* T5 phase 2 */
    s->pc.b.l = s->dbus;

    /* T6 phase 1 */
    ++s->abus.w;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle6_BRK_CMOS;
}

static void Cycle6_BRK_CMOS(M6502 *s) {
    /* T6 phase 2 */
    s->pc.b.h = s->dbus;

    /* T0 phase 1 */
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_R_ABX_BCD_CMOS(M6502 *);
static void Cycle2_R_ABX_BCD_CMOS_D1(M6502 *);
static void Cycle2_R_ABX_BCD_CMOS_D0(M6502 *);
static void Cycle3_R_ABX_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle3_R_ABX_BCD_CMOS_D0_AC0(M6502 *);
static void Cycle4_R_ABX_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle3_R_ABX_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle3_R_ABX_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle4_R_ABX_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle4_R_ABX_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle5_R_ABX_BCD_CMOS_D1_AC1(M6502 *);

static void Cycle0_R_ABX_BCD_CMOS(M6502 *s) {
    /* (called from Cycle0_All) */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_R_ABX_BCD_CMOS;
}

static void Cycle1_R_ABX_BCD_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    if (s->p.bits.d) {
        s->tfn = &Cycle2_R_ABX_BCD_CMOS_D1;
    } else {
        s->tfn = &Cycle2_R_ABX_BCD_CMOS_D0;
    }
}

// No BCD
static void Cycle2_R_ABX_BCD_CMOS_D0(M6502 *s) {
    s->ad.b.h = s->dbus;

    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h != s->ad.b.h) {
        /* Carry */
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABX_BCD_CMOS_D0_AC1;
    } else {
        /* No carry - penultimate cycle. */
        CheckForInterrupts(s);
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABX_BCD_CMOS_D0_AC0;
    }
}

static void Cycle3_R_ABX_BCD_CMOS_D0_AC1(M6502 *s) {
    /* Ignore dummy read */
    s->abus.w = s->ad.w + s->x;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABX_BCD_CMOS_D0_AC1;
    CheckForInterrupts(s);
}

static void Cycle3_R_ABX_BCD_CMOS_D0_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_ABX_BCD_CMOS_D0_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

// BCD
static void Cycle2_R_ABX_BCD_CMOS_D1(M6502 *s) {
    s->ad.b.h = s->dbus;

    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h != s->ad.b.h) {
        /* Carry */
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABX_BCD_CMOS_D1_AC1;
    } else {
        /* No carry */
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABX_BCD_CMOS_D1_AC0;
    }
}

static void Cycle3_R_ABX_BCD_CMOS_D1_AC1(M6502 *s) {
    /* Ignore dummy read */
    s->abus.w = s->ad.w + s->x;
    CheckForInterrupts(s);
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABX_BCD_CMOS_D1_AC1;
}

static void Cycle3_R_ABX_BCD_CMOS_D1_AC0(M6502 *s) {
    s->data = s->dbus;

    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle4_R_ABX_BCD_CMOS_D1_AC0;
}

static void Cycle4_R_ABX_BCD_CMOS_D1_AC0(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_ABX_BCD_CMOS_D1_AC1(M6502 *s) {
    s->data = s->dbus;

    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle5_R_ABX_BCD_CMOS_D1_AC1;
}

static void Cycle5_R_ABX_BCD_CMOS_D1_AC1(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_R_ABY_BCD_CMOS(M6502 *);
static void Cycle2_R_ABY_BCD_CMOS_D1(M6502 *);
static void Cycle2_R_ABY_BCD_CMOS_D0(M6502 *);
static void Cycle3_R_ABY_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle3_R_ABY_BCD_CMOS_D0_AC0(M6502 *);
static void Cycle4_R_ABY_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle3_R_ABY_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle3_R_ABY_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle4_R_ABY_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle4_R_ABY_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle5_R_ABY_BCD_CMOS_D1_AC1(M6502 *);

static void Cycle0_R_ABY_BCD_CMOS(M6502 *s) {
    /* (called from Cycle0_All) */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_R_ABY_BCD_CMOS;
}

static void Cycle1_R_ABY_BCD_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    if (s->p.bits.d) {
        s->tfn = &Cycle2_R_ABY_BCD_CMOS_D1;
    } else {
        s->tfn = &Cycle2_R_ABY_BCD_CMOS_D0;
    }
}

// No BCD
static void Cycle2_R_ABY_BCD_CMOS_D0(M6502 *s) {
    s->ad.b.h = s->dbus;

    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h != s->ad.b.h) {
        /* Carry */
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABY_BCD_CMOS_D0_AC1;
    } else {
        /* No carry - penultimate cycle. */
        CheckForInterrupts(s);
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABY_BCD_CMOS_D0_AC0;
    }
}

static void Cycle3_R_ABY_BCD_CMOS_D0_AC1(M6502 *s) {
    /* Ignore dummy read */
    s->abus.w = s->ad.w + s->y;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABY_BCD_CMOS_D0_AC1;
    CheckForInterrupts(s);
}

static void Cycle3_R_ABY_BCD_CMOS_D0_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_ABY_BCD_CMOS_D0_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

// BCD
static void Cycle2_R_ABY_BCD_CMOS_D1(M6502 *s) {
    s->ad.b.h = s->dbus;

    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h != s->ad.b.h) {
        /* Carry */
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle3_R_ABY_BCD_CMOS_D1_AC1;
    } else {
        /* No carry */
        s->read = M6502ReadType_Data;
        s->abus = ffa;
        s->tfn = &Cycle3_R_ABY_BCD_CMOS_D1_AC0;
    }
}

static void Cycle3_R_ABY_BCD_CMOS_D1_AC1(M6502 *s) {
    /* Ignore dummy read */
    s->abus.w = s->ad.w + s->y;
    CheckForInterrupts(s);
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle4_R_ABY_BCD_CMOS_D1_AC1;
}

static void Cycle3_R_ABY_BCD_CMOS_D1_AC0(M6502 *s) {
    s->data = s->dbus;

    /* Read PBA. */
    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle4_R_ABY_BCD_CMOS_D1_AC0;
}

static void Cycle4_R_ABY_BCD_CMOS_D1_AC0(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_ABY_BCD_CMOS_D1_AC1(M6502 *s) {
    s->data = s->dbus;

    /* Read PBA. */
    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle5_R_ABY_BCD_CMOS_D1_AC1;
}

static void Cycle5_R_ABY_BCD_CMOS_D1_AC1(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void Cycle1_R_INY_BCD_CMOS(M6502 *);
static void Cycle2_R_INY_BCD_CMOS(M6502 *);
static void Cycle3_R_INY_BCD_CMOS_D1(M6502 *);
static void Cycle3_R_INY_BCD_CMOS_D0(M6502 *);
static void Cycle4_R_INY_BCD_CMOS_D0_AC0(M6502 *);
static void Cycle4_R_INY_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle4_R_INY_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle4_R_INY_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle5_R_INY_BCD_CMOS_D0_AC1(M6502 *);
static void Cycle5_R_INY_BCD_CMOS_D1_AC0(M6502 *);
static void Cycle5_R_INY_BCD_CMOS_D1_AC1(M6502 *);
static void Cycle6_R_INY_BCD_CMOS_D1_AC1(M6502 *);

static void Cycle0_R_INY_BCD_CMOS(M6502 *s) {
    /* (called from Cycle0_All) */
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_R_INY_BCD_CMOS;
}

static void Cycle1_R_INY_BCD_CMOS(M6502 *s) {
    s->ia.b.l = s->dbus;
    s->abus.w = s->ia.b.l;
    s->read = M6502ReadType_Address;
    s->tfn = &Cycle2_R_INY_BCD_CMOS;
}

static void Cycle2_R_INY_BCD_CMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = (uint8_t)(s->ia.b.l + 1);
    s->read = M6502ReadType_Address;
    if (s->p.bits.d) {
        s->tfn = &Cycle3_R_INY_BCD_CMOS_D1;
    } else {
        s->tfn = &Cycle3_R_INY_BCD_CMOS_D0;
    }
}

// Not BCD
static void Cycle3_R_INY_BCD_CMOS_D0(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h != s->ad.b.h) {
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle4_R_INY_BCD_CMOS_D0_AC1;
    } else {
        CheckForInterrupts(s);
        s->abus = ffa;
        s->read = M6502ReadType_Data;
        s->tfn = &Cycle4_R_INY_BCD_CMOS_D0_AC0;
    }
}

static void Cycle4_R_INY_BCD_CMOS_D0_AC0(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_INY_BCD_CMOS_D0_AC1(M6502 *s) {
    s->abus.w = s->ad.w + s->y;
    CheckForInterrupts(s);
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle5_R_INY_BCD_CMOS_D0_AC1;
}

static void Cycle5_R_INY_BCD_CMOS_D0_AC1(M6502 *s) {
    s->data = s->dbus;
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

// BCD
static void Cycle3_R_INY_BCD_CMOS_D1(M6502 *s) {
    s->ad.b.h = s->dbus;
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h != s->ad.b.h) {
        s->read = M6502ReadType_Uninteresting;
        s->tfn = &Cycle4_R_INY_BCD_CMOS_D1_AC1;
    } else {
        s->abus = ffa;
        s->read = M6502ReadType_Data;
        s->tfn = &Cycle4_R_INY_BCD_CMOS_D1_AC0;
    }
}

static void Cycle4_R_INY_BCD_CMOS_D1_AC0(M6502 *s) {
    s->data = s->dbus;

    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle5_R_INY_BCD_CMOS_D1_AC0;
}

static void Cycle5_R_INY_BCD_CMOS_D1_AC0(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

static void Cycle4_R_INY_BCD_CMOS_D1_AC1(M6502 *s) {
    s->abus.w = s->ad.w + s->y;
    s->read = M6502ReadType_Data;
    s->tfn = &Cycle5_R_INY_BCD_CMOS_D1_AC1;
}

static void Cycle5_R_INY_BCD_CMOS_D1_AC1(M6502 *s) {
    s->data = s->dbus;

    s->abus = s->pc;
    s->read = M6502ReadType_Uninteresting;

    CheckForInterrupts(s);

    s->tfn = &Cycle6_R_INY_BCD_CMOS_D1_AC1;
}

static void Cycle6_R_INY_BCD_CMOS_D1_AC1(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif

    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// Broken variant that doesn't handle page crossing properly. Make the dp111
// timing test pass.

// http://visual6502.org/JSSim/expert.html?graphics=f&loglevel=2&steps=40&a=fffc&d=0043&a=0010&d=fefcfffc&a=4300&d=a910a201a00193109312

static void Cycle1_W_INY_BROKEN_NMOS(M6502 *s);
static void Cycle2_W_INY_BROKEN_NMOS(M6502 *s);
static void Cycle3_W_INY_BROKEN_NMOS(M6502 *s);
static void Cycle4_W_INY_BROKEN_NMOS(M6502 *s);
static void Cycle5_W_INY_BROKEN_NMOS(M6502 *s);

static void Cycle0_W_INY_BROKEN_NMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_W_INY_BROKEN_NMOS;
}

static void Cycle1_W_INY_BROKEN_NMOS(M6502 *s) {
    s->ia.b.l = s->dbus;
    s->abus.w = s->ia.b.l;
    s->read = M6502ReadType_Address;
    s->tfn = &Cycle2_W_INY_BROKEN_NMOS;
}

static void Cycle2_W_INY_BROKEN_NMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.b.l = s->ia.b.l + 1;
    s->tfn = &Cycle3_W_INY_BROKEN_NMOS;
}

static void Cycle3_W_INY_BROKEN_NMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    s->abus.b.l = s->ad.b.l + s->y;
    s->abus.b.h = s->ad.b.h;
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle4_W_INY_BROKEN_NMOS;
}

static void Cycle4_W_INY_BROKEN_NMOS(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    } else {
        // Broken page crossing.
        s->abus.b.l = ffa.b.l;
        s->abus.b.h = s->data;
    }
    s->dbus = s->data;
    s->read = 0;
    s->tfn = &Cycle5_W_INY_BROKEN_NMOS;
    CheckForInterrupts(s);
}

static void Cycle5_W_INY_BROKEN_NMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// Broken variant that doesn't handle page crossing properly. Make the dp111
// timing test pass.

static void Cycle1_W_ABX_BROKEN_NMOS(M6502 *s);
static void Cycle2_W_ABX_BROKEN_NMOS(M6502 *s);
static void Cycle3_W_ABX_BROKEN_NMOS(M6502 *s);
static void Cycle4_W_ABX_BROKEN_NMOS(M6502 *s);

static void Cycle0_W_ABX_BROKEN_NMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_W_ABX_BROKEN_NMOS;
}

static void Cycle1_W_ABX_BROKEN_NMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->tfn = &Cycle2_W_ABX_BROKEN_NMOS;
}

static void Cycle2_W_ABX_BROKEN_NMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    s->abus.b.l = s->ad.b.l + s->x;
    s->abus.b.h = s->ad.b.h;
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_W_ABX_BROKEN_NMOS;
}

static void Cycle3_W_ABX_BROKEN_NMOS(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502Word ffa = {s->ad.w + s->x};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    } else {
        // Broken page crossing.
        s->abus.b.l = ffa.b.l;
        s->abus.b.h = s->data;
    }
    s->dbus = s->data;
    s->read = 0;
    s->tfn = &Cycle4_W_ABX_BROKEN_NMOS;
    CheckForInterrupts(s);
}

static void Cycle4_W_ABX_BROKEN_NMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// Broken variant that doesn't handle page crossing properly. Make the dp111
// timing test pass.

static void Cycle1_W_ABY_BROKEN_NMOS(M6502 *s);
static void Cycle2_W_ABY_BROKEN_NMOS(M6502 *s);
static void Cycle3_W_ABY_BROKEN_NMOS(M6502 *s);
static void Cycle4_W_ABY_BROKEN_NMOS(M6502 *s);

static void Cycle0_W_ABY_BROKEN_NMOS(M6502 *s) {
    s->abus.w = s->pc.w++;
    s->read = M6502ReadType_Instruction;
    s->tfn = &Cycle1_W_ABY_BROKEN_NMOS;
}

static void Cycle1_W_ABY_BROKEN_NMOS(M6502 *s) {
    s->ad.b.l = s->dbus;
    s->abus.w = s->pc.w++;
    s->tfn = &Cycle2_W_ABY_BROKEN_NMOS;
}

static void Cycle2_W_ABY_BROKEN_NMOS(M6502 *s) {
    s->ad.b.h = s->dbus;
    s->abus.b.l = s->ad.b.l + s->y;
    s->abus.b.h = s->ad.b.h;
    s->read = M6502ReadType_Uninteresting;
    s->tfn = &Cycle3_W_ABY_BROKEN_NMOS;
}

static void Cycle3_W_ABY_BROKEN_NMOS(M6502 *s) {
    (*s->ifn)(s);
#ifdef _DEBUG
    s->ifn = NULL;
#endif
    M6502Word ffa = {s->ad.w + s->y};
    if (ffa.b.h == s->ad.b.h) {
        s->abus = ffa;
    } else {
        // Broken page crossing.
        s->abus.b.l = ffa.b.l;
        s->abus.b.h = s->data;
    }
    s->dbus = s->data;
    s->read = 0;
    s->tfn = &Cycle4_W_ABY_BROKEN_NMOS;
    CheckForInterrupts(s);
}

static void Cycle4_W_ABY_BROKEN_NMOS(M6502 *s) {
    M6502_NextInstruction(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

char *M6502P_GetString(char *dest, M6502P value) {
    dest[0] = value.bits.n ? 'N' : 'n';
    dest[1] = value.bits.v ? 'V' : 'v';
    dest[2] = value.bits._ ? '-' : '_';
    dest[3] = value.bits.b ? 'B' : 'b';
    dest[4] = value.bits.d ? 'D' : 'd';
    dest[5] = value.bits.i ? 'I' : 'i';
    dest[6] = value.bits.z ? 'Z' : 'z';
    dest[7] = value.bits.c ? 'C' : 'c';
    dest[8] = 0;

    return dest;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

const char *M6502AddrMode_GetName(uint8_t mode) {
    switch ((M6502AddrMode)mode) {
    case M6502AddrMode_IMP:
        return "Implied";

    case M6502AddrMode_IMM:
        return "Immediate";

    case M6502AddrMode_REL:
        return "Relative";

    case M6502AddrMode_ZPG:
        return "Zero Page";

    case M6502AddrMode_ZPX:
        return "Zero Page,X";

    case M6502AddrMode_ZPY:
        return "Zero Page,Y";

    case M6502AddrMode_INX:
        return "(Zero Page,X)";

    case M6502AddrMode_INY:
        return "(Zero Page),Y";

    case M6502AddrMode_ABS:
        return "Absolute";

    case M6502AddrMode_ABX:
        return "Absolute,X";

    case M6502AddrMode_ABY:
        return "Absolute,Y";

    case M6502AddrMode_IND:
        return "(Absolute)";

    case M6502AddrMode_ACC:
        return "Accumulator";

    case M6502AddrMode_INZ:
        return "(Zero Page)";

    case M6502AddrMode_INDX:
        return "(Absolute,X)";

    case M6502AddrMode_ZPG_REL_ROCKWELL:
        return "Zero Page,Relative";
    }

    return "?";
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

struct NamedFn {
    const char *name;
    M6502Fn fn;
};
typedef struct NamedFn NamedFn;

#include <6502_internal.inl>

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static const char *FindNameByFn(const NamedFn *named_fns, M6502Fn fn) {
    for (const NamedFn *named_fn = named_fns; named_fn->name; ++named_fn) {
        if (named_fn->fn == fn) {
            return named_fn->name;
        }
    }

    return NULL;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

const M6502Config M6502_defined_config = {
    .name = "6502 (defined instructions only)",
    .fns = g_defined_fns,
    .interrupt_tfn = &Cycle0_Interrupt,
    .disassembly_info = g_defined_disassembly_info,
};

const M6502Config M6502_nmos6502_config = {
    .name = "NMOS 6502",
    .xaa_magic = 0xee,
    .fns = g_nmos6502_fns,
    .interrupt_tfn = &Cycle0_Interrupt,
    .disassembly_info = g_nmos6502_disassembly_info,
};

const M6502Config M6502_cmos6502_config = {
    .name = "CMOS 65C02",
    .fns = g_cmos6502_fns,
    .interrupt_tfn = &Cycle0_InterruptCMOS,
    .disassembly_info = g_cmos6502_disassembly_info,
};

const M6502Config M6502_rockwell65c02_config = {
    .name = "Rockwell 65C02",
    .fns = g_rockwell65c02_fns,
    .interrupt_tfn = &Cycle0_InterruptCMOS,
    .disassembly_info = g_rockwell65c02_disassembly_info,
};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

const char *M6502ReadType_GetName(uint8_t read_type) {
    switch (read_type) {
    case 0:
        return "Write";

    case M6502ReadType_Data:
        return "Read Data";

    case M6502ReadType_Instruction:
        return "Read Instruction";

    case M6502ReadType_Address:
        return "Read Address";

    case M6502ReadType_Uninteresting:
        return "Read Internal";

    case M6502ReadType_Opcode:
        return "Read Opcode";

    case M6502ReadType_Interrupt:
        return "Read Interrupted Opcode";
    }

    return "?";
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_Init(M6502 *s, const M6502Config *config) {
    memset(s, 0, sizeof *s);

    s->s.b.h = 1;

    s->config = config;

    s->fns = s->config->fns;
    s->interrupt_tfn = s->config->interrupt_tfn;

    M6502_Reset(s);
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_Destroy(M6502 *s) {
    (void)s;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_Reset(M6502 *s) {
    s->d1x1 = 1;
    s->tfn = &Cycle0_Reset;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_Halt(M6502 *s) {
    s->tfn = &Cycle1_HLT;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

static void DisassembleByte(char *buf,
                            size_t buf_size,
                            const M6502DisassemblyInfo *di,
                            const char *prefix,
                            uint8_t value,
                            const char *suffix) {
    snprintf(buf, buf_size, "%s %s$%02x%s", di->mnemonic, prefix, value, suffix);
}

static void DisassembleWord(char *buf,
                            size_t buf_size,
                            const M6502DisassemblyInfo *di,
                            const char *prefix,
                            uint16_t value,
                            const char *suffix) {
    snprintf(buf, buf_size, "%s %s$%04x%s", di->mnemonic, prefix, value, suffix);
}

//uint16_t M6502Config_DisassembleInstruction(const M6502Config *config,char *buf,size_t buf_size,uint16_t pc,uint8_t a,uint8_t b,uint8_t c) {
//    const M6502DisassemblyInfo *di=&config->disassembly_info[a];
//
//    switch(di->mode) {
//    default:
//        assert(0);
//        // fall through
//    case M6502AddrMode_IMP:
//        snprintf(buf,buf_size,"%s",di->mnemonic);
//        return 1;
//
//    case M6502AddrMode_IMM:
//        DisassembleByte(buf,buf_size,di,"#",b,"");
//        return 2;
//
//    case M6502AddrMode_REL:
//        DisassembleWord(buf,buf_size,di,"",(uint16_t)(pc+2+(uint16_t)(int16_t)(int8_t)b),"");
//        return 2;
//
//    case M6502AddrMode_ZPG:
//        DisassembleByte(buf,buf_size,di,"",b,"");
//        return 2;
//
//    case M6502AddrMode_ZPX:
//        DisassembleByte(buf,buf_size,di,"",b,",x");
//        return 2;
//
//    case M6502AddrMode_ZPY:
//        DisassembleByte(buf,buf_size,di,"",b,",y");
//        return 2;
//
//    case M6502AddrMode_INX:
//        DisassembleByte(buf,buf_size,di,"(",b,",x)");
//        return 2;
//
//    case M6502AddrMode_INY:
//        DisassembleByte(buf,buf_size,di,"(",b,"),y");
//        return 2;
//
//    case M6502AddrMode_ABS:
//        DisassembleWord(buf,buf_size,di,"",(M6502Word) { .b.l=b,.b.h=c }.w,"");
//        return 3;
//
//    case M6502AddrMode_ABX:
//        DisassembleWord(buf,buf_size,di,"",(M6502Word) { .b.l=b,.b.h=c }.w,",x");
//        return 3;
//
//    case M6502AddrMode_ABY:
//        DisassembleWord(buf,buf_size,di,"",(M6502Word) { .b.l=b,.b.h=c }.w,",y");
//        return 3;
//
//    case M6502AddrMode_IND:
//        DisassembleWord(buf,buf_size,di,"(",(M6502Word) { .b.l=b,.b.h=c }.w,")");
//        return 3;
//
//    case M6502AddrMode_ACC:
//        snprintf(buf,buf_size,"%s A",di->mnemonic);
//        return 1;
//
//    case M6502AddrMode_INZ:
//        DisassembleByte(buf,buf_size,di,"(",b,")");
//        return 1;
//
//    case M6502AddrMode_INDX:
//        DisassembleWord(buf,buf_size,di,"(",(M6502Word) { .b.l=b,.b.h=c }.w,",x)");
//        return 2;
//    }
//
//}

void M6502_DisassembleLastInstruction(M6502 *s, char *buf, size_t buf_size, int *ia, int *ad) {
    const M6502DisassemblyInfo *di = &s->config->disassembly_info[s->opcode];
    int ia_tmp, ad_tmp;

    if (!ia) {
        ia = &ia_tmp;
    }

    if (!ad) {
        ad = &ad_tmp;
    }

    *ia = -1;
    *ad = -1;

    switch (di->mode) {
    default:
        assert(0);
        /* fall through */
    case M6502AddrMode_IMP:
        snprintf(buf, buf_size, "%s", di->mnemonic);
        break;

    case M6502AddrMode_IMM:
        DisassembleByte(buf, buf_size, di, "#", s->data, "");
        break;

    case M6502AddrMode_REL:
        {
            uint16_t tmp;

            /* T1_Branch could update s->ad when the branch isn't
             * taken, but (somewhat surprisingly...) in my quick test
             * the cost of this seemed to be actually measurable. */

            if (!s->data) {
                /* pc-1, because the CPU is after phase 1 of T0, so
                 * it's already done pc++. */
                tmp = (uint16_t)(s->pc.w - 1u + (uint16_t)(int16_t)(int8_t)s->ad.b.l);
            } else {
                tmp = s->ad.w;
            }

            DisassembleWord(buf, buf_size, di, "", tmp, "");
        }
        break;

    case M6502AddrMode_ZPG:
        DisassembleByte(buf, buf_size, di, "", s->ad.b.l, "");
        break;

    case M6502AddrMode_ZPX:
        DisassembleByte(buf, buf_size, di, "", s->ad.b.l, ",x");
        *ad = (uint8_t)(s->ad.b.l + s->x);
        break;

    case M6502AddrMode_ZPY:
        DisassembleByte(buf, buf_size, di, "", s->ad.b.l, ",y");
        *ad = (uint8_t)(s->ad.b.l + s->y);
        break;

    case M6502AddrMode_INX:
        DisassembleByte(buf, buf_size, di, "(", s->ia.b.l, ",x)");
        *ia = (uint8_t)(s->ia.b.l + s->x);
        *ad = s->ad.w;
        break;

    case M6502AddrMode_INY:
        DisassembleByte(buf, buf_size, di, "(", s->ia.b.l, "),y");
        *ad = (uint16_t)(s->ad.w + s->y);
        break;

    case M6502AddrMode_ABS:
        DisassembleWord(buf, buf_size, di, "", s->ad.w, "");
        break;

    case M6502AddrMode_ABX:
        DisassembleWord(buf, buf_size, di, "", s->ad.w, ",x");
        *ad = (uint16_t)(s->ad.w + s->x);
        break;

    case M6502AddrMode_ABY:
        DisassembleWord(buf, buf_size, di, "", s->ad.w, ",y");
        *ad = (uint16_t)(s->ad.w + s->y);
        break;

    case M6502AddrMode_IND:
        DisassembleWord(buf, buf_size, di, "(", s->ia.w, ")");
        *ad = s->ad.w;
        break;

    case M6502AddrMode_ACC:
        snprintf(buf, buf_size, "%s A", di->mnemonic);
        break;

    case M6502AddrMode_INZ:
        DisassembleByte(buf, buf_size, di, "(", s->ia.b.l, ")");
        *ad = s->ad.b.l;
        break;

    case M6502AddrMode_INDX:
        DisassembleWord(buf, buf_size, di, "(", s->ad.w, ",x)");
        *ad = s->ad.w + s->x;
        break;
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

uint8_t M6502_GetOpcode(const M6502 *s) {
    if (s->read == M6502ReadType_Opcode) {
        return s->dbus;
    } else {
        return s->opcode;
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

// When a valid IRQ/NMI signal is received, record the status so it
// can be picked up by CheckForInterrupts.
//
// Don't reset the flag when the signal goes away - the 6502 seems to
// store it off every cycle, so provided `irq' or `nmi' is 0 for 1+
// cycles it'll get spotted.
//
// On a real 6502 it's possible to send a blip that's too short to be
// detected - the simulator doesn't support this.

void M6502_SetDeviceIRQ(M6502 *s, M6502_DeviceIRQFlags mask, int wants_irq) {
    assert(mask != 0);

    if (wants_irq) {
        s->device_irq_flags |= mask;
        s->irq_flags = s->device_irq_flags;
    } else {
        s->device_irq_flags &= ~mask;
        s->irq_flags = s->device_irq_flags;

        //        // If there are now no IRQs, but there were IRQs since the
        //        // last check, ignore those IRQs if the CPU is in a state
        //        // where the real 6502 would have ignored them as they came
        //        // in.
        //        if(s->device_irq_flags==0&&s->p.bits.i) {
        //            s->irq_flags=0;
        //        }
    }
}

void M6502_SetDeviceNMI(M6502 *s, M6502_DeviceIRQFlags mask, int wants_nmi) {
    assert(mask != 0);

    if (wants_nmi) {
        s->nmi_flags |= ~s->device_nmi_flags & mask;
        s->device_nmi_flags |= mask;
    } else {
        s->device_nmi_flags &= ~mask;
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

#define CASE(X)     \
    if (fn == &(X)) \
    return #X

static const NamedFn g_known_fns[] = {
    {"M6502_NextInstruction", &M6502_NextInstruction},
    {NULL, NULL},
};

static const NamedFn *const g_named_fn_lists[] = {
    g_known_fns,
    g_named_tfns,
    g_named_ifns,
    NULL,
};

static const char *FindFnName(M6502Fn fn) {
    for (size_t i = 0; g_named_fn_lists[i]; ++i) {
        const char *name = FindNameByFn(g_named_fn_lists[i], fn);
        if (name) {
            return name;
        }
    }

    return "?";
}

#undef CASE

static char g_fn_name_buf[200];

const char *M6502_GetStateName(M6502 *s, uint8_t is_dbus_valid) {
    const char *tfn_name = FindFnName(s->tfn);
    if (!tfn_name) {
        tfn_name = "?";
    }

    if (s->tfn == &Cycle0_All) {
        const char *t0fn_name = NULL;
        if (is_dbus_valid) {
            t0fn_name = FindFnName(s->fns[s->dbus].t0fn);
        }

        if (!t0fn_name) {
            t0fn_name = "?";
        }

        snprintf(g_fn_name_buf, sizeof g_fn_name_buf,
                 "tfn=%s t0fn=%s", tfn_name, t0fn_name);
    } else {
        const char *ifn_name = FindNameByFn(g_named_ifns, s->ifn);
        if (!ifn_name) {
            ifn_name = "?";
        }

        snprintf(g_fn_name_buf, sizeof g_fn_name_buf,
                 "tfn=%s ifn=%s", tfn_name, ifn_name);
    }

    return g_fn_name_buf;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_ForEachFn(M6502_ForEachFnFn fn, void *context) {
    for (size_t i = 0; g_named_fn_lists[i]; ++i) {
        const NamedFn *named_fn = g_named_fn_lists[i];
        while (named_fn->name) {
            (*fn)(named_fn->name, named_fn->fn, context);
            ++named_fn;
        }
    }
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void M6502_SetP(M6502 *s, uint8_t p) {
    s->p.value = p;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

M6502P M6502_GetP(const M6502 *s) {
    M6502P p = GetP(s);

    // Stupid perfect6502 thing.
    p.bits._ = 0;

    return p;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
