#include	"l.h"

void
span(void)
{
	Prog *p, *q;
	long v, c, idat;
	Optab *o;
	int m, n;

	xdefine("etext", STEXT, 0L);
	xdefine("a6base", STEXT, 0L);
	idat = INITDAT;
	for(p = firstp; p != P; p = p->link) {
		if(p->as == ATEXT)
			curtext = p;
		n = 0;
		if((q = p->pcond) != P)
			if(q->back != 2)
				n = 1;
		p->back = n;
		if(p->as == AADJSP) {
			p->to.type = D_A0+7;
			v = -p->from.offset;
			p->from.offset = v;
			if((v < -8 && v >= -32768L) || (v > 8 && v < 32768L)) {
				p->as = ALEA;
				p->from.type = I_INDIR | (D_A0+7);
				continue;
			}
			p->as = AADDL;
			if(v < 0) {
				p->as = ASUBL;
				v = -v;
				p->from.offset = v;
			}
			if(v >= 0 && v <= 8)
				p->from.type = D_QUICK;
			if(v == 0)
				p->as = ANOP;
		}
	}
	n = 0;

start:
	if(debug['v'])
		Bprint(&bso, "%5.2f span\n", cputime());
	Bflush(&bso);
	c = INITTEXT;
	for(p = firstp; p != P; p = p->link) {
		if(p->as == ATEXT)
			curtext = p;
		o = &optab[p->as];
		p->pc = c;
		m = mmsize[o->optype];
		if(m == 0) {
			if(p->as == AWORD)
				m = 2;
			if(p->as == ALONG)
				m = 4;
			p->mark = m;
			c += m;
			continue;
		}
		if(p->from.type != D_NONE)
			m += andsize(p, &p->from);
		if(p->to.type == D_BRANCH) {
			if(p->pcond == P)
				p->pcond = p;
			c += m;
			if(m == 2)
				m |= 0100;
			p->mark = m;
			continue;
		}
		if(p->to.type != D_NONE)
			m += andsize(p, &p->to);
		p->mark = m;
		c += m;
	}

loop:
	n++;
	if(debug['v'])
		Bprint(&bso, "%5.2f span %d\n", cputime(), n);
	Bflush(&bso);
	if(n > 60) {
		diag("span must be looping");
		errorexit();
	}
	c = INITTEXT;
	for(p = firstp; p != P; p = p->link) {
		if(p->as == ATEXT)
			curtext = p;
		if((m = p->mark) & 0100) {
			q = p->pcond;
			v = q->pc - 2;
			if(p->back)
				v -= c;
			else
				v -= p->pc;
			p->pc = c;
			if(v < -32768L || v >= 32768L) {
				c += 6;		/* only jsr and jmp can do this */
			} else
			if(v < -128 || v >= 128)
				c += 4;
			else
			if(v == 0) {
				c += 4;
				p->mark = 4;
			} else
				c += 2;
			continue;
		}
		p->pc = c;
		c += m;
	}
	if(c != textsize) {
		textsize = c;
		goto loop;
	}
	if(INITRND)
		INITDAT = rnd(c, INITRND);
	if(INITDAT != idat) {
		idat = INITDAT;
		goto start;
	}
	xdefine("etext", STEXT, c);
	xdefine("a6base", STEXT, INITDAT+A6OFFSET);
	if(debug['v'])
		Bprint(&bso, "etext = %lux\n", c);
	Bflush(&bso);
	for(p = textp; p != P; p = p->pcond)
		p->from.sym->value = p->pc;
	textsize = c - INITTEXT;
}

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;
	}
	if(s->type == STEXT && s->value == 0)
		s->value = v;
}

