#include "common"
#define OSIZE	256

#define	GEN	0
#define	BRAN	1
#define	JUMP	2
#define	LOAD	3
#define	RFLAG	4

select(f) int f;
	{if (fout != f) {flush(); fout = f; } }

char *mako(i) int i;
	{int j, k;
	static char buf[8];
	j = k = 0;
	if (i < 0)
		{buf[k++] = '1';
		i =& 077777; }
	else for ( ; j < 4; j++)
		{if ((i >> 12) & 7) break;
		i =<< 3; }
	for ( ; j < 5; j++)
		{buf[k++] = (i >> 12) & 7 | '0';
		i =<< 3; }
	buf[k] = '\0';
	return(buf); }

puto(i) int i;
	{printf(" %s", mako(i)); }

int wdot 0;		/* working dot for pipe */
int wbias 1;		/* working dot bias for pipe */
int welcs[3] {0, 0, 0};	/* working elc's for pipe */
struct	{int o, v, b; }
	out[OSIZE];	/* the output pipe */
int hiwater 0;		/* high water mark in pipe */
int nout 0;		/* no. of words in pipe */
int osize OSIZE;	/* working size of pipe */
int shorter 0;		/* no. of jumps made shorter */
int longer 0;		/* no. of jumps left longer */

putovb(o, v, b) int o, v, b;
	{write(fcode, &o, 2);
	write(fcode, &v, 2);
	write(fcode, &b, 2); }

pipe(o, v, b) int o, v, b;
	{int drain, i, j, k;
	extern struct token stack[];
	extern int sthigh;
	extern int stackoff;
	if (o == -1) drain = TRUE;
	else {
		drain = FALSE;
		out[nout].o = o;
		out[nout].v = v;
		out[nout].b = b;
		if (debug)
			{puto(o); puto(v); puto(b);
			putchar('\n'); }
		nout++; }
	for (i = 0; i < nout; i++)
		{o = out[i].o;
		b = out[i].b;
		if (b > 2 && test(&symtab[b], DEFF))
			{v = symtab[b].value;
			b = symtab[b].bias; }
		else v = out[i].v;
		if ((o & 3) != JUMP) switch (o & 3) {
	 case GEN:
	 case BRAN:	putovb(o, out[i].v, out[i].b);
			wdot =+ 2; break;

	 case LOAD:	putovb(o, v, b);
			welcs[wbias] = wdot;
			wdot = v; wbias = b; break;	}

		else if (b > 2 && nout < osize && !drain) break;
		else if (b > 2 || b != wbias ||
			(v - wdot) < -254 || 256 < (v - wdot) || drain)
			{putovb(o, out[i].v, out[i].b);
			wdot =+ (o >> 8) == TRUE ? 4 : 6;
			longer++; }
		else {
			putovb(o & ~3 | BRAN, out[i].v, out[i].b);
			wdot =+ 2;
			k = (o >> 8) == TRUE ? 2 : 4;
			if (i + 1 >= nout) elc->value =- k;
			else {
				shorter++;
				for (j = i + 1; j < nout; j++)
					if (out[j].b == wbias &&
						out[j].v >= wdot) out[j].v =- k;
				for (j = dot - symtab; j < spused; j++)
					if (symtab[j].bias == wbias &&
						symtab[j].value >= wdot)
						symtab[j].value =- k;
				for (j = 0; j <= sthigh; j++)
					if (stack[j].sbias == wbias &&
						stack[j].start >= wdot)
						stack[j].start =- k;
				}
			}
		}
	if (i)
		{if (i > hiwater && i < nout) hiwater = i;
		for (j = i; j < nout; j++)
			{out[j - i].o = out[j].o;
			out[j - i].v = out[j].v;
			out[j - i].b = out[j].b; }
		nout =- i;
	}
	if (drain)
		{welcs[wbias] = wdot;
		if (stackoff)
			error("stack offset %o", stackoff);
		}
	}

gen(v, b) int v, b;
	{if (elc->bias != 1 && elc->bias != 2 || elc->value & 1)
		{error("bad . %o+%o", elc->value, elc->bias);
		setelc(&symtab[1].value, 1);
		if (elc->value & 1) elc->value++; }
	settoelc(tlist); tlist = NULL;
	elc->value =+ 2;
	pipe(GEN, v, b); }

int errors FALSE;		/* TRUE if any errors */

error(f, s1, s2, s3, s4, s5) char *f, *s1, *s2, *s3, *s4, *s5;
	{errors = TRUE;
	select(1);
	printf("%d: ", line);
	printf(f, s1, s2, s3, s4, s5);
	putchar('\n'); }

char sbuf[128];		/* holds diagnostic strings */
char *sp sbuf;		/* -> current 32-byte buffer */

char *sterm[] {"EOF", "??", "error", "DECL", ",", "(", ")", "\"", ";", "[",
		"]", "++", "OP", "&&", "||", "ROP", "STRING",
		"IDENT", "GOTO", "IF", "ELSE", "DO", "BREAK", "WHILE",
		"CONDITION", "LABEL" };
char *pre[] {"reg ", "--[", "[", "&", "mem ", "", "[reg ",
		"[--[", "[[", "[&", "[mem ", "[" };
