/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	      Copyright	(c) 1985 by Brian R. Anderson		    */
/*								    */
/*	   Symbol table	manipulation - 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.    */
/*								    */
/*------------------------------------------------------------------*/

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

long Value;	/* Passed from ReadSymTab to CalcValue */

/* Functions */
extern int  Instructions(), ObjDir();
extern int  GetInstModeSize(), GetMultReg();
extern int  GetArgs(), GetAReg();
extern long AddrBndW(),	AddrBndL();
extern char *malloc();
extern FILE *fopen();

int  LineParts(), GetField(), ReadSymTab(), GetSize(), OpenIncl();
long GetValue(), CalcValue();



int OpenIncl (name, dirlist) char name[], dirlist[];
/* Opens the file whose	name is	in "name".  The current
    directory is tried first.  If that fails, the directory
    names in "dirlist" (separated by commas) are then tried
    until either a file	is found or the	list is	exhausted.
    If the file	is found in a subdirectory, "name" is
    modified to	include	the entire path	specification.
    If another input file is open when this routine is called,
    it is closed first.	 Returns TRUE if successful, FALSE if not.  */
{
    register int i;
    char dirname[MAXLINE];

    if (InFile != NULL)
	fclose (InFile);	/* Close the inner file	*/

    if ((InFile	= fopen	(name, "r")) != NULL)
	return (TRUE);		/* Found it in current directory */

    i =	0;
    while (dirlist[i] != '\0') {
	i = GetField (dirlist, i, dirname);
	if ((dirname[i-1] != '/') && (dirname[i-1] != ':'))
	    strcat (dirname, "/");      /* Slash after directory name */
	strcat (dirname, name);
	if ((InFile = fopen (dirname, "r")) != NULL) {
	    strcpy (name, dirname);	/* Return entire path */
	    return (TRUE);	/* Found it in a subdirectory */
	}
	if (dirlist[i] != '\0')
	    i++;		/* Skip	over separator and try again */
    }
    return (FALSE);		/* Couldn't find it anywhere */
}



