mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
2494 lines
96 KiB
C++
2494 lines
96 KiB
C++
/*
|
|
* 65C02core.c - 65C02/65SC02 emulation core.
|
|
*
|
|
* Written by
|
|
* Marco van den Heuvel <blackystardust68@yahoo.com>
|
|
*
|
|
* 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 the following CPU definition files:
|
|
- drivecpu65c02.c
|
|
*/
|
|
|
|
/* any CPU definition file that includes this file needs to do the following:
|
|
*
|
|
* - define reg_a as 8bit.
|
|
* - define reg_x as 8bit.
|
|
* - define reg_y as 8bit.
|
|
* - define reg_pc as 16bit.
|
|
* - define reg_sp as 8bit.
|
|
* - define reg_p as 8bit
|
|
* - define the cpu being emulated in a var 'cpu_type' (CPU_WDC65C02, CPU_R65C02, CPU_65SC02).
|
|
* - define a function to handle the WDC65C02 STP opcode (WDC_STP(void)).
|
|
* - define a function to handle the WDC65C02 WAI opcode (WDC_WAI(void)).
|
|
*
|
|
*/
|
|
|
|
/* still to check and possibly fix:
|
|
*
|
|
* - BRK does get interrupted by an IRQ/NMI on the 65(S)C02?
|
|
*/
|
|
|
|
#ifndef CPU_STR
|
|
#define CPU_STR "65(S)C02 CPU"
|
|
#endif
|
|
|
|
//#include "traps.h"
|
|
|
|
/* To avoid 'magic' numbers, we will use the following defines. */
|
|
#define CYCLES_0 0
|
|
#define CYCLES_1 1
|
|
#define CYCLES_2 2
|
|
#define CYCLES_3 3
|
|
#define CYCLES_4 4
|
|
#define CYCLES_5 5
|
|
|
|
#define SIZE_1 1
|
|
#define SIZE_2 2
|
|
#define SIZE_3 3
|
|
|
|
#define BIT_0 0
|
|
#define BIT_1 1
|
|
#define BIT_2 2
|
|
#define BIT_3 3
|
|
#define BIT_4 4
|
|
#define BIT_5 5
|
|
#define BIT_6 6
|
|
#define BIT_7 7
|
|
|
|
#define IRQ_CYCLES 7
|
|
#define NMI_CYCLES 7
|
|
#define RESET_CYCLES 6
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Backup for non-6509 CPUs. */
|
|
|
|
#ifndef LOAD_IND
|
|
#define LOAD_IND(a) LOAD(a)
|
|
#endif
|
|
#ifndef STORE_IND
|
|
#define STORE_IND(a, b) STORE(a, b)
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Backup for non-variable cycle CPUs. */
|
|
|
|
#ifndef CLK_ADD
|
|
#define CLK_ADD(clock, amount) clock += amount
|
|
#endif
|
|
|
|
#ifndef REWIND_FETCH_OPCODE
|
|
#define REWIND_FETCH_OPCODE(clock, amount) clock -= amount
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* Hook for additional delay. */
|
|
|
|
#ifndef CPU_DELAY_CLK
|
|
#define CPU_DELAY_CLK
|
|
#endif
|
|
|
|
#ifndef CPU_REFRESH_CLK
|
|
#define CPU_REFRESH_CLK
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
#ifndef CYCLE_EXACT_ALARM
|
|
#define PROCESS_ALARMS \
|
|
while (CLK >= alarm_context_next_pending_clk(ALARM_CONTEXT)) { \
|
|
alarm_context_dispatch(ALARM_CONTEXT, CLK); \
|
|
CPU_DELAY_CLK \
|
|
}
|
|
#else
|
|
#define PROCESS_ALARMS
|
|
#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
|
|
|
|
#ifdef LAST_OPCODE_ADDR
|
|
#define SET_LAST_ADDR(x) LAST_OPCODE_ADDR = (x)
|
|
#else
|
|
#error "please define LAST_OPCODE_ADDR"
|
|
#endif
|
|
|
|
#ifndef DRIVE_CPU
|
|
/* Export the local version of the registers. */
|
|
#define EXPORT_REGISTERS() \
|
|
do { \
|
|
GLOBAL_REGS.pc = reg_pc; \
|
|
GLOBAL_REGS.a = reg_a; \
|
|
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 = 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; \
|
|
bank_start = bank_limit = 0; /* prevent caching */ \
|
|
JUMP(GLOBAL_REGS.pc); \
|
|
} while (0)
|
|
#else /* DRIVE_CPU */
|
|
#define IMPORT_REGISTERS()
|
|
#define EXPORT_REGISTERS()
|
|
#endif /* !DRIVE_CPU */
|
|
|
|
/* Stack operations. */
|
|
|
|
#ifndef PUSH
|
|
#define PUSH(val) ((PAGE_ONE)[(reg_sp--)] = ((BYTE)(val)))
|
|
#endif
|
|
#ifndef PULL
|
|
#define PULL() ((PAGE_ONE)[(++reg_sp)])
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#define TRACE_NMI(clk) \
|
|
do { \
|
|
if (TRACEFLG) { \
|
|
debug_nmi(CPU_INT_STATUS, (clk)); \
|
|
} \
|
|
} while (0)
|
|
#define TRACE_IRQ(clk) \
|
|
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(clk)
|
|
#define TRACE_IRQ(clk)
|
|
#define TRACE_BRK()
|
|
#endif
|
|
|
|
/* Perform the interrupts in `int_kind'. If we have both NMI and IRQ,
|
|
execute NMI. NMI can _not_ take over an in progress IRQ. */
|
|
/* FIXME: LOCAL_STATUS() should check byte ready first. */
|
|
#define DO_INTERRUPT(int_kind) \
|
|
do { \
|
|
BYTE ik = (int_kind); \
|
|
\
|
|
if (ik & (IK_IRQ | IK_IRQPEND | IK_NMI)) { \
|
|
if ((ik & IK_NMI) \
|
|
&& interrupt_check_nmi_delay(CPU_INT_STATUS, CLK)) { \
|
|
TRACE_NMI(CLK); \
|
|
if (monitor_mask[CALLER] & (MI_STEP)) { \
|
|
monitor_check_icount_interrupt(); \
|
|
} \
|
|
interrupt_ack_nmi(CPU_INT_STATUS); \
|
|
if (NMI_CYCLES == 7) { \
|
|
LOAD(reg_pc); /* dummy reads */ \
|
|
CLK_ADD(CLK, 1); \
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, 1); \
|
|
} \
|
|
LOCAL_SET_BREAK(0); \
|
|
PUSH(reg_pc >> 8); \
|
|
PUSH(reg_pc & 0xff); \
|
|
CLK_ADD(CLK, 2); \
|
|
PUSH(LOCAL_STATUS()); \
|
|
CLK_ADD(CLK, 1); \
|
|
LOCAL_SET_DECIMAL(0); \
|
|
LOCAL_SET_INTERRUPT(1); \
|
|
JUMP(LOAD_ADDR(0xfffa)); \
|
|
SET_LAST_OPCODE(0); \
|
|
CLK_ADD(CLK, 2); \
|
|
} \
|
|
if ((ik & (IK_IRQ | IK_IRQPEND)) && (!LOCAL_INTERRUPT() || OPINFO_DISABLES_IRQ(LAST_OPCODE_INFO)) \
|
|
&& interrupt_check_irq_delay(CPU_INT_STATUS, CLK)) { \
|
|
TRACE_IRQ(CLK); \
|
|
if (monitor_mask[CALLER] & (MI_STEP)) { \
|
|
monitor_check_icount_interrupt(); \
|
|
} \
|
|
interrupt_ack_irq(CPU_INT_STATUS); \
|
|
if (NMI_CYCLES == 7) { \
|
|
LOAD(reg_pc); /* dummy reads */ \
|
|
CLK_ADD(CLK, 1); \
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, 1); \
|
|
} \
|
|
LOCAL_SET_BREAK(0); \
|
|
PUSH(reg_pc >> 8); \
|
|
PUSH(reg_pc & 0xff); \
|
|
CLK_ADD(CLK, 2); \
|
|
PUSH(LOCAL_STATUS()); \
|
|
CLK_ADD(CLK, 1); \
|
|
LOCAL_SET_DECIMAL(0); \
|
|
LOCAL_SET_INTERRUPT(1); \
|
|
JUMP(LOAD_ADDR(0xfffe)); \
|
|
SET_LAST_OPCODE(0); \
|
|
CLK_ADD(CLK, 2); \
|
|
} \
|
|
} \
|
|
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); \
|
|
cpu65c02_reset(); \
|
|
bank_start = bank_limit = 0; /* prevent caching */ \
|
|
JUMP(LOAD_ADDR(0xfffc)); \
|
|
DMA_ON_RESET; \
|
|
} \
|
|
} \
|
|
if (ik & (IK_MONITOR | IK_DMA)) { \
|
|
if (ik & IK_MONITOR) { \
|
|
if (monitor_force_import(CALLER)) { \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
if (monitor_mask[CALLER]) { \
|
|
EXPORT_REGISTERS(); \
|
|
} \
|
|
if (monitor_mask[CALLER] & (MI_STEP)) { \
|
|
monitor_check_icount((WORD)reg_pc); \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
if (monitor_mask[CALLER] & (MI_BREAK)) { \
|
|
if (monitor_check_breakpoints(CALLER, (WORD)reg_pc)) { \
|
|
monitor_startup(CALLER); \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
} \
|
|
if (monitor_mask[CALLER] & (MI_WATCH)) { \
|
|
monitor_check_watchpoints(LAST_OPCODE_ADDR, (WORD)reg_pc); \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
} \
|
|
if (ik & IK_DMA) { \
|
|
EXPORT_REGISTERS(); \
|
|
DMA_FUNC; \
|
|
interrupt_ack_dma(CPU_INT_STATUS); \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* Addressing modes. For convenience, page boundary crossing cycles and
|
|
``idle'' memory reads are handled here as well. */
|
|
|
|
#define LOAD_ABS(addr) LOAD(addr)
|
|
|
|
#define LOAD_ABS_X(addr) \
|
|
((((addr) & 0xff) + reg_x) > 0xff \
|
|
? (LOAD(reg_pc + 2), \
|
|
CLK_ADD(CLK, CYCLES_1), \
|
|
LOAD((addr) + reg_x)) \
|
|
: LOAD((addr) + reg_x))
|
|
|
|
#define LOAD_ABS_X_RMW(addr) \
|
|
(LOAD(reg_pc + 2), \
|
|
CLK_ADD(CLK, CYCLES_1), \
|
|
LOAD((addr) + reg_x))
|
|
|
|
#define LOAD_ABS_Y(addr) \
|
|
((((addr) & 0xff) + reg_y) > 0xff \
|
|
? (LOAD(reg_pc + 2), \
|
|
CLK_ADD(CLK, CYCLES_1), \
|
|
LOAD((addr) + reg_y)) \
|
|
: LOAD((addr) + reg_y))
|
|
|
|
#define LOAD_INDIRECT(addr) (CLK_ADD(CLK, CYCLES_2), LOAD(LOAD_ZERO_ADDR((addr))))
|
|
|
|
#define LOAD_IND_X(addr) (CLK_ADD(CLK, CYCLES_3), LOAD(LOAD_ZERO_ADDR((addr) + reg_x)))
|
|
|
|
#define LOAD_IND_Y(addr) \
|
|
(CLK_ADD(CLK, CYCLES_2), ((LOAD_ZERO_ADDR((addr)) & 0xff) + reg_y) > 0xff \
|
|
? (LOAD(reg_pc + 1), \
|
|
CLK_ADD(CLK, CYCLES_1), \
|
|
LOAD(LOAD_ZERO_ADDR((addr)) + reg_y)) \
|
|
: LOAD(LOAD_ZERO_ADDR((addr)) + reg_y))
|
|
|
|
#define LOAD_ZERO_X(addr) (LOAD_ZERO((addr) + reg_x))
|
|
|
|
#define LOAD_ZERO_Y(addr) (LOAD_ZERO((addr) + reg_y))
|
|
|
|
#define LOAD_IND_Y_BANK(addr) \
|
|
(CLK_ADD(CLK, CYCLES_2), ((LOAD_ZERO_ADDR((addr)) & 0xff) + reg_y) > 0xff \
|
|
? (LOAD(reg_pc + 1), \
|
|
CLK_ADD(CLK, CYCLES_1), \
|
|
LOAD_IND(LOAD_ZERO_ADDR((addr)) + reg_y)) \
|
|
: LOAD_IND(LOAD_ZERO_ADDR((addr)) + reg_y))
|
|
|
|
#define STORE_ABS(addr, value) \
|
|
do { \
|
|
STORE((addr), (value)); \
|
|
} while (0)
|
|
|
|
#define STORE_ABS_RRW(addr, value) \
|
|
do { \
|
|
LOAD(addr); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
STORE(addr, value); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STORE_ABS_X(addr, value) \
|
|
do { \
|
|
LOAD(reg_pc - 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
STORE((addr) + reg_x, (value)); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STORE_ABS_X_RRW(addr, value) \
|
|
do { \
|
|
LOAD((addr) + reg_x); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
STORE((addr) + reg_x, (value)); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STORE_ABS_Y(addr, value) \
|
|
do { \
|
|
LOAD(reg_pc - 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
STORE((addr) + reg_y, (value)); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} 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(value, clk_inc, pc_inc) \
|
|
do { \
|
|
unsigned int tmp_value; \
|
|
unsigned int tmp, tmp2; \
|
|
\
|
|
tmp_value = (value); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
\
|
|
if (LOCAL_DECIMAL()) { \
|
|
tmp = (reg_a & 0xf) + (tmp_value & 0xf) + LOCAL_CARRY(); \
|
|
tmp2 = (reg_a & 0xf0) + (tmp_value & 0xf0); \
|
|
if (tmp > 9) { \
|
|
tmp2 += 0x10; \
|
|
tmp += 6; \
|
|
} \
|
|
LOCAL_SET_OVERFLOW(~(reg_a ^ tmp_value) & (reg_a ^ tmp) & 0x80); \
|
|
if (tmp2 > 0x90) { \
|
|
tmp2 += 0x60; \
|
|
} \
|
|
LOCAL_SET_CARRY(tmp2 & 0xff00); \
|
|
tmp = (tmp & 0xf) + (tmp2 & 0xf0); \
|
|
LOCAL_SET_NZ(tmp); \
|
|
LOAD(reg_pc + pc_inc - 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} else { \
|
|
tmp = tmp_value + reg_a + LOCAL_CARRY(); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
LOCAL_SET_OVERFLOW(!((reg_a ^ tmp_value) & 0x80) && ((reg_a ^ tmp) & 0x80)); \
|
|
LOCAL_SET_CARRY(tmp > 0xff); \
|
|
} \
|
|
reg_a = tmp; \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define AND(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_a = (BYTE)(reg_a & (value)); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define ASL(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp_value, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp_value = load_func(tmp_addr); \
|
|
LOCAL_SET_CARRY(tmp_value & 0x80); \
|
|
tmp_value = (tmp_value << 1) & 0xff; \
|
|
LOCAL_SET_NZ(tmp_value); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp_addr, tmp_value); \
|
|
} while (0)
|
|
|
|
#define ASL_A() \
|
|
do { \
|
|
LOCAL_SET_CARRY(reg_a & 0x80); \
|
|
reg_a = reg_a << 1; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define BBR(bit) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
unsigned int dest_addr; \
|
|
BYTE value; \
|
|
\
|
|
if (cpu_type == CPU_65SC02) { \
|
|
NOOP_IMM(SIZE_1); \
|
|
} else { \
|
|
tmp_addr = LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
value = LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
tmp = LOAD_ZERO(tmp_addr) & (1 << bit); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_3); \
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (!tmp) { \
|
|
dest_addr = reg_pc + (signed char)(value); \
|
|
OPCODE_DELAYS_INTERRUPT(); \
|
|
JUMP(dest_addr & 0xffff); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define BBS(bit) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
unsigned int dest_addr; \
|
|
BYTE value; \
|
|
\
|
|
if (cpu_type == CPU_65SC02) { \
|
|
NOOP_IMM(SIZE_1); \
|
|
} else { \
|
|
tmp_addr = LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
value = LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
tmp = LOAD_ZERO(tmp_addr) & (1 << bit); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_3); \
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
\
|
|
if (tmp) { \
|
|
dest_addr = reg_pc + (signed char)(value); \
|
|
OPCODE_DELAYS_INTERRUPT(); \
|
|
JUMP(dest_addr & 0xffff); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define BIT(value, clk_inc, pc_inc) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = (value); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
LOCAL_SET_SIGN(tmp & 0x80); \
|
|
LOCAL_SET_OVERFLOW(tmp & 0x40); \
|
|
LOCAL_SET_ZERO(!(tmp & reg_a)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define BIT_IMM(value) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = (value); \
|
|
LOCAL_SET_ZERO(!(tmp & reg_a)); \
|
|
INC_PC(SIZE_2); \
|
|
} while (0)
|
|
|
|
#define BRANCH(cond, value) \
|
|
do { \
|
|
unsigned int dest_addr = 0; \
|
|
INC_PC(SIZE_2); \
|
|
\
|
|
if (cond) { \
|
|
dest_addr = reg_pc + (signed char)(value); \
|
|
\
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if ((reg_pc ^ dest_addr) & 0xff00) { \
|
|
LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} else { \
|
|
OPCODE_DELAYS_INTERRUPT(); \
|
|
} \
|
|
JUMP(dest_addr & 0xffff); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define BRK() \
|
|
do { \
|
|
EXPORT_REGISTERS(); \
|
|
TRACE_BRK(); \
|
|
INC_PC(SIZE_2); \
|
|
LOCAL_SET_BREAK(1); \
|
|
PUSH(reg_pc >> 8); \
|
|
PUSH(reg_pc & 0xff); \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
PUSH(LOCAL_STATUS()); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOCAL_SET_DECIMAL(0); \
|
|
LOCAL_SET_INTERRUPT(1); \
|
|
JUMP(LOAD_ADDR(0xfffe)); \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
} while (0)
|
|
|
|
#define CLC() \
|
|
do { \
|
|
INC_PC(SIZE_1); \
|
|
LOCAL_SET_CARRY(0); \
|
|
} while (0)
|
|
|
|
#define CLD() \
|
|
do { \
|
|
INC_PC(SIZE_1); \
|
|
LOCAL_SET_DECIMAL(0); \
|
|
} while (0)
|
|
|
|
#define CLI() \
|
|
do { \
|
|
INC_PC(SIZE_1); \
|
|
if (LOCAL_INTERRUPT()) { \
|
|
OPCODE_ENABLES_IRQ(); \
|
|
} \
|
|
LOCAL_SET_INTERRUPT(0); \
|
|
} while (0)
|
|
|
|
#define CLV() \
|
|
do { \
|
|
INC_PC(SIZE_1); \
|
|
LOCAL_SET_OVERFLOW(0); \
|
|
} while (0)
|
|
|
|
#define CMP(value, clk_inc, pc_inc) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = reg_a - (value); \
|
|
LOCAL_SET_CARRY(tmp < 0x100); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define CPX(value, clk_inc, pc_inc) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = reg_x - (value); \
|
|
LOCAL_SET_CARRY(tmp < 0x100); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define CPY(value, clk_inc, pc_inc) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = reg_y - (value); \
|
|
LOCAL_SET_CARRY(tmp < 0x100); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define DEA() \
|
|
do { \
|
|
reg_a--; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define DEC(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp = load_func(tmp_addr); \
|
|
tmp = (tmp - 1) & 0xff; \
|
|
LOCAL_SET_NZ(tmp); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
store_func(tmp_addr, tmp); \
|
|
} while (0)
|
|
|
|
#define DEX() \
|
|
do { \
|
|
reg_x--; \
|
|
LOCAL_SET_NZ(reg_x); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define DEY() \
|
|
do { \
|
|
reg_y--; \
|
|
LOCAL_SET_NZ(reg_y); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define EOR(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_a = (BYTE)(reg_a ^ (value)); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define INA() \
|
|
do { \
|
|
reg_a++; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define INC(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp = (load_func(tmp_addr) + 1) & 0xff; \
|
|
LOCAL_SET_NZ(tmp); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
store_func(tmp_addr, tmp); \
|
|
} while (0)
|
|
|
|
#define INX() \
|
|
do { \
|
|
reg_x++; \
|
|
LOCAL_SET_NZ(reg_x); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define INY() \
|
|
do { \
|
|
reg_y++; \
|
|
LOCAL_SET_NZ(reg_y); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
/* The 0x02 NOP opcode is also used to patch the ROM. The function trap_handler()
|
|
returns nonzero if this is not a patch, but a `real' NOP instruction. */
|
|
|
|
#define NOP_02() \
|
|
do { \
|
|
DWORD trap_result; \
|
|
EXPORT_REGISTERS(); \
|
|
if (!ROM_TRAP_ALLOWED() \
|
|
|| (trap_result = ROM_TRAP_HANDLER()) == (DWORD)-1) { \
|
|
NOOP_IMM(SIZE_2); \
|
|
} else { \
|
|
if (trap_result) { \
|
|
REWIND_FETCH_OPCODE(CLK, CYCLES_2); \
|
|
SET_OPCODE(trap_result); \
|
|
IMPORT_REGISTERS(); \
|
|
goto trap_skipped; \
|
|
} else { \
|
|
IMPORT_REGISTERS(); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define JMP(addr) \
|
|
do { \
|
|
JUMP(addr); \
|
|
} while (0)
|
|
|
|
#define JMP_IND() \
|
|
do { \
|
|
WORD dest_addr; \
|
|
dest_addr = LOAD(p2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
dest_addr |= (LOAD((p2 + 1) & 0xffff) << 8); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
JUMP(dest_addr); \
|
|
} while (0)
|
|
|
|
#define JMP_IND_X() \
|
|
do { \
|
|
WORD dest_addr; \
|
|
dest_addr = LOAD((p2 + reg_x) & 0xffff); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
dest_addr |= (LOAD((p2 + reg_x + 1) & 0xffff) << 8); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
JUMP(dest_addr); \
|
|
} while (0)
|
|
|
|
#define JSR() \
|
|
do { \
|
|
unsigned int tmp_addr; \
|
|
\
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
PUSH(((reg_pc) >> 8) & 0xff); \
|
|
PUSH((reg_pc) & 0xff); \
|
|
tmp_addr = (p1 | (LOAD(reg_pc) << 8)); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
JUMP(tmp_addr); \
|
|
} while (0)
|
|
|
|
#define LDA(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_a = (BYTE)(value); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define LDX(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_x = (BYTE)(value); \
|
|
LOCAL_SET_NZ(reg_x); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define LDY(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_y = (BYTE)(value); \
|
|
LOCAL_SET_NZ(reg_y); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define LSR(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp = load_func(tmp_addr); \
|
|
LOCAL_SET_CARRY(tmp & 0x01); \
|
|
tmp >>= 1; \
|
|
LOCAL_SET_NZ(tmp); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp_addr, tmp); \
|
|
} while (0)
|
|
|
|
#define LSR_A() \
|
|
do { \
|
|
LOCAL_SET_CARRY(reg_a & 0x01); \
|
|
reg_a = reg_a >> 1; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define ORA(value, clk_inc, pc_inc) \
|
|
do { \
|
|
reg_a = (BYTE)(reg_a | (value)); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define NOOP(clk_inc, pc_inc) (CLK_ADD(CLK, (clk_inc)), INC_PC(pc_inc))
|
|
|
|
#define NOOP_IMM(pc_inc) INC_PC(pc_inc)
|
|
|
|
#define NOOP_ZP() \
|
|
do { \
|
|
LOAD_ZERO(p1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
} while (0)
|
|
|
|
#define NOOP_ZP_X() \
|
|
do { \
|
|
LOAD_ZERO(p1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD_ZERO(p1 + reg_x); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
} while (0)
|
|
|
|
#define NOOP_ABS() \
|
|
do { \
|
|
LOAD(p2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_3); \
|
|
} while (0)
|
|
|
|
#define NOOP_5C() \
|
|
do { \
|
|
LOAD(p2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(reg_pc + 2); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_3); \
|
|
} while (0)
|
|
|
|
#define NOP() NOOP_IMM(SIZE_1)
|
|
|
|
#define PHA() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
PUSH(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PHP() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
PUSH(LOCAL_STATUS() | P_BREAK); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PHX() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
PUSH(reg_x); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PHY() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
PUSH(reg_y); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PLA() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
reg_a = PULL(); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PLP() \
|
|
do { \
|
|
BYTE s = PULL(); \
|
|
\
|
|
if (!(s & P_INTERRUPT) && LOCAL_INTERRUPT()) { \
|
|
OPCODE_ENABLES_IRQ(); \
|
|
} else if ((s & P_INTERRUPT) && !LOCAL_INTERRUPT()) { \
|
|
OPCODE_DISABLES_IRQ(); \
|
|
} \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
LOCAL_SET_STATUS(s); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PLX() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
reg_x = PULL(); \
|
|
LOCAL_SET_NZ(reg_x); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define PLY() \
|
|
do { \
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
reg_y = PULL(); \
|
|
LOCAL_SET_NZ(reg_y); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define RMB(bit) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
\
|
|
if (cpu_type == CPU_65SC02) { \
|
|
NOOP_IMM(SIZE_1); \
|
|
} else { \
|
|
tmp_addr = LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD_ZERO(tmp_addr); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
tmp = LOAD_ZERO(tmp_addr) & ~(1 << bit); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
STORE_ZERO(tmp_addr, tmp); \
|
|
CLK_ADD(CLK, 1); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ROL(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp = load_func(tmp_addr); \
|
|
tmp = (tmp << 1) | LOCAL_CARRY(); \
|
|
LOCAL_SET_CARRY(tmp & 0x100); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp_addr, tmp); \
|
|
} while (0)
|
|
|
|
#define ROL_A() \
|
|
do { \
|
|
unsigned int tmp = reg_a << 1; \
|
|
\
|
|
reg_a = tmp | LOCAL_CARRY(); \
|
|
LOCAL_SET_CARRY(tmp & 0x100); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define ROR(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int src, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
src = load_func(tmp_addr); \
|
|
if (LOCAL_CARRY()) { \
|
|
src |= 0x100; \
|
|
} \
|
|
LOCAL_SET_CARRY(src & 0x01); \
|
|
src >>= 1; \
|
|
LOCAL_SET_NZ(src); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
store_func(tmp_addr, src); \
|
|
} while (0)
|
|
|
|
#define ROR_A() \
|
|
do { \
|
|
BYTE tmp = reg_a; \
|
|
\
|
|
reg_a = (reg_a >> 1) | (reg_p << 7); \
|
|
LOCAL_SET_CARRY(tmp & 0x01); \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} 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; \
|
|
\
|
|
CLK_ADD(CLK, CYCLES_4); \
|
|
tmp = (WORD)PULL(); \
|
|
LOCAL_SET_STATUS((BYTE)tmp); \
|
|
tmp = (WORD)PULL(); \
|
|
tmp |= (WORD)PULL() << 8; \
|
|
JUMP(tmp); \
|
|
} while (0)
|
|
|
|
#define RTS() \
|
|
do { \
|
|
WORD tmp; \
|
|
\
|
|
CLK_ADD(CLK, CYCLES_3); \
|
|
tmp = PULL(); \
|
|
tmp = tmp | (PULL() << 8); \
|
|
LOAD(tmp); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
tmp++; \
|
|
JUMP(tmp); \
|
|
} while (0)
|
|
|
|
#define SBC(value, clk_inc, pc_inc) \
|
|
do { \
|
|
WORD src, tmp; \
|
|
\
|
|
src = (WORD)(value); \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
if (LOCAL_DECIMAL()) { \
|
|
tmp = reg_a - (src & 0xf) + LOCAL_CARRY() - 1; \
|
|
if ((tmp & 0xf) > (reg_a & 0xf)) { \
|
|
tmp -= 6; \
|
|
} \
|
|
tmp -= (src & 0xf0); \
|
|
if ((tmp & 0xf0) > (reg_a & 0xf0)) { \
|
|
tmp -= 0x60; \
|
|
} \
|
|
LOCAL_SET_OVERFLOW(!(tmp > reg_a)); \
|
|
LOCAL_SET_CARRY(!(tmp > reg_a)); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
LOAD(reg_pc + pc_inc - 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} else { \
|
|
tmp = reg_a - src - ((LOCAL_CARRY()) ? 0 : 1); \
|
|
LOCAL_SET_NZ(tmp & 0xff); \
|
|
LOCAL_SET_CARRY(tmp < 0x100); \
|
|
LOCAL_SET_OVERFLOW(((reg_a ^ tmp) & 0x80) && ((reg_a ^ src) & 0x80)); \
|
|
} \
|
|
reg_a = (BYTE)tmp; \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#undef SEC /* defined in time.h on SunOS. */
|
|
#define SEC() \
|
|
do { \
|
|
LOCAL_SET_CARRY(1); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define SED() \
|
|
do { \
|
|
LOCAL_SET_DECIMAL(1); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define SEI() \
|
|
do { \
|
|
if (!LOCAL_INTERRUPT()) { \
|
|
OPCODE_DISABLES_IRQ(); \
|
|
} \
|
|
LOCAL_SET_INTERRUPT(1); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define SMB(bit) \
|
|
do { \
|
|
unsigned tmp, tmp_addr; \
|
|
\
|
|
if (cpu_type == CPU_65SC02) { \
|
|
NOOP_IMM(SIZE_1); \
|
|
} else { \
|
|
tmp_addr = LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
LOAD(tmp_addr); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
tmp = LOAD_ZERO(tmp_addr) | (1 << bit); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
STORE_ZERO(tmp_addr, tmp); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define STA(addr, clk_inc1, clk_inc2, pc_inc, store_func) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
CLK_ADD(CLK, (clk_inc1)); \
|
|
tmp = (addr); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc2 - 1); \
|
|
store_func(tmp, reg_a); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STA_ZERO(addr, clk_inc, pc_inc) \
|
|
do { \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
STORE_ZERO((addr), reg_a); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define STA_IND_Y(addr) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
CLK_ADD(CLK, CYCLES_2); \
|
|
tmp = LOAD_ZERO_ADDR(addr); \
|
|
LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
INC_PC(SIZE_2); \
|
|
STORE_IND(tmp + reg_y, reg_a); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STP() \
|
|
do { \
|
|
if (cpu_type == CPU_WDC65C02) { \
|
|
WDC_STP(); \
|
|
} else { \
|
|
REWIND_FETCH_OPCODE(CLK, 2); \
|
|
NOOP_IMM(SIZE_1); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define STX(addr) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = (addr); \
|
|
INC_PC(SIZE_3); \
|
|
STORE(tmp, reg_x); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STX_ZERO(addr, clk_inc, pc_inc) \
|
|
do { \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
STORE_ZERO((addr), reg_x); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define STY(addr) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = (addr); \
|
|
INC_PC(SIZE_3); \
|
|
STORE(tmp, reg_y); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} while (0)
|
|
|
|
#define STY_ZERO(addr, clk_inc, pc_inc) \
|
|
do { \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
STORE_ZERO((addr), reg_y); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define STZ(addr, clk_inc, pc_inc, store_func) \
|
|
do { \
|
|
unsigned int tmp; \
|
|
\
|
|
tmp = (addr); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp, 0); \
|
|
} while (0)
|
|
|
|
#define STZ_ZERO(addr, clk_inc, pc_inc) \
|
|
do { \
|
|
CLK_ADD(CLK, (clk_inc)); \
|
|
STORE_ZERO((addr), 0); \
|
|
INC_PC(pc_inc); \
|
|
} while (0)
|
|
|
|
#define TAX() \
|
|
do { \
|
|
reg_x = reg_a; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define TAY() \
|
|
do { \
|
|
reg_y = reg_a; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define TRB(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp_value, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp_value = load_func(tmp_addr); \
|
|
LOCAL_SET_ZERO(!(tmp_value & reg_a)); \
|
|
tmp_value &= (~reg_a); \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp_addr, tmp_value); \
|
|
} while (0)
|
|
|
|
#define TSB(addr, clk_inc, pc_inc, load_func, store_func) \
|
|
do { \
|
|
unsigned int tmp_value, tmp_addr; \
|
|
\
|
|
tmp_addr = (addr); \
|
|
tmp_value = load_func(tmp_addr); \
|
|
LOCAL_SET_ZERO(!(tmp_value & reg_a)); \
|
|
tmp_value |= reg_a; \
|
|
INC_PC(pc_inc); \
|
|
CLK_ADD(CLK, clk_inc); \
|
|
store_func(tmp_addr, tmp_value); \
|
|
} while (0)
|
|
|
|
#define TSX() \
|
|
do { \
|
|
reg_x = reg_sp; \
|
|
LOCAL_SET_NZ(reg_sp); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define TXA() \
|
|
do { \
|
|
reg_a = reg_x; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define TXS() \
|
|
do { \
|
|
reg_sp = reg_x; \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define TYA() \
|
|
do { \
|
|
reg_a = reg_y; \
|
|
LOCAL_SET_NZ(reg_a); \
|
|
INC_PC(SIZE_1); \
|
|
} while (0)
|
|
|
|
#define WAI() \
|
|
do { \
|
|
if (cpu_type == CPU_WDC65C02) { \
|
|
WDC_WAI(); \
|
|
} else { \
|
|
REWIND_FETCH_OPCODE(CLK, 2); \
|
|
NOOP_IMM(SIZE_1); \
|
|
} \
|
|
} while (0)
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* These tables have a different meaning than for the 6502, it represents
|
|
the amount of extra fetches to the opcode fetch.
|
|
*/
|
|
static const BYTE fetch_tab[] = {
|
|
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
|
|
/* $00 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $00 */
|
|
/* $10 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $10 */
|
|
/* $20 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $20 */
|
|
/* $30 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $30 */
|
|
/* $40 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $40 */
|
|
/* $50 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $50 */
|
|
/* $60 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $60 */
|
|
/* $70 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $70 */
|
|
/* $80 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $80 */
|
|
/* $90 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $90 */
|
|
/* $A0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $A0 */
|
|
/* $B0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0, /* $B0 */
|
|
/* $C0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 2, 0, /* $C0 */
|
|
/* $D0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 2, 2, 2, 2, 0, /* $D0 */
|
|
/* $E0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, /* $E0 */
|
|
/* $F0 */ 1, 1, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 2, 2, 2, 0 /* $F0 */
|
|
};
|
|
|
|
#if !defined WORDS_BIGENDIAN && defined ALLOW_UNALIGNED_ACCESS
|
|
|
|
#define opcode_t DWORD
|
|
|
|
#define FETCH_OPCODE(o) \
|
|
do { \
|
|
if (((int)reg_pc) < bank_limit) { \
|
|
o = (*((DWORD *)(bank_base + reg_pc)) & 0xffffff); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[o & 0xff]) { \
|
|
CLK_ADD(CLK, fetch_tab[o & 0xff]); \
|
|
} \
|
|
} else { \
|
|
o = LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[o & 0xff]) { \
|
|
o |= LOAD(reg_pc + 1) << 8; \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[o & 0xff] - 1) { \
|
|
o |= (LOAD(reg_pc + 2) << 16); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define p0 (opcode & 0xff)
|
|
#define p1 ((opcode >> 8) & 0xff)
|
|
#define p2 (opcode >> 8)
|
|
|
|
#else /* WORDS_BIGENDIAN || !ALLOW_UNALIGNED_ACCESS */
|
|
|
|
#define opcode_t \
|
|
struct { \
|
|
BYTE ins; \
|
|
union { \
|
|
BYTE op8[2]; \
|
|
WORD op16; \
|
|
} op; \
|
|
}
|
|
|
|
#define FETCH_OPCODE(o) \
|
|
do { \
|
|
if (((int)reg_pc) < bank_limit) { \
|
|
(o).ins = *(bank_base + reg_pc); \
|
|
(o).op.op16 = (*(bank_base + reg_pc + 1) | (*(bank_base + reg_pc + 2) << 8)); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[(o).ins]) { \
|
|
CLK_ADD(CLK, fetch_tab[(o).ins]); \
|
|
} \
|
|
} else { \
|
|
(o).ins = LOAD(reg_pc); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[(o).ins]) { \
|
|
(o).op.op16 = LOAD(reg_pc + 1); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
if (fetch_tab[(o).ins - 1]) { \
|
|
(o).op.op16 |= (LOAD(reg_pc + 2) << 8); \
|
|
CLK_ADD(CLK, CYCLES_1); \
|
|
} \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define p0 (opcode.ins)
|
|
#define p2 (opcode.op.op16)
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
# define p1 (opcode.op.op8[1])
|
|
#else
|
|
# define p1 (opcode.op.op8[0])
|
|
#endif
|
|
|
|
#endif /* !WORDS_BIGENDIAN */
|
|
|
|
/* SET_OPCODE for traps */
|
|
#if !defined WORDS_BIGENDIAN && defined ALLOW_UNALIGNED_ACCESS
|
|
#define SET_OPCODE(o) (opcode) = o;
|
|
#else
|
|
#if !defined WORDS_BIGENDIAN
|
|
#define SET_OPCODE(o) \
|
|
do { \
|
|
opcode.ins = (o) & 0xff; \
|
|
opcode.op.op8[0] = ((o) >> 8) & 0xff; \
|
|
opcode.op.op8[1] = ((o) >> 16) & 0xff; \
|
|
} while (0)
|
|
#else
|
|
#define SET_OPCODE(o) \
|
|
do { \
|
|
opcode.ins = (o) & 0xff; \
|
|
opcode.op.op8[1] = ((o) >> 8) & 0xff; \
|
|
opcode.op.op8[0] = ((o) >> 16) & 0xff; \
|
|
} while (0)
|
|
#endif
|
|
#endif
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/* Here, the CPU is emulated. */
|
|
|
|
{
|
|
CPU_DELAY_CLK;
|
|
|
|
PROCESS_ALARMS;
|
|
|
|
{
|
|
enum cpu_int pending_interrupt;
|
|
|
|
if (!(CPU_INT_STATUS->global_pending_int & IK_IRQ)
|
|
&& (CPU_INT_STATUS->global_pending_int & IK_IRQPEND)
|
|
&& CPU_INT_STATUS->irq_pending_clk <= CLK) {
|
|
interrupt_ack_irq(CPU_INT_STATUS);
|
|
}
|
|
|
|
pending_interrupt = (cpu_int)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;
|
|
}
|
|
CPU_DELAY_CLK
|
|
|
|
PROCESS_ALARMS \
|
|
}
|
|
}
|
|
|
|
{
|
|
opcode_t opcode;
|
|
#ifdef DEBUG
|
|
CLOCK debug_clk;
|
|
#ifdef DRIVE_CPU
|
|
debug_clk = CLK;
|
|
#else
|
|
debug_clk = maincpu_clk;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef FEATURE_CPUMEMHISTORY
|
|
#ifndef DRIVE_CPU
|
|
memmap_state |= (MEMMAP_STATE_INSTR | MEMMAP_STATE_OPCODE);
|
|
#endif
|
|
#endif
|
|
SET_LAST_ADDR(reg_pc);
|
|
FETCH_OPCODE(opcode);
|
|
|
|
#ifdef FEATURE_CPUMEMHISTORY
|
|
#ifndef DRIVE_CPU
|
|
/* HACK to cope with FETCH_OPCODE optimization in x64 */
|
|
if (((int)reg_pc) < bank_limit) {
|
|
memmap_mem_read(reg_pc);
|
|
}
|
|
#endif
|
|
if (p0 == 0x20) {
|
|
monitor_cpuhistory_store(reg_pc, p0, p1, LOAD(reg_pc + 2), reg_a, reg_x, reg_y, reg_sp, LOCAL_STATUS());
|
|
} else {
|
|
monitor_cpuhistory_store(reg_pc, p0, p1, p2 >> 8, reg_a, reg_x, reg_y, reg_sp, LOCAL_STATUS());
|
|
}
|
|
memmap_state &= ~(MEMMAP_STATE_INSTR | MEMMAP_STATE_OPCODE);
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#ifdef DRIVE_CPU
|
|
if (TRACEFLG) {
|
|
BYTE op = (BYTE)(p0);
|
|
BYTE lo = (BYTE)(p1);
|
|
BYTE hi = (BYTE)(p2 >> 8);
|
|
|
|
debug_drive((DWORD)(reg_pc), debug_clk,
|
|
mon_disassemble_to_string(e_disk8_space,
|
|
reg_pc, op,
|
|
lo, hi, 0, 1, "R65(SC)02"),
|
|
reg_a, reg_x, reg_y, reg_sp, drv->mynumber + 8);
|
|
}
|
|
#else
|
|
if (TRACEFLG) {
|
|
BYTE op = (BYTE)(p0);
|
|
BYTE lo = (BYTE)(p1);
|
|
BYTE hi = (BYTE)(p2 >> 8);
|
|
|
|
if (op == 0x20) {
|
|
hi = LOAD(reg_pc + 2);
|
|
}
|
|
|
|
debug_maincpu((DWORD)(reg_pc), debug_clk,
|
|
mon_disassemble_to_string(e_comp_space,
|
|
reg_pc, op,
|
|
lo, hi, 0, 1, "65(SC)02"),
|
|
reg_a, reg_x, reg_y, reg_sp);
|
|
}
|
|
if (debug.perform_break_into_monitor) {
|
|
monitor_startup_trap();
|
|
debug.perform_break_into_monitor = 0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
trap_skipped:
|
|
SET_LAST_OPCODE(p0);
|
|
|
|
switch (p0) {
|
|
default: /* 1 byte, 1 cycle NOP */
|
|
NOOP_IMM(SIZE_1);
|
|
break;
|
|
|
|
case 0x22: /* NOP #$nn */
|
|
case 0x42: /* NOP #$nn */
|
|
case 0x62: /* NOP #$nn */
|
|
case 0x82: /* NOP #$nn */
|
|
case 0xc2: /* NOP #$nn */
|
|
case 0xe2: /* NOP #$nn */
|
|
NOOP_IMM(SIZE_2);
|
|
break;
|
|
|
|
case 0x44: /* NOP $nn */
|
|
NOOP_ZP();
|
|
break;
|
|
|
|
case 0x54: /* NOP $nn,X */
|
|
case 0xd4: /* NOP $nn,X */
|
|
case 0xf4: /* NOP $nn,X */
|
|
NOOP_ZP_X();
|
|
break;
|
|
|
|
case 0xdc: /* NOP $nnnn */
|
|
case 0xfc: /* NOP $nnnn */
|
|
NOOP_ABS();
|
|
break;
|
|
|
|
case 0x5c: /* NOP $nnnn + 4 (FIXME: correct ??) */
|
|
NOOP_5C();
|
|
break;
|
|
|
|
case 0x00: /* BRK */
|
|
BRK();
|
|
break;
|
|
|
|
case 0x01: /* ORA ($nn,X) */
|
|
ORA(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x02: /* NOP #$nn - also used for traps */
|
|
STATIC_ASSERT(TRAP_OPCODE == 0x02);
|
|
NOP_02();
|
|
break;
|
|
|
|
case 0x04: /* TSB $nn */
|
|
TSB(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x05: /* ORA $nn */
|
|
ORA(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x06: /* ASL $nn */
|
|
ASL(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x07: /* RMB0 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_0);
|
|
break;
|
|
|
|
case 0x08: /* PHP */
|
|
PHP();
|
|
break;
|
|
|
|
case 0x09: /* ORA #$nn */
|
|
ORA(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0x0a: /* ASL A */
|
|
ASL_A();
|
|
break;
|
|
|
|
case 0x0c: /* TSB $nnnn */
|
|
TSB(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x0d: /* ORA $nnnn */
|
|
ORA(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x0e: /* ASL $nnnn */
|
|
ASL(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x0f: /* BBR0 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_0);
|
|
break;
|
|
|
|
case 0x10: /* BPL $nnnn */
|
|
BRANCH(!LOCAL_SIGN(), p1);
|
|
break;
|
|
|
|
case 0x11: /* ORA ($nn),Y */
|
|
ORA(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x12: /* ORA ($nn) */
|
|
ORA(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x14: /* TRB $nn */
|
|
TRB(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x15: /* ORA $nn,X */
|
|
ORA(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x16: /* ASL $nn,X */
|
|
ASL((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x17: /* RMB1 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_1);
|
|
break;
|
|
|
|
case 0x18: /* CLC */
|
|
CLC();
|
|
break;
|
|
|
|
case 0x19: /* ORA $nnnn,Y */
|
|
ORA(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x1a: /* INA */
|
|
INA();
|
|
break;
|
|
|
|
case 0x1c: /* TRB $nnnn */
|
|
TRB(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x1d: /* ORA $nnnn,X */
|
|
ORA(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x1e: /* ASL $nnnn,X */
|
|
ASL(p2, CYCLES_1, SIZE_3, LOAD_ABS_X, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0x1f: /* BBR1 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_1);
|
|
break;
|
|
|
|
case 0x20: /* JSR $nnnn */
|
|
JSR();
|
|
break;
|
|
|
|
case 0x21: /* AND ($nn,X) */
|
|
AND(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x24: /* BIT $nn */
|
|
BIT(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x25: /* AND $nn */
|
|
AND(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x26: /* ROL $nn */
|
|
ROL(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x27: /* RMB2 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_2);
|
|
break;
|
|
|
|
case 0x28: /* PLP */
|
|
PLP();
|
|
break;
|
|
|
|
case 0x29: /* AND #$nn */
|
|
AND(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0x2a: /* ROL A */
|
|
ROL_A();
|
|
break;
|
|
|
|
case 0x2c: /* BIT $nnnn */
|
|
BIT(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x2d: /* AND $nnnn */
|
|
AND(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x2e: /* ROL $nnnn */
|
|
ROL(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x2f: /* BBR2 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_2);
|
|
break;
|
|
|
|
case 0x30: /* BMI $nnnn */
|
|
BRANCH(LOCAL_SIGN(), p1);
|
|
break;
|
|
|
|
case 0x31: /* AND ($nn),Y */
|
|
AND(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x32: /* AND ($nn) */
|
|
AND(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x34: /* BIT $nn,X */
|
|
BIT(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x35: /* AND $nn,X */
|
|
AND(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x36: /* ROL $nn,X */
|
|
ROL((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x37: /* RMB3 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_3);
|
|
break;
|
|
|
|
case 0x38: /* SEC */
|
|
SEC();
|
|
break;
|
|
|
|
case 0x39: /* AND $nnnn,Y */
|
|
AND(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x3a: /* DEA */
|
|
DEA();
|
|
break;
|
|
|
|
case 0x3c: /* BIT $nnnn,X */
|
|
BIT(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x3d: /* AND $nnnn,X */
|
|
AND(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x3e: /* ROL $nnnn,X */
|
|
ROL(p2, CYCLES_1, SIZE_3, LOAD_ABS_X, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0x3f: /* BBR3 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_3);
|
|
break;
|
|
|
|
case 0x40: /* RTI */
|
|
RTI();
|
|
break;
|
|
|
|
case 0x41: /* EOR ($nn,X) */
|
|
EOR(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x45: /* EOR $nn */
|
|
EOR(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x46: /* LSR $nn */
|
|
LSR(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x47: /* RMB4 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_4);
|
|
break;
|
|
|
|
case 0x48: /* PHA */
|
|
PHA();
|
|
break;
|
|
|
|
case 0x49: /* EOR #$nn */
|
|
EOR(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0x4a: /* LSR A */
|
|
LSR_A();
|
|
break;
|
|
|
|
case 0x4c: /* JMP $nnnn */
|
|
JMP(p2);
|
|
break;
|
|
|
|
case 0x4d: /* EOR $nnnn */
|
|
EOR(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x4e: /* LSR $nnnn */
|
|
LSR(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x4f: /* BBR4 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_4);
|
|
break;
|
|
|
|
case 0x50: /* BVC $nnnn */
|
|
BRANCH(!LOCAL_OVERFLOW(), p1);
|
|
break;
|
|
|
|
case 0x51: /* EOR ($nn),Y */
|
|
EOR(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x52: /* EOR ($nn) */
|
|
EOR(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x55: /* EOR $nn,X */
|
|
EOR(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x56: /* LSR $nn,X */
|
|
LSR((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x57: /* RMB5 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_5);
|
|
break;
|
|
|
|
case 0x58: /* CLI */
|
|
CLI();
|
|
break;
|
|
|
|
case 0x59: /* EOR $nnnn,Y */
|
|
EOR(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x5a: /* PHY */
|
|
PHY();
|
|
break;
|
|
|
|
case 0x5d: /* EOR $nnnn,X */
|
|
EOR(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x5e: /* LSR $nnnn,X */
|
|
LSR(p2, CYCLES_1, SIZE_3, LOAD_ABS_X, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0x5f: /* BBR5 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_5);
|
|
break;
|
|
|
|
case 0x60: /* RTS */
|
|
RTS();
|
|
break;
|
|
|
|
case 0x61: /* ADC ($nn,X) */
|
|
ADC(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x64: /* STZ $nn */
|
|
STZ_ZERO(p1, CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x65: /* ADC $nn */
|
|
ADC(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x66: /* ROR $nn */
|
|
ROR(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x67: /* RMB6 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_6);
|
|
break;
|
|
|
|
case 0x68: /* PLA */
|
|
PLA();
|
|
break;
|
|
|
|
case 0x69: /* ADC #$nn */
|
|
ADC(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0x6a: /* ROR A */
|
|
ROR_A();
|
|
break;
|
|
|
|
case 0x6c: /* JMP ($nnnn) */
|
|
JMP_IND();
|
|
break;
|
|
|
|
case 0x6d: /* ADC $nnnn */
|
|
ADC(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x6e: /* ROR $nnnn */
|
|
ROR(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0x6f: /* BBR6 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_6);
|
|
break;
|
|
|
|
case 0x70: /* BVS $nnnn */
|
|
BRANCH(LOCAL_OVERFLOW(), p1);
|
|
break;
|
|
|
|
case 0x71: /* ADC ($nn),Y */
|
|
ADC(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x72: /* ADC ($nn) */
|
|
ADC(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x74: /* STZ $nn,X */
|
|
STZ_ZERO(p1 + reg_x, CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x75: /* ADC $nn,X */
|
|
ADC(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x76: /* ROR $nn,X */
|
|
ROR((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0x77: /* RMB7 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
RMB(BIT_7);
|
|
break;
|
|
|
|
case 0x78: /* SEI */
|
|
SEI();
|
|
break;
|
|
|
|
case 0x79: /* ADC $nnnn,Y */
|
|
ADC(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x7a: /* PLY */
|
|
PLY();
|
|
break;
|
|
|
|
case 0x7c: /* JMP ($nnnn,X) */
|
|
JMP_IND_X();
|
|
break;
|
|
|
|
case 0x7d: /* ADC $nnnn,X */
|
|
ADC(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0x7e: /* ROR $nnnn,X */
|
|
ROR(p2, CYCLES_1, SIZE_3, LOAD_ABS_X, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0x7f: /* BBR7 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBR(BIT_7);
|
|
break;
|
|
|
|
case 0x80: /* BRA $nnnn */
|
|
BRANCH(1, p1);
|
|
break;
|
|
|
|
case 0x81: /* STA ($nn,X) */
|
|
STA(LOAD_ZERO_ADDR(p1 + reg_x), CYCLES_3, CYCLES_1, SIZE_2, STORE_ABS);
|
|
break;
|
|
|
|
case 0x84: /* STY $nn */
|
|
STY_ZERO(p1, CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x85: /* STA $nn */
|
|
STA_ZERO(p1, CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x86: /* STX $nn */
|
|
STX_ZERO(p1, CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0x87: /* SMB0 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_0);
|
|
break;
|
|
|
|
case 0x88: /* DEY */
|
|
DEY();
|
|
break;
|
|
|
|
case 0x89: /* BIT #$nn */
|
|
BIT_IMM(p1);
|
|
break;
|
|
|
|
case 0x8a: /* TXA */
|
|
TXA();
|
|
break;
|
|
|
|
case 0x8c: /* STY $nnnn */
|
|
STY(p2);
|
|
break;
|
|
|
|
case 0x8d: /* STA $nnnn */
|
|
STA(p2, CYCLES_0, CYCLES_1, SIZE_3, STORE_ABS);
|
|
break;
|
|
|
|
case 0x8e: /* STX $nnnn */
|
|
STX(p2);
|
|
break;
|
|
|
|
case 0x8f: /* BBS0 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_0);
|
|
break;
|
|
|
|
case 0x90: /* BCC $nnnn */
|
|
BRANCH(!LOCAL_CARRY(), p1);
|
|
break;
|
|
|
|
case 0x91: /* STA ($nn),Y */
|
|
STA_IND_Y(p1);
|
|
break;
|
|
|
|
case 0x92: /* STA ($nn) */
|
|
STA(LOAD_ZERO_ADDR(p1), CYCLES_2, CYCLES_1, SIZE_2, STORE_ABS);
|
|
break;
|
|
|
|
case 0x94: /* STY $nn,X */
|
|
STY_ZERO(p1 + reg_x, CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x95: /* STA $nn,X */
|
|
STA_ZERO(p1 + reg_x, CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x96: /* STX $nn,Y */
|
|
STX_ZERO(p1 + reg_y, CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0x97: /* SMB1 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_1);
|
|
break;
|
|
|
|
case 0x98: /* TYA */
|
|
TYA();
|
|
break;
|
|
|
|
case 0x99: /* STA $nnnn,Y */
|
|
STA(p2, CYCLES_0, CYCLES_0, SIZE_3, STORE_ABS_Y);
|
|
break;
|
|
|
|
case 0x9a: /* TXS */
|
|
TXS();
|
|
break;
|
|
|
|
case 0x9c: /* STZ $nnnn */
|
|
STZ(p2, CYCLES_1, SIZE_3, STORE_ABS);
|
|
break;
|
|
|
|
case 0x9d: /* STA $nnnn,X */
|
|
STA(p2, CYCLES_0, CYCLES_0, SIZE_3, STORE_ABS_X);
|
|
break;
|
|
|
|
case 0x9e: /* STZ $nnnn,X */
|
|
STZ(p2, CYCLES_0, SIZE_3, STORE_ABS_X);
|
|
break;
|
|
|
|
case 0x9f: /* BBS1 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_1);
|
|
break;
|
|
|
|
case 0xa0: /* LDY #$nn */
|
|
LDY(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xa1: /* LDA ($nn,X) */
|
|
LDA(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xa2: /* LDX #$nn */
|
|
LDX(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xa4: /* LDY $nn */
|
|
LDY(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xa5: /* LDA $nn */
|
|
LDA(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xa6: /* LDX $nn */
|
|
LDX(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xa7: /* SMB2 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_2);
|
|
break;
|
|
|
|
case 0xa8: /* TAY */
|
|
TAY();
|
|
break;
|
|
|
|
case 0xa9: /* LDA #$nn */
|
|
LDA(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xaa: /* TAX */
|
|
TAX();
|
|
break;
|
|
|
|
case 0xac: /* LDY $nnnn */
|
|
LDY(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xad: /* LDA $nnnn */
|
|
LDA(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xae: /* LDX $nnnn */
|
|
LDX(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xaf: /* BBS2 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_2);
|
|
break;
|
|
|
|
case 0xb0: /* BCS $nnnn */
|
|
BRANCH(LOCAL_CARRY(), p1);
|
|
break;
|
|
|
|
case 0xb1: /* LDA ($nn),Y */
|
|
LDA(LOAD_IND_Y_BANK(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xb2: /* LDA ($nn) */
|
|
LDA(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xb4: /* LDY $nn,X */
|
|
LDY(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0xb5: /* LDA $nn,X */
|
|
LDA(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0xb6: /* LDX $nn,Y */
|
|
LDX(LOAD_ZERO_Y(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0xb7: /* SMB3 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_3);
|
|
break;
|
|
|
|
case 0xb8: /* CLV */
|
|
CLV();
|
|
break;
|
|
|
|
case 0xb9: /* LDA $nnnn,Y */
|
|
LDA(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xba: /* TSX */
|
|
TSX();
|
|
break;
|
|
|
|
case 0xbc: /* LDY $nnnn,X */
|
|
LDY(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xbd: /* LDA $nnnn,X */
|
|
LDA(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xbe: /* LDX $nnnn,Y */
|
|
LDX(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xbf: /* BBS3 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_3);
|
|
break;
|
|
|
|
case 0xc0: /* CPY #$nn */
|
|
CPY(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xc1: /* CMP ($nn,X) */
|
|
CMP(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xc4: /* CPY $nn */
|
|
CPY(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xc5: /* CMP $nn */
|
|
CMP(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xc6: /* DEC $nn */
|
|
DEC(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0xc7: /* SMB4 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_4);
|
|
break;
|
|
|
|
case 0xc8: /* INY */
|
|
INY();
|
|
break;
|
|
|
|
case 0xc9: /* CMP #$nn */
|
|
CMP(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xca: /* DEX */
|
|
DEX();
|
|
break;
|
|
|
|
case 0xcb: /* WAI (WDC65C02) / single byte, single cycle NOP (R65C02/65SC02) */
|
|
WAI();
|
|
break;
|
|
|
|
case 0xcc: /* CPY $nnnn */
|
|
CPY(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xcd: /* CMP $nnnn */
|
|
CMP(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xce: /* DEC $nnnn */
|
|
DEC(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0xcf: /* BBS4 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_4);
|
|
break;
|
|
|
|
case 0xd0: /* BNE $nnnn */
|
|
BRANCH(!LOCAL_ZERO(), p1);
|
|
break;
|
|
|
|
case 0xd1: /* CMP ($nn),Y */
|
|
CMP(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xd2: /* CMP ($nn) */
|
|
CMP(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xd5: /* CMP $nn,X */
|
|
CMP(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0xd6: /* DEC $nn,X */
|
|
DEC((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0xd7: /* SMB5 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_5);
|
|
break;
|
|
|
|
case 0xd8: /* CLD */
|
|
CLD();
|
|
break;
|
|
|
|
case 0xd9: /* CMP $nnnn,Y */
|
|
CMP(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xda: /* PHX */
|
|
PHX();
|
|
break;
|
|
|
|
case 0xdb: /* STP (WDC65C02) / single byte, single cycle NOP (R65C02/65SC02) */
|
|
STP();
|
|
break;
|
|
|
|
case 0xdd: /* CMP $nnnn,X */
|
|
CMP(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xde: /* DEC $nnnn,X */
|
|
DEC(p2, CYCLES_1, SIZE_3, LOAD_ABS_X_RMW, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0xdf: /* BBS5 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_5);
|
|
break;
|
|
|
|
case 0xe0: /* CPX #$nn */
|
|
CPX(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xe1: /* SBC ($nn,X) */
|
|
SBC(LOAD_IND_X(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xe4: /* CPX $nn */
|
|
CPX(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xe5: /* SBC $nn */
|
|
SBC(LOAD_ZERO(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xe6: /* INC $nn */
|
|
INC(p1, CYCLES_3, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0xe7: /* SMB6 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_6);
|
|
break;
|
|
|
|
case 0xe8: /* INX */
|
|
INX();
|
|
break;
|
|
|
|
case 0xe9: /* SBC #$nn */
|
|
SBC(p1, CYCLES_0, SIZE_2);
|
|
break;
|
|
|
|
case 0xea: /* NOP */
|
|
NOP();
|
|
break;
|
|
|
|
case 0xec: /* CPX $nnnn */
|
|
CPX(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xed: /* SBC $nnnn */
|
|
SBC(LOAD(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xee: /* INC $nnnn */
|
|
INC(p2, CYCLES_1, SIZE_3, LOAD_ABS, STORE_ABS_RRW);
|
|
break;
|
|
|
|
case 0xef: /* BBS6 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_6);
|
|
break;
|
|
|
|
case 0xf0: /* BEQ $nnnn */
|
|
BRANCH(LOCAL_ZERO(), p1);
|
|
break;
|
|
|
|
case 0xf1: /* SBC ($nn),Y */
|
|
SBC(LOAD_IND_Y(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xf2: /* SBC ($nn) */
|
|
SBC(LOAD_INDIRECT(p1), CYCLES_1, SIZE_2);
|
|
break;
|
|
|
|
case 0xf5: /* SBC $nn,X */
|
|
SBC(LOAD_ZERO_X(p1), CYCLES_2, SIZE_2);
|
|
break;
|
|
|
|
case 0xf6: /* INC $nn,X */
|
|
INC((p1 + reg_x) & 0xff, CYCLES_4, SIZE_2, LOAD_ZERO, STORE_ZERO);
|
|
break;
|
|
|
|
case 0xf7: /* SMB7 $nn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
SMB(BIT_7);
|
|
break;
|
|
|
|
case 0xf8: /* SED */
|
|
SED();
|
|
break;
|
|
|
|
case 0xf9: /* SBC $nnnn,Y */
|
|
SBC(LOAD_ABS_Y(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xfa: /* PLX */
|
|
PLX();
|
|
break;
|
|
|
|
case 0xfd: /* SBC $nnnn,X */
|
|
SBC(LOAD_ABS_X(p2), CYCLES_1, SIZE_3);
|
|
break;
|
|
|
|
case 0xfe: /* INC $nnnn,X */
|
|
INC(p2, CYCLES_1, SIZE_3, LOAD_ABS_X_RMW, STORE_ABS_X_RRW);
|
|
break;
|
|
|
|
case 0xff: /* BBS7 $nn,$nnnn (65C02) / single byte, single cycle NOP (65SC02) */
|
|
BBS(BIT_7);
|
|
break;
|
|
}
|
|
}
|
|
}
|