/* asmain.c */

/*
 * (C) Copyright 1989-2003
 * All Rights Reserved
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 *
 *
 *   With enhancements from
 *
 *	John L. Hartman	(JLH)
 *	jhartman@compuserve.com
 */

#include <stdio.h>
#include <setjmp.h>
#include <string.h>

#ifdef WIN32
#include <stdlib.h>
#else
#include <alloc.h>
#endif

#include "asxxxx.h"


/*)Module	asmain.c
 *
 *	The module asmain.c includes the command argument parser,
 *	the three pass sequencer, and the machine independent
 *	assembler parsing code.
 *
 *	asmain.c contains the following functions:
 *		int	main(argc, argv)
 *		VOID	asexit(n)
 *		VOID	asmbl()
 *		FILE *	afile(fn, ft, wf)
 *		int	fndidx(str)
 *		VOID	newdot(nap)
 *		VOID	phase(ap, a)
 *		VOID	usage()
 *
 *	asmain.c contains the array char *usetxt[] which
 *	references the usage text strings printed by usage().
 */

/*)Function	int	main(argc, argv)
 *
 *		int	argc		argument count
 *		char *	argv		array of pointers to argument strings
 *
 *	The function main() is the entry point to the assembler.
 *	The purpose of main() is to (1) parse the command line
 *	arguments for options and source file specifications and
 *	(2) to process the source files through the 3 pass assembler.
 *	Before each assembler pass various variables are initialized
 *	and source files are rewound to their beginning.  During each
 *	assembler pass each assembler-source text line is processed.
 *	After each assembler pass the assembler information is flushed
 *	to any opened output files and the if-else-endif processing
 *	is checked for proper termination.
 *
 *	The function main() is also responsible for opening all
 *	output files (REL, LST, and SYM), sequencing the global (-g)
 *	and all-global (-a) variable definitions, and dumping the
 *	REL file header information.
 *
 *	local variables:
 *		char *	p		pointer to argument string
 *		int	c		character from argument string
 *		int	i		argument loop counter
 *		area *	ap		pointer to area structure
 *		def *	dp		pointer to def structure
 *
 *	global variables:
 *		int	aflag		-a, make all symbols global flag
 *		char	afn[]		afile() constructed filespec
 *		int	afp		afile constructed path length
 *		area *	areap		pointer to an area structure
 *		int	aserr		assembler error counter
 *		int	bflag		-b(b), listing mode flag
 *		int	cb[]		array of assembler output values
 *		int	cbt[]		array of assembler relocation types
 *					describing the data in cb[]
 *		int	cfile		current file handle index
 *					of input assembly files
 *		int *	cp		pointer to assembler output array cb[]
 *		int *	cpt		pointer to assembler relocation type
 *					output array cbt[]
 *		def *	defp		pointer to a def structure
 *		char	eb[]		array of generated error codes
 *		char *	ep		pointer into error list array eb[]
 *		int	fflag		-f(f), relocations flagged flag
 *		int	flevel		IF-ELSE-ENDIF flag will be non
 *					zero for false conditional case
 *		int	nlevel		LIST-NLIST flag will be non
 *				 	zero for nolist
 *		a_uint	fuzz		tracks pass to pass changes in the
 *					address of symbols caused by
 *					variable length instruction formats
 *		int	gflag		-g, make undefined symbols global flag
 *		char	ib[]		assembler-source text line
 *		int	inpfil		count of assembler
 *					input files specified
 *		int	ifcnd[]		array of IF statement condition
 *					values (0 = FALSE) indexed by tlevel
 *		int	iflvl[]		array of IF-ELSE-ENDIF flevel
 *					values indexed by tlevel
 *		int	incfil		current file handle index
 *					for include files
 *		char *	ip		pointer into the assembler-source
 *					text line in ib[]
 *		jmp_buf	jump_env	compiler dependent structure
 *					used by setjmp() and longjmp()
 *		int	lflag		-l, generate listing flag
 *		int	line		current assembler source
 *					line number
 *		int	lop		current line number on page
 *		int	oflag		-o, generate relocatable output flag
 *		int	page		current page number
 *		int	pflag		enable listing pagination
 *		int	pass		assembler pass number
 *		int	radix		current number conversion radix:
 *					2 (binary), 8 (octal), 10 (decimal),
 *					16 (hexadecimal)
 *		int	sflag		-s, generate symbol table flag
 *		char	srcfn[][]	array of source file names
 *		int	srcfp[]		array of source file path lengths
 *		int	srcline[]	current source file line
 *		char	stb[]		Subtitle string buffer
 *		sym *	symp		pointer to a symbol structure
 *		int	tlevel		current conditional level
 *		int	uflag		-u, disable .list/.nlist processing
 *		int	wflag		-w, enable wide listing format
 *		int	xflag		-x, listing radix flag
 *		int	zflag		-z, disable symbol case sensitivity
 *		FILE *	lfp		list output file handle
 *		FILE *	ofp		relocation output file handle
 *		FILE *	tfp		symbol table output file handle
 *		FILE *	sfp[]		array of assembler-source file handles
 *
 *	called functions:
 *		FILE *	afile()		asmain.c
 *		VOID	allglob()	assym.c
 *		VOID	asexit()	asmain.c
 *		VOID	diag()		assubr.c
 *		VOID	err()		assubr.c
 *		int	fprintf()	c-library
 *		int	getline()	aslex.c
 *		VOID	list()		aslist.c
 *		VOID	lstsym()	aslist.c
 *		VOID	minit()		___mch.c
 *		VOID	newdot()	asmain.c
 *		VOID	outbuf()	asout.c
 *		VOID	outchk()	asout.c
 *		VOID	outgsd()	asout.c
 *		int	rewind()	c-library
 *		int	setjmp()	c-library
 *		VOID	symglob()	assym.c
 *		VOID	syminit()	assym.c
 *		VOID	usage()		asmain.c
 *
 *	side effects:
 *		Completion of main() completes the assembly process.
 *		REL, LST, and/or SYM files may be generated.
 */

