/*
 * 6510dtvcore.c - Cycle based 6510 emulation core.
 *
 * Written by
 *  Ettore Perazzoli <ettore@comm2000.it>
 *  Andreas Boose <viceteam@t-online.de>
 *
 * DTV sections written by
 *  M.Kiesel <mayne@users.sourceforge.net>
 *  Hannu Nuotio <hannu.nuotio@tut.fi>
 *
 * Cycle based rewrite by
 *  Hannu Nuotio <hannu.nuotio@tut.fi>
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

/* This file is included by (some) CPU definition files */
/* (maindtvcpu.c) */

#define CPU_STR "Main CPU"

#include "traps.h"

#ifndef C64DTV
/* The C64DTV can use different shadow registers for accu read/write. */
/* For standard 6510, this is not the case. */
#define reg_a_write reg_a
#define reg_a_read  reg_a
#endif

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

#define LOCAL_SET_NZ(val)        (flag_z = flag_n = (val))

#define LOCAL_SET_OVERFLOW(val)   \
    do {                          \
        if (val)                  \
            reg_p |= P_OVERFLOW;  \
        else                      \
            reg_p &= ~P_OVERFLOW; \
    } while (0)

#define LOCAL_SET_BREAK(val)   \
    do {                       \
        if (val)               \
            reg_p |= P_BREAK;  \
        else                   \
            reg_p &= ~P_BREAK; \
    } while (0)

#define LOCAL_SET_DECIMAL(val)   \
    do {                         \
        if (val)                 \
            reg_p |= P_DECIMAL;  \
        else                     \
            reg_p &= ~P_DECIMAL; \
    } while (0)

#define LOCAL_SET_INTERRUPT(val)   \
    do {                           \
        if (val)                   \
            reg_p |= P_INTERRUPT;  \
        else                       \
            reg_p &= ~P_INTERRUPT; \
    } while (0)

#define LOCAL_SET_CARRY(val)   \
    do {                       \
        if (val)               \
            reg_p |= P_CARRY;  \
        else                   \
            reg_p &= ~P_CARRY; \
    } while (0)

#define LOCAL_SET_SIGN(val)      (flag_n = (val) ? 0x80 : 0)
#define LOCAL_SET_ZERO(val)      (flag_z = !(val))
#define LOCAL_SET_STATUS(val)    (reg_p = ((val) & ~(P_ZERO | P_SIGN)), \
                                  LOCAL_SET_ZERO((val) & P_ZERO),       \
                                  flag_n = (val))

#define LOCAL_OVERFLOW()         (reg_p & P_OVERFLOW)
#define LOCAL_BREAK()            (reg_p & P_BREAK)
#define LOCAL_DECIMAL()          (reg_p & P_DECIMAL)
#define LOCAL_INTERRUPT()        (reg_p & P_INTERRUPT)
#define LOCAL_CARRY()            (reg_p & P_CARRY)
#define LOCAL_SIGN()             (flag_n & 0x80)
#define LOCAL_ZERO()             (!flag_z)
#define LOCAL_STATUS()           (reg_p | (flag_n & 0x80) | P_UNUSED    \
                                  | (LOCAL_ZERO() ? P_ZERO : 0))

#ifdef LAST_OPCODE_INFO

/* If requested, gather some info about the last executed opcode for timing
   purposes.  */

/* Remember the number of the last opcode.  By default, the opcode does not
   delay interrupt and does not change the I flag.  */
#define SET_LAST_OPCODE(x) \
    OPINFO_SET(LAST_OPCODE_INFO, (x), 0, 0, 0)

/* Remember that the last opcode delayed a pending IRQ or NMI by one cycle.  */
#define OPCODE_DELAYS_INTERRUPT() \
    OPINFO_SET_DELAYS_INTERRUPT(LAST_OPCODE_INFO, 1)

/* Remember that the last opcode changed the I flag from 0 to 1, so we have
   to dispatch an IRQ even if the I flag is 0 when we check it.  */
#define OPCODE_DISABLES_IRQ() \
    OPINFO_SET_DISABLES_IRQ(LAST_OPCODE_INFO, 1)

/* Remember that the last opcode changed the I flag from 1 to 0, so we must
   not dispatch an IRQ even if the I flag is 1 when we check it.  */
#define OPCODE_ENABLES_IRQ() \
    OPINFO_SET_ENABLES_IRQ(LAST_OPCODE_INFO, 1)

#else

/* Info about the last opcode is not needed.  */
#define SET_LAST_OPCODE(x)
#define OPCODE_DELAYS_INTERRUPT()
#define OPCODE_DISABLES_IRQ()
#define OPCODE_ENABLES_IRQ()

#endif

#ifndef C64DTV
/* Export the local version of the registers.  */
#define EXPORT_REGISTERS()      \
  do {                          \
      GLOBAL_REGS.pc = reg_pc;  \
      GLOBAL_REGS.a = reg_a_read;    \
      GLOBAL_REGS.x = reg_x;    \
      GLOBAL_REGS.y = reg_y;    \
      GLOBAL_REGS.sp = reg_sp;  \
      GLOBAL_REGS.p = reg_p;    \
      GLOBAL_REGS.n = flag_n;   \
      GLOBAL_REGS.z = flag_z;   \
  } while (0)

/* Import the public version of the registers.  */
#define IMPORT_REGISTERS()      \
  do {                          \
      reg_a_write = GLOBAL_REGS.a; \
      reg_x = GLOBAL_REGS.x;    \
      reg_y = GLOBAL_REGS.y;    \
      reg_sp = GLOBAL_REGS.sp;  \
      reg_p = GLOBAL_REGS.p;    \
      flag_n = GLOBAL_REGS.n;   \
      flag_z = GLOBAL_REGS.z;   \
      JUMP(GLOBAL_REGS.pc);     \
  } while (0)

#else  /* C64DTV */

/* Export the local version of the registers.  */
#define EXPORT_REGISTERS()      \
  do {                          \
      GLOBAL_REGS.pc = reg_pc;  \
      GLOBAL_REGS.a = dtv_registers[0]; \
      GLOBAL_REGS.x = dtv_registers[2]; \
      GLOBAL_REGS.y = dtv_registers[1]; \
      GLOBAL_REGS.sp = reg_sp;  \
      GLOBAL_REGS.p = reg_p;    \
      GLOBAL_REGS.n = flag_n;   \
      GLOBAL_REGS.z = flag_z;   \
      GLOBAL_REGS.r3 = dtv_registers[3]; \
      GLOBAL_REGS.r4 = dtv_registers[4]; \
      GLOBAL_REGS.r5 = dtv_registers[5]; \
      GLOBAL_REGS.r6 = dtv_registers[6]; \
      GLOBAL_REGS.r7 = dtv_registers[7]; \
      GLOBAL_REGS.r8 = dtv_registers[8]; \
      GLOBAL_REGS.r9 = dtv_registers[9]; \
      GLOBAL_REGS.r10 = dtv_registers[10]; \
      GLOBAL_REGS.r11 = dtv_registers[11]; \
      GLOBAL_REGS.r12 = dtv_registers[12]; \
      GLOBAL_REGS.r13 = dtv_registers[13]; \
      GLOBAL_REGS.r14 = dtv_registers[14]; \
      GLOBAL_REGS.r15 = dtv_registers[15]; \
      GLOBAL_REGS.acm = (reg_a_write_idx << 4) | (reg_a_read_idx); \
      GLOBAL_REGS.yxm = (reg_y_idx << 4) | (reg_x_idx); \
  } while (0)

/* Import the public version of the registers.  */
#define IMPORT_REGISTERS()      \
  do {                          \
      dtv_registers[0] = GLOBAL_REGS.a; \
      dtv_registers[2] = GLOBAL_REGS.x; \
      dtv_registers[1] = GLOBAL_REGS.y; \
      reg_sp = GLOBAL_REGS.sp;  \
      reg_p = GLOBAL_REGS.p;    \
      flag_n = GLOBAL_REGS.n;   \
      flag_z = GLOBAL_REGS.z;   \
      dtv_registers[3] = GLOBAL_REGS.r3; \
      dtv_registers[4] = GLOBAL_REGS.r4; \
      dtv_registers[5] = GLOBAL_REGS.r5; \
      dtv_registers[6] = GLOBAL_REGS.r6; \
      dtv_registers[7] = GLOBAL_REGS.r7; \
      dtv_registers[8] = GLOBAL_REGS.r8; \
      dtv_registers[9] = GLOBAL_REGS.r9; \
      dtv_registers[10] = GLOBAL_REGS.r10; \
      dtv_registers[11] = GLOBAL_REGS.r11; \
      dtv_registers[12] = GLOBAL_REGS.r12; \
      dtv_registers[13] = GLOBAL_REGS.r13; \
      dtv_registers[14] = GLOBAL_REGS.r14; \
      dtv_registers[15] = GLOBAL_REGS.r15; \
      reg_a_write_idx = GLOBAL_REGS.acm >> 4; \
      reg_a_read_idx = GLOBAL_REGS.acm & 0xf; \
      reg_y_idx = GLOBAL_REGS.yxm >> 4; \
      reg_x_idx = GLOBAL_REGS.yxm & 0xf; \
      JUMP(GLOBAL_REGS.pc); \
  } while (0)

#endif /* C64DTV */

#ifdef DEBUG
#define TRACE_NMI() \
    do { if (TRACEFLG) debug_nmi(CPU_INT_STATUS, CLK); } while (0)
#define TRACE_IRQ() \
    do { if (TRACEFLG) debug_irq(CPU_INT_STATUS, CLK); } while (0)