int LineParts (dummy) int dummy;
/* Gets	the next statement and extracts	its component parts.
    If end of file is reached, and we're in a macro or include
    file, the file is closed and the next outermost file is
    continued.	If we have reached the end of the source file,
    or encounter an ENDM or MEXIT directive, the current input
    file closed	and TRUE is returned.

    If we're in a user macro (indicated by UPtr being nonzero),
    we'll get the next statement from the save area in memory instead.

    Macro arguments, if	any, are substituted.

    LineCount is incremented if	a statement was	successfully read.

    If this is the first call of this routine (i.e. LineCount is zero)
    and	HeaderFN is not	a null string, we'll return an INCLUDE statement
    requesting the specified header file, rather than reading the first
    statement from the source file.

    The	following fields are set up:
	Line	- statement line image
	Label	- instruction label (without trailing colon)
	OpCode	- instruction mnemonic (converted to upper case)
	SrcOp	- first	(source) operand
	DestOp	- second (destination) operand
	Size	- size from OpCode
	SrcLoc	- displacement to start	of source operand
	DestLoc	- displacement to start	of destination operand
	InFNum	- decremented if end of	file is	reached
	InF	- incremented if end of	file is	reached
	LabLine	- set to LineCount if this line	is labeled
			(unless	it's a local label)
								*/
{
    register int i, j, c;
    register char *argptr;
    char subline[MAXLINE];

    while (1) {	/* Repeat until	we get something (not end of INCLUDE) */
	Line[0]	= Label[0] = OpCode[0] = SrcOp[0] = DestOp[0] =	'\0';
	OpLoc =	SrcLoc = DestLoc = 0;

	if ((LineCount==0) && (HeaderFN[0])) {	/* Header file */
	    strcpy (Line, "        INCLUDE ");  /* Make an INCLUDE stmt. */
	    strcat (Line, HeaderFN);
	    strcat (Line, "        ;Generated for header file");
	    strcpy (OpCode, "INCLUDE"); /* Dummy op code */
	    OpLoc = 8;
	    strcpy (SrcOp, HeaderFN);	/* Dummy source	operand	*/
	    SrcLoc = 16;
	    LineCount++;
	    errlim = AddrAdv = InstSize	= nO = nS = nD = nX = 0;
	    PrntAddr = FALSE;
	    return (FALSE);
	}

	if (InF->UPtr != 0) {			/* User	macro input */
	    strcpy (Line, InF->UPtr);
	    InF->UPtr += strlen	(Line) + 1;
	    c =	'\n';
	} else {				/* Normal file input */
	    i =	0;
	    while ((c=getc(InFile)) != '\n') {  /* Get Line, expanding tabs */
		if (c == 26)
		    c =	EOF;			/* Catch MS-DOS	EOF char. */
		if (c == EOF) {
		    if (i > 0) {		/* Last	line has no \n -   */
			c = ungetc (c, InFile);	/*  push back the EOF and  */
			c = '\n';               /*  process the last line. */
		    }
		    break;
		}
		if ((i < (MAXLINE - 1))	&& (c != 13)) {	/* Ignore excess */
		    if (c != '\t')
			Line[i++] = c;		/* Normal character */
		    else {
			j = (i + 8) & ~7;	/* Expand tabs */
			if (j >	(MAXLINE - 1))
			    j =	MAXLINE	- 1;	/* Tabbed off the end */
			while (i < j)
			    Line[i++] =	' ';
		    }
		}
	    }
	    Line[i] = '\0';
	}
	if ((Line[0] !=	'\0') && (Line[0] != '*') && (Line[0] != ';')) {

	    /* --------	Macro argument substitution routine -------- */

	    i =	0;
	    while ((Line[i] != '\0') && (InF->NArg != -1)) {
		if ((subline[i]	= Line[i]) != '\\') {
		    i++;
		    continue;
		}
		if (Line[i+1] == '@') { /* \@ - substitute macro number */
		    subline[i] = '.';
		    j =	InF->MCnt % 1000;
		    subline[i+1] = j / 100 + '0';
		    j =	j % 100;
		    subline[i+2] = j / 10 + '0';
		    subline[i+3] = j % 10 + '0';
		    subline[i+4] = '\0';
		    strcat (subline, &Line[i+2]); /* Remainder of Line */
		    strcpy (Line, subline);	/* Replace Line	*/
		    i =	0;			/* Check for more */
		    continue;
		}

		if (Line[i+1] <	'0' || Line[i+1] > '9') {
		    continue;		/* False alarm */
		}

		subline[i++] = '\0';    /* Chop off first portion */
		j = 0;			/* Get argument	index */
		while ((Line[i]	>= '0') && (Line[i] <= '9')) {
		    j *= 10;
		    j += Line[i++] - '0';       /* Current digit */
		}
		if ((j > 0) && (j <= InF->NArg)) {
		    argptr = InF->NPtr;
		    while (j > 0) {		/* Find	argument */
			argptr += strlen (argptr) + 1;
			j--;
		    }
		    strcat (subline, argptr);	/* Insert it */
		}
		strcat (subline, &Line[i]);	/* Remainder of	Line */
		strcpy (Line, subline);		/* Replace Line	*/
		i = 0;				/* Check for more */
	    }

	    /* ------- Break up	Line into its component	parts ------- */

	    i =	0;
	    if (Line[i]	!= ' ')
		i = GetField (Line, i, Label);		/* Label */
	    while ((j =	strlen(Label)) > 0) {
		if (Label[j-1] != ':')
		    break;
		Label[j-1] = '\0';      /* Strip trailing colon(s) */
	    }
	    while (Line[i] == ' ') i++;
	    if (Line[i]	!= ';') {
		OpLoc =	i;
		i = GetField (Line, i, OpCode);		/* Mnemonic */
		while (Line[i] == ' ') i++;
		if ((Line[i] !=	';') && (Line[i] != '\0')) {
		    SrcLoc = i;
		    i =	GetField (Line,	i, SrcOp);	/* Op1 (source)	*/
		    if (Line[i]	== ',')
			i++;
		    if ((Line[i]!=' ') && (Line[i]!='\0') && (Line[i]!=';')) {
			DestLoc	= i;
			i = GetField (Line, i, DestOp);	/* Op2 (dest.) */
		    }
		}
	    }
	}
	if ((j = strlen	(OpCode)) == 0)
	    Size = S0;
	else {
	    Size = GetSize (OpCode);	/* Instruction operand size */
	    for	(i = 0;	i < j; i++)		/* Convert OpCode */
		OpCode[i] = toupper(OpCode[i]);	/*  to upper case */
	}

	/* ------ If we	have reached the end of	a macro	or ------ */
	/* ------ include file,	return to the calling file ------ */

	i = (c == EOF);					/* End of file */
	if ((Dir != Macro) && (SkipNest	== 0)) {	/* Macro exits */
	    i |= (strcmp (OpCode, "ENDM") == 0);
	    i |= (strcmp (OpCode, "MEXIT") == 0);
	}

	if (!i)	{			/* Not end of file or macro */
	    if ((PrevDir == MacCall) &&	(strcmp	(OpCode, "MACRO") == 0))
		continue;		/* Ignore macro	header */
	    if (SkipNest == 0)		/* We're not skipping */
		break;			/* We got something */
	    else {
		if (strcmp (OpCode, "ENDC") == 0) SkipNest--;
		else if	(strcmp	(OpCode, "IFEQ") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFNE") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFGT") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFGE") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFLT") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFLE") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFC" ) == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFNC") == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFD" ) == 0) SkipNest++;
		else if	(strcmp	(OpCode, "IFND") == 0) SkipNest++;
		continue;
	    }
	}
	if (InFNum == 0)
	    break;			/* End of source file */
	if (InF->UPtr == 0) {
	    fclose (InFile);		/* Close inner file */
	    InFile = NULL;
	}
	NextFNS	= InF->NPtr;		/* Release space on name stack */
	InFNum--;			/* Return to outer file	*/
	InF++;
	if (InFNum < OuterMac)
	    OuterMac = 0;		/* End of outer	macro */
	if ((InF->UPtr == 0) &&	(InFile	== NULL)) {
	    InFile = fopen (InF->NPtr, "r");
	    fseek (InFile, InF->Pos, 0);
	}
    }
    LineCount++;			/* Bump	line counter */
    (InF->Line)++;
    if (Label[0] != '\0')
	if ((Label[0] <	'0') || (Label[0] > '9'))
	    LabLine = LineCount;	/* Save	line number of label */
    if ((LineCount % 10) == 0) {
	printf ("%4d\b\b\b\b", LineCount);      /* Display progress */
#ifdef AZTEC_C
	fflush (stdout);		/* Make	sure it	gets out */
#endif
    }
    errlim = AddrAdv = InstSize	= nO = nS = nD = nX = 0;
    PrntAddr = FALSE;

    return (c == EOF);
}



