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

char Version[] = "1.02 (September 9, 1987)";


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


/********************************************************************/
/*								    */
/*     NOTE: the following line, plus any additional references	    */
/*     to _fmode, 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 _fmode = 0;		/* File	mode - 0x8000 for binary */



/* Functions */
extern int  LineParts(), GetField(), Instructions(), ObjDir();
extern int  GetSize(), GetInstModeSize(), GetMultReg();
extern int  ReadSymTab(), GetArgs(), GetAReg(),	OpenIncl();
extern long AddrBndW(),	AddrBndL(), GetValue(),	CalcValue();
#ifdef AZTEC_C
extern char *lmalloc();
#else
extern char *malloc();
#endif
extern FILE *fopen();



main(argc,argv)	int argc; char *argv[];
{
    char ListFN[MAXFN],	SrecFN[MAXFN], EquateFN[MAXFN];	/* File	names */
    int	 MakeEqu;			/* Create an equate file */
    FILE *EquFile;			/* Equate file pointer */
    int	 endfile;			/* End-of-file flag */
    long maxheap, maxheap2;		/* Maximum heap	sizes */
    int	 cmderror, dummy;
    long templong;
    register struct SymTab *sym;
    register int i, j;

    cmderror = FALSE;		/* Clear command-line error flag */
    SourceFN[0]	= '\0';                 /* Don't have source name yet */
    HeaderFN[0]	= EquateFN[0] =	'\0';   /* No header or equate files yet */
    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;
    XrefList = DumpSym = GotEqur = MakeEqu = FALSE;
    SuppList = TRUE;		/* Default to no listing file */
    maxheap  = DEFHEAP;		/* Primary heap	size default */
    maxheap2 = DEFHEAP2;	/* Secondary heap size default */

    printf ("\n68000 Cross Assembler\n");
    printf ("Copyright (c) 1985 by Brian R. Anderson\n\n");
    printf ("AmigaDOS conversion copyright (c) 1987 by Charlie Gibbs.\n");
    printf ("Version %s\n\n", Version);

    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 {
		printf ("Too many file names.\n");
		cmderror = TRUE;
	    }
	} else {
	    switch (argv[i][1])	{
	    case 'D':
	    case 'd':
		DumpSym	 = TRUE;
		if (argv[i][2])	{
		    printf ("Invalid symbol table dump switch.\n");
		    cmderror = TRUE;
		}
		break;
	    case 'E':                           /* Equate file name */
	    case 'e':
		MakeEqu	= TRUE;
		if (EquateFN[0]) {
		    printf ("Equate file is declared more than once.\n");
		    cmderror = TRUE;
		} else if (argv[i][2])
		    strcpy (EquateFN, &argv[i][2]);
		break;
	    case 'H':                           /* Header file name */
	    case 'h':
		if (HeaderFN[0]) {
		    printf ("Header file is declared more than once.\n");
		    cmderror = TRUE;
		} else if (argv[i][2]) {
		    strcpy (HeaderFN, &argv[i][2]);
		} else {
		    printf ("Header file name is missing\n");
		    cmderror = TRUE;
		}
		break;
	    case 'I':                           /* Include directories */
	    case 'i':
		if (argv[i][2])	{
		    if (InclList[0])
			strcat (InclList, ","); /* Add to previous list */
		    strcat (InclList, &argv[i][2]);
		} else {
		    printf ("Include directory list is missing.\n");
		    cmderror = TRUE;
		}
		break;
	    case 'L':                           /* Listing file name */
	    case 'l':
		SuppList = FALSE;		/* Produce a listing */
		if (ListFN[0]) {
		    printf ("List file is declared more than once.\n");
		    cmderror = TRUE;
		} else if (argv[i][2])
		    strcpy (ListFN, &argv[i][2]);
		break;
	    case 'O':                           /* Object file name */
	    case 'o':
		if (SrecFN[0]) {
		    printf ("Object file is declared more than once.\n");
		    cmderror = TRUE;
		} else if (argv[i][2]) {
		    strcpy (SrecFN, &argv[i][2]);
		} else {
		    printf ("Object file name is missing\n");
		    cmderror = TRUE;
		}
		break;
	    case 'P':
	    case 'p':
		if ((LnMax = CalcValue(&argv[i][2], 0))	< 10) {
		    printf ("Invalid page depth.\n");
		    cmderror = TRUE;
		}
		break;
	    case 'S':
	    case 's':
		SFormat	 = TRUE;
		if (argv[i][2])	{
		    printf ("Invalid S-format switch.\n");
		    cmderror = TRUE;
		}
		break;
	    case 'W':
	    case 'w':
		if (argv[i][2] != ',') {
		    maxheap = CalcValue(&argv[i][2], 0);
		    if (maxheap	< MAXLINE)
			maxheap	= MAXLINE;	/* Minimum heap	size */
		    maxheap &= ~3L;		/* Long-word alignment */
		}
		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 'X':
	    case 'x':
		XrefList  = TRUE;
		SuppList = FALSE;	/* We must want	a listing */
		if (argv[i][2])	{
		    printf ("Invalid cross-reference switch.\n");
		    cmderror = TRUE;
		}
		break;
	    default:
		printf ("Unrecognized switch.\n");
		cmderror = TRUE;
		break;
	    }
	}
    }

    if (SourceFN[0] == '\0') {          /* Default list file name */
	printf ("Source file name is missing.\n");
	cmderror = TRUE;
    }

    if (EquateFN[0] == '\0') {          /* Default equate file name */
	strcpy (EquateFN, SourceFN);
	i = strlen (EquateFN);
	while (--i > 0)	{
	    if (EquateFN[i] == '.') {
		EquateFN[i] = '\0';     /* Chop off name extension */
		break;
	    }
	}
	strcat (EquateFN, ".equ");      /* Equate file name extension */
    }

    if (ListFN[0] == '\0') {            /* Default list file name */
	strcpy (ListFN,	SourceFN);
	i = strlen (ListFN);
	while (--i > 0)	{
	    if (ListFN[i] == '.') {
		ListFN[i] = '\0';       /* Chop off name extension */
		break;
	    }
	}
	strcat (ListFN,	".lst");        /* List file name extension */
    }

    if (SrecFN[0] == '\0') {            /* Default object file name */
	strcpy (SrecFN,	SourceFN);
	i = strlen (SrecFN);
	while (--i > 0)	{
	    if (SrecFN[i] == '.') {
		SrecFN[i] = '\0';       /* Chop off name extension */
		break;
	    }
	}
	if (SFormat)
	    strcat (SrecFN, ".s");      /* S-format name extension */
	else
	    strcat (SrecFN, ".o");      /* AmigaDOS format extension */
    }

    if (strcmp (SourceFN, EquateFN) == 0) {
	printf ("Source and equate file names are the same.\n");
	cmderror = TRUE;
    }
    if (strcmp (SourceFN, ListFN) == 0)	{
	printf ("Source and listing file names are the same.\n");
	cmderror = TRUE;
    }
    if (strcmp (SourceFN, SrecFN) == 0)	{
	printf ("Source and object file names are the same.\n");
	cmderror = TRUE;
    }
    if (strcmp (EquateFN, ListFN) == 0)	{
	printf ("Equate and listing file names are the same.\n");
	cmderror = TRUE;
    }
    if (strcmp (EquateFN, SrecFN) == 0)	{
	printf ("Equate and object file names are the same.\n");
	cmderror = TRUE;
    }
    if (strcmp (ListFN,	SrecFN)	== 0) {
	printf ("Listing and object file names are the same.\n");
	cmderror = TRUE;
    }