#define TRACE_BRK() do { if (TRACEFLG) debug_text("*** BRK"); } while (0)
#else
#define TRACE_NMI()
#define TRACE_IRQ()
#define TRACE_BRK()
#endif

/* Perform the interrupts in `int_kind'.  If we have both NMI and IRQ,
   execute NMI.  */
/* FIXME: Dummy LOAD() cycles are missing!  */
/* FIXME: Improper BRK handling!  */
/* FIXME: LOCAL_STATUS() should check byte ready first.  */
#define DO_INTERRUPT(int_kind)                                        \
    do {                                                              \
        BYTE ik = (int_kind);                                         \
        WORD addr;                                                    \
                                                                      \
        if (ik & (IK_IRQ | IK_IRQPEND | IK_NMI)) {                    \
            if ((ik & IK_NMI)                                         \
                && interrupt_check_nmi_delay(CPU_INT_STATUS, CLK)) {  \
                TRACE_NMI();                                          \
                if (monitor_mask[CALLER] & (MI_STEP)) {               \
                    monitor_check_icount_interrupt();                 \
                }                                                     \
                interrupt_ack_nmi(CPU_INT_STATUS);                    \
                if (!SKIP_CYCLE) {                                    \
                    CLK_INC();                                        \
                    CLK_INC();                                        \
                }                                                     \
                LOCAL_SET_BREAK(0);                                   \
                PUSH(reg_pc >> 8);                                    \
                CLK_INC();                                            \
                PUSH(reg_pc & 0xff);                                  \
                CLK_INC();                                            \
                PUSH(LOCAL_STATUS());                                 \
                CLK_INC();                                            \
                addr = LOAD(0xfffa);                                  \
                CLK_INC();                                            \
                addr |= (LOAD(0xfffb) << 8);                          \
                CLK_INC();                                            \
                LOCAL_SET_INTERRUPT(1);                               \
                JUMP(addr);                                           \
                SET_LAST_OPCODE(0);                                   \
            } else if ((ik & (IK_IRQ | IK_IRQPEND))                   \
                       && (!LOCAL_INTERRUPT()                         \
                       || OPINFO_DISABLES_IRQ(LAST_OPCODE_INFO))      \
                       && interrupt_check_irq_delay(CPU_INT_STATUS,   \
                                                    CLK)) {           \
                TRACE_IRQ();                                          \
                if (monitor_mask[CALLER] & (MI_STEP)) {               \
                    monitor_check_icount_interrupt();                 \
                }                                                     \
                interrupt_ack_irq(CPU_INT_STATUS);                    \
                if (!SKIP_CYCLE) {                                    \
                    CLK_INC();                                        \
                    CLK_INC();                                        \
                }                                                     \
                LOCAL_SET_BREAK(0);                                   \
                PUSH(reg_pc >> 8);                                    \
                CLK_INC();                                            \
                PUSH(reg_pc & 0xff);                                  \
                CLK_INC();                                            \
                PUSH(LOCAL_STATUS());                                 \
                CLK_INC();                                            \
                addr = LOAD(0xfffe);                                  \
                CLK_INC();                                            \
                addr |= (LOAD(0xffff) << 8);                          \
                CLK_INC();                                            \
                LOCAL_SET_INTERRUPT(1);                               \
                JUMP(addr);                                           \
                SET_LAST_OPCODE(0);                                   \
            }                                                         \
        }                                                             \
        if (ik & (IK_TRAP | IK_RESET)) {                              \
            if (ik & IK_TRAP) {                                       \
                EXPORT_REGISTERS();                                   \
                interrupt_do_trap(CPU_INT_STATUS, (WORD)reg_pc);      \
                IMPORT_REGISTERS();                                   \
                if (CPU_INT_STATUS->global_pending_int & IK_RESET)    \
                    ik |= IK_RESET;                                   \
            }                                                         \
            if (ik & IK_RESET) {                                      \
                interrupt_ack_reset(CPU_INT_STATUS);                  \
                cpu_reset();                                          \
                addr = LOAD(0xfffc);                                  \
                addr |= (LOAD(0xfffd) << 8);                          \
                JUMP(addr);                                           \
                DMA_ON_RESET;                                         \
            }                                                         \
        }                                                             \
        if (ik & (IK_MONITOR | IK_DMA)) {                             \
            if (ik & IK_MONITOR) {                                    \
                caller_space = CALLER;                                \
                if (monitor_force_import(CALLER))                     \
                    IMPORT_REGISTERS();                               \
                if (monitor_mask[CALLER])                             \
                    EXPORT_REGISTERS();                               \
                if (monitor_mask[CALLER] & (MI_BREAK)) {              \
                    if (monitor_check_breakpoints(CALLER,             \
                        (WORD)reg_pc)) {                              \
                        monitor_startup();                            \
                        IMPORT_REGISTERS();                           \
                    }                                                 \
                }                                                     \
                if (monitor_mask[CALLER] & (MI_STEP)) {               \
                    monitor_check_icount((WORD)reg_pc);               \
                    IMPORT_REGISTERS();                               \
                }                                                     \
                if (monitor_mask[CALLER] & (MI_WATCH)) {              \
                    monitor_check_watchpoints((WORD)reg_pc);          \
                    IMPORT_REGISTERS();                               \
                }                                                     \
            }                                                         \
            if (ik & IK_DMA) {                                        \
                EXPORT_REGISTERS();                                   \
                DMA_FUNC;                                             \
                interrupt_ack_dma(CPU_INT_STATUS);                    \
                IMPORT_REGISTERS();                                   \
                JUMP(reg_pc);                                         \
            }                                                         \
        }                                                             \
    } while (0)

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

/* Addressing modes.  For convenience, page boundary crossing cycles and
   ``idle'' memory reads are handled here as well. */

#define GET_TEMP(dest) \
    dest = new_value;

#define GET_IMM(dest) \
    dest = (BYTE)(p1);

#define GET_ABS(dest)        \
    dest = (BYTE)(LOAD(p2)); \
    CLK_INC();

#define SET_ABS(value) \
    STORE(p2, value);  \
    CLK_INC();

#define SET_ABS_RMW(old_value, new_value) \
    if (!SKIP_CYCLE) {                    \
        STORE(p2, old_value);             \
        CLK_INC();                        \
    }                                     \
    STORE(p2, new_value);                 \
    CLK_INC();

#define INT_ABS_I_R(reg_i)                                 \
    if (!SKIP_CYCLE && ((((p2) & 0xff) + reg_i) > 0xff)) { \
        LOAD((((p2) + reg_i) & 0xff) | ((p2) & 0xff00));   \
        CLK_INC();                                         \
    }

#define INT_ABS_I_W(reg_i)                                 \
    if (!SKIP_CYCLE) {                                     \
        LOAD((((p2) + reg_i) & 0xff) | ((p2) & 0xff00));   \
        CLK_INC();                                         \
    }

#define GET_ABS_X(dest)        \
    INT_ABS_I_R(reg_x)         \
    dest = LOAD((p2) + reg_x); \
    CLK_INC();

#define GET_ABS_Y(dest)        \
    INT_ABS_I_R(reg_y)         \
    dest = LOAD((p2) + reg_y); \
    CLK_INC();

#define GET_ABS_X_RMW(dest)    \
    INT_ABS_I_W(reg_x)         \
    dest = LOAD((p2) + reg_x); \
    CLK_INC();

#define GET_ABS_Y_RMW(dest)    \
    INT_ABS_I_W(reg_y)         \
    dest = LOAD((p2) + reg_y); \
    CLK_INC();

#define SET_ABS_X(value)       \
    INT_ABS_I_W(reg_x)         \
    STORE(p2 + reg_x, value);  \
    CLK_INC();

#define SET_ABS_Y(value)       \
    INT_ABS_I_W(reg_y)         \
    STORE(p2 + reg_y, value);  \
    CLK_INC();

#define SET_ABS_I_RMW(reg_i, old_value, new_value) \
    if (!SKIP_CYCLE) {                             \
        STORE(p2 + reg_i, old_value);              \
        CLK_INC();                                 \
    }                                              \
    STORE(p2 + reg_i, new_value);                  \
    CLK_INC();

#define SET_ABS_X_RMW(old_value, new_value)    \
    SET_ABS_I_RMW(reg_x, old_value, new_value)

#define SET_ABS_Y_RMW(old_value, new_value)    \
    SET_ABS_I_RMW(reg_y, old_value, new_value)

#define GET_ZERO(dest)    \
    dest = LOAD_ZERO(p1); \
    CLK_INC();

#define SET_ZERO(value)    \
    STORE_ZERO(p1, value); \
    CLK_INC();

#define SET_ZERO_RMW(old_value, new_value) \
    if (!SKIP_CYCLE) {                     \
        STORE_ZERO(p1, old_value);         \
        CLK_INC();                         \
    }                                      \
    STORE_ZERO(p1, new_value);             \
    CLK_INC();

#define INT_ZERO_I      \
    if (!SKIP_CYCLE) {  \
        LOAD_ZERO(p1);  \
        CLK_INC();      \
    }

#define GET_ZERO_X(dest)          \
    INT_ZERO_I                    \
    dest = LOAD_ZERO(p1 + reg_x); \
    CLK_INC();

#define GET_ZERO_Y(dest)          \
    INT_ZERO_I                    \
    dest = LOAD_ZERO(p1 + reg_y); \
    CLK_INC();

#define SET_ZERO_X(value)          \
    INT_ZERO_I                     \
    STORE_ZERO(p1 + reg_x, value); \
    CLK_INC();

#define SET_ZERO_Y(value)          \
    INT_ZERO_I                     \
    STORE_ZERO(p1 + reg_y, value); \
    CLK_INC();

