#include "asm.h"
#include "y.tab.h"

typedef struct Exc Exc;
typedef struct Etab Etab;
typedef struct Ldts Ldts;
typedef struct Ldt Ldt;

struct Ldts
{
	int n;
	Ldt *ldt;
	Ldts *next;
};

struct Ldt{
	int sign;
	char *name;
	Ldt *next;
};

struct Exc
{
	int n1, n2, n3, n4, n5, n6;
	Etab *etab;
	Exc	*next;
};

struct Etab
{
	int n;
	char *name;
	Etab *next;
};

static int inldt;
static int nldts;
static Ldts *aldts;
static Ldts *curl;
static int nexcs;
static Exc* aexcs;
static Exc* cure;
static char *srcpath;

static void ldtw(int);
static void ldte(int, char*);

List*
newa(int i, int size)
{
	List *l;
	Array *a;

	a = malloc(sizeof(Array));
	a->i = i;
	a->size = size;
	l = malloc(sizeof(List));
	l->u.a = a;
	l->link = nil;

	return l;
}

List*
newi(vlong v, List *l)
{
	List *n, *t;

	n = malloc(sizeof(List));
	if(l == nil)
		l = n;
	else {
		for(t = l; t->link; t = t->link)
			;
		t->link = n;
	}
	n->link = nil;
	n->u.ival = v;
	n->addr = -1;

	return l;
}

List*
news(String *s, List *l)
{
	List *n;

	n = malloc(sizeof(List));
	n->link = l;
	l = n;
	n->u.str = s;
	n->addr = -1;
	return l;
}

int
digit(char x)
{
	if(x >= 'A' && x <= 'F')
		return x - 'A' + 10;
	if(x >= 'a' && x <= 'f')
		return x - 'a' + 10;
	if(x >= '0' && x <= '9')
		return x - '0';
	diag("bad hex value in pointers");
	return 0;
}

void
heap(int id, int size, String *ptr)
{
	Desc *d, *f;
	char *p;
	int k, i;

	d = malloc(sizeof(Desc));
	d->id = id;
	d->size = size;
	size /= IBY2WD;
	d->map = malloc(size);
	d->np = 0;
	if(dlist == nil)
		dlist = d;
	else {
		for(f = dlist; f->link != nil; f = f->link)
			;
		f->link = d;
	}
	d->link = nil;
	dcount++;

	if(ptr == 0)
		return;
	if(--ptr->len & 1) {
		diag("pointer descriptor has bad length");
		return;	
	}

	k = 0;
	p = ptr->string;
	for(i = 0; i < ptr->len; i += 2) {
		d->map[k++] = (digit(p[0])<<4)|digit(p[1]);
		if(k > size) {
			diag("pointer descriptor too long");
			break;
		}
		p += 2;	
	}
	d->np = k;
}

void
conout(int val)
{
	if(val >= -64 && val <= 63) {
		Bputc(bout, val & ~0x80);
		return;
	}
	if(val >= -8192 && val <= 8191) {
		Bputc(bout, ((val>>8) & ~0xC0) | 0x80);
		Bputc(bout, val);
		return;
	}
	if(val < 0 && ((val >> 29) & 0x7) != 7
	|| val > 0 && (val >> 29) != 0)
		diag("overflow in constant 0x%lux\n", val);
	Bputc(bout, (val>>24) | 0xC0);
	Bputc(bout, val>>16);
	Bputc(bout, val>>8);
	Bputc(bout, val);
}

void
aout(Addr *a)
{
	if(a == nil)
		return;
	if(a->mode & AIND)
		conout(a->off);
	conout(a->val);
}

void
lout(void)
{
	char *p;
	Link *l;

	if(module == nil)
		module = enter("main", 0);

	for(p = module->name; *p; p++)
		Bputc(bout, *p);
	Bputc(bout, '\0');

	for(l = links; l; l = l->link) {
		conout(l->addr);
		conout(l->desc);
		Bputc(bout, l->type>>24);
		Bputc(bout, l->type>>16);
		Bputc(bout, l->type>>8);
		Bputc(bout, l->type);
		for(p = l->name; *p; p++)
			Bputc(bout, *p);
		Bputc(bout, '\0');
	}
}

void
ldtout(void)
{
	Ldts *ls;
	Ldt *l;
	char *p;

	conout(nldts);
	for(ls = aldts; ls != nil; ls = ls->next){
		conout(ls->n);
		for(l = ls->ldt; l != nil; l = l->next){
			Bputc(bout, l->sign>>24);
			Bputc(bout, l->sign>>16);
			Bputc(bout, l->sign>>8);
			Bputc(bout, l->sign);
			for(p = l->name; *p; p++)
				Bputc(bout, *p);
			Bputc(bout, '\0');
		}
	}
	conout(0);
}

