/* asmflt.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 "asmopcod.h"

#define TOLOWER(c)	(c | 0x20)	/* works only for alpha inputs */


/* Handle 8087 opcodes, they have the following types:

	Fnoargs:	No arguments at all.
	F2memstk:	0-2 args; memory 4,8 byte | ST,ST(i) | ST(i),ST
			| blank( equiv ST )
	Fstks:		ST(i),ST
	Fmemstk:	memory 4,8 | ST | ST(i) | blank
	Fstk:		ST(i)
	Fmem42: 	memory 4,8 byte
	Fmem842:	memory 2,4,8 bytes
	Fmem4810	memory 4,8,10 bytes | ST(i)
	Fmem2:		memory 2 byte
	Fmem14: 	memory 14 bytes( don't force size )
	Fmem94: 	memory 94 bytes( don't force size )
	Fwait:		Noargs, output WAIT
	Fbcdmem:	memory Bcd
 */



/***	fltwait - output WAIT for 8087 instruction
 *
 *	fltwait (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */

VOID PASCAL CODESIZE
fltwait (
	UCHAR fseg
){
	register SHORT idx;
	char	override;
	register struct psop *pso;

	if (fltemulate) {
		idx = 0;
		/* Check for data and fixup space */
		if (pass2 && (emitcleanq ((UCHAR)(5)) || !fixroom (15)))
			emitdumpdata (0xA1); /* RN */
		if (opctype != FWAIT) {
			override = 0;
			if (fltdsc) {
				pso = &(fltdsc->dsckind.opnd);
				if ((idx = pso->seg) < NOSEG && idx != fseg)
					override = 1;
			}
			if (override)
				emitfltfix ('I',fltfixmisc[idx][0],&fltfixmisc[idx][1]);
			else
				emitfltfix ('I','D',&fltfixmisc[7][1]);
		}
		else {
			emitfltfix ('I','W', &fltfixmisc[8][1]);
			emitopcode(0x90);
		}
	}
	if (fltemulate || cputype&P86 || (cpu & FORCEWAIT)) {
		emitopcode (O_WAIT);
		if (fltemulate && override && idx)
			emitfltfix ('J',fltfixmisc[idx+3][0],&fltfixmisc[idx+3][1]);
	}
}


SHORT CODESIZE
if_fwait()
{
	/* if second byte of opcode is 'N', we don't generate fwait */

	return (TOLOWER(svname.pszName[1]) != 'n');
}



/***	fltmodrm - emit 8087 MODRM byte
 *
 *	fltmodrm (base, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 *	Note   The MODRM byte for 8087 opcode:
 *		M M b b b R / M
 *		M = mode, 3 is for non-memory 8087
 *		b = base opcode. Together with ESC gives 6 bit opcode
 *		R/M memory indexing type
 */


VOID PASCAL CODESIZE
fltmodrm (
	register USHORT	base,
	struct fltrec	  *p
){
	register USHORT mod;

	mod = modrm;
	if (!fltdsc) {

	    if (mod < 8)
		    mod <<= 3;

	    if (mod < 0xC0)
		    mod += 0xC0;
	    /* ST(i) mode */
	    emitopcode ((UCHAR)(mod + base + p->stknum));
	}
	else {

	   emitmodrm ((USHORT)fltdsc->dsckind.opnd.mode, (USHORT)(mod + base),
		      fltdsc->dsckind.opnd.rm);

	   emitrest (fltdsc);
	}
}




/***	fltscan - scan operands and build fltdsc
 *
 *	fltscan (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
fltscan (
	register struct fltrec	*p
){
	register struct psop *pso;

	p->args = FALSE;
	fltdsc = NULL;
	skipblanks ();
	if (ISTERM (PEEKC ())) {
		p->fseg = NOSEG;
		p->stknum = 1;
	}
	else {
		p->args = TRUE;
		p->fseg = DSSEG;
		fltdsc = expreval (&p->fseg);
		pso = &(fltdsc->dsckind.opnd);

		if (pso->mode == 3
		  && !(pso->rm == 0 && opcbase == O_FSTSW && modrm == R_FSTSW
		  && (cputype & (P286|P386))))
			errorc (E_IUR); /* Illegal use of reg */

		if (1 << FLTSTACK & pso->dtype) {
			/* Have ST or ST(i) */
			p->stknum = (USHORT)(pso->doffset & 7);
			if (pso->doffset > 7 || pso->dsign)
				/* # too big */
				errorc (E_VOR);
			if (pso->dsegment || pso->dcontext ||
			    pso->dflag == XTERNAL || pso->mode != 4)
				/* Must have a constant */
				errorc (E_CXP);
			/* This means ST(i) */
			pso->mode = 3;
			oblititem (fltdsc);
			fltdsc = NULL;
		}
		else if (pso->mode == 4){

		    /* pass1 error caused invalide mode assignment,
		       map immdiate to direct, error on pass 2 */

		    if (pass2)
			errorc(E_NIM);

		    pso->mode = 2;
		    if (wordsize == 4)
			pso->mode = 7;
		}

	}
}




