/* asmopc.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 <string.h>
#include "asm86.h"
#include "asmfcn.h"
#include "asmctype.h"
#include "asmopcod.h"

static SHORT CODESIZE nolong(struct psop *);
VOID CODESIZE pmovx(struct parsrec *);
VOID CODESIZE psetcc(struct parsrec *);
VOID CODESIZE pbit(struct parsrec *);
VOID CODESIZE pbitscan(struct parsrec *);
CODESIZE checkwreg(struct psop *);
VOID PASCAL CODESIZE pclts (void);

#define M_ESCAPE  (M_PRELJMP | M_PCALL | M_PJUMP | M_PRETURN | M_PINT | M_PARITH | \
		   M_PINOUT | M_PLOAD | M_PSTR | M_PESC | M_PBOUND | M_PARSL)

#define M_ERRIMMED (M_PSHIFT | M_PARITH | M_PINCDEC | M_PCALL | M_PJUMP |    \
		    M_PMOV | M_PSTR | M_PRELJMP | M_PGENARG | M_PXCHG |      \
		    M_PBOUND | M_PCLTS | M_PDESCRTBL | M_PDTTRSW | M_PARSL | \
		    M_PARPL | M_PVER)


/* EMITcall decides what type of calljump is present and outputs
      the appropriate code. Coding of last 4 args to EMITcall:

        DIRto:	Direct to different segment(inter)
        DIRin:	DIRect in same segment(intra)
        INDto:	Indirect to different segment(inter)
        INDin:	Indirect in same segment(intra)

*/



/***	emitcall - emit call
 *
 *	emitcall (dirin, dirto, indin, indto, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
emitcall (
         UCHAR       dirin,
         UCHAR       dirto,
         UCHAR       indin,
         UCHAR       indto,
         struct parsrec  *p
         )
{
    register struct psop *pso;  /* parse stack operand structure */
    char fNop = FALSE;

    pso = &(p->dsc1->dsckind.opnd);
    if (!isdirect(pso)) {

        /* Have indexing? */
        if (pso->dsize == 0) {
            /* make [BX] be word */
            pso->dsize = wordsize;
            pso->dtype |= xltsymtoresult[DVAR];
        } else if (pso->dsize >= CSFAR) {
            errorc (E_ASD);
            pso->dsize = wordsize;
            /* Data only, force word */
        }
    }

    if ((M_DATA & pso->dtype) && pso->dflag == UNDEFINED)
        pso->dflag = KNOWN;

    if (pso->dsize == CSNEAR ||
        (pso->dflag == UNDEFINED && !(M_PTRSIZE & pso->dtype))) {

#ifndef FEATURE
        if (regsegment[CSSEG] == pFlatGroup)
            pso->dcontext = pFlatGroup;
#endif
        if (regsegment[CSSEG] != pso->dcontext &&
            pso->dflag != XTERNAL)
            errorc (E_JCD);  /* Can't go near to dif assume */

        pso->dsize = wordsize;
        pso->dtype |= M_SHRT;
        emitopcode (dirin);
    } else if (pso->dsize == CSFAR) {

        if (M_FORTYPE & pso->dtype) /* Forward far */
            errorc (E_FOF); /* Couldn't guess */

        pso->fixtype = FPOINTER;
        pso->dsize = wordsize;

        if (pso->dsegment) {

            /* target has different segment size */

            pso->dsize = pso->dsegment->symu.segmnt.use32;

            if (pso->dsize != wordsize) {

                if (!(M_BACKREF & pso->dsegment->attr))
                    errorc (E_FOF); /* Forward mixed type */

                emitsize(0x66);

                if (wordsize == 4) {    /* set modes so you get the */
                    pso->mode = 0;      /* correct OFFSET size */
                    pso->rm = 6;
                    fNop++;         /* 16:32 -> 0x66 16:16 */
                } else {
                    pso->fixtype = F32POINTER;
                    pso->mode = 8;
                    pso->rm = 5;
                }
            }
        }
        pso->dsize += 2;
        emitopcode (dirto);

    } else {

#ifdef V386
        emit67(pso, NULL);
#endif

        if ((pso->dsize == wordsize) || (pso->dsize == wordsize+2)) {

            /* Indirect */
#ifdef V386
            /* if mode is through register, then it must be a near
             * call, so we can tell if its a foreign mode call */

            if (pso->dsize != wordsize && pso->mode == 3)
                emitsize(0x66);
#endif
            emitescape (p->dsc1, p->defseg);
            emitopcode (255);        /*  must use defseg([BP]) */

            if (pso->dsize == wordsize || pso->mode == 3)
                /* Near indirect */
                emitmodrm ((USHORT)pso->mode, (USHORT)(indin>>3), pso->rm);
            else
                /* Far indirect */
                emitmodrm ((USHORT)pso->mode, (USHORT)(indto>>3), pso->rm);
        }

#ifdef V386
        else if (pso->dsize == 2 || pso->dsize == 6) {

            /* indirect foreign mode call */
            /* in 16 bit mode normal near and far get done by the
             * above, and only 32 bit mode far gets here.  for 32
             * bit normal only 16 bit near.  the latter seems a bit
             * useless....*/

            emitsize(0x66);
            emitescape (p->dsc1, p->defseg);
            emitopcode (255);       /*  must use defseg([BP]) */

            if (pso->dsize == 2)
                /* Near indirect */
                emitmodrm ((USHORT)pso->mode, (USHORT)(indin>>3), pso->rm);
            else
                /* Far indirect */
                emitmodrm ((USHORT)pso->mode, (USHORT)(indto>>3), pso->rm);
        }
#endif
        else
            /* Bad size */
            errorc (E_IIS);
    }

    emitrest (p->dsc1);

    if (fNop)
        emitnop();
}




/***	movesegreg - emit move to/from segment register
 *
 *	movesegreg (first, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
movesegreg (
           char    first,
           struct parsrec  *p
           )
{
    register struct psop *pso1;  /* parse stack operand structure */
    register struct psop *pso2;  /* parse stack operand structure */
    DSCREC         *t;

    if (!first) {
        if (p->dsc1->dsckind.opnd.mode != 3 && impure)
            /* MOV cs:mem,segreg */
            errorc (E_IMP);
        t = p->dsc1;
        p->dsc1 = p->dsc2;
        p->dsc2 = t;
    }
    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    if ((pso2->dsize | wordsize) == 6)
        emitsize(0x66);

    emitopcode ((UCHAR)(first? 142: 140));
    errorimmed (p->dsc2);

#ifdef V386
    rangecheck (&pso1->rm, (UCHAR)((cputype&P386)?5:3));
#else
    rangecheck (&pso1->rm, (UCHAR)3);
#endif
    if ((pso2->mode == 3)
        && (pso2->dsegment->symu.regsym.regtype == SEGREG))
        errorc (E_WRT);    /* MOV segreg,segreg not allowed */

    if (pso2->sized && !pso2->w)
        errorc (E_IIS);

    if (first && (pso1->rm == CSSEG))
        /* CS illegal */
        errorc (E_CSI);


    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}

#ifdef V386

/***	movecreg - emit move to/from control/debug/test register
 *
 *	movecreg (first, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */

VOID
PASCAL
CODESIZE
movecreg (
         char    first,
         struct parsrec  *p
         )
{
    register struct psop *pso1;  /* parse stack operand structure */
    register struct psop *pso2;  /* parse stack operand structure */
    UCHAR opbase;

    if ((cputype&(P386|PROT)) != (P386|PROT)) {
        errorc(E_WRT);
        return;
    }
    emitopcode (0x0F);

    pso1 = &(p->dsc1->dsckind.opnd);
    opbase = 0x22;

    if (first)

        pso2 = &(p->dsc2->dsckind.opnd);
    else {
        opbase = 0x20;
        pso2 = pso1;
        pso1 = &(p->dsc2->dsckind.opnd);
    }

    if ((pso2->dsegment->symkind != REGISTER)
        || (pso2->dsegment->symu.regsym.regtype != DWRDREG))
        errorc (E_OCI);

    if ((pso1->rm&030) == 020) /* test register */
        opbase += 2;

    emitopcode((UCHAR)(opbase + (pso1->rm >> 3)));
    emitmodrm((USHORT)3, (USHORT)(pso1->rm & 7), (USHORT)(pso2->rm & 7));

    if (pso2->mode != 3)     /* only allowed to from register */
        errorc(E_MBR);
}

