/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	       Copyright (c) 1985 by Brian R. Anderson		    */
/*								    */
/*		   Main	program	- January 10, 1989		    */
/*								    */
/*   This program may be copied	for personal, non-commercial use    */
/*   only, provided that the above copyright notice is included	    */
/*   on	all copies of the source code.	Copying	for any	other use   */
/*   without the consent of the	author is prohibited.		    */
/*								    */
/*------------------------------------------------------------------*/
/*								    */
/*		Originally published (in Modula-2) in		    */
/*	    Dr.	Dobb's Journal, April, May, and June 1986.          */
/*								    */
/*	 AmigaDOS conversion copyright 1989 by Charlie Gibbs.	    */
/*								    */
/*------------------------------------------------------------------*/

char Version[] = "2.42 (January 10, 1989)";


#include <stdio.h>
#define	PRIMARY
#include "a68kdef.h"
#include "a68kglb.h"


#ifdef MSDOS
/********************************************************************/
/*								    */
/*     NOTE: the following line, plus any additional references	    */
/*     to _iomode, is inserted to make this program work under	    */
/*     the MS-DOS version of Lattice C.	 It is not necessary	    */
/*     for the Amiga version, but does no harm if left in.	    */
/*								    */
/********************************************************************/
int _iomode = 0;	/* File	mode - 0x8000 for binary */
#endif



/* Functions */
extern int  LineParts(), Instructions(), ObjDir();
extern int  GetInstModeSize(), GetMultReg(), CountNest();
extern int  ReadSymTab(), GetArgs(), GetAReg(),	OpenIncl();
extern long AddrBndW(),	AddrBndL(), GetValue(),	CalcValue ();
extern char *AddName(),	*GetField();
extern struct SymTab *NextSym();
extern struct SymTab **HashIt();