int GetField (source, pos, dest) char source[],	dest[];	int pos;
/* Gets	a field	from "source", starting at position "pos".
    Result is returned in "dest".
    Stops on the first comma, semicolon, or white space	not
	enclosed within	apostrophes or parentheses.
    Returns stopping location.
    If already at end of "source", "dest" is set to null string. */
{
    register char *s;
    register int j, parncnt, instring;

    s =	source + pos;	/* Use a pointer for speed */
    instring = FALSE;
    parncnt = j	= 0;

    while (*s) {
	if (instring) {
	    dest[j++] =	*s;
	} else {
	    if ((*s == ' ') || (*s == ';'))
		break;
	    else if ((*s == ',') && (parncnt == 0))
		break;
	    else
		dest[j++] = *s;
	}
	if (*s == '(')
	    parncnt++;
	else if	(*s == ')')
	    parncnt--;
	else if	(*s == '\'')
	    instring = !instring;
	s++;
    }
    dest[j] = '\0';
    return (s -	source);
}



AddMacLine (line) char *line;
/* Adds	the line in "line" to the heap. */
{
    HeapSpace (strlen (line) + 1);	/* Make	sure we	have room */
    strcpy (HeapLim, line);		/* Store the line */
    HeapLim += strlen (line) + 1;	/* Update heap limit */
}



