/*------------------------------------------------------------------*/
/*								    */
/*			MC68000 Cross Assembler			    */
/*								    */
/*		Copyright (c) 1985 by Brian R. Anderson		    */
/*								    */
/*                  Main program - January 11, 1990		    */
/*								    */
/*   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 1990 by Charlie Gibbs.	    */
/*								    */
/*------------------------------------------------------------------*/

char Version[] = "2.61 (January 11, 1990)";


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

int getfilename(), checkswitch(), checkdupfile();


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, **fsp;
    struct FwdBr *fp1, *fp2, *fp3;

    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';			/* We 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;

    for (i = 1; i < argc; i++) {	/* Analyze the 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;
		strcpy (DumpSymList, &argv[i][2]);	/* Selections */
		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 'N':			/* Suppress optimization. */
		NoOpt = TRUE;
		cmderror |= checkswitch (argv[i][2], "optimization suppress");
		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)) < 0) {
	    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, "68000 Assembler - version %s\n", Version);
	fprintf (stderr, "Copyright 1985 by Brian R. Anderson\n");
	fprintf (stderr, "AmigaDOS conversion copyright 1989 by Charlie Gibbs.\n\n");
	fprintf (stderr, "Usage: a68k <source file>\n");
	fprintf (stderr, "            [-d[[!]<prefix>]]\n");
	fprintf (stderr, "            [-e[<equate file>]]\n");
	fprintf (stderr, "            [-f]\n");
	fprintf (stderr, "            [-h<header file>]\n");
	fprintf (stderr, "            [-i<include dirlist>]\n");
	fprintf (stderr, "            [-k]\n");
	fprintf (stderr, "            [-l[<listing file>]]\n");
	fprintf (stderr, "            [-n]\n");
	fprintf (stderr, "            [-o<object file>]\n");
	fprintf (stderr, "            [-p<page depth>]\n");
	fprintf (stderr, "            [-q[<quiet interval>]]\n");
	fprintf (stderr, "            [-s] [-t]\n");
	fprintf (stderr, "            [-w[<hash size>][,<heap size>]]\n");
	fprintf (stderr, "            [-x] [-y]\n");
	fprintf (stderr, "            [-z[<debug start>][,<debug end>]]\n\n");
	fprintf (stderr, "Heap size default:  -w");
	fprintf (stderr, "%ld,%ld\n", (long) DEFHASH, (long) DEFHEAP2);
	SrecFN[0] = '\0';	/* Don't scratch the object file! */
	quit_cleanup ("\n");
    }

    if (Quiet != 0) {
	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");
	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;

/* Allocate the forward branch optimization log. */

    if (NoOpt) {
	FwdStart = NULL;
    } else {
	FwdStart = (struct FwdTable *) malloc ((unsigned) FWDSIZE);
	if (FwdStart == NULL)
	    quit_cleanup ("Out of memory!\n");
	FwdCurr = FwdStart;	/* Make the first chunk current. */
	FwdCurr->Link = NULL;	/* Clear forward pointer. */
	FwdLim2 = (int *) ((char *) FwdCurr + sizeof (struct FwdTable *));
	FwdPtr = FwdLim2;	/* Current position in pass 2 */
    }

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

    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;
    FwdLim1     = FwdBranch;	/* Forward branch controls */
    FwdFixLimit = FwdBranchFix;

    /* 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 the 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 */
	    }
	    if (!NoOpt && (Dir != Equ)) {	/* Forward optimization */
		PackFwdBranch (dummy);		/* Drop expired entries. */
		fp1 = FwdBranch;
		while (fp1 < FwdLim1) {		/* Scan forward branches. */
		    if (fp1->FwdSym == Sym) {	/* It branched here. */
			if (fp1->Loc != (AddrCnt-4)) {	/* Don't make zero displacement! */
			    if (fp1->Line < LineCount)
				Sym->Val -= 2;	/* Move the label back. */
			    AddrCnt -= 2;	/* Shorten the program. */
			    for (fsp = FwdBranchFix; fsp < FwdFixLimit; fsp++) {
				if (fp1->Loc<(*fsp)->Val) {	/* Adjust labels  */
				    (*fsp)->Val -= 2;		/*  within range. */
				}
			    }
			    if ((char *) FwdLim2 >= ((char *) FwdCurr + FWDSIZE)) {
				FwdCurr->Link =	/* Get a new chunk. */
				    (struct FwdTable *) malloc ((unsigned) FWDSIZE);
				if (FwdCurr->Link == NULL)
				    quit_cleanup ("Out of memory!\n");
				FwdCurr = FwdCurr->Link;
				FwdLim2 = (int *)
				    ((char *) FwdCurr + sizeof (struct FwdTable *));
			    }
			    *FwdLim2++ = fp1->Line; /* Flag this branch in pass 2. */
			}
			fp3 = fp2 = fp1;
			fp3++;
			while (fp3 < FwdLim1) {	/* Remove processed entry. */
			    fp2->Loc    = fp3->Loc - 2;	/* Locations shifted too! */
			    fp2->FwdSym = fp3->FwdSym;
			    fp2->Line   = fp3->Line;
			    fp2++;
			    fp3++;
			}
			FwdLim1--;	/* Decrement table limit pointer. */
			fp1--;		/* Offset increment below. */
		    }
		    fp1++;		/* Check the next entry. */
		}
		if (FwdLim1 > FwdBranch) {	/* Store labels within    */
		    *FwdFixLimit++ = Sym;	/*  range of fwd. branch. */
		}
	    }
	}
	AddrCnt	+= AddrAdv * DupFact;	/* Advance the 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 if (Quiet < 0)
	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 * DupFact;	/* Advance the 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 if (Quiet < 0)
	fprintf (stderr, "%d\n", InF->Line);
    fflush (stderr);			/* Make sure it gets out. */

    close (In.fd);		/* We're finished with the source file. */
    In.fd = NULL;
    free (In.Buf);
    In.Buf = NULL;

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

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

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

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

/* Display final error count. */

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

/* Display heap usage. */

    if (Quiet != 0)
	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);
    if (Quiet != 0)
	fprintf (stderr, ",%ld\n", templong);
    if (!SuppList) {
	sprintf (tempchar, ",%ld\n", templong);
	xputs (&List, tempchar);
    }

/* Display the total size of all section types. */

    if (Quiet != 0) {
	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);		/* We're finished with the 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 if (Quiet < 0) {
	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 = FALSE;
    SmallData = -1;
    TTLstring[0] = '\0';		/* Clear the title string. */
}



quit_cleanup (s) char *s;
/* Clean up and exit.  If "s" doesn't point to a null string, print the
    string as an error message, remove the partially-formed object
    file if it exists, and exit with an error code.			*/
{
    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. */
    }

    FwdCurr = FwdStart;
    while (FwdCurr != NULL) {
	FwdLim2 = (int *) FwdCurr;
	FwdCurr = FwdCurr->Link;
	free (FwdLim2);			/* Free the forward branch log. */
    }

    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 */
    }
}
