// 8608emulator.c
// This is the backend portion of the 8608 emulator application.
// It uses the generated instruction code from instructions_gen.c to emulate the internals of the 8608 CPU.
// It is included into the application by 8608emulator.lua, which provides the frontend.

#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)%256)+0x0100)
#define loadimmed readmemory(cpu->i++)
#define loadstackrel readmemory(cpu->t)
#define loadstackrelp1 readmemory(cpu->t+1)
#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 loadq readmemory(cpu->q)
#define loadpinc readmemory((cpu->p++)%65536)
#define loadqinc readmemory((cpu->q++)%65536)
#define loadpp1 readmemory((cpu->p+1)%65536)
#define loadqp1 readmemory((cpu->q+1)%65536)
#define loadput readmemory((cpu->p+wordut)%65536)
#define loadqut readmemory((cpu->q+wordut)%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->t, x);
#define storestackrelu storestackrel(cpu->u);
#define storestackrel161(x) writememory(cpu->t  , hibyte(x));
#define storestackrel162(x) writememory(cpu->t+1, 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 storeq(x) writememory(cpu->q, x);
#define storepinc(x) writememory(cpu->p++, x);
#define storeqinc(x) writememory(cpu->q++, x);
#define storepp1(x) writememory((cpu->p+1)%65536, x);
#define storeqp1(x) writememory((cpu->q+1)%65536, x);
#define storeput(x) writememory((cpu->p+wordut)%65536, x);
#define storequt(x) writememory((cpu->q+wordut)%65536, x);
#define pushretaddr1 writememory(((cpu->s++)%256)+0x0100, hibyte((cpu->i-1)%65536));
#define pushretaddr2 writememory(((cpu->s++)%256)+0x0100, 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)&0x1FF; cpu->cf=x>=256; x&=0xFF; setzf(x); }
#define subf(x,y) addf(x,(-y)&0xFF);
#define cmpf(x,y) { int t=x+((-y)&0xFF); cpu->cf=t>=256; t&=0xFF; 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++)%256)+0x0100, 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; cpu->cycle = 0;
#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 = 0x01; }
		if(cpu->rfg || cpu->ifg || (cpu->irq && cpu->ien)) {
			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;
}