long GetValue (operand,	loc) char operand[]; int loc;
/* Determines value of expression */
/* Hunk2 is set	to hunk	number of result (ABSHUNK if absolute).
   If the expression consists solely of	self-defining terms,
	DefLine2 is set	to zero.  Otherwise, DefLine2 is set
	to the highest statement number	in which any symbol in
	the expression was defined.  If	the expression contains
	any undefined symbols, DefLine2	is set to 32767.
    The	following code is based	on a regular-to-Polish expression
	converter described in "A Guide to FORTRAN IV Programming"
	by Daniel D. McCracken (John Wiley & Sons, Inc.	1965,
	3rd printing August 1968).  However, rather than generating
	the entire Polish expression, this routine will	evaluate
	and combine two	terms as soon as an operator of	lower
	precedence is encountered.				*/
{
    register char *o, *s;
    char tempop[MAXLINE];
    int	 oloc, parncnt,	nextprec, instring;
    long templong;
    struct TermStack *origterm;

    Hunk2 = ABSHUNK;
    parncnt = DefLine2 = 0;
    o =	NextFNS;
    if ((templong = (o - Heap) & 3L) !=	0)
	o += 4 - templong;
    origterm = Term = (struct TermStack	*) o;	/* Term	stack */
    Ops	= (struct OpStack *) InF;		/* Operator stack */
    Ops--;
    ParseSpace (0);
    Ops->chr = ' ';     /* Prime the operator stack */
    Ops->prec =	-1;
    if ((char *) Ops < Low2)
	Low2 = (char *)	Ops;

    /* Get all tokens.
	Terms are evaluated, and operator precedence is	determined.
	Left and right parentheses are given a precedence of
	    1 and 2 respectively.
	Binary operators are given precedence values starting at 3.
	Unary plus is ignored.
	Unary minus is converted to zero minus the remainder of
	    of the expression -	its precedence is set to 9 to
	    ensure that	the simulated unary operator is	evaluated
	    before the remainder of the	expression.
	Logical	not (~), being another unary operator, is converted
	    to -1 exclusive-ORed with the remainder of the expression.
	    Its	precedence is also set to 9.				*/

    o =	operand;			/* Current position in operand */

    while (1) {
	while (*o == '(') {             /* Left parenthesis */
	    Ops--;
	    ParseSpace (0);
	    Ops->chr  =	'(';
	    Ops->prec =	1;
	    if ((char *) Ops < Low2)
		Low2 = (char *)	Ops;
	    parncnt++;
	    o++;
	}
	if ((*o	== '+') || (*o == '-') || (*o == '~')) {    /* Unary op */
	    if (*o != '+') {    /* Ignore unary plus */
		Ops--;
		ParseSpace (sizeof (struct TermStack));
		Term->value   =	(*o == '-') ? 0 : -1;   /* Dummy value */
		Term->hunk    =	ABSHUNK;
		Term->oploc   =	loc + (o - operand);
		Term->defline =	0;
		Term++;
		if ((char *) Term > High2)
		    High2 = (char *) Term;
		Ops->chr  = *o;		/* Now get the operator	itself */
		Ops->prec = 9;		/* Do it ASAP */
		if ((char *) Ops < Low2)
		    Low2 = (char *) Ops;
	    }
	    o++;
	    if (*o == '(')
		continue;	/* Inner parenthesized expression */
	}
	oloc = loc + (o	- operand);

	s = tempop;				/* Get a term */
	if (*o == '*') {        /* It's a location counter reference, */
	    *s++ = *o++;	/*   not a multiplication operator!   */
	} else {
	    if (IsOperator (o) || (*o == '\0')) {
		Error (oloc, OperErr);	/* Unexpected operator or no terms */
		return (0L);
	    }
	    instring = FALSE;
	    while (*o) {
		if (*o == '\'')
		    instring = !instring;   /* String delimiter	*/
		if (!instring && (IsOperator (o) || (*o	== '~')))
		    break;		    /* Found an	operator - stop	*/
		*s++ = *o++;		    /* Get a character */
	    }
	}
	*s = '\0';
	ParseSpace (sizeof (struct TermStack));
	Term->value   =	CalcValue (tempop, oloc);
	Term->hunk    =	Hunk2;
	Term->oploc   =	oloc;
	Term->defline =	DefLine2;
	Term++;
	if ((char *) Term > High2)
	    High2 = (char *) Term;
	Hunk2 =	DefLine2 = 0;

	while (*o == ')') {                     /* Right parenthesis */
	    if (parncnt	== 0) {
		Error ((int) (loc + (o - operand)), OperErr);
		return (0L);
	    }
	    CondCalc (2);		/* Unstack what	we can */
	    if (Ops->chr == '(')
		Ops++;			/* Drop	paired parentheses */
	    else {
		Ops--;
		ParseSpace (0);
		Ops->chr  = ')';        /* Stack parenthesis for now */
		Ops->prec = 2;
		if ((char *) Ops < Low2)
		    Low2 = (char *) Ops;
	    }
	    parncnt--;
	    o++;
	}
	if (*o)	{
	    if (!IsOperator (o)	|| (*o == '(')) {
		Error ((int) (loc + (o - operand)), OperErr);
		return (0L);		/* Expected an operator	*/
	    }
	    switch (*o)	{
	    case '+': nextprec = 3; break;
	    case '-': nextprec = 3; break;
	    case '*': nextprec = 4; break;
	    case '/': nextprec = 4; break;
	    case '&': nextprec = 5; break;
	    case '!': nextprec = 5; break;
	    case '<': nextprec = 6; break;
	    case '>': nextprec = 6; break;
	    }
	    CondCalc (nextprec);	/* Unstack what	we can */
	    Ops--;
	    ParseSpace (0);
	    Ops->chr  =	*o;		/* Stack the next operator */
	    Ops->prec =	nextprec;
	    if ((char *) Ops < Low2)
		Low2 = (char *)	Ops;
	    if ((*o == '<') || (*o == '>'))
		o++;	/* Skip	over two-character operator */
	    o++;
	} else {
	    if (parncnt) {
		Error ((int) (loc + (o - operand)), OperErr);
		return (0L);	/* Too many left parentheses */
	    }
	    CondCalc (0);		/* Unstack what's left */
	    if (--Term != origterm)	/* Should be only one term left	*/
		Error (Term->oploc, OperErr);		/* Parser bug? */
	    Hunk2    = Term->hunk;
	    DefLine2 = Term->defline;
	    return (Term->value);	/* Final value */
	}
    }
}