main (argc,argv) int argc; char	*argv[];
{
    char ListFN[MAXFN],	EquateFN[MAXFN]; /* File names */
    int	 makeequ;			/* Make	an equate file */
    int	 keepobj;			/* Keep	object file with errors	*/
    int	 endfile;			/* End-of-file flag */
    long maxheap2;			/* Maximum secondary heap size */
    int	 cmderror, dummy;
    long codesize, datasize, bsssize;
    int	 *intptr;
    long templong;
    char tempchar[MAXLINE];
    register struct SymTab *sym;
    register int i, j;
    struct SymTab **hashptr;

    Hash = NULL;	/* Clear all memory pointers - */
    SymStart = NULL;	/*  we haven't allocated anything yet. */
    NameStart =	NULL;
    RelStart = NULL;
    Heap2 = NULL;
    SymSort = NULL;
    In.fd = Eq.fd = List.fd = Srec.fd =	NULL;	/* No files are	open yet */
    In.Buf = Eq.Buf = List.Buf = Srec.Buf = NULL;

    cmderror = FALSE;		/* Clear command-line error flag */
    InclErrs = FALSE;
    SourceFN[0]	= '\0';                 /* Don't have source name yet */
    HeaderFN[0]	= EquateFN[0] =	'\0';   /* No header or equate files yet */
    makeequ = FALSE;
    ListFN[0] =	SrecFN[0] = '\0';       /* Indicate default file names */
    InclList[0]	= '\0';         /* Clear the include directory list */
    IdntName[0]	= '\0';         /* Clear program unit name */
    LnMax = 60;
    Quiet = 10;			/* Show	progress every 10 lines	*/
    strcpy (MacSize, "W");      /* Macro call size (\0) */
    XrefList = DumpSym = GotEqur = KeepTabs = keepobj =	FALSE;
    SuppList = TRUE;		/* Default to no listing file */
    HashStats =	FALSE;		/* Default to no hashing statistics */
    HashSize = DEFHASH;		/* Hash	table size default */
    maxheap2 = DEFHEAP2;	/* Secondary heap size default */
    DebugStart = 32767;	DebugEnd = 0;	/* Disable debug displays */

    for	(i = 0;	i < 256; i++)
	OpPrec[i] = '\0';       /* Set up the operator precedence table */
    i =	(unsigned int) '('; OpPrec[i] = 1;
    i =	(unsigned int) ')'; OpPrec[i] = 2;
    i =	(unsigned int) '+'; OpPrec[i] = 3;
    i =	(unsigned int) '-'; OpPrec[i] = 3;
    i =	(unsigned int) '*'; OpPrec[i] = 4;
    i =	(unsigned int) '/'; OpPrec[i] = 4;
    i =	(unsigned int) '&'; OpPrec[i] = 5;
    i =	(unsigned int) '!'; OpPrec[i] = 5;
    i =	(unsigned int) '|'; OpPrec[i] = 5;
    i =	(unsigned int) '<'; OpPrec[i] = 6;
    i =	(unsigned int) '>'; OpPrec[i] = 6;

    printf ("68000 Assembler - version %s\n", Version);
    printf ("Copyright 1985 by Brian R. Anderson\n");
    printf ("AmigaDOS conversion copyright 1989 by Charlie Gibbs.\n\n");

    for	(i = 1;	i < argc; i++) {	/* Analyze command line	*/
	if (argv[i][0] != '-') {
	    if (SourceFN[0] == '\0')
		strcpy (SourceFN, argv[i]);	/* Source file name */
	    else if (SrecFN[0] == '\0')
		strcpy (SrecFN,	argv[i]);	/* Object file name */
	    else if (ListFN[0] == '\0')
		strcpy (ListFN,	argv[i]);	/* Listing file	name */
	    else {
		fprintf	(stderr, "Too many file names.\n");
		cmderror = TRUE;
	    }
	} else {
	    switch (toupper(argv[i][1])) {
	    case 'D':                   /* Dump the symbol table */
		DumpSym	 = TRUE;
		cmderror |= checkswitch	(argv[i][2], "symbol table dump");
		break;
	    case 'E':                   /* Equate file name */
		makeequ	= TRUE;
		if (getfilename	(EquateFN, &argv[i][2],	"Equate", FALSE))
		    cmderror = keepobj = TRUE;
		break;
	    case 'F':                   /* Dump the symbol table */
		FwdProc	 = TRUE;
		cmderror |= checkswitch	(argv[i][2], "forward reference");
		break;
	    case 'H':                   /* Header file name */
		if (getfilename	(HeaderFN, &argv[i][2],	"Header", TRUE))
		    cmderror = keepobj = TRUE;
		break;
	    case 'I':                   /* Include directories */
		if (argv[i][2])	{
		    if (InclList[0])
			strcat (InclList, ","); /* Add to previous list */
		    strcat (InclList, &argv[i][2]);
		} else {
		    fprintf (stderr, "Include directory list is missing.\n");
		    cmderror = keepobj = TRUE;
		}
		break;
	    case 'K':                   /* Keep object code file */
		keepobj	 = TRUE;
		cmderror |= checkswitch	(argv[i][2], "object file keep");
		break;
	    case 'X':                   /* Cross-reference listing */
		XrefList = TRUE;	/* Falls through to case 'L': */
	    case 'L':                   /* Produce a listing file */
		SuppList = FALSE;
		if (getfilename	(ListFN, &argv[i][2], "List", FALSE))
		    cmderror = keepobj = TRUE;
		break;
	    case 'O':                   /* Object file name */
		if (getfilename	(SrecFN, &argv[i][2], "Object", TRUE))
		    cmderror = keepobj = TRUE;
		break;
	    case 'P':                   /* Page depth */
		if (argv[i][2] == '\0') {
		    fprintf (stderr, "Page depth is missing.\n");
		    cmderror = keepobj = TRUE;
		    break;
		}
		if ((LnMax = CalcValue (&argv[i][2], 0)) < 10) {
		    fprintf (stderr, "Page depth is invalid.\n");
		    cmderror = TRUE;
		}
		break;
	    case 'Q':                   /* Quiet console display */
		Quiet =	CalcValue (&argv[i][2],	0);
		break;
	    case 'S':                   /* Motorola S-format */
		SFormat	= TRUE;
		cmderror |= checkswitch	(argv[i][2], "S-format");
		break;
	    case 'T':                   /* Keep tabs in listing */
		KeepTabs = TRUE;
		cmderror |= checkswitch	(argv[i][2], "tab");
		break;
	    case 'W':                   /* Work storage size(s) */
		if (argv[i][2] == '\0') {
		    fprintf (stderr, "Work storage size is missing.\n");
		    cmderror = keepobj = TRUE;
		    break;
		}
		if (argv[i][2] != ',') {
		    GetField (argv[i]+2, tempchar);
		    HashSize = CalcValue (tempchar, 0);
		    if (HashSize >= 16384) {
			fprintf	(stderr, "Hash table size is too big.\n");
			cmderror = TRUE;
		    }
		}
		for (j = 2; argv[i][j];	j++) {
		    if (argv[i][j] == ',') {    /* Find secondary size */
			maxheap2 = CalcValue (&argv[i][j+1], 0);
			if (maxheap2 < MAXLINE)
			    maxheap2 = MAXLINE;
			maxheap2 &= ~3L;
			break;
		    }
		}
		break;
	    case 'Y':                   /* Display hashing statistics */
		HashStats = TRUE;
		cmderror |= checkswitch	(argv[i][2], "hash statistics");
		break;
	    case 'Z':                   /* Debug option */
		DebugStart = 0;
		DebugEnd = 32767;
		if (argv[i][2] != ',') {        /* Debug dump starts here */
		    GetField (argv[i]+2, tempchar);
		    DebugStart = CalcValue (tempchar, 0);
		}
		for (j = 2; argv[i][j];	j++) {
		    if (argv[i][j] == ',') {    /* Debug dump ends here */
			DebugEnd = CalcValue (&argv[i][j+1], 0);
			if (DebugEnd ==	0)
			    DebugEnd = 32767;
		    }
		}
		break;
	    default:
		fprintf	(stderr, "Unrecognized switch: %c\n", argv[i][1]);
		cmderror = TRUE;
		break;
	    }
	}
    }

    if (makeequ)
	defaultfile (EquateFN, ".equ"); /* Default equate file name */
    if (!SuppList)
	defaultfile (ListFN, ".lst");   /* Default list file name */
    else			/* If there's no listing, don't bother */
	KeepTabs = TRUE;	/*  expanding tabs - it's faster.      */
    if (SFormat)
	defaultfile (SrecFN, ".s");     /* Default S-format file name */
    else
	defaultfile (SrecFN, ".o");     /* Default object file name */

/* Check for duplicate file names. */

    if (SourceFN[0]) {
	cmderror |= checkdupfile (SourceFN, "Source", EquateFN, "equate");
	cmderror |= checkdupfile (SourceFN, "Source", ListFN, "listing");
	cmderror |= checkdupfile (SourceFN, "Source", SrecFN, "object");
    } else {
	fprintf	(stderr, "Source file name is missing.\n");
	cmderror = TRUE;
    }
    if (EquateFN[0]) {
	cmderror |= checkdupfile (EquateFN, "Equate", ListFN, "listing");
	cmderror |= checkdupfile (EquateFN, "Equate", SrecFN, "object");
    }
    if (ListFN[0]) {
	cmderror |= checkdupfile (ListFN, "Listing", SrecFN, "object");
    }

/*	Open files.	*/

    if (!cmderror) {				/* Source file */
	if ((In.Buf = (char *) malloc (BUFFSIZE)) == NULL)
	    quit_cleanup ("Out of memory!\n");
#ifdef MSDOS
	_iomode	= 0x8000;
#endif
	if ((In.fd = open (SourceFN, 0)) == -1)	{
	    fprintf (stderr, "Unable to open source file.\n");
	    In.fd = NULL;
	    cmderror = TRUE;
	}
	In.Ptr = In.Lim	= In.Buf;
    }
#ifdef MSDOS
    _iomode = 0;
#endif
    if (!cmderror && EquateFN[0])		/* Equate file */
	cmderror |= xopen (EquateFN, &Eq, "equate");

    if (!cmderror && !SuppList)			/* Listing file	*/
	cmderror |= xopen (ListFN, &List, "listing");

#ifdef MSDOS
    if (!SFormat)
	_iomode	= 0x8000;
#endif
    if (!cmderror)				/* Object code file */
	cmderror |= xopen (SrecFN, &Srec, "object code");
#ifdef MSDOS
    _iomode = 0x8000;
#endif

    if (cmderror) {
	fprintf	(stderr, "\n");
	fprintf	(stderr, "Usage: a68k <source file>\n");
	fprintf	(stderr, "            [-e<equate file>]\n");
	fprintf	(stderr, "            [-h<header file>]\n");
	fprintf	(stderr, "            [-i<include dirlist>]\n");
	fprintf	(stderr, "            [-l<listing file>]\n");
	fprintf	(stderr, "            [-o<object file>]\n");
	fprintf	(stderr, "            [-p<page depth>]\n");
	fprintf	(stderr, "            [-q[<quiet interval>]]\n");
	fprintf	(stderr, "            [-w[<hash size>][,<heap size>]]\n");
	fprintf	(stderr, "            [-z[<debug start>][,<debug end>]]\n");
	fprintf	(stderr, "            [-d] [-f] [-k] [-s] [-t] [-x] [-y]\n\n");
	fprintf	(stderr, "Command-line arguments can appear in any order.\n");
	fprintf	(stderr, "Heap size default:  -w");
	fprintf	(stderr, "%ld,%ld\n", (long) DEFHASH, (long) DEFHEAP2);
	if (keepobj)
	    SrecFN[0] =	'\0';   /* Don't scratch object file! */
	quit_cleanup ("\n");
    }

    printf ("Assembling %s\n\n", SourceFN);

/* Allocate initial symbol table chunks. */

    templong = sizeof (struct SymTab *)	* HashSize;
    Hash = (struct SymTab **) malloc ((unsigned) templong);
    if (Hash ==	NULL)
	quit_cleanup ("Out of memory!\n");
    for	(hashptr = Hash, i = 0;	i < HashSize; hashptr++, i++)
	*hashptr = NULL;	/* Clear the hash table	*/

    SymStart = (struct SymTab *) malloc	((unsigned) CHUNKSIZE);
    if (SymStart == NULL)
	quit_cleanup ("Out of memory!\n");
    SymCurr = SymStart;			/* Make	the first chunk	current	*/
    SymCurr->Link = NULL;		/* Clear forward pointer */
    SymLim = SymCurr;
    SymLim++;				/* Start of names */

    NameStart =	(struct	NameChunk *) malloc ((unsigned)	CHUNKSIZE);
    if (NameStart == NULL)
	quit_cleanup ("Out of memory!\n");
    NameCurr = NameStart;		/* Make	the first chunk	current	*/
    NameCurr->Link = NULL;		/* Clear forward pointer */
    NameLim = (char *) NameCurr	+ sizeof (char *);  /* Start of	names */

/* Allocate the	relocation attribute table. */

    RelStart = (struct RelTab *) malloc	((unsigned) CHUNKSIZE);
    if (RelStart == NULL)
	quit_cleanup ("Out of memory!\n");
    RelCurr = RelStart;			/* Relocation table */
    RelCurr->Link = NULL;		/* No additional chunks	*/
    RelLast = NULL;			/* There are no	entries	yet */
    RelLim = RelStart;
    RelLim++;				/* First unused	space */

/* Allocate the	secondary heap (input files and	parser stack). */

    Heap2 = malloc ((unsigned) maxheap2);
    if (Heap2 == NULL)
	quit_cleanup ("Out of memory!\n");

/* Allocate the	INCLUDE	skip table. */

    SkipLim = (struct SkipEnt *) malloc	((unsigned) INCSKSIZ);
    if (SkipLim	== NULL)
	quit_cleanup ("Out of memory!\n");
    SkipIdx = SkipLim;
    SetFixLim =	(struct	SetFixup *) ((char *) SkipLim +	INCSKSIZ);
    IncStart = 0;

/*-------------------------------------------------------------------

    Begin Pass 1.
								   */
    Pass2 = FALSE;
    startpass ('1', maxheap2);
    NumSyms = 0;	/* There's nothing in the symbol table yet */
    NextHunk = 0L;	/* Start in hunk zero */
    LowInF = InF;	/* Initialize secondary	heap usage pointers */
    High2 = NextFNS;
    Low2  = (char *) LowInF;

    /* Define ".A68K" as a SET symbol with an absolute value of 1.
	This allows programs to	identify this assembler.	*/
    AddSymTab (".A68K", 1L, (long) ABSHUNK, 0, 4);  /* All spellings */
    AddSymTab (".A68k", 1L, (long) ABSHUNK, 0, 4);
    AddSymTab (".a68K", 1L, (long) ABSHUNK, 0, 4);
    AddSymTab (".a68k", 1L, (long) ABSHUNK, 0, 4);

    endfile = FALSE;
    Dir	= None;
    while (!endfile && (Dir != End)) {
	PrevDir	= Dir;			/* Save	previous directive */
	endfile	= LineParts (dummy);	/* Get a statement */
	GetObjectCode (dummy);		/* Process the statement */

	if (IncStart !=	0) {
	    if ((OpCode[0] != '\0') && (Dir < SkipDir)) {
		IncStart = 0;			/* We can't      */
		if (SkipLim->Set1 != NULL) {	/*  skip this	 */
		    SetFixLim =	SkipLim->Set1;	/*  INCLUDE file */
		    SetFixLim++;		/*  in pass 2.	 */
		}
	    }
	}
	if ((HunkType == HunkNone) && (AddrAdv != 0)) {
	    DoSection ("", 0, "", 0, "", 0);    /* Start unnamed CODE section */
	    MakeHunk = TRUE;
	}
	if ((Label[0] != '\0')                  /* If statement is labeled */
	&& (Dir	!= Set)	&& (Dir	!= Equr) && (Dir != Reg)) {
	    if (!ReadSymTab (Label)) {		/* Make	a new entry */
		AddSymTab (Label, AddrCnt, CurrHunk, LineCount,	0);
	    } else if ((Sym->Flags & 1)		/* If dup., ignore */
	    || (Sym->Defn == NODEF)) {		/* else	fill in	*/
		Sym->Val = AddrCnt;		/* Current loc.	*/
		Sym->Hunk = CurrHunk;		/* Hunk	number */
		Sym->Defn = LineCount;		/* Statement number */
		Sym->Flags &= ~1;		/* Clear XREF flag */
		if (Sym->Flags & 0x80) {	/* If it's PUBLIC, */
		    Sym->Flags |= 2;		/*  make it XDEF */
		}
	    }
	    if (Dir == Equ) {
		Sym->Val = ObjSrc;		/* Equated value */
		Sym->Hunk = Src.Hunk;		/* Hunk	number */
	    }
	}
	AddrCnt	+= AddrAdv;	/* Advance location counter */
    }
    if ((HunkType == HunkNone) && (NumSyms != 0)) { /* Dummy section   */
	DoSection ("", 0, "", 0, "", 0);            /*  to get XDEF    */
	MakeHunk = TRUE;			    /*	symbols	if any */
    }
    if (HunkType != HunkNone)
	if (AddrCnt > OrgHigh)
	    Sect->Val =	AddrCnt;	/* End of the last section */
	else
	    Sect->Val =	OrgHigh;	/* We've ORGed higher */

    if (InclErrs)
	quit_cleanup ("Fatal errors - assembly aborted\n");

    if (Quiet >= 0)
	fprintf	(stderr, "%d\n", LineCount);
    else
	fprintf	(stderr, "%d\n\n", InF->Line);



/*----------------------------------------------------------------

	Begin Pass 2.
							       */
    Pass2 = TRUE;
    lseek (In.fd, 0L, 0);		/* "Rewind" the source file */
    In.Ptr = In.Lim = In.Buf;
    startpass ('2', maxheap2);
    RefLim = (struct Ref *) SymLim;	/* Cross-reference table */

/* Calculate the total size of each section type,
    reset all section pointers to the beginning, and
    write all absolute symbols to an equate file if desired. */

    codesize = datasize	= bsssize = 0;
    if (EquateFN[0]) {
	xputs (&Eq, "* Equate file for ");
	xputs (&Eq, SourceFN);
	xputs (&Eq, "\n* Created by");
	xputs (&Eq, " A68k version ");
	xputs (&Eq, Version);
	xputs (&Eq, "\n");
    }
    Sym	= SymChunk = SymStart;
    Sym++;
    SymChLim = (struct SymTab *) ((char	*) SymChunk + CHUNKSIZE);
    while (Sym)	{
	if (Sym->Flags & 0x10) {
	    templong = (Sym->Val + 3) &	~3L;		/* Hunk	size */
	    j =	(Sym->Hunk & 0x3FFF0000L) >> 16;	/* Hunk	type */
	    if (j == HunkCode)		    /* Accumulate sizes	by type	*/
		codesize += templong;
	    else if (j == HunkData)
		datasize += templong;
	    else
		bsssize	+= templong;
	    Sym->Val = 0L;	/* Back	to start of all	sections */
	}
	if (EquateFN[0]) {
	    if (((Sym->Hunk & 0x00007FFFL) == ABSHUNK)
	    && ((Sym->Flags == 0) || (Sym->Flags == 2))) {
		xputs (&Eq, Sym->Nam);
		xputs (&Eq, "\tEQU\t$");
		LongPut	(&Eq, Sym->Val,	4);
		xputs (&Eq, "\n");
	    }
	}
	Sym = NextSym (Sym);	/* Try for another symbol table	entry */
    }
    if (EquateFN[0])
	xclose (&Eq);

/* Write sign-on messages for listing file */

    LnCnt = LnMax;
    PgCnt = 0;
    if (!SuppList) {
	CheckPage (&List, FALSE);		/* Print headings */
	xputs (&List, "68000 Assembler - version ");
	xputs (&List, Version);
	xputs (&List, "\nCopyright 1985 by Brian R. Anderson.\n");
	xputs (&List, "AmigaDOS conversion copyright 1989");
	xputs (&List, " by Charlie Gibbs.\n\n");
	LnCnt += 4;
    }

    StartSrec (&Srec, IdntName);	/* Write object	header record */

/*	Process	the second pass.	*/

    endfile = FALSE;
    Dir	= None;
    while (!endfile && (Dir != End)) {
	PrevDir	= Dir;			/* Save	previous directive */
	endfile	= LineParts (dummy);	/* Get a statement */
	if (!endfile) {
	    GetObjectCode (dummy);	/* Process the statement */
	    if (Label[0] != '\0') {     /* If statement is labeled, */
		ReadSymTab (Label);	/*  check for duplicate	defn. */
		if (Sym->Defn != LineCount) {
		    AddRef (LineCount);	/* Got one - flag as reference */
		    if (Dir == Set) {
			if ((Sym->Flags	& 4) ==	0)
			    Error (LabLoc, SymDup); /* Can't SET normal label */
		    } else {
			Error (LabLoc, SymDup);	/* Ordinary duplicate */
		    }
		} else if (Dir == Set) {
		    AddRef (LineCount);	/* Flag	all SETs as references */
		} else {
		    if (Sym->Val != AddrCnt)
			if ((Dir != Equ) && (Dir != Equr) && (Dir != Reg))
			    Error (0, Phase);	/* Assembler error */
		}
	    }
	    WriteListLine (&List);
	    WriteSrecLine (&Srec);
	    AddrCnt += AddrAdv;		/* Advance location counter */
	} else {
	    Error (0, EndErr);		/* END statement is missing */
	    WriteListLine (&List);
	}
    }
    if ((HunkType == HunkNone) && (NumSyms != 0)) { /* Dummy section   */
	DoSection ("", 0, "", 0, "", 0);            /*  to get XDEF    */
	MakeHunk = TRUE;			    /*	symbols	if any */
    }

/*---------------------------------------------------------------------

	Clean up.
								*/

    if (HunkType != HunkNone)
	if (AddrCnt > OrgHigh)
	    Sect->Val =	AddrCnt;	/* End of the last section */
	else
	    Sect->Val =	OrgHigh;	/* We've ORGed higher */

    if (Quiet >= 0)
	fprintf	(stderr, "%d", LineCount);      /* Final line number */
    else
	fprintf	(stderr, "%d\n", InF->Line);
    fflush (stderr);			/* Make	sure it	gets out */

    close (In.fd);		/* Finished with source	file */
    In.fd = NULL;
    free (In.Buf);
    In.Buf = NULL;

    EndSdata (&Srec, EndAddr);	/* Write remaining data	and end	record */
    xclose (&Srec);		/* Finished with object	file */
    if ((ErrorCount != 0) && (!keepobj))
	unlink (SrecFN);	/* Scratch it if there were errors */

    RelCurr = RelStart;
    while (RelCurr != NULL) {
	RelLim = RelCurr;
	RelCurr	= RelCurr->Link;
	free (RelLim);		/* Free	the relocation table */
	RelCurr	= NULL;
    }

    if (Heap2 != NULL) {
	free (Heap2);		/* Free	the secondary heap */
	Heap2 =	NULL;
    }

    if (XrefList)
	WriteSymTab (&List);	/* List	the symbol table */

/* Display final error count. */
    fprintf (stderr, "\nEnd of assembly - ");
    if (!SuppList)
	xputs (&List, "\nEnd of assembly - ");
    if (ErrorCount == 0) {
	fprintf	(stderr, "no errors were found.\n");
	if (!SuppList)
	    xputs (&List, "no errors were found.\n");
    } else if (ErrorCount == 1)	{
	fprintf	(stderr, "1 error was found.\n");
	if (!SuppList)
	    xputs (&List, "1 error was found.\n");
    } else {
	fprintf	(stderr, "%d errors were found.\n", ErrorCount);
	if (!SuppList) {
	    sprintf (tempchar, "%d errors were found.\n", ErrorCount);
	    xputs (&List, tempchar);
	}
    }

/* Display heap	usage. */
    fprintf (stderr, "Heap usage:  -w%ld", HashSize);
    if (!SuppList) {
	sprintf	(tempchar, "Heap usage:  -w%ld", HashSize);
	xputs (&List, tempchar);
    }
    templong = (long) (High2 - Heap2);
    if (Low2 < (char *)	LowInF)
	templong += (long) (Heap2 + maxheap2 - Low2);
    else
	templong += (long) (Heap2 + maxheap2 - (char *)	LowInF);
    fprintf (stderr, ",%ld\n", templong);
    if (!SuppList) {
	sprintf	(tempchar, ",%ld\n", templong);
	xputs (&List, tempchar);
    }

/* Display the total size of all section types.	*/
    fprintf (stderr, "Total hunk sizes:  %lx code, ", codesize);
    fprintf (stderr, "%lx data, %lx BSS\n", datasize, bsssize);
    if (!SuppList) {
	sprintf	(tempchar, "Total hunk sizes:  %lx code, ", codesize);
	xputs (&List, tempchar);
	sprintf	(tempchar, "%lx data, %lx BSS\n", datasize, bsssize);
	xputs (&List, tempchar);
    }

/* Display hashing statistics if required. */
    if (HashStats && (NumSyms != 0)) {
	printf ("\n");
	printf ("HASH CHAIN STATISTICS - %d symbols\n\n", NumSyms);
	templong = (NumSyms + 1) * sizeof (int);
	HashCount = (int *) malloc ((unsigned) templong);
	if (HashCount == NULL)
	    quit_cleanup ("Out of memory!\n");

	printf ("Length     No. of chains\n");
	printf ("------     -------------\n");
	intptr = HashCount;
	for (i = 0; i <= NumSyms; i++)
	    *(intptr++)	= 0;	/* Clear hash chain length counters */

	hashptr	= Hash;
	for (i = 0; i <	HashSize; i++) {
	    j =	0;
	    if ((Sym = *hashptr) != NULL) {
		j++;		/* This	chain has at least one entry */
		while ((Sym = Sym->Link) != NULL) {
		    j++;	/* Count entries in the	chain */
		}
	    }
	    intptr = HashCount + j;
	    (*intptr)++;	/* Bump	counter	by chain length	*/
	    hashptr++;
	}
	intptr = HashCount;
	for (i = 0; i <= NumSyms; i++) {
	    if (*intptr)
		printf ("%4d          %4d\n", i, *intptr);
	    intptr++;
	}
	free (HashCount);		/* Free	hash statistics	table */
	HashCount = NULL;
    }

/* All done! */
    if (!SuppList) {
	xputs (&List, "\f");    /* One last page eject */
	xclose (&List);		/* Finished with listing file */
    }
    quit_cleanup ("");          /* Normal termination */
}