int
main(argc, argv)
int argc;
char *argv[];
{
	register char *p;
	register int c, i;
	struct area *ap;
	struct def *dp;

	if (sizeof(a_mask) < 4) {
		fprintf(stderr, "?ASxxxx-Error-Size of INT32 is not 32 bits or larger.\n\n");
		exit(ER_FATAL);
	}

	fprintf(stdout, "\n");
	inpfil = -1;
	pflag = 1;
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			if (inpfil >= 0)
				usage(ER_FATAL);
			++p;
			while ((c = *p++) != 0)
				switch(c) {

				case 'a':
				case 'A':
					++aflag;
					break;

				case 'b':
				case 'B':
					++bflag;
					break;

				case 'g':
				case 'G':
					++gflag;
					break;

#if NOICE
				case 'j':		/* NoICE Debug  JLH */
				case 'J':
					++jflag;
					++oflag;	/* force object */
					break;
#endif

#if SDCDB
				case 'y':		/* SDCC Debug */
				case 'Y':
					++yflag;
					break;
#endif

				case 'l':
				case 'L':
					++lflag;
					break;

				case 'o':
				case 'O':
					++oflag;
					break;

				case 's':
				case 'S':
					++sflag;
					break;

				case 'p':
				case 'P':
					pflag = 0;
					break;

				case 'u':
				case 'U':
					++uflag;

				case 'w':
				case 'W':
					++wflag;
					break;

				case 'z':
				case 'Z':
					++zflag;
					break;

				case 'x':
				case 'X':
					xflag = 0;
					break;

				case 'q':
				case 'Q':
					xflag = 1;
					break;

				case 'd':
				case 'D':
					xflag = 2;
					break;

				case 'f':
				case 'F':
					++fflag;
					break;

				default:
					usage(ER_FATAL);
				}
		} else {
			if (++inpfil == MAXFIL) {
				fprintf(stderr, "too many input files\n");
				asexit(ER_FATAL);
			}
			sfp[inpfil] = afile(p, "", 0);
			strcpy(srcfn[inpfil],afn);
			srcfp[inpfil] = afp;
			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(ER_WARNING);
	exprmasks(2);
	syminit();
	if (bflag != 0) {
		il = ib;
	} else {
		il = ic;
	}
	for (pass=0; pass<3; ++pass) {
		aserr = 0;
		if (gflag && pass == 1)
			symglob();
		if (aflag && pass == 1)
			allglob();
		if (oflag && pass == 2)
			outgsd();
		dp = defp;
		while (dp) {
			dp->d_dflag = 0;
			dp = dp->d_dp;
		}
		flevel = 0;
		tlevel = 0;
		nlevel = 0;
		ifcnd[0] = 0;
		iflvl[0] = 0;
		radix = 10;
		srcline[0] = 0;
		page = 0;
		stb[0] = 0;
		lop  = NLPP;
		cfile = 0;
		strcpy(afn, srcfn[cfile]);
		afp = srcfp[cfile];
		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;
		as_msb = 1;
		dot.s_addr = 0;
		dot.s_area = &dca;
		outbuf("I");
		outchk(0,0);
		symp = &dot;
		minit();
		while ((i = getline()) != 0) {
			cp = cb;
			cpt = cbt;
			ep = eb;
			ip = ib;

			/*
			 * String substitution or recursion error.
			 */
			if (i != 1) {
				err('s');
			}

			/* JLH: if line begins with ";!", then
			 * pass this comment on to the output file
			 */
			if (oflag && (pass == 1) && (ip[0] == ';') && (ip[1] == '!')) {
				fprintf(ofp, "%s\n", ip );
			}

			if (setjmp(jump_env) == 0)
				asmbl();
			if (pass == 2) {
				diag();
				list();
			}
		}
		newdot(dot.s_area);	/* Flush area info */
	}
	if (flevel || tlevel) {
		err('i');
		fprintf(stderr, "?ASxxxx-Error-<i> at end of assembly\n");
		fprintf(stderr, "              %s\n", geterr('i'));
	}
	if (oflag)
		outchk(HUGE, HUGE);	/* Flush */
	if (sflag) {
		lstsym(tfp);
	} else
	if (lflag) {
		lstsym(lfp);
	}
	asexit(aserr ? ER_ERROR : ER_NONE);
	return(0);
}

/*)Function	VOID	asexit(i)
 *
 *			int	i	exit code
 *
 *	The function asexit() explicitly closes all open
 *	files and then terminates the program.
 *
 *	local variables:
 *		int	j		loop counter
 *
 *	global variables:
 *		FILE *	ifp[]		array of include-file file handles
 *		FILE *	lfp		list output file handle
 *		FILE *	ofp		relocation output file handle
 *		FILE *	tfp		symbol table output file handle
 *		FILE *	sfp[]		array of assembler-source file handles
 *
 *	functions called:
 *		int	fclose()	c-library
 *		VOID	exit()		c-library
 *
 *	side effects:
 *		All files closed. Program terminates.
 */

VOID
asexit(i)
int i;
{
	int j;

	if (lfp != NULL) fclose(lfp);
	if (ofp != NULL) fclose(ofp);
	if (tfp != NULL) fclose(tfp);

	for (j=0; j<MAXFIL && sfp[j] != NULL; j++) {
		fclose(sfp[j]);
	}

	for (j=0; j<MAXINC && ifp[j] != NULL; j++) {
		fclose(ifp[j]);
	}
	exit(i);
}

