#include <stdio.h>
#include "asm.h"

#ifdef	MSDOS
#define	TMP1	"asm1XXXX.XXX"
#define	TMP2	"asm2XXXX.XXX"
#else
#define	TMP1	"/tmp/asm185XXXXXX"
#define	TMP2	"/tmp/asm285XXXXXX"
#endif

opdclass o_none  = {0, 0, 0, 0};
insclass i_noopd = {1, 0, &o_none, &o_none, 0, 0};
opdef pseudotab[] = {
	"bss"    , &i_noopd, 0x00, do_bss   ,
	"data"   , &i_noopd, 0x00, do_data  ,
	"data2"  , &i_noopd, 0x00, do_data2 ,
	"end"    , &i_noopd, 0x00, do_end   ,
	"equ"    , &i_noopd, 0x00, do_equ   ,
	"include", &i_noopd, 0x00, do_incl  ,
	"listoff", &i_noopd, 0x00, do_loff  ,
	"liston" , &i_noopd, 0x00, do_lon   ,
	"org"    , &i_noopd, 0x00, do_org   ,
	"seg"    , &i_noopd, 0x00, do_seg   ,
	"set"    , &i_noopd, 0x00, do_set   ,
	"string" , &i_noopd, 0x00, do_string,
};

#include	"ops.h"

#define pseudolen sizeof(pseudotab)/sizeof(opdef)

FILE *input[INCLSTACK], *output, *list;
FILE *srcin, *binout;

int errorstat = 0;
char *name;
char *prog;
char *label, *op, *opd;
char buf[100], linecopy[100];
char *filename[INCLSTACK];
int lineno[INCLSTACK];
int currinput = 0;
int pass = 0;
int liston = YES;
int listing = 0;
int binary = 0;
int xref = 0;
int verbose = 0;
int ignerr;
symbol *symtab;
Memad lc;
Word *dummy;
segmnt segmain = {0, 0, "", 0};
segmnt *seghd = &segmain;
segmnt *curseg = &segmain;
segmnt *exprseg;

main (argc, argv)
int argc;
char **argv;
{
	char *t1, *t2;

#ifdef	CPU
	prog=CPU "asm";
#else
	prog="asm";
#endif
	srcin = stdin;
	filename[0] = "stdin";
	binout = stdout;
	getargs (argc, argv);
	if(verbose) (void)fprintf(stderr,"%s " __DATE__ "\n",prog);
	init ();
	output = fopen ((t1 = mktemp (TMP1)), "w+");
	input[0] = srcin;

	if(verbose) (void)fprintf(stderr,"%s pass 1\n",prog);
	if (process ()) reporterr (E_PASS1);
	(void) fclose (input[0]);
	init2 ();
	rewind (output);
	input[0] = output;
	output = fopen ((t2 = mktemp (TMP2)), "w+");

	if(verbose) (void)fprintf(stderr,"%s pass 2\n",prog);
	if (process ()) reporterr (E_PASS2);
	(void) fclose (input[0]);
	(void) unlink (t1);
	input[0] = output;
	output = binout;
	rewind (input[0]);
	sort ();
	(void) fclose (input[0]);
	(void) unlink (t2);
	(void) fclose (output);
	if (xref) crossref ();

	if(verbose) (void)fprintf(stderr,"%s end\n",prog);
	exit(0);
}


