#include <stdio.h>
#include <unistd.h>
#include "memtype.h"
#include "mem.h"
#include "6502.h"


/* Emulation of a 6502 processor
 *  by Frank Barrus
 */

static byte cycles[256] = {
/*      0  1  2  3    4  5  6  7    8  9  a  b    c  d  e  f  */
/*0*/   7, 6, 0, 0,   5, 3, 5, 0,   3, 2, 2, 0,   6, 4, 6, 0,
/*1*/   2, 5, 5, 0,   5, 4, 6, 0,   2, 4, 2, 0,   6, 4, 6, 0,
/*2*/   6, 6, 0, 0,   3, 3, 5, 0,   4, 2, 2, 0,   4, 4, 6, 0,
/*3*/   2, 5, 5, 0,   4, 4, 6, 0,   2, 4, 2, 0,   4, 4, 6, 0,
/*4*/   6, 6, 0, 0,   0, 3, 5, 0,   3, 2, 2, 0,   3, 4, 6, 0,
/*5*/   2, 5, 5, 0,   0, 4, 6, 0,   2, 4, 3, 0,   0, 4, 6, 0,
/*6*/   6, 6, 0, 0,   3, 3, 5, 0,   4, 2, 2, 0,   6, 4, 6, 0,
/*7*/   2, 5, 5, 0,   4, 4, 6, 0,   2, 4, 4, 0,   6, 4, 6, 0,
/*8*/   2, 6, 0, 0,   3, 3, 3, 0,   2, 2, 2, 0,   4, 4, 4, 0,
/*9*/   2, 6, 5, 0,   4, 4, 4, 0,   2, 5, 2, 0,   4, 5, 5, 0,
/*a*/   2, 6, 2, 0,   3, 3, 3, 0,   2, 2, 2, 0,   4, 4, 4, 0,
/*b*/   2, 5, 5, 0,   4, 4, 4, 0,   2, 4, 2, 0,   4, 4, 4, 0,
/*c*/   4, 6, 0, 0,   4, 3, 5, 0,   2, 2, 2, 0,   4, 4, 6, 0,
/*d*/   2, 5, 5, 0,   0, 4, 6, 0,   2, 4, 3, 0,   0, 4, 6, 0,
/*e*/   2, 6, 2, 0,   3, 3, 5, 0,   2, 2, 0, 0,   4, 4, 6, 0,
/*f*/   2, 5, 5, 0,   0, 4, 6, 0,   2, 4, 4, 0,   0, 4, 6, 0
};


#ifdef notdefined
static byte cycles[256] = {
/*      0  1  2  3    4  5  6  7    8  9  a  b    c  d  e  f  */
/*0*/	7, 2, 0, 0,   0, 2, 4, 0,   3, 2, 2, 0,   0, 2, 4, 0,
/*1*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0,
/*2*/	6, 2, 0, 0,   2, 2, 4, 0,   4, 2, 2, 0,   2, 2, 4, 0,
/*3*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0,
/*4*/	6, 2, 0, 0,   0, 2, 4, 0,   3, 2, 2, 0,   3, 2, 4, 0,
/*5*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0,
/*6*/	6, 2, 0, 0,   0, 2, 4, 0,   4, 2, 2, 0,   5, 2, 4, 0,
/*7*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0,
/*8*/	0, 2, 0, 0,   2, 2, 2, 0,   2, 0, 2, 0,   2, 2, 2, 0,
/*9*/	2, 2, 0, 0,   2, 2, 2, 0,   2, 2, 2, 0,   0, 2, 0, 0,
/*a*/	2, 2, 2, 0,   2, 2, 2, 0,   2, 2, 2, 0,   2, 2, 2, 0,
/*b*/	2, 2, 0, 0,   2, 2, 2, 0,   2, 2, 2, 0,   2, 2, 2, 0,
/*c*/	2, 2, 0, 0,   2, 2, 4, 0,   2, 2, 2, 0,   2, 2, 4, 0,
/*d*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0,
/*e*/	2, 2, 0, 0,   2, 2, 4, 0,   2, 2, 2, 0,   2, 2, 4, 0,
/*f*/	2, 2, 0, 0,   0, 2, 4, 0,   2, 2, 0, 0,   0, 2, 4, 0
};
#endif