#endif


/***	emitmove - emit code for MOV reg and MOV accum
 *
 *	emitmove (opcode, first, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
emitmove (
         UCHAR       opc,
         char    first,
         struct parsrec  *p
         )
{
    DSCREC         *t;
    char    accummove;
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */


    accummove = (opc == 160);
    if (!first) {
        t = p->dsc1;
        p->dsc1 = p->dsc2;
        p->dsc2 = t;
    }
    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    emit66 (pso1, pso2);

    if ((pso1->dsize != pso2->dsize) && pso2->sized)
        errorc (E_OMM);

    emitopcode ((UCHAR)(opc + ((accummove != first)? 2: 0) + pso1->w));
    errorimmed (p->dsc2);
    if (!accummove)
        emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}




/***	moveaccum - move to/from accumulator and direct address
 *
 *	moveaccum (first, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
moveaccum (
          char    first,
          struct parsrec  *p
          )
{
    if (!first && p->dsc1->dsckind.opnd.mode != 3 && impure)
        errorc (E_IMP);
    emitmove (160, first, p);
}




/***	movereg - emit general move between register and memory
 *
 *	movereg (first, p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
movereg (
        char    first,
        struct parsrec  *p
        )
{
    register struct psop *pso2; /* parse stack operand structure */
    char    flag;

    flag = FALSE;
    pso2 = &(p->dsc2->dsckind.opnd);
    /* Is not special */
    if (pso2->mode == 3)
        /* 2nd is reg */
        switch (pso2->dsegment->symu.regsym.regtype) {
            case SEGREG:
                /* Catch 2nd is SEGREG */
                movesegreg (FALSE, p);
                return;
#ifdef V386
            case CREG:
                /* Catch 2nd is SEGREG */
                movecreg (FALSE, p);
                return;
#endif
        }
    if (p->dsc1->dsckind.opnd.mode != 3 && impure)
        errorc (E_IMP);
    emitmove (136, first, p);
}




/***	segdefault - return default segment for operand
 *
 *	seg = segdefault (op);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


USHORT
PASCAL
CODESIZE
segdefault (
           register char goo
           )
{
    register USHORT  defseg;
    register char op;

    defseg = NOSEG;
    if (1 << goo & xoptoseg[opctype])
        defseg = DSSEG;

    if (opctype == PSTR) {
        op = (opcbase == O_CMPS || opcbase == O_LODS || opcbase == O_OUTS);
        defseg = ((goo == FIRSTDS) != op)?  ESSEG: DSSEG;
    }
    return (defseg);
}



/***	errorover -
 *
 *	errorover (seg);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
errorover (
          char    seg
          )
{
    if (seg != ESSEG && seg != NOSEG)
        errorc (E_OES);
}

/***	checksize - check for memory s byte and immed is word
 *
 *	checksize (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


SHORT
PASCAL
CODESIZE
checksize (
          struct parsrec  *p
          )
{
    OFFSET  off;
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    if (pso1->sized) {

        /* Only set dsc2->w if dsc2 has no size. Set
         * dsc1->w to dsc2->w, not TRUE(WORD). [BX],WRD PTR 5 */

        if (!pso2->sized)
            pso2->w = pso1->w;
    } else
        pso1->w = pso2->w;

    if (pso2->fixtype == FCONSTANT) {  /* check for constant overflow */

        off = (pso2->doffset > 0x7fffffff)? -(long)pso2->doffset: pso2->doffset;

        if ((pso1->dsize == 1 && off > 0xff && off < 0xff00) ||
            (pso1->dsize == 2 && off > 0xffff))
            errorc (E_VOR);
    }
    /* check fixup'ed constants with implied sizes */

    if ((pso1->sized && pso1->dsize != 2) &&
        (pso2->dtype & (M_SEGMENT) ||
         pso2->fixtype == FGROUPSEG || pso2->fixtype == FBASESEG))

        errorc (E_OMM);

    if (!(pso1->sized || pso2->sized))
        errorc (E_OHS);

    /*  Also need to set <w> field if operand 1 sized */
    if (pso1->sized) {/* Force size */
        pso2->dsize = pso1->dsize;
        pso2->w = pso1->w;
    }
    if (pso2->dsize == 1 && pso2->dflag == XTERNAL
        && pso2->fixtype != FHIGH)
        /*    makes sure linker puts out correct stuff */
        pso2->fixtype = FLOW;

    return(0);
}




/***	opcode - process opcode and emit code
 *
 *	opcode ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


SHORT
PASCAL
CODESIZE
opcode ()
{
    struct parsrec  a;
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    long        opctypemask;    /* 1L << opctype */
    char        leaflag;

    a.dsc1 = a.dsc2 = NULL;
    pso1 = pso2 = NULL;
    impure = FALSE;

    if (xoptoargs[opctype] != NONE) {
        /* Evaulate 1st arg */
        a.dirscan = lbufp;
        /* In case JMP should be SHORT */
        a.defseg = (unsigned char)segdefault (FIRSTDS);
        a.dsc1 = expreval (&a.defseg);

        if (noexp && (xoptoargs[opctype] == ONE
                      || xoptoargs[opctype] == TWO))
            errorc(E_MDZ);

        if ((pso1 = &(a.dsc1->dsckind.opnd))
            && pso1->dtype & M_STRUCTEMPLATE)
            errorc(E_IOT);

        /*  Give error so sizes >wordsize and not CODE don't get thru */
        if (!((opctypemask = 1L << opctype) & (M_PLOAD | M_PCALL | M_PJUMP | M_PDESCRTBL))
            && ((pso1->dsize > wordszdefault) &&
                (pso1->dsize < CSFAR)))
            if (pso1->mode != 4) {
                errorc (E_IIS);
                /* No error if cst */
                /* Don't allow CSFAR or CSNEAR if not CODE opcode */
                pso1->dsize = wordszdefault;
            }

        if (!(opctypemask & (M_PRELJMP | M_PCALL | M_PJUMP)))
            if (pso1->dsize >= CSFAR)
                errorc (E_IIS);

        if (!(opctypemask & M_ESCAPE))
            emitescape (a.dsc1, a.defseg);

        if (opctypemask & M_ERRIMMED)
            /* 1st operand not immediate */
            errorimmed (a.dsc1);

        if (!(opctypemask & (M_PMOV | M_PSTACK)))
            /* Give error if segment reg used */
            errorsegreg (a.dsc1);

        if (opctypemask & (M_PRETURN | M_PINT | M_PESC | M_PENTER))
            forceimmed (a.dsc1);

        if ((xoptoargs[opctype] == TWO) || ((opctype == PSTR) &&
                                            ((opcbase == O_MOVS) || (opcbase == O_CMPS) ||
                                             (opcbase == O_INS) || (opcbase == O_OUTS)))) {

            /* Two args or 2 arg string oper */

            if (NEXTC () != ',')
                error (E_EXP,"comma");

            leaflag = (opcbase == O_LEA)? TRUE: FALSE;
            a.defseg = (unsigned char)segdefault (SECONDDS);
            a.dsc2 = expreval (&a.defseg);

            if (noexp)
                errorc(E_MDZ);

            if ((pso2 = &(a.dsc2->dsckind.opnd))
                && pso2->dtype & M_STRUCTEMPLATE)
                errorc(E_IOT);

            /* IF LEA(215), then never segment prefix */
            if ((opcbase != O_LEA) && (opctype != PSTR))
                emitescape (a.dsc2, a.defseg);

            if (opctypemask & (M_PLOAD | M_PXCHG | M_PESC |
                               M_PSTR | M_PBOUND | M_PARSL | M_PARPL))
                errorimmed (a.dsc2);

            if (opctype != PMOV)
                /* Give error if SEGREG and not a MOV opcode */
                errorsegreg (a.dsc2);

            if (!(opctypemask & (M_PLOAD | M_PBOUND)) &&
                (pso2->dsize > 2 &&
#ifdef V386
                 ( !(cputype & P386) || pso2->dsize != 4) &&
#endif
                 pso2->dsize < CSFAR))

                /* Give error so sizes > 2 and not CODE don't
                 * get thru */

                if (pso2->mode != 4)
                    errorc (E_IIS);

            if (pso2->dsize >= CSFAR && !leaflag)
                /*    Don't allow CSFAR or CSNEAR if not
                      code opcode. But allow LEA since
                      it is untyped anyway. */
                errorc (E_IIS);
        }
    }