#define SET_ZERO_I_RMW(reg_i, old_value, new_value) \
    if (!SKIP_CYCLE) {                              \
        STORE_ZERO(p1 + reg_i, old_value);          \
        CLK_INC();                                  \
    }                                               \
    STORE_ZERO(p1 + reg_i, new_value);              \
    CLK_INC();

#define SET_ZERO_X_RMW(old_value, new_value)    \
    SET_ZERO_I_RMW(reg_x, old_value, new_value)

#define SET_ZERO_Y_RMW(old_value, new_value)    \
    SET_ZERO_I_RMW(reg_y, old_value, new_value)

#define INT_IND_X                      \
    unsigned int tmpa, addr;           \
    LOAD_ZERO(p1);                     \
    CLK_INC();                         \
    tmpa = (p1 + reg_x) & 0xff;        \
    addr = LOAD_ZERO(tmpa);            \
    CLK_INC();                         \
    tmpa = (tmpa + 1) & 0xff;          \
    addr |= (LOAD_ZERO(tmpa) << 8);    \
    CLK_INC();

#define GET_IND_X(dest) \
    INT_IND_X           \
    dest = LOAD(addr);  \
    CLK_INC();

#define SET_IND_X(value) \
    INT_IND_X            \
    STORE(addr, value);  \
    CLK_INC();

#define INT_IND_Y_R()                                        \
    unsigned int tmpa, addr;                                 \
    tmpa = LOAD_ZERO(p1);                                    \
    CLK_INC();                                               \
    tmpa |= (LOAD_ZERO(p1 + 1) << 8);                        \
    CLK_INC();                                               \
    if (!SKIP_CYCLE && ((((tmpa) & 0xff) + reg_y) > 0xff)) { \
        LOAD((tmpa & 0xff00) | ((tmpa + reg_y) & 0xff));     \
        CLK_INC();                                           \
    }                                                        \
    addr = (tmpa + reg_y) & 0xffff;                          \

#define INT_IND_Y_W()                                    \
    unsigned int tmpa, addr;                             \
    tmpa = LOAD_ZERO(p1);                                \
    CLK_INC();                                           \
    tmpa |= (LOAD_ZERO(p1 + 1) << 8);                    \
    CLK_INC();                                           \
    if (!SKIP_CYCLE) {                                   \
        LOAD((tmpa & 0xff00) | ((tmpa + reg_y) & 0xff)); \
        CLK_INC();                                       \
    }                                                    \
    addr = (tmpa + reg_y) & 0xffff;

#define GET_IND_Y(dest) \
    INT_IND_Y_R()       \
    dest = LOAD(addr);  \
    CLK_INC();

#define GET_IND_Y_RMW(dest) \
    INT_IND_Y_W()           \
    dest = LOAD(addr);      \
    CLK_INC();

#define SET_IND_Y(value) \
    INT_IND_Y_W()        \
    STORE(addr, value);  \
    CLK_INC();

#define SET_IND_RMW(old_value, new_value) \
    if (!SKIP_CYCLE) {                    \
        STORE(addr, old_value);           \
        CLK_INC();                        \
    }                                     \
    STORE(addr, new_value);               \
    CLK_INC();

#define SET_ABS_SH_I(reg_and, reg_i)                     \
  do {                                                   \
      unsigned int tmp2, value;                          \
                                                         \
      value = reg_and & (((p2 + reg_i) >> 8) + 1);       \
      if (!SKIP_CYCLE) {                                 \
          LOAD(((p2 + reg_i) & 0xff) | ((p2) & 0xff00)); \
          CLK_INC();                                     \
      }                                                  \
      tmp2 = p2 + reg_i;                                 \
      if (((p2) & 0xff) + reg_i > 0xff)                  \
          tmp2 = (tmp2 & 0xff) | ((value) << 8);         \
      STORE(tmp2, (value));                              \
      CLK_INC();                                         \
  } while (0)

#define INC_PC(value)   (reg_pc += (value))

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

/* Opcodes.  */

/*
   A couple of caveats about PC:

   - the VIC-II emulation requires PC to be incremented before the first
     write access (this is not (very) important when writing to the zero
     page);

   - `p0', `p1' and `p2' can only be used *before* incrementing PC: some
     machines (eg. the C128) might depend on this.
*/

#define ADC(get_func, pc_inc)                                                      \
  do {                                                                             \
      unsigned int tmp_value;                                                      \
      unsigned int tmp;                                                            \
                                                                                   \
      get_func(tmp_value);                                                         \
                                                                                   \
      if (LOCAL_DECIMAL()) {                                                       \
          tmp = (reg_a_read & 0xf) + (tmp_value & 0xf) + (reg_p & 0x1);            \
          if (tmp > 0x9)                                                           \
              tmp += 0x6;                                                          \
          if (tmp <= 0x0f)                                                         \
              tmp = (tmp & 0xf) + (reg_a_read & 0xf0) + (tmp_value & 0xf0);        \
          else                                                                     \
              tmp = (tmp & 0xf) + (reg_a_read & 0xf0) + (tmp_value & 0xf0) + 0x10; \
          LOCAL_SET_ZERO(!((reg_a_read + tmp_value + (reg_p & 0x1)) & 0xff));      \
          LOCAL_SET_SIGN(tmp & 0x80);                                              \
          LOCAL_SET_OVERFLOW(((reg_a_read ^ tmp) & 0x80)                           \
                              && !((reg_a_read ^ tmp_value) & 0x80));              \
          if ((tmp & 0x1f0) > 0x90)                                                \
              tmp += 0x60;                                                         \
          LOCAL_SET_CARRY((tmp & 0xff0) > 0xf0);                                   \
      } else {                                                                     \
          tmp = tmp_value + reg_a_read + (reg_p & P_CARRY);                        \
          LOCAL_SET_NZ(tmp & 0xff);                                                \
          LOCAL_SET_OVERFLOW(!((reg_a_read ^ tmp_value) & 0x80)                    \
                              && ((reg_a_read ^ tmp) & 0x80));                     \
          LOCAL_SET_CARRY(tmp > 0xff);                                             \
      }                                                                            \
      reg_a_write = tmp;                                                           \
      INC_PC(pc_inc);                                                              \
  } while (0)

#define ANC()                                     \
  do {                                            \
      reg_a_write = (BYTE)(reg_a_read & p1);      \
      LOCAL_SET_NZ(reg_a_read);                   \
      LOCAL_SET_CARRY(LOCAL_SIGN());              \
      INC_PC(2);                                  \
  } while (0)

#define AND(get_func, pc_inc)                   \
  do {                                          \
      unsigned int value;                       \
      get_func(value)                           \
      reg_a_write = (BYTE)(reg_a_read & value); \
      LOCAL_SET_NZ(reg_a_read);                 \
      INC_PC(pc_inc);                           \
  } while (0)

#define ANE()                                                 \
  do {                                                        \
      reg_a_write = (BYTE)((reg_a_read | 0xee) & reg_x & p1); \
      LOCAL_SET_NZ(reg_a_read);                               \
      INC_PC(2);                                              \
  } while (0)

/* The fanciest opcode ever... ARR! */
#define ARR()                                                      \
  do {                                                             \
      unsigned int tmp;                                            \
                                                                   \
      tmp = reg_a_read & (p1);                                     \
      if (LOCAL_DECIMAL()) {                                       \
          int tmp_2 = tmp;                                         \
          tmp_2 |= (reg_p & P_CARRY) << 8;                         \
          tmp_2 >>= 1;                                             \
          LOCAL_SET_SIGN(LOCAL_CARRY());                           \
          LOCAL_SET_ZERO(!tmp_2);                                  \
          LOCAL_SET_OVERFLOW((tmp_2 ^ tmp) & 0x40);                \
          if (((tmp & 0xf) + (tmp & 0x1)) > 0x5)                   \
              tmp_2 = (tmp_2 & 0xf0) | ((tmp_2 + 0x6) & 0xf);      \
          if (((tmp & 0xf0) + (tmp & 0x10)) > 0x50) {              \
              tmp_2 = (tmp_2 & 0x0f) | ((tmp_2 + 0x60) & 0xf0);    \
              LOCAL_SET_CARRY(1);                                  \
          } else                                                   \
              LOCAL_SET_CARRY(0);                                  \
          reg_a_write = tmp_2;                                     \
      } else {                                                     \
          tmp |= (reg_p & P_CARRY) << 8;                           \
          tmp >>= 1;                                               \
          LOCAL_SET_NZ(tmp);                                       \
          LOCAL_SET_CARRY(tmp & 0x40);                             \
          LOCAL_SET_OVERFLOW((tmp & 0x40) ^ ((tmp & 0x20) << 1));  \
          reg_a_write = tmp;                                       \
      }                                                            \
      INC_PC(2);                                                   \
  } while (0)

#define ASL(pc_inc, get_func, set_func)                    \
  do {                                                     \
      unsigned int old_value, new_value;                   \
      get_func(old_value)                                  \
      LOCAL_SET_CARRY(old_value & 0x80);                   \
      new_value = (old_value << 1) & 0xff;                 \
      LOCAL_SET_NZ(new_value);                             \
      INC_PC(pc_inc);                                      \
      set_func(old_value, new_value);                      \
  } while (0)

#define ASL_A()                           \
  do {                                    \
      LOCAL_SET_CARRY(reg_a_read & 0x80); \
      reg_a_write = reg_a_read << 1;      \
      LOCAL_SET_NZ(reg_a_read);           \
      INC_PC(1);                          \
  } while (0)