byte flags_set[512];
int dobrk = False;


/* Read/write macros: */
#define Read		MEM_READ(addr,data)
#define Write		MEM_WRITE(addr,data)
#define ZRead		MEM_ZREAD(addr,data)
#define ZWrite		MEM_ZWRITE(addr,data)


/* Macros used for different addressing modes: */

#define GetAcc		data = cpu.a;
#define SaveAcc		cpu.a = data;	
#define GetImm		{MEM_READ(cpu.pc,opd); INCW(cpu.pc); data=opd;}
#define GetAddr_ZPG	{MEM_READ(cpu.pc,opd); INCW(cpu.pc); addr=opd;}
#define GetAddr_ZPX	{MEM_READ(cpu.pc,opd); INCW(cpu.pc); addr=opd;\
			 ADDW(addr,cpu.x); }
#define GetAddr_ZPY	{MEM_READ(cpu.pc,opd); INCW(cpu.pc); addr=opd;\
			 ADDW(addr,cpu.y); }
#define GetAddr_ABS	{MEM_WREAD(cpu.pc,opd); ADDW(cpu.pc,2); addr=opd;}
#define GetAddr_ABX	{MEM_WREAD(cpu.pc,opd); ADDW(cpu.pc,2); addr=opd;\
			 ADDW(addr, cpu.x);\
			 if((addr^opd)&0x100) cpu.tcycles++;}
#define GetAddr_ABY	{MEM_WREAD(cpu.pc,opd); ADDW(cpu.pc,2); addr=opd;\
			 ADDW(addr, cpu.y); \
			 if((addr^opd)&0x100) cpu.tcycles++;}
#define GetAddr_REL     {MEM_READ(cpu.pc,opd); INCW(cpu.pc); \
				addr=(cpu.pc+(sbyte)opd)&0xffff;}
#define GetAddr_IXI	{MEM_READ(cpu.pc,opd); INCW(cpu.pc); addr=opd;\
			 ADDB(addr, cpu.x); MEM_WZREAD(addr,addr); }
#define GetAddr_IIX	{MEM_READ(cpu.pc,opd); INCW(cpu.pc); \
			 MEM_WZREAD(opd,n); addr=n; ADDW(addr,cpu.y);\
			 if((addr^n)&0x100) cpu.tcycles++;}
#define GetAddr_IND	{MEM_WREAD(cpu.pc,opd); ADDW(cpu.pc, 2); \
			 MEM_WREAD_BUG(opd, addr); }



/* Macros for each instruction: */

#define Flags(d,f)	FLAGS=(FLAGS&~(f))|(flags_set[(d)&0x1ff]&(f));

uint bcd_fl[32] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
		    0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,
		    0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,
		    0x1e,0x1f};

uint bcd_fh[32]={0x000,0x010,0x020,0x030,0x040,0x050,0x060,0x070,0x080,0x090,
	         0x1a0,0x1b0,0x1c0,0x1d0,0x1e0,0x1f0,0x100,0x110,0x120,0x130,
	         0x140,0x150,0x160,0x170,0x180,0x190,0x1a0,0x1b0,0x1c0,0x1d0,
	         0x1e0,0x1f0};

uint bcd_low[32] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,
		    0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,
		    0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,
		    0x0,0x1};

uint bcd_hi[32]={0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,
	         0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,
	         0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x80,0x90,
	         0x00,0x10};


#define Inst_ADC	{if(FLAGS&FLAG_D) {\
				c = (cpu.a&0x0f)+(data&0x0f)+(FLAGS&FLAG_C);\
				n = ((cpu.a&0xf0)+(data&0xf0)+bcd_fl[c])>>4; \
				i = bcd_fh[n]|(c&0xf);\
				Flags(i,FLAG_V|FLAG_N|FLAG_Z|FLAG_C);\
				FLAGS |= (((i^cpu.a)&(i^data)&0x80)>>1);\
				cpu.a = bcd_hi[n]|bcd_low[c];\
			 } else {\
				i = cpu.a+data+(FLAGS&FLAG_C);\
			 	Flags(i,FLAG_V|FLAG_N|FLAG_Z|FLAG_C);\
				FLAGS |= (((i^cpu.a)&(i^data)&0x80)>>1);\
				cpu.a = i&0xff;\
			 }}