/*)Function	VOID	asmbl()
 *
 *	The function asmbl() scans the assembler-source text for
 *	(1) labels, global labels, equates, global equates, and local
 *	symbols, (2) .if, .else, .endif, and .page directives,
 *	(3) machine independent assembler directives, and (4) machine
 *	dependent mnemonics.
 *
 *	local variables:
 *		mne *	mp		pointer to a mne structure
 *		sym *	sp		pointer to a sym structure
 *		tsym *	tp		pointer to a tsym structure
 *		int	c		character from assembler-source
 *					text line
 *		area *	ap		pointer to an area structure
 *		expr	e1		expression structure
 *		char	id[]		id string
 *		char	opt[]		options string
 *		char	fn[]		filename string
 *		char *	p		pointer into a string
 *		int	d		temporary value
 *		int	n		temporary value
 *		int	uaf		user area options flag
 *		int	uf		area options
 *
 *	global variables:
 *		area *	areap		pointer to an area structure
 *		char	ctype[]		array of character types, one per
 *					ASCII character
 *		int	flevel		IF-ELSE-ENDIF flag will be non
 *					zero for false conditional case
 *		int	nlevel		LIST-NLIST flag will be non
 *				 	zero for nolist
 *		a_uint	fuzz		tracks pass to pass changes in the
 *					address of symbols caused by
 *					variable length instruction formats
 *		int	ifcnd[]		array of IF statement condition
 *					values (0 = FALSE) indexed by tlevel
 *		int	iflvl[]		array of IF-ELSE-ENDIF flevel
 *					values indexed by tlevel
 *		FILE *	ifp[]		array of include-file file handles
 *		char	incfn[][]	array of include file names
 *		int	incfp[]		array of include file path lengths
 *		int	incline[]	current include file line
 *		int	incfil		current file handle index
 *					for include files
 *		a_uint	laddr		address of current assembler line
 *					or value of .if argument
 *		int	lmode		listing mode
 *		int	lop		current line number on page
 *		char	module[]	module name string
 *		int	pass		assembler pass number
 *		int	radix		current number conversion radix:
 *					2 (binary), 8 (octal), 10 (decimal),
 *					16 (hexadecimal)
 *		char	stb[]		Subtitle string buffer
 *		sym *	symp		pointer to a symbol structure
 *		char	tb[]		Title string buffer
 *		int	tlevel		current conditional level
 *
 *	functions called:
 *		a_uint	absexpr()	asexpr.c
 *		area *	alookup()	assym.c
 *		VOID	clrexpr()	asexpr.c
 *		int	digit()		asexpr.c
 *		char	endline()	aslex.c
 *		VOID	err()		assubr.c
 *		VOID	expr()		asexpr.c
 *		int	fndidx()	asmain.c
 *		FILE *	fopen()		c-library
 *		int	get()		aslex.c
 *		VOID	getid()		aslex.c
 *		int	getmap()	aslex.c
 *		int	getnb()		aslex.c
 *		VOID	getst()		aslex.c
 *		sym *	lookup()	assym.c
 *		VOID	machine()	___mch.c
 *		mne *	mlookup()	assym.c
 *		int	more()		aslex.c
 *		VOID *	new()		assym.c
 *		VOID	newdot()	asmain.c
 *		VOID	outall()	asout.c
 *		VOID	outab()		asout.c
 *		VOID	outchk()	asout.c
 *		VOID	outrb()		asout.c
 *		VOID	outrw()		asout.c
 *		VOID	phase()		asmain.c
 *		VOID	qerr()		assubr.c
 *		int	strcmp()	c-library
 *		char *	strcpy()	c-library
 *		char *	strncpy()	c-library
 *		char *	strsto()	assym.c
 *		VOID	unget()		aslex.c
 */