/*======================================================================*/
/*									*/
/*		Subroutines used by the	main program			*/
/*									*/
/*======================================================================*/



int getfilename	(name, arg, desc, needit)
char *name, *arg, *desc;
int needit;
/* If "name" is not a duplicate, copies "arg" to it, else flags
    duplicate using "desc".  If "needit" is TRUE, also flags
    an error if	"arg" is a null string.
    Returns TRUE if an error is	found, FALSE otherwise.	*/
{
    if (*name) {
	fprintf	(stderr, "%s file is declared more than once.\n", desc);
	return (TRUE);
    }
    if (*arg) {
	strcpy (name, arg);
	return (FALSE);
    }
    if (needit)	{
	fprintf	(stderr, "%s file name is missing\n", desc);
	return (TRUE);
    }
    return (FALSE);
}



int checkswitch	(c, name) char c, *name;
/* Displays an error message and returns TRUE if "c" isn't a NULL.
    Just returns FALSE otherwise.				*/
{
    if (c) {
	fprintf	(stderr, "Invalid %s switch.\n", name);
	return (TRUE);
    } else {
	return (FALSE);
    }
}



defaultfile (name, ext)	char *name, *ext;
/* If "name" is a null string, search for the last period in "name"
    (if	any) and append	"ext".
    If "name" doesn't contain a period, append a period and "ext". */
{
    char *s;

    if (*name == '\0') {        /* If name isn't specified... */
	strcpy (name,SourceFN);	/* Start with source file name */
	s = name+strlen(name);	/* Scan	backwards for period */
	while (--s > name) {
	    if (*s == '.') {
		*s = '\0';      /* Chop off name extension */
		break;
	    }
	}
	strcat (name, ext);	/* Add name extension */
    }
}



