/*
 *  CPU_emulline.h - 6510/6502 emulation core (body of
 *                   EmulateLine() function, the same for
 *                   both 6510 and 6502)
 *
 *  Frodo (C) 1994-1997,2002-2004 Christian Bauer
 *
 *  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
 */


/*
 *  Addressing mode macros
 */

// Read immediate operand
#if PC_IS_POINTER
#define read_byte_imm() (*pc++)
#else
#define read_byte_imm() read_byte(pc++)
#endif

// Read zeropage operand address
#define read_adr_zero() ((uint16)read_byte_imm())

// Read zeropage x-indexed operand address
#define read_adr_zero_x() ((read_byte_imm() + x) & 0xff)

// Read zeropage y-indexed operand address
#define read_adr_zero_y() ((read_byte_imm() + y) & 0xff)

// Read absolute operand address (uses adr!)
#if PC_IS_POINTER
#if LITTLE_ENDIAN_UNALIGNED
#define	read_adr_abs() (adr = *(UWORD *)pc, pc+=2, adr)
#else
#define	read_adr_abs() (adr = ((*(pc+1)) << 8) | *pc, pc+=2, adr)
#endif
#else
#define read_adr_abs() (adr = read_word(pc), pc+=2, adr)
#endif

// Read absolute x-indexed operand address
#define read_adr_abs_x() (read_adr_abs() + x)

// Read absolute y-indexed operand address
#define read_adr_abs_y() (read_adr_abs() + y)

// Read indexed indirect operand address
#define read_adr_ind_x() (read_zp_word(read_byte_imm() + x))

// Read indirect indexed operand address
#define read_adr_ind_y() (read_zp_word(read_byte_imm()) + y)

// Read zeropage operand
#define read_byte_zero() read_zp(read_adr_zero())

// Read zeropage x-indexed operand
#define read_byte_zero_x() read_zp(read_adr_zero_x())

// Read zeropage y-indexed operand
#define read_byte_zero_y() read_zp(read_adr_zero_y())

// Read absolute operand
#define read_byte_abs() read_byte(read_adr_abs())

#if PRECISE_CPU_CYCLES
// Acount for cyles due to crossing page boundaries
#define page_plus(exp, reg) \
	(adr = exp, page_cycles = (adr & 0xff) + reg >= 0x100, adr + reg)

// Read absolute x-indexed operand
#define read_byte_abs_x() read_byte(page_plus(read_adr_abs(), x))

// Read absolute x-indexed operand
#define read_byte_abs_y() read_byte(page_plus(read_adr_abs(), y))

// Read indirect y-indexed operand
#define read_byte_ind_y() read_byte(page_plus(read_zp_word(read_byte_imm()), y))

#else

// Read absolute x-indexed operand
#define read_byte_abs_x() read_byte(read_adr_abs_x())

// Read absolute x-indexed operand
#define read_byte_abs_y() read_byte(read_adr_abs_y())

// Read indirect y-indexed operand
#define read_byte_ind_y() read_byte(read_adr_ind_y())
#endif

// Read indexed indirect operand
#define read_byte_ind_x() read_byte(read_adr_ind_x())


/*
 *  Set N and Z flags according to byte
 */

#define set_nz(x) (z_flag = n_flag = (x))


/*
 * End of opcode, decrement cycles left
 */

#define ENDOP(cyc) last_cycles = cyc; break;


	// Main opcode fetch/execute loop