CondCalc (newprec) int newprec;
/* As long as the top operator on the operator stack has a precedence
    greater than or equal to the contents of "newprec", this routine
    will pop the two top terms from the	term stack, combine them
    according to the operator on the top of the	operator stack (which
    is also popped), and push the result back onto the term stack. */
{
    while (Ops->prec >=	newprec) {	/* Unstack an operator */
	Term -=	2;
	if (Ops->chr ==	'+') {          /* Relocatable addition */
	    if (Term->hunk == ABSHUNK)
		Term->hunk = (Term+1)->hunk;	/* A+R */
	    else if ((Term+1)->hunk != ABSHUNK)	{
		Error ((Term+1)->oploc,	RelErr);    /* R+R - error */
		Term->hunk = ABSHUNK;		/* Make	it absolute */
	    }
	} else if (Ops->chr == '-') {           /* Subtraction */
	    if (Term->hunk == (Term+1)->hunk)
		Term->hunk = ABSHUNK;		/* R-R - absolute */
	    else if (Term->hunk	!= ABSHUNK) {	    /* R-R across hunks	*/
		Error ((Term+1)->oploc,	RelErr);    /*	is an error -	*/
		Term->hunk = ABSHUNK;		    /* make it absolute	*/
	    }
	} else if ((Term->hunk != ABSHUNK)
	|| ((Term+1)->hunk != ABSHUNK))	{
	    Error (Term->oploc,RelErr);		/* All other operations	*/
	    Term->hunk = ABSHUNK;		/*   must be absolute	*/
	}
	if ((Term+1)->defline >	Term->defline)	/* Definition */
	    Term->defline = (Term+1)->defline;	/*  line nos. */

	switch (Ops->chr) {		/* Perform the operation */
	case '+':
	    Term->value	+= (Term+1)->value;
	    break;
	case '-':
	    Term->value	-= (Term+1)->value;
	    break;
	case '*':
	    Term->value	*= (Term+1)->value;
	    break;
	case '/':
	    if ((Term+1)->value)
		Term->value /= (Term+1)->value;
	    else
		Term->value = 0;     /*	Don't divide by zero */
	    break;
	case '&':
	    Term->value	&= (Term+1)->value;
	    break;
	case '!':
	    Term->value	|= (Term+1)->value;
	    break;
	case '<':
	    Term->value	<<= (Term+1)->value;
	    break;
	case '>':
	    Term->value	>>= (Term+1)->value;
	    break;
	case '~':
	    Term->value	^= (Term+1)->value;
	    break;
	default:
	    Error (Term->oploc,	OperErr);	/* Parser bug? */
	    break;
	}
	Term++;
	Ops++;
    }
}