VOID
asmbl()
{
	register struct mne *mp;
	register struct sym *sp;
	register struct tsym *tp;
	register int c;
	struct area  *ap;
	struct bank  *bp;
	struct def *dp;
	struct expr e1;
	char id[NCPS];
	char opt[NCPS];
	char fn[FILSPC+FILSPC];
	char *p;
	int d, n, uf;
	int con_ovr, rel_abs, npg_pag, csg_dsg;
	int flags;
	a_uint base, size, map;
	int cnt, v;

	laddr = dot.s_addr;
	lmode = SLIST;
loop:
	if ((c=endline()) == 0) { return; }
	/*
	 * If the first character is a digit then assume
	 * a reusable symbol is being specified.  The symbol
	 * must end with $: to be valid.
	 *	pass 0:
	 *		Construct a tsym structure at the first
	 *		occurance of the symbol.  Flag the symbol
	 *		as multiply defined if not the first occurance.
	 *	pass 1:
	 *		Load area, address, and fuzz values
	 *		into structure tsym.
	 *	pass 2:
	 *		Check for assembler phase error and
	 *		multiply defined error.
	 */
	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_flg = 0;
				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');
			}
		}
		lmode = ALIST;
		goto loop;
	}
	/*
	 * If the first character is a letter then assume a lable,
	 * symbol, assembler directive, or assembler mnemonic is
	 * being processed.
	 */
	if ((ctype[c] & LETTER) == 0) {
		if (flevel) {
			return;
		} else {
			qerr();
		}
	}
	getid(id, c);
	c = getnb();
	/*
	 * If the next character is a : then a label is being processed.
	 * A double :: defines a global label.  If this is a new label
	 * then create a symbol structure.
	 *	pass 0:
	 *		Flag multiply defined labels.
	 *	pass 1:
	 *		Load area, address, and fuzz values
	 *		into structure symp.
	 *	pass 2:
	 *		Check for assembler phase error and
	 *		multiply defined error.
	 */
	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 the next character is a = then an equate is being processed.
	 * A == defines a global equate.
	 * A =: defines an internal machine equate.
	 * If this is a new variable then create a symbol structure.
	 */
	if (c == '=') {
		if (flevel)
			return;
		c = get();
		if (c == ':') {
			clrexpr(&e1);
			expr(&e1, 0);
			abscheck(&e1);
			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_LCL;
			sp->s_area = e1.e_base.e_ap;
			sp->s_addr = laddr = e1.e_addr;
			sp->s_flag |= S_ASG;
			lmode = ELIST;
			goto loop;
		} else {
			if (c != '=') {
				unget(c);
				c = 0;
			}
			clrexpr(&e1);
			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;
			if (e1.e_flag && (e1.e_base.e_sp->s_type == S_NEW)) {
				rerr();
			} else {
				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 = ELIST;
			goto loop;
		}
	}
	unget(c);
	lmode = flevel ? SLIST : CLIST;
	if ((mp = mlookup(id)) == NULL) {
		if (!flevel)
			err('o');
		return;
	}
	/*
	 * If we have gotten this far then we have found an
	 * assembler directive or an assembler mnemonic.
	 *
	 * Check for .if, .else, .endif, .list,
	 * .nlist, and .page directives which are
	 * not controlled by the conditional flags
	 */
	switch (mp->m_type) {
	case S_CONDITIONAL:
		switch(mp->m_valu) {
		case O_IF:
			if (flevel) {
				while (get()) ;
				n = 0;
			} else {
				n = absexpr();
			}
			if (tlevel < MAXIF) {
				++tlevel;
				ifcnd[tlevel] = n;
				iflvl[tlevel] = flevel;
				if (n == 0) {
					++flevel;
				}
			} else {
				err('i');
			}
			lmode = ELIST;
			laddr = n;
			return;

		case O_IFDEF:
		case O_IFNDEF:
			if (flevel) {
				while (get()) ;
				n = 0;
			} else {
				getid(id, -1);
				if ((dp = dlookup(id)) != NULL) {
					n = 1;
				} else
				if ((sp = slookup(id)) != NULL) {
					n = (sp->s_flag & S_ASG) ? 1 : 0;
				} else {
					n = 0;
				}
				if (mp->m_valu == O_IFNDEF) {
					n = n ? 0 : 1;
				}
			}
			if (tlevel < MAXIF) {
				++tlevel;
				ifcnd[tlevel] = n;
				iflvl[tlevel] = flevel;
				if (n == 0) {
					++flevel;
				}
			} else {
				err('i');
			}
			lmode = ELIST;
			laddr = n ? 1 : 0;
			return;

		case O_ELSE:
			if (ifcnd[tlevel]) {
				if (++flevel > (iflvl[tlevel]+1)) {
					err('i');
				}
			} else {
				if (--flevel < iflvl[tlevel]) {
					err('i');
				}
			}
			lmode = SLIST;
			return;

		case O_ENDIF:
			if (tlevel) {
				flevel = iflvl[tlevel--];
			} else {
				err('i');
			}
			lmode = SLIST;
			return;

		default:
			return;
		}
		return;

	case S_LISTING:
		lmode = NLIST;
		switch(mp->m_valu) {
		case O_LIST:
			if (nlevel) {
				nlevel -= 1;
			}
			return;

		case O_NLIST:
			nlevel += 1;
			return;

		default:
			return;
		}
		return;

	case S_PAGE:
		lop = NLPP;
		lmode = NLIST;
		return;

	default:
		break;
	}
	if (flevel)
		return;
	/*
	 * If we are not in a false state for .if/.else then
	 * process the assembler directives here.
	 */
	switch (mp->m_type) {

	case S_HEADER:
		switch(mp->m_valu) {
		case O_TITLE:
			p = tb;
			if ((c = getnb()) != 0) {
				do {
					if (p < &tb[NTITL-1])
						*p++ = c;
				} while ((c = get()) != 0);
			}
			*p = 0;
			unget(c);
			lmode = SLIST;
			break;

		case O_SBTTL:
			p = stb;
			if ((c = getnb()) != 0) {
				do {
					if (p < &stb[NSBTL-1])
						*p++ = c;
				} while ((c = get()) != 0);
			}
			*p = 0;
			unget(c);
			lmode = SLIST;
			break;

		default:
			break;
		}
		break;

	case S_MODUL:
		getst(id, -1);
		if (module[0]) {
			if (strcmp(id, module)) {
				err('m');
			}
		} else {
			strncpy(module, id, NCPS);
		}
		lmode = SLIST;
		break;

	case S_INCL:
		lmode = SLIST;
    		if (++incfil == MAXINC) {
			--incfil;
			err('i');
			break;
		}
		/*
		 * Copy path of file opening the include file
		 */
		strncpy(fn,afn,afp);
		p = fn + afp;
		/*
		 * Concatenate the .include file specification
		 */
		d = getnb();
		while ((c = get()) != d) {
			if (p < &fn[FILSPC+FILSPC-1]) {
				*p++ = c;
			} else {
				break;
			}
		}
		*p = 0;
		/*
		 * If .include specifies a path
		 * 	use it
		 * else
		 *	use path of file opening the include file
		 */
		if (fndidx(fn + afp) != 0) {
			afilex(fn + afp, "");
		} else {
			afilex(fn, "");
		}
		/*
		 * Open File
		 */
    		if ((ifp[incfil] = fopen(afntmp, "r")) == NULL) {
			--incfil;
			err('i');
		} else {
			lop = NLPP;
			incline[incfil] = 0;
			strcpy(afn, afntmp);
			afp = afptmp;
			strcpy(incfn[incfil],afn);
			incfp[incfil] = afp;
		}
		break;

	case S_AREA:
		getid(id, -1);
		uf  = 0;
		con_ovr = 0;
		rel_abs = 0;
		npg_pag = 0;
		csg_dsg = 0;
		bp = NULL;
		if ((c = getnb()) == '(') {
			do {
				getid(opt, -1);
				mp = mlookup(opt);
				if (mp && mp->m_type == S_ATYP) {
					v = mp->m_valu;
					uf |= v;
					if (v == A_BNK) {
						if ((c = getnb()) != '=')
							qerr();
						if (!more())
							qerr();
						getid(opt, -1);
						if ((bp = blookup(opt)) == NULL) {
							err('u');
							uf &= ~v;
						}
					}
					if (v & A_CON) {
						con_ovr = v;
					} else
					if (v & A_REL) {
						rel_abs = v;
					} else
					if (v & A_NOPAG) {
						npg_pag = v;
					} else
					if (v & A_CSEG) {
						csg_dsg = v;
					}
				} else {
					err('u');
				}
			} while ((c = getnb()) == ',');
			if (c != ')')
				qerr();
		} else {
			unget(c);
		}
		if ((ap = alookup(id)) != NULL) {
			flags = ap->a_flag;
			if (uf & A_BNK) {
			 	if (flags & A_BNK) {
					if (bp != ap->b_bp)
						err('m');
				} else {
					ap->a_flag |= A_BNK;
					ap->b_bp = bp;
				}
			}
			if (uf & A_OVR) {
			 	if (flags & A_OVR) {
					if (con_ovr != (A_OVR & flags))
						err('m');
				} else {
					ap->a_flag |= con_ovr;
				}
			}
			if (uf & A_ABS) {
			 	if (flags & A_ABS) {
					if (rel_abs != (A_ABS & flags))
						err('m');
				} else {
					ap->a_flag |= rel_abs;
				}
			}
			if (uf & A_PAG) {
			 	if (flags & A_PAG) {
					if (npg_pag != (A_PAG & flags))
						err('m');
				} else {
					ap->a_flag |= npg_pag;
				}
			}
			if (uf & A_DSEG) {
			 	if (flags & A_DSEG) {
					if ((A_DSEG & csg_dsg) != (A_DSEG & flags))
						err('m');
				} else {
					ap->a_flag &= ~(A_DSEG | A_BYTES);
					ap->a_flag |= csg_dsg;
				}
			}
		} else {
			ap = (struct area *) new (sizeof(struct area));
			ap->a_ap = areap;
			ap->b_bp = (uf & A_BNK) ? bp : NULL;
			ap->a_id = strsto(id);
			ap->a_ref = areap->a_ref + 1;
			ap->a_size = 0;
			ap->a_fuzz = 0;
			/*
			 * The default PC increment is defined in ___pst.c as area[0]
			 */
			ap->a_flag = (uf & A_DSEG) ? uf : uf | (area[0].a_flag & A_BYTES);

			areap = ap;
		}
		newdot(ap);
		lmode = SLIST;
		break;

	case S_BANK:
		getid(id, -1);
		uf  = 0;
		base = 0;
		size = 0;
		map = 0;
		if ((c = getnb()) == '(') {
			do {
				getid(opt, -1);
				mp = mlookup(opt);
				if (mp && mp->m_type == S_BTYP) {
					v = mp->m_valu;
					uf |= v;
					if (v == B_BASE) {
						if ((c = getnb()) != '=')
							qerr();
						base = absexpr();
					} else
					if (v == B_SIZE) {
						if ((c = getnb()) != '=')
							qerr();
						size = absexpr();
					} else
					if (v == B_FSFX) {
						if ((c = getnb()) != '=')
							qerr();
						getid(opt, -1);
						if (opt[0] == 0)
							qerr();
					} else
					if (v == B_MAP) {
						if ((c = getnb()) != '=')
							qerr();
						map = absexpr();
					}
				} else {
					err('u');
				}
			} while ((c = getnb()) == ',');
			if (c != ')')
				qerr();
		} else {
			unget(c);
		}
		if ((bp = blookup(id)) != NULL) {
			flags = bp->b_flag;
			if (uf & B_BASE) {
			 	if (flags & B_BASE) {
					if (base != bp->b_base)
						err('m');
				} else {
					bp->b_base  = base;
					bp->b_flag |= B_BASE;
				}
			}
			if (uf & B_SIZE) {
			 	if (flags & B_SIZE) {
					if (size != bp->b_size)
						err('m');
				} else {
					bp->b_size  = size;
					bp->b_flag |= B_SIZE;
				}
			}
			if (uf & B_FSFX) {
				if (flags & B_FSFX) {
					if (!symeq(opt, bp->b_fsfx, 1))
						err('m');
				} else {
					bp->b_fsfx  = strsto(opt);
					bp->b_flag |= B_FSFX;
				}
			}
			if (uf & B_MAP) {
			 	if (flags & B_MAP) {
					if (map != bp->b_map)
						err('m');
				} else {
					bp->b_map  = map;
					bp->b_flag |= B_MAP;
				}
			}
		} else {
			bp = (struct bank *) new (sizeof(struct bank));
			bp->b_bp = bankp;
			bp->b_id = strsto(id);
			bp->b_fsfx = (uf & B_FSFX) ? strsto(opt) : NULL;
			bp->b_ref = bankp->b_ref + 1;
			bp->b_base = (uf & B_BASE) ? base : 0;
			bp->b_size = (uf & B_SIZE) ? size : 0;
			bp->b_map = (uf & B_MAP) ? map : 0;
			bp->b_flag =  uf;

			bankp = bp;
		}
		lmode = SLIST;
		break;

	case S_ORG:
		if (dot.s_area->a_flag & A_ABS) {
			outall();
			laddr = dot.s_addr = absexpr();
		} else {
			err('o');
		}
		outall();
		lmode = ALIST;
		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:
				radix = 10;
				qerr();
				break;
			}
		} else {
			radix = 10;
		}
		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_EQU:
		getid(id, -1);
		if ((c = getnb()) != ',') {
			qerr();
		}
		clrexpr(&e1);
		expr(&e1, 0);
		sp = lookup(id);
		if (sp == &dot) {
			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 (mp->m_valu) {
			sp->s_flag |= S_GBL;
		}
		lmode = ELIST;
		break;

	case S_DATA:
		/*
		 * Data size
		 */
		size = mp->m_valu;
		if ((a_uint) a_bytes < size) {
			err('o');
		}
		/*
		 * Intrinsic size
		 */
		n = 1 + ((dot.s_area->a_flag) & A_BYTES);
		/*
		 * Intrinsic size == 1	or
		 * Intrinsic size == Data size
		 *
		 * Use full relocation mode
		 */
		if ((n == 1) || ((a_uint) n == size)) {
			do {
				clrexpr(&e1);
				expr(&e1, 0);
				/*
				 * MSB coercion is not allowed
				 */
				if ((e1.e_rlcf & R_MSB) && (size != 1)) {
					err('o');
				}
				switch(size) {
				default:
				case 1:	outrb(&e1, R_NORM); break;
				case 2: outrw(&e1, R_NORM); break;
				case 3: outr3b(&e1, R_NORM); break;
				case 4: outr4b(&e1, R_NORM); break;
				}
			} while ((c = getnb()) == ',');
			unget(c);
		} else
		/*
		 * Data size == 1
		 *
		 * Coerce bytes into intrinsic size words
		 * **********-(NO relocation)-**********
		 */
		if (size == 1) {
			cnt = 0;
			v = 0;
			do {
				clrexpr(&e1);
				expr(&e1, 0);
				/*
				 * MSB coercion is not allowed
				 */
				if (e1.e_rlcf & R_MSB) {
					err('o');
				}
				if (!is_abs(&e1))
					err('r');
				if (hilo) {
					cnt += 1;
					v |= ((e1.e_addr & 0xFF) << 8 * (n - cnt));
				} else {
					v |= ((e1.e_addr & 0xFF) << 8 * cnt);
					cnt += 1;
				}
				if ((cnt % n) == 0) {
					switch(n) {
					default:
					case 1:	outab(v); break;
					case 2: outaw(v); break;
					case 3: outa3b(v); break;
					case 4: outa4b(v); break;
					}
					cnt = 0;
					v = 0;
				}
			} while ((c = getnb()) == ',');
			unget(c);
			if (cnt != 0) {
				switch(n) {
				default:
				case 1:	outab(v); break;
				case 2: outaw(v); break;
				case 3: outa3b(v); break;
				case 4: outa4b(v); break;
				}
			}
		} else
		/*
		 * Coerce to Intrinsic size
		 */
		{
			do {
				clrexpr(&e1);
				expr(&e1, 0);
				/*
				 * MSB coercion is not allowed
				 */
				if ((e1.e_rlcf & R_MSB) && (size != 1)) {
					err('o');
				}
				switch(n) {
				default:
				case 1:	outrb(&e1, R_NORM); break;
				case 2: outrw(&e1, R_NORM); break;
				case 3: outr3b(&e1, R_NORM); break;
				case 4: outr4b(&e1, R_NORM); break;
				}
			} while ((c = getnb()) == ',');
			unget(c);
		}
		break;

	case S_BLK:
		if ((a_uint) a_bytes < mp->m_valu) {
			err('o');
		}
		clrexpr(&e1);
		expr(&e1, 0);
		outchk(HUGE,HUGE);
		size = e1.e_addr*mp->m_valu;
		n = 1 + ((dot.s_area->a_flag) & A_BYTES);
		dot.s_addr += (size/n) + (size % n ? 1 : 0);
		lmode = BLIST;
		break;

	case S_ASCIX:
		switch(mp->m_valu) {
		case O_ASCII:
		case O_ASCIZ:
			if ((d = getnb()) == '\0')
				qerr();
			size = 1 + ((dot.s_area->a_flag) & A_BYTES);
			cnt = 0;
			v = 0;
			while ((c = getmap(d)) >= 0) {
				if (hilo) {
					cnt += 1;
					v |= ((c & 0x7F) << 8 * (size - cnt));
				} else {
					v |= ((c & 0x7F) << 8 * cnt);
					cnt += 1;
				}
				if ((cnt % size) == 0) {
					switch(size) {
					default:
					case 1:	outab(v); break;
					case 2: outaw(v); break;
					case 3: outa3b(v); break;
					case 4: outa4b(v); break;
					}
					cnt = 0;
					v = 0;
				}
			}
			if (mp->m_valu == O_ASCIZ) {
				cnt += 1;
			}
			if (cnt != 0) {
				switch(size) {
				default:
				case 1:	outab(v); break;
				case 2: outaw(v); break;
				case 3: outa3b(v); break;
				case 4: outa4b(v); break;
				}
			}
			break;

		case O_ASCIS:
			if ((d = getnb()) == '\0')
				qerr();
			size = 1 + ((dot.s_area->a_flag) & A_BYTES);
			cnt = 0;
			v = 0;
			c = getmap(d);
			while (c >= 0) {
				c &= 0x7F;
				if ((n = getmap(d)) < 0) {
					c |= 0x80;
				}
				if (hilo) {
					cnt += 1;
					v |= (c << 8 * (size - cnt));
				} else {
					v |= (c << 8 * cnt);
					cnt += 1;
				}
				if ((cnt % size) == 0) {
					switch(size) {
					default:
					case 1:	outab(v); break;
					case 2: outaw(v); break;
					case 3: outa3b(v); break;
					case 4: outa4b(v); break;
					}
					cnt = 0;
					v = 0;
				}
				c = n;
			}
			if (cnt != 0) {
				switch(size) {
				default:
				case 1:	outab(v); break;
				case 2: outaw(v); break;
				case 3: outa3b(v); break;
				case 4: outa4b(v); break;
				}
			}
			break;
		default:
			break;
		}
		break;

	case S_DEFINE:
		/*
		 * Extract the .(un)define key word.
		 */
		getid(id, -1);
		/*
		 * Extract the substitution string
		 */
		p = opt;
		d = getnb();
		while ((c = get()) != d) {
			if (p < &opt[NCPS-2]) {
				*p++ = c;
			} else {
				break;
			}
		}
		*p = 0;

		switch(mp->m_valu) {
		case O_DEF:
			/*
			 * Verify the definition or
			 * add a new definition.
			 */
			dp = dlookup(id);
			if (dp) {
				if (!symeq(opt, dp->d_define, zflag)) {
					if (dp->d_dflag) {
						err('m');
					}
					dp->d_define = strsto(opt);
				}
				dp->d_dflag = 1;
			} else {
				dp = (struct def *) new (sizeof(struct def));
				dp->d_dp = defp;
				dp->d_id = strsto(id);
				dp->d_define = strsto(opt);
				dp->d_dflag = 1;
				defp = dp;
			}
			break;

		case O_UNDEF:
			/*
			 * Find and undefine the definition.
			 */
			dp = dlookup(id);
			if (dp) {
				dp->d_dflag = 0;
			}
			break;

		default:
			break;
		}
		lmode = SLIST;
		break;

	case S_BOUNDARY:
		switch(mp->m_valu) {
		case O_EVEN:
			outall();
			laddr = dot.s_addr = (dot.s_addr + 1) & ~1;
			lmode = ALIST;
			break;

		case O_ODD:
			outall();
			laddr = dot.s_addr |= 1;
			lmode = ALIST;
			break;

		default:
			break;
		}
		break;

	case S_MSG:
		lmode = SLIST;
		/*
		 * Print the .msg message
		 */
		p = fn;
		d = getnb();
		while ((c = get()) != d) {
			if (p < &fn[FILSPC+FILSPC-1]) {
				*p++ = c;
			} else {
				break;
			}
		}
		*p = 0;
		if (pass == 2) {
			printf("%s\n", fn);
		}
		break;

	case S_ERROR:
		clrexpr(&e1);
		if (more()) {
			expr(&e1, 0);
		}
		if (e1.e_addr != 0) {
			err('e');
		}
		lmode = SLIST;
		break;

	case S_MSB:
		clrexpr(&e1);
		expr(&e1, 0);
		if (!is_abs(&e1)) {
			err('o');
		}
		if (e1.e_addr >= (a_uint) a_bytes) {
			err('o');
		} else {
			as_msb = e1.e_addr;
		}
		lmode = SLIST;
		break;

	case S_END:
		if (more()) {
			clrexpr(&e1);
			expr(&e1, 0);
			sp = lookup(".__.END.");
			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;
			lmode = ELIST;
		}
		break;

	/*
	 * If not an assembler directive then go to
	 * the machine dependent function which handles
	 * all the assembler mnemonics.
	 */
	default:
		machine(mp);

		/*
		 * Include Files are not Debugged
		 */
		if (incfil < 0) {

#if NOICE
	                /*
		         * NoICE	JLH
			 */
	                if (jflag && (pass == 1)) {
				DefineNoICE_Line();
                	}
#endif

#if SDCDB
			/*
			 * SDCC Debug Information
			 */
			if (yflag && (pass == 1)) {
				DefineSDCC_Line();
			}
#endif
		}

		break;
	}
	goto loop;
}