#if PRECISE_CPU_CYCLES
	if (cycles_left != 1)
		cycles_left -= borrowed_cycles;
	int page_cycles = 0;
	for (;;) {
		if (last_cycles) {
			last_cycles += page_cycles;
			page_cycles = 0;
#if PRECISE_CIA_CYCLES && !defined(IS_CPU_1541)
			TheCIA1->EmulateLine(last_cycles);
			TheCIA2->EmulateLine(last_cycles);
#endif
		}
		if ((cycles_left -= last_cycles) < 0) {
			borrowed_cycles = -cycles_left;
			break;
		}
#else
	while ((cycles_left -= last_cycles) >= 0) {
#endif

		switch (read_byte_imm()) {


		// Load group
		case 0xa9:	// LDA #imm
			set_nz(a = read_byte_imm());
			ENDOP(2);

		case 0xa5:	// LDA zero
			set_nz(a = read_byte_zero());
			ENDOP(3);

		case 0xb5:	// LDA zero,X
			set_nz(a = read_byte_zero_x());
			ENDOP(4);

		case 0xad:	// LDA abs
			set_nz(a = read_byte_abs());
			ENDOP(4);

		case 0xbd:	// LDA abs,X
			set_nz(a = read_byte_abs_x());
			ENDOP(4);

		case 0xb9:	// LDA abs,Y
			set_nz(a = read_byte_abs_y());
			ENDOP(4);

		case 0xa1:	// LDA (ind,X)
			set_nz(a = read_byte_ind_x());
			ENDOP(6);
		
		case 0xb1:	// LDA (ind),Y
			set_nz(a = read_byte_ind_y());
			ENDOP(5);

		case 0xa2:	// LDX #imm
			set_nz(x = read_byte_imm());
			ENDOP(2);

		case 0xa6:	// LDX zero
			set_nz(x = read_byte_zero());
			ENDOP(3);

		case 0xb6:	// LDX zero,Y
			set_nz(x = read_byte_zero_y());
			ENDOP(4);

		case 0xae:	// LDX abs
			set_nz(x = read_byte_abs());
			ENDOP(4);

		case 0xbe:	// LDX abs,Y
			set_nz(x = read_byte_abs_y());
			ENDOP(4);

		case 0xa0:	// LDY #imm
			set_nz(y = read_byte_imm());
			ENDOP(2);

		case 0xa4:	// LDY zero
			set_nz(y = read_byte_zero());
			ENDOP(3);

		case 0xb4:	// LDY zero,X
			set_nz(y = read_byte_zero_x());
			ENDOP(4);

		case 0xac:	// LDY abs
			set_nz(y = read_byte_abs());
			ENDOP(4);

		case 0xbc:	// LDY abs,X
			set_nz(y = read_byte_abs_x());
			ENDOP(4);


		// Store group
		case 0x85:	// STA zero
			write_byte(read_adr_zero(), a);
			ENDOP(3);

		case 0x95:	// STA zero,X
			write_byte(read_adr_zero_x(), a);
			ENDOP(4);

		case 0x8d:	// STA abs
			write_byte(read_adr_abs(), a);
			ENDOP(4);

		case 0x9d:	// STA abs,X
			write_byte(read_adr_abs_x(), a);
			ENDOP(5);

		case 0x99:	// STA abs,Y
			write_byte(read_adr_abs_y(), a);
			ENDOP(5);

		case 0x81:	// STA (ind,X)
			write_byte(read_adr_ind_x(), a);
			ENDOP(6);

		case 0x91:	// STA (ind),Y
			write_byte(read_adr_ind_y(), a);
			ENDOP(6);

		case 0x86:	// STX zero
			write_byte(read_adr_zero(), x);
			ENDOP(3);

		case 0x96:	// STX zero,Y
			write_byte(read_adr_zero_y(), x);
			ENDOP(4);

		case 0x8e:	// STX abs
			write_byte(read_adr_abs(), x);
			ENDOP(4);

		case 0x84:	// STY zero
			write_byte(read_adr_zero(), y);
			ENDOP(3);

		case 0x94:	// STY zero,X
			write_byte(read_adr_zero_x(), y);
			ENDOP(4);

		case 0x8c:	// STY abs
			write_byte(read_adr_abs(), y);
			ENDOP(4);


		// Transfer group
		case 0xaa:	// TAX
			set_nz(x = a);
			ENDOP(2);

		case 0x8a:	// TXA
			set_nz(a = x);
			ENDOP(2);

		case 0xa8:	// TAY
			set_nz(y = a);
			ENDOP(2);

		case 0x98:	// TYA
			set_nz(a = y);
			ENDOP(2);

		case 0xba:	// TSX
			set_nz(x = sp);
			ENDOP(2);

		case 0x9a:	// TXS
			sp = x;
			ENDOP(2);


		// Arithmetic group
		case 0x69:	// ADC #imm
			do_adc(read_byte_imm());
			ENDOP(2);

		case 0x65:	// ADC zero
			do_adc(read_byte_zero());
			ENDOP(3);

		case 0x75:	// ADC zero,X
			do_adc(read_byte_zero_x());
			ENDOP(4);

		case 0x6d:	// ADC abs
			do_adc(read_byte_abs());
			ENDOP(4);

		case 0x7d:	// ADC abs,X
			do_adc(read_byte_abs_x());
			ENDOP(4);

		case 0x79:	// ADC abs,Y
			do_adc(read_byte_abs_y());
			ENDOP(4);

		case 0x61:	// ADC (ind,X)
			do_adc(read_byte_ind_x());
			ENDOP(6);

		case 0x71:	// ADC (ind),Y
			do_adc(read_byte_ind_y());
			ENDOP(5);

		case 0xe9:	// SBC #imm
		case 0xeb:	// Undocumented opcode
			do_sbc(read_byte_imm());
			ENDOP(2);

		case 0xe5:	// SBC zero
			do_sbc(read_byte_zero());
			ENDOP(3);

		case 0xf5:	// SBC zero,X
			do_sbc(read_byte_zero_x());
			ENDOP(4);

		case 0xed:	// SBC abs
			do_sbc(read_byte_abs());
			ENDOP(4);

		case 0xfd:	// SBC abs,X
			do_sbc(read_byte_abs_x());
			ENDOP(4);

		case 0xf9:	// SBC abs,Y
			do_sbc(read_byte_abs_y());
			ENDOP(4);

		case 0xe1:	// SBC (ind,X)
			do_sbc(read_byte_ind_x());
			ENDOP(6);

		case 0xf1:	// SBC (ind),Y
			do_sbc(read_byte_ind_y());
			ENDOP(5);


		// Increment/decrement group
		case 0xe8:	// INX
			set_nz(++x);
			ENDOP(2);

		case 0xca:	// DEX
			set_nz(--x);
			ENDOP(2);

		case 0xc8:	// INY
			set_nz(++y);
			ENDOP(2);

		case 0x88:	// DEY
			set_nz(--y);
			ENDOP(2);

		case 0xe6:	// INC zero
			adr = read_adr_zero();
			write_zp(adr, set_nz(read_zp(adr) + 1));
			ENDOP(5);

		case 0xf6:	// INC zero,X
			adr = read_adr_zero_x();
			write_zp(adr, set_nz(read_zp(adr) + 1));
			ENDOP(6);

		case 0xee:	// INC abs
			adr = read_adr_abs();
			write_byte(adr, set_nz(read_byte(adr) + 1));
			ENDOP(6);

		case 0xfe:	// INC abs,X
			adr = read_adr_abs_x();
			write_byte(adr, set_nz(read_byte(adr) + 1));
			ENDOP(7);

		case 0xc6:	// DEC zero
			adr = read_adr_zero();
			write_zp(adr, set_nz(read_zp(adr) - 1));
			ENDOP(5);

		case 0xd6:	// DEC zero,X
			adr = read_adr_zero_x();
			write_zp(adr, set_nz(read_zp(adr) - 1));
			ENDOP(6);

		case 0xce:	// DEC abs
			adr = read_adr_abs();
			write_byte(adr, set_nz(read_byte(adr) - 1));
			ENDOP(6);

		case 0xde:	// DEC abs,X
			adr = read_adr_abs_x();
			write_byte(adr, set_nz(read_byte(adr) - 1));
			ENDOP(7);


		// Logic group
		case 0x29:	// AND #imm
			set_nz(a &= read_byte_imm());
			ENDOP(2);

		case 0x25:	// AND zero
			set_nz(a &= read_byte_zero());
			ENDOP(3);

		case 0x35:	// AND zero,X
			set_nz(a &= read_byte_zero_x());
			ENDOP(4);

		case 0x2d:	// AND abs
			set_nz(a &= read_byte_abs());
			ENDOP(4);

		case 0x3d:	// AND abs,X
			set_nz(a &= read_byte_abs_x());
			ENDOP(4);

		case 0x39:	// AND abs,Y
			set_nz(a &= read_byte_abs_y());
			ENDOP(4);

		case 0x21:	// AND (ind,X)
			set_nz(a &= read_byte_ind_x());
			ENDOP(6);

		case 0x31:	// AND (ind),Y
			set_nz(a &= read_byte_ind_y());
			ENDOP(5);

		case 0x09:	// ORA #imm
			set_nz(a |= read_byte_imm());
			ENDOP(2);

		case 0x05:	// ORA zero
			set_nz(a |= read_byte_zero());
			ENDOP(3);

		case 0x15:	// ORA zero,X
			set_nz(a |= read_byte_zero_x());
			ENDOP(4);

		case 0x0d:	// ORA abs
			set_nz(a |= read_byte_abs());
			ENDOP(4);

		case 0x1d:	// ORA abs,X
			set_nz(a |= read_byte_abs_x());
			ENDOP(4);

		case 0x19:	// ORA abs,Y
			set_nz(a |= read_byte_abs_y());
			ENDOP(4);

		case 0x01:	// ORA (ind,X)
			set_nz(a |= read_byte_ind_x());
			ENDOP(6);

		case 0x11:	// ORA (ind),Y
			set_nz(a |= read_byte_ind_y());
			ENDOP(5);

		case 0x49:	// EOR #imm
			set_nz(a ^= read_byte_imm());
			ENDOP(2);

		case 0x45:	// EOR zero
			set_nz(a ^= read_byte_zero());
			ENDOP(3);

		case 0x55:	// EOR zero,X
			set_nz(a ^= read_byte_zero_x());
			ENDOP(4);

		case 0x4d:	// EOR abs
			set_nz(a ^= read_byte_abs());
			ENDOP(4);

		case 0x5d:	// EOR abs,X
			set_nz(a ^= read_byte_abs_x());
			ENDOP(4);

		case 0x59:	// EOR abs,Y
			set_nz(a ^= read_byte_abs_y());
			ENDOP(4);

		case 0x41:	// EOR (ind,X)
			set_nz(a ^= read_byte_ind_x());
			ENDOP(6);

		case 0x51:	// EOR (ind),Y
			set_nz(a ^= read_byte_ind_y());
			ENDOP(5);


		// Compare group
		case 0xc9:	// CMP #imm
			set_nz(adr = a - read_byte_imm());
			c_flag = adr < 0x100;
			ENDOP(2);

		case 0xc5:	// CMP zero
			set_nz(adr = a - read_byte_zero());
			c_flag = adr < 0x100;
			ENDOP(3);

		case 0xd5:	// CMP zero,X
			set_nz(adr = a - read_byte_zero_x());
			c_flag = adr < 0x100;
			ENDOP(4);

		case 0xcd:	// CMP abs
			set_nz(adr = a - read_byte_abs());
			c_flag = adr < 0x100;
			ENDOP(4);

		case 0xdd:	// CMP abs,X
			set_nz(adr = a - read_byte_abs_x());
			c_flag = adr < 0x100;
			ENDOP(4);

		case 0xd9:	// CMP abs,Y
			set_nz(adr = a - read_byte_abs_y());
			c_flag = adr < 0x100;
			ENDOP(4);

		case 0xc1:	// CMP (ind,X)
			set_nz(adr = a - read_byte_ind_x());
			c_flag = adr < 0x100;
			ENDOP(6);

		case 0xd1:	// CMP (ind),Y
			set_nz(adr = a - read_byte_ind_y());
			c_flag = adr < 0x100;
			ENDOP(5);

		case 0xe0:	// CPX #imm
			set_nz(adr = x - read_byte_imm());
			c_flag = adr < 0x100;
			ENDOP(2);

		case 0xe4:	// CPX zero
			set_nz(adr = x - read_byte_zero());
			c_flag = adr < 0x100;
			ENDOP(3);

		case 0xec:	// CPX abs
			set_nz(adr = x - read_byte_abs());
			c_flag = adr < 0x100;
			ENDOP(4);

		case 0xc0:	// CPY #imm
			set_nz(adr = y - read_byte_imm());
			c_flag = adr < 0x100;
			ENDOP(2);

		case 0xc4:	// CPY zero
			set_nz(adr = y - read_byte_zero());
			c_flag = adr < 0x100;
			ENDOP(3);

		case 0xcc:	// CPY abs
			set_nz(adr = y - read_byte_abs());
			c_flag = adr < 0x100;
			ENDOP(4);


		// Bit-test group
		case 0x24:	// BIT zero
			z_flag = a & (tmp = read_byte_zero());
			n_flag = tmp;
			v_flag = tmp & 0x40;
			ENDOP(3);

		case 0x2c:	// BIT abs
			z_flag = a & (tmp = read_byte_abs());
			n_flag = tmp;
			v_flag = tmp & 0x40;
			ENDOP(4);


		// Shift/rotate group
		case 0x0a:	// ASL A
			c_flag = a & 0x80;
			set_nz(a <<= 1);
			ENDOP(2);

		case 0x06:	// ASL zero
			tmp = read_zp(adr = read_adr_zero());
			c_flag = tmp & 0x80;
			write_zp(adr, set_nz(tmp << 1));
			ENDOP(5);

		case 0x16:	// ASL zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			c_flag = tmp & 0x80;
			write_zp(adr, set_nz(tmp << 1));
			ENDOP(6);

		case 0x0e:	// ASL abs
			tmp = read_byte(adr = read_adr_abs());
			c_flag = tmp & 0x80;
			write_byte(adr, set_nz(tmp << 1));
			ENDOP(6);

		case 0x1e:	// ASL abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			c_flag = tmp & 0x80;
			write_byte(adr, set_nz(tmp << 1));
			ENDOP(7);

		case 0x4a:	// LSR A
			c_flag = a & 0x01;
			set_nz(a >>= 1);
			ENDOP(2);

		case 0x46:	// LSR zero
			tmp = read_zp(adr = read_adr_zero());
			c_flag = tmp & 0x01;
			write_zp(adr, set_nz(tmp >> 1));
			ENDOP(5);

		case 0x56:	// LSR zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			c_flag = tmp & 0x01;
			write_zp(adr, set_nz(tmp >> 1));
			ENDOP(6);

		case 0x4e:	// LSR abs
			tmp = read_byte(adr = read_adr_abs());
			c_flag = tmp & 0x01;
			write_byte(adr, set_nz(tmp >> 1));
			ENDOP(6);

		case 0x5e:	// LSR abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			c_flag = tmp & 0x01;
			write_byte(adr, set_nz(tmp >> 1));
			ENDOP(7);

		case 0x2a:	// ROL A
			tmp2 = a & 0x80;
			set_nz(a = c_flag ? (a << 1) | 0x01 : a << 1);
			c_flag = tmp2;
			ENDOP(2);

		case 0x26:	// ROL zero
			tmp = read_zp(adr = read_adr_zero());
			tmp2 = tmp & 0x80;
			write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1));
			c_flag = tmp2;
			ENDOP(5);

		case 0x36:	// ROL zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			tmp2 = tmp & 0x80;
			write_zp(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1));
			c_flag = tmp2;
			ENDOP(6);

		case 0x2e:	// ROL abs
			tmp = read_byte(adr = read_adr_abs());
			tmp2 = tmp & 0x80;
			write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1));
			c_flag = tmp2;
			ENDOP(6);

		case 0x3e:	// ROL abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			tmp2 = tmp & 0x80;
			write_byte(adr, set_nz(c_flag ? (tmp << 1) | 0x01 : tmp << 1));
			c_flag = tmp2;
			ENDOP(7);

		case 0x6a:	// ROR A
			tmp2 = a & 0x01;
			set_nz(a = (c_flag ? (a >> 1) | 0x80 : a >> 1));
			c_flag = tmp2;
			ENDOP(2);

		case 0x66:	// ROR zero
			tmp = read_zp(adr = read_adr_zero());
			tmp2 = tmp & 0x01;
			write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1));
			c_flag = tmp2;
			ENDOP(5);

		case 0x76:	// ROR zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			tmp2 = tmp & 0x01;
			write_zp(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1));
			c_flag = tmp2;
			ENDOP(6);

		case 0x6e:	// ROR abs
			tmp = read_byte(adr = read_adr_abs());
			tmp2 = tmp & 0x01;
			write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1));
			c_flag = tmp2;
			ENDOP(6);

		case 0x7e:	// ROR abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			tmp2 = tmp & 0x01;
			write_byte(adr, set_nz(c_flag ? (tmp >> 1) | 0x80 : tmp >> 1));
			c_flag = tmp2;
			ENDOP(7);


		// Stack group
		case 0x48:	// PHA
			push_byte(a);
			ENDOP(3);

		case 0x68:	// PLA
			set_nz(a = pop_byte());
			ENDOP(4);

		case 0x08:	// PHP
			push_flags(true);
			ENDOP(3);

		case 0x28:	// PLP
			pop_flags();
			if (interrupt.intr_any && !i_flag)
				goto handle_int;
			ENDOP(4);


		// Jump/branch group
		case 0x4c:	// JMP abs
			adr = read_adr_abs();
			jump(adr);
			ENDOP(3);

		case 0x6c:	// JMP (ind)
			adr = read_adr_abs();
			adr = read_byte(adr) | (read_byte((adr + 1) & 0xff | adr & 0xff00) << 8);
			jump(adr);
			ENDOP(5);

		case 0x20:	// JSR abs
