/* asmrec.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"


struct recpars {
	SYMBOL FARSYM    *recptr;
	SYMBOL FARSYM    *curfld;
	OFFSET	recval;
};

struct recdef {
	USHORT	fldcnt;
	USHORT	reclen;
	SYMBOL FARSYM    *recptr;
	SYMBOL FARSYM    *curfld;
	short	i;
};

VOID  PASCAL CODESIZE  recordspec PARMS((struct recdef *));
VOID  PASCAL CODESIZE  recfill PARMS((struct recpars *));


static OFFSET svpc = 0;
static struct duprec FARSYM *pDUPCur;

/***	checkvalue - insure value will fit in field
 *
 *	checkvalue (width, sign, magnitude)
 *
 *	Entry	width = width of field
 *		sign = sign of result
 *		magnitude= magnitude of result
 *	Exit	none
 *	Returns adjusted value
 *	Calls	none
 */


OFFSET PASCAL CODESIZE
checkvalue (
	register SHORT width,
	register char sign,
	register OFFSET mag
){
	register OFFSET mask;

	if (width == sizeof(OFFSET)*8)
		mask = OFFSETMAX;
	else
		mask = (1 << width) - 1;
	if (!sign) {
		if (width < sizeof(OFFSET)*8)
			if (mag > mask) {
				errorc (E_VOR);
				mag = mask;
			}
	}
	else {
		mag = OFFSETMAX - mag;
		mag++;
		if (width < sizeof(OFFSET)*8)
			if ((mag ^ OFFSETMAX) & ~mask) {
				errorc (E_VOR);
				mag = mask;
			}
	}
	return (mag & mask);
}




/***	recordspec - parse record field specification fld:wid[=val]
 *
 *	recordspec (p);
 *
 *	Entry	p = pointer to record definition structure
 *	Exit
 *	Returns
 *	Calls
 */


VOID	 PASCAL CODESIZE
recordspec (
	register struct recdef	  *p
){
	register SYMBOL FARSYM	*fldptr;
	register USHORT  width;
	register struct symbrecf FARSYM *s;
	char	sign;
	OFFSET	mag;

	getatom ();
	if (*naim.pszName) {

	    if (!symFet ())
		    symcreate (M_DEFINED | M_BACKREF, RECFIELD);
	    else {
		    if (symptr->symu.rec.recptr != p->recptr ||
			M_BACKREF & symptr->attr)

			errorn (E_SMD);

		    symptr->attr |= M_BACKREF;
	    }
	    crefdef ();
	    s = &(symptr->symu.rec);
	    if (symptr->symkind != RECFIELD)
		    /* Not field */
		    errorn (E_SDK);
	    else {
		    /* Ptr to field */
		    fldptr = symptr;

		    if (!p->curfld)
			p->recptr->symu.rsmsym.rsmtype.rsmrec.reclist = fldptr;
		    else
			p->curfld->symu.rec.recnxt = fldptr;

		    /* New last field */
		    p->curfld = fldptr;
		    s->recptr = p->recptr;
		    s->recnxt = NULL;
		    p->fldcnt++;
		    if (NEXTC () != ':')
			    error (E_EXP,"colon");

		    /* Get field width */
		    width = exprconst ();

		    if (skipblanks () == '=') {
			    SKIPC ();
			    mag = exprsmag (&sign);
		    }
		    else {
			    sign = FALSE;
			    mag = 0;
		    }

		    if (width == 0 ||
			p->reclen + width > wordsize*8) {
			    STRNFCPY (save, p->curfld->nampnt->id);
			    /*Overflow */
			    error (E_VOR, save);
			    width = 0;
		    }

		    s->recinit = checkvalue (width, sign, mag);
		    s->recmsk = (OFFSET)((1L << width) - 1);
		    s->recwid = width;
		    p->reclen += width;
	    }
	}
}