int
andsize(Prog *p, Adr *ap)
{
	int t, n;
	long v;
	Optab *o;

	t = ap->type;
	n = simple[t];
	if(n != 0177) {
		v = ap->offset;
		if(v == 0)
			return 0;
		if((n&070) != 020)	/* D_INDIR */
			return 0;
		if(v == 0)
			return 0;
		return 2;
	}
	if((t&I_MASK) == I_ADDR)
		t = D_CONST;
	switch(t) {

	default:
		return 0;

	case D_STACK:
	case D_AUTO:
	case D_PARAM:
		v = ap->offset;
		if(v == 0)
			return 0;
		return 2;

	case I_INDIR|D_CONST:
		v = ap->offset;
		if(v < -32768L || v >= 32768L)
			return 4;
		return 2;

	case D_STATIC:
	case D_EXTERN:
		if(ap->sym->type == STEXT) {
			if(HEADTYPE == 4)
				return 2;
			return 4;
		}
		v = ap->sym->value + ap->offset - A6OFFSET;
		if(v == 0)
			return 0;
		if(v < -32768L || v >= 32768L)
			return 4;
		return 2;

	case D_CONST:
	case D_FCONST:
		o = &optab[p->as];
		if(ap == &(p->from))
			return o->srcsp;
		return o->dstsp;

	case D_CCR:
	case D_SR:
		if(p->as == AMOVW)
			return 0;
		return 2;

	case D_USP:
		t = p->from.type;
		if(t >= D_A0 && t <= D_A0+8)
			return 0;
		t = p->to.type;
		if(t >= D_A0 && t <= D_A0+8)
			return 0;

	case D_SFC:
	case D_DFC:
	case D_CACR:
	case D_VBR:
	case D_CAAR:
	case D_MSP:
	case D_ISP:
	case D_FPCR:
	case D_FPSR:
	case D_FPIAR:
 	case D_TC:
	case D_ITT0:
	case D_ITT1:
	case D_DTT0:
	case D_DTT1:
	case D_MMUSR:
	case D_URP:
	case D_SRP:
		return 2;
	}
}

void
putsymb(Sym *s, int t, long v)
{
	int i, f;
	char *n;

	n = s->name;
	if(t == 'f')
		n++;
	lput(v);
	if(s->version)
		t += 'a' - 'A';
	CPUT(t+0x80);			/* 0x80 is variable length */

	if(t == 'Z' || t == 'z') {
		CPUT(n[0]);
		for(i=1; n[i] != 0 || n[i+1] != 0; i += 2) {
			CPUT(n[i]);
			CPUT(n[i+1]);
		}
		CPUT(0);
		CPUT(0);
		i++;
	}
	else {
		for(i=0; n[i]; i++)
			CPUT(n[i]);
		CPUT(0);
	}
	symsize += 4 + 1 + i + 1;

	if(debug['n']) {
		if(t == 'z' || t == 'Z') {
			Bprint(&bso, "%c %.8lux ", t, v);
			for(i=1; n[i] != 0 || n[i+1] != 0; i+=2) {
				f = ((n[i]&0xff) << 8) | (n[i+1]&0xff);
				Bprint(&bso, "/%x", f);
			}
			Bprint(&bso, "\n");
			return;
		}
		if(s->version)
			Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, n, s->version);
		else
			Bprint(&bso, "%c %.8lux %s\n", t, v, n);
	}
}

void
asmsym(void)
{
	Prog *p;
	Auto *a;
	Sym *s;
	int h;

	s = lookup("etext", 0);
	if(s->type == STEXT)
		putsymb(s, 'T', s->value);
	s = lookup("a6base", 0);
	if(s->type == STEXT)
		putsymb(s, 'D', s->value);

	for(h=0; h<NHASH; h++)
		for(s=hash[h]; s!=S; s=s->link)
			switch(s->type) {
			case SDATA:
				putsymb(s, 'D', s->value+INITDAT);
				continue;

			case SBSS:
				putsymb(s, 'B', s->value+INITDAT);
				continue;

			case SFILE:
				putsymb(s, 'f', s->value);
				continue;
			}

	for(p=textp; p!=P; p=p->pcond) {
		s = p->from.sym;
		if(s->type != STEXT)
			continue;

		/* filenames first */
		for(a=p->to.autom; a; a=a->link)
			if(a->type == D_FILE)
				putsymb(a->asym, 'z', a->aoffset);
			else
			if(a->type == D_FILE1)
				putsymb(a->asym, 'Z', a->aoffset);

		putsymb(s, 'T', s->value);

		/* auto and param after */
		for(a=p->to.autom; a; a=a->link)
			if(a->type == D_AUTO)
				putsymb(a->asym, 'a', -a->aoffset);
			else
			if(a->type == D_PARAM)
				putsymb(a->asym, 'p', a->aoffset);
	}
	if(debug['v'] || debug['n'])
		Bprint(&bso, "symsize = %lud\n", symsize);
	Bflush(&bso);
}

