/* asmequ.c -- microsoft 80x86 assembler
**
** microsoft (r) macro assembler
** copyright (c) microsoft corp 1986.  all rights reserved
**
** randy nevin
**
** 10/90 - Quick conversion to 32 bit by Jeff Spencer
*/

#include <stdio.h>
#include "asm86.h"
#include "asmfcn.h"
#include "asmctype.h"
#include "asmmsg.h"

/* EQU statement :	 There are 3 basic kinds of EQU:

	1.	To expression
	2.	To symbol( synonym )
	3.	All others are text macros

 */

VOID PASCAL CODESIZE assignconst ( USHORT );

char isGolbal;		/* flag indicating if equ symbol was global */

/***	assignvalue - assign value to symbol
 *
 *	assignvalue ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
assignvalue ()
{
	struct eqar	a;
	register struct psop *pso;
	register SYMBOL FARSYM *sym;
	register DSCREC *dsc;

	switchname ();

	if (createequ(EXPR)) {

	    sym = symptr;
	    sym->attr |= M_BACKREF;	    /* Set we have DEFINED */

	    dsc = (equflag)? itemptr: expreval (&nilseg);
	    pso = &(dsc->dsckind.opnd);

	    if (noexp)
		    errorc (E_OPN);

	    /*	If error, set undefined */
	    if (errorcode && errorcode != E_RES)
		    sym->attr &= ~(M_DEFINED | M_BACKREF);

	    if (equflag && equdef) {
		    if (sym->offset != pso->doffset ||
			sym->symu.equ.equrec.expr.esign != pso->dsign ||
			sym->symsegptr != pso->dsegment)
			    muldef ();
	    }
	    /* If = involves forward, don't set BACKREF */
	    if (M_FORTYPE & pso->dtype){
		    sym->attr &= ~M_BACKREF;

		    if (sym->attr & M_GLOBAL)
			sym->attr &= ~M_GLOBAL;
	    }
	    if (pso->mode != 4 &&
	       !(pso->mode == 0 && pso->rm == 6) &&
	       !(pso->mode == 5 && pso->rm == 5) ||
		pso->dflag == XTERNAL)

		    /* Not right kind of result */
		    errorc (E_IOT);

	    sym->symsegptr = pso->dsegment;
	    sym->symu.equ.equrec.expr.eassume = NULL;
	    if (pso->dtype == M_CODE)
		    sym->symu.equ.equrec.expr.eassume = pso->dcontext;

	    sym->length = 0;
	    sym->offset = pso->doffset;
	    /* Note: change sign */
	    sym->symu.equ.equrec.expr.esign = pso->dsign;
	    sym->symtype = pso->dsize;

	    if ((pso->dtype == M_RCONST || !pso->dsegment) &&
		!(M_PTRSIZE & pso->dtype))
		    sym->symtype = 0;

	    if (fNeedList) {

		listbuffer[1] = '=';
		listindex = 3;
		if (sym->symu.equ.equrec.expr.esign)
			listbuffer[2] = '-';

		offsetAscii (sym->offset);
		copyascii ();
	    }
	    dfree ((char *)dsc );
	}
}




/***	createequ - create entry for equ
 *
 *	flag = createequ (typ, p)
 *
 *	Entry	typ = type of equ
 *	Exit
 *	Returns TRUE if equ created or found of right type
 *		FALSE if equ not created or found and wrong type
 *	Calls	labelcreate, switchname
 */


UCHAR PASCAL CODESIZE
createequ (
	UCHAR typ
){

	equsel = typ;
	switchname ();
	labelcreate (0, EQU);

	/* Make sure not set set fields if wrong type, flag to caller */
	if (symptr->symkind != EQU || symptr->symu.equ.equtyp != typ) {

		errorn (E_SDK);
		return (FALSE);
	}
	else {
		switchname ();
		isGolbal = 0;

		if (equsel == ALIAS){	/* lose public on pointer to alias */

		      isGolbal = symptr->attr & M_GLOBAL;
		      symptr->attr &= ~M_GLOBAL;
		}

		if (typ != EXPR)
		    symptr->symsegptr = NULL;

		return (TRUE);
	}
}




/***	equtext - make remainder of line into text form of EQU
 *
 *	equtext ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls	error, skipblanks
 */