#define ASR()                      \
  do {                             \
      unsigned int tmp;            \
                                   \
      tmp = reg_a_read & (p1);     \
      LOCAL_SET_CARRY(tmp & 0x01); \
      reg_a_write = tmp >> 1;      \
      LOCAL_SET_NZ(reg_a_read);    \
      INC_PC(2);                   \
  } while (0)

#define BIT(get_func, pc_inc)              \
  do {                                     \
      unsigned int tmp;                    \
      get_func(tmp)                        \
      LOCAL_SET_SIGN(tmp & 0x80);          \
      LOCAL_SET_OVERFLOW(tmp & 0x40);      \
      LOCAL_SET_ZERO(!(tmp & reg_a_read)); \
      INC_PC(pc_inc);                      \
  } while (0)

#ifdef C64DTV

#define BRANCH(cond)                                            \
  do {                                                          \
      INC_PC(2);                                                \
                                                                \
      if (cond) {                                               \
          unsigned int dest_addr;                               \
                                                                \
          burst_broken = 1;                                     \
          dest_addr = reg_pc + (signed char)(p1);               \
                                                                \
          if (!SKIP_CYCLE) {                                    \
              LOAD(reg_pc);                                     \
              CLK_INC();                                        \
              if ((reg_pc ^ dest_addr) & 0xff00) {              \
                  LOAD((reg_pc & 0xff00) | (dest_addr & 0xff)); \
                  CLK_INC();                                    \
              } else {                                          \
                  OPCODE_DELAYS_INTERRUPT();                    \
              }                                                 \
          }                                                     \
          JUMP(dest_addr & 0xffff);                             \
      }                                                         \
  } while (0)

#else /* !C64DTV */

#define BRANCH(cond)                                            \
  do {                                                          \
      INC_PC(2);                                                \
                                                                \
      if (cond) {                                               \
          unsigned int dest_addr;                               \
                                                                \
          dest_addr = reg_pc + (signed char)(p1);               \
                                                                \
          LOAD(reg_pc);                                         \
          CLK_INC();                                            \
          if ((reg_pc ^ dest_addr) & 0xff00) {                  \
              LOAD((reg_pc & 0xff00) | (dest_addr & 0xff));     \
              CLK_INC();                                        \
          } else {                                              \
              OPCODE_DELAYS_INTERRUPT();                        \
          }                                                     \
          JUMP(dest_addr & 0xffff);                             \
      }                                                         \
  } while (0)

#endif

/* The BRK opcode is also used to patch the ROM.  The function trap_handler()
   returns nonzero if this is not a patch, but a `real' BRK instruction. */

#define BRK()                                                    \
  do {                                                           \
      WORD addr;                                                 \
      EXPORT_REGISTERS();                                        \
      TRACE_BRK();                                               \
      INC_PC(2);                                                 \
      LOCAL_SET_BREAK(1);                                        \
      PUSH(reg_pc >> 8);                                         \
      CLK_INC();                                                 \
      PUSH(reg_pc & 0xff);                                       \
      CLK_INC();                                                 \
      PUSH(LOCAL_STATUS());                                      \
      CLK_INC();                                                 \
      LOCAL_SET_INTERRUPT(1);                                    \
      addr = LOAD(0xfffe);                                       \
      CLK_INC();                                                 \
      addr |= (LOAD(0xffff) << 8);                               \
      CLK_INC();                                                 \
      JUMP(addr);                                                \
  } while (0)

/* The JAM (0x02) opcode is also used to patch the ROM.  The function trap_handler()
   returns nonzero if this is not a patch, but a `real' JAM instruction. */

#define JAM_02()                                                 \
  do {                                                           \
      DWORD trap_result;                                         \
      EXPORT_REGISTERS();                                        \
      if (!ROM_TRAP_ALLOWED()                                    \
          || (trap_result = ROM_TRAP_HANDLER()) == (DWORD)-1) {  \
          REWIND_FETCH_OPCODE(CLK);                              \
          JAM();                                                 \
      } else {                                                   \
          if (trap_result) {                                     \
             REWIND_FETCH_OPCODE(CLK);                           \
             SET_OPCODE(trap_result);                            \
             IMPORT_REGISTERS();                                 \
             goto trap_skipped;                                  \
          } else {                                               \
             IMPORT_REGISTERS();                                 \
          }                                                      \
      }                                                          \
  } while (0)

#define CLC()              \
  do {                     \
      INC_PC(1);           \
      LOCAL_SET_CARRY(0);  \
  } while (0)

#define CLD()                \
  do {                       \
      INC_PC(1);             \
      LOCAL_SET_DECIMAL(0);  \
  } while (0)

#define CLI()                    \
  do {                           \
      INC_PC(1);                 \
      if (LOCAL_INTERRUPT())     \
          OPCODE_ENABLES_IRQ();  \
      LOCAL_SET_INTERRUPT(0);    \
  } while (0)

#define CLV()                 \
  do {                        \
      INC_PC(1);              \
      LOCAL_SET_OVERFLOW(0);  \
  } while (0)

#define CP(reg, get_func, pc_inc)    \
  do {                               \
      unsigned int tmp;              \
      BYTE value;                    \
      get_func(value)                \
      tmp = reg - value;             \
      LOCAL_SET_CARRY(tmp < 0x100);  \
      LOCAL_SET_NZ(tmp & 0xff);      \
      INC_PC(pc_inc);                \
  } while (0)

#define DCP(pc_inc, get_func, set_func)         \
  do {                                          \
      unsigned int old_value, new_value;        \
      get_func(old_value)                       \
      new_value = (old_value - 1) & 0xff;       \
      LOCAL_SET_CARRY(reg_a_read >= new_value); \
      LOCAL_SET_NZ((reg_a_read - new_value));   \
      INC_PC(pc_inc);                           \
      set_func(old_value, new_value)            \
  } while (0)

#define DEC(pc_inc, get_func, set_func)    \
  do {                                     \
      unsigned int old_value, new_value;   \
      get_func(old_value)                  \
      new_value = (old_value - 1) & 0xff;  \
      LOCAL_SET_NZ(new_value);             \
      INC_PC(pc_inc);                      \
      set_func(old_value, new_value)       \
  } while (0)

#define DEX()               \
  do {                      \
      reg_x--;              \
      LOCAL_SET_NZ(reg_x);  \
      INC_PC(1);            \
  } while (0)

#define DEY()               \
  do {                      \
      reg_y--;              \
      LOCAL_SET_NZ(reg_y);  \
      INC_PC(1);            \
  } while (0)

#define EOR(get_func, pc_inc)                     \
  do {                                            \
      unsigned int value;                         \
      get_func(value)                             \
      reg_a_write = (BYTE)(reg_a_read ^ (value)); \
      LOCAL_SET_NZ(reg_a_read);                   \
      INC_PC(pc_inc);                             \
  } while (0)

#define INC(pc_inc, get_func, set_func)   \
  do {                                    \
      unsigned int old_value, new_value;  \
      get_func(old_value)                 \
      new_value = (old_value + 1) & 0xff; \
      LOCAL_SET_NZ(new_value);            \
      INC_PC(pc_inc);                     \
      set_func(old_value, new_value)      \
  } while (0)

#define INX()               \
  do {                      \
      reg_x++;              \
      LOCAL_SET_NZ(reg_x);  \
      INC_PC(1);            \
  } while (0)

#define INY()               \
  do {                      \
      reg_y++;              \
      LOCAL_SET_NZ(reg_y);  \
      INC_PC(1);            \
  } while (0)

#define ISB(pc_inc, get_func, set_func)   \
  do {                                    \
      unsigned int old_value, new_value;  \
      get_func(old_value)                 \
      new_value = (old_value + 1) & 0xff; \
      SBC(GET_TEMP, 0);                   \
      INC_PC(pc_inc);                     \
      set_func(old_value, new_value)      \
  } while (0)

#define JMP(addr)     \
  do {                \
      JUMP(addr);     \
  } while (0)

#define JMP_IND()                                                   \
  do {                                                              \
      WORD dest_addr;                                               \
      dest_addr = LOAD(p2);                                         \
      CLK_INC();                                               \
      dest_addr |= (LOAD((p2 & 0xff00) | ((p2 + 1) & 0xff)) << 8);  \
      CLK_INC();                                               \
      JUMP(dest_addr);                                              \
  } while (0)

#define JSR()                                 \
  do {                                        \
      unsigned int tmp_addr;                  \
      if (!SKIP_CYCLE) {                      \
          STACK_PEEK();                       \
          CLK_INC();                     \
      }                                       \
      INC_PC(2);                              \
      PUSH(((reg_pc) >> 8) & 0xff);           \
      CLK_INC();                         \
      PUSH((reg_pc) & 0xff);                  \
      CLK_INC();                         \
      tmp_addr = (p1 | (LOAD(reg_pc) << 8));  \
      CLK_INC();                         \
      JUMP(tmp_addr);                         \
  } while (0)

#define LAS()                                     \
  do {                                            \
      unsigned int value;                         \
      GET_ABS_Y(value)                            \
      reg_a_write = reg_x = reg_sp = reg_sp & value;  \
      LOCAL_SET_NZ(reg_a_read);                   \
      INC_PC(3);                                  \
  } while (0)

#define LAX(get_func, pc_inc)        \
  do {                               \
      get_func(reg_x);               \
      reg_a_write = reg_x;           \
      LOCAL_SET_NZ(reg_a_read);      \
      INC_PC(pc_inc);                \
  } while (0)

#define LD(dest, get_func, pc_inc)  \
  do {                              \
      get_func(dest);               \
      LOCAL_SET_NZ(dest);           \
      INC_PC(pc_inc);               \
  } while (0)

