/*------------------------------------------------------------------*/
/*								    */
/*		      MC68000 Cross Assembler			    */
/*								    */
/*	      Copyright	(c) 1985 by Brian R. Anderson		    */
/*								    */
/*		 Operand processor - May 31, 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"

/* Functions */
extern int  LineParts(), GetField(), Instructions(), ObjDir();
extern int  GetSize(), ReadSymTab(), OpenIncl();
extern long AddrBndW(),	AddrBndL(), GetValue(),	CalcValue();
extern char *malloc();
extern FILE *fopen();

int GetArgs(), GetAReg(), GetInstModeSize(), GetMultReg();



int GetArgs (name) char	*name;
/* Gets	macro arguments	and adds them to FNStack after adding "name".
    Returns the	number of arguments added to the stack.
    Note that this might not be	the full number	of arguments
    provided if	the stack overflowed.				*/
{
    register int i, j, narg, instring;
    char currarg[MAXLINE];		/* Current argument */

    narg = 0;				/* Argument counter */

    Heap2Space (strlen (name) +	1);	/* Find	space for name */
    strcpy (NextFNS, name);		/* Add name to stack */
    NextFNS += strlen (name) + 1;	/* Bump	pointer	*/
    if (NextFNS	> High2)
	High2 =	NextFNS;		/* Update high-water mark */

    i =	SrcLoc;				/* Now scan Line */
    while ((Line[i] != ' ') && (Line[i] != ';') && (Line[i] != '\0')) {
	j = 0;
	if (instring = (Line[i]	== '<'))    /* String delimiter */
	    i++;
	while (1) {
	    if (instring) {
		if (Line[i] == '>') {
		    i++;
		    break;			/* End of string */
		}
	    } else {
		if (Line[i] == '\0') break;     /* End of line */
		if (Line[i] == ',') break;      /* End of operand */
		if (Line[i] == ' ') break;      /* End of all operands */
		if (Line[i] == ';') break;      /* Start of comments */
	    }
	    currarg[j++] = Line[i++];		/* Get a character */
	}
	currarg[j] = '\0';
	Heap2Space (j +	1);			/* Check for space */
	strcpy (NextFNS, currarg);		/* Store argument */
	NextFNS	+= strlen (currarg) + 1;	/* Next	available space	*/
	if (NextFNS > High2)
	    High2 = NextFNS;			/* High-water mark */
	narg++;					/* Count arguments */
	if (Line[i] == ',')
	    i++;			/* Skip	over separator */
    }
    return (narg);			/* Successful completion */
}



EffAdr (EA, Bad) struct	OpConfig *EA; int Bad;
/* Adds	effective address field	to Op (BITSET representing opcode) */
{
    if ((1 << (EA->Mode	- 1)) IN Bad) {
	Error (EA->Loc,	ModeErr);	/* Invalid mode	*/
	return;
    } else if (EA->Mode	> 12)		/* Special modes */
	return;
    else if (EA->Mode <	8)		/* Register direct or indirect */
	Op |= ((EA->Mode - 1) << 3) | EA->Rn;
    else
	Op |= 0x0038 | (EA->Mode - 8);	/* Absolute modes */
    OperExt (EA);
}



OperExt	(EA) struct OpConfig *EA;
/* Calculate operand Extension word, and check range of	operands */
{
    switch (EA->Mode) {
	case AbsL:
	    break;   /*	No range checking needed */
	case AbsW:
	case ARDisp:
	case PCDisp:
	    if ((EA->Value < -32768) ||	(EA->Value > 32767))
		Error (EA->Loc,	SizeErr);
	    break;
	case ARDisX:
	case PCDisX:
	    if ((EA->Value < -128) || (EA->Value > 127))
		Error (EA->Loc,	SizeErr);
	    EA->Value &= 0x00FF;		      /* Displacement */
	    EA->Value |= EA->Xn	<< 12;			/* Index reg. */
	    if (EA->X == Areg)	   EA->Value |=	0x8000;	/* Addr. Reg. */
	    if (EA->Xsize == Long) EA->Value |=	0x0800;	/* Long	reg.  */
	    break;
	case Imm:
	    if (Size ==	Word) {
		if ((EA->Value < -32768) || (EA->Value > 65535L))
		    Error (EA->Loc, SizeErr);
	    } else if (Size == Byte)
		if ((EA->Value < -128) || (EA->Value > 255))
		    Error (EA->Loc, SizeErr);
	    break;
    }
}