/*	Open Files.	*/

    _fmode = 0x8000;			/******	MS-DOS only ******/
    if (!cmderror) {
	if ((InFile = fopen (SourceFN, "r")) == NULL) {
	    printf ("Unable to open source file.\n");
	    cmderror = TRUE;
	}
    }
    _fmode = 0;				/******	MS-DOS only ******/
    if (!cmderror && MakeEqu) {
	if ((EquFile = fopen (EquateFN,	"w")) == NULL) {
	    printf ("Unable to open equate file.\n");
	    cmderror = TRUE;
	    fclose (InFile);		/* Close any opened files */
	}
    }
    if (!cmderror && !SuppList)	{
	if ((List = fopen (ListFN, "w")) == NULL) {
	    printf ("Unable to open listing file.\n");
	    cmderror = TRUE;
	    fclose (InFile);
	    if (MakeEqu)
		fclose (EquFile);
	}
    }
    if (!SFormat) _fmode = 0x8000;	/******	MS-DOS only ******/
    if (!cmderror) {
	if ((Srec = fopen (SrecFN, "w")) == NULL ) {
	    printf ("Unable to open object code file.\n");
	    cmderror = TRUE;
	    fclose (InFile);
	    if (MakeEqu)
		fclose (EquFile);
	    if (!SuppList)
		fclose (List);
	}
    }

    if (cmderror) {
	printf ("\n");
	printf ("Usage: a68k <source file>\n");
	printf ("            [-e<equate file>]\n");
	printf ("            [-h<header file>]\n");
	printf ("            [-i<include dirlist>]\n");
	printf ("            [-l<listing file>]\n");
	printf ("            [-o<object file>]\n");
	printf ("            [-p<page depth>]\n");
	printf ("            [-w[<heap size>][,<heap size>]]\n");
	printf ("            [-d] [-s] [-x]\n\n");
	printf ("Command-line arguments can appear in any order.\n");
	printf ("Heap size default (bytes):  -w");
	printf ("%ld,%ld\n", (long) DEFHEAP, (long) DEFHEAP2);
	printf ("\n");
	exit(20);
    }

