#include	"l.h"

void
span(void)
{
	Prog *p;
	Sym *setext;
	Optab *o;
	int m;
	long c;

	if(debug['v'])
		Bprint(&bso, "%5.2f span\n", cputime());
	Bflush(&bso);
	c = INITTEXT;
	for(p = firstp; p != P; p = p->link) {
		p->pc = c;
		o = oplook(p);
		m = o->size;
		if(m == 0) {
			if(p->as == ATEXT) {
				curtext = p;
				autosize = p->to.offset + 4;
				if(p->from.sym != S)
					p->from.sym->value = c;
				continue;
			}
			if(p->as != ANOP)
				diag("zero-width instruction\n%P", p);
			continue;
		}
		c += m;
	}
	c = rnd(c, 8);

	setext = lookup("etext", 0);
	if(setext != S) {
		setext->value = c;
		textsize = c - INITTEXT;
	}
	if(INITRND)
		INITDAT = rnd(c, INITRND);
	if(debug['v'])
		Bprint(&bso, "tsize = %lux\n", textsize);
	Bflush(&bso);
}
		
void
xdefine(char *p, int t, long v)
{
	Sym *s;

	s = lookup(p, 0);
	if(s->type == 0 || s->type == SXREF) {
		s->type = t;
		s->value = v;
	}
}

long
regoff(Adr *a)
{

	instoffset = 0;
	aclass(a);
	return instoffset;
}

int
aclass(Adr *a)
{
	Sym *s;
	int t;

	switch(a->type) {
	case D_NONE:
		return C_NONE;

	case D_REG:
		return C_REG;

	case D_FREG:
		return C_FREG;

	case D_CREG:
		return C_CREG;

	case D_PREG:
		if(a->reg == D_FSR)
			return C_FSR;
		if(a->reg == D_FPQ)
			return C_FQ;
		return C_PREG;

	case D_OREG:
		switch(a->name) {
		case D_EXTERN:
		case D_STATIC:
			if(a->sym == S)
				break;
			t = a->sym->type;
			if(t == 0 || t == SXREF) {
				diag("undefined external: %s in %s",
					a->sym->name, TNAME);
				a->sym->type = SDATA;
			}
			instoffset = a->sym->value + a->offset - BIG;
			if(instoffset >= -BIG && instoffset < BIG) {
				if(instoffset & 7)
					return C_OSEXT;
				return C_ESEXT;
			}
			if(instoffset & 7)
				return C_OLEXT;
			return C_ELEXT;
		case D_AUTO:
			instoffset = autosize + a->offset;
			goto dauto;

		case D_PARAM:
			instoffset = autosize + a->offset + 4L;
		dauto:
			if(instoffset >= -BIG && instoffset < BIG) {
				if(instoffset & 7)
					return C_OSAUTO;
				return C_ESAUTO;
			}
			if(instoffset & 7)
				return C_OLAUTO;
			return C_ELAUTO;
		case D_NONE:
			instoffset = a->offset;
			if(instoffset == 0)
				return C_ZOREG;
			if(instoffset >= -BIG && instoffset < BIG)
				return C_SOREG;
			return C_LOREG;
		}
		return C_GOK;

	case D_ASI:
		if(a->name == D_NONE)
			return C_ASI;
		return C_GOK;

	case D_CONST:
		switch(a->name) {

		case D_NONE:
			instoffset = a->offset;
		consize:
			if(instoffset == 0)
				return C_ZCON;
			if(instoffset >= -0x1000 && instoffset <= 0xfff)
				return C_SCON;
			if((instoffset & 0x3ff) == 0)
				return C_UCON;
			return C_LCON;

		case D_EXTERN:
		case D_STATIC:
			s = a->sym;
			if(s == S)
				break;
			t = s->type;
			if(t == 0 || t == SXREF) {
				diag("undefined external: %s in %s",
					s->name, TNAME);
				s->type = SDATA;
			}
			if(s->type == STEXT || s->type == SLEAF) {
				instoffset = s->value + a->offset;
				return C_LCON;
			}
			if(s->type == SCONST) {
				instoffset = s->value + a->offset;
				goto consize;
			}
			instoffset = s->value + a->offset - BIG;
			if(instoffset >= -BIG && instoffset < BIG && instoffset != 0)
				return C_SECON;
			instoffset = s->value + a->offset + INITDAT;
/* not sure why this barfs */
return C_LCON;
/*
			if(instoffset == 0)
				return C_ZCON;
			if(instoffset >= -0x1000 && instoffset <= 0xfff)
				return C_SCON;
			if((instoffset & 0x3ff) == 0)
				return C_UCON;
			return C_LCON;
*/

		case D_AUTO:
			instoffset = autosize + a->offset;
			if(instoffset >= -BIG && instoffset < BIG)
				return C_SACON;
			return C_LACON;

		case D_PARAM:
			instoffset = autosize + a->offset + 4L;
			if(instoffset >= -BIG && instoffset < BIG)
				return C_SACON;
			return C_LACON;
		}
		return C_GOK;

	case D_BRANCH:
		return C_SBRA;
	}
	return C_GOK;
}