VOID PASCAL CODESIZE
equtext (
	USHORT cb
){
    register UCHAR *pFirst, *pT, *pOld;

    if (createequ (TEXTMACRO)) {

	/* find end of line & then delete trailing blanks */

	pFirst = lbufp;

	if (cb == ((USHORT)-1)) {
	    for (pT = pFirst; *pT && *pT != ';'; pT++);

	    for (; pT > pFirst && ISBLANK (pT[-1]) ; pT--);

	    lbufp = pT;
	    cb = pT - pFirst;
	}

	pOld = symptr->symu.equ.equrec.txtmacro.equtext;

	pT = nalloc((USHORT)(cb+1), "equtext");
	pT[cb] = NULL;

	symptr->symu.equ.equrec.txtmacro.equtext =
		    (char *) memcpy(pT, pFirst, cb);
	if (pOld)
	    free (pOld);

	copystring (pT);
    }
}




/***	equdefine - define EQU
 *
 *	equdefine ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
equdefine ()
{
	register SYMBOL FARSYM *pSY;
	struct eqar	a;
	register char *p;
	USHORT cb;
	UCHAR opc = FALSE;

	listbuffer[1] = '=';
	switchname ();
	a.dirscan = lbufp;

	if (PEEKC () == '<') { /* look for <text macro> */

		p = getTMstring();
		a.dirscan = lbufp;
		lbufp = p;
		equtext ((USHORT)(a.dirscan - p - 1));
		lbufp = a.dirscan;
		return;
	}

	getatom ();
	if ((*naim.pszName == '$') && (naim.pszName[1] == 0))
		*naim.pszName = 0;
	/*Need to check if 1st atom is an operator, otherwise
	  will make OFFSET an alias instead of text. */
	if (fnoper ())
		*naim.pszName = 0;

	if (*naim.pszName && ISTERM (PEEKC ()) && !(opc = opcodesearch ())) {

	    /* Alias */
	    if (createequ (ALIAS)) {

		pSY = symptr;

		if (!symsrch ()) {
		    if (pass2)
			    /* Undefined */
			    errorn (E_SND);
		    /* Don't know symbol yet */
		    pSY->symu.equ.equrec.alias.equptr = NULL;
		}
		else {
		    /* Alias symbol is DEFINED */

		    pSY->attr = pSY->attr&~M_BACKREF | symptr->attr&M_BACKREF;

		    if (!pSY->symu.equ.equrec.alias.equptr)
			    pSY->symu.equ.equrec.alias.equptr = symptr;

		    if (pSY->symu.equ.equrec.alias.equptr != symptr) {
			    /* This is multiple definition */
			    symptr = pSY;
			    muldef ();
		    }
		    else {
			    /* See if good */
			    if (pSY = chasealias (pSY))
				pSY->attr |= isGolbal;
		    }
		}
	    }
	}
	else {
	    /* Must be text form or expr */
#ifdef BCBOPT
	    goodlbufp = FALSE;
#endif
	    lbufp = a.dirscan;
	    xcreflag--;
	    emittext = FALSE;

	    if (opc) {		    /* quick patch to allow i.e. SYM equ MOV */
		equtext ((USHORT)-1);
		emittext = TRUE;
		xcreflag++;
		return;
	    }

	    a.dsc = expreval (&nilseg);
	    emittext = TRUE;
	    xcreflag++;

	    /* So don't see double ref */
	    /* force text if OFFSET or : */
	    if (a.dsc->dsckind.opnd.mode != 4 &&
		!(a.dsc->dsckind.opnd.mode == 0 && a.dsc->dsckind.opnd.rm == 6) &&
		!(a.dsc->dsckind.opnd.mode == 5 && a.dsc->dsckind.opnd.rm == 5) ||

		 (errorcode && errorcode != E_SND && errorcode != E_RES) ||

		 (M_EXPLOFFSET|M_EXPLCOLON|M_HIGH|M_LOW) & a.dsc->dsckind.opnd.dtype ||

		 a.dsc->dsckind.opnd.seg != NOSEG ||
		 a.dsc->dsckind.opnd.dflag == XTERNAL) {

		    /* Not good expression */
		    if (errorcode != E_LTL)
			    errorcode = 0;
		    dfree ((char *)a.dsc );
		    lbufp = a.dirscan;
		    equtext ((USHORT)-1);
	    }
	    else {
		    /* This is expression */
		    itemptr = a.dsc;
		    switchname ();
		    equflag = TRUE;
		    assignvalue ();
		    equflag = FALSE;
	    }
	}
}




/***	definesym - define symbol from command line
 *
 *	definesym (p);
 *
 *	Entry	*p = symbol text
 *	Exit	symbol define as EQU with value of 0
 *	Returns none
 *	Calls
 */