#define Inst_AND	{Flags(cpu.a &= data, FLAG_N|FLAG_Z); }
#define Inst_ASL	{i = data << 1; data = i&0xff; \
			 Flags(i,FLAG_N|FLAG_Z|FLAG_C); }
#define Inst_BCC	{if(!(FLAGS&FLAG_C)) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BCS	{if(FLAGS&FLAG_C) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BEQ	{if(FLAGS&FLAG_Z) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BIT	{FLAGS = (FLAGS&~(FLAG_Z|FLAG_N|FLAG_V)) \
			  |(flags_set[cpu.a&data]&FLAG_Z) \
			  |(data&(FLAG_N|FLAG_V)); }	
#define Inst_BMI	{if(FLAGS&FLAG_N) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BNE	{if(!(FLAGS&FLAG_Z)) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BPL	{if(!(FLAGS&FLAG_N)) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BRK	{INCW(cpu.pc); PUSHW(cpu.pc); \
			 PUSH(FLAGS); FLAGS |= FLAG_B|FLAG_I;\
			 MEM_WREAD(VEC_IRQ, cpu.pc); \
			 if(!dobrk) { printf("BREAK!\n"); cpu.halt++; }}
#define Inst_BVC	{if(!(FLAGS&FLAG_V)) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_BVS	{if(FLAGS&FLAG_V) {cpu.pc = addr; cpu.tcycles++;}}
#define Inst_CLC	{FLAGS &= ~FLAG_C; if(cpu.irq) cpu_irq(); }
#define Inst_CLD	{FLAGS &= ~FLAG_D;}
#define Inst_CLI	{FLAGS &= ~FLAG_I;}
#define Inst_CLV	{FLAGS &= ~FLAG_V;}
#define Inst_CMP	{Flags(cpu.a+(data^0xff)+1, FLAG_N|FLAG_Z|FLAG_C);}
#define Inst_CPX	{Flags(cpu.x+(data^0xff)+1, FLAG_N|FLAG_Z|FLAG_C);}
#define Inst_CPY	{Flags(cpu.y+(data^0xff)+1, FLAG_N|FLAG_Z|FLAG_C);}
#define Inst_DEC	{DECB(data); Flags(data, FLAG_N|FLAG_Z); }
#define Inst_DEX	{DECB(cpu.x); Flags(cpu.x, FLAG_N|FLAG_Z); }
#define Inst_DEY	{DECB(cpu.y); Flags(cpu.y, FLAG_N|FLAG_Z); }
#define Inst_EOR	{Flags(cpu.a ^= data, FLAG_N|FLAG_Z); }
#define Inst_INC	{INCB(data); Flags(data, FLAG_N|FLAG_Z); }
#define Inst_INX	{INCB(cpu.x); Flags(cpu.x, FLAG_N|FLAG_Z); }
#define Inst_INY	{INCB(cpu.y); Flags(cpu.y, FLAG_N|FLAG_Z); }
#define Inst_JMP	{cpu.pc = addr; }
#define Inst_JSR	{if(addr >= 0xd800) ostrap(addr);\
			 else { DECW(cpu.pc); PUSHW(cpu.pc); cpu.pc = addr; } }
#define Inst_LDA	{Flags(cpu.a=data,FLAG_N|FLAG_Z);}
#define Inst_LDX	{Flags(cpu.x=data,FLAG_N|FLAG_Z);}
#define Inst_LDY	{Flags(cpu.y=data,FLAG_N|FLAG_Z);}
#define Inst_LSR	{i = data; data = (data>>1)&0x7f;\
			 FLAGS = (FLAGS&~(FLAG_N|FLAG_Z|FLAG_C)) \
				|(flags_set[data]&(FLAG_N|FLAG_Z))|(i&1); }
#define Inst_NOP	
#define Inst_ORA	{Flags(cpu.a |= data, FLAG_N|FLAG_Z); }
#define Inst_PHA	{PUSH(cpu.a); }
#define Inst_PHP	{PUSH(FLAGS); }
#define Inst_PLA	{POP(cpu.a); Flags(cpu.a, FLAG_N|FLAG_Z);}
#define Inst_PLP	{POP(FLAGS); FLAGS |= 0x20; if(cpu.irq) cpu_irq(); }
#define Inst_ROL	{i = (data << 1)|(FLAGS&FLAG_C); data = i&0xff; \
			 FLAGS = (FLAGS&~(FLAG_N|FLAG_Z|FLAG_C)) \
				|(flags_set[i]&(FLAG_N|FLAG_Z|FLAG_C)); }