#ifdef V386
    /* for most instructions, the 386 0x66 prefix is appropriate.
     * for some classes, we either never allow it, or do some
     * special handling specific to the instruction. */

    if (cputype & P386) {
        switch (opctype) {

            default:

                emit67(pso1, pso2);
                emit66(pso1, pso2);
                break;

            case PMOV:
            case PMOVX:
            case PLOAD:
            case PSHIFT:
            case PSTACK:
            case PSTR:
            case PARPL:
            case PDTTRSW:
            case PDESCRTBL:
                emit67(pso1, pso2);
                break;

            case PCALL:
            case PJUMP:
            case PRELJMP:
            case PENTER:
            case PNOARGS:
            case PESC:
            case PRETURN:
            case PINT:
            case PINOUT:
            case PARITH:
                break;
        }
    }
#endif
    switch (opctype) {
        case PNOARGS:
            pnoargs ();
            break;
        case PJUMP:
        case PRELJMP:
            preljmp (&a);
            break;
        case PSHIFT:
            pshift (&a);
            break;
        case PSTACK:
            pstack (&a);
            break;
        case PARITH:
            parith (&a);
            break;
        case PBOUND:
            pbound (&a);
            break;
        case PENTER:
            penter (&a);
            break;
        case PCLTS:
            pclts ();
            break;
        case PDESCRTBL:
            pdescrtbl (&a);
            break;
        case PDTTRSW:
            pdttrsw (&a);
            break;
        case PVER:
            pver (&a);
            break;
        case PARSL:
            parsl (&a);
            break;
        case PARPL:
            parpl (&a);
            break;
        case PRETURN:
            preturn (&a);
            break;
        case PINCDEC:
            pincdec (&a);
            break;
        case PINT:
            pint (&a);
            break;
        case PINOUT:
            pinout (&a);
            break;
        case PLOAD:
            pload (&a);
            break;
        case PCALL:
            emitcall (232, 154, 16, 24, &a);
            break;
        case PMOV:
            pmov (&a);
            break;
        case PGENARG:
            pgenarg (&a);
            break;
        case PXCHG:
            pxchg (&a);
            break;
        case PESC:
            pesc (&a);
            break;
        case PREPEAT:
            prepeat (&a);
            break;
        case PSTR:
            pstr (&a);
            break;
        case PXLAT:
            pxlat (&a);
            break;
#ifdef V386
        case PMOVX:
            pmovx (&a);
            break;
        case PSETCC:
            psetcc (&a);
            break;
        case PBIT:
            pbit (&a);
            break;
        case PBITSCAN:
            pbitscan (&a);
            break;
#endif
    }
    if (a.dsc1)
        dfree ((char *)a.dsc1 );
    if (a.dsc2)
        dfree ((char *)a.dsc2 );

    if (pcsegment) {

        pcsegment->symu.segmnt.hascode = 1;
    }
    return (0);
}




/***	pnoargs - no arguments
 *
 *	pnoargs ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


#ifdef V386

UCHAR stackOps[] = {O_PUSHA, O_PUSHAD,
    O_POPA,  O_POPAD,
    O_PUSHF, O_PUSHFD,
    O_POPF,  O_POPFD,
    O_IRET,  O_IRETD,
    NULL
};

#endif

VOID
PASCAL
CODESIZE
pnoargs ()
{
    /* some no argument instructions have an implied arg which determines
     * whether to do the 386 66 prefix.  that this is the case is encoded
     * in the modrm in the op code table.  -Hans  */

#ifdef V386
    if (modrm != 0 && modrm <= 4 && modrm != wordsize) {

        emitsize(0x66);

        if (strchr(stackOps, (UCHAR) opcbase))
            errorc (E_ONW);
    }
#endif
    emitopcode (opcbase);
    if (opcbase == O_AAM || opcbase == O_AAD)
        /* emit modrm byte for AAD/AAM* */
        emitopcode (modrm);
}


/***	preljmp - Relative jump -128..+127
 *
 *	preljmp (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
preljmp (
        struct parsrec *p
        )
{
    register struct psop *pso1; /* parse stack operand structure */
    register SHORT cPadNop;
    SHORT rangeisshort;

#ifdef V386
    SHORT maybelong;
#else
    #define maybelong 0
#endif

    pso1 = &(p->dsc1->dsckind.opnd);

#ifdef V386
    maybelong = (cputype & P386) && !nolong(pso1) && pso1->dsize != CSFAR;
#endif
    rangeisshort = shortrange(p);
    cPadNop = 0;

    if (opcbase == O_JMP) {

        if (pso1->dtype & M_SHRT ||
            rangeisshort && pso1->dflag != XTERNAL) {

            opcbase += 2;
            if (rangeisshort == 2 &&
                !(pso1->dtype & M_SHRT)) {

                cPadNop = wordsize;
                errorc(E_JSH);

                if (M_PTRSIZE & pso1->dtype && pso1->dsize == CSFAR)
                    cPadNop += 2;
            }
        } else {   /* Is normal jump */
            emitcall (opcbase, 234, 32, 40, p);
            return;
        }
    }

    if (!(M_CODE & pso1->dtype))
        errorc (E_ASC);

    /* an extrn may have no segment with it but still be near */

    if (pso1->dsegment != pcsegment && !(maybelong && !pso1->dsegment))
        errorc (E_NIP);

    if (pso1->dtype & (M_HIGH | M_LOW))
        errorc (E_IOT);

    if (M_SHRT & pso1->dtype) {
        if (pass2 && !rangeisshort)
            errorc (E_JOR);
    } else if (!rangeisshort && !maybelong)
        error (E_JOR, (char *)NULL);    /* common pass1 error */

#ifdef V386
    if (maybelong && !(M_SHRT & pso1->dtype) &&
        (!rangeisshort || pso1->dflag == XTERNAL)) {

        /* 386 long conditional branches */
        emitopcode(0x0f);
        emitopcode((UCHAR)(0x80 | (opcbase&0xf)));

        pso1->dtype |= M_SHRT;
        emitrest(p->dsc1);
        return;
    }
#endif
    emitopcode (opcbase);

    if (pso1->dflag == XTERNAL) {       /* EXTERNAL jump */
        pso1->dsize = 1;
        pso1->fixtype = FLOW;       /* SHORT to EXTERNAL */
        pso1->dtype |= M_SHRT;     /* One byte result */

        emitOP (pso1);
    } else
        emitopcode ((UCHAR)pso1->doffset);

    while (--cPadNop > 0)
        emitnop();
}

#ifdef V386

/* most 386 conditional jumps can take a long or short form.  these can
 * only take a short form */

static
SHORT
CODESIZE
nolong(
      register struct psop *pso1
      )
{
    switch (opcbase) {
        case O_JCXZ:
        case O_LOOP:
        case O_LOOPZ:
        case O_LOOPNZ:
    #ifdef V386

            pso1->dtype |=  M_SHRT;
            pso1->dtype &=  ~M_PTRSIZE;

            /* allow `loop word ptr label' for cx|ecx overide */

            if (modrm && modrm != wordsize ||
                pso1->sized && pso1->dsize != wordsize &&
                (pso1->dsize == 4 || pso1->dsize == 2)) {

                pso1->dtype = (USHORT)((pso1->dtype & ~M_DATA) | M_CODE);
                emitsize(0x67);
            }
    #endif
            return(1);

        default:
            return(0);
    }
}

#endif

/***	shortrange - check range of short jump
 *
 *	flag = shortrange (p);
 *
 *	Entry
 *	Exit
 *	Returns 1 for short jump, not shortened
 *		2 for forward label shortened
 *		0 for not short jmp
 *	Calls
 */