int checkdupfile (name1, desc1,	name2, desc2)
char *name1, *desc1, *name2, *desc2;
/* If "name1" is the same as "name2", display an error message using
    "desc1" and "desc2" and return TRUE.  Otherwise, return FALSE. */
{
    if (strcmp (name1, name2) == 0) {
	fprintf	(stderr,
	    "%s and %s file names are the same.\n", desc1, desc2);
	return (TRUE);
    } else {
	return (FALSE);
    }
}



startpass (pchar, maxheap2) char pchar;	long maxheap2;
/* Set up to start the next pass. */
{
    int	dummy;

    if (Quiet >= 0) {
	fprintf	(stderr, "PASS %c line ", pchar);
	fflush (stderr);
    } else {
	fprintf	(stderr, "PASS %c\n", pchar);
    }
    NextFNS = Heap2;
    InF	= (struct InFCtl *) (Heap2 + maxheap2);
    InF--;
    InFNum = OuterMac =	SkipNest = InF->Pos = InF->MCnt	= 0;
    InF->Line =	0;
    InF->UPtr =	0;
    InF->NPtr =	NextFNS;
    InF->NArg =	-1;
    InF->MCnt =	0;
    strcpy (NextFNS, SourceFN);
    ShowFile (FALSE);			/* Show	source file name */
    NextFNS += strlen (SourceFN) + 1;
    LineCount =	LabLine	= MacCount = ErrorCount	= 0;
    AddrCnt = CurrHunk = SectStart = EndAddr = 0L;
    HunkType = HunkNone;		/* We're not in a hunk yet */
    HunkFlags =	SectLine = HunkSeq = 0;
    ListOff = MakeHunk = InnrFMac = SmallData =	FALSE;
    TTLstring[0] = '\0';                /* Clear the title string */
}