void
excout(void)
{
	Exc *e;
	Etab *et;
	char *p;

	if(nexcs == 0)
		return;
	conout(nexcs);
	for(e = aexcs; e != nil; e = e->next){
		conout(e->n3);
		conout(e->n1);
		conout(e->n2);
		conout(e->n4);
		conout(e->n5|(e->n6<<16));
		for(et = e->etab; et != nil; et = et->next){
			if(et->name != nil){
				for(p = et->name; *p; p++)
					Bputc(bout, *p);
				Bputc(bout, '\0');
			}
			conout(et->n);
		}
	}
	conout(0);
}

void
srcout(void)
{
	char *p;

	for(p = srcpath; *p; p++)
		Bputc(bout, *p);
	Bputc(bout, '\0');
}

void
assem(Inst *i)
{
	Desc *d;
	Inst *f, *link;
	int pc, n, hints, o;

	f = 0;
	while(i) {
		link = i->link;
		i->link = f;
		f = i;
		i = link;
	}
	i = f;

	pc = 0;
	for(f = i; f; f = f->link) {
		f->pc = pc++;
		if(f->sym != nil)
			f->sym->value = f->pc;
	}

	if(pcentry >= pc)
		diag("entry pc out of range");
	if(dentry >= dcount)
		diag("entry descriptor out of range");

	conout(XMAGIC);
	hints = 0;
	if(mustcompile)
		hints |= MUSTCOMPILE;
	if(dontcompile)
		hints |= DONTCOMPILE;
	hints |= HASLDT;
	if(nexcs > 0)
		hints |= HASEXCEPT;
	conout(hints);		/* Runtime flags */
	conout(1024);		/* default stack size */
	conout(pc);
	conout(dseg);
	conout(dcount);
	conout(nlink);
	conout(pcentry);
	conout(dentry);

	for(f = i; f; f = f->link) {
		if(f->dst && f->dst->sym) {
			f->dst->mode = AIMM;
			f->dst->val = f->dst->sym->value;
		}
		o = opcode(f);
		if(o == IRAISE){
			f->src = f->dst;
			f->dst = nil;
		}
		Bputc(bout, o);
		n = 0;
		if(f->src)
			n |= SRC(f->src->mode);
		else
			n |= SRC(AXXX);
		if(f->dst)
			n |= DST(f->dst->mode);
		else
			n |= DST(AXXX);
		if(f->reg)
			n |= f->reg->mode;
		else
			n |= AXNON;
		Bputc(bout, n);
		aout(f->reg);
		aout(f->src);
		aout(f->dst);

		if(listing)
			print("%4ld %i\n", f->pc, f);
	}

	for(d = dlist; d; d = d->link) {
		conout(d->id);
		conout(d->size);
		conout(d->np);
		for(n = 0; n < d->np; n++)
			Bputc(bout, d->map[n]);
	}

	dout();
	lout();
	ldtout();
	excout();
	srcout();
}

void
data(int type, int addr, List *l)
{
	List *f;

	if(inldt){
		ldtw(l->u.ival);
		return;
	}

	l->type = type;
	l->addr = addr;

	if(mdata == nil)
		mdata = l;
	else {
		for(f = mdata; f->link != nil; f = f->link)
			;
		f->link = l;
	}
}

void
ext(int addr, int type, String *s)
{
	int i;
	char *p;
	List *n;

	if(inldt){
		ldte(type, s->string);
		return;
	}

	data(DEFW, addr, newi(type, nil));

	n = nil;
	p = s->string;
	for(i = 0; i < s->len; i++)
		n = newi(*p++, n);
	data(DEFB, addr+IBY2WD, n);

	if(addr+s->len > dseg)
		diag("ext beyond mp");
}

void
mklink(int desc, int addr, int type, String *s)
{
	Link *l;

	for(l = links; l; l = l->link)
		if(strcmp(l->name, s->string) == 0)
			diag("%s already defined", s->string);

	nlink++;
	l = malloc(sizeof(Link));
	l->desc = desc;
	l->addr = addr;
	l->type = type;
	l->name = s->string;
	l->link = nil;

	if(links == nil)
		links = l;
	else
		linkt->link = l;
	linkt = l;
}

