/*
 * CPU Emulator class
 */

#include <stdio.h>
#include "CPU6502Emul.h"

#define WAIT(ncycles)

/* Flags
	CPU_6502		Select CPU
	CPU_6512
	CPU_65C02
	CPU_65C102
	CPU_65C12
	
	EMUL_EXACT		Miss out a couple of optimizations
 */

/* Determine if we are emulating a CMOS CPU */
#if defined(CPU_6502) || defined(CPU_6512)
#undef CMOS
#define DO_CMOS(x)
#elif defined(CPU_65C02) || defined(CPU_65C102) || defined(CPU_65C12)
#define CMOS
#define DO_CMOS(x) x
#endif

#ifdef EMUL_EXACT
#define DO_EXACT(x) x
#define DO_NEXACT(x)
#define RDCYCLE WAIT(1)
#define WRCYCLE WAIT(1)
#define FWAIT(x)
#else
#define DO_EXACT(x)
#define DO_NEXACT(x) x
#define RDCYCLE
#define WRCYCLE
#define FWAIT(x) cycles += x
#endif

#if defined(EMUL_EXACT) && !defined(CMOS)
#define RMW_CYCLE Mem->Write (address.w, d1); WRCYCLE
#define RMW_CYCLE_LOW Mem->WriteSimple (address.w, d1); WRCYCLE
#elif defined(EMUL_EXACT) && defined(CMOS)
#define RMW_CYCLE Mem->Read (address.w); RDCYCLE
#define RMW_CYCLE_LOW Mem->ReadSimple (address.w); RDCYCLE
#else
#define RMW_CYCLE
#define RMW_CYCLE_LOW
#endif

#define FLAGS(x) (P.N | P.V | P.D | P.I | P.Z | P.C | x)
#define GETFLAGS(x) { P.N = x & CPU_FLG_N; P.V = x & CPU_FLG_V; \
			P.D = x & CPU_FLG_D; P.I = x & CPU_FLG_I; \
			P.Z = x & CPU_FLG_Z; P.C = x & CPU_FLG_C; }

extern int trace;

template<class MemEmulCls>
inline void CPU6502Emul<MemEmulCls>::Push (byte b)
{
	Mem->Write (S.w, b);
	S.b.lo--;
}

template<class MemEmulCls>
inline byte CPU6502Emul<MemEmulCls>::Pop (void)
{
	byte b;

#ifndef EMUL_EXACT
	S.b.lo++;
#endif
	b = Mem->Read (S.w);
	return b;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::ResetCPU (void)
{
	CPUEmul::ResetCPU ();
	PC.b.lo = Mem->Read (0xfffc);
	PC.b.hi = Mem->Read (0xfffd);
	S.b.hi = 0x1;
	IRQ_Waiting = NMI_Waiting = 0;
	
	WAIT(7);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::SignalIRQ (int queue_anyway)
{
	if (queue_anyway)
		IRQ_Waiting |= queue_anyway;
	else if (!P.I)
		IRQ_Waiting |= 1;		// General IRQ
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::ClearIRQ (int queue_anyway_id)
{
	if (queue_anyway_id)
		IRQ_Waiting &= ~queue_anyway_id;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::SignalNMI (void)
{
	NMI_Waiting = 1;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::DoIRQ (void)
{
	IRQ_Waiting &= ~1;		// Only generic IRQs are auto-cleared
	
	WAIT(7);
	
	Push (PC.b.hi);
	Push (PC.b.lo);
	Push (P.N | P.V | P.D | P.I | P.Z | P.C | CPU_FLG_ONE);
	PC.b.lo = Mem->Read (0xfffe);
	PC.b.hi = Mem->Read (0xffff);
	
	P.I = CPU_FLG_I;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::DoNMI (void)
{
	NMI_Waiting = 0; //IRQ_Waiting = 0;
	
	if (trace)
		printf ("Handling NMI\n");
	
	WAIT(7);
	
	Push (PC.b.hi);
	Push (PC.b.lo);
	Push (P.N | P.V | P.D | P.I | P.Z | P.C | CPU_FLG_ONE);
	PC.b.lo = Mem->Read (0xfffa);
	PC.b.hi = Mem->Read (0xfffb);
	
	P.I = CPU_FLG_I;
}

// Addressing modes
template<class MemEmulCls>	// Absolute
void CPU6502Emul<MemEmulCls>::addr_abs (void)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
	address.b.lo = d1;
#ifdef EMUL_EXACT
	address.b.hi = Mem->Read (PC.w++); RDCYCLE;
#else
	address.b.hi = Mem->ReadSimple (PC.w++);
#endif
}

template<class MemEmulCls>	// Zero-page Absolute
void CPU6502Emul<MemEmulCls>::addr_zpabs (void)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	PC.w++;
	address.w = d1;
}

template<class MemEmulCls>	// Zero-page indirect
void CPU6502Emul<MemEmulCls>::addr_zpind (void)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	PC.w++;
	address.w = d1;
	d1 = Mem->ReadSimple (address.w); RDCYCLE;
	address.b.lo++;
	address.b.hi = Mem->ReadSimple (address.w); RDCYCLE;
	address.b.lo = d1;
}

template<class MemEmulCls>	// Zero-page X-indexed
void CPU6502Emul<MemEmulCls>::addr_zpidxX (void)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	address.w = d1;
	PC.w++;
#ifdef EMUL_EXACT
	Mem->ReadSimple (address.w); RDCYCLE;
#endif
	address.b.lo += X;
}

template<class MemEmulCls>	// Zero-page Y-indexed
void CPU6502Emul<MemEmulCls>::addr_zpidxY (void)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	address.w = d1;
	PC.w++;
#ifdef EMUL_EXACT
	Mem->ReadSimple (address.w); RDCYCLE;
#endif
	address.b.lo += Y;
}

template<class MemEmulCls>	// Absolute X-indexed
void CPU6502Emul<MemEmulCls>::addr_idxX (int do_extra)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	PC.w++;
	address.b.lo = d1;
	d1 = address.b.hi = Mem->Read (PC.w++); RDCYCLE;
	address.w += X;
#ifdef EMUL_EXACT
	if (do_extra || address.b.hi != d1)
	{
		byte t = address.b.hi;
		
		address.b.hi = d1;
		Mem->Read (address.w);
		RDCYCLE;
		address.b.hi = t;
	}
#else
	if (!do_extra)
	{
		if (address.b.hi != d1)
			extra_cycles = 1;
		else
			extra_cycles = 0;
	}
#endif
}

