/* asmain.c */

/*
 * (C) Copyright 1989
 * All Rights Reserved
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 */

#include <stdio.h>
#include <setjmp.h>
#include "asm.h"


VOID
main(argc, argv)
char *argv[];
{
	register char *p;
	register c, i;
	struct area *ap;
	FILE *afile();

	inpfil = -1;
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			++p;
			while (c = *p++)
				switch(c) {

				case 'a':
				case 'A':
					++aflag;
					break;

				case 'g':
				case 'G':
					++gflag;
					break;

				case 'l':
				case 'L':
					++lflag;
					break;

				case 'o':
				case 'O':
					++oflag;
					break;

				case 's':
				case 'S':
					++sflag;
					break;

				case 'x':
				case 'X':
					xflag = 0;
					break;

				case 'q':
				case 'Q':
					xflag = 1;
					break;

				case 'd':
				case 'D':
					xflag = 2;
					break;

				default:
					usage();
				}
		} else {
			if (++inpfil == MAXFIL) {
				fprintf(stderr, "too many input files\n");
				exit(1);
			}
			sfp[inpfil] = afile(p, "", 0);
			if (inpfil == 0) {
				if (lflag)
					lfp = afile(p, "lst", 1);
				if (oflag)
					ofp = afile(p, "rel", 1);
				if (sflag)
					tfp = afile(p, "sym", 1);
			}
		}
	}
	if (inpfil < 0)
		usage();
	syminit();
	for (pass=0; pass<3; ++pass) {
		if (gflag && pass == 1)
			symglob();
		if (aflag && pass == 1)
			allglob();
		if (oflag && pass == 2)
			outgsd();
		flevel = 0;
		tlevel = 0;
		ifcnd[0] = 0;
		iflvl[0] = 0;
		radix = 10;
		line = 0;
		page = 0;
		stb[0] = 0;
		lop  = NLPP;
		cfile = 0;
		incfil = -1;
		for (i = 0; i <= inpfil; i++)
			rewind(sfp[i]);
		ap = areap;
		while (ap) {
			ap->a_fuzz = 0;
			ap->a_size = 0;
			ap = ap->a_ap;
		}
		fuzz = 0;
		dot->s_addr = 0;
		dot->s_area = dca;
		symp = dot;
		minit();
		while (getline()) {
			++line;
			cp = cb;
			ep = eb;
			ip = ib;
			if (setjmp(jump_env) == 0)
				asmbl();
			if (pass == 2) {
				diag();
				list();
			}
		}
		newdot(dot->s_area); /* Flush area info */
		if (flevel || tlevel)
			err('i');
	}
	if (oflag)
		outchk(HUGE, HUGE);  /* Flush */
	if (sflag) {
		lstsym(tfp);
	} else
	if (lflag) {
		lstsym(lfp);
	}
}