#if PC_IS_POINTER
			push_byte((pc-pc_base+1) >> 8); push_byte(pc-pc_base+1);
#else
			push_byte(pc+1 >> 8); push_byte(pc+1);
#endif
			adr = read_adr_abs();
			jump(adr);
			ENDOP(6);

		case 0x60:	// RTS
			adr = pop_byte();	// Split because of pop_byte ++sp side-effect
			adr = (adr | pop_byte() << 8) + 1;
			jump(adr);
			ENDOP(6);

		case 0x40:	// RTI
			pop_flags();
			adr = pop_byte();	// Split because of pop_byte ++sp side-effect
			adr = adr | pop_byte() << 8;
			jump(adr);
			if (interrupt.intr_any && !i_flag)
				goto handle_int;
			ENDOP(6);

		case 0x00:	// BRK
#if PC_IS_POINTER
			push_byte((pc+1-pc_base) >> 8); push_byte(pc+1-pc_base);
#else
			push_byte((pc+1) >> 8); push_byte(pc+1);
#endif
			push_flags(true);
			i_flag = true;
			adr = read_word(0xfffe);
			jump(adr);
			ENDOP(7);

#if PC_IS_POINTER
#if PRECISE_CPU_CYCLES
#define Branch(flag) \
	if (flag) { \
		pc += (int8)*pc + 1; \
		if (((pc-pc_base) ^ (old_pc - pc_base)) & 0xff00) { \
			ENDOP(4); \
		} else { \
			ENDOP(3); \
		} \
	} else { \
		pc++; \
		ENDOP(2); \
	}