quit_cleanup (s) char *s;
/* Clean up and	exit. */
{
    if (In.fd != NULL)			/* Close all files */
	close (In.fd);
    if (In.Buf != NULL)			/*  and	free buffers */
	free (In.Buf);
    if (Srec.fd	!= NULL)
	xclose (&Srec);
    if (List.fd	!= NULL)
	xclose (&List);
    if (Eq.fd != NULL)
	xclose (&Eq);

    if (Hash !=	NULL)
	free (Hash);			/* Free	the hash table */

    SymCurr = SymStart;
    while (SymCurr != NULL) {
	SymLim = SymCurr;
	SymCurr	= SymCurr->Link;
	free (SymLim);			/* Free	the symbol table */
    }

    NameCurr = NameStart;
    while (NameCurr != NULL) {
	NameLim	= (char	*) NameCurr;
	NameCurr = NameCurr->Link;
	free (NameLim);			/* Free	the name table */
    }

    RelCurr = RelStart;
    while (RelCurr != NULL) {
	RelLim = RelCurr;
	RelCurr	= RelCurr->Link;
	free (RelLim);			/* Free	the relocation table */
    }

    if (Heap2 != NULL)
	free (Heap2);			/* Free	the secondary heap */

    if (SymSort	!= NULL)
	free (SymSort);			/* Free	symbol table sort area */

    if (HashCount != NULL)
	free (HashCount);		/* Free	hash statistics	table */

    if (*s) {				/* If we have an error message,	*/
	if (SrecFN[0])
	    unlink (SrecFN);		/*  scratch the	object file,	*/
	fprintf	(stderr, "%s", s);      /*  display the error message,  */
	exit (20);			/*  and	die. */
    } else {
	exit (ErrorCount ? 10 :	0);	/* Normal termination */
    }
}