#define Inst_ROR	{i = data; data = ((data>>1)&0x7f)|((FLAGS&FLAG_C)<<7);\
			 FLAGS = (FLAGS&~(FLAG_N|FLAG_Z|FLAG_C)) \
				|(flags_set[data]&(FLAG_N|FLAG_Z))|(i&1); }
#define Inst_RTI	{POP(FLAGS); POPW(cpu.pc); \
			 if(cpu.irq) cpu_irq(); }
#define Inst_RTS	{POPW(cpu.pc); cpu.halt = !cpu.pc; INCW(cpu.pc); }
#define Inst_SBC	{i = cpu.a+(data^0xff)+(FLAGS&FLAG_C);\
			 c = (cpu.a&0x0f)+((data&0x0f)^0x0f)+(FLAGS&FLAG_C);\
			 Flags(i,FLAG_N|FLAG_Z|FLAG_C|FLAG_V);\
			 FLAGS |= (((i^cpu.a)&(i^data^0xff)&0x80)>>1);\
			 if(FLAGS&FLAG_D) {\
				if(!(c&0x10)) c -= 6;\
				if(!(i&0x100)) i -= 0x60;\
				cpu.a = (i&0xf0)|(c&0x0f);\
			 } else \
				cpu.a = i&0xff;\
			}
#define Inst_SEC	{FLAGS |= FLAG_C;}
#define Inst_SED	{FLAGS |= FLAG_D;}
#define Inst_SEI	{FLAGS |= FLAG_I;}
#define Inst_STA	{data = cpu.a; }
#define Inst_STX	{data = cpu.x; }
#define Inst_STY	{data = cpu.y; }
#define Inst_TAX	{Flags(cpu.x = cpu.a, FLAG_N|FLAG_Z);}
#define Inst_TAY	{Flags(cpu.y = cpu.a, FLAG_N|FLAG_Z);}
#define Inst_TSX	{Flags(cpu.x = cpu.sp, FLAG_N|FLAG_Z);}
#define Inst_TXA	{Flags(cpu.a = cpu.x, FLAG_N|FLAG_Z);}
#define Inst_TXS	{cpu.sp = cpu.x;}
#define Inst_TYA	{Flags(cpu.a = cpu.y, FLAG_N|FLAG_Z);}


/* NOTES:
	o	Branches that cross a page should be an extra cycle
	o	"abs,y" and "abs,x" should cost 3 or 2 depending on page
	o	"zpy" and "zpx" should cost 2
	o	"absolute" should cost 2
	o	zero page should cost 1
	o	indry should cost 4 or 3
	o	indrx should cost 4
 */