SHORT
PASCAL
CODESIZE
shortrange (
           struct parsrec  *p
           )
{
    register struct psop *pso1; /* parse stack operand structure */
    register OFFSET disp;

    pso1 = &(p->dsc1->dsckind.opnd);

    if (pso1->dtype & M_PTRSIZE
#ifdef V386
        && !((cputype & P386) && (pso1->dsize == CSNEAR))
#endif
       )
        if (opcbase == O_JMP) {
            if (!isdirect(pso1))
                return (0);
        } else
            errorc (E_IIS|E_WARN1);

    if (pso1->dflag == XTERNAL && pso1->dsize == CSNEAR)
        return (1);

    if (pso1->dsegment == pcsegment && M_CODE&pso1->dtype &&
        pso1->dflag != UNDEFINED) {

        if (pso1->dflag == XTERNAL)
            return (1);

        if (pcoffset + 2 < pso1->doffset) {

            /* Forward */
            disp = (pso1->doffset - pcoffset) - 2;
            CondJmpDist = disp - 127;

            /* Get displace, only jump shorten for explicid
             * forward jumps */

            if (disp < 128)

                if (pso1->dflag == KNOWN ||
                    opcbase == O_JMP || !(cputype&P386) ||
                    (cputype&P386 && pso1->dtype & M_SHRT)) {

                    pso1->doffset = disp;

                    if (pso1->dflag == KNOWN)
                        return(1);
                    else
                        return (2);
                } else
                    errorc(E_JSH);
        } else {

            /* Backwards jump */

            disp = (pcoffset + 2) - pso1->doffset;
            CondJmpDist = disp - 128;
            if (disp < 129) {
                pso1->doffset = 256 - disp;
                return (1);
            }
        }
    }

    return (FALSE);
}



/***	pshift - shift opcodes
 *
 *	pshift (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pshift (
       struct parsrec *p
       )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    DSCREC  *op3;

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    if (impure)
        errorc (E_IMP);
#ifdef V386

    /* Shift/rotate opcodes */

    if (pso1->dsize >= 2 && pso1->dsize != wordsize)
        emitsize(0x66);

    /* parse 3rd operand for SHLD and SHRD */
    /* note that we wont have even gotten here if not 386 */

    if (opcbase == O_SHRD || opcbase == O_SHLD) {

        if (pso1->dsize != pso2->dsize)
            errorc (E_OMM);

        pso2->dsegment = NULL;      /* for checksize */
        checksize (p);
        emitopcode(0x0f);
        checkwreg(pso2);
        if (NEXTC() == ',') {
            op3 = expreval (&nilseg);
            if (op3->dsckind.opnd.mode == 3 && op3->dsckind.opnd.rm == 1 && !op3->dsckind.opnd.w)
                emitopcode((UCHAR)(opcbase | 1));
            else {
                forceimmed (op3);
                emitopcode(opcbase);
            }
            emitmodrm ((USHORT)pso1->mode, (USHORT)(pso2->rm & 7), pso1->rm);
            /* Emit any effective address */
            emitrest (p->dsc1);
            /* and the immediate if appropriate */
            if (op3->dsckind.opnd.mode == 4)
                emitrest (op3);
        } else error(E_EXP,"comma");
        return;
    }
#endif
    if (pso2->mode == 3 && pso2->rm == 1 && pso2->dsize == 1)
        /* Have CL now */
        emitopcode ((UCHAR)(0xD2 + pso1->w));
    /* * 1st byte * */
    else {
        /* Shift count is 1 */
        forceimmed (p->dsc2);
        if (pso2->doffset == 1)
            /* * 1st byte */
            emitopcode ((UCHAR)(0xD0 + pso1->w));
        else if (cputype == P86)
            errorc (E_IOT);
        else {
            if (pso2->doffset > 0xFF)
                errorc (E_VOR);
            emitopcode ((UCHAR)(0xC0 + pso1->w));
        }
    }
    /* Must have size or error */
    forcesize (p->dsc1);
    emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
    /* Emit any effective address */
    emitrest (p->dsc1);
    if ((cputype != P86) && (pso2->doffset != 1))
        emitrest (p->dsc2);
}

#ifdef V386

/***	pmovx - 386 movzx, movsx operators
 *
 */
VOID
CODESIZE
pmovx(
     struct parsrec *p
     )
{

    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    checkwreg(pso1);
    if (pso2->mode == 4)
        errorc(E_IOT);

    if (pso1->dsize != wordsize)
        emitsize(0x66);

    if (pso2->sized && pso2->dsize != 1 && (pso1->dsize>>1 != pso2->dsize))
        errorc(E_IIS);

    emitopcode(0x0f);
    emitopcode((UCHAR)(opcbase|pso2->w));
    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}

/***	psetcc - 386 setle, seto, etc
 *
 */
VOID
CODESIZE
psetcc(
      struct parsrec *p
      )
{

    register struct psop *pso1; /* parse stack operand structure */
    pso1 = &(p->dsc1->dsckind.opnd);

    if (pso1->dsize != 1)
        errorc(E_IIS);

    emitopcode(0x0f);
    emitopcode(modrm);
    emitmodrm ((USHORT)pso1->mode, 0, pso1->rm);
    emitrest (p->dsc1);
}

/***	pbit -- 386 bit test and set, complement or reset
 *
 */
VOID
CODESIZE
pbit(
    register struct parsrec *p
    )
{

    register struct psop *pso1;
    struct psop *pso2;

    pso1 = &(p->dsc1->dsckind.opnd);

    emitopcode(0x0f);

    if (pso1->mode == 4)
        errorc(E_NIM);

    pso2 = &(p->dsc2->dsckind.opnd);

    if (pso2->mode == 4) {
        emitopcode(0xBA);
        emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
        emitrest (p->dsc1);
        emitrest (p->dsc2);
        forcesize (p->dsc1);
        byteimmcheck (pso2);
    } else if (pso2->mode == 3) {
        static UCHAR byte2[] = {0xA3, 0xAB, 0xB3, 0xBB};
        emitopcode(byte2[modrm&3]);
        emitmodrm ((USHORT)pso1->mode, pso2->rm, pso1->rm);
        checkmatch (p->dsc2, p->dsc1);
        emitrest (p->dsc1);
    } else
        errorc(E_IOT);
}

/***	pbitscan -- 386 bit scan forward, reverse
 *
 */
VOID
CODESIZE
pbitscan(
        register struct parsrec *p
        )
{

    register struct psop *pso2;
    pso2 = &(p->dsc2->dsckind.opnd);

    checkwreg (&p->dsc1->dsckind.opnd);

    if (pso2->mode == 4)
        errorc (E_NIM);

    checkmatch (p->dsc1, p->dsc2);

    emitopcode(0x0f);
    emitopcode(modrm);
    emitmodrm ((USHORT)pso2->mode, p->dsc1->dsckind.opnd.rm, pso2->rm);
    emitrest (p->dsc2);
}

#endif /* V386 */