#define LSR(pc_inc, get_func, set_func)   \
  do {                                    \
      unsigned int old_value, new_value;  \
      get_func(old_value)                 \
      LOCAL_SET_CARRY(old_value & 0x01);  \
      new_value = old_value >> 1;         \
      LOCAL_SET_NZ(new_value);            \
      INC_PC(pc_inc);                     \
      set_func(old_value, new_value)      \
  } while (0)

#define LSR_A()                       \
  do {                                \
      LOCAL_SET_CARRY(reg_a_read & 0x01); \
      reg_a_write = reg_a_read >> 1;  \
      LOCAL_SET_NZ(reg_a_read);       \
      INC_PC(1);                      \
  } while (0)

/* Note: this is not always exact, as this opcode can be quite unstable!
   Moreover, the behavior is different from the one described in 64doc. */
#define LXA(value, pc_inc)                                 \
  do {                                                     \
      reg_a_write = reg_x = ((reg_a_read | 0xee) & ((BYTE)(value)));  \
      LOCAL_SET_NZ(reg_a_read);                            \
      INC_PC(pc_inc);                                      \
  } while (0)

#define ORA(get_func, pc_inc)                     \
  do {                                            \
      unsigned int value;                         \
      get_func(value)                             \
      reg_a_write = (BYTE)(reg_a_read | (value)); \
      LOCAL_SET_NZ(reg_a_write);                  \
      INC_PC(pc_inc);                             \
  } while (0)

#define NOOP(get_func, pc_inc) \
  do {                         \
      unsigned int dummy;      \
      get_func(dummy)          \
      INC_PC(pc_inc);          \
  } while (0)

#define PHA()           \
  do {                  \
      PUSH(reg_a_read); \
      CLK_INC();   \
      INC_PC(1);        \
  } while (0)

#define PHP()                          \
  do {                                 \
      PUSH(LOCAL_STATUS() | P_BREAK);  \
      CLK_INC();                  \
      INC_PC(1);                       \
  } while (0)

#define PLA()                   \
  do {                          \
      if (!SKIP_CYCLE) {        \
          STACK_PEEK();         \
          CLK_INC();       \
      }                         \
      reg_a_write = PULL();     \
      CLK_INC();           \
      LOCAL_SET_NZ(reg_a_read); \
      INC_PC(1);                \
  } while (0)

#define PLP()                                            \
  do {                                                   \
      BYTE s;                                            \
      if (!SKIP_CYCLE) {                                 \
          STACK_PEEK();                                  \
          CLK_INC();                                \
      }                                                  \
      s = PULL();                                        \
      CLK_INC();                                    \
      if (!(s & P_INTERRUPT) && LOCAL_INTERRUPT())       \
          OPCODE_ENABLES_IRQ();                          \
      else if ((s & P_INTERRUPT) && !LOCAL_INTERRUPT())  \
          OPCODE_DISABLES_IRQ();                         \
      LOCAL_SET_STATUS(s);                               \
      INC_PC(1);                                         \
  } while (0)

#define RLA(pc_inc, get_func, set_func)                 \
  do {                                                  \
      unsigned int old_value, new_value;                \
      get_func(old_value)                               \
      new_value = (old_value << 1) | (reg_p & P_CARRY); \
      LOCAL_SET_CARRY(new_value & 0x100);               \
      reg_a_write = reg_a_read & new_value;             \
      LOCAL_SET_NZ(reg_a_read);                         \
      INC_PC(pc_inc);                                   \
      set_func(old_value, new_value);                   \
  } while (0)

#define ROL(pc_inc, get_func, set_func)                 \
  do {                                                  \
      unsigned int old_value, new_value;                \
      get_func(old_value)                               \
      new_value = (old_value << 1) | (reg_p & P_CARRY); \
      LOCAL_SET_CARRY(new_value & 0x100);               \
      LOCAL_SET_NZ(new_value & 0xff);                   \
      INC_PC(pc_inc);                                   \
      set_func(old_value, new_value);                   \
  } while (0)

#define ROL_A()                               \
  do {                                        \
      unsigned int tmp = reg_a_read << 1;     \
                                              \
      reg_a_write = tmp | (reg_p & P_CARRY);  \
      LOCAL_SET_CARRY(tmp & 0x100);           \
      LOCAL_SET_NZ(reg_a_read);               \
      INC_PC(1);                              \
  } while (0)

#define ROR(pc_inc, get_func, set_func)   \
  do {                                    \
      unsigned int old_value, new_value;  \
      get_func(old_value)                 \
      new_value = old_value;              \
      if (reg_p & P_CARRY)                \
          new_value |= 0x100;             \
      LOCAL_SET_CARRY(new_value & 0x01);  \
      new_value >>= 1;                    \
      LOCAL_SET_NZ(new_value);            \
      INC_PC(pc_inc);                     \
      set_func(old_value, new_value)      \
  } while (0)

#define ROR_A()                             \
  do {                                      \
      BYTE tmp = reg_a_read;                \
                                            \
      reg_a_write = (reg_a_read >> 1) | (reg_p << 7);  \
      LOCAL_SET_CARRY(tmp & 0x01);          \
      LOCAL_SET_NZ(reg_a_read);             \
      INC_PC(1);                            \
  } while (0)

#define RRA(pc_inc, get_func, set_func)   \
  do {                                    \
      unsigned int old_value, new_value;  \
      get_func(old_value)                 \
      new_value = old_value;              \
      if (reg_p & P_CARRY)                \
          new_value |= 0x100;             \
      LOCAL_SET_CARRY(new_value & 0x01);  \
      new_value >>= 1;                    \
      LOCAL_SET_NZ(new_value);            \
      INC_PC(pc_inc);                     \
      ADC(GET_TEMP, 0);                   \
      set_func(old_value, new_value)      \
  } while (0)

/* RTI does must not use `OPCODE_ENABLES_IRQ()' even if the I flag changes
   from 1 to 0 because the value of I is set 3 cycles before the end of the
   opcode, and thus the 6510 has enough time to call the interrupt routine as
   soon as the opcode ends, if necessary.  */
#define RTI()                       \
  do {                              \
      WORD tmp;                     \
      if (!SKIP_CYCLE) {            \
          STACK_PEEK();             \
          CLK_INC();                \
      }                             \
      tmp = (WORD)PULL();           \
      CLK_INC();                    \
      LOCAL_SET_STATUS((BYTE)tmp);  \
      tmp = (WORD)PULL();           \
      CLK_INC();                    \
      tmp |= (WORD)PULL() << 8;     \
      CLK_INC();                    \
      JUMP(tmp);                    \
  } while (0)

#define RTS()                     \
  do {                            \
      WORD tmp;                   \
      if (!SKIP_CYCLE) {          \
          STACK_PEEK();           \
          CLK_INC();              \
      }                           \
      tmp = PULL();               \
      CLK_INC();                  \
      tmp |= (PULL() << 8);       \
      CLK_INC();                  \
      LOAD(tmp);                  \
      CLK_INC();                  \
      tmp++;                      \
      JUMP(tmp);                  \
  } while (0)

#define SAC()                     \
  do {                            \
      reg_a_write_idx = p1 >> 4;  \
      reg_a_read_idx = p1 & 0x0f; \
      INC_PC(2);                  \
  } while (0)

#define SBC(get_func, pc_inc)                                                \
  do {                                                                       \
      WORD src, tmp;                                                         \
                                                                             \
      get_func(src)                                                          \
      tmp = reg_a_read - src - ((reg_p & P_CARRY) ? 0 : 1);                  \
      if (reg_p & P_DECIMAL) {                                               \
          unsigned int tmp_a;                                                \
          tmp_a = (reg_a_read & 0xf) - (src & 0xf) - ((reg_p & P_CARRY)?0:1);\
          if (tmp_a & 0x10)                                                  \
              tmp_a = ((tmp_a - 6) & 0xf)                                    \
                       | ((reg_a_read & 0xf0) - (src & 0xf0) - 0x10);        \
          else                                                               \
              tmp_a = (tmp_a & 0xf) | ((reg_a_read & 0xf0) - (src & 0xf0));  \
          if (tmp_a & 0x100)                                                 \
              tmp_a -= 0x60;                                                 \
          LOCAL_SET_CARRY(tmp < 0x100);                                      \
          LOCAL_SET_NZ(tmp & 0xff);                                          \
          LOCAL_SET_OVERFLOW(((reg_a_read ^ tmp) & 0x80)                     \
                             && ((reg_a_read ^ src) & 0x80));                \
          reg_a_write = (BYTE) tmp_a;                                        \
      } else {                                                               \
          LOCAL_SET_NZ(tmp & 0xff);                                          \
          LOCAL_SET_CARRY(tmp < 0x100);                                      \
          LOCAL_SET_OVERFLOW(((reg_a_read ^ tmp) & 0x80)                     \
                             && ((reg_a_read ^ src) & 0x80));                \
          reg_a_write = (BYTE) tmp;                                          \
      }                                                                      \
      INC_PC(pc_inc);                                                        \
    }                                                                        \
  while (0)

#define SBX()                           \
  do {                                  \
      unsigned int tmp;                 \
      INC_PC(2);                        \
      tmp = (reg_a_read & reg_x) - p1;  \
      LOCAL_SET_CARRY(tmp < 0x100);     \
      reg_x = tmp & 0xff;               \
      LOCAL_SET_NZ(reg_x);              \
  } while (0)

#undef SEC    /* defined in time.h on SunOS. */
#define SEC()              \
  do {                     \
      LOCAL_SET_CARRY(1);  \
      INC_PC(1);           \
  } while (0)

#define SED()                \
  do {                       \
      LOCAL_SET_DECIMAL(1);  \
      INC_PC(1);             \
  } while (0)

