/* asmain.c */

/*
 *  Copyright (C) 1989-2026  Alan R. Baldwin
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 *
 *
 *   With enhancements from
 *
 *	John L. Hartman	(JLH)
 *	jhartman at compuserve dot com
 *
 *	Boisy G. Pitre (BGP)
 *	boisy at boisypitre dot com
 *
 *	Mike McCarty
 *	mike dot mccarty at sbcglobal dot net
 */

#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()
 *		void	equate(id, *e1, equtype);
 *		FILE *	afile(fn, ft, wf)
 *		int	fndidx(str)
 *		int	intsiz()
 *		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, HLR, 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
 *		char *	lstfil		pointer to .lst filename
 *		char *	lstext		pointer to .lst file extension
 *		char *	relfil		pointer to .rel filename
 *		char *	relext		pointer to .rel file extension
 *		char *	symfil		pointer to .sym filename
 *		char *	symext		pointer to .sym file extension
 *
 *	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
 *		asmf *	asmc 		pointer to current assembler file structure
 *		int	asmline		assembler source file line number
 *		asmf *	asmp		pointer to first assembler file 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 *	cp		pointer to assembler output array cb[]
 *		int *	cpt		pointer to assembler relocation type
 *					output array cbt[]
 *		time_t	curtim		current time string pointer
 *		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
 *		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
 *		int	tflag		-h, hidden option for diagnostic info dump
 *		char	ib[]		assembler-source text line
 *		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 include file count
 *		int	incline		include source file line number
 *		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	lnlist		current LIST-NLIST state
 *		int	lop		current line number on page
 *		int	maxinc		maximum include file nesting counter
 *		int	oflag		-o, generate relocatable output flag
 *		int	page		current page number
 *		int	pflag		enable listing pagination
 *		int	pass		assembler pass number
 *		a_uint	p_mask		memory page length mask
 *		int	radix		current number conversion radix:
 *					2 (binary), 8 (octal), 10 (decimal),
 *					16 (hexadecimal)
 *		int	sflag		-s, generate symbol table flag
 *		int	srcline		current source line number
 *		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 *	hfp		relocation helper file handle
 *		FILE *	tfp		symbol table output file handle
 *
 *	called functions:
 *		FILE *	afile()		asmain.c
 *		void	allglob()	assym.c
 *		void	asexit()	asmain.c
 *		void	diag()		assubr.c
 *		void	err()		assubr.c
 *		void	exprmasks()	asexpr.c
 *		char *	filespec()	asmain.c
 *		int	fprintf()	c_library
 *		int	int32siz()	asmain.c
 *		void	list()		aslist.c
 *		void	lstsym()	aslist.c
 *		void	mcrinit()	asmcro.c
 *		void	minit()		___mch.c
 *		void	multipass()	asmain.c
 *		void *	new()		assym.c
 *		void	newdot()	asmain.c
 *		int	nxtline()	aslex.c
 *		void	outbuf()	asout.c
 *		void	outchk()	asout.c
 *		void	outgsd()	asout.c
 *		int	rewind()	c_library
 *		int	setjmp()	c_library
 *		char *	strcpy()	c_library
 *		void	symglob()	assym.c
 *		void	syminit()	assym.c
 *		time_t	time()		c_library
 *		void	usage()		asmain.c
 *
 *	side effects:
 *		Completion of main() completes the assembly process.
 *		REL, LST, HLR, and/or SYM files may be generated.
 */

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

	char *relfil, *relext;

	if (argc == 1) {
		usage();
		exit(ER_NONE);
	}

	if (intsiz() != 4) {
		fprintf(stderr, "?ASxxxx-Error-Size of INT32 is not 32 bits.\n\n");
		exit(ER_FATAL);
	}

	fprintf(stdout, "\n");

	relfil = "";
	relext = "rel";

	asmc = NULL;
	asmp = NULL;
	for (i=1; i<argc; ++i) {
		p = argv[i];
		if (*p == '-') {
			if (asmp != NULL) {
				usage();
				fprintf(stderr, "?ASxxxx-Error-Options come first.\n");
				asexit(ER_FATAL);
			}
			++p;
			while ((c = *p++) != 0) {
				switch(c) {
				/*
				 * -h   Show the help list
				 */
				case 'h':
				case 'H':
					usage();
					exit(ER_NONE);
					break;

				/*
				 * Enable Object Output (Change name.ext Optional)
				 *   -o   Enable Object Output, Use Default file1.rel 
				 *   -o+[ ][name][.ext]  Change name and/or ext
				 */
				case 'o':
				case 'O':
					++oflag;
					p = filespec(argc, argv, &i, p, &relfil, &relext, c);
					break;

				/*
				 * Create Listing Output
				 */
				case 'l':
				case 'L':
					++lflag;
					break;

				/*
				 * Create Symbol Output
				 */
				case 's':
				case 'S':
					++sflag;
					break;

				/*
				 * Listing:
				 *   -d   Decimal listing
				 *   -q   Octal   listing
				 *   -x   Hex     listing (default)
				 *   -b   Display .define substitutions in listing
				 *   -bb  and display without .define substitutions
				 *   -c   Disable instruction cycle count in listing
				 *   -f   Flag relocatable references by  `   in listing file
				 *   -ff  Flag relocatable references by mode in listing file
				 *   -k   Disable error output to listing file
				 *   -p   Disable automatic listing pagination
				 *   -u   Disable .list/.nlist processing
				 *   -w   Wide listing format for symbol table
				 */
				case 'd':
				case 'D':
					xflag = 2;
					break;

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

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

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

				case 'c':
				case 'C':
					++cflag;
					break;

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

				case 'k':
				case 'K':
					++kflag;
					break;

				case 'p':
				case 'P':
					++pflag;
					break;

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

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

				/*
				 * Assembly Processing Options:",
				 *   -i   Insert assembler line before input file(s)
				 *   -n#  Specify number of assembler scanning passes
				 *   -v   Enable out of range signed / unsigned errors
				 */
				case 'i':
				case 'I':
					if (iflag < 0) {
						iflag = 0;
					}
					++iflag;
					break;

				case 'n':
				case 'N':
					while ((v = digit((c = *p++), 10)) >= 0) {
						nflglmt = 10*nflglmt + v;
					}
					if (nflglmt == 0) nflglmt = -1;
					--p;
					break;

				case 'v':
				case 'V':
					++vflag;
					break;

				/*
				 * Symbol Options:
				 *   -a   All user symbols made global
				 *   -g   Undefined symbols made global
				 *   -z   Disable case sensitivity for symbols
				 */
				case 'a':
				case 'A':
					++aflag;
					break;

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

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

				/*
				 * Debug Options:
				 *   -j   Enable NoICE Debug Symbols
				 *   -y   Enable SDCC  Debug Symbols
				 */
#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

				/*
				 * Unlisted Options:
				 *   -r   list line numbers in .hst help file
				 *   -rr  list line numbers of NON listed lines
				 *   -t   show internal block allocations
				 */
				case 'r':
				case 'R':
					++rflag;
					break;

				case 't':
				case 'T':
					++tflag;
					break;

				/*
				 * Unknown Options:
				 */
				default:
					fprintf(stderr, "?ASxxxx-Warning-Unkown option -%c ignored\n", c);
					break;
				}
			}
		} else {
			if (iflag-- > 0) {
				if (asmo == NULL) {
					/*
					 * Default to NO Listing
					 */
					insline("	.nlist",0);
				}
				insline(p,1);
			} else {
				if (asmp == NULL) {
					fixrelfil(p, &relfil);
					asmp = (struct asmf *)
						new (sizeof (struct asmf));
					if (asmo != NULL) {
						/*
						 * Enable listing after insertions
						 */
						insline("	.list",0);
						insline("	.page",0);
						/*
						 * Append files to insertions
						 */
						asmc->next = asmp;
					} else {
						asmo = asmp;
					}
					asmc = asmp;
				} else {
					asmc->next = (struct asmf *)
							new (sizeof (struct asmf));
					asmc = asmc->next;
				}
				asmc->next = NULL;
				asmc->objtyp = T_ASM;
				asmc->line = 0;
				asmc->flevel = 0;
				asmc->tlevel = 0;
				asmc->lnlist = LIST_NORM;
				asmc->fp = afile(p, dsft, 8);
				asmc->afp = afptmp;
				asmc->afs = strsto(afstmp);
				asmc->afn = strsto(afntmp);
			}
		}
	}
	if (asmp == NULL) {
		usage();
		fprintf(stderr, "?ASxxxx-Error-Missing input file(s)\n");
		asexit(ER_FATAL);
	}
	if (lflag)
		lfp = afile(relfil, "lst", 1);
	if (oflag)
		ofp = afile(relfil, relext, 1);
	if (lflag && oflag)
		hfp = afile(relfil, "hlr", 1);
	if (sflag)
		tfp = afile(relfil, "sym", 1);
	exprmasks(2);
	syminit();
	curtim = time(NULL);
	if (bflag != 0) {
		il = ib;
	} else {
		il = ic;
	}
	for (pass=0; pass<3; ++pass) {
		aserr = 0;
		rlsym = 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;
		lnlist = LIST_NORM;
		ifcnd[0] = 0;
		iflvl[0] = 0;
		radix = 10;
		page = 0;
		stb[0] = 0;
		lop  = NLPP;
		incfil = 0;
		maxinc = 0;
		alevel = 0;
		srcline = 0;
		asmline = 0;
		incline = 0;
		trcflags = 0;
		asmc = asmp;
		afs = asmc->afs;
		afn = asmc->afn;
		afp = asmc->afp;
		while (asmc) {
			if (asmc->fp)
				rewind(asmc->fp);
			asmc = asmc->next;
		}
		asmc = asmo;
		ap = areap;
		while (ap) {
			ap->a_fuzz = 0;
			ap->a_size = 0;
			ap = ap->a_ap;
		}
		fuzz = 0;
		as_msb = 1;
		p_mask = DEFAULT_PMASK;
		dot.s_addr = 0;
		dot.s_area = &dca;
		outbuf("I");
		outchk(0,0);
		symp = &dot;
		mcrinit();
		awg = 0;
		csn = 0;
		minit();
		while ((i = nxtline()) != 0) {
			cp = cb;
			cpt = cbt;
			ep = eb;
			ip = ib;

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

                        opcycles = CYCL_NONE;
			if (setjmp(jump_env) == 0)
				asmbl();
			if (pass == 2) {
				diag();
				list();
			}
		}
		newdot(dot.s_area);	/* Flush area info */
		multipass();		/* Multiple Pass Option */
	}
	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	multipass(void)
 *
 *	The function multipass() compares the values of all
 *	symbols and labels from pass to pass.  At the end of
 *	pass 2 the assembler gives a warning that a symbol or
 *	label value is not stable.  This warning normally
 *	indicates a forward reference has not yet been resolved.
 *	Use the -n# option to increase the pass 1 scans to fully
 *	resolve the forward passes.
 *
 *	The function multipass() is explicitly enabled by assemblers
 *	that support variable size instructions.  Any assembler
 *	using the setbit()/getbit() construct for variable size
 *	instructions must add the following code in the machine
 *	function minit(), where passlmt is the maximum # of passes:
 *
 *	passlmt = 100;
 *	if (pass < 2) {
 *		for (i=0; i<NB; i++) {
 *			bb[i] = 0;
 *		}
 *	}
 *
 *
 *	local variables:
 *		int		i	symbol scan loop counter
 *		int		cflg	a flag indicating a changing value
 *		int		plmt	pass limit parameter
 *		struct	sym *	sp	symbol structure pointer
 *
 *	global variables:
 *		int		nflglmt	-n(#) passlmt override
 *		CONSTANT	NHASH	number of symbol hash buckets
 *		int		pass	current assembler pass
 *		int		passcnt	number of passes completeed
 *		int		passfuz addressing fuzz at end of pass
 *		int		passJLH	flag indicating pass to dump NOICE comments
 *		int		passlmt	pass limit value [0 or defined in minit()]
 *		int		tflag	-t option flag
 *
 *	functions called:
 *		fprintf()		c_library
 *		symhash()		assym()
 *
 *	side effects:
 *		may change pass number to allow rescan of input files
 *		may output warnings about changing values
 *		and update sp->p_addr on each pass
 */
void
multipass(void)
{
	int i, cflg, plmt;
	struct sym *sp;

	/*
	 * Report Changing Symbols/Labels
	 */
	cflg = 0;
	for (i=0; i<NHASH; i++) {
		sp = symhash[i];
		while (sp) {
			if (sp->s_addr != sp->p_addr) {
				cflg |= 1;
				if (pass == 2) {
					fprintf(stderr, "?ASxxxx-Warning-Value of \"%s\" is changing\n", sp->s_id);
				}
			}
			sp->p_addr = sp->s_addr;
			sp = sp->s_sp;
		}
	}
	/*
	 * passlmt Override
	 */
	if (nflglmt == 0) {	/* default */
		plmt = passlmt;
	} else
	if (nflglmt > 0) {	/* -n# */
		plmt = 3 + nflglmt;
	} else {
		plmt = 0;	/* -n / -n0 */
	}
	/*
	 * Multi-Pass Processing
	 */
	passcnt += 1;
	if (plmt) {
		if (pass == 0) {
			if (tflag) fprintf(stderr, "?ASxxxx-Info-pass = %d", passcnt);
		}
		if (passJLH) {
			if (tflag) fprintf(stderr, ",%d\n", passcnt);
			passJLH = 0;
		} else
		if (pass == 1) {
			if (tflag) fprintf(stderr, ",%d", passcnt);
			if (passcnt < (plmt - 2)) {
				if ((fuzz == 0) && (cflg == 0)) {
					passJLH = 1;
				}
			} else {
				passJLH = 1;
			}
			pass = 0;
		}
	} else {
		passJLH = (pass == 0) ? 1 : 0;
	}
	passfuz = fuzz;
}

/*)Function	char *	filespec(argc, argv, i, p, nam, ext, opt)
 *
 *		int		argc	number of arguments
 *		char *		argv[]	pointer to the list of arguments
 *		int *		i	pointer to the argument list index
 *		char *		p	pointer to characters of argv
 *		char **		nam	pointer to address of filename address
 *		char **		ext	pointer to address of extension address
 *		char		opt	option calling this function
 *
 *	The function filespec() returns end point of the scanning
 *
 *	local variables:
 *		char *		q	pointer into argv string
 *		int		pFile	index to beginning of p filename
 *
 *	global variables:
 *		char		ib[]	assembly line buffer used as a temporary
 *		char *		ip	assembly line buffer character pointer
 *
 *	functions called:
 *		asexit()		asmain.c
 *		fndidx()		asmain.c
 *		fprintf()		c_library
 *		strchr()		c_library
 *		strlen()		c_library
 *		strsto()		assym.c
 *
 *	side effects:
 *		evaluates an option for a file name and/or extension
 *		and loads relfil/relext respectively
 */
char *
filespec(int argc, char *argv[], int *i, char *p, char **nam, char **ext, int opt)
{
	char *q;
	int pFile;

	if (*p == '+') {
		/* Skip '+' */
		++p;
		if ((*p == '\0') && ((*i + 2) <= (argc - 1))) {
			p = argv[++(*i)];
		}
		/* Forms: -*+name. / -*+.ext / -*+name.ext */
		pFile = fndidx(p);
		if ((q = strchr(p + pFile, FSEPX)) != NULL) {
			if ((p == q) && (*p == FSEPX)) {
				if (*(++p)) {
					*ext = strsto(p);
					p += strlen(p);
				} else {
					fprintf(stderr, "?ASxxxx-Error-Blank File Extension For -%c+\n", opt);
					asexit(ER_FATAL);
				}
			} else {
				*q = '\0';
				if (*p) {
					*nam = strsto(p);
					p += strlen(p);
				}
				if (*(++q)) {
					*ext = strsto(q);
					p = q + strlen(q);
				}
			}
		} else
		/* Form -*+name */
		if (*p != '\0') {
			*nam = strsto(p);
			p += strlen(p);
		} else {
			fprintf(stderr, "?ASxxxx-Error-Missing [name][.ext] After -%c+", opt);
			asexit(ER_FATAL);
		}
	}
	return(p);
}

/*)Function	void	fixrelfil(p, pthnam)
 *
 *		char *		p	pointer to first path/file/ext
 *		char **		pthnam	pointer to address of path/file address
 *
 *	local variables:
 *		char *		q	temporary string pointer
 *		int		i	looping variable
 *		int		pFile	index to beginning of p filename
 *
 *	global variables:
 *		char		ib[]	string
 *		char *		ip	pointer into ib[]
 *
 *	functions called:
 *		int		fndidx()	asmain.c
 *		char *		strsto()	aslex.c
 *
 *	side effects:
 *		evaluates an option for a file name / extension
 *		(1) if pthnam has a path/name - use it
 *		      else use p path/name
 *
 *		[Notes]
 *			All output files will use the
 *			constructed path/filename
 *
 *			Object output will use the
 *			relext (default is .rel)
 *
 *			The list, symbol, and helper
 *			output files will use their
 *			defaults: .lst, .sym and .hlr
 */
void
fixrelfil(char *p, char **pthnam)
{
	char *q;
	int i, pFile;

 	if (**pthnam == '\0') {
		ip = ib;		/* Use as temporary string */
		pFile = fndidx(p);	/* Index to file name of p */
		for (i=0,q=p; i<pFile; i++) {
			*ip++ = *q++;
		}
		q = p + pFile;		/* Pointer to file.ext string */
		while ((*q != '.') && (*q != '\0')) {
			*ip++ = *q++;
		}
		*ip = '\0';
		*pthnam = strsto(ib);
	}
}

/*)Function	int	intsiz(void)
 *
 *	The function intsiz() returns the size of INT32
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		none
 */

int
intsiz(void)
{
	return(sizeof(INT32));
}

/*)Function	void	insline(str,i)
 *
 *		char *		str	-i command line string
 *		int		i	listing allowed - (0-No/1-Yes)
 *
 *	The function insline() inserts a -i command line
 *	string into the assembly stream by creating a psuedo
 *	assembly file structure	with an object type of T_INSERT.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		asmf *	asmo		command line string structure
 *		asmf *	asmc
 *
 *	functions called:
 *		new()			assym.c
 *		strcpy()		c_library
 *
 *	side effects:
 *		A T_INSERT type assembly file structure is
 *		created which will be appended to any previously
 *		created T_INSERT assembly file structures.
 */
void
insline(char *str, int i)
{
	if (asmo == NULL) {
		asmo = (struct asmf *)
			new (sizeof (struct asmf));
		asmc = asmo;
	} else {
		asmc->next = (struct asmf *)
				new (sizeof (struct asmf));
		asmc = asmc->next;
	}
	asmc->next = NULL;
	asmc->objtyp = T_INSERT;
	asmc->line = 0;
	asmc->flevel = 0;
	asmc->tlevel = 0;
	asmc->lnlist = i;
	asmc->fp = NULL;
	asmc->afp = 0;
	asmc->afs = strsto(str);
	asmc->afn = "";
}

/*)Function	void	asexit(i)
 *
 *		int	i	exit code
 *
 *	The function asexit() explicitly closes all open
 *	files and then terminates the program.
 *
 *	local variables:
 *
 *	global variables:
 *		asmf *	asmc		pointer to first include file structure
 *		asmf *	asmp		pointer to first assembler file structure
 *		FILE *	lfp		list output file handle
 *		FILE *	hfp		rel helper file handle
 *		FILE *	ofp		relocation output file handle
 *		FILE *	tfp		symbol table output file handle
 *		FILE *	stderr		standard error output handle
 *		int	tflag		list following parameters flag
 *		int	maxinc		maximum include file level
 *		int	maxmcr		maximum macro expansion level
 *		int	asmblk		assembler allocation in 1K blocks
 *		int	mcrblk		macro allocation in 1K blocks
 *
 *	functions called:
 *		void	asfree()	asmcro.c
 *		int	fclose()	c_library
 *		int	fprintf()	c_library
 *		void	exit()		c_library
 *
 *	side effects:
 *		All files closed. Program terminates.
 */

void
asexit(int i)
{
	if (lfp != NULL) fclose(lfp);
	if (hfp != NULL) fclose(hfp);
	if (ofp != NULL) fclose(ofp);
	if (tfp != NULL) fclose(tfp);

	while (asmc != NULL) {
		if ((asmc->objtyp == T_INCL) && (asmc->fp != NULL)) {
			fclose(asmc->fp);
		}
		asmc = asmc->next;
	}

	asmc = asmp;
	while (asmc != NULL) {
		if ((asmc->objtyp == T_ASM) && (asmc->fp != NULL)) {
			fclose(asmc->fp);
		}
		asmc = asmc->next;
	}

	asfree();

	if (tflag) {
		if (passlmt || nflglmt) {
			fprintf(stderr, "?ASxxxx-Info-pass count                    = %3d\n", passcnt);
 		        if (passfuz) {
				fprintf(stderr, "?ASxxxx-Info-residual fuzz                 = %3d\n", passfuz);
	 	        }
		}
		fprintf(stderr, "?ASxxxx-Info-maxinc(include file level)    = %3d\n", maxinc);
		fprintf(stderr, "?ASxxxx-Info-maxmcr(macro expansion level) = %3d\n", maxmcr);
		fprintf(stderr, "?ASxxxx-Info-mcrcnt(macro definitions)     = %3d\n", mcrcnt);
		fprintf(stderr, "?ASxxxx-Info-mcrexe(macros executed)       = %3d\n", mcrexe);
		fprintf(stderr, "?ASxxxx-Info-inlexe(inlines executed)      = %3d\n", inlexe);
		fprintf(stderr, "?ASxxxx-Info-asmblk(1K Byte Allocations)   = %3d\n", asmblk);
		fprintf(stderr, "?ASxxxx-Info-mcrblk(1K Byte Allocations)   = %3d\n", mcrblk);
		fprintf(stderr, "\n");
	}

	exit(i);
}

/*)Function	void	asmbl(void)
 *
 *	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, (4) macros and
 *	macro definitions, and (5) machine dependent mnemonics.
 *
 *	local variables:
 *		mne *	mp		pointer to a mne structure
 *		mne *	xp		pointer to a mne structure
 *		mcrdef *np		pointer to a macro definition 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
 *		bank *	bp		pointer to a  bank structure
 *		def *	dp		pointer to a  definition structure
 *		expr	e1		expression structure
 *		char	equ[]		equated string
 *		int	equtype		equate type (O_EQU,O_LCLEQU,O_GBLEQU)
 *		char	id[]		id string
 *		char	opt[]		options string
 *		char	fn[]		filename string
 *		char *	p		pointer into a string
 *		char *	q		pointer to macro argument string
 *		int	d		temporary value
 *		int	uf		area options
 *		int	con_ovr		concatenate / overlay flag
 *		int	rel_abs		relocatable / absolute flag
 *		int	npg_pag		page / non page flag
 *		int	csg_dsg		code / data segment flag
 *		a_uint	base		bank base address
 *		a_uint	size		bank size limit
 *		a_uint	map		bank map parameter
 *		a_uint	n		temporary value
 *		a_uint	v		temporary value
 *		int	cnt	        temporary counter
 *		int	flags		temporary flag
 *		FILE *	fp		include file handle
 *		int	m_type		mnemonic type
 *
 *	global variables:
 *		area *	areap		pointer to an area structure
 *		int	alevel		area stack pointer
 *		struct	area *astack[]	area stack
 *		bank *	bankp		pointer to a  bank 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	lnlist		current LIST-NLIST state
 *		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
 *		int	incline		current include file line
 *		int	incfil		current include file count
 *		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
 *		int	csn		'C' Style Numbers option
 *		int	awg		default size value option
 *
 *	functions called:
 *		a_uint	absexpr()	asexpr.c
 *		area *	alookup()	assym.c
 *		bank *	blookup()	assym.c
 *		def *	dlookup()	assym.c
 *		void	clrexpr()	asexpr.c
 *		int	digit()		asexpr.c
 *		char	endline()	aslex.c
 *		void	equate()	asmain.c
 *		void	err()		assubr.c
 *		void	expr()		asexpr.c
 *		int	fndidx()	asmain.c
 *		FILE *	fopen()		c_library
 *		int	fseek()		c_library
 *		int	get()		aslex.c
 *		void	getid()		aslex.c
 *		int	getmap()	aslex.c
 *		int	getnb()		aslex.c
 *		void	getst()		aslex.c
 *		void	getxstr()	asmcro.c
 *		sym *	lookup()	assym.c
 *		void	machine()	___mch.c
 *		int	macro()		asmcro.c
 *		int	mcrprc()	asmcro.c
 *		mne *	mlookup()	assym.c
 *		int	more()		aslex.c
 *		mcrdef *nlookup()	asmcro.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	skpcomma()	aslex.c
 *		int	strcmp()	c_library
 *		char *	strcpy()	c_library
 *		char *	strncpy()	c_library
 *		char *	strsto()	assym.c
 *		void	unget()		aslex.c
 */

void
asmbl(void)
{
	struct mne *mp, *xp;
	struct mcrdef *np;
	struct sym *sp;
	struct tsym *tp;
	int c;
	struct area  *ap;
	struct bank  *bp;
	struct def *dp;
	struct expr e1;
	char id[NCPS];
	char equ[NCPS];
	char *equ_ip;
	int  equtype;
	char opt[NCPS];
	char fn[FILSPC+FILSPC];
	char *p, *q;
	int d, nc, uf;
	int con_ovr, rel_abs, npg_pag, csg_dsg;
	a_uint base, size, map, n, v;
	int skp, flags;
	a_uint cnt;
	FILE * fp;
	int m_type;

	laddr = dot.s_addr;
	lmode = SLIST;

	/*
	 * Check iiff-iift-iiftf processing
	 */
	if (ftflevel != 0) {
		flevel = ftflevel - 1;
		ftflevel = 0;
	}

	/*
	 * Check if Building a Macro
	 * Check if Exiting  a Macro
	 */
	if (mcrprc(O_CHECK) != 0) {
		return;
	}
loop:
	if ((c=endline()) == 0) { return; }

	/*
	 * Clear Expression (On Looping)
	 */
	clrexpr(&e1);
	/*
	 * Provide Option To Restart Scan
	 */
	p = ip - 1;

	/*
	 * 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;
		equ_ip = ip - 1;
		while ((d = digit(c, 10)) >= 0) {
			n = 10*n + d;
			c = get();
		}
		if (c != '$' || get() != ':') {
			/*
			 * Evaluate any arguments
			 * as a default size value
			 */
			if (awg) {
	                	ip = equ_ip;
				expr(&e1);
				switch(awg) {
				case 1:	outrb(&e1, 0);	break;
				case 2:	outrw(&e1, 0);	break;
				case 3:	outr3b(&e1, 0);	break;
				case 4:	outr4b(&e1, 0);	break;
				default:	break;
				}
				if (comma(0)) {
					goto loop;
				}
				lmode = CLIST;
				return;
			}
			qerr();
		}
		tp = symp->s_tsym;
		while (tp) {
			if (n == tp->t_num) {
				if (pass == 0) {
					tp->t_flg |= S_MDF;
				}
				break;
			}
			tp = tp->t_lnk;
		}
		if (tp == NULL) {
			tp=(struct tsym *) new (sizeof(struct tsym));
			tp->t_area = dot.s_area;
			tp->t_addr = dot.s_addr;
			tp->t_lnk = symp->s_tsym;
			tp->t_num = n;
			tp->t_flg = 0;
			symp->s_tsym = tp;
		}
		if (tp->t_flg & S_MDF)
			err('m');
		phase(tp->t_area, tp->t_addr);
		fuzz = tp->t_addr - dot.s_addr;
		tp->t_area = dot.s_area;
		tp->t_addr = dot.s_addr;
		lmode = ALIST;
		goto loop;
	}
	/*
	 * If the first character is a letter then assume a label,
	 * symbol, assembler directive, or assembler mnemonic is
	 * being processed.
	 */
	if ((ctype[c] & LETTER) == 0) {
		if (flevel) {
			return;
		} else {
			qerr();
		}
	}
	getid(id, c);
	/* Disallow Constants */
	mp = mlookup(id);
	if ((mp != NULL) && (mp->m_type == S_CONST)) {
		xerr('q', "Invalid Use Of A Predefined Constant");
	}
	/* Save position for Macro processing */
	q = ip;
	/* Skip white space to next character */
	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)
			qerr();
		if (pass == 0) {
			if ((symp->s_type != S_NEW) && ((symp->s_flag & S_ASG) == 0))
				symp->s_flag |= S_MDF;
		}
		if (symp->s_flag & S_MDF)
			err('m');
		symp->s_type = S_USER;
		phase(symp->s_area, symp->s_addr);
		fuzz = symp->s_addr - dot.s_addr;
		symp->s_area = dot.s_area;
		symp->s_addr = dot.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.
	 *
	 * Syntax:
	 *    [labels] sym =  value   defines an equate.
	 *    [labels] sym == value   defines a global equate.
	 *    [labels] sym =: value   defines an internal machine equate.
	 * If this is a new variable then create a symbol structure.
	 */
	if (c == '=') {
		if (flevel)
			return;
		switch (c = get()) {
		    case '=':   equtype = O_GBLEQU;			break;
		    case ':':   equtype = O_LCLEQU;     		break;
		    default:    equtype = O_EQU;	unget(c);	break;
		}
		equate(id, &e1, equtype);
		goto loop;
	}
	unget(c);
	/*
	 * Check for Equates if 'id' is not an Assembler Directive
	 *
	 * Syntax:
	 *     [labels] sym .equ    value   defines an equate
	 *     [labels] sym .glbequ value   defines a global equate
	 *     [labels] sym .lclequ value   defines a local equate
	 */
	if ((mlookup(id) == NULL) && (nlookup(id) == NULL)) {
		if (flevel)
               		return;
		/*
		 * Alternates for =, ==, and =:
		 */
		equ_ip = ip;	 /* Save current char pointer for case equate not found. */
		if (more() && (ctype[c = getnb()] == LETTER)) {
			getid(equ, c);
                	if ((mp = mlookup(equ)) == NULL || mp->m_type != S_EQU) {
                		ip = equ_ip;
			} else {
	                	equate(id, &e1, mp->m_valu);
	                	goto loop;
			}
		}
	}
	/*
	 * Completed scan for lables , equates, and symbols.
	 */
	lmode = flevel ? SLIST : CLIST;
	/*
	 * An assembler directive, mnemonic, or macro is
	 * required to continue processing line.
	 */
	mp = mlookup(id);
	np = nlookup(id);
	if ((mp == NULL) && (np == NULL)) {
		/*
		 * Evaluate any arguments
		 * as a default size value
		 */
		if (awg) {
			ip = p;
			expr(&e1);
			switch(awg) {
			case 1:	outrb(&e1, 0);	break;
			case 2:	outrw(&e1, 0);	break;
			case 3:	outr3b(&e1, 0);	break;
			case 4:	outr4b(&e1, 0);	break;
			default:	break;
			}
			if (comma(0)) {
				goto loop;
			}
			lmode = CLIST;
			return;
		}
		if (!flevel) {
			err('o');
		}
		return;
	}
	/*
	 * If we have gotten this far then we have found an
	 * assembler directive an assembler mnemonic or
	 * an assembler macro.
	 *
	 * Check for .if[], .iif[], .else, .endif,
	 * .list, .nlist, and .page directives.
	 */
	m_type = (mp != NULL) ? mp->m_type : ~0;

	switch (m_type) {

	case S_CONDITIONAL:
		/*
		 * BGP - .ifeq, .ifne, .ifgt, .iflt, .ifge, .ifle
		 */
		if (mp->m_valu < O_IFEND) {
			if (mp->m_valu == O_IF) {
				/*
				 * Process conditionals of the form
				 *
				 *	.if	cnd(,)	arg1 (, arg2)
				 *
				 * where cnd is one of the following:
				 *
				 *	eq	ne
				 *	gt	lt	ge	le
				 *	def	ndef
				 *	b	nb	idn	dif
				 *	t	f	tf
				 */
				p = ip;
				strcpy(id,".if");
				getid(&id[3],getnb());
				xp = mlookup(id);
				if ((xp != NULL) &&
				    (xp->m_type == S_CONDITIONAL) &&
				    (xp->m_valu != O_IF)) {
			    		mp = xp;
					skpcomma();	/* Skip an immediate (,) */
				} else {
					ip = p;
				}
			}
			if (flevel) {
				n = 0;
			} else {
				switch (mp->m_valu) {
				case O_IF:
				case O_IFNE:	/* .if ne,.... */
				case O_IFEQ:	/* .if eq,.... */
				case O_IFGT:	/* .if gt,.... */
				case O_IFLT:	/* .if lt,.... */
				case O_IFGE:	/* .if ge,.... */
				case O_IFLE:	/* .if le,.... */
					n = absexpr();
					switch (mp->m_valu) {
					default:
					case O_IF:
					case O_IFNE:	n = (((v_sint) n) != 0);	break;
					case O_IFEQ:	n = (((v_sint) n) == 0);	break;
					case O_IFGT:	n = (((v_sint) n) >  0);	break;
					case O_IFLT:	n = (((v_sint) n) <  0);	break;
					case O_IFGE:	n = (((v_sint) n) >= 0);	break;
					case O_IFLE:	n = (((v_sint) n) <= 0);	break;
					}
					break;

				case O_IFB:	/* .if b,.... */
				case O_IFNB:	/* .if nb,.... */
					n = comma(0) ? 0 : 1;
					if (n) {
						getxstr(id);
						if (*id == '\0') {
							n = 0;
						}
						comma(0);
					}
					switch (mp->m_valu) {
					default:
					case O_IFB:	n = (((v_sint) n) == 0);	break;
					case O_IFNB:	n = (((v_sint) n) != 0);	break;
					}
					break;

				case O_IFDEF:	/* .if def,.... */
				case O_IFNDEF:	/* .if ndef,.... */
					getid(id, -1);
					if (((dp = dlookup(id)) != NULL) && (dp->d_dflag != 0)) {
						n = 1;
					} else
					if ((sp = slookup(id)) != NULL) {
						n = (sp->s_type == S_USER) ? 1 : 0;
					} else {
						n = 0;
					}
					switch (mp->m_valu) {
					default:
					case O_IFDEF:	n = (((v_sint) n) != 0);	break;
					case O_IFNDEF:	n = (((v_sint) n) == 0);	break;
					}
					break;

				case O_IFIDN:	/* .if idn,.... */
				case O_IFDIF:	/* .if dif,.... */
					getxstr(id);
					comma(0);
					getxstr(equ);
					n = symeq(id, equ, zflag);
					switch (mp->m_valu) {
					default:
					case O_IFIDN:	n = (((v_sint) n) != 0);	break;
					case O_IFDIF:	n = (((v_sint) n) == 0);	break;
					}
					break;

				case O_IFF:	/* .if f */
				case O_IFT:	/* .if t */
				case O_IFTF:	/* .if tf */
					n = 0;
					break;

				default:
					n = 0;
					qerr();
					break;
				}
			}
			switch (mp->m_valu) {
			default:
				if (tlevel < MAXIF) {
					++tlevel;
					ifcnd[tlevel] = (int) n;
					iflvl[tlevel] = flevel;
					if (!n) {
						++flevel;
					}
				} else {
					err('i');
				}
				if (!iflvl[tlevel]) {
					lmode = ELIST;
					eqt_area = NULL;
					laddr = n;
				} else {
					lmode = SLIST;
				}
				break;

			case O_IFF:		/* .if f */
			case O_IFT:		/* .if t */
			case O_IFTF:	/* .if tf */
				if (tlevel == 0) {
					err('i');
					lmode = SLIST;
					break;
				}
				if (iflvl[tlevel] == 0) {
					if (ifcnd[tlevel]) {
						switch (mp->m_valu) {
						default:
						case O_IFF:	flevel = 1;	break;
						case O_IFT:	flevel = 0;	break;
						case O_IFTF:	flevel = 0;	break;
						}
					} else {
						switch (mp->m_valu) {
						default:
						case O_IFF:	flevel = 0;	break;
						case O_IFT:	flevel = 1;	break;
						case O_IFTF:	flevel = 0;	break;
						}
					}
					lmode = ELIST;
					eqt_area = NULL;
					laddr = flevel ? 0 : 1;
				} else {
					lmode = SLIST;
				}
				break;
			}
			return;
		} else
		if (mp->m_valu < O_IIFEND) {
			if (mp->m_valu == O_IIF) {
				/*
				 * Process conditionals of the form
				 *
				 *	.iif	cnd(,)	arg1 (, arg2)
				 *
				 * where cnd is one of the following:
				 *
				 *	eq	ne
				 *	gt	lt	ge	le
				 *	def	ndef
				 *	b	nb	idn	dif
				 *	t	f	tf
				 */
				p = ip;
				strcpy(id,".iif");
				getid(&id[4],getnb());
				skpcomma();	/* Skip an immediate (,) */
				xp = mlookup(id);
				if ((xp != NULL) &&
				    (xp->m_type == S_CONDITIONAL) &&
				    (xp->m_valu != O_IIF)) {
				    	mp = xp;
					comma(0);
				} else {
					ip = p;
				}
			}
			switch (mp->m_valu) {
			case O_IIFF:	/* .iif f */
			case O_IIFT:	/* .iif t */
			case O_IIFTF:	/* .iif tf */
				if (tlevel == 0) {
					err('i');
					lmode = SLIST;
					return;
				}
				if (iflvl[tlevel] == 0) {
					ftflevel = flevel + 1;
					if (ifcnd[tlevel] != 0) {
						switch (mp->m_valu) {
						default:
						case O_IIFF:	flevel = 1;	break;
						case O_IIFT:	flevel = 0;	break;
						case O_IIFTF:	flevel = 0;	break;
						}
					} else {
						switch (mp->m_valu) {
						default:
						case O_IIFF:	flevel = 0;	break;
						case O_IIFT:	flevel = 1;	break;
						case O_IIFTF:	flevel = 0;	break;
						}
					}
				}
				n = flevel ? 0 : 1;
				/*
				 * Skip trailing ','
				 */
				comma(0);
				lmode = SLIST;
				if (n) {
					goto loop;
				}
				return;

			default:
				if (flevel) {
					return;
				}
				break;
			}
			switch (mp->m_valu) {
			case O_IIF:
			case O_IIFNE:	/* .iif ne,.... */
			case O_IIFEQ:	/* .iif eq,.... */
			case O_IIFGT:	/* .iif gt,.... */
			case O_IIFLT:	/* .iif lt,.... */
			case O_IIFGE:	/* .iif ge,.... */
			case O_IIFLE:	/* .iif le,.... */
				n = absexpr();
				switch (mp->m_valu) {
				default:
				case O_IIF:
				case O_IIFNE:	n = (((v_sint) n) != 0);	break;
				case O_IIFEQ:	n = (((v_sint) n) == 0);	break;
				case O_IIFGT:	n = (((v_sint) n) >  0);	break;
				case O_IIFLT:	n = (((v_sint) n) <  0);	break;
				case O_IIFGE:	n = (((v_sint) n) >= 0);	break;
				case O_IIFLE:	n = (((v_sint) n) <= 0);	break;
				}
				break;

			case O_IIFB:	/* .iif b,.... */
			case O_IIFNB:	/* .iif nb,.... */
				n = comma(0) ? 0 : 1;
				if (n) {
					getxstr(id);
					if (*id == '\0') {
						n = 0;
					}
					comma(0);
				}
				switch (mp->m_valu) {
				default:
				case O_IIFB:	n = (((v_sint) n) == 0);	break;
				case O_IIFNB:	n = (((v_sint) n) != 0);	break;
				}
				break;

			case O_IIFDEF:	/* .iif def,.... */
			case O_IIFNDEF:	/* .iif ndef,.... */
				getid(id, -1);
				if (((dp = dlookup(id)) != NULL) && (dp->d_dflag != 0)) {
					n = 1;
				} else
				if ((sp = slookup(id)) != NULL) {
					n = (sp->s_type == S_USER) ? 1 : 0;
				} else {
					n = 0;
				}
				switch (mp->m_valu) {
				default:
				case O_IIFDEF:	n = (((v_sint) n) != 0);	break;
				case O_IIFNDEF:	n = (((v_sint) n) == 0);	break;
				}
				break;

			case O_IIFIDN:	/* .iif idn,.... */
			case O_IIFDIF:	/* .iif dif,.... */
				getxstr(id);
				comma(0);
				getxstr(equ);
				n = symeq(id, equ, zflag);
				switch (mp->m_valu) {
				default:
				case O_IIFIDN:	n = (((v_sint) n) != 0);	break;
				case O_IIFDIF:	n = (((v_sint) n) == 0);	break;
				}
				break;

			default:
				n = 0;
				qerr();
				break;
			}
			/*
			 * Skip trailing ','
			 */
			comma(0);
			lmode = SLIST;
			if (n) {
				goto loop;
			}
			return;
		}

		switch (mp->m_valu) {
		case O_ELSE:
			if (tlevel != 0) {
				if (ifcnd[tlevel]) {
					flevel = iflvl[tlevel] + 1;
					ifcnd[tlevel] = 0;
				} else {
					flevel = iflvl[tlevel];
					ifcnd[tlevel] = 1;
				}
				if (!iflvl[tlevel]) {
					lmode = ELIST;
					eqt_area = NULL;
					laddr = ifcnd[tlevel];
					return;
				}
			} else {
				err('i');
			}
			lmode = SLIST;
			return;

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

		default:
			break;
		}
		qerr();
		break;

	case S_LISTING:
		lmode = NLIST;
		flags = 0;
		while ((c=endline()) != 0) {
			if (c == ',') {
				c = getnb();
			}
			if (c == '(') {
				do {
					if ((c = getnb()) == '!') {
						flags |= LIST_NOT;
 					} else {
						unget(c);
						getid(id, -1);
						if (symeq(id, "err", 1)) { flags |= LIST_ERR; } else
						if (symeq(id, "loc", 1)) { flags |= LIST_LOC; } else
						if (symeq(id, "bin", 1)) { flags |= LIST_BIN; } else
						if (symeq(id, "eqt", 1)) { flags |= LIST_EQT; } else
						if (symeq(id, "cyc", 1)) { flags |= LIST_CYC; } else
						if (symeq(id, "lin", 1)) { flags |= LIST_LIN; } else
						if (symeq(id, "src", 1)) { flags |= LIST_SRC; } else
						if (symeq(id, "pag", 1)) { flags |= LIST_PAG; } else
						if (symeq(id, "lst", 1)) { flags |= LIST_LST; } else
						if (symeq(id, "md" , 1)) { flags |= LIST_MD;  } else
						if (symeq(id, "me" , 1)) { flags |= LIST_ME;  } else
						if (symeq(id, "meb", 1)) { flags |= LIST_MEB; } else
						if (symeq(id, "mel", 1)) { flags |= LIST_MEL; } else {
							err('u');
						}
					}
					c = endline();
				} while (c == ',') ;
				if (c != ')') {
					qerr();
				}
			} else {
				unget(c);
				if (absexpr()) {
					flags |=  LIST_TORF;
				} else {
					flags &= ~LIST_TORF;
				}
			}
		}
		if (!(flags & LIST_TORF) && flevel) {
			return;
		}
		if (flags & ~LIST_TORF) {
			if (flags & LIST_NOT) {
				switch(mp->m_valu) {
				case O_LIST:	lnlist = LIST_NONE;	break;
				case O_NLIST:	lnlist = LIST_NORM;	break;
				default:				break;
				}
			}
			if (flags & LIST_BITS) {
				switch(mp->m_valu) {
				case O_LIST:	lnlist |=  (flags & LIST_BITS);	break;
				case O_NLIST:	lnlist &= ~(flags & LIST_BITS);	break;
				default:					break;
				}
			}
		} else {
			switch(mp->m_valu) {
			case O_LIST:	lnlist = LIST_NORM;	break;
			case O_NLIST:	lnlist = LIST_NONE;	break;
			default:				break;
			}
		}
		return;

	case S_TRACE:
		flags = 0;
		while ((c=endline()) != 0) {
			if (c == ',') {
				c = getnb();
			}
			if (c == '(') {
				do {
					if ((c = getnb()) == '!') {
						flags |= TRC_NOT;
 					} else {
						unget(c);
						getid(id, -1);
						if (symeq(id, "ins", 1)) { flags |= TRC_INS; } else
						if (symeq(id, "asm", 1)) { flags |= TRC_ASM; } else
						if (symeq(id, "inc", 1)) { flags |= TRC_INC; } else
						if (symeq(id, "mcr", 1)) { flags |= TRC_MCR; } else
						if (symeq(id, "rpt", 1)) { flags |= TRC_RPT; } else {
							err('u');
						}
					}
					c = endline();
				} while (c == ',') ;
				if (c != ')') {
					qerr();
				}
			} else {
				flags = TRC_ALL;
			}
		}
		if (flags == 0) {
			flags = TRC_ALL;
		} else
		if (flags & TRC_NOT) {
			switch(mp->m_valu) {
			case O_TRC:	trcflags = TRC_NONE;	break;
			case O_NTRC:	trcflags = TRC_ALL;	break;
			default:				break;
			}
		}
		switch(mp->m_valu) {
		case O_TRC:	trcflags |=  (flags & TRC_ALL);	break;
		case O_NTRC:	trcflags &= (~flags & TRC_ALL);	break;
		default:				break;
		}
		return;

	case S_PAGE:
		lmode = NLIST;
		if (more()) {
			n = absexpr() ? 1 : 0;
		} else {
			n = 0;
		}
		if (!n && flevel)
			return;

		lop = (lnlist & LIST_PAG) ? NLPP : 1;
		return;

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

	case S_OPTN:	/* .enabl (op = 1), .dsabl (op = 0) */
		if ((c = getnb()) == '(') {
			do {
				getid(id, -1);
				/* 'C' Style Numbers - 0nnn (Octal), 0xnnn (Hex), Else Decimal */
				if (symeq(id, "csn", 1)) { csn = mp->m_flag; } else
				/* External Machine Options */
				if (*mchoptn_ptr && ((*mchoptn_ptr)(id, (int) (mp->m_valu)))) { ; } else {
					err('u');
				}
			} while ((c = getnb()) == ',');
			if (c != ')')
				qerr();
		}
		lmode = SLIST;
		break;

	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:
		switch(mp->m_valu) {
		case I_CODE:
			lmode = SLIST;
    			if (++incfil > MAXINC) {
				--incfil;
				err('i');
				break;
			}
			if (incfil > maxinc) {
				maxinc = incfil;
			}
			/*
			 * Copy path of file opening the include file
			 */
			strncpy(fn,afs,afp);
			/*
			 * Concatenate the .include file specification
			 */
			getdstr(fn + afp, FILSPC + FILSPC - afp);
			/*
			 * If .include specifies a path
			 * 	use it
			 * else
			 *	use path of file opening the include file
			 */
			if (fndidx(fn + afp) != 0) {
				afilex(fn + afp, "", 8);
			} else {
				afilex(fn, "", 8);
			}
			/*
			 * Open File
			 */
    			if ((fp = fopen(afstmp, "r")) == NULL) {
				--incfil;
				err('i');
			} else {
				asmi = (struct asmf *) new (sizeof (struct asmf));
				asmi->next = asmc;
				asmi->objtyp = T_INCL;
				asmi->line = srcline;
				asmi->flevel = flevel;
				asmi->tlevel = tlevel;
				asmi->lnlist = lnlist;
				asmi->fp = fp;
				asmi->afp = afptmp;
				asmi->afs = strsto(afstmp);
				asmi->afn = asmc->afn;

				if (lnlist & LIST_PAG) {
					lop = NLPP;
				}
			}
			break;

		case I_BNRY:
			/*
			 * Copy path of file opening the .incbin file
			 */
			strncpy(fn,afs,afp);
			/*
			 * Concatenate the .incbin file specification
			 */
			getdstr(fn + afp, FILSPC + FILSPC - afp);
			/*
			 * If .incbin specifies a path
			 * 	use it
			 * else
			 *	use path of file opening the .incbin file
			 */
			if (fndidx(fn + afp) != 0) {
				afilex(fn + afp, "", 8);
			} else {
				afilex(fn, "", 8);
			}
			/*
			 * Skip Count
			 */
			skp = 0;
			comma(0);
			if (more() && !comma(0))
				skp = (int) absexpr();
			/*
			 * Insert Count
			 */
			cnt = ~0;
			comma(0);
			if (more())
				cnt = absexpr();
			/*
			 * Open File
			 */
			p = "rb";
    			if ((fp = fopen(afstmp, p)) == NULL) {
				xerr('i', "File not found.");
				break;
			}
			/*
			 * Skip To Position
			 */
			fseek(fp, skp, SEEK_SET);
			if (fread(&c, 1, 1, fp) != 1) {
				xerr('i', "Offset past End-Of-File.");
				break;
			}
			fseek(fp, skp, SEEK_SET);
			/*
			 * Read Bytes
			 */
			while (cnt > 0) {
				if (fread(&c, 1, 1, fp) == 1) {
					outab(c);
				} else {
					break;
				}
				cnt -= 1;
			}
			/*
			 * Close File
			 */
			fclose(fp);
			break;

		default:
			xerr('i', "Internal ___PST.C Error.");
			break;
		}
		break;

	case S_AREA:
		if (mp->m_valu == O_POP) {
			if (alevel == 0) {
				xerr('q', "Area Stack Is Empty");
			}
			newdot(astack[--alevel]);
			lmode = SLIST;
			break;
		} else
		if (mp->m_valu == O_PSH) {
			if (alevel >= 16) {
				xerr('q', "Area Stack Is Full");
			}
			astack[alevel++] = dot.s_area;
			lmode = SLIST;
			break;
		}
		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);
				if (symeq(opt, "BANK", 1)) {
					uf |= A_BNK;
					if ((c = getnb()) != '=')
						qerr();
					if (!more())
						qerr();
					getid(opt, -1);
					if ((bp = blookup(opt)) == NULL) {
						err('u');
						uf &= ~A_BNK;
					}
				} else
				if (symeq(opt, "CON", 1)) {
					uf |= A_CON;
					con_ovr = A_CON;
				} else
				if (symeq(opt, "OVR", 1)) {
					uf |= A_OVR;
					con_ovr = A_OVR;
				} else
				if (symeq(opt, "REL", 1)) {
					uf |= A_REL;
					rel_abs = A_REL;
				} else
				if (symeq(opt, "ABS", 1)) {
					uf |= A_ABS;;
					rel_abs = A_ABS;
				} else
				if (symeq(opt, "NOPAG", 1)) {
					uf |= A_NOPAG;
					npg_pag = A_NOPAG;
				} else
				if (symeq(opt, "PAG", 1)) {
					uf |= A_PAG;
					npg_pag = A_PAG;
				} else
				if (symeq(opt, "CSEG", 1)) {
					csg_dsg = area[0].a_flag & (A_CSEG | A_BYTES);
					uf |= csg_dsg;
				} else
				if (symeq(opt, "DSEG", 1)) {
					csg_dsg = area[1].a_flag & (A_DSEG | A_BYTES);
					uf |= csg_dsg;
				} 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;
			ap->a_bn = NULL;
			ap->a_bndry = 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);
				if (symeq(opt, "BASE", 1)) {
					uf |= B_BASE;
					if ((c = getnb()) != '=')
						qerr();
					base = absexpr();
				} else
				if (symeq(opt, "SIZE", 1)) {
					uf |= B_SIZE;
					if ((c = getnb()) != '=')
						qerr();
					size = absexpr();
				} else
				if (symeq(opt, "FSFX", 1)) {
					uf |= B_FSFX;
					if ((c = getnb()) != '=')
						qerr();
					if ((ctype[c = getnb()] & (LETTER|DIGIT)) == 0)
						qerr();
					getid(equ, c);
				} else
				if (symeq(opt, "MAP", 1)) {
					uf |= 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(equ, bp->b_fsfx, 1))
						err('m');
				} else {
					bp->b_fsfx  = strsto(equ);
					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(equ) : 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) == A_ABS) {
			outall();
			laddr = dot.s_addr = absexpr();
		} else {
			err('o');
		}
		outall();
		lmode = ALIST;
		break;

	case S_RADIX:
		if (more()) {
			switch (ccase[getnb()& 0x7F]) {
			case 'b':	radix = 2;	break;	/* B */
			case '@':				/* @ */
			case 'o':				/* O */
			case 'q':	radix = 8;	break;	/* Q */
			case 'd':	radix = 10;	break;	/* D */
			case 'h':				/* H */
			case 'x':	radix = 16;	break;	/* X */
			default:				/* 2, 8, 10, 16 */
				unget(c);
				expr(&e1);
				if (is_abs(&e1)) {
					v = e1.e_addr;
					if ((v == 2) || (v == 8) ||
					   (v == 10) || (v == 16)) {
						radix = (int) v;
						break;
					}
				}
				radix = 10;
				qerr();
				break;
			}
		} else {
			radix = 10;
		}
		lmode = SLIST;
		break;

	case S_GLOBL:
		do {
			getid(id, -1);
			sp = lookup(id);
			sp->s_flag &= ~S_LCL;
			sp->s_flag |=  S_GBL;
		} while (comma(0));
		lmode = SLIST;
		break;

	case S_LOCAL:
		do {
			getid(id, -1);
			sp = lookup(id);
			sp->s_flag &= ~S_GBL;
			sp->s_flag |=  S_LCL;
		} while (comma(0));
		lmode = SLIST;
		break;

	case S_EQU:
		/*
		 * Syntax:
		 *     [labels] .equ    sym, value   defines an equate
		 *     [labels] .glbequ sym, value   defines a global equate
		 *     [labels] .lclequ sym, value   defines a local equate
		 */
		getid(id, -1);
		comma(1);
		equate(id, &e1, mp->m_valu);
		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);
		/*
		 * Blank argument outputs a '0' value
		 */
		if (!more()) {
			switch(size) {
			default:
			case 1:	outab(0); break;
			case 2: outaw(0); break;
			case 3: outa3b(0); break;
			case 4: outa4b(0); break;
			}
			break;
		}
		/*
		 * Intrinsic size == 1	or
		 * Intrinsic size == Data size
		 *
		 * Use full relocation mode
		 */
		if ((n == 1) || (n == size)) {
			do {
				clrexpr(&e1);
				expr(&e1);
				/*
				 * 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 (comma(0));
		} else
		/*
		 * Data size == 1
		 *
		 * Coerce bytes into intrinsic size words
		 * **********-(NO relocation)-**********
		 */
		if (size == 1) {
			cnt = 0;
			v = 0;
			do {
				clrexpr(&e1);
				expr(&e1);
				/*
				 * MSB coercion is not allowed
				 */
				if (e1.e_rlcf & R_MSB) {
					err('o');
				}
				if (!is_abs(&e1))
					err('r');
				if ((int) 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 (comma(0));
			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);
				/*
				 * 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 (comma(0));
		}
		break;

	case S_BLK:
		if ((a_uint) a_bytes < mp->m_valu) {
			err('o');
		}
		clrexpr(&e1);
		expr(&e1);
		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:
		size = 1 + ((dot.s_area->a_flag) & A_BYTES);
		cnt = 0;
		n = 0;
		v = 0;
		while (more()) {
			d = getdlm();
			if (d == LFTERM) {
				/*
				 * Isolate the term '(...)' so that
				 * following arithmetic characters
				 * like %,^,&,*,-,+,!,|,/ don't cause
				 * expr() to cough on the evaluation.
				 */
				p = ip - 1;	/* position of LFTERM */
				d = 1;
				while (d != 0) {
					c = getnb();
					if (c == LFTERM) d += 1;
					if (c == RTTERM) d -= 1;
				}
				q = ip;	/* Save position following RTTERM */
				c = *q; /* Save the character following RTTERM */
				*q = 0; /* Make a terminator */
				ip = p; /* Restore pointer to LFTERM and evaluate */
				clrexpr(&e1);
				expr(&e1);
				*q = c;	/* Restore character */
				ip = q;	/* Restore pointer */
				abscheck(&e1);
				c = (int) e1.e_addr;
				if (mp->m_valu == O_ASCIS) {
					if (!more()) {
						c |= 0x80;
					}
					n += 1;
				}
				pckchr(size, c, &v, &cnt);
			} else {
				switch(mp->m_valu) {
				case O_ASCII:
				case O_ASCIZ:
					while ((c = getmap(d)) >= 0) {
						pckchr(size, c, &v, &cnt);
					}
					break;
				case O_ASCIS:
					if ((c = getmap(d)) > 0) {
						while (c >= 0) {
							c &= 0x7F;
							if ((nc = getmap(d)) < 0) {
								if (!more()) {
									c |= 0x80;
								}
							}
							n += 1;
							pckchr(size, c, &v, &cnt);
							c = nc;
						}
					}
					break;
				default:
					break;
				}
			}
		}
		switch(mp->m_valu) {
		case O_ASCIZ:
			pckchr(size, '\0', &v, &cnt);
			break;
		case O_ASCIS:
			if (n == 0) {
				pckchr(size, 0x80, &v, &cnt);
			}
			break;
		default:
			break;
		}
		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 S_DEFINE:
		/*
		 * Extract the .(un)define key word.
		 */
		getid(id, -1);

		switch(mp->m_valu) {
		case O_DEF:
			/*
			 * Extract the substitution string
			 */
			comma(0);
			*opt = 0;
			if (more()) {
				getdstr(opt, NCPS);
			}
			/*
			 * 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:
			boundary(0);
			outall();
			laddr = dot.s_addr = (dot.s_addr + 1) & ~1;
			lmode = ALIST;
			break;

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

		case O_BNDRY:
			v = absexpr();
			n = dot.s_addr % v;
			if (n != 0) {
				dot.s_addr += (v - n);
			}
			boundary(v);
			outall();
			laddr = dot.s_addr;
			lmode = ALIST;
			break;

		default:
			break;
		}
		break;

	case S_MSG:
		lmode = SLIST;
		/*
		 * Print the .msg message
		 */
		getdstr(fn, FILSPC+FILSPC);
		if (pass == 2) {
			printf("%s\n", fn);
		}
		break;

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

	case S_MSB:
		switch (mp->m_valu) {
		default:
		case O_MSB:
			clrexpr(&e1);
			expr(&e1);
			if (!is_abs(&e1)) {
				err('o');
			}
			if (e1.e_addr >= (a_uint) a_bytes) {
				err('o');
			} else {
				as_msb = (int) e1.e_addr;
			}
			break;

		case O_HILO:
		case O_LOHI:
			c = (mp->m_valu == O_HILO) ? 1 : 0;
			if ((pass != 0) && ((int) hilo != c)) {
				err('m');
			}
			hilo = c;
			break;
		}
		lmode = SLIST;
		break;

	case S_END:
		lmode = SLIST;
		if (more()) {
			sp = lookup(".__.END.");
			if (sp->s_type != S_NEW && (sp->s_flag & S_ASG) == 0) {
				err('m');
			}
			equate(".__.END.", &e1, O_GBLEQU);
		}
		break;

	case S_MACRO:
		lmode = SLIST;
		mcrprc((int) mp->m_valu);
		return;

	/*
	 * If not an assembler directive then go to the
	 * macro function or machine dependent function
	 * which handles all the assembler mnemonics.
	 *
	 * MACRO Definitions take precedence
	 * over machine specific mnemonics.
	 */
	default:
		if (np != NULL) {
			ip = q;
			macro(np);
			lmode = SLIST;
		} else {
			machine(mp);
		}

		/*
		 * Include Files and Macros are not Debugged
		 */
		if (asmc->objtyp == T_ASM) {

#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	void	pckchr(size, c, v, cnt)
 *
 *		a_uint		size		machine size
 *		int		c		character to pack
 *		a_uint		*v		pointer to value of packed bytes
 *		a_uint		*cnt		pointer to packing byte count
 *
 *	The function pckchr() packs a character into a multi-character
 *	value based on the machine size and hilo byte ordering.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		int		hilo		machine byte order
 *
 *	functions called:
 *		void		outab()		asout.c
 *		void		outaw()		asout.c
 *		void		outa3b()	asout.c
 *		void		outa4b()	asout.c
 *
 *	side effects:
 *		The function updates the packed value and the
 *		character count.  When enough characters have
 *		been packed the packed value is output.
 */
void
pckchr(a_uint size, int c, a_uint *v, a_uint *cnt)
{
	if ((int) hilo) {
		*cnt += 1;
		*v |= ((c & 0xFF) << 8 * (size - (*cnt)));
	} else {
		*v |= ((c & 0xFF) << 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;
	}
}

/*)Function	void	boundary(n)
 *
 *		a_uint		n		boundary value
 *
 *	The function boundary() adds a boundary value required
 *	by the assembled code in the current area and determines
 *	the smallest common boundary of all boundaries.
 *
 *	boundary has no return value
 *
 *	local variables:
 *		struct area *	ap	pointer to current area structure
 *		struct bndry *	bn	pointer to a boundary structure
 *		struct bndry *	ibn	pointer to a boundary structure
 *		struct bndry *	jbn	pointer to a boundary structure
 *		struct bndry *	kbn	pointer to a boundary structure
 *		a_uint		m	modulus factor
 *
 *	global variables:
 *		dot.s_area		current area pointer
 *
 *	functions called:
 *		void	err()		assubr.c
 *              char *	new()		assym.c
 *
 *	side effects:
 *		A new boundary structure may be created
 *		and added to the linked boundary list.
 *		The smallest common boudary is determined
 *		and saved in the current area structure.
 */

void
boundary(a_uint n)
{
	struct area *ap;
	struct bndry *bn, *ibn, *jbn, *kbn;
	a_uint m;

	ap = dot.s_area;

#ifdef	LONGINT
	m = 0x00010000l; /* last 'l' is lower case L */
#else
	m = 0x00010000;
#endif
	if (ap->a_bndry >= m) {
		err('c');
		return;
	} else 
	if (n >= m) {
		err('c');
		return;
	} else
	/*
	 * .even and .odd
	 */
	if (n <= 1) {
		n = 2;
	}

	/*
	 * Scan boundary entries and
	 * exit on a duplicate.
	 */
	bn = ap->a_bn;
	while (bn != NULL) {
		if (bn->a_bndry == n) {
			return;
		}
		bn = bn->a_bn;
	}
	/*
	 * Create new boundary entry and
	 * place at beginning of list.
	 */
	bn = (struct bndry *) new (sizeof(struct bndry));
	bn->a_bndry = n;
	bn->a_bn = ap->a_bn;
	ap->a_bn = bn;

	/*
	 * Exit on first boundary definition
	 */
	if (bn->a_bn == NULL) {
		ap->a_bndry = n;
		return;
	}

	/*
	 * Compute smallest common boundary
	 */
	ap->a_bndry *= n;
	ibn = ap->a_bn;
	while (ibn != NULL) {
		for (jbn=ap->a_bn; jbn!=NULL; jbn=jbn->a_bn) {
			m = ibn->a_bndry % jbn->a_bndry;
			for (kbn=ap->a_bn; kbn!=NULL; kbn=kbn->a_bn) {
				if (m < 2) {
					break;
				}
				if ((ap->a_bndry / m) < kbn->a_bndry) {
					break;
				}
				if (((ap->a_bndry / m) % kbn->a_bndry) != 0) {
					break;
				}
			}
			if (kbn == NULL) {
				ap->a_bndry /= m;
				break;
			}
		}
		if (jbn != NULL) {
			ibn = ap->a_bn;
		} else {
			ibn = ibn->a_bn;
		}
	}
#ifdef	LONGINT
	m = 0x00010000l; /* last 'l' is lower case L */
#else
	m = 0x00010000;
#endif
	if (ap->a_bndry >= m) {
		ap->a_bndry = m;
		err('c');
	}
}

/*)Function	void	equate(id, e1, equtype)
 *
 *		char *		id		ident to equate
 *		struct expr *	e1		value of equate
 *		a_uint		equtype		equate type (O_EQU,O_LCLEQU,O_GBLEQU)
 *
 *	The function equate() installs an equate of a
 *	given type.
 *
 *	equate has no return value
 *
 *	local variables:
 *		struct sym *    sp	symbol being equated
 *
 *	global variables:
 *		lmode                   set to ELIST
 *
 *	functions called:
 *              void    abscheck()      asexpr.c
 *		void	err()		assubr.c
 *		sym  *	lookup()	assym.c
 *              void    outall()        asout.c
 *		void	rerr()		assubr.c
 *
 *	side effects:
 *		A new symbol may be created.
 *		Symbol parameters are updated.
 */

void
equate(char *id, struct expr *e1, a_uint equtype)
{
	struct sym *sp;

	clrexpr(e1);
	rprterr = 1;
	expr(e1);

	sp = lookup(id);
	if (sp == &dot) {
		outall();
		if (e1->e_flag || e1->e_base.e_ap != dot.s_area)
			err('.');
	}

#if 0
	if (aflag == 0) {	/* Inhibit When All Are Global (-a) */
		switch(equtype) {
		case O_EQU:	/* Assumes Previous Type - Check Global */
		case O_GBLEQU:	/* Redefiniton Of Type Must Have Same Value */
			if ((sp->s_type != S_NEW) && (sp->s_flag & S_GBL) &&
			   ((sp->s_addr != e1->e_addr) || (sp->s_area != e1->e_base.e_ap))) {
				sp->s_flag |=  S_MDF;
			} else {
				sp->s_flag &= ~S_MDF;
			}
			break;
	
		case O_LCLEQU:	/* Redefiniton Of Type Inhibits Global Output */
		default:
			break;
		}
		if (sp->s_flag & S_MDF)
			xerr('m', "Declared global has multiple values");
	}
#endif

	switch(equtype) {
	default:
	case O_EQU:
		break;

	case O_GBLEQU:
		sp->s_flag &= ~S_LCL;
		sp->s_flag |=  S_GBL;
		break;

	case O_LCLEQU:
		sp->s_flag &= ~S_GBL;
		sp->s_flag |=  S_LCL;
		break;
	}

	if (e1->e_flag && (e1->e_base.e_sp->s_type == S_NEW)) {
		rerr();
		xerr('x', "global argument NOT ALLOWED in an equate");
	} else {
		sp->s_area = e1->e_base.e_ap;
	}
	sp->s_flag |= S_ASG;
	sp->s_type = S_USER;

	sp->s_addr = laddr = e1->e_addr;
	lmode = ELIST;
	if (sp->s_area != NULL) {
		eqt_area = sp->s_area->a_id;
	} else {
		eqt_area = NULL;
	}
}

/*)Function	FILE *	afile(fs, ft, wf)
 *
 *		char *	fs		file specification string
 *		char *	ft		file type string
 *		int	wf		0 ==>> read
 *					1 ==>> write
 *					2 ==>> binary read
 *					3 ==>> binary write
 *
 *					add 8 to the wf code to allow
 *					any extension on a file
 *
 *	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
 *		char *	frmt		read/write format string
 *
 *	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(char *fs, char *ft, int wf)
{
	FILE *fp;
	char *frmt;

	afilex(fs, ft, wf);

	/*
	 * Select (Binary) Read/Write
	 */
	switch(wf & 3) {
	default:
	case 0:	frmt = "r";	break;
	case 1:	frmt = "w";	break;
	case 2:	frmt = "rb";	break;
	case 3:	frmt = "wb";	break;
	}

	if ((fp = fopen(afstmp, frmt)) == NULL) {
	    fprintf(stderr, "?ASxxxx-Error-<cannot %s> : \"%s\"\n", (frmt[0] == 'w')?"create":"open", afstmp);
	    asexit(ER_FATAL);
	}

	return (fp);
}

/*)Function	void	afilex(fs, ft, wf)
 *
 *		char *	fs		file specification string
 *		char *	ft		file type string
 *		int	wf		0 ==>> read
 *					1 ==>> write
 *					2 ==>> binary read
 *					3 ==>> binary write
 *
 *					add 8 to the wf code to allow
 *					any extension on a file
 *
 *	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 fs
 *			and the extension in ft.
 *		(2)	If the file type specification string ft
 *			is NULL then the file specification is
 *			constructed from fs.  If fs 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 afstmp
 *		char *	p2		pointer into filespec string fn
 *
 *	global variables:
 *		char *	afstmp		afilex() constructed filespec
 *		char *	afntmp		afilex() constructed filename
 *		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(char *fs, char *ft, int wf)
{
	char *p1, *p2;
	int c;

	if (strlen(fs) > (FILSPC-7)) {
		fprintf(stderr, "?ASxxxx-Error-<filspc to long> : \"%s\"\n", fs);
		asexit(ER_FATAL);
	}

	/*
	 * Save the Filespec and Name Index
	 */
	strcpy(afstmp, fs);
	afptmp = fndidx(afstmp);

	/*
	 * Skip to File Extension separator
	 */
	p1 = strrchr(&afstmp[afptmp], FSEPX);

	/*
	 * Save File Name
	 */
	if (p1 == NULL) {
		strcpy(afntmp, &afstmp[afptmp]);
	} else {
		*p1 = 0;
		strcpy(afntmp, &afstmp[afptmp]);
		*p1 = FSEPX;
	}

	/*
	 * Allow any extension if FSEPX
	 * is present. <path><name><FSEPX>...
	 */
	if ((wf & 8) && (p1 != NULL)) {
		/*
		 * Remove FSEPX when extension is BLANK
		 */
		if (*(p1+1) == 0) {
			*p1 = 0;
		}
	/*
	 * Else all reads and writes default to ft.
	 * <path><name>... -> <path><name><FSEPX><ft>
	 */
	} else {
		/*
		 * Copy File Extension
		 */
		p2 = ft;
		if (p1 == NULL) {
			p1 = &afstmp[strlen(afstmp)];
		}
		*p1++ = FSEPX;
		while ((c = *p2++) != 0) {
			if (p1 < &afstmp[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(char *str)
{
	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((int) (p1 - str));
}

/*)Function	void	newdot(nap)
 *
 *		area *	nap		pointer to the new area structure
 *
 *	The function newdot():
 *		(1)	loads dot with the pointer to the new area and
 *			loads the fuzz and last address parameters
 *		(2)	outall() is called to flush any remaining
 *			bufferred code from the old area to the output
 *
 *	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:
 *		outall()		asout.c
 *
 *	side effects:
 *		Area changed and buffers flushed.
 */

void
newdot(struct area *nap)
{
	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(struct area *ap, a_uint a)
{
	if (ap != dot.s_area || a != dot.s_addr)
		err('p');
}

char *usetxt[] = {
	"Usage: [-Options] [-Option with arg] file1 [file2 ...]",
	"  -h   or NO ARGUMENTS  Show this help list",
	"Output:",
	"  -o   Enable  object output  (-o+ change file1[.rel])",
	"    -o+  Conditional Options   -o+[ ][name][.ext]",
	"    '-o+.ext'      (or)  '-o+  .ext'      ->  file1.ext ",
	"    '-o+name'      (or)  '-o+  name'      ->  name[.rel]",
	"    '-o+name.ext'  (or)  '-o+  name.ext'  ->   name.ext",
	"  -l   Create list   file1[.lst]   (file1  <-  name)",
	"  -s   Create symbol file1[.sym]   (file1  <-  name)",
	"Listing:",
	"  -d   Decimal listing",
	"  -q   Octal   listing",
	"  -x   Hex     listing (default)",
	"  -b   Display .define substitutions in listing",
	"  -bb  and display without .define substitutions",
	"  -c   Disable instruction cycle count in listing",
	"  -f   Flag relocatable references by  `   in listing file",
	"  -ff  Flag relocatable references by mode in listing file",
	"  -k   Disable error messages to listing file",
	"  -p   Disable automatic listing pagination",
	"  -u   Disable .list/.nlist processing",
	"  -w   Wide listing format for symbol table",
	"Assembly:",
	"  -i   Insert assembler line before input file(s)",
	"  -v   Enable out of range signed / unsigned errors",
	"  -n#  Set the maximum number of 'Pass 2' scans",
	"Symbols:",
	"  -a   All user symbols made global",
	"  -g   Undefined symbols made global",
	"  -z   Disable case sensitivity for symbols",
#if NOICE | SDCDB
	"Debugging:",
#if NOICE
	"  -j   Enable NoICE Debug Symbols",
#endif
#if SDCDB
	"  -y   Enable SDCC  Debug Symbols",
#endif
#endif
	"",
	NULL
};

/*
 *      The unlisted -t option is a diagnostic which
 *	prints the maximum include file and macro nesting
 *	during the assembly process and also prints the
 *	'hunk' allocations required during the assembly.
 */

/*)Function	void	usage(void)
 *
 *	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:
 *		int	fprintf()	c_library
 *
 *	side effects:
 *		none
 */

void
usage(void)
{
	char   **dp;

	fprintf(stdout, "\nASxxxx Assembler %s  (%s)", VERSION, cpu);
	fprintf(stdout, "\nCopyright (C) %s  Alan R. Baldwin", COPYRIGHT);
	fprintf(stdout, "\nThis program comes with ABSOLUTELY NO WARRANTY.\n\n");
	for (dp = usetxt; *dp; dp++) {
		fprintf(stdout, "%s\n", *dp);
	}
}