/*      Current arguments:
		infile  input file name (default stdin)
		-lfile  listing output to file (default no listing),
			(default file is infile.l);
		-ofile  binary output to file (default binary to stdout),
			(default file is infile.o);
		-xfile  cross reference output to listing file (default no xref);
		-v	verbose flag
*/
void getargs (argc, argv)
int argc;
char **argv;
{
	char *cp, *arg, *lname, *oname;

	while (--argc) {
		arg = *(++argv);
		if (*arg == '-') {
			switch (*(++arg)) {
			case 'l':
				listing ++;
				lname = ++arg;
				break;
			case 'o':
				binary ++;
				oname = ++arg;
				break;
			case 'x':
				xref ++;
				break;
			case 'v':
				verbose ++;
				break;
			}
		} else {
			name = arg;
			if ((srcin = fopen (name, "r")) == NULL)
				reporterr (E_INPUT);
			filename[0] = strcpy (malloc ((unsigned) strlen (name) + 1), name);
			if (cp = index (name, '.'))
				*cp = '\0';
		}
	}
	if (binary) {
		if (!*oname) {
			(void) strcat (strcpy (buf, name), ".o");
			oname = buf;
		}
		binout = fopen (oname, "w");
	}
	if (listing || xref) {
		if (!*lname) {
			(void) strcat (strcpy (buf, name), ".l");
			lname = buf;
		}
		list = fopen (lname, "w");
	}
	return;
}


void init ()
{
	symbol *spt;
	int i;

	lc = seghd->lc = 0;
	symtab = (symbol *) malloc ((unsigned) sizeof (symbol) * NSYMS);
	for (i = 0; i < NSYMS; i++)
		symtab->name = (char *) 0;
	for (spt = predef; *(spt->name); spt++)
		insert (spt->name, spt->value, spt->type, spt->segp, NO);
	return;
}


void init2 ()
{
	segmnt *sp;
	Memad acc;

	acc = seghd->lc;
	seghd->lc = 0;
	for (sp = seghd->next; sp; sp = sp->next) {
		acc += sp->lc;
		sp->lc = sp->start = acc - sp->lc;
	}
	curseg = seghd;
	lc = curseg->lc;
	errorstat = 0;
	return;
}



/*      Gross syntax rules:      <and the rest of us simply obey>
	Input line:     [label] op [operands]
	Comments: Blank lines and anything following ';'
	White space is any combination of blank and/or tab chars.
	Operands cannot contain white space (except within strings)
	    and are separated by commas.
	Label must start in col 1 if it exists.
*/
int process ()
{
	char *ipt;
	int i, done;
	static char *zit = "";
	char *fgets();

	pass ++;
	lineno[0] = 0;
contproc:
	while (fgets (buf, sizeof buf, input[currinput]) != NULL) {
		(void) strcpy (linecopy, buf);
		linecopy[strlen(linecopy)-1] = 0;
		if (pass == 1) fputs (buf, output);
		lineno[currinput]++;
		ipt = buf;
		label = ipt;
		op = opd = zit;
		while (islabel (*ipt)) ipt++;
		if (iseol (*ipt)) {
			if (pass == 2)
				listit (linecopy, lc, dummy, 0);
			continue;
		}
		*ipt++ = 0;
		while (iswhite (*ipt)) ipt++;
		if (iseol (*ipt)) {
			if (pass == 2)
				listit (linecopy, lc, dummy, 0);
			continue;
		}
		op = ipt;
		while (isalnum (*ipt)) ipt++;
		if (iseol (*ipt)) {
			*ipt = 0;
			goto parsed;
		}
		*ipt++ = 0;
		while (iswhite (*ipt)) ipt++;
		opd = ipt;
		while (*ipt != '\n') ipt++;
		*ipt = 0;
parsed:
		if ((i = opsrch (op, pseudotab, pseudolen)) >= 0) {
			done = (pseudotab[i].action) ();
		} else if ((i = opsrch (op, optab, oplen)) >= 0) {
			(void) (optab[i].action) (i);
		} else {
			reporterr (E_ILLOP);
			listit (linecopy, lc, dummy, 0);
		}
		if (done) break;
	}
	if (currinput > 0) {
		free (filename[currinput]);
		filename[currinput] = NULL;
		(void) fclose (input[currinput--]);
		goto contproc;
	}
	return (errorstat);
}

int opsrch (op, table, tablen)
	char *op;
	int tablen;
	opdef table[];
{
	int i;

	for (i = 0; i < tablen; i++)
		if (strcmp (table[i].name, op) == 0)
			return (i);
	return (-1);
}