VOID
asmbl()
{
	register struct mne *mp;
	register struct sym *sp;
	register struct tsym *tp;
	register c;
	struct area  *ap;
	struct expr e1;
	char id[NCPS];
	char opt[NCPS];
	char fn[FILSPC];
	char *p;
	int d, n, uaf, uf;

	laddr = dot->s_addr;
	lmode = SLIST;
loop:
	if ((c=endline()) == 0) { return; }
	if (ctype[c] == DIGIT) {
		if (flevel)
			return;
		n = 0;
		while ((d = digit(c, 10)) >= 0) {
			n = 10*n + d;
			c = get();
		}
		if (c != '$' || get() != ':')
			qerr();
		tp = symp->s_tsym;
		if (pass == 0) {
			while (tp) {
				if (n == tp->t_num) {
					tp->t_flg |= S_MDF;
					break;
				}
				tp = tp->t_lnk;
			}
			if (tp == NULL) {
				tp=(struct tsym *) new (sizeof(struct tsym));
				tp->t_lnk = symp->s_tsym;
				tp->t_num = n;
				tp->t_area = dot->s_area;
				tp->t_addr = dot->s_addr;
				symp->s_tsym = tp;
			}
		} else {
			while (tp) {
				if (n == tp->t_num) {
					break;
				}
				tp = tp->t_lnk;
			}
			if (tp) {
				if (pass == 1) {
					fuzz = tp->t_addr - dot->s_addr;
					tp->t_area = dot->s_area;
					tp->t_addr = dot->s_addr;
				} else {
					phase(tp->t_area, tp->t_addr);
					if (tp->t_flg & S_MDF)
						err('m');
				}
			} else {
				err('u');
			}
		}
		goto loop;
	}
	if (ctype[c] != LETTER) 
		if (flevel) {
			return;
		} else {
			qerr();
		}
	getid(id, c);
	c = getnb();
	if (c == ':') {
		if (flevel)
			return;
		if ((c = get()) != ':') {
			unget(c);
			c = 0;
		}
		symp = lookup(id);
		if (symp == dot)
			err('.');
		if (pass == 0)
			if (symp->s_type != S_NEW &&
			   (symp->s_flag & S_ASG) == 0)
				symp->s_flag |= S_MDF;
		if (pass != 2) {
			fuzz = symp->s_addr - dot->s_addr;
			symp->s_type = S_USER;
			symp->s_area = dot->s_area;
			symp->s_addr = dot->s_addr;
		} else {
			if (symp->s_flag & S_MDF)
				err('m');
			phase(symp->s_area, symp->s_addr);
		}
		if (c) {
			symp->s_flag |= S_GBL;
		}
		lmode = ALIST;
		goto loop;
	}
	if (c == '=') {
		if (flevel)
			return;
		if ((c = get()) != '=') {
			unget(c);
			c = 0;
		}
		expr(&e1, 0);
		sp = lookup(id);
		if (sp == dot) {
			outall();
			if (e1.e_flag || e1.e_base.e_ap != dot->s_area)
				err('.');
		} else
		if (sp->s_type != S_NEW && (sp->s_flag & S_ASG) == 0) {
			err('m');
		}
		sp->s_type = S_USER;
		sp->s_area = e1.e_base.e_ap;
		sp->s_addr = laddr = e1.e_addr;
		sp->s_flag |= S_ASG;
		if (c) {
			sp->s_flag |= S_GBL;
		}
		lmode = ALIST;
		goto loop;
	}
	unget(c);
	lmode = flevel ? SLIST : CLIST;
	if ((mp = mlookup(id)) == NULL) {
		if (!flevel)
			err('o');
		return;
	}
	switch (mp->m_type) {

	case S_IF:
		n = absexpr();
		if (tlevel < MAXIF) {
			++tlevel;
			ifcnd[tlevel] = n;
			iflvl[tlevel] = flevel;
			if (n == 0) {
				++flevel;
			}
		} else {
			err('i');
		}
		lmode = ALIST;
		laddr = n;
		return;

	case S_ELSE:
		if (ifcnd[tlevel]) {
			if (++flevel > (iflvl[tlevel]+1)) {
				err('i');
			}
		} else {
			if (--flevel < iflvl[tlevel]) {
				err('i');
			}
		}
		lmode = SLIST;
		return;

	case S_ENDIF:
		if (tlevel) {
			flevel = iflvl[tlevel--];
		} else {
			err('i');
		}
		lmode = SLIST;
		return;

	case S_PAGE:
		lop = NLPP;
		lmode = NLIST;
		return;

	default:
		break;
	}
	if (flevel)
		return;
	switch (mp->m_type) {

	case S_EVEN:
		outall();
		dot->s_addr = (dot->s_addr + 1) & ~1;
		lmode = SLIST;
		break;

	case S_ODD:
		outall();
		dot->s_addr |= 1;
		lmode = SLIST;
		break;

	case S_BYTE:
	case S_WORD:
		do {
			expr(&e1, 0);
			if (mp->m_type == S_BYTE) {
				outrb(&e1, 0);
			} else {
				outrw(&e1, 0);
			}
		} while ((c = getnb()) == ',');
		unget(c);
		break;

	case S_ASCII:
	case S_ASCIZ:
		if ((d = getnb()) == '\0')
			qerr();
		while ((c = getmap(d)) >= 0)
			outab(c);
		if (mp->m_type == S_ASCIZ)
			outab(0);
		break;

	case S_BLK:
		expr(&e1, 0);
		outall();
		dot->s_addr += e1.e_addr*mp->m_valu;
		break;

	case S_TITLE:
		p = tb;
		if (c = getnb()) {
			do {
				if (p < &tb[NTITL-1])
					*p++ = c;
			} while (c = get());
		}
		*p = 0;
		unget(c);
		lmode = SLIST;
		break;

	case S_SBTL:
		p = stb;
		if (c = getnb()) {
			do {
				if (p < &stb[NSBTL-1])
					*p++ = c;
			} while (c = get());
		}
		*p = 0;
		unget(c);
		lmode = SLIST;
		break;

	case S_MODUL:
		getid(id, -1);
		if (pass == 0) {
			if (module[0])
				err('m');
			strncpy(module, id, NCPS);
		}
		lmode = SLIST;
		break;

	case S_GLOBL:
		do {
			getid(id, -1);
			sp = lookup(id);
			sp->s_flag |= S_GBL;
		} while ((c = getnb()) == ',');
		unget(c);
		lmode = SLIST;
		break;

	case S_DAREA:
		getid(id, -1);
		uaf = 0;
		uf  = A_CON|A_REL;
		if ((c = getnb()) == '(') {
			do {
				getid(opt, -1);
				mp = mlookup(opt);
				if (mp && mp->m_type == S_ATYP) {
					++uaf;
					uf |= mp->m_valu;
				} else {
					err('u');
				}
			} while ((c = getnb()) == ',');
			if (c != ')')
				qerr();
		} else {
			unget(c);
		}
		if (ap = alookup(id)) {
			if (uaf && uf != ap->a_flag)
				err('m');
		} else {
			ap = (struct area *) new (sizeof(struct area));
			ap->a_ap = areap;
			strncpy(ap->a_id, id, NCPS);
			ap->a_ref = areap->a_ref + 1;
			ap->a_flag = uaf ? uf : (A_CON|A_REL);
			areap = ap;
		}
		newdot(ap);
		lmode = SLIST;
		break;

	case S_ORG:
		if (dot->s_area->a_flag & A_ABS) {
			outall();
			dot->s_addr = absexpr();
		} else {
			err('o');
		}
		lmode = SLIST;
		break;

	case S_RADIX:
		if (more()) {
			switch (getnb()) {
			case 'b':
			case 'B':
				radix = 2;
				break;
			case '@':
			case 'o':
			case 'O':
			case 'q':
			case 'Q':
				radix = 8;
				break;
			case 'd':
			case 'D':
				radix = 10;
				break;
			case 'h':
			case 'H':
			case 'x':
			case 'X':
				radix = 16;
				break;
			default:
				qerr();
				radix = 10;
				break;
			}
		} else {
		radix = 10;
		}
		lmode = SLIST;
		break;

	case S_INCL:
		d = getnb();
		p = fn;
		while ((c = get()) != d) {
			if (p < &fn[FILSPC-1]) {
				*p++ = c;
			} else {
				break;
			}
		}
		*p = 0;
		if (++incfil == MAXINC ||
		   (ifp[incfil] = fopen(fn, "r")) == NULL) {
			--incfil;
			err('i');
			lmode = SLIST;
		} else {
			lop = NLPP;
			lmode = NLIST;
		}
		break;

	default:
		machine(mp);
	}
	goto loop;
}