GetOperand (oper, op) char oper[]; struct OpConfig *op;
/* Finds mode and value	for source or destination operand. */
{
    register char ch;
    register int  i, j;
    char UCoper[MAXLINE], tempop[MAXLINE];
    int	 rloc, MultFlag, opend;

    op->Value =	op->Defn = 0;
    op->Mode = Null;
    op->X    = X0;
    op->Hunk = ABSHUNK;

    if ((opend = strlen	(oper) - 1) < 0)
	return;				/* Nothing to process */

    for	(i = 0;	i <= opend; i++)
	UCoper[i] = toupper (oper[i]);	/* Upper-case version */
    UCoper[i] =	'\0';

    if (oper[0]	== '#') {               /* Immediate */
	strcpy (oper, &oper[1]);
	op->Value = GetValue (oper, op->Loc);
	op->Mode  = Imm;
	op->Hunk  = Hunk2;
	op->Defn  = DefLine2;
	return;
    }

    i =	IsRegister (oper, opend+1);
    if (i >= 0)	{
	op->Mode = (i &	8) ? ARDir : DReg;	/* Register type */
	op->Rn = i & 7;				/* Register number */
	return;
    } else if (i == -2)	{
	op->Mode = MultiM;			/* Equated register list */
	op->Value = Sym->Val;
	return;
    } else if (strcmp (UCoper, "SP") == 0) {
	op->Mode = ARDir;			/* Stack Pointer */
	op->Rn = 7;				/* (it's A7) */
	return;
    } else if (strcmp (UCoper, "SR") == 0) {
	op->Mode = SR;				/* Status Register */
	return;
    } else if (strcmp (UCoper, "CCR") == 0) {
	op->Mode = CCR;			/* Condition Code Register */
	return;
    } else if (strcmp (UCoper, "USP") == 0) {
	op->Mode = USP;			/* User	Stack Pointer */
	return;

    } else if ((oper[0]	== '(') && (oper[opend] == ')')) {
	i = IsRegister (&oper[1], opend-1);
	if (i >= 8 && i	<= 15) {
	    op->Mode = ARInd;		/* Address Register indirect */
	    op->Rn = i - 8;
	    return;
	} else if (i > 0) {
	    Error (op->Loc, AddrErr);	/* Data	register is invalid */
	    return;
	}	/* else	may be parenthesized expression	*/

    } else if ((oper[0]	== '(')         /* Post-increment */
    && (oper[opend-1] == ')')
    && (oper[opend] == '+')) {
	op->Mode = ARPost;
	op->Rn = GetAReg (&oper[1], opend-2, op->Loc + 1);
	return;

    } else if ((oper[0]	== '-')         /* Pre-decrement */
    && (oper[1]	== '(')
    && (oper[opend] == ')')) {
	i = IsRegister (&oper[2], opend-2);
	if (i >= 8 && i	<= 15) {
	    op->Mode = ARPre;
	    op->Rn = i - 8;
	    return;
	} else if (i > 0) {
	    Error (op->Loc, AddrErr);	/* Data	register is invalid */
	    return;
	}	/* else	parenthesized expression with leading minus? */
    }

    /* Try to split off	displacement (if present).
	We'll assume we have a register expression if the operand
	ends with a parenthesized expression not preceded by an
	operator.  I know this code is a real kludge, but that's
	the result of the bloody syntax.  Thanks, Motorola.	*/

    j =	strlen(oper) - 1;		/* Last	character */
    if (i = (oper[j] ==	')'))           /* Trailing parenthesis? */
	while (oper[--j] != '(')        /* Find left parenthesis */
	    if (j <= 0)
		break;
    if (j <= 0)				/* Must	not be at beginning */
	i = FALSE;
    if (i) {
	if (j == 1) {
	    if (oper[0]	== '-')
		i = FALSE;		/* Leading minus sign */
	} else {
	    if (oper[j-1] == '*') {     /* Location counter? */
		if (!IsOperator	(&oper[j-2]) ||	(oper[j-2] == ')'))
		    i =	FALSE;		/* No, it's multiplication */
	    } else if (IsOperator (&oper[j-1]) && (oper[j-1] !=	')')) {
		i = FALSE;		/* Preceded by an operator */
	    }
	}
    }

    if (i) {		/* Looks like a	displacement mode */
	oper[j]	= '\0';
	op->Value = GetValue (oper, op->Loc);	/* Displacement	*/
	op->Hunk  = Hunk2;			/* Hunk	number */
	op->Defn  = DefLine2;			/* Line	where defined */
	oper[j++] = '(';                        /* Restore parenthesis */

	rloc = op->Loc + j;		/* The register	starts here */
	j = GetField (oper, j, tempop);	/* Get address register	*/
	if (oper[j] == '\0')            /* If there's no index register */
	    tempop[strlen(tempop)-1] = '\0';    /* chop parenthesis */

	if ((tempop[2] == '\0')
	&& (toupper (tempop[0])	== 'P')
	&& (toupper (tempop[1])	== 'C')) {
	    op->Mode = PCDisp;			/* Program Counter */
	    if (op->Hunk == CurrHunk) {
		op->Value -= (AddrCnt +	2);	/* Adjust displacement */
		op->Hunk = ABSHUNK;
	    }
	} else {
	    op->Mode = ARDisp;			/* Address Register */
	    op->Rn = GetAReg (tempop, strlen (tempop), rloc);
	}
	if (oper[j] != '\0') {          /* Index register is present */
	    if (op->Mode == PCDisp)
		op->Mode = PCDisX;	/* Program Counter indexed */
	    else
		op->Mode = ARDisX;	/* Address Register indexed */
	    if (oper[j]	!= ',')
		Error (op->Loc,	AddrErr);	/* Bad separator */
	    j++;				/* Skip	separator */
	    rloc = op->Loc + j;			/* Start of index */
	    j =	GetField (oper,	j, tempop);	/* Get index register */
	    if (oper[j]	== '\0')
		tempop[strlen(tempop)-1]='\0';  /* Chop parenthesis */
	    else
		Error (rloc, AddrErr);		/* It better be	there */

	    op->Xsize =	GetSize	(tempop);	/* Index register size */
	    if (op->Xsize == Byte) {
		Error (op->Loc+j-1, SizeErr);	/* Must	not be byte */
		op->Xsize = Word;		/* Make	it word	for now	*/
	    }
	    i =	IsRegister (tempop, strlen (tempop));	/* Get register	*/
	    op->Xn = i & 7;			/* Index register number */
	    if ((i >= 0) && (i <= 7))
		op->X =	Dreg;			/* Data	Register */
	    else if ((i	>= 8) && (i <= 15))
		op->X =	Areg;			/* Address Register */
	    else
		Error (rloc, AddrErr);		/* Invalid register */
	}

	if ((op->Hunk >= 0) && (op->Hunk != ABSHUNK))
	    Error (op->Loc, RelErr);	/*  Relocatable	displacement */
	return;
    }

    /* Check to	see whether this could be a register list for MOVEM. */
    i =	0;
    MultFlag = FALSE;
    while(1) {
	if ((ch	= UCoper[i++]) == '\0') {
	    MultFlag = FALSE;
	    break;
	}
	if ((ch	== 'A') || (ch == 'D')) {
	    ch = UCoper[i++];
	    if (ch == '\0') {
		MultFlag = FALSE;
		break;
	    }
	    if ((ch >= '0') && (ch <= '7')) {
		ch = UCoper[i++];
		if (ch == '\0')
		    break;
		if ((ch	== '/') || (ch == '-'))
		    MultFlag = TRUE;
	    } else {
		MultFlag = FALSE;
		break;
	    }
	} else {
	    MultFlag = FALSE;
	    break;
	}
    }
    if (MultFlag) {
	op->Mode = MultiM;
	op->Value = 0;		/* We must still get the mask */
	return;
    }

    op->Value =	GetValue (oper,	op->Loc);   /* Assume absolute mode */
    op->Hunk  =	Hunk2;
    op->Defn  =	DefLine2;
    if ((Hunk2 == ABSHUNK) && (DefLine2	<= LineCount)
    && (op->Value >= -32768) &&	(op->Value <= 32767))
	op->Mode = AbsW;	/* Fits	in a word */
    else
	op->Mode = AbsL;
}