int do_incl ()
{
	if (pass == 1) {
		char *ip;

		if (++currinput > INCLSTACK)
			reporterr (E_INCL);
		opd++;
		if ((ip = index (opd, '"')) == 0)
			reporterr (E_INCLSYN);
		*ip = 0;
		if ((input[currinput] = fopen (opd, "r")) == NULL)
			reporterr (E_INPUT);
		filename[currinput] = strcpy (malloc ((unsigned) strlen (opd) + 1), opd);
		lineno[currinput] = 0;
	}
	return (0);
}


int do_org ()
{
	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	if (pass == 2)
		listit (linecopy, lc, dummy, 0);
	lc = (Memad) expr (&opd, &ignerr, NO);
	return (0);
}


int do_loff ()
{
	liston = NO;
	return (0);
}


int do_lon ()
{
	liston = YES;
	return (0);
}


int do_equ ()
{
	long eqtemp;

	eqtemp = expr (&opd, &ignerr, NO);
	if (label && *label)
		insert (label, eqtemp, &o_mem, exprseg, NO);
	if (pass == 2)
		listit (linecopy, eqtemp, dummy, 0);
	return (0);
}


int do_seg ()
{
	segmnt *sp, *osp;

	curseg->lc = lc;
	if (opd && *opd) {
		for (sp = seghd; sp; osp = sp, sp = sp->next) {
			if (strcmp (sp->name, opd) == 0) {
				lc = sp->lc;
				curseg = sp;
				goto fin_seg;
			}
		}
	} else {
		lc = seghd->lc;
		curseg = seghd;
	}
	curseg = osp->next = (segmnt *) malloc ((unsigned) sizeof (segmnt));
	curseg->name = strcpy (malloc ((unsigned) strlen (opd) + 1), opd);
	curseg->lc = lc = 0;
	curseg->next = (segmnt *) 0;
fin_seg:
	if (pass == 2)
		listit (linecopy, lc, dummy, 0);
	return (0);
}


int do_set ()
{
	long eqtemp;

	eqtemp = expr (&opd, &ignerr, NO);
	if (label && *label)
		insert (label, eqtemp, &o_mem, exprseg, YES);
	if (pass == 2)
		listit (linecopy, eqtemp, dummy, 0);
	return (0);
}


int do_bss ()
{
	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	if (pass == 2)
		listit (linecopy, lc, dummy, 0);
	lc += (Memad) expr (&opd, &ignerr, NO);
	return (0);
}


int do_end ()
{
	curseg->lc = lc;
	if (pass == 2) {
		listit (linecopy, lc, dummy, 0);
		lc = 0xffff;
		putoutbin (dummy, 0);
	}
	curseg = seghd;
	return (1);
}


int do_data ()
{
	Word temp, templist[MAXBYTPERINS];
	char *tp;
	int count;

	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	tp = opd;
	count = 1;
	if (*opd == ':') {
		++opd;
		count = expr (&opd, &ignerr, NO);
		if (*opd == ':') {
			opd ++;
		} else {
			reporterr (E_EXPR);
			count = 1;
			opd = tp;
		}
	}
	if (pass == 2) {
		int i, j;

		temp = (Word) expr (&opd, &ignerr, NO);
		for (i = 0; i < MAXBYTPERINS; i++)
		       templist[i] = temp;
		for (i = count; i > 0; i -= 3) {
			j = i > MAXBYTPERINS ? MAXBYTPERINS : i;
			listit (linecopy, lc, templist, j);
			lc += j;
			*linecopy = 0;
		}
		for (i = 0; i < count; i++)
			putoutbin (&temp, 1);
	} else
		lc += count;
	return (0);
}