int IsOperator (o) char	*o;
/* Tests whether "o" points to a valid operator or parenthesis. */
{
    return ((*o	== '+')
	||  (*o	== '-')
	||  (*o	== '*')
	||  (*o	== '/')
	||  (*o	== '&')
	||  (*o	== '!')
	|| ((*o	== '<') && (*(o+1) == '<'))
	|| ((*o	== '>') && (*(o+1) == '>'))
	||  (*o	== '(')
	||  (*o	== ')'));
}



long CalcValue (operand, loc) char operand[]; int loc;
/* Evaluates a single term (called by GetValue).
    Hunk2 receives relative hunk number	(ABSHUNK if absolute).
    If the value is a symbol, DefLine2 is set to the line number
	where it was defined, or 32767 if it is	undefined.
	For self-defining terms, DefLine2 is set to zero.	*/
{
    register long result;	/* Result is calculated	here */
    register int  i, radix;
    int	 neg, numstart,	local;
    char maxdig;	/* Highest valid digit in current radix	*/

    local = FALSE;
    if (neg = (operand[0] == '-'))
	numstart = 1;			/* Negative value */
    else {
	numstart = 0;			/* Positive value */
	i = strlen(operand) - 1;
	if (operand[i] == '$') {
	    local = TRUE;		/* Assume it's a local label */
	    while (i > 0) {
		i--;
		if ((operand[i]	< '0') || (operand[i] > '9')) {
		    local = FALSE;	/* False alarm */
		    break;
		}
	    }
	}
    }
    Hunk2 = ABSHUNK;			/* Assume value	is absolute */
    DefLine2 = 0;			/* and self-defining */

    if ((operand[numstart] >= '0') && (operand[numstart] <= '9')) {
	radix =	10;		/* Decimal number */
	maxdig = '9';
    } else if (operand[numstart] == '$') {
	radix =	16;		/* Hexadecimal number */
	maxdig = '9';
    } else if (operand[numstart] == '@') {
	radix =	8;		/* Octal number	*/
	maxdig = '7';
    } else if (operand[numstart] == '%') {
	radix =	2;		/* Binary number */
	maxdig = '1';
    } else
	radix =	0;		/* Not a number	*/
    if (local)
	radix =	0;		/* Local variable - not	a number */

    if (radix != 0) {			/* Some	sort of	number */
	result = 0;
	if (radix != 10)
	    numstart++;			/* Allow for type character */
	for (i = numstart; operand[i] != '\0'; i++) {
	    result *= radix;
	    if ((operand[i] >= '0') && (operand[i] <= maxdig))
		result += (operand[i] -	'0');
	    else if (radix == 16)
		if ((operand[i]	>= 'A') && (operand[i] <= 'F'))
		    result += (operand[i] - 'A' + 10);
		else if	((operand[i] >=	'a') && (operand[i] <= 'f'))
		    result += (operand[i] - 'a' + 10);
		else
		    Error (loc + i, OperErr);
	    else
		Error (loc + i,	OperErr);
	}
    } else if (operand[0] == '\'') {            /* Character value */
	result = 0;
	for (i = 1; operand[i] != '\''; i++) {
	    if (operand[i] == '\0') {
		Error (loc + i,	OperErr);	/* End is missing */
		break;
	    }
	    result = (result <<	8) + operand[i];
	}
    } else if ((operand[0] == '*') && (operand[1] == '\0')) {
	result = AddrCnt;	/* Value of location counter */
	Hunk2 =	CurrHunk;	/* Use current section's hunk number */
    } else if (strcmp (operand,	"NARG") == 0) {
	result = InF->NArg;		/* Number of arguments */
	if (result == -1)
	    result = 0;			/* No arguments	outside	macros */
    } else {
	if (ReadSymTab (operand)) {	/* Look	up symbol */
	    AddRef (LineCount);		/* Found - add reference */
	    if (Sym->Flags & 0x60)
		Error (loc, AddrErr);	/* Can't use a register equate */
	} else
	    Error (loc,	Undef);		/* Undefined */
	result = Value;
    }
    if (neg) {
	result = -result;		/* Negative value */
	if (Hunk2 != ABSHUNK)
	    Error (loc,	RelErr);	/* Must	be absolute */
    }
    return(result);
}