#else
#define Branch(flag) \
	if (flag) { \
		pc += (int8)*pc + 1; \
		ENDOP(3); \
	} else { \
		pc++; \
		ENDOP(2); \
	}
#endif
#else
#define Branch(flag) \
	if (flag) { \
		uint16 old_pc = pc; \
		pc += (int8)read_byte(pc) + 1; \
		if ((pc ^ old_pc) & 0xff00) { \
			ENDOP(4); \
		} else { \
			ENDOP(3); \
		} \
	} else { \
		pc++; \
		ENDOP(2); \
	}
#endif

		case 0xb0:	// BCS rel
			Branch(c_flag);

		case 0x90:	// BCC rel
			Branch(!c_flag);

		case 0xf0:	// BEQ rel
			Branch(!z_flag);

		case 0xd0:	// BNE rel
			Branch(z_flag);

		case 0x70:	// BVS rel
#ifndef IS_CPU_1541
			Branch(v_flag);
#else
			Branch((via2_pcr & 0x0e) == 0x0e ? 1 : v_flag);	// GCR byte ready flag
#endif

		case 0x50:	// BVC rel
#ifndef IS_CPU_1541
			Branch(!v_flag);
#else
			Branch(!((via2_pcr & 0x0e) == 0x0e) ? 0 : v_flag);	// GCR byte ready flag