int do_data2 ()
{
	long temp;
	char *tp;
	int count;

	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	tp = opd;
	count = 1;
	if (*opd == ':') {
		++opd;
		count = expr (&opd, &ignerr, NO);
		if (*opd == ':') {
			opd ++;
		} else {
			reporterr (E_EXPR);
			count = 1;
			opd = tp;
		}
	}
	if (pass == 2) {
		int i;

		temp = expr (&opd, &ignerr, NO);
		for (i = 0; i < count; i++) {
			listit (linecopy, lc, (Word *)&temp, 2);
			lc += 2;
			*linecopy = 0;
			putoutbin ((Word *)&temp, 1);
		}
	} else
		lc += (count * 2);
	return (0);
}


int do_string ()
{
	Word buf[120], *bp;
	Word delim;
	int len, i;

	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	delim = *opd++;
	bp = buf;
	while (*opd != delim) {
		if (*opd != '\\')
			*bp++ = *opd;
		else {
			++opd;
			*bp++ = escape (&opd);
		}
		opd++;
	}
	*bp++ = '\0';
	len = bp - buf;
	if (pass == 2) {
		for (bp = buf, i = 0; i < len; i += 3, bp += 3) {
			listit (linecopy, lc + i, bp, len + buf - bp);
			*linecopy = 0;
		}
		putoutbin (buf, len);
	}
	lc += len;
	return (0);
}


int choiceinstr (ins)
	int ins;
{
	choicedef *chpt;
	int num, i, fits;
	Long value;
	static char *opdpt;

	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	chpt = (choicedef *)optab[ins].class;
	num = chpt->field;
	for (opdpt = opd; num > 1; num --) {
		opdpt = index (opdpt, ',');
		if (opdpt == NULL) {
			reporterr (E_NOPS);
			return (0);
		}
		opdpt++;
	}
	value = expr (&opdpt, &i, YES);
	fits = i ? NO :
		(value >= chpt->lorange &&
		 value <= chpt->hirange &&
		 curseg == exprseg);
	if ((i = opsrch ((fits ? chpt->rname : chpt->nname), optab, oplen)) >= 0) {
		(optab[i].action) (i);
	} else {
		reporterr (E_ILLOP);
	}
	return (0);
}


int geninstr (ins)
	int ins;
{
	int nargs, length, i, j, k;
	opdclass *oclass;
	insclass *iclass;
	Word itemtemp, obuf[MAXBYTPERINS];
	unsigned long mask;
	static unsigned long cons = ~0L;
	union itemitem {
		Word s[BYTPERLONG];
		unsigned long l;
		long ls;
	} item;
	char *nextopd;

	if (label && *label)
		insert (label, (long) lc, &o_mem, curseg, NO);
	iclass = optab[ins].class;
	length = iclass->length;
	if (pass == 2) {
		nargs = iclass->mopds;
		item.l = 0L;
		for (i = 0; i < MAXBYTPERINS; i++)
			obuf[i] = 0;
		*obuf = optab[ins].mask;
		nextopd = opd;
		for (j = 0; j < nargs; j++) {
			if (j != 0) {
				if (*nextopd != ',') {
					reporterr (E_NOPS);
				} else {
					nextopd++;
				}
			}
			oclass = iclass->type[j];
			item.l = expr (&nextopd, &ignerr, NO) + oclass->offset -
				(oclass->relative ? lc : 0);
			mask = cons >> (LONGLEN - oclass->length);
			if (item.l < 0L && !oclass->sign)
				reporterr (E_NEG);
			if (((item.ls < 0L && oclass->sign) ? -item.ls : item.ls) & ~mask)
				reporterr (E_TOOBIG);
			item.l &= mask;
			if (oclass->byteswapped) {
				itemtemp = item.s[BYTPERLONG - 2];
				item.s[BYTPERLONG - 2] = item.s[BYTPERLONG - 1];
				item.s[BYTPERLONG - 1] = itemtemp;
			}
			item.l <<= (LONGLEN - 8 - iclass->offset[j]);
			for (k = 0; k < MAXBYTPERINS; k++)
				obuf[k] |= item.s[k];
		}
		putoutbin (obuf, length);
		listit (linecopy, lc, obuf, length);
	}
	lc += length;
	return (0);
}