int GetAReg (op, len, loc) char	op[]; int len, loc;
/* Validate an address register	specification.
    Valid specifications are A0	through	A7 , SP, or an EQUR label.
    The	address	register number	will be	returned if it is valid.
    Otherwise, Error will be called, using "loc" for the error
    location (this is its only use), and zero (A0) will	be returned. */
{
    register int i;

    i =	IsRegister (op,	len);		/* Get register	number */
    if ((i >= 8) && (i <= 15))
	return (i - 8);			/* Valid address register */
    else {
	Error (loc, AddrErr);		/* Not an address register */
	return (0);			/* Set to A0 */
    }
}



int IsRegister (op, len) char op[]; int	len;
/* Check whether the current operand is	an address or data register.
    Valid specifications are D0	throuth	D7, A0 through A7, SP,
    or any symbol equated to a register	with the EQUR directive.
    Return values:
	0 through 7 - data registers 0 through 7 respectively
	8 through 15 - address registers 0 through 7 respectively
	-1 - not a recognizable	register
	-2 - Equated register list for MOVEM instruction (REG)	*/
{
    char tempop[MAXLINE];
    register char *s;
    register int  i;

    if (len == 2) {		/* Two-character specification */
	i = toupper (op[0]);
	if ((i == 'S') && (toupper (op[1]) == 'P')) {
	    return (15);		    /* Stack Pointer */
	} else if ((op[1] >= '0') && (op[1] <= '7')) {
	    if (i == 'A') {
		return (op[1] -	'0' + 8);   /* Address Register */
	    } else if (i == 'D') {
		return (op[1] -	'0');       /* Data Register */
	    }
	}
    }
    if (!GotEqur)			/* If we have no EQURs to check	*/
	return (-1);			/*  don't waste any time here.  */
    for	(i = 0,	s = op;	i < len; i++) {
	if (IsOperator (s))
	    return (-1);		/* It sure isn't a label */
	tempop[i] = *s++;
    }
    tempop[i] =	'\0';
    if (ReadSymTab (tempop)) {
	if (Sym->Flags & 0x60) {
	    AddRef (LineCount);		/* Found a register or list */
	    return ((Sym->Flags	& 0x20)	? (int)	Sym->Val : -2);
	}
    }
    return (-1);			/* Not a recognizable register */
}