#define	MINLC	2
void
asmsp(void)
{
	long oldpc, oldsp;
	Prog *p;
	int s;
	long v;

	oldpc = INITTEXT;
	oldsp = 0;
	for(p = firstp; p != P; p = p->link) {
		if(p->stkoff == oldsp || p->as == ATEXT || p->as == ANOP) {
			if(p->as == ATEXT)
				curtext = p;
			if(debug['G'])
				Bprint(&bso, "%6lux %4ld%P\n",
					p->pc, p->stkoff, p);
			continue;
		}
		if(debug['G'])
			Bprint(&bso, "\t\t%6ld", spsize);
		v = (p->pc - oldpc) / MINLC;
		while(v) {
			s = 127;
			if(v < 127)
				s = v;
			CPUT(s+128);	/* 129-255 +pc */
			if(debug['G'])
				Bprint(&bso, " pc+%d*2(%d)", s, s+128);
			v -= s;
			spsize++;
		}
		v = p->stkoff - oldsp;
		oldsp = p->stkoff;
		oldpc = p->pc + MINLC;
		if(v & 3 || v > 64L*4L || v < -64L*4L) {
			CPUT(0);	/* 0 vvvv +sp */
			lput(v);
			if(debug['G']) {
				if(v > 0)
					Bprint(&bso, " sp+%ld*1(%d,%ld)\n",
						v, 0, v);
				else
					Bprint(&bso, " sp%ld*1(%d,%ld)\n",
						v, 0, v);
				Bprint(&bso, "%6lux %4ld%P\n",
					p->pc, p->stkoff, p);
			}
			spsize += 5;
			continue;
		}
		s = v/4;
		if(s > 0) {
			CPUT(0+s);	/* 1-64 +sp */
			if(debug['G']) {
				Bprint(&bso, " sp+%d*4(%d)\n", s, 0+s);
				Bprint(&bso, "%6lux %4ld%P\n",
					p->pc, p->stkoff, p);
			}
		} else {
			CPUT(64-s);	/* 65-128 -sp */
			if(debug['G']) {
				Bprint(&bso, " sp%d*4(%d)\n", s, 64-s);
				Bprint(&bso, "%6lux %4ld%P\n",
					p->pc, p->stkoff, p);
			}
		}
		spsize++;
	}
	while(spsize & 1) {
		s = 129;
		CPUT(s);
		spsize++;
	}
	if(debug['v'] || debug['G'])
		Bprint(&bso, "stsize = %ld\n", spsize);
	Bflush(&bso);
}

void
asmlc(void)
{
	long oldpc, oldlc;
	Prog *p;
	long v, s;

	oldpc = INITTEXT;
	oldlc = 0;
	for(p = firstp; p != P; p = p->link) {
		if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
			if(p->as == ATEXT)
				curtext = p;
			if(debug['L'])
				Bprint(&bso, "%6lux %P\n",
					p->pc, p);
			continue;
		}
		if(debug['L'])
			Bprint(&bso, "\t\t%6ld", lcsize);
		v = (p->pc - oldpc) / MINLC;
		while(v) {
			s = 127;
			if(v < 127)
				s = v;
			CPUT(s+128);	/* 129-255 +pc */
			if(debug['L'])
				Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
			v -= s;
			lcsize++;
		}
		s = p->line - oldlc;
		oldlc = p->line;
		oldpc = p->pc + MINLC;
		if(s > 64 || s < -64) {
			CPUT(0);	/* 0 vv +lc */
			CPUT(s>>24);
			CPUT(s>>16);
			CPUT(s>>8);
			CPUT(s);
			if(debug['L']) {
				if(s > 0)
					Bprint(&bso, " lc+%ld(%d,%ld)\n",
						s, 0, s);
				else
					Bprint(&bso, " lc%ld(%d,%ld)\n",
						s, 0, s);
				Bprint(&bso, "%6lux %P\n",
					p->pc, p);
			}
			lcsize += 5;
			continue;
		}
		if(s > 0) {
			CPUT(0+s);	/* 1-64 +lc */
			if(debug['L']) {
				Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
				Bprint(&bso, "%6lux %P\n",
					p->pc, p);
			}
		} else {
			CPUT(64-s);	/* 65-128 -lc */
			if(debug['L']) {
				Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
				Bprint(&bso, "%6lux %P\n",
					p->pc, p);
			}
		}
		lcsize++;
	}
	while(lcsize & 1) {
		s = 129;
		CPUT(s);
		lcsize++;
	}
	if(debug['v'] || debug['L'])
		Bprint(&bso, "lcsize = %ld\n", lcsize);
	Bflush(&bso);
}