long expr (string, errind, test)
	char **string;
	int *errind;
	int test;
{
	long exp, val;
	int op, err;
	int uniop = 0;
	segmnt *seg1, *seg2;

	*errind = NO;
	if (**string == '-')
		uniop = 1;
	else if (**string == '~')
		uniop = 2;
	if (uniop)
		(*string)++;
	exp = getval(string, &err, test, &seg1);
	if (err) goto errorexit;
	if (uniop == 2)
		exp = ~exp;
	else if (uniop == 1)
		exp = -exp;
	while (!isdelim(**string)) {
		if ((op = getop (string)) == NOOP) {
			if (!test) reporterr (E_EXPR);
errorexit:              for (; !isdelim (**string); *string++)  ;
			*errind = YES;
			return (0);
		}
		val = getval (string, &err, test, &seg2);
		if (err) goto errorexit;
		if (seg1 && seg2 && seg1 != seg2 && pass == 1)
			reporterr (E_SEG);
		switch (op) {
		case PLUS:
			exp += val;
			break;
		case MINUS:
			exp -= val;
			break;
		case MULT:
			exp *= val;
			break;
		case DIV:
			exp /= val;
			break;
		case MOD:
			exp %= val;
			break;
		case OR:
			exp |= val;
			break;
		case AND:
			exp &= val;
			break;
		case EXOR:
			exp ^= val;
			break;
		case SHLF:
			exp <<= val;
			break;
		case SHRT:
			exp >>= val;
			break;
		default:
			if (!test) reporterr (E_EXPR);
		}
		seg1 = seg2;
	}
	exprseg = seg1;
	return (exp);
}


/*      Snag literals and variable names. The literal classes are:

	[digit]....[digit]      unsigned decimal number
	0[hexdigit]...[hexdigit]unsigned hexadecimal number
	$                       current location counter
	'[char]                 single character, right just, zero fill
	"[char][char]           character pair

	Returns a 16 bit value. Unary operations taken care of in expr;
	byte swapping done if required by geninstr.
*/
long getval (strpt, errorret, test, segment)
	segmnt **segment;
	char **strpt;
	int *errorret;
	int test;
{
	long total;
	char name[33], *npt;
	int i;

	*segment = (segmnt *) 0;
	*errorret = 0;
	total = 0L;
	if (isdigit (**strpt)) {
		if (**strpt == '0') {
			while (isxdigit (**strpt)) {
				total *= 16;
				total += xtod (**strpt);
				(*strpt)++;
			}
		} else {
			while (isdigit (**strpt)) {
				total *= 10;
				total += (Long)(**strpt - '0');
				(*strpt)++;
			}
		}
	} else if (islabel (**strpt)) {
		npt = name;
		while (islabel (**strpt)) {
			*npt++ = **strpt;
			(*strpt)++;
		}
		*npt = '\0';
		if ((i = lookup (name)) == -1) {
			if (pass == 2)
				if (!test) reporterr (E_UNDEF);
			*errorret = 1;
			total = 0L;
		} else {
			total = symtab[i].value;
			if (pass == 2 && symtab[i].segp)
				total += (symtab[i].segp)->start;
			*segment = symtab[i].segp;
		}
	} else if (**strpt == '\'') {
		(*strpt)++;
		if (**strpt == '\\') {
			(*strpt)++;
			total = escape (strpt);
		} else {
			total = **strpt;
			(*strpt)++;
		}
	} else if (**strpt == '"') {
		(*strpt)++;
		if (**strpt == '\\') {
			(*strpt)++;
			total = escape (strpt);
		} else {
			total = **strpt;
			(*strpt)++;
		}
		total <<= 8;
		if (**strpt == '\\') {
			(*strpt)++;
			total |= escape (strpt);
		} else {
			total |= **strpt;
			(*strpt)++;
		}
	} else if (**strpt == '$') {
		total = lc;
		*segment = curseg;
		(*strpt)++;
	} else {
		if (!test) reporterr (E_EXPR);
		*errorret = 1;
	}
	return (total);
}