/*)Function	FILE *	afile(fn, ft, wf)
 *
 *		char *	fn		file specification string
 *		char *	ft		file type string
 *		int	wf		read(0)/write(1) flag
 *
 *	The function afile() opens a file for reading or writing.
 *
 *	afile() returns a file handle for the opened file or aborts
 *	the assembler on an open error.
 *
 *	local variables:
 *		FILE *	fp		file handle for opened file
 *
 *	global variables:
 *		char	afn[]		afile() constructed filespec
 *		int	afp		afile() constructed path length
 *		char	afntmp[]	afilex() constructed filespec
 *		int	afptmp		afilex() constructed path length
 *
 *	functions called:
 *		VOID	afilex()	asmain.c
 *		int	fndidx()	asmain.c
 *		FILE *	fopen()		c_library
 *		int	fprintf()	c_library
 *		char *	strcpy()	c_library
 *
 *	side effects:
 *		File is opened for read or write.
 */

FILE *
afile(fn, ft, wf)
char *fn;
char *ft;
int wf;
{
	FILE *fp;

	afilex(fn, ft);

	if ((fp = fopen(afntmp, wf?"w":"r")) == NULL) {
	    fprintf(stderr, "%s: cannot %s.\n", afntmp, wf?"create":"open");
	    asexit(ER_FATAL);
	}

	strcpy(afn, afntmp);
	afp = afptmp;

	return (fp);
}

