/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	       Copyright (c) 1985 by Brian R. Anderson		    */
/*								    */
/*		   Main	program	- August 20, 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.01 - August 20, 1987";

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


	    M A	I N T E	N A N C	E    H I S T O R Y


Version	1.00 (Charlie Gibbs, June 18, 1987) - initial release


Version	1.01 (Charlie Gibbs, August 20,	1987)

	The following bugs in version 1.00 have	been corrected:

	      -	Long-word constants and	storage	areas were being
		aligned	on a double-word boundary.  The	only place
		where double-word alignment is now forced is at	a
		break between SECTIONs,	since the length of an
		AmigaDOS hunk must be a	multiple of 4 bytes.
		(CNOP 0,4 can still be used if double-word
		alignment is desired by	the programmer.)

	      -	If a label on an END statement or the first statement
		of a SECTION was named in an XDEF statement, it	would
		not be written to the object code file.	 The latter
		case includes both the label of	a SECTION directive
		and the	label of the first executable instruction in
		the absence of any SECTION directives (defaulting to
		an unnamed CODE	section).  In the final	case (default
		unnamed	CODE section), references to XREF symbols
		in the first statement would also not be written
		to the object code file.

	      -	If the last statement in the source file was not
		terminated with	a newline character (premature EOF),
		it was being ignored altogether.

	      -	A register list	as the source operand of a MOVE
		instruction was	not being flagged as an	error.
		(MOVE to a register list was being flagged, however.)

	      -	MOVE from USP was generating incorrect code.  Also,
		MOVE from SR or	CCR to an address register was
		generating incorrect code rather than being flagged.


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


#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];	/* File	names */
    int	 endfile;			/* End-of-file flag */
    long maxheap, maxheap2;		/* Maximum heap	sizes */
    int	 cmderror, i, j, dummy;
    long templong;

    cmderror = FALSE;		/* Clear command-line error flag */
    SourceFN[0]	= '\0';                 /* Don't have source name 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 = 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 '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 (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, 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 (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 && !SuppList)	{
	if ((List = fopen (ListFN, "w")) == NULL) {
	    printf ("Unable to open listing file.\n");
	    cmderror = TRUE;
	}
    }
    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;
	}
    }

    if (cmderror) {
	printf ("\n");
	printf ("Usage: a68k <source file>\n");
	printf ("            [-o<object file>]\n");
	printf ("            [-l<listing file>]\n");
	printf ("            [-i<include dirlist>]\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 (strlen (Label) != 0) {	/* If statement	is labeled, */
		ReadSymTab (Label);	/*  check for duplicate	defn. */
		if (Sym->Defn != LineCount) {
		    AddRef (LineCount);	/* Got one - flag as reference */
		    if (Sym->Flags & 4)	{
			if (Dir	!= Set)
			    Error (0, SymDup);
		    } else {
			if (Dir	== Set)
			    Error (0, SymDup);
		    }
		} else {
		    if ((Dir !=	Equ) &&	(Dir !=	Set)
		    && (Dir != Equr) &&	(Dir !=	Reg))
			if (Sym->Val !=	AddrCnt)
			    Error (0, Phase);	/* Assembler error */
		}
	    }
	    if (!SuppList)
		WriteListLine (List);
	    WriteSrecLine (Srec);
	    AddrCnt += AddrAdv;		/* Advance location 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 (!SuppList)
	WriteSymTab (List);	/* List	the symbol table */

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