#define SEI()                     \
  do {                            \
      if (!LOCAL_INTERRUPT())     \
          OPCODE_DISABLES_IRQ();  \
      LOCAL_SET_INTERRUPT(1);     \
      INC_PC(1);                  \
  } while (0)

#define SHA_IND_Y()                                    \
  do {                                                 \
      BYTE val;                                        \
      INT_IND_Y_W();                                   \
      val = reg_a_read & reg_x & ((tmpa >> 8) + 1);    \
      if ((tmpa & 0xff) + reg_y > 0xff)                \
          addr = ((tmpa + reg_y) & 0xff) | (val << 8); \
      INC_PC(2);                                       \
      STORE(addr, val);                                \
      CLK_INC();                                       \
  } while (0)

#define SH_ABS_I(reg_and, reg_i)    \
  do {                              \
      SET_ABS_SH_I(reg_and, reg_i); \
      INC_PC(3);                    \
  } while (0)

#define SHS_ABS_Y()                        \
  do {                                     \
      SH_ABS_I(reg_a_read & reg_x, reg_y); \
      reg_sp = reg_a_read & reg_x;         \
  } while (0)

#define SIR()                \
  do {                       \
      reg_y_idx = p1 >> 4;   \
      reg_x_idx = p1 & 0x0f; \
      INC_PC(2);             \
  } while (0)

#define SLO(pc_inc, get_func, set_func)     \
  do {                                      \
      unsigned int old_value, new_value;    \
      get_func(old_value)                   \
      new_value = old_value;                \
      LOCAL_SET_CARRY(old_value & 0x80);    \
      new_value <<= 1;                      \
      reg_a_write = reg_a_read | new_value; \
      LOCAL_SET_NZ(reg_a_read);             \
      INC_PC(pc_inc);                       \
      set_func(old_value, new_value)        \
  } while (0)

#define SRE(pc_inc, get_func, set_func)     \
  do {                                      \
      unsigned int old_value, new_value;    \
      get_func(old_value)                   \
      new_value = old_value;                \
      LOCAL_SET_CARRY(old_value & 0x01);    \
      new_value >>= 1;                      \
      reg_a_write = reg_a_read ^ new_value; \
      LOCAL_SET_NZ(reg_a_read);             \
      INC_PC(pc_inc);                       \
      set_func(old_value, new_value)        \
  } while (0)

#define ST(value, set_func, pc_inc) \
  do {                              \
      set_func(value);              \
      INC_PC(pc_inc);               \
  } while (0)

#define TAX()               \
  do {                      \
      reg_x = reg_a_read;   \
      LOCAL_SET_NZ(reg_a_read); \
      INC_PC(1);            \
  } while (0)

#define TAY()               \
  do {                      \
      reg_y = reg_a_read;   \
      LOCAL_SET_NZ(reg_a_read); \
      INC_PC(1);            \
  } while (0)

#define TSX()                \
  do {                       \
      reg_x = reg_sp;        \
      LOCAL_SET_NZ(reg_sp);  \
      INC_PC(1);             \
  } while (0)

#define TXA()                   \
  do {                          \
      reg_a_write = reg_x;      \
      LOCAL_SET_NZ(reg_a_read); \
      INC_PC(1);                \
  } while (0)

#define TXS()          \
  do {                 \
      reg_sp = reg_x;  \
      INC_PC(1);       \
  } while (0)

#define TYA()                   \
  do {                          \
      reg_a_write = reg_y;      \
      LOCAL_SET_NZ(reg_a_read); \
      INC_PC(1);                \
  } while (0)


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

static const BYTE fetch_tab[] = {
            /* 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F */
    /* $00 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $00 */
    /* $10 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $10 */
    /* $20 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $20 */
    /* $30 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $30 */
    /* $40 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $40 */
    /* $50 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $50 */
    /* $60 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $60 */
    /* $70 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $70 */
    /* $80 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $80 */
    /* $90 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $90 */
    /* $A0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $A0 */
    /* $B0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $B0 */
    /* $C0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $C0 */
    /* $D0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, /* $D0 */
    /* $E0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, /* $E0 */
    /* $F0 */  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1  /* $F0 */
};


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

/* Here, the CPU is emulated. */