/***	recorddefine - parse record definition
 *
 *	recorddefine ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
recorddefine ()
{
	struct recdef	  a;
	struct symbrecf FARSYM *s;
	register SHORT cbRec;

	a.reclen = 0;
	a.fldcnt = 0;
	checkRes();
	if (!symFet ()) {
		/* Make record */
		symcreate (M_DEFINED | M_BACKREF, REC);
	}
	else
		symptr->attr |= M_BACKREF;

	/* This is def */
	crefdef ();
	if (symptr->symkind != REC)
		/* Wasn't record */
		errorn (E_SDK);
	else {
		/* Leftmost bit of record */
		a.reclen = 0;
		/*No record filed yet */
		a.curfld = NULL;
		/* In case error */
		symptr->symu.rsmsym.rsmtype.rsmrec.reclist = NULL;
		/* Pointer to record name */
		a.recptr = symptr;
		/* Parse record field list */
		BACKC ();
		do {
			SKIPC ();
			recordspec (&a);

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

		/* Length of record in bits */
		cbRec = a.reclen;

		a.recptr->length = cbRec;
		a.recptr->offset = (OFFSET)((1L << cbRec) - 1);
		a.recptr->symtype = (cbRec > 16 )? 4: ((cbRec > 8)? 2: 1);
		/* # of fields in record */
		a.recptr->symu.rsmsym.rsmtype.rsmrec.recfldnum = a.fldcnt;
		/* 1st field */
		a.curfld = a.recptr->symu.rsmsym.rsmtype.rsmrec.reclist;
	}

	/* For all the fields adjust the shift (stored in offset),
	 * initial value and mask so the last field is right justified */

	while (a.curfld) {

		s = &(a.curfld->symu.rec);

		/* Start of field */
		cbRec = (cbRec > s->recwid)? cbRec - s->recwid: 0;

		/* Shift count */
		a.curfld->offset = cbRec;
		s->recinit <<= cbRec;
		s->recmsk  <<= cbRec;

		a.curfld = s->recnxt;	/* Next field */
	}
}




/***	recfill - get initial value for field in list
 *
 *	recfill (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID	PASCAL CODESIZE
recfill (
	register struct recpars	*p
){
	register char cc;
	struct symbrecf FARSYM *s;
	char	sign;
	OFFSET	mag, t;

	if (!p->curfld) {
		/* More fields than exist */
		errorc (E_MVD);
	}
	else {
		s = &(p->curfld->symu.rec);

		if ((cc = skipblanks ()) == ',' || cc == '>') {
			/* Use default value */
			t = s->recinit;
		}
		else {
			/* Have an override */
			mag = exprsmag (&sign);
			t = checkvalue (s->recwid, sign, mag);
			/* Scale value */
			t <<= p->curfld->offset;
		}
		/* Add in new field */

		if (s->recwid)
			p->recval = (p->recval & ~(s->recmsk)) | t;

		p->curfld = s->recnxt;
	}
}




/***	recordparse - parse record specification
 *
 *	recordparse ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


OFFSET	PASCAL CODESIZE
recordparse ()
{
	struct recpars	 a;
	struct symbrecf FARSYM *s;


	a.recptr = symptr;		/* Current record */

	if (PEEKC () != '<')
		error (E_EXP,"<");	/* Must have < */
	else
		SKIPC ();

	/* No value yet */
	a.recval = 0;
	/* 1st field in record */
	a.curfld = a.recptr->symu.rsmsym.rsmtype.rsmrec.reclist;

	BACKC ();
	do {			    /* Fill in values */
		SKIPC ();
		recfill (&a);

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

	while (a.curfld) {
		/* Fill in remaining defaults */
		s = &(a.curfld->symu.rec);
		if (s->recwid)
			a.recval = (a.recval & ~(s->recmsk)) | s->recinit;
		a.curfld = s->recnxt;
	}
	if (NEXTC () != '>')	    /* Must have > */
		error (E_EXP,">");

	return (a.recval);	    /* Value of record */
}




/***	recordinit - parse record allocation
 *
 *	recordinit ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID	PASCAL CODESIZE
recordinit ()
{
	initflag = TRUE;
	strucflag = FALSE;	/* This is RECORD init */
	recptr = symptr;

	optyp = TDB;

	if (symptr->symtype == 2)
		optyp = TDW;
#ifdef V386
	else if (symptr->symtype == 4)
		optyp = TDD;
#endif

	datadefine ();
	initflag = FALSE;
}