char escape (st)
	char **st;
{
	switch (*((*st)++)) {
	case 'b':
		return ('\b');
	case 'n':
		return ('\n');
	case 'r':
		return ('\r');
	case '^':
		return ('\033');
	case 'f':
		return ('\f');
	case 't':
		return ('\t');
	case '?':
		return ('\177');
	case '\\':
		return ('\\');
	default:
		(*st)--;
		if (isxdigit (**st) && isxdigit (*(*st + 1))) {
			*st += 2;
			return (xtod (*(*st - 2)) << 4 | xtod (*(*st - 1)));
		} else {
			return (*(*st++));
		}
	}
}


int xtod (c)
	char c;
{
	return ((int) c - (c > '9' ? (c > 'F' ? 'W' : '7') : '0'));
}


int getop (string)
	char **string;
{
	static char ops[] = OPSTRING;
	char *k;

	for (k = ops; *k; k++)
		if (*k == **string) {
			(*string)++;
			return ((int) (k - ops));
		}
	(*string)++;
	return (NOOP);
}


void listit (line, xlc, binbuf, length)
	char *line;
	Memad xlc;
	Word *binbuf;
	int length;
{
	int i;

	if (listing && liston) {
		(void) fprintf (list, "%4ld %04x: ", lineno[currinput], xlc);
		if (length > 3) length = 3;
		for (i = 0; i < length; i++)
			(void) fprintf (list, "%02x ", binbuf[i]);
		for (i = length; i < MAXBYTPERINS; i++)
			(void) fprintf (list, "   ");
		(void) fprintf (list, "    %s\n", line);
	}
	return;
}


/*      Binary output format:

	Intel standard hexadecimal format!!!!

        Output is a series of variable length records consisting of ascii
        characters 0-9, A-F (0x30-0x39, 0x41-0x6). Each character contains
        four bits of significant data; two such characters form a binary
	byte of data, the binary equivalent to the hexadecimal ascii
	characters.

	The data records have the format:

					    /  len covers this \
	+---+---+---+---+---+---+---+---+---+--------//--------+---+---+
	| : |  len  |    address    | 0 | 0 |    data bytes    | cksum |
	+---+---+---+---+---+---+---+---+---+--------//--------+---+---+
	    \             checksum covers this                 /

	Checksum computed such that sum of all bytes except ':' modulo
	the bytelength is 0.

	End Record:

	+---+---+---+---+---+---+---+---+---+---+---+
	| : | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | f | f |
	+---+---+---+---+---+---+---+---+---+---+---+


*/
void putoutbin (binbuf, length)
	Word binbuf[];
	int length;
{
	static Memad address;
	static int count;
	static Word checksum;
	static Word array[BINBUFLEN];
	static Word *curpt = 0;
	static Memad binlc = ~0;
	int i;

	if (binlc != lc) {
		if (curpt != 0) {
			(void) fwrite (":", 1, 1, output);
			putout ((Word) count);
			putout ((Word) address >> 8);
			putout ((Word) address & 0xff);
			(void) fwrite ("00", 2, 1, output);
			for (i = 0; i < count; i++)
				putout (array[i]);
			putout (checksum);
			if (length == 0) {
				(void) fwrite (":00000001ff", 11, 1, output);
			}
		}
		curpt = array;
		count = 0;
		address = binlc = lc;
		checksum = 0;
	}
	for (i = 0; i < length; i++) {
		*curpt++ = binbuf[i];
		checksum += binbuf[i];
		count++;
		binlc ++;
	}
	if (count > BINBUFLEN - MAXBYTPERINS)
		binlc = 0;
	return;
}