/***	fltopcode - process 8087 opcode
 *
 *	routine ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID PASCAL CODESIZE
fltopcode ()
{
	struct fltrec	a;
	USHORT	i;
	register struct psop *pso;

	/* Save opcode name */
	switchname ();
	a.stknum = 0;
	/* Scan 1st arg, if any */
	fltscan (&a);

	if (if_fwait() || (opcbase == O_FNOP && modrm == R_FNOP))
	    fltwait (a.fseg);

	if (fltdsc){
		pso = &(fltdsc->dsckind.opnd);
		emit67(pso, NULL);
	}

	switch (opctype) {
	    case FNOARGS:
		    /* No args allowed */
		    a.stknum = 0;
		    if (opcbase == O_FSETPM && modrm == R_FSETPM) {
			    if (!(cputype&PROT))
				    errorcSYN ();
		    }
		    /* Output escape byte */
		    emitopcode (opcbase);
		    fltmodrm (0, &a);
		    if (a.args)
			    /* Operands not allowed */
			    errorc (E_ECL);
		    break;
	    case FWAIT:
		    a.stknum = 0;
		    if (a.args)
			    /* Operands not allowed */
			    errorc (E_ECL);
		    break;
	    case FSTK:
		    if (TOLOWER(svname.pszName[1]) == 'f' && !a.args) /* ffree w/o arg */
			    errorc(E_MOP);
		    /* Output Escape */
		    emitopcode (opcbase);
		    /* Modrm byte */
		    fltmodrm (0, &a);
		    if (fltdsc)
			    /*Must be ST(i) */
			    errorc (E_IOT);
		    break;
	    case FMEM42:
	    case FMEM842:
	    case FMEM2:
	    case FMEM14:
	    case FMEM94:
	    case FBCDMEM:
		    /* All use a memory operand. Some force size */
		    if (fltemulate && !if_fwait())
			    /* Can't emulate */
			    errorc (E_7OE);
		    if (!fltdsc)
			    /* must have arg */
			    errorc (E_IOT);
		    else {
			emitescape (fltdsc, a.fseg);
			if (opctype == FMEM42) {
			    /* Integer 2,4 byte */
			    forcesize (fltdsc);
			    if (pso->dsize == 4)
				    /* 4 byte */
				    emitopcode (opcbase);
			    else {
				    emitopcode ((UCHAR)(opcbase + 4));
				    if (pso->dsize != 2)
					    errorc (E_IIS);
			    }
			}
			else if (opctype == FMEM842) {
			    /* Int 8,4,2 */
			    forcesize (fltdsc);
			    if (pso->dsize == 2 || pso->dsize == 8)
				    emitopcode ((UCHAR)(opcbase + 4));
			    else {
				    emitopcode (opcbase);
				    if (pso->dsize != 4)
					    errorc (E_IIS);
			    }
			}
			else if ((opctype == FMEM2) || (opctype == FBCDMEM)) {
			    if (opctype == FMEM2)
				if (pso->dsize != 2 && pso->dsize)
				    errorc (E_IIS);
				else {
				    if (cputype & (P286|P386) &&
					opcbase == O_FSTSW && modrm == R_FSTSW &&
					pso->mode == 3 && pso->rm == 0) {
					     opcbase = O_FSTSWAX;
					     modrm = R_FSTSWAX;
				    }
				}
			    else if (pso->dsize != 10 && pso->dsize )
				    errorc (E_IIS);
			    emitopcode (opcbase);
			}
			else
				emitopcode (opcbase);
			if ((pso->mode == 3 || pso->mode == 4) &&
			    (opcbase != O_FSTSWAX || modrm != R_FSTSWAX))
				/* Only memory operands */
				errorc (E_IOT);
			if (opctype == FMEM842 && pso->dsize == 8)
				if (TOLOWER(svname.pszName[2]) == 'l')
					fltmodrm (5, &a);
				else
					fltmodrm (4, &a);
			else
				fltmodrm (0, &a);
		    }
		    break;
	    case FSTKS:
		    if (!a.args)
			    /* Operand required */
			    errorc (E_MOP);
		    else if (fltdsc)
			    /* Must be stack */
			    errorc (E_IOT);
		    else {
			    /* ESC */
			    emitopcode (opcbase);
			    /* ST(i) */
			    fltmodrm (0, &a);
			    if (PEEKC () != ',')
				    error (E_EXP,"comma");
				    /* Must have 2 args */
			    /* Get 2nd operand */
			    SKIPC ();
			    fltscan (&a);
			    pso = NULL;
			    if (!a.args || fltdsc)
				    errorc (E_IOT);
			    if (a.stknum)
				    errorc (E_OCI);
		    }
		    break;
	    case FMEM4810:
		    /* Fwait */
		    if (TOLOWER(svname.pszName[1]) == 'l')
			/* FLD */
			if (!fltdsc) {/* Have ST(i) */
				if (!a.args) /* fld w/o arg */
					errorc(E_MOP);
				emitopcode (opcbase);
				fltmodrm (0, &a);
			}
			else {
				/* Any segment override */
				emitescape (fltdsc, a.fseg);
				if (pso->dsize == 10) {
					/* Have temp real */
					emitopcode ((UCHAR)(opcbase + 2));
					fltmodrm (5, &a);
				}
				else {
					/* Have normal real */
					forcesize (fltdsc);
					if (pso->dsize == 8)
						emitopcode ((UCHAR)(opcbase + 4));
					else {
						emitopcode (opcbase);
						if (pso->dsize != 4)
							errorc (E_IOT);
					}
					fltmodrm (0, &a);
				}
			}
		    else if (!fltdsc) {
			    /* Have ST(i) */
			    /* Have FSTP */
			    if (!a.args)
				    errorc( E_IOT );
			    emitopcode ((UCHAR)(opcbase + 4));
			    fltmodrm (0, &a);
		    }
		    else {
			    emitescape (fltdsc, a.fseg);
			    /* Any segment override */
			    if (pso->dsize == 10) {
				    /* Have temp real */
				    emitopcode( (UCHAR)(opcbase + 2) );
				    fltmodrm (4, &a);
			    }
			    else {
				    /* Have normal real */
				    forcesize (fltdsc);
				    if (pso->dsize == 8)
					    emitopcode( (UCHAR)(opcbase + 4) );
				    else
					    emitopcode (opcbase);
				    fltmodrm (0, &a);
			    }
		    }
		    break;
	    case F2MEMSTK:
		    if (!a.args) {
			    /* Have ST(1),ST */
			    emitopcode( (UCHAR)(opcbase + 6) );
			    if ((i = modrm & 7) > 3)
				    modrm = i^1;
			    fltmodrm (0, &a);
		    }
		    else if (!fltdsc) {/* Have stacks */
			    if (a.stknum == 0)
				    emitopcode (opcbase);
			    else {
				    /* Might need to reverse R bit */
				    if ((modrm & 7) > 3) /* Have FSUBx FDIVx */
					    modrm ^= 1;
				    emitopcode( (UCHAR)(opcbase + 4) );
				    /* D bit is set */
			    }
			    /* Save in case ST(i) */
			    a.stk1st = a.stknum;
			    if (PEEKC () != ',')
				    /* Must have , */
				    error (E_EXP,"comma");
			    /* Get 2nd operand */
			    SKIPC ();
			    fltscan (&a);
			    if (fltdsc)
				    /* not stack */
				    errorc (E_IOT);
			    if (a.args && a.stknum && a.stk1st)
				    errorc (E_IOT);
			    if (a.stk1st)
				    a.stknum = a.stk1st;
			    fltmodrm (0, &a);
		    }
		    else {  /* Have real memory */
			    forcesize (fltdsc);
			    emitescape (fltdsc, a.fseg);
			    if (pso->dsize == 8)
				    emitopcode( (UCHAR)(opcbase + 4) );
			    else {
				    emitopcode (opcbase);
				    if (pso->dsize != 4)
					    errorc (E_IIS);
			    }
			    fltmodrm (0, &a);
		    }
		    break;
	    case FMEMSTK:
		    if (!fltdsc)/* Have ST(i) */
			    if (TOLOWER(svname.pszName[1]) == 's') {
				    /* Special case */
				    if (!a.args)
					    errorc( E_IOT );
				    emitopcode( (UCHAR)(opcbase + 4) );
			    }
			    else
				    emitopcode (opcbase);
		    else {
			    /* Have real memory */
			    emitescape (fltdsc, a.fseg);
			    forcesize (fltdsc);
			    if (pso->dsize == 8)
				    emitopcode( (UCHAR)(opcbase + 4) );
			    else {
				    emitopcode (opcbase);
				    if (pso->dsize != 4)
					    errorc (E_IOT);
			    }
		    }
		    fltmodrm (0, &a);
		    break;
	}
	if (fltdsc)
		oblititem (fltdsc);
}