/***	parith - arithmetic operators
 *
 *	parith (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
parith (
       register struct parsrec *p
       )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    DSCREC      *op1;

    pso1 = &(p->dsc1->dsckind.opnd);

    /* note that opcbase is the same for IMUL and IDIV--thus this was
     * trying to accept immediates. modrm has the right stuff, strangely */

    if (opcbase == O_IMUL && (modrm == R_IMUL) &&
        (PEEKC () == ',') && (cputype != P86)) {

        /* IMUL reg | ea,imm */
        SKIPC ();
        if (pso1->dsize != 2 && pso1->dsize != 4)
            errorc (E_BRI);
        p->defseg = (unsigned char)segdefault (SECONDDS);
        p->dsc2 = expreval (&p->defseg);
        pso2 = &(p->dsc2->dsckind.opnd);
        if (PEEKC () == ',') {
            SKIPC ();
            if (pso2->sized && ((pso2->dsize != 2 && pso2->dsize != 4)
                                || pso2->dsize != pso1->dsize))
                errorc (E_IIS);
            /* IMUL reg,ea,immed */
#ifdef V386
            emit67 (pso1, pso2);
            emit66 (pso1, pso2);
#endif
            op1 = p->dsc1;
            p->dsc1 = p->dsc2;
            pso1 = pso2;
            p->dsc2 = expreval (&nilseg);
            pso2 = &(p->dsc2->dsckind.opnd);
            forceimmed (p->dsc2);
            emitescape (p->dsc1, p->defseg);
            emitopcode ((UCHAR)(IMUL3 + 2 * pso2->s));
            emitmodrm ((USHORT)pso1->mode, op1->dsckind.opnd.rm, pso1->rm);
            emitrest (p->dsc1);
            pso2->w = !pso2->s; /* shorten to byte if necessary */
            if (!pso2->w)
                byteimmcheck(pso2);
            /* force size immediate size to match op 1 */
            pso2->dsize = op1->dsckind.opnd.dsize;
            emitrest (p->dsc2);
            dfree ((char *)op1 );
        }
#ifdef V386
        else if (pso2->mode != 4 && (cputype & P386)) {
            /* IMUL reg, reg/mem */
            if (pso1->dsize != pso2->dsize && pso2->sized)
                errorc (E_OMM);
            emit67 (pso1, pso2);
            emit66 (pso1, pso2);
            emitescape (p->dsc2, p->defseg);
            emitopcode(0x0f);
            emitopcode(0xaf);
            emitmodrm(pso2->mode, pso1->rm, pso2->rm);
            emitrest(p->dsc2);
        }

#endif /* V386 */
        else {
            /* IMUL reg,immed */
#ifdef V386		/* recompute immediate size based op 1 size not word size */

            if (!(pso2->dflag & (UNDEFINED|FORREF|XTERNAL))
                && pso2->fixtype == FCONSTANT
                && pso2->doffset & 0x8000)
                if (pso1->dsize == 2)
                    pso2->s = (char)((USHORT)(((USHORT) pso2->doffset & ~0x7F ) == (USHORT)(~0x7F)));
                else
                    pso2->s = (char)((OFFSET)((pso2->doffset & ~0x7F ) == (OFFSET)(~0x7F)));

            emit67 (pso1, pso2);
            emit66 (pso1, pso2);
#endif
            forceimmed (p->dsc2);
            checksize(p);
            emitopcode ((UCHAR)(IMUL3 + 2 * pso2->s));
            emitmodrm ((USHORT)pso1->mode, pso1->rm, pso1->rm);
            pso2->w = !pso2->s; /* shorten to byte if necessary */
            if (!pso2->w)
                byteimmcheck(pso2);
            pso2->dsize = pso1->dsize;
            emitrest (p->dsc2);
        }
    } else {
#ifdef V386
        emit67 (pso1, NULL);
        emit66 (pso1, NULL);
#endif
        forcesize (p->dsc1);
        emitescape (p->dsc1, p->defseg);
        if ((opcbase == O_NEG || opcbase == O_NOT) && impure)
            errorc (E_IMP);
        emitopcode ((UCHAR)(ARITHBASE + pso1->w));
        emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
        emitrest (p->dsc1);
    }
}




/***	pbound - bounds operators
 *
 *	pbound (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pbound (
       struct parsrec *p
       )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    checkwreg(pso1);
    if (pso2->dsize != pso1->dsize*2)
        errorc (E_IIS);

#ifdef V386_0

    if (wordsize != pso1->dsize)
        emitsize(0x66);
#endif
    emitopcode (opcbase);
    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}




/***	penter - enter operators
 *
 *	penter (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
penter (
       register struct parsrec *p
       )
{

    emitopcode (opcbase);

    p->dsc1->dsckind.opnd.dsize = 2;
    emitOP (&p->dsc1->dsckind.opnd);

    p->dsc2->dsckind.opnd.dsize = 1;
    forceimmed (p->dsc2);
    emitOP (&p->dsc2->dsckind.opnd);
}




/***	pclts - 	   operators
 *
 *	pclts ();
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pclts ()
{
    emitopcode (opcbase);
    emitopcode (modrm);
}




/***	pdescrtbl - table operators
 *
 *	pdescrtbl (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pdescrtbl (
          struct parsrec *p
          )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    if (pso1->dsize != 6)
        errorc (E_IIS);
    emitopcode (opcbase);
    emitopcode (1);
    emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
    emitrest (p->dsc1);
}




/***	pdttrsw -	     operators
 *
 *	pdttrsw (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL CODESIZE

pdttrsw (
        struct parsrec *p
        )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    if (!pso1->w || (pso1->sized && pso1->dsize != 2))
        errorc ((USHORT)(pso1->mode != 3? E_IIS: E_IIS & ~E_WARN1));
    emitopcode (opcbase);
    if ((modrm == R_LMSW) || (modrm == R_SMSW))
        emitopcode (1);
    else
        emitopcode (0);
    emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
    emitrest (p->dsc1);
}




/***	pver -		  operators
 *
 *	pver (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pver (
     struct parsrec *p
     )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    if (!pso1->w || (pso1->sized && pso1->dsize != 2))
        errorc ((UCHAR)(pso1->mode != 3? E_IIS: E_IIS & ~E_WARN1));
    emitopcode (opcbase);
    emitopcode (0);
    emitmodrm ((USHORT)pso1->mode, modrm, pso1->rm);
    emitrest (p->dsc1);
}




/***	parsl - 	   operators
 *
 *	parsl (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
parsl (
      struct parsrec *p
      )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    checkmatch (p->dsc1, p->dsc2);
    checkwreg(pso1);

    emitopcode (opcbase);
    emitopcode (modrm);
    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}




/***	parpl - 	   operators
 *
 *	parpl (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
parpl (
      struct parsrec *p
      )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    if (pso2->dsize != 2)
        errorc (E_IIS);

    checkmatch (p->dsc2, p->dsc1);
    emitopcode (opcbase);
    emitmodrm ((USHORT)pso1->mode, pso2->rm, pso1->rm);
    emitrest (p->dsc1);
}




/***	pstack - push|pos stack
 *
 *	pstack (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pstack (
       struct parsrec *p
       )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);

#ifdef V386
    if (!(pso1->fixtype == FBASESEG || pso1->fixtype == FGROUPSEG) &&
        pso1->sized && (pso1->dsize|wordsize) == 6 &&
        !(pso1->mode == 3 && pso1->dsegment->symu.regsym.regtype == SEGREG)) {
        emitsize(0x66);
        errorc (E_ONW);
    }
#endif

    if (pso1->mode == 3) {          /* Using register */
        /* Forward is error */
        errorforward (p->dsc1);
        switch (pso1->dsegment->symu.regsym.regtype) {
            case SEGREG:
                /* CS | DS | ES | SS | FS | GS */
                rangecheck (&pso1->rm, (UCHAR)7);
                if (opcbase == O_POP && pso1->rm == CSSEG)
                    errorc (E_CSI);
#ifdef V386
                if (pso1->rm >= FSSEG) {
                    emitopcode(0x0f);
                    emitopcode ((UCHAR)(((pso1->rm << 3)+ 0x80) + (opcbase == O_POP)));
                } else
#endif
                    emitopcode ((UCHAR)(((pso1->rm << 3)+ 6) + (opcbase == O_POP)));
                break;
            case WRDREG:
            case INDREG:
#ifdef V386
            case DWRDREG:
#endif
                rangecheck (&pso1->rm, (UCHAR)7);
                emitopcode ((UCHAR)(opcbase + pso1->rm));
                /* Reg form */
                break;
            default:
                errorc(E_BRI);
        }
    } else if (pso1->mode == 4) {

#ifdef V386		/* detect immediate too big */
        if (wordsize == 2 && pso1->dsize != 4 && highWord(pso1->doffset))
            if (highWord(pso1->doffset) != 0xFFFF || !pso1->s)
                errorc(E_VOR);
#endif
        if (opcbase == O_POP || cputype == P86)
            errorimmed (p->dsc1);

        emitopcode ((UCHAR)(0x68 + 2 * pso1->s));
        pso1->w = !pso1->s; /* shorten to byte if necessary */
        if (!pso1->w)
            byteimmcheck(pso1);

        else if (!(M_PTRSIZE & pso1->dtype))
            pso1->dsize = wordsize; /* force size to wordsize */

        emitrest (p->dsc1);
    } else {

        if (pso1->sized && pso1->dsize &&
            !(pso1->dsize == 2 || pso1->dsize == 4))

            errorc(E_IIS);

        /* Have memory operand of some kind */

        if (opcbase == O_POP && impure)
            errorc (E_IMP);

        emitopcode ((UCHAR)((opcbase == O_PUSH)? O_PUSHM: O_POPM));
        emitmodrm ((USHORT)pso1->mode,
                   (USHORT)((opcbase == O_PUSH)? 6: 0),
                   pso1->rm);
        emitrest (p->dsc1);
    }
}