#endif

		case 0x30:	// BMI rel
			Branch(n_flag & 0x80);

		case 0x10:	// BPL rel
			Branch(!(n_flag & 0x80));


		// Flags group
		case 0x38:	// SEC
			c_flag = true;
			ENDOP(2);

		case 0x18:	// CLC
			c_flag = false;
			ENDOP(2);

		case 0xf8:	// SED
			d_flag = true;
			ENDOP(2);

		case 0xd8:	// CLD
			d_flag = false;
			ENDOP(2);

		case 0x78:	// SEI
			i_flag = true;
			ENDOP(2);

		case 0x58:	// CLI
			i_flag = false;
			if (interrupt.intr_any)
				goto handle_int;
			ENDOP(2);

		case 0xb8:	// CLV
			v_flag = false;
			ENDOP(2);


		// NOP group
		case 0xea:	// NOP
			ENDOP(2);


/*
 * Undocumented opcodes start here
 */

		// NOP group
		case 0x1a:	// NOP
		case 0x3a:
		case 0x5a:
		case 0x7a:
		case 0xda:
		case 0xfa:
			ENDOP(2);

		case 0x80:	// NOP #imm
		case 0x82:
		case 0x89:
		case 0xc2:
		case 0xe2:
			pc++;
			ENDOP(2);

		case 0x04:	// NOP zero
		case 0x44:
		case 0x64:
			pc++;
			ENDOP(3);

		case 0x14:	// NOP zero,X
		case 0x34:
		case 0x54:
		case 0x74:
		case 0xd4:
		case 0xf4:
			pc++;
			ENDOP(4);

		case 0x0c:	// NOP abs
			pc+=2;
			ENDOP(4);

		case 0x1c:	// NOP abs,X
		case 0x3c:
		case 0x5c:
		case 0x7c:
		case 0xdc:
		case 0xfc:
#if PRECISE_CPU_CYCLES
			read_byte_abs_x();
#else
			pc+=2;
#endif
			ENDOP(4);


		// Load A/X group
		case 0xa7:	// LAX zero
			set_nz(a = x = read_byte_zero());
			ENDOP(3);

		case 0xb7:	// LAX zero,Y
			set_nz(a = x = read_byte_zero_y());
			ENDOP(4);

		case 0xaf:	// LAX abs
			set_nz(a = x = read_byte_abs());
			ENDOP(4);

		case 0xbf:	// LAX abs,Y
			set_nz(a = x = read_byte_abs_y());
			ENDOP(4);

		case 0xa3:	// LAX (ind,X)
			set_nz(a = x = read_byte_ind_x());
			ENDOP(6);

		case 0xb3:	// LAX (ind),Y
			set_nz(a = x = read_byte_ind_y());
			ENDOP(5);


		// Store A/X group
		case 0x87:	// SAX zero
			write_byte(read_adr_zero(), a & x);
			ENDOP(3);

		case 0x97:	// SAX zero,Y
			write_byte(read_adr_zero_y(), a & x);
			ENDOP(4);

		case 0x8f:	// SAX abs
			write_byte(read_adr_abs(), a & x);
			ENDOP(4);

		case 0x83:	// SAX (ind,X)
			write_byte(read_adr_ind_x(), a & x);
			ENDOP(6);


		// ASL/ORA group
#define ShiftLeftOr \
	c_flag = tmp & 0x80; \
	tmp <<= 1; \
	set_nz(a |= tmp);

		case 0x07:	// SLO zero
			tmp = read_zp(adr = read_adr_zero());
			ShiftLeftOr;
			write_zp(adr, tmp);
			ENDOP(5);

		case 0x17:	// SLO zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			ShiftLeftOr;
			write_zp(adr, tmp);
			ENDOP(6);

		case 0x0f:	// SLO abs
			tmp = read_byte(adr = read_adr_abs());
			ShiftLeftOr;
			write_byte(adr, tmp);
			ENDOP(6);

		case 0x1f:	// SLO abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			ShiftLeftOr;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x1b:	// SLO abs,Y
			tmp = read_byte(adr = read_adr_abs_y());
			ShiftLeftOr;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x03:	// SLO (ind,X)
			tmp = read_byte(adr = read_adr_ind_x());
			ShiftLeftOr;
			write_byte(adr, tmp);
			ENDOP(8);

		case 0x13:	// SLO (ind),Y
			tmp = read_byte(adr = read_adr_ind_y());
			ShiftLeftOr;
			write_byte(adr, tmp);
			ENDOP(8);


		// ROL/AND group