void PASCAL
definesym (
	UCHAR *p
){
	struct eqar	a;

	fCheckRes++;
	fSkipList++;

#ifdef BCBOPT
	goodlbufp = FALSE;
#endif

	strcpy (lbufp = save, p);
	getatom ();
	if ((PEEKC() == 0 || PEEKC() == '=') && *naim.pszName) {
		if (PEEKC() == '=')
			SKIPC();

		switchname ();
		equtext ((USHORT)-1);
	}
	else
		errorcode++;

	fSkipList--;
	fCheckRes--;
}



/***	defwordsize - define @WordSize using definesym()
 *
 *	defwordsize ( );
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls	    definesym()
 */


VOID PASCAL
defwordsize ()
{
    static char wstext[] = "@WordSize=0D";

    wstext[10] = wordsize + '0';
    definesym(wstext);
    symptr->attr |= M_NOCREF;	/* don't cref @WordSize */

}




/***	chasealias - return value of alias list
 *
 *	symb = chasealias (equsym);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


SYMBOL FARSYM * PASCAL CODESIZE
chasealias (
	SYMBOL FARSYM *equsym
){
	register SYMBOL FARSYM *endalias;

	endalias = equsym;

	do {
	    /*	Must check to see if EQU to self */

	    if (endalias->symu.equ.equrec.alias.equptr == equsym) {

		    endalias->symu.equ.equrec.alias.equptr = NULL;
		    errorc (E_CEA);
		    return (NULL);
	    }

	    endalias = endalias->symu.equ.equrec.alias.equptr;

	    if (!endalias) {
		errorn (E_SND);
		return(NULL);	    /* This is undefined */
	    }

	} while (!(endalias->symkind != EQU ||
		   endalias->symu.equ.equtyp != ALIAS));

	/* Now check final is ok - Only constant allowed */

	if (endalias->symkind == EQU &&
	    endalias->symu.equ.equtyp != EXPR){

		errorc (E_IOT);
		return (NULL);
	}

	return (endalias);
}



/***	getTMstring - process a string or text macro
 *		      used by substring, catstring, sizestring, & instring
 *
 *	char * getTMstring ();
 *
 *	Entry	lbufp points to beginning of string or TM
 *	Exit
 *	Returns Pointer to string or equtext of TM
 *	Calls
 */


char * PASCAL CODESIZE
getTMstring ()
{
    char    cc;
    register char * p;
    static char   tms [] = "text macro";
    static char   digitsT[33];
    char * ret = NULL;


    skipblanks ();

    p = lbufp;

    if ((cc = *p) == '<' ) {

	ret = p + 1;

	while (*(++p) && (*p != '>'))
	    ;

	if (!*p)
	    error(E_EXP,tms);
	else
	    *(p++) = 0;

	lbufp = p;

    }
    else if (test4TM()) {
	ret = symptr->symu.equ.equrec.txtmacro.equtext;

    }
    else if (cc == '%') {

	pTextEnd = (char *) -1;
	lbufp = p+1;
        *xxradixconvert (exprconst(), digitsT) = NULL;
	return (digitsT);
    }
    else
	error(E_EXP,tms );

    return (ret);
}



/***	substring - process the subStr directive
 *
 *	substring ();
 *
 *	Syntax:
 *
 *	  <ident> subStr <subjectString> , <startIndex> {, <length> }
 *
 *	  Defines <ident> as a TEXTMACRO.
 *	  <subjectString> must be a TEXTMACRO or a string: " ", < >, ' '
 *	  <startIndex>: constant expression between 1 and strlen(subjectString)
 *	  Optional <length>: constant expression between 0 and
 *			     (strlen(subjectString) - startIndex + 1)
 *
 *	Entry	lbufp points to beginning of subjectString
 *	Exit
 *	Returns
 *	Calls	getTMstring
 */


VOID PASCAL CODESIZE
substring ()
{
    struct eqar   a;
    char	  *p;
    register USHORT cb;
    char	  cc;
    register char *subjtext;
    USHORT	    slength;
    USHORT	    startindex = 0;

    listbuffer[1] = '=';
    switchname ();

    /* First find string or text macro */

    if (!(subjtext = getTMstring () ))
	return;

    cb = strlen(subjtext);

    /* then check for start index */

    if (skipblanks () == ',') {
	SKIPC ();
	startindex = exprconst() - 1;	/* get start index */

    } else
	error(E_EXP,"comma");


    /* then check for length */

    if (skipblanks () == ',') {
	SKIPC ();

	slength = exprconst();		/* get start index */

    } else
	slength = cb - startindex;

    if (startindex > cb || slength > cb - startindex) {
	errorc (E_VOR);
	return;
    }

    p = lbufp;

    lbufp = subjtext + startindex;	/* set lbufp to start of substring */
    equtext(slength);			/* end of string index */

    lbufp = p;

    if (errorcode && symptr)
	symptr->attr &= ~(M_DEFINED | M_BACKREF);
}