/***	buildFrame - builds stack frame
 *
 *	preturn (p);
 *
 *	Entry before first instruction is generated in proc
 */


VOID
PASCAL
CODESIZE
buildFrame()
{
    char szLocal[32];
    char szT[48];
    SHORT i;

    strcpy(save, lbuf);     /* save line for later .. */
    fSkipList++;

    fProcArgs = -fProcArgs;     /* mark already processed */

    if (fProcArgs < -ARGS_REG) {

        *radixconvert (cbProcLocals,  szLocal) = NULL;
        if (cputype & P86) {

            doLine("push bp");
            doLine("mov  bp,sp");

            if (fProcArgs == -ARGS_LOCALS)     /* locals present */
                doLine(strcat( strcpy(szT, "sub sp,"), szLocal));
        } else
            doLine(strcat( strcat( strcpy(szT, "enter "), szLocal), ",0"));
    }

    for (i = 0; i <= iRegSave; i++) {  /* push all the saved registers */

        doLine( strcat( strcpy(lbuf, "push "), regSave[i]) );
    }

    fSkipList--;
    lbufp = strcpy(lbuf, save);
    linebp = lbufp + strlen(lbufp);
    strcpy(linebuffer, save);
    parse();
}


/***	preturn - various forms of return
 *
 *	preturn (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */



VOID
PASCAL
CODESIZE
preturn (
        struct parsrec *p
        )
{
    register struct psop *pso1; /* parse stack operand structure */
    SHORT i;

    pso1 = &(p->dsc1->dsckind.opnd);

    /* Decide whether inter or intra segment */

    if (!modrm) {    /* determine distance, if not RETN or RETF */

        if (fProcArgs) {            /* tear down the stack frame */

            strcpy(save, linebuffer);
            fSkipList++;

            for (i = iRegSave; i >= 0; i--) {  /* pop all the saved registers */

                doLine( strcat( strcpy(lbuf, "pop "), regSave[i]) );
            }

            if (fProcArgs < -ARGS_REG)
                if (cputype & P86) {

                    if (fProcArgs == -ARGS_LOCALS)  /* locals present */
                        doLine("mov  sp,bp");

                    doLine("pop bp");
                } else
                    doLine("leave");

            if (!(pcproc->attr & M_CDECL))
                pso1->doffset = cbProcParms;

            strcpy(linebuffer, save);
            listindex = 1;
            fSkipList = FALSE;
        }

        opcbase = O_RET;

        if (pcproc && pcproc->symtype == CSFAR)
            opcbase = O_RET + 8;
    }

    /* Optimize, if constant is 0 and not forward, use SHORT */

    if (pso1->doffset == 0 && pso1->dflag != FORREF)
        emitopcode (opcbase);

    else {  /* Gen 2 byte version */
        emitopcode ((UCHAR)(opcbase - 1));  /* Pop form */
        /* Force word--always 2 bytes, even on 386 */
        pso1->dsize = 2;
        emitOP (pso1);          /* Immediate word */

    }
}




/***	pincdec - increment|decrement
 *
 *	pincdec (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pincdec (
        struct parsrec *p
        )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    /* INC | DEC */
    if (!pso1->sized)
        errorc (E_OHS);
    if (pso1->mode == 3 && pso1->w)
        /* Is word reg */
        emitopcode ((UCHAR)(opcbase + pso1->rm));
    else {
        /* Use mod reg r/m form */
        if (impure)
            errorc (E_IMP);
        emitopcode ((UCHAR)(0xFE + pso1->w));
        emitmodrm ((USHORT)pso1->mode,
                   (USHORT)(opcbase == O_DEC), pso1->rm);
        emitrest (p->dsc1);
    }
}




/***	pint - interrupt
 *
 *	pint (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pint (
     struct parsrec *p
     )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    /* INT */
    valuecheck (&pso1->doffset, 255);
    if (pso1->doffset == 3 && pso1->dflag != FORREF)
        /* Use SHORT form */
        emitopcode (opcbase);
    else {
        /* Use long form */
        emitopcode ((UCHAR)(opcbase + 1));
        emitopcode ((UCHAR)(pso1->doffset & 255));
    }
}




/***	pinout - input|output
 *
 *	pinout (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pinout (
       struct parsrec *p
       )
{
    register DSCREC *pso1;
    register DSCREC *pso2;

    pso1 = p->dsc1;
    pso2 = p->dsc2;

    if (opcbase == O_OUT) {
        pso2 = pso1;
        pso1 = p->dsc2;
    }

    /* IN  ax|al,	 DX|immed */
    /* OUT DX|immed, ax|al, */

#ifdef V386
    emit66(&pso1->dsckind.opnd, NULL);
#endif
    forceaccum (pso1);

    /* Must be accum */
    if (pso2->dsckind.opnd.mode == 3 && pso2->dsckind.opnd.rm == 2) {
        /* Have DX */
        emitopcode ((UCHAR)(opcbase + pso1->dsckind.opnd.w + 8));

        if (pso2->dsckind.opnd.dsize != 2)
            errorc(E_IRV);
    } else {
        /* Have port # */
        forceimmed (pso2);
        /* Must be constant */
        valuecheck (&pso2->dsckind.opnd.doffset, 255);
        emitopcode ((UCHAR)(opcbase + pso1->dsckind.opnd.w));
        emitopcode ((UCHAR)(pso2->dsckind.opnd.doffset));
    }
}




/***	pload - load
 *
 *	pload (p);	lea, les, les, etc
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pload (
      struct parsrec *p
      )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    /* LDS | LEA | LES */

    if (pso1->mode != 3)
        /* Must be reg */
        errorc (E_MBR);

    else if (1 << pso1->dsegment->symu.regsym.regtype
             & (M_STKREG | M_SEGREG | M_BYTREG))
        errorc (E_WRT);

    if (pso2->mode == 3)
        errorc (E_IUR);

    if (opcbase != O_LEA) {
        if (pso2->dsize && pso2->dsize != 4 && pso2->dsize != 6)
            errorc (E_IIS);

        /* complain about mismatching source and destination */

        if (pso2->dsize && pso1->dsize &&
            pso1->dsize + 2 != pso2->dsize)
            errorc (E_IIS);
#ifdef V386
        else if (pso2->dsize && pso2->dsize != wordsize+2)
            emitsize(0x66);
        else if (pso1->dsize && pso1->dsize != wordsize)
            emitsize(0x66);
#endif
    }

#ifdef V386
    else
        if (pso1->dsize != wordsize)
        emitsize(0x66);

    switch (opcbase) {
        case O_LFS:
        case O_LGS:
        case O_LSS:
            emitopcode(0x0F);
            break;
    }
#endif
    emitopcode (opcbase);
    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);

    /* If FAR, make offset so only 2 bytes out */

    if (pso2->fixtype == FPOINTER)
        pso2->fixtype = FOFFSET;

    emitrest (p->dsc2);
}