/*)Function	VOID	afilex(fn, ft)
 *
 *		char *	fn		file specification string
 *		char *	ft		file type string
 *
 *	The function afilex() processes the file specification string:
 *		(1)	If the file type specification string ft
 *			is not NULL then a file specification is
 *			constructed with the file path\name in fn
 *			and the extension in ft.
 *		(2)	If the file type specification string ft
 *			is NULL then the file specification is
 *			constructed from fn.  If fn does not have
 *			a file type then the default source file
 *			type dsft is appended to the file specification.
 *
 *	afilex() aborts the assembler on a file specification length error.
 *
 *	local variables:
 *		int	c		character value
 *		char *	p1		pointer into filespec string afntmp
 *		char *	p2		pointer into filespec string fn
 *		char *	p3		pointer to filetype string ft
 *
 *	global variables:
 *		char	afntmp[]	afilex() constructed filespec
 *		int	afptmp		afilex() constructed path length
 *		char	dsft[]		default assembler file type string
 *
 *	functions called:
 *		VOID	asexit()	asmain.c
 *		int	fndidx()	asmain.c
 *		int	fprintf()	c_library
 *		char *	strcpy()	c_library
 *
 *	side effects:
 *		File specification string may be modified.
 */

VOID
afilex(fn, ft)
char *fn;
char *ft;
{
	register char *p1, *p2, *p3;
	register int c;

	if (strlen(fn) > (FILSPC-5)) {
		fprintf(stderr, "File Specification %s is too long.", fn);
		asexit(ER_FATAL);
	}

	/*
	 * Save the File Name Index
	 */
	strcpy(afntmp, fn);
	afptmp = fndidx(afntmp);
	p1 = &afntmp[afptmp];
	p2 = &fn[afptmp];

	/*
	 * Skip to File Extension Seperator
	 */
	while (((c = *p2++) != 0) && (c != FSEPX)) {
		p1++;
	}
	*p1++ = FSEPX;

	/*
	 * Copy File Extension
	 */
	 p3 = ft;
	 if (*p3 == 0) {
		if (c == FSEPX) {
			p3 = p2;
		} else {
			p3 = dsft;
		}
	}
	while ((c = *p3++) != 0) {
		if (p1 < &afntmp[FILSPC-1])
			*p1++ = c;
	}
	*p1++ = 0;
}