#ifdef AZTEC_C
    Heap = lmalloc (maxheap);
#else
    Heap = malloc ((unsigned) maxheap);
#endif
    if (Heap ==	NULL) {
	printf ("Unable to allocate primary heap!\n");
	exit(20);
    }
    HeapLim = Heap;		/* Heap	limit (start out empty)	*/
    LowHeap = (Heap + maxheap);
    SymStart = (struct SymTab *) (LowHeap);	/* Symbol table	*/

#ifdef AZTEC_C
    Heap2 = lmalloc (maxheap2);
#else
    Heap2 = malloc ((unsigned) maxheap2);
#endif
    if (Heap2 == NULL) {
	printf ("Unable to allocate secondary heap!\n");
	exit(20);
    }
    NextFNS = Heap2;
    InF	= (struct InFCtl *) (Heap2 + maxheap2);
    LowInF = --InF;
    InFNum = OuterMac =	SkipNest = InF->Pos = InF->MCnt	= 0;
    InF->Line =	0;
    InF->UPtr =	0;
    InF->NPtr =	NextFNS;
    InF->NArg =	-1;
    strcpy (NextFNS, SourceFN);
    NextFNS += strlen (SourceFN) + 1;
    High2 = NextFNS;
    Low2  = (char *) LowInF;

    _fmode = 0x8000;			/******	MS-DOS only ******/
    printf ("\nAssembling %s\n\n\n", SourceFN);

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

    Begin Pass 1.
								   */
    printf ("PASS 1\n");
    LineCount =	LabLine	= MacCount = ErrorCount	= NumSyms = 0;
    AddrCnt = SectStart	= 0L;	/* Assume ORG =	0 to start */
    CurrHunk = NextHunk	= 0L;	/* Start in hunk zero */
    HunkType = HunkNone;	/* We're not in a hunk yet */
    HunkFlags =	SectLine = 0;
    endfile = ListOff =	Pass2 =	FALSE;

    while (!endfile && (strcmp (OpCode,	"END") != 0)) {
	PrevDir	= Dir;			/* Save	previous directive */
	endfile	= LineParts (dummy);	/* Get a statement */
	Dir = ObjDir (Srec);		/* Process directives */
	GetObjectCode (dummy);		/* Length if executable	*/

	if ((HunkType == HunkNone) && (AddrAdv != 0)) {
	    Sym	= SymStart;	/* Insert at head of table */
	    templong = (long) HunkCode << 16;
	    AddSymTab ("  ", 0L, templong, LineCount, 16);
	    Sect = Sym;		/* Create an unnamed section */
	    HunkType = HunkCode;
	    SectLine = LineCount;
	    NextHunk++;
	}
	if ((Label[0] != '\0')
	&& (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 == 0)) {		/* 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 (Dir == Equ) {
		Sym->Val = ObjSrc;		/* Equated value */
		Sym->Hunk = Src.Hunk;		/* Hunk	number */
	    }
	}
	AddrCnt	+= AddrAdv;	/* Advance location counter */
    }


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

	Begin Pass 2.
							       */
    printf ("PASS 2\n");
    InF	= (struct InFCtl *) (Heap2 + maxheap2);
    InF--;
    InFNum = OuterMac =	SkipNest = InF->Pos = InF->MCnt	= 0;
    InF->Line =	0;
    InF->UPtr =	0;
    InF->NPtr =	Heap2;
    InF->NArg =	-1;
    fclose (InFile);			/* "Rewind" the source file */
    InFile = fopen (SourceFN, "r");
    NextFNS = Heap2 + strlen (SourceFN)	+ 1;
    Pass2 = TRUE;
    LineCount =	LabLine	= MacCount = ErrorCount	= 0;
    AddrCnt = CurrHunk = SectStart = EndAddr = 0L;
    HunkType = HunkNone;
    HunkFlags =	SectLine = 0;
    endfile = ListOff =	FALSE;
    if ((Sym = SymStart) < (struct SymTab *) (Heap + maxheap)) {
	while (Sym->Flags & 16)	{
	    Sym->Val = 0L;	/* Back	to start of all	sections */
	    Sym++;
	}
    }
    TTLstring[0] = '\0';        /* Clear title string */
    if ((templong = (HeapLim - Heap) & 3L) != 0)
	HeapLim	+= 4 - templong;
    HighHeap = HeapLim;		/* High-water mark in heap */
    RelStart = (struct RelTab *) HeapLim;	/* Relocation table */
    RefStart = (struct Ref *) SymStart;	/* Cross-reference table */