{
    while (CLK >= alarm_context_next_pending_clk(ALARM_CONTEXT)) {
        alarm_context_dispatch(ALARM_CONTEXT, CLK);
    }

    {
        enum cpu_int pending_interrupt;

        pending_interrupt = CPU_INT_STATUS->global_pending_int;
        if (pending_interrupt != IK_NONE) {
            DO_INTERRUPT(pending_interrupt);
            if (!(CPU_INT_STATUS->global_pending_int & IK_IRQ)
                && CPU_INT_STATUS->global_pending_int & IK_IRQPEND)
                    CPU_INT_STATUS->global_pending_int &= ~IK_IRQPEND;
            while (CLK >= alarm_context_next_pending_clk(ALARM_CONTEXT)) {
                alarm_context_dispatch(ALARM_CONTEXT, CLK);
            }
        }
    }

    {
        opcode_t opcode;
#ifdef DEBUG
        CLOCK debug_clk;
        debug_clk = maincpu_clk;
#endif

#ifdef FEATURE_CPUMEMHISTORY
        memmap_state |= (MEMMAP_STATE_INSTR | MEMMAP_STATE_OPCODE);
#endif

        FETCH_OPCODE(opcode);

#ifdef FEATURE_CPUMEMHISTORY
        if (p0 == 0x20) {
            monitor_cpuhistory_store(reg_pc, p0, p1, LOAD(reg_pc+2), reg_a_read, reg_x, reg_y, reg_sp, LOCAL_STATUS());
        } else {
            monitor_cpuhistory_store(reg_pc, p0, p1, p2 >> 8, reg_a_read, reg_x, reg_y, reg_sp, LOCAL_STATUS());
        }
        memmap_state &= ~(MEMMAP_STATE_INSTR | MEMMAP_STATE_OPCODE);
#endif

#ifdef DEBUG
        if (TRACEFLG) {
            BYTE op = (BYTE)(p0);
            BYTE lo = (BYTE)(p1);
            BYTE hi = (BYTE)(p2 >> 8);

            debug_maincpu((DWORD)(reg_pc), debug_clk,
                          mon_disassemble_to_string(e_comp_space,
                                                    (WORD) reg_pc, op,
                                                    lo, hi, 0, 1, "6502"),
                          reg_a_read, reg_x, reg_y, reg_sp);
        }
        if (debug.perform_break_into_monitor)
        {
            monitor_startup_trap();
            debug.perform_break_into_monitor = 0;
        }
#endif

trap_skipped:
        SET_LAST_OPCODE(p0);

        switch (p0) {

          case 0x00:            /* BRK */
            BRK();
            break;

          case 0x01:            /* ORA ($nn,X) */
            ORA(GET_IND_X, 2);
            break;

          case 0x02:            /* JAM - also used for traps */
            STATIC_ASSERT(TRAP_OPCODE == 0x02);
            JAM_02();
            break;

          case 0x22:            /* JAM */
          case 0x52:            /* JAM */
          case 0x62:            /* JAM */
          case 0x72:            /* JAM */
          case 0x92:            /* JAM */
          case 0xb2:            /* JAM */
          case 0xd2:            /* JAM */
          case 0xf2:            /* JAM */
#ifndef C64DTV
          case 0x12:            /* JAM */
          case 0x32:            /* JAM */
          case 0x42:            /* JAM */
#endif
            REWIND_FETCH_OPCODE(CLK);
            JAM();
            break;

#ifdef C64DTV
          case 0x12:            /* BRA $nnnn */
            BRANCH(1);
            break;

          case 0x32:            /* SAC #$nn */
            SAC();
            break;

          case 0x42:            /* SIR #$nn */
            SIR();
            break;
#endif

          case 0x03:            /* SLO ($nn,X) */
            SLO(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0x04:            /* NOOP $nn */
          case 0x44:            /* NOOP $nn */
          case 0x64:            /* NOOP $nn */
            NOOP(GET_ZERO, 2);
            break;

          case 0x05:            /* ORA $nn */
            ORA(GET_ZERO, 2);
            break;

          case 0x06:            /* ASL $nn */
            ASL(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x07:            /* SLO $nn */
            SLO(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x08:            /* PHP */
            PHP();
            break;

          case 0x09:            /* ORA #$nn */
            ORA(GET_IMM, 2);
            break;

          case 0x0a:            /* ASL A */
            ASL_A();
            break;

          case 0x0b:            /* ANC #$nn */
          case 0x2b:            /* ANC #$nn */
            ANC();
            break;

          case 0x0c:            /* NOOP $nnnn */
            NOOP(GET_ABS, 3);
            break;

          case 0x0d:            /* ORA $nnnn */
            ORA(GET_ABS, 3);
            break;

          case 0x0e:            /* ASL $nnnn */
            ASL(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x0f:            /* SLO $nnnn */
            SLO(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x10:            /* BPL $nnnn */
            BRANCH(!LOCAL_SIGN());
            break;

          case 0x11:            /* ORA ($nn),Y */
            ORA(GET_IND_Y, 2);
            break;

          case 0x13:            /* SLO ($nn),Y */
            SLO(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0x14:            /* NOOP $nn,X */
          case 0x34:            /* NOOP $nn,X */
          case 0x54:            /* NOOP $nn,X */
          case 0x74:            /* NOOP $nn,X */
          case 0xd4:            /* NOOP $nn,X */
          case 0xf4:            /* NOOP $nn,X */
            NOOP(GET_ZERO_X, 2);
            break;

          case 0x15:            /* ORA $nn,X */
            ORA(GET_ZERO_X, 2);
            break;

          case 0x16:            /* ASL $nn,X */
            ASL(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x17:            /* SLO $nn,X */
            SLO(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x18:            /* CLC */
            CLC();
            break;

          case 0x19:            /* ORA $nnnn,Y */
            ORA(GET_ABS_Y, 3);
            break;

          case 0x1a:            /* NOOP */
          case 0x3a:            /* NOOP */
          case 0x5a:            /* NOOP */
          case 0x7a:            /* NOOP */
          case 0xda:            /* NOOP */
          case 0xfa:            /* NOOP */
          case 0xea:            /* NOP */
            NOOP(GET_IMM, 1);
            break;

          case 0x1b:            /* SLO $nnnn,Y */
            SLO(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0x1c:            /* NOOP $nnnn,X */
          case 0x3c:            /* NOOP $nnnn,X */
          case 0x5c:            /* NOOP $nnnn,X */
          case 0x7c:            /* NOOP $nnnn,X */
          case 0xdc:            /* NOOP $nnnn,X */
          case 0xfc:            /* NOOP $nnnn,X */
            NOOP(GET_ABS_X, 3);
            break;

          case 0x1d:            /* ORA $nnnn,X */
            ORA(GET_ABS_X, 3);
            break;

          case 0x1e:            /* ASL $nnnn,X */
            ASL(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x1f:            /* SLO $nnnn,X */
            SLO(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x20:            /* JSR $nnnn */
            JSR();
            break;

          case 0x21:            /* AND ($nn,X) */
            AND(GET_IND_X, 2);
            break;

          case 0x23:            /* RLA ($nn,X) */
            RLA(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0x24:            /* BIT $nn */
            BIT(GET_ZERO, 2);
            break;

          case 0x25:            /* AND $nn */
            AND(GET_ZERO, 2);
            break;

          case 0x26:            /* ROL $nn */
            ROL(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x27:            /* RLA $nn */
            RLA(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x28:            /* PLP */
            PLP();
            break;

          case 0x29:            /* AND #$nn */
            AND(GET_IMM, 2);
            break;

          case 0x2a:            /* ROL A */
            ROL_A();
            break;

          case 0x2c:            /* BIT $nnnn */
            BIT(GET_ABS, 3);
            break;

          case 0x2d:            /* AND $nnnn */
            AND(GET_ABS, 3);
            break;

          case 0x2e:            /* ROL $nnnn */
            ROL(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x2f:            /* RLA $nnnn */
            RLA(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x30:            /* BMI $nnnn */
            BRANCH(LOCAL_SIGN());
            break;

          case 0x31:            /* AND ($nn),Y */
            AND(GET_IND_Y, 2);
            break;

          case 0x33:            /* RLA ($nn),Y */
            RLA(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0x35:            /* AND $nn,X */
            AND(GET_ZERO_X, 2);
            break;

          case 0x36:            /* ROL $nn,X */
            ROL(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x37:            /* RLA $nn,X */
            RLA(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x38:            /* SEC */
            SEC();
            break;

          case 0x39:            /* AND $nnnn,Y */
            AND(GET_ABS_Y, 3);
            break;

          case 0x3b:            /* RLA $nnnn,Y */
            RLA(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0x3d:            /* AND $nnnn,X */
            AND(GET_ABS_X, 3);
            break;

          case 0x3e:            /* ROL $nnnn,X */
            ROL(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x3f:            /* RLA $nnnn,X */
            RLA(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x40:            /* RTI */
            RTI();
            break;

          case 0x41:            /* EOR ($nn,X) */
            EOR(GET_IND_X, 2);
            break;

          case 0x43:            /* SRE ($nn,X) */
            SRE(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0x45:            /* EOR $nn */
            EOR(GET_ZERO, 2);
            break;

          case 0x46:            /* LSR $nn */
            LSR(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x47:            /* SRE $nn */
            SRE(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x48:            /* PHA */
            PHA();
            break;

          case 0x49:            /* EOR #$nn */
            EOR(GET_IMM, 2);
            break;

          case 0x4a:            /* LSR A */
            LSR_A();
            break;

          case 0x4b:            /* ASR #$nn */
            ASR();
            break;

          case 0x4c:            /* JMP $nnnn */
            JMP(p2);
            break;

          case 0x4d:            /* EOR $nnnn */
            EOR(GET_ABS, 3);
            break;

          case 0x4e:            /* LSR $nnnn */
            LSR(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x4f:            /* SRE $nnnn */
            SRE(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x50:            /* BVC $nnnn */
            BRANCH(!LOCAL_OVERFLOW());
            break;

          case 0x51:            /* EOR ($nn),Y */
            EOR(GET_IND_Y, 2);
            break;

          case 0x53:            /* SRE ($nn),Y */
            SRE(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0x55:            /* EOR $nn,X */
            EOR(GET_ZERO_X, 2);
            break;

          case 0x56:            /* LSR $nn,X */
            LSR(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x57:            /* SRE $nn,X */
            SRE(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x58:            /* CLI */
            CLI();
            break;

          case 0x59:            /* EOR $nnnn,Y */
            EOR(GET_ABS_Y, 3);
            break;

          case 0x5b:            /* SRE $nnnn,Y */
            SRE(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0x5d:            /* EOR $nnnn,X */
            EOR(GET_ABS_X, 3);
            break;

          case 0x5e:            /* LSR $nnnn,X */
            LSR(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x5f:            /* SRE $nnnn,X */
            SRE(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x60:            /* RTS */
            RTS();
            break;

          case 0x61:            /* ADC ($nn,X) */
            ADC(GET_IND_X, 2);
            break;

          case 0x63:            /* RRA ($nn,X) */
            RRA(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0x65:            /* ADC $nn */
            ADC(GET_ZERO, 2);
            break;

          case 0x66:            /* ROR $nn */
            ROR(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x67:            /* RRA $nn */
            RRA(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0x68:            /* PLA */
            PLA();
            break;

          case 0x69:            /* ADC #$nn */
            ADC(GET_IMM, 2);
            break;

          case 0x6a:            /* ROR A */
            ROR_A();
            break;

          case 0x6b:            /* ARR #$nn */
            ARR();
            break;

          case 0x6c:            /* JMP ($nnnn) */
            JMP_IND();
            break;

          case 0x6d:            /* ADC $nnnn */
            ADC(GET_ABS, 3);
            break;

          case 0x6e:            /* ROR $nnnn */
            ROR(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x6f:            /* RRA $nnnn */
            RRA(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0x70:            /* BVS $nnnn */
            BRANCH(LOCAL_OVERFLOW());
            break;

          case 0x71:            /* ADC ($nn),Y */
            ADC(GET_IND_Y, 2);
            break;

          case 0x73:            /* RRA ($nn),Y */
            RRA(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0x75:            /* ADC $nn,X */
            ADC(GET_ZERO_X, 2);
            break;

          case 0x76:            /* ROR $nn,X */
            ROR(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x77:            /* RRA $nn,X */
            RRA(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0x78:            /* SEI */
            SEI();
            break;

          case 0x79:            /* ADC $nnnn,Y */
            ADC(GET_ABS_Y, 3);
            break;

          case 0x7b:            /* RRA $nnnn,Y */
            RRA(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0x7d:            /* ADC $nnnn,X */
            ADC(GET_ABS_X, 3);
            break;

          case 0x7e:            /* ROR $nnnn,X */
            ROR(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x7f:            /* RRA $nnnn,X */
            RRA(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0x80:            /* NOOP #$nn */
          case 0x82:            /* NOOP #$nn */
          case 0x89:            /* NOOP #$nn */
          case 0xc2:            /* NOOP #$nn */
          case 0xe2:            /* NOOP #$nn */
            NOOP(GET_IMM, 2);
            break;

          case 0x81:            /* STA ($nn,X) */
            ST(reg_a_read, SET_IND_X, 2);
            break;

          case 0x83:            /* SAX ($nn,X) */
            ST(reg_a_read & reg_x, SET_IND_X, 2);
            break;

          case 0x84:            /* STY $nn */
            ST(reg_y, SET_ZERO, 2);
            break;

          case 0x85:            /* STA $nn */
            ST(reg_a_read, SET_ZERO, 2);
            break;

          case 0x86:            /* STX $nn */
            ST(reg_x, SET_ZERO, 2);
            break;

          case 0x87:            /* SAX $nn */
            ST(reg_a_read & reg_x, SET_ZERO, 2);
            break;

          case 0x88:            /* DEY */
            DEY();
            break;

          case 0x8a:            /* TXA */
            TXA();
            break;

          case 0x8b:            /* ANE #$nn */
            ANE();
            break;

          case 0x8c:            /* STY $nnnn */
            ST(reg_y, SET_ABS, 3);
            break;

          case 0x8d:            /* STA $nnnn */
            ST(reg_a_read, SET_ABS, 3);
            break;

          case 0x8e:            /* STX $nnnn */
            ST(reg_x, SET_ABS, 3);
            break;

          case 0x8f:            /* SAX $nnnn */
            ST(reg_a_read & reg_x, SET_ABS, 3);
            break;

          case 0x90:            /* BCC $nnnn */
            BRANCH(!LOCAL_CARRY());
            break;

          case 0x91:            /* STA ($nn),Y */
            ST(reg_a_read, SET_IND_Y, 2);
            break;

          case 0x93:            /* SHA ($nn),Y */
            SHA_IND_Y();
            break;

          case 0x94:            /* STY $nn,X */
            ST(reg_y, SET_ZERO_X, 2);
            break;

          case 0x95:            /* STA $nn,X */
            ST(reg_a_read, SET_ZERO_X, 2);
            break;

          case 0x96:            /* STX $nn,Y */
            ST(reg_x, SET_ZERO_Y, 2);
            break;

          case 0x97:            /* SAX $nn,Y */
            ST(reg_a_read & reg_x, SET_ZERO_Y, 2);
            break;

          case 0x98:            /* TYA */
            TYA();
            break;

          case 0x99:            /* STA $nnnn,Y */
            ST(reg_a_read, SET_ABS_Y, 3);
            break;

          case 0x9a:            /* TXS */
            TXS();
            break;

          case 0x9b:            /* NOP (SHS) $nnnn,Y */
#ifdef C64DTV
            NOOP(GET_ABS_Y, 3);
#else
            SHS_ABS_Y();
#endif
            break;

          case 0x9c:            /* SHY $nnnn,X */
            SH_ABS_I(reg_y, reg_x);
            break;

          case 0x9d:            /* STA $nnnn,X */
            ST(reg_a_read, SET_ABS_X, 3);
            break;

          case 0x9e:            /* SHX $nnnn,Y */
            SH_ABS_I(reg_x, reg_y);
            break;

          case 0x9f:            /* SHA $nnnn,Y */
            SH_ABS_I(reg_a_read & reg_x, reg_y);
            break;

          case 0xa0:            /* LDY #$nn */
            LD(reg_y, GET_IMM, 2);
            break;

          case 0xa1:            /* LDA ($nn,X) */
            LD(reg_a_write, GET_IND_X, 2);
            break;

          case 0xa2:            /* LDX #$nn */
            LD(reg_x, GET_IMM, 2);
            break;

          case 0xa3:            /* LAX ($nn,X) */
            LAX(GET_IND_X, 2);
            break;

          case 0xa4:            /* LDY $nn */
            LD(reg_y, GET_ZERO, 2);
            break;

          case 0xa5:            /* LDA $nn */
            LD(reg_a_write, GET_ZERO, 2);
            break;

          case 0xa6:            /* LDX $nn */
            LD(reg_x, GET_ZERO, 2);
            break;

          case 0xa7:            /* LAX $nn */
            LAX(GET_ZERO, 2);
            break;

          case 0xa8:            /* TAY */
            TAY();
            break;

          case 0xa9:            /* LDA #$nn */
            LD(reg_a_write, GET_IMM, 2);
            break;

          case 0xaa:            /* TAX */
            TAX();
            break;

          case 0xab:            /* LXA #$nn */
            LXA(p1, 2);
            break;

          case 0xac:            /* LDY $nnnn */
            LD(reg_y, GET_ABS, 3);
            break;

          case 0xad:            /* LDA $nnnn */
            LD(reg_a_write, GET_ABS, 3);
            break;

          case 0xae:            /* LDX $nnnn */
            LD(reg_x, GET_ABS, 3);
            break;

          case 0xaf:            /* LAX $nnnn */
            LAX(GET_ABS, 3);
            break;

          case 0xb0:            /* BCS $nnnn */
            BRANCH(LOCAL_CARRY());
            break;

          case 0xb1:            /* LDA ($nn),Y */
            LD(reg_a_write, GET_IND_Y, 2);
            break;

          case 0xb3:            /* LAX ($nn),Y */
            LAX(GET_IND_Y, 2);
            break;

          case 0xb4:            /* LDY $nn,X */
            LD(reg_y, GET_ZERO_X, 2);
            break;

          case 0xb5:            /* LDA $nn,X */
            LD(reg_a_write, GET_ZERO_X, 2);
            break;

          case 0xb6:            /* LDX $nn,Y */
            LD(reg_x, GET_ZERO_Y, 2);
            break;

          case 0xb7:            /* LAX $nn,Y */
            LAX(GET_ZERO_Y, 2);
            break;

          case 0xb8:            /* CLV */
            CLV();
            break;

          case 0xb9:            /* LDA $nnnn,Y */
            LD(reg_a_write, GET_ABS_Y, 3);
            break;

          case 0xba:            /* TSX */
            TSX();
            break;

          case 0xbb:            /* LAS $nnnn,Y */
            LAS();
            break;

          case 0xbc:            /* LDY $nnnn,X */
            LD(reg_y, GET_ABS_X, 3);
            break;

          case 0xbd:            /* LDA $nnnn,X */
            LD(reg_a_write, GET_ABS_X, 3);
            break;

          case 0xbe:            /* LDX $nnnn,Y */
            LD(reg_x, GET_ABS_Y, 3);
            break;

          case 0xbf:            /* LAX $nnnn,Y */
            LAX(GET_ABS_Y, 3);
            break;

          case 0xc0:            /* CPY #$nn */
            CP(reg_y, GET_IMM, 2);
            break;

          case 0xc1:            /* CMP ($nn,X) */
            CP(reg_a_read, GET_IND_X, 2);
            break;

          case 0xc3:            /* DCP ($nn,X) */
            DCP(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0xc4:            /* CPY $nn */
            CP(reg_y, GET_ZERO, 2);
            break;

          case 0xc5:            /* CMP $nn */
            CP(reg_a_read, GET_ZERO, 2);
            break;

          case 0xc6:            /* DEC $nn */
            DEC(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0xc7:            /* DCP $nn */
            DCP(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0xc8:            /* INY */
            INY();
            break;

          case 0xc9:            /* CMP #$nn */
            CP(reg_a_read, GET_IMM, 2);
            break;

          case 0xca:            /* DEX */
            DEX();
            break;

          case 0xcb:            /* SBX #$nn */
            SBX();
            break;

          case 0xcc:            /* CPY $nnnn */
            CP(reg_y, GET_ABS, 3);
            break;

          case 0xcd:            /* CMP $nnnn */
            CP(reg_a_read, GET_ABS, 3);
            break;

          case 0xce:            /* DEC $nnnn */
            DEC(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0xcf:            /* DCP $nnnn */
            DCP(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0xd0:            /* BNE $nnnn */
            BRANCH(!LOCAL_ZERO());
            break;

          case 0xd1:            /* CMP ($nn),Y */
            CP(reg_a_read, GET_IND_Y, 2);
            break;

          case 0xd3:            /* DCP ($nn),Y */
            DCP(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0xd5:            /* CMP $nn,X */
            CP(reg_a_read, GET_ZERO_X, 2);
            break;

          case 0xd6:            /* DEC $nn,X */
            DEC(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0xd7:            /* DCP $nn,X */
            DCP(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0xd8:            /* CLD */
            CLD();
            break;

          case 0xd9:            /* CMP $nnnn,Y */
            CP(reg_a_read, GET_ABS_Y, 3);
            break;

          case 0xdb:            /* DCP $nnnn,Y */
            DCP(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0xdd:            /* CMP $nnnn,X */
            CP(reg_a_read, GET_ABS_X, 3);
            break;

          case 0xde:            /* DEC $nnnn,X */
            DEC(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0xdf:            /* DCP $nnnn,X */
            DCP(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0xe0:            /* CPX #$nn */
            CP(reg_x, GET_IMM, 2);
            break;

          case 0xe1:            /* SBC ($nn,X) */
            SBC(GET_IND_X, 2);
            break;

          case 0xe3:            /* ISB ($nn,X) */
            ISB(2, GET_IND_X, SET_IND_RMW);
            break;

          case 0xe4:            /* CPX $nn */
            CP(reg_x, GET_ZERO, 2);
            break;

          case 0xe5:            /* SBC $nn */
            SBC(GET_ZERO, 2);
            break;

          case 0xe6:            /* INC $nn */
            INC(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0xe7:            /* ISB $nn */
            ISB(2, GET_ZERO, SET_ZERO_RMW);
            break;

          case 0xe8:            /* INX */
            INX();
            break;

          case 0xe9:            /* SBC #$nn */
          case 0xeb:            /* USBC #$nn (same as SBC) */
            SBC(GET_IMM, 2);
            break;

          case 0xec:            /* CPX $nnnn */
            CP(reg_x, GET_ABS, 3);
            break;

          case 0xed:            /* SBC $nnnn */
            SBC(GET_ABS, 3);
            break;

          case 0xee:            /* INC $nnnn */
            INC(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0xef:            /* ISB $nnnn */
            ISB(3, GET_ABS, SET_ABS_RMW);
            break;

          case 0xf0:            /* BEQ $nnnn */
            BRANCH(LOCAL_ZERO());
            break;

          case 0xf1:            /* SBC ($nn),Y */
            SBC(GET_IND_Y, 2);
            break;

          case 0xf3:            /* ISB ($nn),Y */
            ISB(2, GET_IND_Y_RMW, SET_IND_RMW);
            break;

          case 0xf5:            /* SBC $nn,X */
            SBC(GET_ZERO_X, 2);
            break;

          case 0xf6:            /* INC $nn,X */
            INC(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0xf7:            /* ISB $nn,X */
            ISB(2, GET_ZERO_X, SET_ZERO_X_RMW);
            break;

          case 0xf8:            /* SED */
            SED();
            break;

          case 0xf9:            /* SBC $nnnn,Y */
            SBC(GET_ABS_Y, 3);
            break;

          case 0xfb:            /* ISB $nnnn,Y */
            ISB(3, GET_ABS_Y_RMW, SET_ABS_Y_RMW);
            break;

          case 0xfd:            /* SBC $nnnn,X */
            SBC(GET_ABS_X, 3);
            break;

          case 0xfe:            /* INC $nnnn,X */
            INC(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;

          case 0xff:            /* ISB $nnnn,X */
            ISB(3, GET_ABS_X_RMW, SET_ABS_X_RMW);
            break;
        }
    }
}