/***	nodecreate - create one DUP record
 *
 *	nodecreate ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


struct duprec FARSYM * PASCAL CODESIZE
nodecreate ()
{
	register struct duprec FARSYM *node;

	node = (struct duprec FARSYM *)falloc (sizeof (*node), "nodecreate");
	node->rptcnt = 1;
	node->itemcnt = 0;
        node->duptype.dupnext.dup = NULL;
	node->itemlst = NULL;
	node->dupkind = NEST;
	return (node);
}




/***	strucdefine - define structure
 *
 *	strucdefine ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID	PASCAL CODESIZE
strucdefine ()
{
	checkRes();
	if (!symFet()) {

		/* Make STRUC */
		symcreate (M_DEFINED | M_BACKREF, STRUC);
	}
	else
		symptr->attr |= M_BACKREF;

	/* This is definition */
	crefdef ();
	if (symptr->symkind != STRUC)
	    errorn (E_SDK);

	else {
	    symptr->attr |= M_BACKREF;
	    recptr = symptr;		/* Pointer to STRUC name */
	    recptr->symu.rsmsym.rsmtype.rsmstruc.strucfldnum = 0;

	    if (! pass2) {
		recptr->symu.rsmsym.rsmtype.rsmstruc.type = typeIndex;
		typeIndex += 3;

		if (pStrucCur)
		    pStrucCur->alpha = recptr;
		else
		    pStrucFirst = recptr;

		pStrucCur = recptr;
	    }

	    /* No labeled fields yet */
	    recptr->symu.rsmsym.rsmtype.rsmstruc.struclist = NULL;

	    /* Delete old STRUC */
	    scandup (recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody, oblitdup);
	    recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody = nodecreate ();

	    struclabel = NULL;	    /* No named fields */
	    strucprev = NULL;	    /* No body yet */
	    count = 0;		    /* No fields yet */
	    strucflag = TRUE;	    /* We are STRUC not RECORD */

	    svpc = pcoffset;	    /* Save normal PC */
	    pcoffset = 0;	    /* Relative to STRUC begin */

	    swaphandler = TRUE;     /* Switch to STRUC builder */
	    handler = HSTRUC;
	}
}




/***	strucbuild - build the struc block
 *
 *	strucbuild ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
strucbuild ()
{
	labelflag = FALSE;
	optyp = 0;
	getatom ();

#ifndef FEATURE

	if (naim.pszName[0] == '%' && naim.pszName[1] == 0) {  /* expand all text macros */
	    *begatom = ' ';
	    substituteTMs();
	    getatom();
	}

#endif

	/* First, look for IF, ELSE & ENDIF stuff */

	if (fndir () && (opkind & CONDBEG)) {
		firstDirect();
	}
	else if (generate && *naim.pszName) {

	    /* next, classify the current token, which is either
	     * and ENDS, data label or data name */

	    if (optyp == 0 || !fndir2 ()){

		/* first token was a label */

		switchname ();
		getatom ();
		optyp = 0;

		if (!fndir2 ())
		    errorc(E_DIS);

		labelflag = TRUE;   /* Do have label */
		switchname ();
	    }

	    if (optyp == TENDS) {

		if (!symFet () || symptr != recptr)
		    errorc(E_BNE);

		/* Have end of STRUC */

		handler = HPARSE;
		swaphandler = TRUE;
		strucflag = FALSE;
		recptr->symu.rsmsym.rsmtype.rsmstruc.strucfldnum =
			/* # of fields */
			recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody->itemcnt;

		if (pcoffset & 0xFFFF0000)
		    errorc (E_DVZ);
		recptr->symtype = pcoffset;	/* Size of STRUC */
		recptr->length = 1;

		pcdisplay ();
		/* Restore PC */
		pcoffset = svpc;
	    }
	    else if (! (optyp >= TDB && optyp <= TDW))
		errorc (E_DIS);

	    else {	/* Have another line of body */

		if (!strucprev) {
		    /* Make first node */
		    strucprev = nodecreate ();
		    recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody->
                            duptype.dupnext.dup = strucprev;
		}
		else {
			strucprev->itemlst = nodecreate ();
			strucprev = strucprev->itemlst;
		}
		recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody->itemcnt++;
		/* Add new data line to STRUC */
		datadefine ();
		strucprev->decltype = optyp;
	    }
	}
	if (generate) {
	    if (!ISTERM (skipblanks()))
	       errorc (E_ECL);
	}
	listline ();
}

struct srec {
	struct duprec FARSYM  *curfld;
	USHORT	curlen;
};




/***	createduprec - create short data record with null data
 *
 *	createduprec ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


struct duprec FARSYM * PASCAL CODESIZE
createduprec ()
{
	register struct duprec FARSYM *newrec;

	newrec = (struct duprec FARSYM	*)falloc (sizeof (*newrec), "createduprec");
	newrec->rptcnt = 1;
	/* Not a DUP */
	newrec->itemcnt = 0;
	newrec->itemlst = NULL;
	newrec->dupkind = ITEM;
	/* this also clears ddata and dup in other variants of struc */
	newrec->duptype.duplong.ldata = NULL;
	newrec->duptype.duplong.llen = 1;
	return (newrec);
}