/* Sign	on messages for	listing	file */
    LnCnt = 999;
    PgCnt = 0;
    TTLstring[0] = '\0';
    if (!SuppList) {
	CheckPage (List, FALSE);		    /* Print headings */
	fprintf	(List, "68000 Cross Assembler\n");
	fprintf	(List, "Copyright (c) 1985 by Brian R. Anderson\n\n");
	fprintf	(List, "AmigaDOS conversion copyright (c) 1987");
	fprintf	(List, " by Charlie Gibbs.\n");
	fprintf	(List, "Version %s\n\n", Version);
	LnCnt += 6;
    }

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

/*	Process	the second pass.	*/

    OpCode[0] =	'\0';                   /* Kill "END" from pass 1! */
    while (!endfile && (strcmp(OpCode,"END") != 0)) {
	PrevDir	= Dir;			/* Save	previous directive */
	endfile	= LineParts (dummy);	/* Get a statement */
	if (!endfile) {
	    Dir	= ObjDir (Srec);	/* Process directives */
	    GetObjectCode (dummy);	/* Executable object code */
	    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 (0, SymDup);	/* Can't SET normal label */
		    } else {
			Error (0, 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 */
		}
	    }
	    if (!SuppList)
		WriteListLine (List);
	    WriteSrecLine (Srec);
	    AddrCnt += AddrAdv;		/* Advance locaton counter */
	} else {
	    Error (0, EndErr);		/* END statement is missing */
	    if (!SuppList)
		WriteListLine (List);
	}
    }

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

	Clean up.
								*/

    fclose (InFile);		/* Finished with source	file */

    EndSdata (Srec, EndAddr);	/* Write remaining data	and end	record */
    fclose (Srec);		/* Finished with object	file */

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

/* Write all absolute symbols to an equate file	if desired. */
    if (MakeEqu) {
	fprintf	(EquFile, "* Equate file for %s\n", SourceFN);
	fprintf	(EquFile, "* Created by A68k version %s\n", Version);
	for (i = 0, sym	= SymStart; i <	NumSyms; i++, sym++) {
	    if (((sym->Hunk & 0x00007FFFL) == ABSHUNK)
	    && ((sym->Flags == 0) || (sym->Flags == 2))) {
		fprintf	(EquFile, "%s\tEQU\t$", sym->Nam);
		LongPut	(EquFile, sym->Val, 4);
		fprintf	(EquFile, "\n");
	    }
	}
	fclose (EquFile);
    }

/* Write error count to	console	and listing file. */
    printf ("    \n\nEnd of assembly - ");
    if (!SuppList)
	fprintf	(List, "\n\nEnd of assembly - ");
    if (ErrorCount == 0) {
	printf ("no errors were found.\n\n");
	if (!SuppList)
	    fprintf (List, "no errors were found.\n\n");
    } else if (ErrorCount == 1)	{
	printf ("1 error was found.\n\n");
	if (!SuppList)
	    fprintf (List, "1 error was found.\n\n");
    } else {
	printf ("%d errors were found.\n\n", ErrorCount);
	if (!SuppList)
	    fprintf (List, "%d errors were found.\n\n", ErrorCount);
    }
    templong = (long) (HighHeap	- Heap);
    if (LowHeap	< (char	*) RefStart)
	templong += (long) (Heap + maxheap - LowHeap);
    else
	templong += (long) (Heap + maxheap - (char *) RefStart);
    printf ("Heap usage (bytes):  -w%ld", templong);
    if (!SuppList)
	fprintf	(List, "Heap usage (bytes):  -w%ld", templong);
    templong = (long) (High2 - Heap2);
    if (Low2 < (char *)	LowInF)
	templong += (long) (Heap2 + maxheap2 - Low2);
    else
	templong += (long) (Heap2 + maxheap2 - (char *)	LowInF);
    printf (",%ld\n\n", templong);
    if (!SuppList) {
	fprintf	(List, ",%ld\n\f", templong);   /* One last page eject */
	fclose (List);		/* Finished with listing file */
    }
    free (Heap);		/* Release heap	space */
    free (Heap2);
    exit (ErrorCount ? 10 : 0);	/* All done */
}