FILE *
afile(fn, ft, wf)
char *fn;
char *ft;
{
	register char *p1, *p2, *p3;
	register c;
	FILE *fp;
	char fb[FILSPC];

	p1 = fn;
	p2 = fb;
	p3 = ft;
	while ((c = *p1++) && c != '.') {
		if (p2 < &fb[FILSPC-4])
			*p2++ = c;
	}
	*p2++ = '.';
	if (*p3 == 0) {
		if (c == '.') {
			p3 = p1;
		} else {
			p3 = dsft;
		}
	}
	while (c = *p3++) {
		if (p2 < &fb[FILSPC-1])
			*p2++ = c;
	}
	*p2++ = 0;
	if ((fp = fopen(fb, wf?"w":"r")) == NULL) {
		fprintf(stderr, "%s: cannot %s.\n", fb, wf?"create":"open");
		exit(1);
	}
	return (fp);
}

VOID
newdot(nap)
register struct area *nap;
{
	register struct area *oap;

	oap = dot->s_area;
	oap->a_fuzz = fuzz;
	oap->a_size = dot->s_addr;
	fuzz = nap->a_fuzz;
	dot->s_area = nap;
	dot->s_addr = nap->a_size;
	outall();
}

VOID
phase(ap, a)
struct area *ap;
addr_t a;
{
	if (ap != dot->s_area || a != dot->s_addr)
		err('p');
}

char *usetxt[] = {
	"Usage: [-dqxgalos] file1 [file2 file3 ...]",
	"  d	decimal	listing",
	"  q	octal	listing",
	"  x	hex	listing (default)",
	"  g	undefined symbols made global",
	"  a	all user symbols made global",
	"  l	create list   output file1.lst",
	"  o	create object output file1.rel",
	"  s	create symbol output file1.sym",
	"",
	0
};

VOID
usage()
{
	register char	**dp;

	fprintf(stderr, "\nAssembler (%s)\n\n", cpu);
	for (dp = usetxt; *dp; dp++)
		fprintf(stderr, "%s\n", *dp);
	exit(1);
}