AddSymTab (label, value, hunk, line, flags)
char label[];
long value, hunk;
int line, flags;
/* Inserts a new entry to the symbol table and updates NumSyms.
   The insertion position is taken from	"Sym" - it must be
    such that the table	will be	kept in	sequence by label.
    "Sym" is set by "ReadSymTab".
    If the label is a local label (i.e.	the first character is
    numeric), the current contents of LabLine will be converted
    to characters and appended to the label before it is added.	*/
{
    register char *pj1,	*pj2, *psym;
    char wlab[MAXLINE],	wnum[6];

    strcpy (wlab, label);
    if ((label[0] >= '0') && (label[0] <= '9')) {
	sprintf	(wnum, "%d", LabLine);  /* If it's a local label, */
	strcat (wlab, wnum);		/*    append LabLine	  */
    }
    pj2	= (char	*) SymStart;
    SymStart--;
    pj1	= (char	*) SymStart;
    HeapSpace (strlen(wlab)+1);	/* Make	sure we	have room */
    psym = (char *) Sym;	/* Try for some	speed */
    while (pj2 < psym)		/* Shift the table */
	*pj1++ = *pj2++;
    Sect--;			/* Adjust section pointer */
    Sym--;			/* Insert to left of search point */
    Sym->Nam   = HeapLim;	/* Pointer to symbol */
    Sym->Val   = value;		/* Value */
    Sym->Hunk  = hunk;		/* Hunk	number */
    Sym->Defn  = line;		/* Statement number */
    Sym->Flags = flags;		/* Flags */
    Sym->Ref1  = 0;		/* Reference pointer */
    NumSyms++;			/* Count symbols */
    strcpy (HeapLim, wlab);	/* Store the symbol */
    HeapLim += strlen(wlab)+1;	/* Update heap limit */
}



int ReadSymTab (label) char label[];
/* Searches the	symbol table for the given label.
   If not found, points	Sym to where the new entry should
     be	inserted and returns FALSE.
   If found, points Sym	to the proper table entry,
     and sets up the following fields:
	Value	 - value of symbol
	Hunk2	 - hunk	number in which	symbol resides
		   ABSHUNK if value is absolute
		   ones	complement of symbol table index if external
	DefLine2 - statement number in which symbol was	defined
			(32767 if undefined )
    If the label is a local label (i.e.	the first character is
    numeric), the current contents of LabLine will be converted
    to characters and appended to the label before it is searched. */
{
    register int lower,	upper, mid, search;
    char wlab[MAXLINE],	wnum[6];

    strcpy (wlab, label);
    if ((label[0] >= '0') && (label[0] <= '9')) {
	sprintf	(wnum, "%d", LabLine);  /* If it's a local label, */
	strcat (wlab, wnum);		/*    append LabLine	  */
    }
    for	(lower = 0, upper = NumSyms; lower < upper; ) {
	mid = (lower + upper) /	2;
	Sym = SymStart + mid;
	search = strcmp	(wlab, Sym->Nam);
	if (search < 0)
	    upper = mid;
	else if	(search	> 0)
	    lower = mid	+ 1;
	else {
	    Value = Sym->Val;		/* Found it */
	    Hunk2 = Sym->Hunk &	0x0000FFFFL;
	    if (Hunk2 &	0x00008000L)
		Hunk2 |= 0xFFFF0000L;	/* Extend sign */
	    DefLine2 = Sym->Defn;
	    return (TRUE);
	}
    }
    Value = 0;			/* Didn't find it - */
    Hunk2 = ABSHUNK;		/* set value to	absolute zero */
    DefLine2 = 32767;
    Sym	= SymStart + lower;	/* Insert a new	entry here */
    return (FALSE);
}