template<class MemEmulCls>	// Absolute Y-indexed
void CPU6502Emul<MemEmulCls>::addr_idxY (int do_extra)
{
	DO_NEXACT (d1 = Mem->ReadSimple (PC.w));
	PC.w++;
	address.b.lo = d1;
	d1 = address.b.hi = Mem->Read (PC.w++); RDCYCLE;
	address.w += Y;
#ifdef EMUL_EXACT
	if (do_extra || address.b.hi != d1)
	{
		byte t = address.b.hi;
		
		address.b.hi = d1;
		Mem->Read (address.w);
		RDCYCLE;
		address.b.hi = t;
	}
#else
	if (!do_extra)
	{
		if (address.b.hi != d1)
			extra_cycles = 1;
		else
			extra_cycles = 0;
	}
#endif
}

template<class MemEmulCls>	// Absolute Indirect
void CPU6502Emul<MemEmulCls>::addr_ind (void)
{
	wordu w;
	
	DO_NEXACT (d1 = Mem->Read (PC.w)); PC.w++;
	w.b.lo = d1;
	w.b.hi = Mem->Read (PC.w); PC.w++; RDCYCLE;
	address.b.lo = Mem->Read (w.w); RDCYCLE;
#ifndef CMOS
	w.b.lo++;
#else
	w.w++;
#endif
	address.b.hi = Mem->Read (w.w); RDCYCLE;
}

template<class MemEmulCls>	// Pre-X-Indexed indirect
void CPU6502Emul<MemEmulCls>::addr_preindX (void)
{
	wordu w;
	
	DO_NEXACT (d1 = Mem->Read (PC.w));
	PC.w++;
	w.w = d1;
#ifdef EMUL_EXACT
	Mem->ReadSimple (w.w);
	RDCYCLE;
#endif
	w.b.lo += X;
	
	address.b.lo = Mem->Read (w.w); RDCYCLE;
	w.b.lo++;
	address.b.hi = Mem->Read (w.w); RDCYCLE;
}

template<class MemEmulCls>	// Post-Y-Indexed indirect
void CPU6502Emul<MemEmulCls>::addr_postindY (int do_extra)
{
	byte t;
	
	DO_NEXACT (d1 = Mem->Read (PC.w));
	PC.w++;
	address.b.lo = Mem->ReadSimple (d1); RDCYCLE;
	address.b.hi = Mem->ReadSimple ((d1 + 1) & 0xff); RDCYCLE;
	t = address.b.hi;
	address.w += Y;
	if (do_extra || address.b.hi != t)
#ifdef EMUL_EXACT
	{
		t2 = address.b.hi;
		address.b.hi = t;
		Mem->Read (address.w);
		address.b.hi = t2;
	}
#else
		extra_cycles = 1;
	else
		extra_cycles = 0;
#endif
}