int GetInstModeSize (Mode) int Mode;
/* Determines the size for the various instruction modes. */
{
    switch (Mode) {
	case ARDisp:
	case ARDisX:
	case PCDisp:
	case PCDisX:
	case AbsW:
	    return (2);
	case AbsL:
	    return (4);
	case MultiM:
	    return (0);		/* Accounted for by code generator */
	case Imm:
	    if (Size ==	Long)
		return (4);
	    else
		return (2);
	default:
	    return (0);
    }
}



int GetMultReg (oper, predec, loc) char	oper[];	int predec, loc;
/* Builds a register mask for the MOVEM	instruction */
{
    register char ch;
    register int  i, j;
    int	T1, T2;		/* Temporary variables for registers */
    int	Range;		/* We're processing a range of registers */
    int	RegType;	/* Register type (0=data, 1=address, 2=none) */
    int	MultExt;	/* The result is built here */

    MultExt = 0;
    Range = FALSE;
    RegType = 2;
    i =	0;

    ch = toupper (oper[i]);
    while (ch != '\0') {
	if (ch == 'A') {
	    if (RegType	== 2)
		RegType	= 1;		/* Address register */
	    else {
		Error (loc, OperErr);
		return(0);
	    }
	} else if (ch == 'D') {
	    if (RegType	== 2)
		RegType	= 0;		/* Data	register */
	    else {
		Error (loc, OperErr);
		return(0);
	    }
	} else if ((ch >= '0') && (ch <= '7')) { /* Register number */
	    if (RegType	!= 2) {
		T2 = (RegType *	8) + (ch - '0');  /* Bit number */
		if (predec)
		    T2 = 15 - T2;	/* Predecrement	reverses mask */
		if (!Range) {
		    MultExt |= (1 << T2);	/* Single register */
		    T1 = T2;	/* Save	number in case it's a range */
		} else {			/* Range of registers */
		    Range = FALSE;
		    if (T1 > T2) {
			j = T1;		/* Swap	registers if backwards */
			T1 = T2;
			T2 = j;
		    }
		    for	(j = T1; j <= T2; j++)
			MultExt	|= (1 << j);	/* Registers in	range */
		}
	    } else {
		Error (loc, OperErr);
		return(0);
	    }
	} else if (ch == '-') {         /* Range indicator */
	    if ((Range == FALSE) && (RegType !=	2) && (i > 0)) {
		RegType	= 2;
		Range =	TRUE;
	    } else {
		Error (loc, OperErr);
		return(0);
	    }
	} else if (ch == '/') {         /* Separator */
	    if ((Range == FALSE) && (RegType !=	2) && (i > 0))
		RegType	= 2;
	    else {
		Error (loc, OperErr);
		return(0);
	    }
	} else {			/* Garbage */
	    Error (loc,	OperErr);
	    return(0);
	}
	ch = toupper (oper[++i]);
    }
    return(MultExt);
}