/*)Function	int	fndidx(str)
 *
 *		char *	str		file specification string
 *
 *	The function fndidx() scans the file specification string
 *	to find the index to the file name.  If the file
 *	specification contains a 'path' then the index will
 *	be non zero.
 *
 *	fndidx() returns the index value.
 *
 *	local variables:
 *		char *	p1		temporary pointer
 *		char *	p2		temporary pointer
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		char *	strrchr()	c_library
 *
 *	side effects:
 *		none
 */

int
fndidx(str)
char *str;
{
	register char *p1, *p2;

	/*
	 * Skip Path Delimiters
	 */
	p1 = str;
	if ((p2 = strrchr(p1,  ':')) != NULL) { p1 = p2 + 1; }
	if ((p2 = strrchr(p1,  '/')) != NULL) { p1 = p2 + 1; }
	if ((p2 = strrchr(p1, '\\')) != NULL) { p1 = p2 + 1; }

	return(p1 - str);
}

/*)Function	VOID	newdot(nap)
 *
 *		area *	nap		pointer to the new area structure
 *
 *	The function newdot():
 *		(1)	copies the current values of fuzz and the last
 *			address into the current area referenced by dot
 *		(2)	loads dot with the pointer to the new area and
 *			loads the fuzz and last address parameters
 *		(3)	outall() is called to flush any remaining
 *			bufferred code from the old area to the output
 *
 *	local variables:
 *		area *	oap		pointer to old area
 *
 *	global variables:
 *		sym	dot		defined as sym[0]
 *		a_uint	fuzz		tracks pass to pass changes in the
 *					address of symbols caused by
 *					variable length instruction formats
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		Current area saved, new area loaded, buffers flushed.
 */

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();
}