/***	pmov - move
 *
 *	pmov (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pmov (
     struct parsrec *p
     )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    /* If 1st arg is memory or undef, force 2nd to be
     * immed for pass 1 and set <EXPLOFFSET> in pass 2 */

    if ((pso1->mode < 3) && (pso2->mode != 3)) {
        /* mem,immed */
        pso2->dtype |= M_EXPLOFFSET;
        /* Look like OFFSET val */
        if (!pass2)
            /* Force immed on pass1 */
            pso2->mode = 4;
    }
    /* See if this is immediate move */
    if (pso2->mode == 4) {
        emit66 (pso1, pso2);

        /* MOV arg,immed */
        if (pso1->mode == 3) {
            /* MOV reg,immed */
            if (1 << pso1->dsegment->symu.regsym.regtype
                & (M_SEGREG | M_STKREG | M_CREG ))
                /* Wrong type of register */
                errorc (E_NIM);
            emitopcode ((UCHAR)(176 + 8*pso1->w + pso1->rm));
            /* Make sure agree */
            checksize (p);
            emitrest (p->dsc2);
            /* Emit immed */
            if (pso1->rm &&
                pso2->dtype & M_FORTYPE &&
                !pso2->dsegment && !(M_EXPLOFFSET & pso2->dtype))
                /* Pass 1 assumed not immed */
                emitnop();
        } else {/* MOV mem,immed */
            checksize (p);
            if (!(pso1->sized || pso2->sized)) {
                pso1->sized = pso2->sized = TRUE;
                pso1->w = pso2->w = TRUE;
            }
            /* Make sure agree */
            if (impure)
                errorc (E_IMP);
            emitopcode    (   (   UCHAR)(   198    +    pso1->w));
            emitmodrm ((USHORT)pso1->mode, 0, pso1->rm);
            emitrest (p->dsc1);
            emitrest (p->dsc2);
        }

        if (!pso1->w)

            /*	1st operand is byte, 2nd is immed
             *	Check below on dsc1 should only be done
             *	on MOV since the PGENARG opcodes always shorten a known
             *	byte const */

            if ((pso1->dtype & (M_FORTYPE|M_PTRSIZE|M_EXPLOFFSET)) == M_FORTYPE ||
                (pso2->dtype & (M_FORTYPE|M_PTRSIZE|M_EXPLOFFSET)) == M_FORTYPE)
                emitnop();

    }
    /* See if either is segment register */
    else if (pso1->mode == 3) {
        /* 1st arg is reg */
        switch (pso1->dsegment->symu.regsym.regtype) {
            case SEGREG:
                /* MOV SEGREG,arg */
                movesegreg (TRUE, p);
                break;
#ifdef V386
            case CREG:
                /* mov CREG,reg */
                movecreg (TRUE, p);
                break;

            case DWRDREG:
#endif
            case BYTREG:
            case WRDREG:
                /* MOV ac,addr? */
                if ((pso1->rm == 0) && isdirect(pso2))
                    /* MOV ac,addr */
                    moveaccum (TRUE, p);
                else
                    /* MOV reg,arg */
                    movereg (TRUE, p);
                break;
            case INDREG:
                /* MOV indreg,arg */
                movereg (TRUE, p);
                break;
            default:
                errorc (E_WRT);
                break;
        }
    } else if (pso2->mode == 3) {
        /* 2nd arg is reg */
        switch (pso2->dsegment->symu.regsym.regtype) {
            case SEGREG:
                /* MOV arg,SEGREG */
                movesegreg (FALSE, p);
                break;
#ifdef V386
            case CREG:
                /* mov reg, CREG */
                movecreg(FALSE, p);
                break;
            case DWRDREG:
#endif
            case BYTREG:
            case WRDREG:
                /* MOV addr,ac? */
                if ((pso2->rm == 0) && isdirect(pso1))
                    /* MOV addr,ac */
                    moveaccum (FALSE, p);
                else
                    /* MOV arg,reg */
                    movereg (FALSE, p);
                break;
            case INDREG:
                /* MOV arg,indreg */
                movereg (FALSE, p);
                break;
            default:
                errorc (E_WRT);
                break;
        }
    } else
        errorc (E_IOT);
}




/***	pgenarg
 *
 *	pgenarg (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */

VOID
PASCAL
CODESIZE
pgenarg (
        struct parsrec *p
        )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    char fAccumMode = 0;

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    /* ADC | ADD | AND | CMP | OR | SBB  SUB | XOR | TEST */
    if (pso1->mode != 3 && pso2->mode != 3) {
        /* Force to mem,immed */
        if (!pass2)
            /* Force immediate */
            pso2->mode = 4;
    }
    /* Not AX,immed */
    if (pso2->mode == 4) {

#ifdef V386	/* recompute immediate size based op 1 size not word size */

        if (!(pso2->dflag & (UNDEFINED|FORREF|XTERNAL))
            && pso2->fixtype == FCONSTANT
            && pso2->doffset & 0x8000)
            if (pso1->dsize == 2)
                pso2->s = (char)((USHORT)(((USHORT) pso2->doffset & ~0x7F ) == (USHORT)(~0x7F)));
            else
                pso2->s = (char)((OFFSET)((pso2->doffset & ~0x7F ) == (OFFSET)(~0x7F)));

#endif
        /* OP mem/reg,immed */
        if (pso1->mode == 3 && pso1->rm == 0
#ifdef V386
            && !(pso1->dsize == 4 && pso2->s &&
                 opcbase != O_TEST)      /* chose size extended */
#endif						    /* general purpose over ac*/
           ) {

            /* OP al|ax|eax,immed */
            checksize (p);
            /* Make sure agree */
            if (opcbase == O_TEST)
                /* * TEST is special * */
                emitopcode ((UCHAR)(0xA8 + pso1->w));
            else/* Other reg immed */
                /* Is AX,immed */
                emitopcode ((UCHAR)(opcbase + 4 + pso1->w));
            fAccumMode = 1;
        } else {/* OP mem/reg, immed */

            checksize (p);
            if (!(pso1->sized || pso2->sized)) {
                pso1->sized = pso2->sized = TRUE;
                pso1->w = pso2->w = TRUE;
            }
            /* Make sure agree */
            if (opcbase == O_TEST) {
                /* TEST is special */
                emitopcode ((UCHAR)(ARITHBASE + pso1->w));
                emitmodrm ((USHORT)pso1->mode, 0, pso1->rm);
            } else {
                if (opcbase != O_CMP && impure)
                    errorc (E_IMP);

                if (pso2->w) {
                    /* Try to shorten word */
                    emitopcode ((UCHAR)(0x80 + (pso2->s <<1) +pso1->w));
                    pso2->w = !pso2->s;
                    /* So only byte out */
                    if (!pso2->w) {
                        fAccumMode = wordsize - 1;
                        byteimmcheck(pso2);
                    }
                } else {
                    emitopcode (128);
                }
                emitmodrm ((USHORT)pso1->mode, (USHORT)(opcbase>>3), pso1->rm);
            }
            emitrest (p->dsc1);
        }
        if (pso2->w && !pso1->w)
            /* size mismatch */
            errorc (E_VOR);

        emitrest (p->dsc2);     /* Emit immed */

        if (!pso1->w)

            if (((pso2->dtype & (M_FORTYPE|M_PTRSIZE|M_EXPLOFFSET)) == M_FORTYPE ||
                 opcbase == O_TEST && pso1->mode != 3) &&

                ((pso1->dtype & (M_FORTYPE|M_PTRSIZE|M_EXPLOFFSET)) == M_FORTYPE ||
                 pso1->mode == 3))

                emitnop();

        if (fAccumMode &&
            M_FORTYPE & pso2->dtype &&
            !(M_EXPLOFFSET & pso2->dtype))

            while (--fAccumMode >= 0)
                emitnop();
    } else {  /* Not immediate */
        if (pso1->mode == 3) {
            /* OP reg,mem/reg */
            checkmatch (p->dsc1, p->dsc2);
            if (opcbase == O_TEST)
                opcbase = O_TEST - 2;

            emitopcode ((UCHAR)(opcbase + 2 + pso1->w));
            emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
            emitrest (p->dsc2);
        } else if (pso2->mode != 3)
            errorc (E_IOT);

        else { /* Have OP mem,reg */
            if (opcbase != O_CMP && opcbase != O_TEST && impure)
                errorc (E_IMP);

            checkmatch (p->dsc2, p->dsc1);
            emitopcode ((UCHAR)(opcbase + pso2->w));
            emitmodrm ((USHORT)pso1->mode, pso2->rm, pso1->rm);
            emitrest (p->dsc1);
        }
    }
}