Optab*
oplook(Prog *p)
{
	int a1, a2, a3, r;
	char *c1, *c3;
	Optab *o, *e;

	a1 = p->optab;
	if(a1)
		return optab+(a1-1);
	a1 = p->from.class;
	if(a1 == 0) {
		a1 = aclass(&p->from) + 1;
		p->from.class = a1;
	}
	a1--;
	a3 = p->to.class;
	if(a3 == 0) {
		a3 = aclass(&p->to) + 1;
		p->to.class = a3;
	}
	a3--;
	a2 = C_NONE;
	if(p->reg != NREG)
		a2 = C_REG;
	r = p->as;
	o = oprange[r].start;
	if(o == 0)
		o = oprange[r].stop; /* just generate an error */
	e = oprange[r].stop;
	c1 = xcmp[a1];
	c3 = xcmp[a3];
	for(; o<e; o++)
		if(o->a2 == a2)
		if(c1[o->a1])
		if(c3[o->a3]) {
			p->optab = (o-optab)+1;
			return o;
		}
	diag("illegal combination %A %d %d %d",
		p->as, a1, a2, a3);
	if(1||!debug['a'])
		prasm(p);
	if(o == 0)
		errorexit();
	return o;
}

int
cmp(int a, int b)
{

	if(a == b)
		return 1;
	switch(a) {
	case C_LCON:
		if(b == C_ZCON || b == C_SCON || b == C_UCON)
			return 1;
		break;
	case C_UCON:
		if(b == C_ZCON)
			return 1;
		break;
	case C_SCON:
		if(b == C_ZCON)
			return 1;
		break;
	case C_LACON:
		if(b == C_SACON)
			return 1;
		break;
	case C_LBRA:
		if(b == C_SBRA)
			return 1;
		break;
	case C_ELEXT:
		if(b == C_ESEXT)
			return 1;
		break;
	case C_LEXT:
		if(b == C_SEXT ||
		   b == C_ESEXT || b == C_OSEXT ||
		   b == C_ELEXT || b == C_OLEXT)
			return 1;
		break;
	case C_SEXT:
		if(b == C_ESEXT || b == C_OSEXT)
			return 1;
		break;
	case C_ELAUTO:
		if(b == C_ESAUTO)
			return 1;
		break;
	case C_LAUTO:
		if(b == C_SAUTO ||
		   b == C_ESAUTO || b == C_OSAUTO ||
		   b == C_ELAUTO || b == C_OLAUTO)
			return 1;
		break;
	case C_SAUTO:
		if(b == C_ESAUTO || b == C_OSAUTO)
			return 1;
		break;
	case C_REG:
		if(b == C_ZCON)
			return 1;
		break;
	case C_LOREG:
		if(b == C_ZOREG || b == C_SOREG)
			return 1;
		break;
	case C_SOREG:
		if(b == C_ZOREG)
			return 1;
		break;

	case C_ANY:
		return 1;
	}
	return 0;
}

int
ocmp(const void *a1, const void *a2)
{
	Optab *p1, *p2;
	int n;

	p1 = (Optab*)a1;
	p2 = (Optab*)a2;
	n = p1->as - p2->as;
	if(n)
		return n;
	n = p1->a1 - p2->a1;
	if(n)
		return n;
	n = p1->a2 - p2->a2;
	if(n)
		return n;
	n = p1->a3 - p2->a3;
	if(n)
		return n;
	return 0;
}