AddRef (linenum) int linenum;
/* Adds	"linenum" to the list of references
    for	the symbol pointed to by Sym.		*/
{
    register int i;
    register struct Ref	*ref, *prevref;

    if (!Pass2)
	return;			/* Pass	2 only!	*/
    if (!XrefList)
	return;			/* No cross-reference */
    prevref = 0;
    ref	= Sym->Ref1;
    while (ref)	{		/* Chase pointers */
	for (i = 0; i <	MAXREF;	i++) {	    /* Scan reference entry */
	    if (ref->RefNum[i] == 0) {	    /*	for an empty slot   */
		ref->RefNum[i] = linenum;   /* Insert new line number */
		return;
	    }
	}
	prevref	= ref;			/* Remember where we were */
	ref = ref->NextRef;		/* Link	to the next entry */
    }
    HeapSpace (sizeof (struct Ref));	/* Find	space for a new	entry */
    RefStart--;
    RefStart->NextRef =	0;		/* Pointer to next entry */
    RefStart->RefNum[0]	= linenum;	/* First reference in new entry	*/
    for	(i = 1;	i < MAXREF; i++)
	RefStart->RefNum[i] = 0;	/* Clear remaining slots */
    if (prevref	== 0)
	Sym->Ref1 = RefStart;		/* Link	to first entry */
    else
	prevref->NextRef = RefStart;	/* Link	to next	entry */
}



int GetSize (symbol) char symbol[];
/* Determines size of opcode/operand: Byte, Word, Long */
{
    register int  i;
    register char c;

    i =	0;
    while (1) {
	c = symbol[i++];
	if ((c == '\0') || (c == '.'))
	    break;
    }
    if (c == '\0')
	return(Word);	 /* Default to size Word = 16 bits */
    else {
	c = toupper (symbol[i]);	/* Record size extension */
	symbol[i - 1] =	'\0';           /* Chop size extension off */
	if ((c == 'B') || (c == 'S'))   /* Byte or Short Branch/Jump */
	    return(Byte);
	else if	(c == 'L')              /* Long */
	    return(Long);
	else
	    return(Word);		/* Default to Word */
    }
}



HeapSpace (n) int n;
/* Die if we can't find "n" bytes of free space on the heap. */
{
    if ((HeapLim + n) >	(Pass2 ? (char *) RefStart : (char *) SymStart)) {
	printf ("    \n%4d   %s\n", LineCount, Line);
	printf ("Primary heap overflow - assembly terminated.\n");
	free (Heap);
	free (Heap2);
	exit(20);
    }
}



Heap2Space (n) int n;
/* Works like HeapSpace, but on	the secondary heap. */
{
    if ((NextFNS + n) >	(char *) InF) {
	printf ("    \n%4d   %s\n", LineCount, Line);
	printf ("Secondary heap overflow - assembly terminated.\n");
	free (Heap);
	free (Heap2);
	exit(20);
    }
}



ParseSpace (n) int n;
/* Special version of Heap2Space for the expression parser */
{
    if (((char *) Term + n) > (char *) Ops) {
	printf ("    \n%4d   %s\n", LineCount, Line);
	printf ("Secondary heap overflow - assembly terminated.\n");
	free (Heap);
	free (Heap2);
	exit(20);
    }
}