/***	strucerror - generate structure error message
 *
 *	strucerror ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


struct duprec  FARSYM * PASCAL CODESIZE
strucerror (
	SHORT	code,
	struct duprec	FARSYM *node
){
	errorc (code);
	/* Get rid of bad Oitem */
	oblitdup (node);
	/* Make up a dummy */
	return (createduprec ());
}




/***	strucfill - fill in structure values
 *
 *	strucfill ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID	 PASCAL CODESIZE
strucfill ()
{
    register struct duprec  FARSYM *pOver;
    register struct duprec  FARSYM *pInit;
    register char *cp;
    char    svop;
    short   i, cbCur;
    struct datarec drT;


    if (!pDUPCur) {
	errorc (E_MVD);
	return;
    }

    if (skipblanks() == ',' || PEEKC() == '>') {
	/* use default values */
	pOver = createduprec ();
    }
    else {
	/* Save operation type */
	svop = optyp;
	/* Original directive type */
	optyp = pDUPCur->decltype;

	pOver = datascan (&drT);    /* Get item */

	optyp = svop;
        pInit = pDUPCur->duptype.dupnext.dup;
	cbCur = pInit->duptype.duplong.llen;

	if (pOver->dupkind == NEST)
	    /* Bad override val */
	    pOver = strucerror (E_ODI, pOver);

	else if (pDUPCur->itemcnt != 1 || pInit->itemcnt)
	    /* Can't override field */
	    pOver = strucerror (E_FCO, pOver);

	else if (pOver->dupkind != pInit->dupkind) {

	    if (pInit->dupkind == ITEM)
		cbCur = pInit->duptype.dupitem.ddata->dsckind.opnd.dsize;
	}

	if (pOver->dupkind == LONG) {
	    /* If too long, truncate */

	    if ((i = pOver->duptype.duplong.llen) < cbCur) {

		/* Space fill short (after reallocating more space) */

		if (!(pOver->duptype.duplong.ldata =
		  realloc (pOver->duptype.duplong.ldata, cbCur)))
		    memerror("strucfil");

		cp = pOver->duptype.duplong.ldata + i;

		for (; i < cbCur; i++)
		    *cp++ = ' ';
	    }
	    else if (pOver->duptype.duplong.llen > cbCur)
		errorc (E_OWL);

	    pOver->duptype.duplong.llen = cbCur;
	}
	if ((pOver->dupkind == pInit->dupkind) &&
	    (pOver->dupkind == ITEM) && !errorcode)

	    pOver->duptype.dupitem.ddata->dsckind.opnd.dsize =
	      pInit->duptype.dupitem.ddata->dsckind.opnd.dsize;
    }
    pDUPCur = pDUPCur->itemlst;

    if (strucoveride)
	strclastover->itemlst = pOver;
    else
	strucoveride = pOver;

    strclastover = pOver;
}





/***	strucparse - parse structure specification
 *
 *	strucparse ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


struct duprec FARSYM * PASCAL CODESIZE
strucparse ()
{
	/* No items yet */
	strucoveride = NULL;
	recptr = symptr;

	if (skipblanks () != '<')
		error (E_EXP,"<");

	/* 1st default field */
        pDUPCur = recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody->duptype.dupnext.dup;
	initflag = FALSE;
	strucflag = FALSE;
				      /* Build list of overrides */
	do {
		SKIPC ();
		strucfill ();

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

	initflag = TRUE;
	strucflag = TRUE;
	while (pDUPCur) {/* Fill rest with overrides */
	       /* Make dummy entry */
		strclastover->itemlst = createduprec ();
		strclastover = strclastover->itemlst;
		/* Advance to next field */
		pDUPCur = pDUPCur->itemlst;
	}
	if (PEEKC () != '>')
		error (E_EXP,">");
	else
		SKIPC ();
	return (recptr->symu.rsmsym.rsmtype.rsmstruc.strucbody);
}




/***	strucinit - initialize structure
 *
 *	strucinit ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID	PASCAL CODESIZE
strucinit ()
{
	initflag = TRUE;
	strucflag = TRUE;
	recptr = symptr;
	optyp = TMACRO;
	datadsize[TMACRO - TDB] = recptr->symtype;
	datadefine ();
	initflag = FALSE;
	strucflag = FALSE;
}