/***	catstring - process the catstr directive
 *
 *	catstring ();
 *
 *	Syntax:
 *
 *	  <ident> catStr <subjectString> {, <subjectString> } ...
 *
 *	  Defines <ident> as a TEXTMACRO.
 *	  Each <subjectString> must be a TEXTMACRO or a string: " ", < >, ' '
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
catstring ()
{
    struct eqar   a;
    register USHORT cb;
    char	  *subjtext;
    char	  resulttext[LBUFMAX];
    USHORT	  cbresult = 0;
    register char *p = resulttext;

    listbuffer[1] = '=';
    switchname ();
    *p = '\0';

    /* First find string or text macro */

    do {

	if (!(subjtext = getTMstring () ))
	    break;

	cb = strlen (subjtext);
	cbresult += cb;

	if(cbresult > LBUFMAX) {
	    errorc(E_LTL);
	    break;
	}

	memcpy (p, subjtext, cb + 1);	/* + 1 copies NULL */
	p += cb;

    } while (skipblanks() && NEXTC () == ',');

    p = --lbufp;
    lbufp = resulttext;
    equtext(cbresult);
    lbufp = p;

    if (errorcode)
	symptr->attr &= ~(M_DEFINED | M_BACKREF);
}



/***	assignconst - like assignvalue, only takes value as argument
 *
 *	assignconst (cb);
 *
 *	Entry	USHORT cb == value to assign
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
assignconst (
	USHORT cb
){
    register SYMBOL FARSYM *sym;
    struct eqar   a;

    if (createequ(EXPR)) {

	sym = symptr;

	if (errorcode)
	    sym->attr &= ~(M_DEFINED | M_BACKREF);
	else
	    sym->attr |= M_BACKREF;	    /* Set we have DEFINED */

	sym->symsegptr = NULL;
	sym->symu.equ.equrec.expr.eassume = NULL;
	sym->length = 0;
	sym->offset = cb;

	sym->symu.equ.equrec.expr.esign = 0;
	sym->symtype = 0;

	if (fNeedList) {

	    listbuffer[1] = '=';
	    listindex = 3;

	    offsetAscii (sym->offset);
	    copyascii ();
	}
    }
}


/***	sizestring - process the sizeStr directive
 *
 *	sizestring ();
 *
 *	Syntax:
 *
 *	  <ident> sizeStr <subjectString>
 *
 *	  Defines <ident> as a EXPR.
 *	  The <subjectString> must be a TEXTMACRO or a string: " ", < >, ' '
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
sizestring ()
{
    register USHORT cb = 0;
    char	  *p;

    switchname ();

    /* First find string or text macro */

    if (p = getTMstring () )
	cb = strlen (p);

    assignconst (cb);
}



/***	instring - process the instr directive
 *
 *	instring ();
 *
 *	Syntax:
 *
 *	  <ident> inStr { <startIndex> } , <subjectString> , <searchString>
 *
 *	  Defines <ident> as a TEXTMACRO.
 *	  <startIndex>: constant expression between 1 and strlen(subjectString)
 *	  <subjectString> must be a TEXTMACRO or a string: " ", < >, ' '
 *	  <searchString> must be a TEXTMACRO or a string: " ", < >, ' '
 *
 *	Entry	lbufp points to beginning of subjectString
 *	Exit
 *	Returns
 *	Calls	getTMstring
 */

char * strstr();


VOID PASCAL CODESIZE
instring ()
{
    register char *p;
    register USHORT cb = 0;
    register char cc;
    char	  *subjtext;
    char	  *searchtext;
    USHORT	    startindex = 1;

    switchname ();

    /* First find start index */

    p = lbufp;

    if ((cc = *p) != '"' && cc != '\'' && cc != '<' && !test4TM ()) {

	lbufp = p;
	startindex = exprconst();	/* get start index */

	if (lbufp != p)
	    if (skipblanks () == ',')
		SKIPC ();
	    else
		error(E_EXP,"comma");

    } else
	lbufp = p;

    if (subjtext = getTMstring () ) {

	cb = strlen(subjtext);

	if (startindex < 1 || startindex > cb)
	    errorc (E_VOR);

	if (skipblanks () == ',')
	    SKIPC ();
	else
	    error(E_EXP,"comma");


	/* then check for searchtext */

	if (searchtext = getTMstring () ) {

	   p = subjtext + startindex - 1;
	   if (p = strstr (p, searchtext))
	       cb = p - subjtext + 1;
	   else
	       cb = 0;
	}
    }

    assignconst (cb);
}