#define lobyte(x) (x&0xFF)
#define hibyte(x) ((x>>8)&0xFF)
#define wordfrombytes(h,l) (((h<<8)&0xFF00)|(l&0xFF))
#define popbyte readmemory(--cpu->s)
#define loadimmed readmemory(cpu->i++)
#define loadstackrel readmemory((cpu->s+signed8(cpu->t))%65536)
#define loadstackrelp1 readmemory((cpu->s+signed8(cpu->t)+1)%65536)
#define wordut (wordfrombytes(cpu->u, cpu->t))
#define wordcb (wordfrombytes(cpu->c, cpu->b))
#define loadut readmemory(wordut)
#define loadutp1 readmemory((wordut+1)%65536)
#define loadp readmemory(cpu->p)
#define loadpinc readmemory(cpu->p++)
#define loadpp1 readmemory((cpu->p+1)%65536)
#define loadq readmemory(cpu->q)
#define loadqinc readmemory(cpu->q++)
#define loadqp1 readmemory((cpu->q+1)%65536)
#define signed8(x) (x>=128 ? x|0xFF00 : x)

#define setzf(x) cpu->nz=(x!=0);
#define loadimmedt cpu->t = loadimmed;
#define loadimm161 cpu->u = loadimmed;
#define loadimm162 cpu->t = loadimmed;
#define loadstackrelu cpu->u = loadstackrel;
#define storestackrel(x) writememory(cpu->s+signed8(cpu->t), x);
#define storestackrel161(x) writememory((cpu->s+signed8(cpu->t)  )%65536, hibyte(x));
#define storestackrel162(x) writememory((cpu->s+signed8(cpu->t)+1)%65536, lobyte(x));
#define loadstackrel161 cpu->u=loadstackrel;
#define loadstackrel162 cpu->t=loadstackrelp1;
#define storeut(x) writememory(wordut, x);
#define storeutp1(x) writememory((wordut+1)%65536, x);
#define storep(x) writememory(cpu->p, x);
#define storepinc(x) writememory(cpu->p++, x);
#define storepp1(x) writememory((cpu->p+1)%65536, x);
#define storeq(x) writememory(cpu->q, x);
#define storeqinc(x) writememory(cpu->q++, x);
#define storeqp1(x) writememory((cpu->q+1)%65536, x);
#define pushretaddr1 writememory(cpu->s++, hibyte((cpu->i-1)%65536));
#define pushretaddr2 writememory(cpu->s++, lobyte((cpu->i-1)%65536));
#define lni cpu->instr = readmemory(cpu->i++); cpu->cycle = 0;
#define ldi cpu->instr = readmemory(cpu->i); cpu->cycle = 0;
#define addf(x,y) { x=(x+y); cpu->cf=x>=256; x&=0xFF; setzf(x); }
#define subf(x,y) addf(x,-y);
#define cmpf(x,y) { int t=x-y; cpu->cf=(t<0); setzf(t); }
#define rol(x,y) x=(x<<y)|(x>>(8-y));
#define ror(x,y) x=(x>>y)|(x<<(8-y));
#define sra(x,y) x=(x>>y);
#define jmpabsp cpu->i=cpu->p; lni;
#define jmpabsq cpu->i=cpu->q; lni;
#define jmpabsut cpu->i=wordut; lni;
#define jmpabsutplus1 cpu->i=(wordut+1)%65536; lni;
#define pushbyte(b) writememory(cpu->s++, b);
#define push161(x) pushbyte(hibyte(x));
#define push162(x) pushbyte(lobyte(x));
#define pop161 cpu->t=popbyte;
#define pop162 cpu->u=popbyte;
#define tst(x) cpu->cf = x>=128; cpu->nz = x!=0;
#define instrpreload cpu->instrpre = loadimmed;
#define instrloadpre cpu->instr = cpu->instrpre;
#define jmprelt cpu->i = (cpu->i + signed8(cpu->t))%65536; lni;
#define saveretaddr cpu->q = (cpu->i+1)%65536;

#define readmemory(x) _readmemory(cpu, mem, x)
#define writememory(x, y) _writememory(cpu, mem, x, y)

struct CPU {
	int a;
	int b;
	int c;
	int u;
	int t;
	int p;
	int q;
	int s;
	int v;
	int i;
	int cf;
	int nz;
	int irq;
	int ifg;
	int rfg;
	int ien;
	int instr;
	int cycle;
	int instrpre;
	int frame;
};
struct Event {
	int id;
	int addr;
};
struct Memory {
	int data[65536];
	int canwrite[65536];
	int writes[65536];
	int reads[65536];
	int onread[65536];
	int onwrite[65536];
	struct Event events[4096];
	int numevents;
};

void postEvent(struct Memory* const mem, int id, int addr) {
	if(mem->numevents<4096) {
		mem->events[mem->numevents].id = id;
		mem->events[mem->numevents].addr = addr;
		mem->numevents++;
	}
}
int _readmemory(const struct CPU* const cpu, struct Memory* const mem, const int addr) {
	int addr2 = addr%65536;
	mem->reads[addr2] = cpu->frame;
	if(mem->onread[addr2]) {
		postEvent(mem, mem->onread[addr2], addr2);
	}
	return mem->data[addr2];
}
int _writememory(const struct CPU* const cpu, struct Memory* const mem, const int addr, const int data) {
	int addr2 = addr%65536;
	if(mem->canwrite[addr2]) {
		mem->writes[addr2] = cpu->frame;
		mem->data[addr2] = data%256;
	}
	if(mem->onwrite[addr2]) {
		postEvent(mem, mem->onwrite[addr2], addr2);
	}
}

typedef void(*CPUInstruction)(struct CPU* const cpu, struct Memory* const mem);

#include "instructions_gen.c"

int TickCPU(struct CPU* const cpu, struct Memory* const mem, const int count, const int countinstrs, const int breakaddr) {
	int i = 0;
	while(i<count) {
		if(cpu->irq && !cpu->ifg && cpu->cycle==0 && cpu->ien) { cpu->instr = 0xF2; }
		if(cpu->rfg || cpu->ifg || cpu->irq) {
			CPUInstruction instr = CPUInstructions[cpu->instr][cpu->cycle];
			if(instr) instr(cpu, mem);
			if(!countinstrs || cpu->cycle==0) { i++; }
			if(cpu->i==breakaddr) { i = count; break; }
		} else { i = count; break; }
		if(mem->numevents!=0) { break; }
	}
	return count-i;
}