void
dout(void)
{
	int n, i;
	List *l, *e;

	e = nil;
	for(l = mdata; l; l = e) {
		switch(l->type) {
		case DEFB:
			n = 1;
			for(e = l->link; e && e->addr == -1; e = e->link)
				n++;
			if(n < DMAX)
				Bputc(bout, DBYTE(DEFB, n));
			else {
				Bputc(bout, DBYTE(DEFB, 0));
				conout(n);
			}
			conout(l->addr);
			while(l != e) {
				Bputc(bout, l->u.ival);
				l = l->link;
			}
			break;
		case DEFW:
			n = 1;
			for(e = l->link; e && e->addr == -1; e = e->link)
				n++;
			if(n < DMAX)
				Bputc(bout, DBYTE(DEFW, n));
			else {
				Bputc(bout, DBYTE(DEFW, 0));
				conout(n);
			}
			conout(l->addr);
			while(l != e) {
				n = (int)l->u.ival;
				Bputc(bout, n>>24);
				Bputc(bout, n>>16);
				Bputc(bout, n>>8);
				Bputc(bout, n);
				l = l->link;
			}
			break;
		case DEFL:
			n = 1;
			for(e = l->link; e && e->addr == -1; e = e->link)
				n++;
			if(n < DMAX)
				Bputc(bout, DBYTE(DEFL, n));
			else {
				Bputc(bout, DBYTE(DEFL, 0));
				conout(n);
			}
			conout(l->addr);
			while(l != e) {
				Bputc(bout, l->u.ival>>56);
				Bputc(bout, l->u.ival>>48);
				Bputc(bout, l->u.ival>>40);
				Bputc(bout, l->u.ival>>32);
				Bputc(bout, l->u.ival>>24);
				Bputc(bout, l->u.ival>>16);
				Bputc(bout, l->u.ival>>8);
				Bputc(bout, l->u.ival);
				l = l->link;
			}
			break;
		case DEFF:
			n = 1;
			for(e = l->link; e && e->addr == -1; e = e->link)
				n++;
			if(n < DMAX)
				Bputc(bout, DBYTE(DEFF, n));
			else {
				Bputc(bout, DBYTE(DEFF, 0));
				conout(n);
			}
			conout(l->addr);
			while(l != e) {
				Bputc(bout, l->u.ival>>56);
				Bputc(bout, l->u.ival>>48);
				Bputc(bout, l->u.ival>>40);
				Bputc(bout, l->u.ival>>32);
				Bputc(bout, l->u.ival>>24);
				Bputc(bout, l->u.ival>>16);
				Bputc(bout, l->u.ival>>8);
				Bputc(bout, l->u.ival);
				l = l->link;
			}
			break;
		case DEFS:
			n = l->u.str->len-1;
			if(n < DMAX && n != 0)
				Bputc(bout, DBYTE(DEFS, n));
			else {
				Bputc(bout, DBYTE(DEFS, 0));
				conout(n);
			}
			conout(l->addr);
			for(i = 0; i < n; i++)
				Bputc(bout, l->u.str->string[i]);

			e = l->link;
			break;
		case DEFA:
			Bputc(bout, DBYTE(DEFA, 1));
			conout(l->addr);
			Bputc(bout, l->u.a->i>>24);
			Bputc(bout, l->u.a->i>>16);
			Bputc(bout, l->u.a->i>>8);
			Bputc(bout, l->u.a->i);
			Bputc(bout, l->u.a->size>>24);
			Bputc(bout, l->u.a->size>>16);
			Bputc(bout, l->u.a->size>>8);
			Bputc(bout, l->u.a->size);
			e = l->link;
			break;
		case DIND:
			Bputc(bout, DBYTE(DIND, 1));
			conout(l->addr);
			Bputc(bout, 0);
			Bputc(bout, 0);
			Bputc(bout, 0);
			Bputc(bout, 0);
			e = l->link;
			break;
		case DAPOP:
			Bputc(bout, DBYTE(DAPOP, 1));
			conout(0);
			e = l->link;
			break;
		}
	}

	Bputc(bout, DBYTE(DEFZ, 0));
}

void
ldts(int n)
{
	nldts = n;
	inldt = 1;
}

static void
ldtw(int n)
{
	Ldts *ls, *p;

	ls = malloc(sizeof(Ldts));
	ls->n = n;
	ls->ldt = nil;
	ls->next = nil;
	if(aldts == nil)
		aldts = ls;
	else{
		for(p = aldts; p->next != nil; p = p->next)
			;
		p->next = ls;
	}
	curl = ls;
}

static void
ldte(int n, char *s)
{
	Ldt *l, *p;

	l = malloc(sizeof(Ldt));
	l->sign = n;
	l->name = s;
	l->next = nil;
	if(curl->ldt == nil)
		curl->ldt = l;
	else{
		for(p = curl->ldt; p->next != nil; p = p->next)
			;
		p->next = l;
	}
}

void
excs(int n)
{
	nexcs = n;
}

void
exc(int n1, int n2, int n3, int n4, int n5, int n6)
{
	Exc *e, *es;

	e = malloc(sizeof(Exc));
	e->n1 = n1;
	e->n2 = n2;
	e->n3 = n3;
	e->n4 = n4;
	e->n5 = n5;
	e->n6 = n6;
	e->etab = nil;
	e->next = nil;
	if(aexcs == nil)
		aexcs = e;
	else{
		for(es = aexcs; es->next != nil; es = es->next)
			;
		es->next = e;
	}
	cure = e;
}

void
etab(String *s, int n)
{
	Etab *et, *ets;

	et = malloc(sizeof(Etab));
	et->n = n;
	if(s != nil)
		et->name = s->string;
	else
		et->name = nil;
	et->next = nil;
	if(cure->etab == nil)
		cure->etab = et;
	else{
		for(ets = cure->etab; ets->next != nil; ets = ets->next)
			;
		ets->next = et;
	}
}

void
source(String *s)
{
	srcpath = s->string;
}