/*)Function	VOID	phase(ap, a)
 *
 *		area *	ap		pointer to area
 *		a_uint	a		address in area
 *
 *	Function phase() compares the area ap and address a
 *	with the current area dot.s_area and address dot.s_addr
 *	to determine if the position of the symbol has changed
 *	between assembler passes.
 *
 *	local variables:
 *		none
 *
 *	global varaibles:
 *		sym *	dot		defined as sym[0]
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		The p error is invoked if the area and/or address
 *		has changed.
 */

VOID
phase(ap, a)
struct area *ap;
a_uint a;
{
	if (ap != dot.s_area || a != dot.s_addr)
		err('p');
}

char *usetxt[] = {
	"Usage: [-options] file1 [file2 file3 ...]",
	"  -d   Decimal listing",
	"  -q   Octal   listing",
	"  -x   Hex     listing (default)",
	"  -g   Undefined symbols made global",
	"  -a   All user symbols made global",
	"  -b   Display .define substitutions in listing",
	"  -bb  and display without .define substitutions",
#if NOICE
	"  -j   Enable NoICE Debug Symbols",
#endif
#if SDCDB
	"  -y   Enable SDCC  Debug Symbols",
#endif
	"  -l   Create list   output file1[.lst]",
	"  -o   Create object output file1[.rel]",
	"  -s   Create symbol output file1[.sym]",
	"  -p   Disable listing pagination",
	"  -u   Disable .list/.nlist processing",
	"  -w   Wide listing format for symbol table",
	"  -z   Disable case sensitivity for symbols",
	"  -f   Flag relocatable references by  `   in listing file",
	"  -ff  Flag relocatable references by mode in listing file",
	"",
	NULL
};

/*)Function	VOID	usage(n)
 *
 *		int	n		exit code
 *
 *	The function usage() outputs to the stderr device the
 *	assembler name and version and a list of valid assembler options.
 *
 *	local variables:
 *		char **	dp		pointer to an array of
 *					text string pointers.
 *
 *	global variables:
 *		char	cpu[]		assembler type string
 *		char *	usetxt[]	array of string pointers
 *
 *	functions called:
 *		VOID	asexit()	asmain.c
 *		int	fprintf()	c_library
 *
 *	side effects:
 *		program is terminated
 */

VOID
usage(n)
int n;
{
	register char   **dp;

	fprintf(stderr, "\nASxxxx Assembler %s  (%s)\n\n", VERSION, cpu);
	for (dp = usetxt; *dp; dp++)
		fprintf(stderr, "%s\n", *dp);
	asexit(n);
}