void run_6502(int ninst)
{
register uint addr;
register int i,c;
register uint n;
register uint data;
register struct sched *sp;
register uint opcode, opd;
#define FLAGS cpu.flags
	addr = data = opd = 0;
	while(ninst--) {
		if((cpu.htime-cpu.tcycles) < 0) {
			cpu.htime += cpu.hcount;
			break;
		}
		if((sp=cpu.sched) && (sp->time-cpu.tcycles) < 0) {
			cpu.sched = NULL;
			sp->time = 0;
			sp->func(sp->param);
			if(!cpu.sched) {
				sp = NULL;
				for(i=0; i<NSCHED; i++)
				    if(cpu.event[i].time && (!sp
				      || (cpu.event[i].time-cpu.tcycles)
					< (sp->time-cpu.tcycles)))
						sp = &cpu.event[i];
				cpu.sched = sp;
			}
		}
		if(cpu.halt || cpu.sync)
			break;
		if(trace) {
			trace[tracepos].a = cpu.a;
			trace[tracepos].x = cpu.x;
			trace[tracepos].y = cpu.y;
			trace[tracepos].flags = cpu.flags;
			trace[tracepos].sp = cpu.sp;
			trace[tracepos].pc = cpu.pc;
			trace[tracepos].cycle = cpu.tcycles;
		}
		MEM_READ(cpu.pc, opcode); INCW(cpu.pc);
		cpu.tcycles += cycles[opcode];
		switch(opcode) {
		case 0x00: 			Inst_BRK; 		break;
		case 0x01: GetAddr_IXI; Read;	Inst_ORA;		break;
		case 0x05: GetAddr_ZPG; ZRead;	Inst_ORA;		break;
		case 0x06: GetAddr_ZPG; ZRead;	Inst_ASL; ZWrite;	break;
		case 0x08: 			Inst_PHP;		break;
		case 0x09: GetImm;		Inst_ORA;		break;
		case 0x0a: GetAcc;		Inst_ASL; SaveAcc;	break;
		case 0x0d: GetAddr_ABS; Read;   Inst_ORA;		break;
		case 0x0e: GetAddr_ABS; Read;	Inst_ASL; Write;	break;
		case 0x10: GetAddr_REL;		Inst_BPL; 		break;
		case 0x11: GetAddr_IIX; Read;	Inst_ORA;		break;
		case 0x15: GetAddr_ZPX; ZRead;	Inst_ORA;		break;
		case 0x16: GetAddr_ZPX; ZRead;	Inst_ASL; ZWrite;	break;
		case 0x18:			Inst_CLC;		break;
		case 0x19: GetAddr_ABY; Read;	Inst_ORA;		break;
		case 0x1d: GetAddr_ABX; Read;	Inst_ORA;		break;
		case 0x1e: GetAddr_ABX; Read;	Inst_ASL; Write;	break;
		case 0x20: GetAddr_ABS;		Inst_JSR;		break;
		case 0x21: GetAddr_IXI;	Read;	Inst_AND;		break;
		case 0x24: GetAddr_ZPG; ZRead;	Inst_BIT;		break;
		case 0x25: GetAddr_ZPG; ZRead;	Inst_AND;		break;
		case 0x26: GetAddr_ZPG; ZRead;	Inst_ROL; ZWrite;	break;
		case 0x28: 			Inst_PLP;		break;
		case 0x29: GetImm; 		Inst_AND;		break;
		case 0x2a: GetAcc;		Inst_ROL; SaveAcc;	break;
		case 0x2c: GetAddr_ABS; Read;	Inst_BIT;		break;
		case 0x2d: GetAddr_ABS; Read;	Inst_AND;		break;
		case 0x2e: GetAddr_ABS; Read;	Inst_ROL; Write;	break;
		case 0x30: GetAddr_REL;		Inst_BMI; 		break;
		case 0x31: GetAddr_IIX; Read;	Inst_AND;		break;
		case 0x35: GetAddr_ZPX; ZRead;	Inst_AND;		break;
		case 0x36: GetAddr_ZPX; ZRead;	Inst_ROL; ZWrite;	break;
		case 0x38:			Inst_SEC;		break;
		case 0x39: GetAddr_ABY; Read;	Inst_AND;		break;
		case 0x3d: GetAddr_ABX; Read;	Inst_AND;		break;
		case 0x3e: GetAddr_ABX; Read;	Inst_ROL; Write;	break;
		case 0x40: 			Inst_RTI;		break;
		case 0x41: GetAddr_IXI; Read;	Inst_EOR;		break;
		case 0x45: GetAddr_ZPG; ZRead;	Inst_EOR;		break;
		case 0x46: GetAddr_ZPG; ZRead;	Inst_LSR; ZWrite;	break;
		case 0x48: 			Inst_PHA;		break;
		case 0x49: GetImm;		Inst_EOR;		break;
		case 0x4a: GetAcc;		Inst_LSR; SaveAcc;	break;
		case 0x4c: GetAddr_ABS;		Inst_JMP;		break;	
		case 0x4d: GetAddr_ABS;	Read;	Inst_EOR;		break;
		case 0x4e: GetAddr_ABS;	Read;	Inst_LSR; Write;	break;
		case 0x50: GetAddr_REL;		Inst_BVC; 		break;
		case 0x51: GetAddr_IIX; Read;	Inst_EOR;		break;
		case 0x55: GetAddr_ZPX; ZRead;	Inst_EOR;		break;
		case 0x56: GetAddr_ZPX; ZRead;	Inst_LSR; ZWrite;	break;
		case 0x58: 			Inst_CLI;		break;
		case 0x59: GetAddr_ABY; Read;	Inst_EOR;		break;
		case 0x5d: GetAddr_ABX; Read;	Inst_EOR;		break;
		case 0x5e: GetAddr_ABX; Read;	Inst_LSR; Write;	break;
		case 0x60: 			Inst_RTS;		break;
		case 0x61: GetAddr_IXI; Read;	Inst_ADC;		break;
		case 0x65: GetAddr_ZPG; ZRead;	Inst_ADC;		break;
		case 0x66: GetAddr_ZPG; ZRead;	Inst_ROR; ZWrite;	break;
		case 0x68:			Inst_PLA;		break;
		case 0x69: GetImm;		Inst_ADC;		break;
		case 0x6a: GetAcc;		Inst_ROR; SaveAcc;	break;
		case 0x6c: GetAddr_IND;		Inst_JMP;		break;
		case 0x6d: GetAddr_ABS;	Read;	Inst_ADC;		break;
		case 0x6e: GetAddr_ABS;	Read;	Inst_ROR; Write;	break;
		case 0x70: GetAddr_REL;		Inst_BVS; 		break;
		case 0x71: GetAddr_IIX; Read;	Inst_ADC;		break;
		case 0x75: GetAddr_ZPX; ZRead;	Inst_ADC;		break;
		case 0x76: GetAddr_ZPX; ZRead;	Inst_ROR; ZWrite;	break;
		case 0x78:			Inst_SEI;		break;
		case 0x79: GetAddr_ABY; Read;	Inst_ADC;		break;
		case 0x7d: GetAddr_ABX; Read;	Inst_ADC;		break;
		case 0x7e: GetAddr_ABX; Read;	Inst_ROR; Write;	break;
		case 0x81: GetAddr_IXI; 	Inst_STA; Write;	break;
		case 0x84: GetAddr_ZPG;		Inst_STY; ZWrite;	break;
		case 0x85: GetAddr_ZPG;		Inst_STA; ZWrite;	break;
		case 0x86: GetAddr_ZPG;		Inst_STX; ZWrite;	break;
		case 0x88:			Inst_DEY;		break;
		case 0x8a:			Inst_TXA;		break;
		case 0x8c: GetAddr_ABS;		Inst_STY; Write;	break;
		case 0x8d: GetAddr_ABS;		Inst_STA; Write;	break;
		case 0x8e: GetAddr_ABS;		Inst_STX; Write;	break;
		case 0x90: GetAddr_REL;		Inst_BCC; 		break;
		case 0x91: GetAddr_IIX; 	Inst_STA; Write;	break;
		case 0x94: GetAddr_ZPX;		Inst_STY; ZWrite;	break;
		case 0x95: GetAddr_ZPX;		Inst_STA; ZWrite;	break;
		case 0x96: GetAddr_ZPY;		Inst_STX; ZWrite;	break;
		case 0x98:			Inst_TYA;		break;
		case 0x99: GetAddr_ABY;		Inst_STA; Write;	break;
		case 0x9a: 			Inst_TXS;		break;
		case 0x9d: GetAddr_ABX;		Inst_STA; Write;	break;
		case 0xa0: GetImm;		Inst_LDY;		break;
		case 0xa1: GetAddr_IXI;	Read;	Inst_LDA;		break;
		case 0xa2: GetImm;		Inst_LDX;		break;
		case 0xa4: GetAddr_ZPG; ZRead;	Inst_LDY;		break;
		case 0xa5: GetAddr_ZPG; ZRead;	Inst_LDA;		break;
		case 0xa6: GetAddr_ZPG; ZRead;	Inst_LDX;		break;
		case 0xa8:			Inst_TAY;		break;
		case 0xa9: GetImm;		Inst_LDA;		break;
		case 0xaa: 			Inst_TAX;		break;
		case 0xac: GetAddr_ABS; Read;	Inst_LDY;		break;
		case 0xad: GetAddr_ABS; Read;	Inst_LDA;		break;
		case 0xae: GetAddr_ABS; Read;	Inst_LDX;		break;
		case 0xb0: GetAddr_REL;		Inst_BCS; 		break;
		case 0xb1: GetAddr_IIX; Read;	Inst_LDA;		break;
		case 0xb4: GetAddr_ZPX; ZRead;	Inst_LDY;		break;
		case 0xb5: GetAddr_ZPX; ZRead;	Inst_LDA;		break;
		case 0xb6: GetAddr_ZPY; ZRead;	Inst_LDX;		break;
		case 0xb8:			Inst_CLV;		break;
		case 0xb9: GetAddr_ABY; Read;	Inst_LDA;		break;
		case 0xba:			Inst_TSX;		break;
		case 0xbc: GetAddr_ABX; Read;	Inst_LDY;		break;
		case 0xbd: GetAddr_ABX; Read;	Inst_LDA;		break;
		case 0xbe: GetAddr_ABY; Read;	Inst_LDX;		break;
		case 0xc0: GetImm; 		Inst_CPY;		break;
		case 0xc1: GetAddr_IXI; Read;	Inst_CMP;		break;
		case 0xc4: GetAddr_ZPG; ZRead;	Inst_CPY;		break;
		case 0xc5: GetAddr_ZPG; ZRead;	Inst_CMP;		break;
		case 0xc6: GetAddr_ZPG; ZRead;	Inst_DEC; ZWrite;	break;
		case 0xc8: 			Inst_INY;		break;
		case 0xc9: GetImm;		Inst_CMP;		break;
		case 0xca:			Inst_DEX;		break;
		case 0xcc: GetAddr_ABS;	Read;	Inst_CPY;		break;
		case 0xcd: GetAddr_ABS; Read;	Inst_CMP;		break;
		case 0xce: GetAddr_ABS; Read;	Inst_DEC; Write;	break;
		case 0xd0: GetAddr_REL;		Inst_BNE; 		break;
		case 0xd1: GetAddr_IIX; Read;	Inst_CMP;		break;
		case 0xd5: GetAddr_ZPX; ZRead;	Inst_CMP;		break;
		case 0xd6: GetAddr_ZPX; ZRead;	Inst_DEC; ZWrite;	break;
		case 0xd8: 			Inst_CLD;		break;
		case 0xd9: GetAddr_ABY; Read;	Inst_CMP;		break;
		case 0xdd: GetAddr_ABX; Read;	Inst_CMP;		break;
		case 0xde: GetAddr_ABX; Read;	Inst_DEC; Write;	break;
		case 0xe0: GetImm;		Inst_CPX;		break;
		case 0xe1: GetAddr_IXI; Read;	Inst_SBC;		break;
		case 0xe4: GetAddr_ZPG; ZRead;	Inst_CPX;		break;
		case 0xe5: GetAddr_ZPG; ZRead;	Inst_SBC;		break;
		case 0xe6: GetAddr_ZPG; ZRead;	Inst_INC; ZWrite;	break;
		case 0xe8: 			Inst_INX;		break;
		case 0xe9: GetImm;		Inst_SBC;		break;
		case 0xea: 			Inst_NOP;		break;
		case 0xec: GetAddr_ABS; Read;	Inst_CPX;		break;
		case 0xed: GetAddr_ABS; Read;	Inst_SBC;		break;
		case 0xee: GetAddr_ABS; Read;	Inst_INC; Write;	break;
		case 0xf0: GetAddr_REL;		Inst_BEQ; 		break;
		case 0xf1: GetAddr_IIX; Read;	Inst_SBC;		break;
		case 0xf5: GetAddr_ZPX; ZRead;	Inst_SBC;		break;
		case 0xf6: GetAddr_ZPX; ZRead;	Inst_INC; ZWrite;	break;
		case 0xf8: 			Inst_SED;		break;
		case 0xf9: GetAddr_ABY; Read;	Inst_SBC;		break;
		case 0xfd: GetAddr_ABX; Read;	Inst_SBC;		break;
		case 0xfe: GetAddr_ABX; Read;	Inst_INC; Write;	break;
		default: 
			printf("Unknown opcode: %02x at %04x --> HALT\n",	
				opcode, cpu.pc-1);
			cpu.halt++;
		}
		if(trace) {
			trace[tracepos].opcode = opcode;
			trace[tracepos].operand = opd;
			trace[tracepos].addr = addr;
			trace[tracepos].data = data;
			if(++tracepos >= tracesize)
				tracepos = 0;
		}
	}
#undef FLAGS
}