#define hex(x) x+(x<10?'0':'7')

void putout (c)
	Word c;
{
	Word outc[2], t;

	t = c>>4;
	c &= 0xf;
	outc[0] = hex(t);
	outc[1] = hex(c);
	(void) fwrite ((char *)outc, 2, 1, output);
	return;
}


void insert (name, value, type, segment, mult)
	char *name;
	long value;
	opdclass *type;
	segmnt *segment;
	int mult;
{
	int x, y;
	symbol *sp;

	x = y = hash (name);
	while ((sp = &symtab[x])->name != (char *) 0) {
		if (strcmp (sp->name, name) == 0) {
			if (!mult && value != sp->value && pass == 1) {
				reporterr (E_MULT);
				return;
			}
			break;
		}
		if (++x == y) reporterr (E_STOFLO);
		if (x == NSYMS) x = 0;
	}
	sp->name = malloc ((unsigned) strlen (name) + 1);
	(void) strcpy (sp->name, name);
	sp->value = value;
	sp->type = type;
	sp->segp = segment;
	return;
}


int lookup (name)
	char *name;
{
	int x, y;

	x = y = hash (name);
	while (symtab[x].name != (char *) 0) {
		if (strcmp (name, symtab[x].name) == 0)
			return (x);
		x++;
		if (x == y) return (-1);
		if (x == NSYMS) x = 0;
	}
	return (-1);
}


int hash (string)
	char *string;
{
	char *pt;
	int tot;

	tot = 0;
	for (pt = string; *pt; pt++)
		tot += *pt;
	if (NSYMS >= 256)
		tot += (tot << 8);
	return (tot % NSYMS);
}


void reporterr (errcode)
	int errcode;
{
	static char *elist[] = {
		"",
		"aborting at end of pass1",
		"aborting at end of pass2",
		"symbol table overflow",
		"input file error",
		"too many nested includes",
		"include syntax error",
		"illegal expression",
		"undefined symbol",
		"unknown op code",
		"multiply defined symbol",
		"insufficient operands",
		"value too large for field",
		"unsigned value negative",
		"segment violation",
	};

	if (pass == 1) {
		(void) fprintf (list, "----> Error in file %s, line %d: %s\n", filename[currinput], lineno[currinput], elist[errcode]);
		(void) fprintf (stderr, "----> Error in file %s, line %d: %s\n", filename[currinput], lineno[currinput], elist[errcode]);
	} else {
		(void) fprintf (list, "----> %s: %s\n", filename[currinput], elist[errcode]);
		(void) fprintf (stderr, "----> %s: %s\n", filename[currinput], elist[errcode]);
	}
	(void) fflush (list);
	if (errcode <= A_MAX) {
		(void) fprintf (stderr, "%s: Abort error: %s\n", prog, elist[errcode]);
		exit (1);
	}
	errorstat++;
	return;
}


void sort ()
{
	char c;

	while ((c = getc (input[0])) > 0)
		putc (c, output);
	return;
}


void crossref ()
{
	int i, j, k, gap;
	symbol symtemp;

	(void) fprintf (list, "\n\n\n                     Cross Reference\n\n");
	k = 0;
	for (i = 0; i < NSYMS; i++)
		if (symtab[i].name && *symtab[i].name)
			symtab[k++] = symtab[i];
	for (gap = k / 2; gap > 0; gap /= 2)
		for (i = gap; i < k; i++)
			for (j = i - gap; j >= 0; j -= gap) {
				if (strcmp (symtab[j].name,
						  symtab[j+gap].name) <= 0)
					break;
				symtemp = symtab[j];
				symtab[j] = symtab[j+gap];
				symtab[j+gap] = symtemp;
			}
	for (i = 0; i < k; i++)
		(void) fprintf (list, "    %s: 0x%x\n", symtab[i].name,
							symtab[i].value);
	return;
}