char *post[] {"", "]", "]++", "", "", "]", "]",
		"]]", "]++]", "]", "]", "]]" };

char *scopy(s1, s2) char *s1, *s2;
	{while (*s1) *s2++ = *s1++;
	return (s2); }

char *s(p1) struct at *p1;
	{int t;
	t = p1->type;
	if (t < REG) return (test(p1, DEFF) || p1->value == NULL ?
		sterm[t] :
		sn((p1->value)->name));
	t = (t - REG) >> 3;
	sp = scopy(pre[t], sp);
	if (!test(p1, DEFF)) sp = scopy(sn((p1->value)->name), sp);
	else {
		if (p1->value || !p1->bias) sp = scopy(mako(p1->value), sp);
		if (p1->value && p1->bias) *sp++ = '+';
		if (p1->bias) sp = scopy(sn(symtab[p1->bias].name), sp);
	}
	if ((p1->type & ~R) == MIDX || (p1->type & ~R) == IMIDX)
		{*sp++ = '['; *sp++ = 'r';
		*sp++ = (p1->type & R) + '0'; }
	sp = scopy(post[t], sp);
	*sp = '\0';
	t = sp - sbuf;
	sp = &sbuf[((t + 32) & ~31) % 128];
	return (&sbuf[t & ~31]); }

int stackoff 0;		/* stack offset during calls */
int nargs;		/* no. of arg words in instruction */
int argb[2];		/* offset for stack references */
int argf[2];		/* TRUE if arg is self-relative */
struct at *argp[2];	/* -> stack entry for arg */

doloc(p1) struct at *p1;
	{struct at *p;
	int c, r, v;
	c = p1->type & ~R;
	r = p1->type & R;
	v = (p = ref(p1))->value;
	switch (c) {
 case REG:	break;
 case IREG:	v =+ 010; break;
 case RINC:	v =+ 020; break;
 case IRINC:	v =+ 030; break;
 case RDEC:	v =+ 040; break;
 case IRDEC:	v =+ 050; break;

 default:	argb[nargs] = 0;
		argf[nargs] = FALSE;
		argp[nargs] = p;
		switch (c) {
	 case MIDX:	if (test(p, DEFF) && v == 0 && p->bias == 0 &&
				(stackoff == 0 || r != 6))
				return (010 + r);
			v = 060 + r; break;
	 case IMIDX:	v = 070 + r; break;
	 case CON:	v = 027; break;
	 case ICON:	v = 037; break;
	 case MEM:	v = 067; argf[nargs] = TRUE; break;
	 case IMEM:	v = 077; argf[nargs] = TRUE; break;

	 default:	v = -1;
					}
		if ((v & 067) == 066)
			argb[nargs] = stackoff;
		nargs++;
				}
	return (v); }

geninst(i, ps, pd) int i; struct at *ps, *pd;
	{int j;
	nargs = 0;
	j = ps ? doloc(ps) << 6 : 0;
	if (pd) j =| doloc(pd);
	if (j < 0) error ("illegal operand in %s op %s",
		s(ps), s(pd));
	else {
		gen(i | j, 0);
		for (j = 0; j < nargs; j++) {
			elc->value =+ 2;
			pipe(GEN + (argf[j] ? RFLAG : 0),
				argb[j] + argp[j]->value,
				argp[j]->bias);
			}
		}
	}

target(pp1) struct at **pp1;
	{
	struct at *linkup();
	if (*pp1 == NULL) return;
	tlist = linkup(tlist, *pp1);
	*pp1 = NULL; }

struct at *fjump(k, p1, p2) int k; struct at *p1, *p2;
	{char crname[10];
	p1 = linkup(p1, p2);
	if (k == FALSE) return (p1);
	if (p1 == NULL)
		{*scopy(mako(spused), scopy("..", crname))
			= '\0';
		p1 = add(crname, CRSF, MEM, 0, 0); }
	jump(k, p1->value, p1->bias);
	return (p1); }

struct at *linkup(p1, p2) struct symbol *p1, *p2;
	{struct symbol *p;
	if (p1 == NULL) {p1 = p2; p2 = NULL; }
	if (p2 == NULL) return (p1);
	if (test(p2, DEFF)) {p = p1; p1 = p2; p2 = p; }
	if (test(p1, DEFF)) define(p2, 0, MEM, p1->value, p1->bias);
	else {
		for (p = p1; p != &symtab[p->bias]; p = &symtab[p->bias]) ;
		p->bias = p2 - symtab; }
	return (p1); }

jump(k, v, b) int k, v, b;
	{int i;
	if (k == FALSE) return;
	if (tlist && k == TRUE)
		{if (!test(&symtab[b], DEFF)) linkup(&symtab[b], tlist);
		else define(tlist, 0, MEM, v, b); }
	else settoelc(tlist);
	tlist = NULL;
	elc->value =+ (k == TRUE) ? 4 : 6;
	pipe(JUMP | (k << 8), v, b);
	}

setelc(v, b) int v, b;
	{if (b != elc->bias || v > elc->value)
		{if (v & 1) v++;
		settoelc(tlist); tlist = NULL;
		elc = &symtab[b];
		elc->value = v;
		if (b == 1 || b == 2) pipe(LOAD, v, b);
		}
	else if (v < elc->value) error("can't back up . to %o+%o", v, b); }