template<class MemEmulCls>	// Relative
void CPU6502Emul<MemEmulCls>::addr_rel (void)
{
	DO_NEXACT (d1 = Mem->Read (PC.w));
	PC.w++;
	address.w = PC.w;
	address.w += ((sbyte) d1);
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_ASL (byte val)
{
	P.C = val >> 7;		// Simplified
	val <<= 1;
	Set_Sign (val);
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_INC (byte val)
{
	val++;
	Set_Sign (val);
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_DEC (byte val)
{
	val--;
	Set_Sign (val);
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_LSR (byte val)
{
	P.C = val & 0x01;	// Simplified
	val >>= 1;
	//Set_Sign (val);
	P.N = 0;
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_ROL (byte val)
{
	int top;
	
	top = val >> 7;
	val <<= 1;
	val |= P.C;
	P.C = top;		// Simplified
	Set_Sign (val);
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_ROR (byte val)
{
	int top;
	
	top = P.C;
	P.C = val & 0x01;	// Simplified
	val >>= 1;
	val |= top << 7;
	Set_Sign (val);
	Set_Zero (val);
	return val;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_ADC (byte val)
{
	byte oldA;
	
	oldA = A;
	if (P.D)
	{
		int Alo = A & 0x0f;
		int Ahi = A >> 4;
		int vlo = val & 0x0f;
		int vhi = val >> 4;
		
		Alo += vlo + P.C;
		if (Alo >= 10)
		{
			Alo -= 10;
			Ahi++;
		}
		Ahi += vhi;
		
		A = (Alo | (Ahi << 4)) & 0xff;
		Set_Zero (A);
		Set_Sign (A);
		Set_Overflow (oldA, A, val);
		P.C = 0;
		if (Ahi >= 10)
		{
			Ahi += 6;
			P.C = CPU_FLG_C;
			Ahi &= 0x0f;
		}
		else
			P.C = 0;
		A = (Alo | (Ahi << 4));
	}
	else
	{
		int Anew;
		
		Anew = A + val + P.C;
		P.C = Anew >> 8;	// Simplified
		A = Anew & 0xff;
		Set_Zero (A);
		Set_Sign (A);
		Set_Overflow (oldA, A, val);
	}
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_AND (byte val)
{
	A &= val;
	Set_Sign (A);
	Set_Zero (A);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_BIT (byte val)
{
	Set_Sign (val);
	P.V = val & 0x40;
	Set_Zero (val & A);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_CMP (byte reg, byte val)
{
	P.C = (reg >= val) ? CPU_FLG_C : 0;
	P.Z = (reg == val) ? CPU_FLG_Z : 0;
	P.N = (reg < val) ? CPU_FLG_N : 0;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_EOR (byte val)
{
	A ^= val;
	Set_Sign (A);
	Set_Zero (A);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_ORA (byte val)
{
	A |= val;
	Set_Sign (A);
	Set_Zero (A);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_SBC (byte val)
{
	int Anew;
	
	Anew = A - val + P.C - 1;
	Set_Sign (Anew);
	Set_Overflow (A, Anew & 0xff, val);
	Set_Zero (Anew & 0xff);
	if (P.D)
	{
		int Alo = A & 0x0f;
		int Ahi = A >> 4;
		int vlo = val & 0x0f;
		int vhi = val >> 4;
		
		Alo -= vlo + P.C - 1;
		if (Alo < 0)
		{
			Alo += 10;
			Ahi--;
		}
		
		Ahi -= vhi;
		if (Ahi < 0)
			Ahi += 10;
		
		A = Alo | (Ahi << 4);
	}
	else
		A = Anew & 0xff;
	
	if (Anew >= 0)
		P.C = CPU_FLG_C;
	else
		P.C = 0;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_TSB (byte val)
{
	Set_Zero (val & A);
	return A | val;
}

template<class MemEmulCls>
byte CPU6502Emul<MemEmulCls>::Do_TRB (byte val)
{
	Set_Zero (val & A);
	return (val & ~A);
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Set_Sign (byte val)
{
	/*if (val & 0x80)
		P.N = CPU_FLG_N;
	else
		P.N = 0;*/
	P.N = val & 0x80;	// Simplified
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Set_Zero (byte val)
{
	if (!val)
		P.Z = CPU_FLG_Z;
	else
		P.Z = 0;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Set_Overflow (byte old, byte newval, byte val)
{
	if (!((old ^ val) & 0x80) && ((old ^ newval) & 0x80))
		P.V = CPU_FLG_V;
	else
		P.V = 0;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Do_Branch (int condition)
{
	if (condition)
	{
#ifdef EMUL_EXACT
		Mem->Read (PC.w);
		RDCYCLE;
		if (PC.b.hi != address.b.hi)
		{
			PC.b.lo = address.b.lo;
			Mem->Read (PC.w);
			RDCYCLE;
		}
#else
		if (PC.b.hi != address.b.hi)
			extra_cycles = 3;
		else
			extra_cycles = 2;
#endif
		PC.w = address.w;
	}
	else
		extra_cycles = 0;
}

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::ExecInstruction (void)
{
	byte instruc;
#if defined (HACK_RDCH) || defined (HACK_WRCH)
	wordu w;
#endif
	
	if (NMI_Waiting)
		DoNMI ();
	else if (IRQ_Waiting && !P.I)
		DoIRQ ();
	
	if (!PC.w)
	{
		printf ("NULL Program Counter!!! Resetting.\n");
		ResetCPU ();
	}
	
#ifdef EMUL_EXACT
	instruc = Mem->Read (PC.w);
	PC.w++;
	RDCYCLE;
	d1 = Mem->Read (PC.w);
	RDCYCLE;
#else
	instruc = Mem->ReadSimple (PC.w);
	PC.w++;
#endif
	 
	//printf ("%x %x A=%x X=%x Y=%x S=%x\n", PC.w - 1, instruc, A, X, Y, S.w);
	
	/* OSWRCH and OSRDCH hacks */
#ifdef HACK_WRCH
	w.b.lo = Mem->ReadSimple (0x20e);
	w.b.hi = Mem->ReadSimple (0x20f);
	if ((PC.w - 1) == w.w)	// WRCHV
	{
		putchar (A);
		fflush (stdout);
		//instruc = 0x60;	// RTS
	}
#endif /* HACK_WRCH */
#ifdef HACK_RDCH
	//printf ("RDCH\n");
	w.b.lo = Mem->ReadSimple (0x210);
	w.b.hi = Mem->ReadSimple (0x211);
	if ((PC.w - 1) == w.w)	// RDCHV
	{
		A = getchar ();
		if (A == 10)
			A = 13;
		instruc = 0x60;	// RTS
	}
#endif /* HACK_RDCH */
	
	switch (instruc)
	{
		// BRK
		case 0x00:
		PC.w++;
		Push (PC.b.hi); WRCYCLE;
		Push (PC.b.lo); WRCYCLE;
		Push (FLAGS(CPU_FLG_ONE | CPU_FLG_B)); WRCYCLE;
		// No need for ReadSimple since we give an exact address which the
		// compiler will optimize
		PC.b.lo = Mem->Read (0xfffe); RDCYCLE;
		PC.b.hi = Mem->Read (0xffff); RDCYCLE;
		DO_CMOS (P.D = 0);
		FWAIT (7);
		break;
		
		// RTI
		case 0x40:
		DO_EXACT (S.b.lo++); RDCYCLE;
		d1 = Pop (); GETFLAGS (d1); RDCYCLE;
		PC.b.lo = Pop (); RDCYCLE;
		PC.b.hi = Pop (); RDCYCLE;
		FWAIT (6);
		break;
		
		// RTS
		case 0x60:
		DO_EXACT (S.b.lo++); RDCYCLE;
		PC.b.lo = Pop (); RDCYCLE;
		PC.b.hi = Pop (); RDCYCLE;
		PC.w++; RDCYCLE;
		FWAIT (6);
		break;
		
		// PHA
		case 0x48:
		Push (A); WRCYCLE;
		FWAIT (3);
		break;
		
		// PHP
		case 0x08:
		Push (FLAGS (CPU_FLG_ONE | CPU_FLG_B)); WRCYCLE;
		FWAIT (3);
		break;
		
#ifdef CMOS
		// PHX
		case 0xda:
		Push (X); WRCYCLE;
		FWAIT (3);
		break;
		
		// PHY
		case 0x5a:
		Push (Y); WRCYCLE;
		FWAIT (3);
		break;
#endif
		
		// PLA
		case 0x68:
		DO_EXACT (S.b.lo++); RDCYCLE;
		A = Pop (); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (4);
		break;
		
		// PLP
		case 0x28:
		DO_EXACT (S.b.lo++); RDCYCLE;
		d1 = Pop (); GETFLAGS (d1); RDCYCLE;
		FWAIT (4);
		break;
		
#ifdef CMOS
		// PLX
		case 0xfa:
		DO_EXACT (S.b.lo++); RDCYCLE;
		X = Pop (); Set_Sign (X); Set_Zero (X); RDCYCLE;
		FWAIT (4);
		break;
		
		// PLY
		case 0x7a:
		DO_EXACT (S.b.lo++); RDCYCLE;
		Y = Pop (); Set_Sign (Y); Set_Zero (Y); RDCYCLE;
		FWAIT (4);
		break;
#endif
		
		// JSR
		case 0x20:
		DO_NEXACT(d1 = Mem->ReadSimple (PC.w)); PC.w++;
		/* Internal operation */; RDCYCLE;
		Push (PC.b.hi); WRCYCLE;
		Push (PC.b.lo); WRCYCLE;
		PC.b.hi = Mem->ReadSimple (PC.w);
		PC.b.lo = d1; RDCYCLE;
		FWAIT (6);
		break;
		
		/* *** Accumulator and implied addressing *** */
		// ASL A
		case 0x0a:
		A = Do_ASL (A);
		FWAIT (2);
		break;
		
#ifdef CMOS
		// DEC A
		case 0x3a:
		A = Do_DEC (A);
		FWAIT (2);
		break;
		
		// INC A
		case 0x1a:
		A = Do_INC (A);
		FWAIT (2);
		break;
#endif
		
		// LSR A
		case 0x4a:
		A = Do_LSR (A);
		FWAIT (2);
		break;
		
		// ROL A
		case 0x2a:
		A = Do_ROL (A);
		FWAIT (2);
		break;
		
		// ROR A
		case 0x6a:
		A = Do_ROR (A);
		FWAIT (2);
		break;
		
		// CLC
		case 0x18:
		P.C = 0;
		FWAIT (2);
		break;
		
		// CLD
		case 0xd8:
		P.D = 0;
		FWAIT (2);
		break;
		
		// CLI
		case 0x58:
		P.I = 0;
		FWAIT (2);
		break;
		
		// CLV
		case 0xb8:
		P.V = 0;
		FWAIT (2);
		break;
		
		// DEX
		case 0xca:
		X = Do_DEC (X);
		FWAIT (2);
		break;
		
		// DEY
		case 0x88:
		Y = Do_DEC (Y);
		FWAIT (2);
		break;
		
		// INX
		case 0xe8:
		X = Do_INC (X);
		FWAIT (2);
		break;
		
		// INY
		case 0xc8:
		Y = Do_INC (Y);
		FWAIT (2);
		break;
		
		// NOP
		case 0xea:
		FWAIT (2);
		break;
		
		// SEC
		case 0x38:
		P.C = CPU_FLG_C;
		FWAIT (2);
		break;
		
		// SED
		case 0xf8:
		P.D = CPU_FLG_D;
		FWAIT (2);
		break;
		
		// SEI
		case 0x78:
		P.I = CPU_FLG_I;
		FWAIT (2);
		break;
		
		// TAX
		case 0xaa:
		X = A; Set_Sign (A); Set_Zero (A);
		FWAIT (2);
		break;
		
		// TAY
		case 0xa8:
		Y = A; Set_Sign (A); Set_Zero (A);
		FWAIT (2);
		break;
		
		// TSX
		case 0xba:
		X = S.b.lo; Set_Sign (X); Set_Zero (X);
		FWAIT (2);
		break;
		
		// TXA
		case 0x8a:
		A = X; Set_Sign (X); Set_Zero (X);
		FWAIT (2);
		break;
		
		// TXS
		case 0x9a:
		S.b.lo = X;
		FWAIT (2);
		break;
		
		// TYA
		case 0x98:
		A = Y; Set_Sign (Y); Set_Zero (Y);
		FWAIT (2);
		break;
		
		/* *** Immediate addressing *** */
		// ADC
		case 0x69:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_ADC (d1);
		FWAIT (2);
		break;
		
		// AND
		case 0x29:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_AND (d1);
		FWAIT (2);
		break;
		
#ifdef CMOS
		// BIT
		case 0x89:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_BIT (d1);
		FWAIT (2);
		break;
#endif

		// CMP
		case 0xc9:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_CMP (A, d1);
		FWAIT (2);
		break;
		
		// CPX
		case 0xe0:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_CMP (X, d1);
		FWAIT (2);
		break;
		
		// CPY
		case 0xc0:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_CMP (Y, d1);
		FWAIT (2);
		break;
		
		// EOR
		case 0x49:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_EOR (d1);
		FWAIT (2);
		break;
		
		// LDA
		case 0xa9:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		A = d1; Set_Sign (A); Set_Zero (A);
		FWAIT (2);
		break;
		
		// LDX
		case 0xa2:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		X = d1; Set_Sign (X); Set_Zero (X);
		FWAIT (2);
		break;
		
		// LDY
		case 0xa0:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Y = d1; Set_Sign (Y); Set_Zero (Y);
		FWAIT (2);
		break;
		
		// ORA
		case 0x09:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_ORA (d1);
		FWAIT (2);
		break;
		
		// SBC
		case 0xe9:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
		Do_SBC (d1);
		FWAIT (2);
		break;
		
		/* *** Absolute addressing *** */
		// JMP
		case 0x4c:
		DO_NEXACT (d1 = Mem->ReadSimple (PC.w)); PC.w++;
#ifdef EMUL_EXACT
		PC.b.hi = Mem->Read (PC.w); RDCYCLE;
#else
		PC.b.hi = Mem->ReadSimple (PC.w);
#endif
		PC.b.lo = d1;
		FWAIT (3);
		break;
		
		// ADC
		case 0x6d:
		addr_abs ();
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// AND
		case 0x2d:
		addr_abs ();
		Do_AND (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// BIT
		case 0x2c:
		addr_abs ();
		Do_BIT (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// CMP
		case 0xcd:
		addr_abs ();
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// CPX
		case 0xec:
		addr_abs ();
		Do_CMP (X, Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// CPY
		case 0xcc:
		addr_abs ();
		Do_CMP (Y, Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// EOR
		case 0x4d:
		addr_abs ();
		Do_EOR (Mem->Read (address.w)); RDCYCLE; FWAIT (4);
		break;
		
		// LDA
		case 0xad:
		addr_abs ();
		A = Mem->Read (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (4);
		break;
		
		// LDX
		case 0xae:
		addr_abs ();
		X = Mem->Read (address.w); Set_Sign (X); Set_Zero (X); RDCYCLE;
		FWAIT (4);
		break;
		
		// LDY
		case 0xac:
		addr_abs ();
		Y = Mem->Read (address.w); Set_Sign (Y); Set_Zero (Y); RDCYCLE;
		FWAIT (4);
		break;
		
		// ORA
		case 0x0d:
		addr_abs ();
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// SBC
		case 0xed:
		addr_abs ();
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4);
		break;
		
		// ASL
		case 0x0e:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ASL (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// DEC
		case 0xce:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_DEC (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// INC
		case 0xee:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_INC (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// LSR
		case 0x4e:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_LSR (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// ROL
		case 0x2e:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ROL (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// ROR
		case 0x6e:
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ROR (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// STA
		case 0x8d:
		addr_abs ();
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (4);
		break;
		
		// STX
		case 0x8e:
		addr_abs ();
		Mem->Write (address.w, X); WRCYCLE;
		FWAIT (4);
		break;
		
		// STY
		case 0x8c:
		addr_abs ();
		Mem->Write (address.w, Y); WRCYCLE;
		FWAIT (4);
		break;

#ifdef CMOS		
		// STZ
		case 0x9c:
		addr_abs ();
		Mem->Write (address.w, 0); WRCYCLE;
		FWAIT (4);
		break;
#endif

		/* *** Zero-page addressing *** */
		// ADC
		case 0x65:
		addr_zpabs ();
		Do_ADC (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// AND
		case 0x25:
		addr_zpabs ();
		Do_AND (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// BIT
		case 0x24:
		addr_zpabs ();
		Do_BIT (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// CMP
		case 0xc5:
		addr_zpabs ();
		Do_CMP (A, Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// CPX
		case 0xe4:
		addr_zpabs ();
		Do_CMP (X, Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// CPY
		case 0xc4:
		addr_zpabs ();
		Do_CMP (Y, Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// EOR
		case 0x45:
		addr_zpabs ();
		Do_EOR (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDA
		case 0xa5:
		addr_zpabs ();
		A = Mem->ReadSimple (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDX
		case 0xa6:
		addr_zpabs ();
		X = Mem->ReadSimple (address.w); Set_Sign (X); Set_Zero (X); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDY
		case 0xa4:
		addr_zpabs ();
		Y = Mem->ReadSimple (address.w); Set_Sign (Y); Set_Zero (Y); RDCYCLE;
		FWAIT (3);
		break;
		
		// ORA
		case 0x05:
		addr_zpabs ();
		Do_ORA (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// SBC
		case 0xe5:
		addr_zpabs ();
		Do_SBC (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// ASL
		case 0x06:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ASL (d1)); WRCYCLE;
		FWAIT (5);
		break;
		
		// DEC
		case 0xc6:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_DEC (d1)); WRCYCLE;
		FWAIT (5);
		break;
		
		// INC
		case 0xe6:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_INC (d1)); WRCYCLE;
		FWAIT (5);
		break;
		
		// LSR
		case 0x46:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_LSR (d1)); WRCYCLE;
		FWAIT (5);
		break;

		// ROL
		case 0x26:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ROL (d1)); WRCYCLE;
		FWAIT (5);
		break;

		// ROR
		case 0x66:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ROR (d1)); WRCYCLE;
		FWAIT (5);
		break;

		// STA
		case 0x85:
		addr_zpabs ();
		Mem->WriteSimple (address.w, A); WRCYCLE;
		FWAIT (3);
		break;
		
		// STX
		case 0x86:
		addr_zpabs ();
		Mem->WriteSimple (address.w, X); WRCYCLE;
		FWAIT (3);
		break;
		
		// STY
		case 0x84:
		addr_zpabs ();
		Mem->WriteSimple (address.w, Y); WRCYCLE;
		FWAIT (3);
		break;
		
#ifdef CMOS
		// STZ
		case 0x64:
		addr_zpabs ();
		Mem->WriteSimple (address.w, 0); WRCYCLE;
		FWAIT (3);
		break;
#endif

		/* *** Zero-page indexed addressing *** */
		// ADC zp,X
		case 0x75:
		addr_zpidxX ();
		Do_ADC (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// AND zp,X
		case 0x35:
		addr_zpidxX ();
		Do_AND (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
#ifdef CMOS
		// BIT zp,X
		case 0x34:
		addr_zpidxX ();
		Do_BIT (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
#endif

		// CMP zp,X
		case 0xd5:
		addr_zpidxX ();
		Do_CMP (A, Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// EOR zp,X
		case 0x55:
		addr_zpidxX ();
		Do_EOR (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDA zp,X
		case 0xb5:
		addr_zpidxX ();
		A = Mem->ReadSimple (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDX zp,Y
		case 0xb6:
		addr_zpidxY ();
		X = Mem->ReadSimple (address.w); Set_Sign (X); Set_Zero (X); RDCYCLE;
		FWAIT (3);
		break;
		
		// LDY zp,X
		case 0xb4:
		addr_zpidxX ();
		Y = Mem->ReadSimple (address.w); Set_Sign (Y); Set_Zero (Y); RDCYCLE;
		FWAIT (3);
		break;
		
		// ORA zp,X
		case 0x15:
		addr_zpidxX ();
		Do_ORA (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// SBC zp,X
		case 0xf5:
		addr_zpidxX ();
		Do_SBC (Mem->ReadSimple (address.w)); RDCYCLE;
		FWAIT (3);
		break;
		
		// ASL zp,X
		case 0x16:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ASL (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// DEC zp,X
		case 0xd6:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_DEC (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// INC zp,X
		case 0xf6:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_INC (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// LSR zp,X
		case 0x56:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_LSR (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// ROL zp,X
		case 0x36:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ROL (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// ROR zp,X
		case 0x76:
		addr_zpidxX ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_ROR (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		// STA zp,X
		case 0x95:
		addr_zpidxX ();
		Mem->WriteSimple (address.w, A); WRCYCLE;
		FWAIT (4);
		break;
		
		// STX zp,Y
		case 0x96:
		addr_zpidxY ();
		Mem->WriteSimple (address.w, X); WRCYCLE;
		FWAIT (4);
		break;
		
		// STY zp,X
		case 0x94:
		addr_zpidxX ();
		Mem->WriteSimple (address.w, Y); WRCYCLE;
		FWAIT (4);
		break;
		
#ifdef CMOS
		// STZ zp,X
		case 0x74:
		addr_zpidxX ();
		Mem->WriteSimple (address.w, Y); WRCYCLE;
		FWAIT (4);
		break;
#endif

		/* *** Absolute indexed addressing *** */
		// ADC abs,X
		case 0x7d:
		addr_idxX (0);
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// ADC abs,Y
		case 0x79:
		addr_idxY (0);
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// AND abs,X
		case 0x3d:
		addr_idxX (0);
		Do_AND (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// AND abs,Y
		case 0x39:
		addr_idxY (0);
		Do_AND (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
#ifdef CMOS
		// BIT abs,X
		case 0x3c:
		addr_idxX (0);
		Do_BIT (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
#endif
		
		// CMP abs,X
		case 0xdd:
		addr_idxX (0);
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// CMP abs,Y
		case 0xd9:
		addr_idxY (0);
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// EOR abs,X
		case 0x5d:
		addr_idxX (0);
		Do_EOR (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// EOR abs,Y
		case 0x59:
		addr_idxY (0);
		Do_EOR (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// LDA abs,X
		case 0xbd:
		addr_idxX (0);
		A = Mem->Read (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// LDA abs,Y
		case 0xb9:
		addr_idxY (0);
		A = Mem->Read (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// LDX abs,Y
		case 0xbe:
		addr_idxY (0);
		X = Mem->Read (address.w); Set_Sign (X); Set_Zero (X); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;

		// LDY abs,X
		case 0xbc:
		addr_idxX (0);
		Y = Mem->Read (address.w); Set_Sign (Y); Set_Zero (Y); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;

		// ORA abs,X
		case 0x1d:
		addr_idxX (0);
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// ORA abs,Y
		case 0x19:
		addr_idxY (0);
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// SBC abs,X
		case 0xfd:
		addr_idxX (0);
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// SBC abs,Y
		case 0xf9:
		addr_idxY (0);
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (4 + extra_cycles);
		break;
		
		// ASL abs,X
		case 0x1e:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ASL (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// DEC abs,X
		case 0xde:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_DEC (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// INC abs,X
		case 0xfe:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_INC (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// LSR abs,X
		case 0x5e:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_LSR (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// ROL abs,X
		case 0x3e:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ROL (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// ROR abs,X
		case 0x7e:
		addr_idxX (1);
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ROR (d1)); WRCYCLE;
		FWAIT (7);
		break;
		
		// STA abs,X
		case 0x9d:
		addr_idxX (1);
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (5);
		break;
		
		// STA abs,Y
		case 0x99:
		addr_idxY (1);
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (5);
		break;
		
#ifdef CMOS
		// STZ abs,X
		case 0x9e:
		addr_idxX (1);
		Mem->Write (address.w, 0); WRCYCLE;
		FWAIT (5);
		break;
#endif

		/* *** Relative addressing *** */
		// BCC
		case 0x90:
		addr_rel ();
		Do_Branch (!P.C);
		FWAIT (2 + extra_cycles);
		break;
		
		// BCS
		case 0xb0:
		addr_rel ();
		Do_Branch (P.C);
		FWAIT (2 + extra_cycles);
		break;
		
		// BEQ
		case 0xf0:
		addr_rel ();
		Do_Branch (P.Z);
		FWAIT (2 + extra_cycles);
		break;
		
		// BMI
		case 0x30:
		addr_rel ();
		Do_Branch (P.N);
		FWAIT (2 + extra_cycles);
		break;
		
		// BNE
		case 0xd0:
		addr_rel ();
		Do_Branch (!P.Z);
		FWAIT (2 + extra_cycles);
		break;
		
		// BPL
		case 0x10:
		addr_rel ();
		Do_Branch (!P.N);
		FWAIT (2 + extra_cycles);
		break;
		
#ifdef CMOS
		// BRA
		case 0x80:
		addr_rel ();
		Do_Branch (1);
		FWAIT (2 + extra_cycles);
		break;
#endif
		
		// BVC
		case 0x50:
		addr_rel ();
		Do_Branch (!P.V);
		FWAIT (2 + extra_cycles);
		break;
		
		// BVS
		case 0x70:
		addr_rel ();
		Do_Branch (P.V);
		FWAIT (2 + extra_cycles);
		break;
		
		/* *** Pre-X-Indexed indirect addressing *** */
		// ADC
		case 0x61:
		addr_preindX ();
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// AND
		case 0x21:
		addr_preindX ();
		Do_AND (Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// CMP
		case 0xc1:
		addr_preindX ();
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// EOR
		case 0x41:
		addr_preindX ();
		Do_EOR (Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// LDA
		case 0xa1:
		addr_preindX ();
		A = Mem->Read (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (6);
		break;
		
		// ORA
		case 0x01:
		addr_preindX ();
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// SBC
		case 0xe1:
		addr_preindX ();
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (6);
		break;
		
		// STA
		case 0x81:
		addr_preindX ();
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (6);
		break;
		
		/* *** Post-Y-indexed indirect *** */
		// ADC
		case 0x71:
		addr_postindY (0);
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// AND
		case 0x31:
		addr_postindY (0);
		Do_AND (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// CMP
		case 0xd1:
		addr_postindY (0);
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// EOR
		case 0x51:
		addr_postindY (0);
		Do_EOR (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// LDA
		case 0xb1:
		addr_postindY (0);
		A = Mem->Read (address.w); Set_Sign (A); Set_Zero (A); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// ORA
		case 0x11:
		addr_postindY (0);
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// SBC
		case 0xf1:
		addr_postindY (0);
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5 + extra_cycles);
		break;
		
		// STA
		case 0x91:
		addr_postindY (1);
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (6);
		break;
		
		/* *** Indirect absolute addressing mode *** */
		// JMP
		case 0x6c:
		addr_ind ();
		PC.w = address.w;
		FWAIT (5);
		break;
		
		/* *** CMOS extra opcodes *** */
#ifdef CMOS
		case 0x04:	// TSB zp
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_TSB (d1)); WRCYCLE;
		FWAIT (5);
		break;
		
		case 0x14:	// TRB zp
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, Do_TRB (d1)); WRCYCLE;
		FWAIT (5);
		break;
		
		case 0x0c:	// TSB ab
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_TSB (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		case 0x1c:	// TRB ab
		addr_abs ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_TRB (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		case 0x0f:	// BBRx zp,rl
		case 0x1f:
		case 0x2f:
		case 0x3f:
		case 0x4f:
		case 0x5f:
		case 0x6f:
		case 0x7f:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		addr_rel ();
		Do_Branch (!(d1 & (1 << (instruc>>4))));
		FWAIT (5 + extra_cycles);
		break;
		
		case 0x8f:	// BBSx zp,rl
		case 0x9f:
		case 0xaf:
		case 0xbf:
		case 0xcf:
		case 0xdf:
		case 0xef:
		case 0xff:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		addr_rel ();
		Do_Branch (d1 & (1 << ((instruc>>4)&0x07)));
		FWAIT (5 + extra_cycles);
		break;
		
		case 0x07:	// RMBx zp
		case 0x17:
		case 0x27:
		case 0x37:
		case 0x47:
		case 0x57:
		case 0x67:
		case 0x77:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, d1 & ~(1 << (instruc >> 4))); WRCYCLE;
		FWAIT (5);
		break;
		
		case 0x87:	// SMBx zp
		case 0x97:
		case 0xa7:
		case 0xb7:
		case 0xc7:
		case 0xd7:
		case 0xe7:
		case 0xf7:
		addr_zpabs ();
		d1 = Mem->ReadSimple (address.w); RDCYCLE;
		RMW_CYCLE_LOW;
		Mem->WriteSimple (address.w, d1 | (1 << ((instruc >> 4) & 0x07))); WRCYCLE;
		FWAIT (5);
		break;
		
		case 0x7c:	// JMP (ab,X)
		address.b.lo = Mem->Read (PC.w); RDCYCLE;
		address.b.hi = Mem->Read (PC.w + 1); RDCYCLE;
		address.w += X;
		PC.b.lo = Mem->Read (address.w); RDCYCLE;
		PC.b.hi = Mem->Read (address.w + 1); RDCYCLE;
		FWAIT (6);
		break;
		
		/* *** Zero-paged indirect addressing *** */
		
		case 0x72:	// ADC (zp)
		addr_zpind ();
		Do_ADC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5);
		break;
		
		case 0x32:	// ASL (zp)
		addr_zpind ();
		d1 = Mem->Read (address.w); RDCYCLE;
		RMW_CYCLE;
		Mem->Write (address.w, Do_ASL (d1)); WRCYCLE;
		FWAIT (6);
		break;
		
		case 0x92:	// STA (zp)
		addr_zpind ();
		Mem->Write (address.w, A); WRCYCLE;
		FWAIT (5);
		break;
		
		case 0xb2:	// LDA (zp)
		addr_zpind ();
		A = Mem->Read (address.w); RDCYCLE;
		Set_Sign (A);
		Set_Zero (A);
		FWAIT (5);
		break;
		
		case 0xd2:	// CMP (zp)
		addr_zpind ();
		Do_CMP (A, Mem->Read (address.w)); RDCYCLE;
		FWAIT (5);
		break;
		
		case 0x52:	// EOR (zp)
		addr_zpind ();
		Do_EOR (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5);
		break;
		
		case 0x12:	// ORA (zp)
		addr_zpind ();
		Do_ORA (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5);
		break;
		
		case 0xf2:	// SBC (zp)
		addr_zpind ();
		Do_SBC (Mem->Read (address.w)); RDCYCLE;
		FWAIT (5);
		break;
		
#endif /* CMOS */
		
		default:
		printf ("Unknown opcode %x @ %x\n", instruc, PC.w);
		break;
	}
} 

template<class MemEmulCls>
void CPU6502Emul<MemEmulCls>::Trace (void)
{
	//extern FILE *debug;
	
	printf ("%x: %x %x %x A=%x X=%x Y=%x S=%x C%d Z%d N%d V%d I%d\n", PC.w, Mem->ReadSimple (PC.w),
		Mem->ReadSimple (PC.w + 1), Mem->ReadSimple (PC.w + 2), A, X, Y, S.w, P.C, P.Z, P.N, P.V, P.I);
}

/* End of file. */