#define RoLeftAnd \
	tmp2 = tmp & 0x80; \
	tmp = c_flag ? (tmp << 1) | 0x01 : tmp << 1; \
	set_nz(a &= tmp); \
	c_flag = tmp2;

		case 0x27:	// RLA zero
			tmp = read_zp(adr = read_adr_zero());
			RoLeftAnd;
			write_zp(adr, tmp);
			ENDOP(5);

		case 0x37:	// RLA zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			RoLeftAnd;
			write_zp(adr, tmp);
			ENDOP(6);

		case 0x2f:	// RLA abs
			tmp = read_byte(adr = read_adr_abs());
			RoLeftAnd;
			write_byte(adr, tmp);
			ENDOP(6);

		case 0x3f:	// RLA abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			RoLeftAnd;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x3b:	// RLA abs,Y
			tmp = read_byte(adr = read_adr_abs_y());
			RoLeftAnd;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x23:	// RLA (ind,X)
			tmp = read_byte(adr = read_adr_ind_x());
			RoLeftAnd;
			write_byte(adr, tmp);
			ENDOP(8);

		case 0x33:	// RLA (ind),Y
			tmp = read_byte(adr = read_adr_ind_y());
			RoLeftAnd;
			write_byte(adr, tmp);
			ENDOP(8);


		// LSR/EOR group
#define ShiftRightEor \
	c_flag = tmp & 0x01; \
	tmp >>= 1; \
	set_nz(a ^= tmp);

		case 0x47:	// SRE zero
			tmp = read_zp(adr = read_adr_zero());
			ShiftRightEor;
			write_zp(adr, tmp);
			ENDOP(5);

		case 0x57:	// SRE zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			ShiftRightEor;
			write_zp(adr, tmp);
			ENDOP(6);

		case 0x4f:	// SRE abs
			tmp = read_byte(adr = read_adr_abs());
			ShiftRightEor;
			write_byte(adr, tmp);
			ENDOP(6);

		case 0x5f:	// SRE abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			ShiftRightEor;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x5b:	// SRE abs,Y
			tmp = read_byte(adr = read_adr_abs_y());
			ShiftRightEor;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x43:	// SRE (ind,X)
			tmp = read_byte(adr = read_adr_ind_x());
			ShiftRightEor;
			write_byte(adr, tmp);
			ENDOP(8);

		case 0x53:	// SRE (ind),Y
			tmp = read_byte(adr = read_adr_ind_y());
			ShiftRightEor;
			write_byte(adr, tmp);
			ENDOP(8);


		// ROR/ADC group
#define RoRightAdc \
	tmp2 = tmp & 0x01; \
	tmp = c_flag ? (tmp >> 1) | 0x80 : tmp >> 1; \
	c_flag = tmp2; \
	do_adc(tmp);

		case 0x67:	// RRA zero
			tmp = read_zp(adr = read_adr_zero());
			RoRightAdc;
			write_zp(adr, tmp);
			ENDOP(5);

		case 0x77:	// RRA zero,X
			tmp = read_zp(adr = read_adr_zero_x());
			RoRightAdc;
			write_zp(adr, tmp);
			ENDOP(6);

		case 0x6f:	// RRA abs
			tmp = read_byte(adr = read_adr_abs());
			RoRightAdc;
			write_byte(adr, tmp);
			ENDOP(6);

		case 0x7f:	// RRA abs,X
			tmp = read_byte(adr = read_adr_abs_x());
			RoRightAdc;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x7b:	// RRA abs,Y
			tmp = read_byte(adr = read_adr_abs_y());
			RoRightAdc;
			write_byte(adr, tmp);
			ENDOP(7);

		case 0x63:	// RRA (ind,X)
			tmp = read_byte(adr = read_adr_ind_x());
			RoRightAdc;
			write_byte(adr, tmp);
			ENDOP(8);

		case 0x73:	// RRA (ind),Y
			tmp = read_byte(adr = read_adr_ind_y());
			RoRightAdc;
			write_byte(adr, tmp);
			ENDOP(8);


		// DEC/CMP group