/***	pxchg - exchange register and register/memory
 *
 *	pxchg (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pxchg (
      struct parsrec  *p
      )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */
    DSCREC *t;

    if (impure)
        errorc (E_IMP);

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    if (pso1->mode != 3) {

        if (pso2->mode != 3) {
            errorc (E_OCI);     /* Illegal */
            return;
        }
        t = p->dsc1;
        p->dsc1 = p->dsc2;
        p->dsc2 = t;

        pso1 = &(p->dsc1->dsckind.opnd);
        pso2 = &(p->dsc2->dsckind.opnd);

    }

    /* First operand is register */

    if (1 << pso1->dsegment->symu.regsym.regtype & (M_STKREG | M_SEGREG))
        errorc (E_WRT);
    rangecheck (&pso1->rm, (UCHAR)7);

    if (pso1->dsize != pso2->dsize && pso2->sized)
        errorc (E_OMM);

    if (pso2->mode == 3) {
        /* XCHG reg, reg */

        if (1 << pso2->dsegment->symu.regsym.regtype & (M_STKREG | M_SEGREG))
            errorc (E_WRT);
        rangecheck (&pso2->rm, (UCHAR)7);

        /* Check for XCHG accum, reg */

        if (pso1->rm == 0 && pso1->w) {
            emitopcode ((UCHAR)(144 + pso2->rm));
            return;
        } else if (pso2->w && pso2->rm == 0) {
            emitopcode ((UCHAR)(144 + pso1->rm));
            return;
        }
    }
    emitopcode ((UCHAR)(134 + pso1->w));
    emitmodrm ((USHORT)pso2->mode, pso1->rm, pso2->rm);
    emitrest (p->dsc2);
}






/***	pesc - escape operators
 *
 *	pesc (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pesc (
     struct parsrec *p
     )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);
    /* ESC opcode,modrm */
    valuecheck (&pso1->doffset, 63);
    emitopcode ((UCHAR)(216 + pso1->doffset / 8));
    emitmodrm ((USHORT)pso2->mode, (USHORT)(pso1->doffset & 7), pso2->rm);
    emitrest (p->dsc2);
}



/***	prepeat - repeat operators
 *
 *	prepeat (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
prepeat (
        struct parsrec *p
        )
{

    /* REP | REPZ | REPE | REPNE | REPNZ */
    emitopcode (opcbase);
    listbuffer[listindex-1] = '/';
    listindex++;
    /* Flag is LOCK/REP */
    getatom ();
    if (!opcodesearch ())
        /* Must have another op */
        errorc (E_OAP);
    else
        /* Prefix for string instr */
        opcode ();
    p->dsc1 = NULL;
    p->dsc2 = NULL;
}




/***	pstr - string operators
 *
 *	pstr (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pstr (
     struct parsrec *p
     )
{
    register struct psop *pso1; /* parse stack operand structure */
    register struct psop *pso2; /* parse stack operand structure */

    /* SCAS | STOS | MOVS | LODS | CMPS */
    if (!p->dsc2)
        p->dsc2 = p->dsc1;
    pso1 = &(p->dsc1->dsckind.opnd);
    pso2 = &(p->dsc2->dsckind.opnd);

    if (opcbase == O_OUTS) {
        if (pso1->mode != 3)
            errorc (E_MBR);
        else if (pso1->rm != 2)
            errorc (E_WRT);
        p->dsc1 = p->dsc2;
        pso1 = pso2;
    }
    if (opcbase == O_INS) {
        if (pso2->mode != 3)
            errorc (E_MBR);
        else if (pso2->rm != 2)
            errorc (E_WRT);
        p->dsc2 = p->dsc1;
        pso2 = pso1;
    }

    /* Had to wait til now, so OUTS, INS would be adjusted already */
    emit66 (pso1, pso2);

    if ((pso1->mode > 2 && pso1->mode < 5) ||
        (pso2->mode > 2 && pso2->mode < 5))
        errorc (E_IOT);

    if (!(pso1->sized || pso2->sized))
        /* Give error if don't have a size specified */
        errorc (E_OHS);

    if (pso1->w != pso2->w)
        errorc (E_OMM);

    if (opcbase == O_MOVS || opcbase == O_LODS || opcbase == O_OUTS) {
        emitescape (p->dsc2, DSSEG);
        /* 2nd can be override */
        if (p->dsc1 != p->dsc2)
            errorover (pso1->seg);
    } else {
        errorover (pso2->seg);
        /* No 2nd override */
        if (p->dsc1 != p->dsc2)
            emitescape (p->dsc1, DSSEG);
    }
    emitopcode ((UCHAR)(opcbase + pso1->w));
    if (p->dsc1 == p->dsc2) {
        p->dsc1 = NULL;
    }
}




/***	pxlat
 *
 *	pxlat (p);
 *
 *	Entry
 *	Exit
 *	Returns
 *	Calls
 */


VOID
PASCAL
CODESIZE
pxlat (
      struct parsrec *p
      )
{
    register struct psop *pso1; /* parse stack operand structure */

    pso1 = &(p->dsc1->dsckind.opnd);
    /* XLAT */
    if (pso1->mode <= 2 || pso1->mode >= 5)
        /* Good mode */
        if (pso1->w)
            /* Must be byte */
            errorc (E_IIS);
    emitopcode (opcbase);
}


/* isdirect -- given a psop representing a modrm, is it mem-direct? */

USHORT
CODESIZE
isdirect(
        register struct psop *pso   /* parse stack operand structure */
        )
{
    return ((pso->mode == 0 && pso->rm == 6) || /* for 8086 */
            (pso->mode == 5 && pso->rm == 5));  /* for 386 */
}

#ifdef V386

/* emit66 -- if dsize == 2 && wordsize == 4, or vice versa, we generate
 * a 66 prefix to locally change the operand mode.
 */

VOID
PASCAL
CODESIZE
emit66(
      register struct psop *pso1, /* parse stack operand structure */
      register struct psop *pso2  /* parse stack operand structure */
      )
{


    if (!pso1)
        return;

    if (!pso2) {

        if (pso1->sized && (pso1->dsize | wordsize) == 6)
            emitsize(0x66);
    } else {
        /* key off the first operand if size known AND second isn't a register */

        if (pso1->sized && pso2->mode != 3 ||

            /* bogusness--sized and dsize 0 means immed bigger than 127 */

            (pso2->sized &&
             (pso1->dsize == pso2->dsize || pso2->dsize == 0))) {
            if ((pso1->dsize | wordsize) == 6)
                emitsize(0x66);
        } else if (pso2->sized) {
            if ((pso2->dsize | wordsize) == 6)
                emitsize(0x66);
        }
    }
    /* otherwise we have inconsistent opcodes and we cant do a thing.
       so dont.  bogus!!! */
}

/* emit67-- checks for operand size not matching wordsize and emits the
 * appropriate override */

VOID
PASCAL
emit67(
      register struct psop *pso1, /* parse stack operand structure */
      register struct psop *pso2  /* parse stack operand structure */
      )
{

    if (!pso1)
        return;

    if ((1<<FIRSTDS) & xoptoseg[opctype]) {
        if (wordsize < 4 && pso1->mode > 4) {
            emitsize(0x67);
            return;
        } else if (wordsize > 2 && pso1->mode < 3) {
            emitsize(0x67);
            return;
        }
    }

    if (!pso2 || !(1<<SECONDDS & xoptoseg[opctype]))
        return;

    if (wordsize < 4 && pso2->mode > 4) {
        emitsize(0x67);
        return;
    } else if (wordsize > 2 && pso2->mode < 3) {
        emitsize(0x67);
        return;
    }
}

#endif /* V386 */

/* check for word register, or if 386, dword register */
CODESIZE
checkwreg(
         register struct psop *psop  /* parse stack operand structure */
         )
{

    if (psop->mode != 3)
        errorc (E_MBR);
    if (psop->dsize != 2

#ifdef V386
        && (!(cputype&P386) || psop->dsize != 4)
#endif
       )
        errorc (E_BRI);
    return(0);
}