void
buildop(void)
{
	int i, n, r;

	for(i=0; i<C_NCLASS; i++)
		for(n=0; n<C_NCLASS; n++)
			xcmp[i][n] = cmp(n, i);
	for(n=0; optab[n].as != AXXX; n++)
		;
	qsort(optab, n, sizeof(optab[0]), ocmp);
	for(i=0; i<n; i++) {
		r = optab[i].as;
		oprange[r].start = optab+i;
		while(optab[i].as == r)
			i++;
		oprange[r].stop = optab+i;
		i--;
		
		switch(r)
		{
		default:
			diag("unknown op in build: %A", r);
			errorexit();
		case AADD:
			oprange[AADDX] = oprange[r];
			oprange[ASUB] = oprange[r];
			oprange[ASUBX] = oprange[r];
			oprange[AMUL] = oprange[r];
			oprange[AXOR] = oprange[r];
			oprange[AXNOR] = oprange[r];
			oprange[AAND] = oprange[r];
			oprange[AANDN] = oprange[r];
			oprange[AOR] = oprange[r];
			oprange[AORN] = oprange[r];
			oprange[ASLL] = oprange[r];
			oprange[ASRL] = oprange[r];
			oprange[ASRA] = oprange[r];
			oprange[AADDCC] = oprange[r];
			oprange[AADDXCC] = oprange[r];
			oprange[ATADDCC] = oprange[r];
			oprange[ATADDCCTV] = oprange[r];
			oprange[ASUBCC] = oprange[r];
			oprange[ASUBXCC] = oprange[r];
			oprange[ATSUBCC] = oprange[r];
			oprange[ATSUBCCTV] = oprange[r];
			oprange[AXORCC] = oprange[r];
			oprange[AXNORCC] = oprange[r];
			oprange[AANDCC] = oprange[r];
			oprange[AANDNCC] = oprange[r];
			oprange[AORCC] = oprange[r];
			oprange[AORNCC] = oprange[r];
			oprange[AMULSCC] = oprange[r];
			oprange[ASAVE] = oprange[r];
			oprange[ARESTORE] = oprange[r];
			break;
		case AMOVB:
			oprange[AMOVH] = oprange[r];
			oprange[AMOVHU] = oprange[r];
			oprange[AMOVBU] = oprange[r];
			oprange[ASWAP] = oprange[r];
			oprange[ATAS] = oprange[r];
			break;
		case ABA:
			oprange[ABN] = oprange[r];
			oprange[AFBA] = oprange[r];
			oprange[AFBN] = oprange[r];
			break;
		case ABE:
			oprange[ABCC] = oprange[r];
			oprange[ABCS] = oprange[r];
			oprange[ABGE] = oprange[r];
			oprange[ABGU] = oprange[r];
			oprange[ABG] = oprange[r];
			oprange[ABLEU] = oprange[r];
			oprange[ABLE] = oprange[r];
			oprange[ABL] = oprange[r];
			oprange[ABNEG] = oprange[r];
			oprange[ABNE] = oprange[r];
			oprange[ABPOS] = oprange[r];
			oprange[ABVC] = oprange[r];
			oprange[ABVS] = oprange[r];

			oprange[AFBE] = oprange[r];
			oprange[AFBG] = oprange[r];
			oprange[AFBGE] = oprange[r];
			oprange[AFBL] = oprange[r];
			oprange[AFBLE] = oprange[r];
			oprange[AFBLG] = oprange[r];
			oprange[AFBNE] = oprange[r];
			oprange[AFBO] = oprange[r];
			oprange[AFBU] = oprange[r];
			oprange[AFBUE] = oprange[r];
			oprange[AFBUG] = oprange[r];
			oprange[AFBUGE] = oprange[r];
			oprange[AFBUL] = oprange[r];
			oprange[AFBULE] = oprange[r];
			break;
		case ATA:
			oprange[ATCC] = oprange[r];
			oprange[ATCS] = oprange[r];
			oprange[ATE] = oprange[r];
			oprange[ATGE] = oprange[r];
			oprange[ATGU] = oprange[r];
			oprange[ATG] = oprange[r];
			oprange[ATLEU] = oprange[r];
			oprange[ATLE] = oprange[r];
			oprange[ATL] = oprange[r];
			oprange[ATNEG] = oprange[r];
			oprange[ATNE] = oprange[r];
			oprange[ATN] = oprange[r];
			oprange[ATPOS] = oprange[r];
			oprange[ATVC] = oprange[r];
			oprange[ATVS] = oprange[r];
			break;
		case AFADDD:
			oprange[AFADDF] = oprange[r];
			oprange[AFADDX] = oprange[r];
			oprange[AFDIVD] = oprange[r];
			oprange[AFDIVF] = oprange[r];
			oprange[AFDIVX] = oprange[r];
			oprange[AFMULD] = oprange[r];
			oprange[AFMULF] = oprange[r];
			oprange[AFMULX] = oprange[r];
			oprange[AFSUBD] = oprange[r];
			oprange[AFSUBF] = oprange[r];
			oprange[AFSUBX] = oprange[r];
			break;
		case AFCMPD:
			oprange[AFCMPF] = oprange[r];
			oprange[AFCMPX] = oprange[r];
			oprange[AFCMPED] = oprange[r];
			oprange[AFCMPEF] = oprange[r];
			oprange[AFCMPEX] = oprange[r];
			break;
		case AFABSF:
			oprange[AFMOVDF] = oprange[r];
			oprange[AFMOVDW] = oprange[r];
			oprange[AFMOVFD] = oprange[r];
			oprange[AFMOVFW] = oprange[r];
			oprange[AFMOVWD] = oprange[r];
			oprange[AFMOVWF] = oprange[r];
			oprange[AFNEGF] = oprange[r];
			oprange[AFSQRTD] = oprange[r];
			oprange[AFSQRTF] = oprange[r];
			break;
		case AFMOVF:
		case AFMOVD:
		case AMOVW:
		case AMOVD:
		case AWORD:
		case ARETT:
		case AJMPL:
		case AJMP:
		case ACMP:
		case ANOP:
		case ATEXT:
		case ADIV:
		case ADIVL:
		case AMOD:
		case AMODL:
			break;
		}
	}
}