#define DecCompare \
	set_nz(adr = a - tmp); \
	c_flag = adr < 0x100;

		case 0xc7:	// DCP zero
			tmp = read_zp(adr = read_adr_zero()) - 1;
			write_zp(adr, tmp);
			DecCompare;
			ENDOP(5);

		case 0xd7:	// DCP zero,X
			tmp = read_zp(adr = read_adr_zero_x()) - 1;
			write_zp(adr, tmp);
			DecCompare;
			ENDOP(6);

		case 0xcf:	// DCP abs
			tmp = read_byte(adr = read_adr_abs()) - 1;
			write_byte(adr, tmp);
			DecCompare;
			ENDOP(6);

		case 0xdf:	// DCP abs,X
			tmp = read_byte(adr = read_adr_abs_x()) - 1;
			write_byte(adr, tmp);
			DecCompare;
			ENDOP(7);

		case 0xdb:	// DCP abs,Y
			tmp = read_byte(adr = read_adr_abs_y()) - 1;
			write_byte(adr, tmp);
			DecCompare;
			ENDOP(7);

		case 0xc3:	// DCP (ind,X)
			tmp = read_byte(adr = read_adr_ind_x()) - 1;
			write_byte(adr, tmp);
			DecCompare;
			ENDOP(8);

		case 0xd3:	// DCP (ind),Y
			tmp = read_byte(adr = read_adr_ind_y()) - 1;
			write_byte(adr, tmp);
			DecCompare;
			ENDOP(8);


		// INC/SBC group
		case 0xe7:	// ISB zero
			tmp = read_zp(adr = read_adr_zero()) + 1;
			do_sbc(tmp);
			write_zp(adr, tmp);
			ENDOP(5);

		case 0xf7:	// ISB zero,X
			tmp = read_zp(adr = read_adr_zero_x()) + 1;
			do_sbc(tmp);
			write_zp(adr, tmp);
			ENDOP(6);

		case 0xef:	// ISB abs
			tmp = read_byte(adr = read_adr_abs()) + 1;
			do_sbc(tmp);
			write_byte(adr, tmp);
			ENDOP(6);

		case 0xff:	// ISB abs,X
			tmp = read_byte(adr = read_adr_abs_x()) + 1;
			do_sbc(tmp);
			write_byte(adr, tmp);
			ENDOP(7);

		case 0xfb:	// ISB abs,Y
			tmp = read_byte(adr = read_adr_abs_y()) + 1;
			do_sbc(tmp);
			write_byte(adr, tmp);
			ENDOP(7);

		case 0xe3:	// ISB (ind,X)
			tmp = read_byte(adr = read_adr_ind_x()) + 1;
			do_sbc(tmp);
			write_byte(adr, tmp);
			ENDOP(8);

		case 0xf3:	// ISB (ind),Y
			tmp = read_byte(adr = read_adr_ind_y()) + 1;
			do_sbc(tmp);
			write_byte(adr, tmp);
			ENDOP(8);


		// Complex functions
		case 0x0b:	// ANC #imm
		case 0x2b:
			set_nz(a &= read_byte_imm());
			c_flag = n_flag & 0x80;
			ENDOP(2);

		case 0x4b:	// ASR #imm
			a &= read_byte_imm();
			c_flag = a & 0x01;
			set_nz(a >>= 1);
			ENDOP(2);

		case 0x6b:	// ARR #imm
			tmp2 = read_byte_imm() & a;
			a = (c_flag ? (tmp2 >> 1) | 0x80 : tmp2 >> 1);
			if (!d_flag) {
				set_nz(a);
				c_flag = a & 0x40;
				v_flag = (a & 0x40) ^ ((a & 0x20) << 1);
			} else {
				n_flag = c_flag ? 0x80 : 0;
				z_flag = a;
				v_flag = (tmp2 ^ a) & 0x40;
				if ((tmp2 & 0x0f) + (tmp2 & 0x01) > 5)
					a = a & 0xf0 | (a + 6) & 0x0f;
				if ((c_flag = ((tmp2 + (tmp2 & 0x10)) & 0x1f0) > 0x50) != 0)
					a += 0x60;
			}
			ENDOP(2);

		case 0x8b:	// ANE #imm
			set_nz(a = read_byte_imm() & x & (a | 0xee));
			ENDOP(2);

		case 0x93:	// SHA (ind),Y
#if PC_IS_POINTER
			tmp2 = read_zp(pc[0] + 1);
#else
			tmp2 = read_zp(read_byte(pc) + 1);
#endif
			write_byte(read_adr_ind_y(), a & x & (tmp2+1));
			ENDOP(6);

		case 0x9b:	// SHS abs,Y
#if PC_IS_POINTER
			tmp2 = pc[1];
#else
			tmp2 = read_byte(pc+1);
#endif
			write_byte(read_adr_abs_y(), a & x & (tmp2+1));
			sp = a & x;
			ENDOP(5);

		case 0x9c:	// SHY abs,X
#if PC_IS_POINTER
			tmp2 = pc[1];
#else
			tmp2 = read_byte(pc+1);
#endif
			write_byte(read_adr_abs_x(), y & (tmp2+1));
			ENDOP(5);

		case 0x9e:	// SHX abs,Y
#if PC_IS_POINTER
			tmp2 = pc[1];
#else
			tmp2 = read_byte(pc+1);
#endif
			write_byte(read_adr_abs_y(), x & (tmp2+1));
			ENDOP(5);

		case 0x9f:	// SHA abs,Y
#if PC_IS_POINTER
			tmp2 = pc[1];
#else
			tmp2 = read_byte(pc+1);
#endif
			write_byte(read_adr_abs_y(), a & x & (tmp2+1));
			ENDOP(5);

		case 0xab:	// LXA #imm
			set_nz(a = x = (a | 0xee) & read_byte_imm());
			ENDOP(2);

		case 0xbb:	// LAS abs,Y
			set_nz(a = x = sp = read_byte_abs_y() & sp);
			ENDOP(4);

		case 0xcb:	// SBX #imm
			x &= a;
			adr = x - read_byte_imm();
			c_flag = adr < 0x100;
			set_nz(x = adr);
			ENDOP(2);

		case 0x02:
		case 0x12:
		case 0x22:
		case 0x32:
		case 0x42:
		case 0x52:
		case 0x62:
		case 0x72:
		case 0x92:
		case 0xb2:
		case 0xd2:
#if PC_IS_POINTER
			illegal_op(*(pc-1), pc-pc_base-1);
#else
			illegal_op(read_byte(pc-1), pc-1);
#endif
			break;
