/* mops.c - handle pseudo-ops */

#include "const.h"
#include "type.h"
#include "globvar.h"
#include "opcode.h"
#include "scan.h"
#undef EXTERN
#define EXTERN
#include "address.h"

#define is8bitadr(offset) ((offset_t) offset < 0x100)
#define is8bitsignedoffset(offset) ((offset_t) (offset) + 0x80 < 0x100)
#define pass2 pass

FORWARD void mshort2 P((void));
FORWARD reg_pt regchk P((void));
FORWARD void reldata P((void));

#ifdef I80386

#define iswordadr(offset) ((offset_t) (offset) < 0x10000L)
#define iswordoffset(offset) ((offset_t) (offset) + 0x8000L < 0x10000L)
#define iswordorswordoffset(offset) ((offset_t) (offset) + 0xFFFFL < 0x1FFFEL)

#define BYTE_SEGWORD        0x00
#define isspecreg(r) ((r) >= CR0REG && (r) <= TR7REG)

#define BASE_MASK   0x07
#define BASE_SHIFT     0
#define INDEX_MASK  0x38
#define INDEX_SHIFT    3
#define MOD_MASK    0xC0
# define REG_MOD    0xC0
# define MEM0_MOD   0x00
# define MEM1_MOD   0x40
# define MEM2_MOD   0x80
#define REG_MASK    0x38
#define REG_SHIFT      3
#define RM_MASK     0x07
#define RM_SHIFT       0
# define D16_RM     0x06
# define D32_RM     0x05
# define SIB_NOBASE 0x05
# define SIB_RM     0x04
#define SREG_MASK   0x38
#define SREG_SHIFT     3
#define SS_MASK     0xC0
#define SS_SHIFT       6

#define SEGMOV      0x04
#define SIGNBIT     0x02
#define TOREGBIT    0x02
#define WORDBIT     0x01

PRIVATE opcode_t baseind16[] =
{
    0x00,			/* BP + BP, illegal */
    0x00,			/* BX + BP, illegal */
    0x03,			/* DI + BP */
    0x02,			/* SI + BP */
    0x00,			/* BP + BX, illegal */
    0x00,			/* BX + BX, illegal */
    0x01,			/* DI + BX */
    0x00,			/* SI + BX */
    0x03,			/* BP + DI */
    0x01,			/* BX + DI */
    0x00,			/* DI + DI, illegal */
    0x00,			/* SI + DI, illegal */
    0x02,			/* BP + SI */
    0x00,			/* BX + SI */
    0x00,			/* DI + SI, illegal */
    0x00,			/* SI + SI, illegal */
};

PRIVATE opcode_t regbits[] =
{
    0x05 << REG_SHIFT,		/* BP */
    0x03 << REG_SHIFT,		/* BX */
    0x07 << REG_SHIFT,		/* DI */
    0x06 << REG_SHIFT,		/* SI */

    0x00 << REG_SHIFT,		/* EAX */
    0x05 << REG_SHIFT,		/* EBP */
    0x03 << REG_SHIFT,		/* EBX */
    0x01 << REG_SHIFT,		/* ECX */
    0x07 << REG_SHIFT,		/* EDI */
    0x02 << REG_SHIFT,		/* EDX */
    0x06 << REG_SHIFT,		/* ESI */
    0x04 << REG_SHIFT,		/* ESP */

    0x00 << REG_SHIFT,		/* AX */
    0x01 << REG_SHIFT,		/* CX */
    0x02 << REG_SHIFT,		/* DX */
    0x04 << REG_SHIFT,		/* SP */

    0x04 << REG_SHIFT,		/* AH */
    0x00 << REG_SHIFT,		/* AL */
    0x07 << REG_SHIFT,		/* BH */
    0x03 << REG_SHIFT,		/* BL */
    0x05 << REG_SHIFT,		/* CH */
    0x01 << REG_SHIFT,		/* CL */
    0x06 << REG_SHIFT,		/* DH */
    0x02 << REG_SHIFT,		/* DL */

    0x01 << REG_SHIFT,		/* CS */
    0x03 << REG_SHIFT,		/* DS */
    0x00 << REG_SHIFT,		/* ES */
    0x04 << REG_SHIFT,		/* FS */
    0x05 << REG_SHIFT,		/* GS */
    0x02 << REG_SHIFT,		/* SS */

    0x00 << REG_SHIFT,		/* CR0 */
    0x02 << REG_SHIFT,		/* CR2 */
    0x03 << REG_SHIFT,		/* CR3 */

    0x00 << REG_SHIFT,		/* DR0 */
    0x01 << REG_SHIFT,		/* DR1 */
    0x02 << REG_SHIFT,		/* DR2 */
    0x03 << REG_SHIFT,		/* DR3 */
    0x06 << REG_SHIFT,		/* DR6 */
    0x07 << REG_SHIFT,		/* DR7 */

    0x03 << REG_SHIFT,		/* TR3 */
    0x04 << REG_SHIFT,		/* TR4 */
    0x05 << REG_SHIFT,		/* TR5 */
    0x06 << REG_SHIFT,		/* TR6 */
    0x07 << REG_SHIFT,		/* TR7 */

    0x00 << REG_SHIFT,		/* ST(0) */
    0x01 << REG_SHIFT,		/* ST(1) */
    0x02 << REG_SHIFT,		/* ST(2) */
    0x03 << REG_SHIFT,		/* ST(3) */
    0x04 << REG_SHIFT,		/* ST(4) */
    0x05 << REG_SHIFT,		/* ST(5) */
    0x06 << REG_SHIFT,		/* ST(6) */
    0x07 << REG_SHIFT,		/* ST(7) */
};

PRIVATE opsize_t regsize[] =
{
    2,				/* BP */
    2,				/* BX */
    2,				/* DI */
    2,				/* SI */

    4,				/* EAX */
    4,				/* EBP */
    4,				/* EBX */
    4,				/* ECX */
    4,				/* EDI */
    4,				/* EDX */
    4,				/* ESI */
    4,				/* ESP */

    2,				/* AX */
    2,				/* CX */
    2,				/* DX */
    2,				/* SP */

    1,				/* AH */
    1,				/* AL */
    1,				/* BH */
    1,				/* BL */
    1,				/* CH */
    1,				/* CL */
    1,				/* DH */
    1,				/* DL */

    2,				/* CS */
    2,				/* DS */
    2,				/* ES */
    2,				/* FS */
    2,				/* GS */
    2,				/* SS */

    4,				/* CR0 */
    4,				/* CR2 */
    4,				/* CR3 */

    4,				/* DR0 */
    4,				/* DR1 */
    4,				/* DR2 */
    4,				/* DR3 */
    4,				/* DR6 */
    4,				/* DR7 */

    4,				/* TR3 */
    4,				/* TR4 */
    4,				/* TR5 */
    4,				/* TR6 */
    4,				/* TR7 */

    10,				/* ST(0) */
    10,				/* ST(1) */
    10,				/* ST(2) */
    10,				/* ST(3) */
    10,				/* ST(4) */
    10,				/* ST(5) */
    10,				/* ST(6) */
    10,				/* ST(7) */

    0,				/* NOREG */
};

PRIVATE opcode_t regsegword[] =
{
    WORDBIT,			/* BP */
    WORDBIT,			/* BX */
    WORDBIT,			/* DI */
    WORDBIT,			/* SI */

    WORDBIT,			/* EAX */
    WORDBIT,			/* EBP */
    WORDBIT,			/* EBX */
    WORDBIT,			/* ECX */
    WORDBIT,			/* EDI */
    WORDBIT,			/* EDX */
    WORDBIT,			/* ESI */
    WORDBIT,			/* ESP */

    WORDBIT,			/* AX */
    WORDBIT,			/* CX */
    WORDBIT,			/* DX */
    WORDBIT,			/* SP */

    BYTE_SEGWORD,		/* AH */
    BYTE_SEGWORD,		/* AL */
    BYTE_SEGWORD,		/* BH */
    BYTE_SEGWORD,		/* BL */
    BYTE_SEGWORD,		/* CH */
    BYTE_SEGWORD,		/* CL */
    BYTE_SEGWORD,		/* DH */
    BYTE_SEGWORD,		/* DL */

    SEGMOV,			/* CS */
    SEGMOV,			/* DS */
    SEGMOV,			/* ES */
    SEGMOV,			/* FS */
    SEGMOV,			/* GS */
    SEGMOV,			/* SS */

    0x20,			/* CR0 */
    0x20,			/* CR2 */
    0x20,			/* CR3 */

    0x21,			/* DR0 */
    0x21,			/* DR1 */
    0x21,			/* DR2 */
    0x21,			/* DR3 */
    0x21,			/* DR6 */
    0x21,			/* DR7 */

    0x24,			/* TR3 */
    0x24,			/* TR4 */
    0x24,			/* TR5 */
    0x24,			/* TR6 */
    0x24,			/* TR7 */

    0x00,			/* ST(0) */
    0x00,			/* ST(1) */
    0x00,			/* ST(2) */
    0x00,			/* ST(3) */
    0x00,			/* ST(4) */
    0x00,			/* ST(5) */
    0x00,			/* ST(6) */
    0x00,			/* ST(7) */

    0x00,			/* NOREG */
};

PRIVATE opcode_t rm[] =
{
    0x05,			/* BP */
    0x03,			/* BX */
    0x07,			/* DI */
    0x06,			/* SI */

    0x00,			/* EAX */
    0x05,			/* EBP */
    0x03,			/* EBX */
    0x01,			/* ECX */
    0x07,			/* EDI */
    0x02,			/* EDX */
    0x06,			/* ESI */
    0x04,			/* ESP */

    0x00,			/* AX */
    0x01,			/* CX */
    0x02,			/* DX */
    0x04,			/* SP */

    0x04,			/* AH */
    0x00,			/* AL */
    0x07,			/* BH */
    0x03,			/* BL */
    0x05,			/* CH */
    0x01,			/* CL */
    0x06,			/* DH */
    0x02,			/* DL */

    0x01,			/* CS */
    0x03,			/* DS */
    0x00,			/* ES */
    0x04,			/* FS */
    0x05,			/* GS */
    0x02,			/* SS */

    0x00,			/* CR0 */
    0x00,			/* CR2 */
    0x00,			/* CR3 */

    0x00,			/* DR0 */
    0x00,			/* DR1 */
    0x00,			/* DR2 */
    0x00,			/* DR3 */
    0x00,			/* DR6 */
    0x00,			/* DR7 */

    0x00,			/* TR3 */
    0x00,			/* TR4 */
    0x00,			/* TR5 */
    0x00,			/* TR6 */
    0x00,			/* TR7 */

    0x00,			/* ST(0) */
    0x00,			/* ST(1) */
    0x00,			/* ST(2) */
    0x00,			/* ST(3) */
    0x00,			/* ST(4) */
    0x00,			/* ST(5) */
    0x00,			/* ST(6) */
    0x00,			/* ST(7) */

    0x04,			/* null index reg for sib only */
};

PRIVATE opcode_t rmfunny[] =
{
    0x06,			/* BP */
    0x07,			/* BX */
    0x05,			/* DI */
    0x04,			/* SI */
};

PRIVATE opcode_t segoverride[] =
{
    0x2E,			/* CS */
    0x3E,			/* DS */
    0x26,			/* ES */
    0x64,			/* FS */
    0x65,			/* GS */
    0x36,			/* SS */
};

PRIVATE opcode_t ss[] =		/* scale to ss bits */
{
    0x00,			/* x0, illegal */
    0x00 << SS_SHIFT,		/* x1 */
    0x01 << SS_SHIFT,		/* x2 */
    0x00,			/* x3, illegal */
    0x02 << SS_SHIFT,		/* x4 */
    0x00,			/* x5, illegal */
    0x00,			/* x6, illegal */
    0x00,			/* x7, illegal */
    0x03 << SS_SHIFT,		/* x8 */
};

PRIVATE unsigned char calljmp_kludge;
PRIVATE opcode_t direction;
PRIVATE bool_t fpreg_allowed;
PRIVATE opcode_t segword;
/*
  Values of segword:
    BYTE_SEGWORD for byte ea's.
    SEGMOV for segment registers
    opcode for special registers
    WORDBIT for other word and dword ea's
*/

PRIVATE struct ea_s source;
PRIVATE struct ea_s source2;
PRIVATE struct ea_s target;

FORWARD void Eb P((struct ea_s *eap));
FORWARD void Ew P((struct ea_s *eap));
FORWARD void Ev P((struct ea_s *eap));
FORWARD void Ex P((struct ea_s *eap));
FORWARD void Gd P((struct ea_s *eap));
FORWARD void Gw P((struct ea_s *eap));
FORWARD void Gv P((struct ea_s *eap));
FORWARD void Gx P((struct ea_s *eap));
FORWARD void buildea P((struct ea_s *eap));
FORWARD void buildfloat P((void));
FORWARD void buildfreg P((void));
FORWARD void buildimm P((struct ea_s *eap, bool_pt signflag));
FORWARD void buildregular P((void));
FORWARD void buildsegword P((struct ea_s *eap));
FORWARD void buildunary P((opcode_pt opc));
FORWARD opsize_pt displsize P((struct ea_s *eap));
FORWARD reg_pt fpregchk P((void));
FORWARD bool_pt getaccumreg P((struct ea_s *eap));
FORWARD void getbinary P((void));
FORWARD bool_pt getdxreg P((struct ea_s *eap));
FORWARD void getea P((struct ea_s *eap));
FORWARD void getimmed P((struct ea_s *eap, count_t immed_count));
FORWARD void getindirect P((struct ea_s *eap));
FORWARD void getshift P((struct ea_s *eap));
FORWARD reg_pt indregchk P((reg_pt matchreg));
FORWARD void kgerror P((error_pt errnum));
FORWARD void lbranch P((int backamount));
FORWARD void notbytesize P((struct ea_s *eap));
FORWARD void notimmed P((struct ea_s *eap));
FORWARD void notindirect P((struct ea_s *eap));
FORWARD void notsegorspecreg P((struct ea_s *eap));
FORWARD void yesimmed P((struct ea_s *eap));
FORWARD void yes_samesize P((void));

PRIVATE void Eb(eap)
register struct ea_s *eap;
{
    Ex(eap);
    if (eap->size != 0x1)
    {
#ifndef NODEFAULTSIZE
	if (eap->size == 0x0)
	    eap->size = 0x1;
	else
#endif
	    kgerror(ILL_SIZE);
    }
}

PRIVATE void Ew(eap)
register struct ea_s *eap;
{
    Ex(eap);
    if (eap->size != 0x2)
    {
#ifndef NODEFAULTSIZE
	if (eap->size == 0x0)
	    eap->size = 0x2;
	else
#endif
	    kgerror(ILL_SIZE);
    }
}

PRIVATE void Ev(eap)
register struct ea_s *eap;
{
    Ex(eap);
    notbytesize(eap);
}

PRIVATE void Ex(eap)
register struct ea_s *eap;
{
    getea(eap);
    notimmed(eap);
    notsegorspecreg(eap);
}

PRIVATE void Gd(eap)
register struct ea_s *eap;
{
    Gx(eap);
    if (eap->size != 0x4)
	kgerror(ILL_SIZE);
}

PRIVATE void Gw(eap)
register struct ea_s *eap;
{
    Gx(eap);
    if (eap->size != 0x2)
	kgerror(ILL_SIZE);
}

PRIVATE void Gv(eap)
register struct ea_s *eap;
{
    Gx(eap);
    notbytesize(eap);
}

PRIVATE void Gx(eap)
register struct ea_s *eap;
{
    Ex(eap);
    notindirect(eap);
}

PRIVATE void buildea(eap)
register struct ea_s *eap;
{
    opsize_t asize;

    ++mcount;
    lastexp = eap->displ;
    if (eap->indcount == 0x0)
	postb = REG_MOD | rm[eap->base];
    else
    {
	if (eap->base == NOREG)
	{
	    if (eap->index == NOREG)
	    {
		if ((asize = displsize(eap)) > 0x2)
		    postb = D32_RM;
		else
		    postb = D16_RM;
	    }
	    else
	    {
		asize = 0x4;
		postb = SIB_NOBASE;	/* for sib later */
	    }
	}
	else
	{
	    if (eap->base > MAX16BITINDREG)
	    {
		asize = 0x4;
		postb = rm[eap->base];
	    }
	    else
	    {
		asize = 0x2;
		if (!(lastexp.data & UNDBIT) &&
		    !iswordorswordoffset(lastexp.offset))
		    error(ABOUNDS);
		if (eap->index == NOREG)
		    postb = rmfunny[eap->base];
		else if (eap->base <= MAX16BITINDREG)
		    postb = baseind16[eap->base + 0x4 * eap->index];
	    }
	}
	if (asize != defsize)
	    aprefix = 0x67;
	if (eap->base == NOREG)
	    mcount += asize;
	else if (lastexp.data & (FORBIT | RELBIT | UNDBIT) ||
		 !is8bitsignedoffset(lastexp.offset))
	{
	    postb |= MEM2_MOD;
	    mcount += asize;
	}
	else if (lastexp.offset != 0x0 ||
		 eap->base == BPREG && eap->index == NOREG ||
		 eap->base == EBPREG)
	{
	    postb |= MEM1_MOD;
	    ++mcount;
	}
	if (asize > 0x2 && (eap->base == ESPREG || eap->index != NOREG))
	{
	    sib = ss[eap->scale] |
		(rm[eap->index] << INDEX_SHIFT) |
		(postb & RM_MASK);
	    postb = (postb & MOD_MASK) | SIB_RM;
	    ++mcount;
	}
    }
}

PRIVATE void buildfloat()
{
    if (mcount != 0x0)
    {
	buildea(&source);
	oprefix = 0x0;
	postb |= (opcode & 0x07) << REG_SHIFT;
	opcode = ESCAPE_OPCODE_BASE | ((opcode & 0x70) >> 0x4);
    }
}

PRIVATE void buildfreg()
{
    mcount += 0x2;
    oprefix = 0x0;
    postb = REG_MOD | ((opcode & 0x07) << REG_SHIFT) | (target.base - ST0REG);
    opcode = ESCAPE_OPCODE_BASE | ((opcode & 0x70) >> 0x4);
}

PRIVATE void buildimm(eap, signflag)
register struct ea_s *eap;
bool_pt signflag;
{
    immadr = eap->displ;
    immcount = eap->size;
    if (!(immadr.data & (FORBIT | RELBIT | UNDBIT)))
    {
	if (immcount == 0x1)
	{
	    if ((offset_t) (immadr.offset + 0x80) >= 0x180)
		datatoobig();
	}
	else if (signflag && is8bitsignedoffset(immadr.offset))
	{
	    opcode |= SIGNBIT;
	    immcount = 0x1;
	}
	else if (immcount == 0x2)
	{
	    if ((offset_t) (immadr.offset + 0x8000L) >= 0x18000L)
		datatoobig();
	}
    }
}

PRIVATE void buildregular()
{
    if (mcount != 0x0)
    {
	buildea(&target);
	postb |= regbits[source.base];
    }
}

/* Check size and build segword. */

PRIVATE void buildsegword(eap)
register struct ea_s *eap;
{
    if (eap->size == 0x0)
#ifdef NODEFAULTSIZE
	kgerror(SIZE_UNK);
#else
	eap->size = defsize;
#endif
    if (eap->indcount != 0x0 || eap->base == NOREG)
    {
	segword = WORDBIT;
	if (eap->size == 0x1)
	    segword = BYTE_SEGWORD;
    }
    else
	segword = regsegword[eap->base];
}

PRIVATE void buildunary(opc)
opcode_pt opc;
{
    if (mcount != 0x0)
    {
	buildea(&target);
	postb |= opcode;
	opcode = opc;
    }
}

PRIVATE opsize_pt displsize(eap)
register struct ea_s *eap;
{
    opsize_t asize;

    asize = defsize;
    if (!(eap->displ.data & UNDBIT))
    {
	if (asize > 0x2)
	{
	    if (!(eap->displ.data & (FORBIT | RELBIT)) &&
		iswordadr(eap->displ.offset))
		asize = 0x2;
	}
	else if (!iswordorswordoffset(eap->displ.offset))
				/* should really use iswordadr() */
				/* but compiler generates signed offsets */
	{
	    if (!(eap->displ.data & (FORBIT | RELBIT)))
		asize = 0x4;
	    else if (pass2)
		error(ABOUNDS);
	}
    }
    return asize;
}

PRIVATE reg_pt fpregchk()
{
    reg_pt fpreg;

    fpreg_allowed = TRUE;
    fpreg = regchk();
    fpreg_allowed = FALSE;
    if (fpreg != ST0REG)
	return NOREG;
    getsym();
    if (sym == LPAREN)
    {
	getsym();
	if (sym != INTCONST || (unsigned) number >= 0x8)
	    error(ILL_FP_REG);
	else
	{
	    fpreg += number;
	    getsym();
	    if (sym != RPAREN)
		error(RPEXP);
	    else
		getsym();
	}
    }
    return fpreg;
}

PRIVATE bool_pt getaccumreg(eap)
register struct ea_s *eap;
{
    if ((eap->base = regchk()) != AXREG && eap->base != ALREG
	&& eap->base != EAXREG)
	return FALSE;
    getsym();
    if ((eap->size = regsize[eap->base]) > 0x1 && eap->size != defsize)
	oprefix = 0x66;
    return TRUE;
}

/*
  Get binary ea's in target & source (flipped if direction is set).
  Put size in source if not already.
  Initialise direction, segword, bump mcount.
*/

PRIVATE void getbinary()
{
    ++mcount;
    getea(&target);
    if (target.indcount == 0x0 && target.base == NOREG)
    {
	error(ILL_IMM_MODE);
	target.base = AXREG;
	target.size = defsize;
    }
    getcomma();
    getea(&source);
    if (source.size == 0x0)
	source.size = target.size;
    else if (target.size != 0x0 && target.size != source.size)
    {
	kgerror(MISMATCHED_SIZE);
	return;
    }
    if (source.indcount == 0x0 && regsegword[target.base] < SEGMOV)
	direction = 0x0;
    else if (target.indcount == 0x0 && regsegword[source.base] < SEGMOV)
    {
	struct ea_s swap;

	direction = TOREGBIT;
	swap = source;
	source = target;
	target = swap;
    }
    else if (target.indcount != 0x0)
    {
	kgerror(ILL_IND_TO_IND);
	return;
    }
    else
    {
	kgerror(ILL_SEG_REG);
	return;
    }
    buildsegword(&source);
}

PRIVATE bool_pt getdxreg(eap)
register struct ea_s *eap;
{
    if ((eap->base = regchk()) != DXREG)
	return FALSE;
    getsym();
    return TRUE;
}

/* parse effective address */

/*
  Syntax is restrictive in that displacements must be in the right spots
  and will not be added up.

  optional size-type prefix, which is
    BYTE
    BYTE PTR
    WORD
    WORD PTR
    DWORD
    DWORD PTR
    PTR
  reg
  segreg
  [scaled index]
  where scaled index =
    indreg
    indreg*scale
    indreg+indreg
    indreg+indreg*scale
  [scaled index+displ]
  [scaled index-displ]
  optional-immediate-prefix displ[scaled index]
  [displ]
  optional-imediate-prefix displ
  (scaled index)                                   -- anachronism
  optional-imediate-prefix displ(scaled index)     -- anachronism
*/

PRIVATE void getea(eap)
register struct ea_s *eap;
{
    bool_t leading_displ;
    bool_t leading_immed;
    register struct sym_s *symptr;

    leading_immed = leading_displ = lastexp.data = eap->indcount
		  = lastexp.offset = 0x0;
    eap->index = eap->base = NOREG;
    eap->scale = 0x1;
    eap->size = mnsize;		/* 0x1 for byte ops, else 0x0 */

    if (sym == IDENT)
    {
	if ((symptr = gsymptr)->type & MNREGBIT)
	{
	    if (symptr->data & SIZEBIT)
	    {
		getsym();
		if (symptr->value_reg_or_op.op.opcode == 0x0)
		    eap->indcount = 0x2 - calljmp_kludge;
		else
		{
		    if (eap->size != 0x0)
		    {
			if (eap->size != symptr->value_reg_or_op.op.opcode)
			    error(MISMATCHED_SIZE);
		    }
		    else
			eap->size = symptr->value_reg_or_op.op.opcode;
		    if (eap->size > 0x1 && eap->size != defsize)
			oprefix = 0x66;
		    if (sym == IDENT &&
			(symptr = gsymptr)->type & MNREGBIT &&
			symptr->data & SIZEBIT &&
			symptr->value_reg_or_op.op.routine == PTROP)
		    {
			getsym();
			eap->indcount = 0x2 - calljmp_kludge;
		    }
		}
	    }
	}
	if (!(symptr->type & (LABIT | MACBIT | MNREGBIT | VARBIT)))
	    symptr->data |= FORBIT;	/* show seen in advance */
    }
    if ((eap->base = regchk()) != NOREG)
    {
	getsym();
	if (eap->indcount != 0x0)
	{
	    error(ILL_IND_PTR);
	    eap->indcount = 0x0;
	}
	if (eap->size != 0x0 && eap->size != regsize[eap->base])
	    error(MISMATCHED_SIZE);
	if ((eap->size = regsize[eap->base]) > 0x1 && eap->size != defsize)
	    oprefix = 0x66;
	eap->displ = lastexp;
	return;
    }
    if (sym != lindirect)
    {
	if (sym == IMMEDIATE || sym == STAR)
	{
	    /* context-sensitive, STAR means signed immediate here */
	    leading_immed = TRUE;
	    getsym();
	}
	leading_displ = TRUE;
	expres();
	eap->displ = lastexp;
    }
    if (sym == lindirect)
    {
	getsym();
	eap->indcount = 0x2 - calljmp_kludge;
	if ((eap->base = indregchk((reg_pt) NOREG)) != NOREG)
	{
	    if (eap->indcount == 0x0 && leading_displ)
		error(IND_REQ);
	    getsym();
	    if (sym == ADDOP)
	    {
		getsym();
		if ((eap->index = indregchk(eap->base)) != NOREG)
		    getsym();
		else
		{
		    if (eap->indcount == 0x0)
			error(IND_REQ);
		    if (leading_displ)
			error(REPEATED_DISPL);
		    expres();	/* this eats ADDOP, SUBOP, MULOP */
		}
	    }
	    if (sym == STAR)
	    {
		/* context-sensitive, STAR means scaled here*/
		if (eap->index == NOREG && eap->base == ESPREG)
		{
		    error(INDEX_REG_EXP);
		    eap->base = EAXREG;
		}
		getsym();
		factor();
		chkabs();
		if (!(lastexp.data & UNDBIT) && lastexp.offset != 0x1)
		{
		    if (eap->base <= MAX16BITINDREG ||
			lastexp.offset != 0x2 && lastexp.offset != 0x4 &&
			lastexp.offset != 0x8)
			error(ILL_SCALE);
		    else
		    {
			eap->scale = lastexp.offset;
			if (eap->index == NOREG)
			{
			    eap->index = eap->base;
			    eap->base = NOREG;
			}
		    }
		}
		lastexp.data = lastexp.offset = 0x0;
	    }
	    if ((sym == ADDOP || sym == SUBOP))
	    {
		if (eap->indcount == 0x0)
		    error(IND_REQ);
		if (leading_displ)
		    error(REPEATED_DISPL);
		expres();
	    }
	}
	else
	{
	    if (leading_displ)
		error(REPEATED_DISPL);
	    expres();
	}
	if (sym != rindirect)
	    error(rindexp);
	else
	    getsym();
    }
    else if (!leading_immed && idefsize <= 0x2)
	eap->indcount = 0x1;	/* compatibility kludge */
    if (!leading_displ)
	eap->displ = lastexp;
}

PRIVATE void getimmed(eap, immed_count)
struct ea_s *eap; 
count_t immed_count;
{
    getea(eap);
    yesimmed(eap);
    if (mcount != 0x0)
    {
	eap->size = immed_count;
	buildimm(eap, FALSE);
    }
}

PRIVATE void getindirect(eap)
register struct ea_s *eap;
{
    getea(eap);
    if (eap->indcount == 0x0)
	kgerror(IND_REQ);
}

PRIVATE void getshift(eap)
register struct ea_s *eap;
{
    getcomma();
    getea(eap);
    if (eap->base != CLREG)
	yesimmed(eap);
}

/*
  Check if current symbol is a compatible index register.
  Generate error if it is a reg but not a compatible index.
  Return register number (adjusted if necessary to a legal index) or NOREG.
*/

PRIVATE reg_pt indregchk(matchreg)
reg_pt matchreg;
{
    reg_pt reg;

    if ((reg = regchk()) != NOREG)
    {
	switch (matchreg)
	{
	case BPREG:
	case BXREG:
	    if (reg != DIREG && reg != SIREG)
	    {
		reg = SIREG;
		error(INDEX_REG_EXP);
	    }
	    break;
	case DIREG:
	case SIREG:
	    if (reg != BPREG && reg != BXREG)
	    {
		reg = BXREG;
		error(INDEX_REG_EXP);
	    }
	    break;
	case NOREG:
	    break;
	default:
	    if (reg <= MAX16BITINDREG || reg == ESPREG)
	    {
		reg = EAXREG;
		error(INDEX_REG_EXP);
	    }
	    break;
	}
	if (reg > MAXINDREG && calljmp_kludge == 0x0)
	{
	    if (matchreg != NOREG)
		reg = EAXREG;
	    else
		reg = BXREG;
	    error(INDEX_REG_EXP);
	}
    }
    return reg;
}

PRIVATE void kgerror(errnum)
error_pt errnum;
{
    error(errnum);
    sprefix = oprefix = aprefix = mcount = 0x0;
}

PRIVATE void lbranch(backamount)
int backamount;
{
    mcount += defsize + 0x1;
    if (pass2)
    {
	reldata();
	if (!(lastexp.data & (RELBIT | UNDBIT)))
	{
	    lastexp.offset = lastexp.offset - lc - lcjump;
	    if (backamount != 0x0 && !(lastexp.data & IMPBIT) &&
		lastexp.offset + backamount < 0x80 + backamount)
		error(SHORTB);	/* -0x8? to 0x7F, warning */
	}
    }
}

/* BCC (long branches emulated by short branch over & long jump) */

PUBLIC void mbcc()
{
    getea(&target);
    if (target.indcount >= 0x2 || target.base != NOREG)
	kgerror(REL_REQ);
    else
    {
	if (defsize != 0x2)
	{
	    page = PAGE1_OPCODE;
	    ++mcount;
	    opcode += 0x10;
	    lbranch(0x84);
	}
	else
	{
	    aprefix = opcode ^ 0x1;	/* kludged storage for short branch
					   over */
	    oprefix = defsize + 0x1;
	    mcount += 0x2;
	    opcode = JMP_OPCODE;
	    lbranch(0x83);
	    mcount -= 0x2;
	}
    }
}

/* bswap r32 */

PUBLIC void mbswap()
{
    ++mcount;
    Gd(&target);
    opcode |= rm[target.base];
}

/* BR, CALL, J, JMP */

PUBLIC void mcall()
{
    opcode_pt far;
    bool_t indirect;
    register struct sym_s *symptr;

    far = 0x0;
    if (sym == IDENT && (symptr = gsymptr)->type & MNREGBIT &&
	symptr->data & SIZEBIT &&
	symptr->value_reg_or_op.op.routine == FAROP)
    {
	far = 0x8;
	getsym();
    }
    indirect = FALSE;
    if (asld_compatible && idefsize <= 0x2)
    {
	calljmp_kludge = 0x2;
	if (sym == INDIRECT)
	{
	    calljmp_kludge = 0x0;
	    indirect = TRUE;
	    getsym();
	}
    }
    getea(&target);
    if (indirect && target.indcount == 0x1)
	target.indcount = 0x2;
    calljmp_kludge = 0x0;
    if (sym == COLON)
    {
	if (opcode == JMP_SHORT_OPCODE)
	    opcode = JMP_OPCODE;
	++mcount;
	yesimmed(&target);
	getsym();
	getea(&source);
	yesimmed(&source);
	if (mcount != 0x0)
	{
	    if (opcode == JMP_OPCODE)
		opcode = 0xEA;
	    else
		opcode = 0x9A;
	    lastexp = source.displ;
	    if (!(lastexp.data & (FORBIT | RELBIT | UNDBIT)) &&
		defsize == 0x2 &&
		(offset_t) (lastexp.offset + 0x8000L) >= 0x18000L)
		datatoobig();
	    mcount += defsize;
	    target.size = 0x2;
	    buildimm(&target, FALSE);
	}
    }
    else if (target.indcount >= 0x2 || target.base != NOREG)
    {
	++mcount;
	notsegorspecreg(&target);
	if (target.indcount == 0)
	    notbytesize(&target);
	if (mcount != 0x0)
	{
	    if (opcode == JMP_SHORT_OPCODE)
		opcode = JMP_OPCODE;
	    buildea(&target);
	    if (opcode == JMP_OPCODE)
		opcode = 0x20;
	    else
		opcode = 0x10;
	    postb |= opcode + far;
	    opcode = 0xFF;
	}
    }
    else if (opcode == JMP_SHORT_OPCODE)
    {
	if (jumps_long)
	{
	    opcode = JMP_OPCODE;
	    lbranch(0x83);
	}
	else
	{
	    lastexp = target.displ;
	    if (lastexp.data & IMPBIT)
	    {
		error(NONIMPREQ);
		lastexp.data = FORBIT | UNDBIT;
    	    }
    	    mshort2();
	}
    }
    else
	lbranch(opcode == JMP_OPCODE ? 0x83 : 0x0);
}

/* CALLI, JMPI */

PUBLIC void mcalli()
{
    bool_t indirect;

    ++mcount;
    indirect = FALSE;
    if (sym == INDIRECT)
    {
	getsym();
	indirect = TRUE;
    }
    getea(&target);
    if (target.indcount >= 0x2 || target.base != NOREG)
	indirect = TRUE;
    if (indirect)
    {
	buildea(&target);
	if (opcode == 0xEA)
	    opcode = 0x28;
	else
	    opcode = 0x18;
	postb |= opcode;
	opcode = 0xFF;
    }
    else
    {
	getcomma();
	getea(&source);
	yesimmed(&source);
	if (mcount != 0x0)
	{
	    lastexp = target.displ;
	    if (!(lastexp.data & (FORBIT | RELBIT | UNDBIT)) &&
		defsize == 0x2 &&
		(offset_t) (lastexp.offset + 0x8000L) >= 0x18000L)
		datatoobig();
	    mcount += defsize;
	    source.size = 0x2;
	    buildimm(&source, FALSE);
	}
    }
}

/* DIV, IDIV, MUL */

PUBLIC void mdivmul()
{
    if (getaccumreg(&source))
    {
	++mcount;
	getcomma();
	Ex(&target);
	yes_samesize();
	buildunary(0xF6 | regsegword[source.base]);
    }
    else
	mnegnot();
}

/* ENTER */

PUBLIC void menter()
{
    ++mcount;
    getimmed(&target, 0x2);
    getcomma();
    getimmed(&source, 0x1);
    if (mcount != 0x0)
    {
	mcount += 2;
	lastexp = target.displ;	/* getimmed(&source) wiped it out */
    }
}

/* arpl r/m16,r16 (Intel manual opcode chart wrongly says EwRw) */

PUBLIC void mEwGw()
{
    ++mcount;
    Ew(&target);
    getcomma();
    Gw(&source);
    oprefix = 0x0;
    buildregular();
}

/* [cmpxchg xadd] [r/m8,r8 r/m16,r16, r/m32,r32] */

PUBLIC void mExGx()
{
    ++mcount;
    Ex(&target);
    getcomma();
    Gx(&source);
    yes_samesize();
    opcode |= segword;
    buildregular();
}

PUBLIC void mf_inher()
{
    mcount += 0x2;
    postb = REG_MOD | (opcode & ~REG_MOD);
    opcode = ESCAPE_OPCODE_BASE | (opcode >> 0x6);
    if (opcode == ESCAPE_OPCODE_BASE)
	opcode = ESCAPE_OPCODE_BASE | 0x6;	/* fix up encoding of fcompp */
}

/* [fldenv fnsave fnstenv frstor] mem */

PUBLIC void mf_m()
{
    ++mcount;
    getindirect(&source);
    if (source.size != 0x0)
	kgerror(ILL_SIZE);
    buildfloat();
}

/* [fldcw fnstcw] mem2i */

PUBLIC void mf_m2()
{
    ++mcount;
    getindirect(&source);
    if (source.size != 0x0 && source.size != 0x2)
	kgerror(ILL_SIZE);
    buildfloat();
}

/* fnstsw [mem2i ax] */

PUBLIC void mf_m2_ax()
{
    if (getaccumreg(&target))
    {
	if (target.base != AXREG)
	    kgerror(ILLREG);
	else
	{
	    opcode = 0x74;
	    target.base = ST0REG;	/* fake, really ax */
	    buildfreg();
	}
    }
    else
	mf_m2();
}

/* [fiadd ficom ficomp fidiv fidivr fimul fist fisub fisubr] [mem2i mem4i] */

PUBLIC void mf_m2_m4()
{
    ++mcount;
    getindirect(&source);
    if (source.size == 0x0)
	kgerror(SIZE_UNK);
    else if (source.size == 0x2)
	opcode |= 0x40;
    else if (source.size != 0x4)
	kgerror(ILL_SIZE);
    buildfloat();
}

/* [fild fistp] [mem2i mem4i mem8i] */

PUBLIC void mf_m2_m4_m8()
{
    ++mcount;
    getindirect(&source);
    if (source.size == 0x0)
	kgerror(SIZE_UNK);
    else if (source.size == 0x2)
	opcode |= 0x40;
    else if (source.size == 0x8)
	opcode |= 0x45;		/* low bits 0 -> 5 and 3 -> 7 */
    else if (source.size != 0x4)
	kgerror(ILL_SIZE);
    buildfloat();
}

/* [fcom fcomp] [mem4r mem8r optional-st(i)] */

PUBLIC void mf_m4_m8_optst()
{
    if (sym == EOLSYM)
    {
	target.base = ST1REG;
	buildfreg();
    }
    else
	mf_m4_m8_st();
}

/* [fadd fdiv fdivr fmul fsub fsubr] [mem4r mem8r st,st(i) st(i),st] */

PUBLIC void mf_m4_m8_stst()
{
    target.base = fpregchk();
    if (target.base != NOREG)
    {
	getcomma();
	source.base = fpregchk();
	if (source.base == NOREG)
	{
	    error(FP_REG_REQ);
	    source.base = ST0REG;
	}
	if (target.base == ST0REG)
	    target.base = source.base;
	else
	{
	    if (source.base != ST0REG)
		error(ILL_FP_REG_PAIR);
	    opcode |= 0x40;
	    if ((opcode & 0x07) >= 0x4)
		opcode ^= 0x01;	/* weird swap of fdiv/fdivr, fsub/fsubr */
	}
	buildfreg();
    }
    else
    {
	++mcount;
	getindirect(&source);
	if (source.size == 0x0)
	    kgerror(SIZE_UNK);
	else if (source.size == 0x8)
	    opcode |= 0x40;
	else if (source.size != 0x4)
	    kgerror(ILL_SIZE);
	buildfloat();
    }
}

/* fst [mem4r mem8r st(i)] */

PUBLIC void mf_m4_m8_st()
{
    target.base = fpregchk();
    if (target.base != NOREG)
    {
	if (opcode == FST_ENCODED)
	    opcode |= 0x40;
	buildfreg();
    }
    else
    {
	++mcount;
	getindirect(&source);
	if (source.size == 0x0)
	    kgerror(SIZE_UNK);
	else if (source.size == 0x8)
	    opcode |= 0x40;
	else if (source.size != 0x4)
	    kgerror(ILL_SIZE);
	buildfloat();
    }
}

/* [fld fstp] [mem4r mem8r mem10r st(i)] */

PUBLIC void mf_m4_m8_m10_st()
{
    target.base = fpregchk();
    if (target.base != NOREG)
    {
	if (opcode == FSTP_ENCODED)
	    opcode |= 0x40;
	buildfreg();
    }
    else
    {
	++mcount;
	getindirect(&source);
	if (source.size == 0x0)
	    kgerror(SIZE_UNK);
	else if (source.size == 0x8)
	    opcode |= 0x40;
	else if (source.size == 0xA)
	    opcode |= 0x25;	/* low bits 0 -> 5 and 3 -> 7 */
	else if (source.size != 0x4)
	    kgerror(ILL_SIZE);
	buildfloat();
    }
}

/* [fbld fbstp] mem10r */

PUBLIC void mf_m10()
{
    ++mcount;
    getindirect(&source);
    if (source.size != 0xA)
	kgerror(ILL_SIZE);
    buildfloat();
}

/* ffree st(i) */

PUBLIC void mf_st()
{
    target.base = fpregchk();
    if (target.base == NOREG)
	kgerror(FP_REG_REQ);
    buildfreg();
}

/* [fucom fucomp fxch] optional-st(i) */

PUBLIC void mf_optst()
{
    if (sym == EOLSYM)
    {
	target.base = ST1REG;
	buildfreg();
    }
    else
	mf_st();
}

/* [faddp fdivp fdivrp fmulp fsubp fsubrp] st(i),st */

PUBLIC void mf_stst()
{
    target.base = fpregchk();
    if (target.base == NOREG)
    {
	kgerror(FP_REG_REQ);
	return;
    }
    getcomma();
    source.base = fpregchk();
    if (source.base == NOREG)
    {
	kgerror(FP_REG_REQ);
	return;
    }
    if (source.base != ST0REG)
    {
	kgerror(ILL_FP_REG);
	return;
    }
    buildfreg();
}

PUBLIC void mf_w_inher()
{
    sprefix = WAIT_OPCODE;
    mf_inher();
}

/* [fsave fstenv] mem */

PUBLIC void mf_w_m()
{
    sprefix = WAIT_OPCODE;
    mf_m();
}

/* fstcw mem2i */

PUBLIC void mf_w_m2()
{
    sprefix = WAIT_OPCODE;
    mf_m2();
}

/* fstsw [mem2i ax] */

PUBLIC void mf_w_m2_ax()
{
    sprefix = WAIT_OPCODE;
    mf_m2_ax();
}

/* ADC, ADD, AND, CMP, OR, SBB, SUB, XOR */

PUBLIC void mgroup1()
{
    getbinary();
    notsegorspecreg(&source);
    if (mcount != 0x0)
    {
	if (source.base == NOREG)
	{
	    if (target.indcount == 0x0 && (target.base == ALREG ||
					   target.base == AXREG ||
					   target.base == EAXREG &&
			  (source.displ.data & (FORBIT | RELBIT | UNDBIT) ||
			   !is8bitsignedoffset(source.displ.offset))))
	    {
		opcode |= 0x04 | segword;
		buildimm(&source, FALSE);
	    }
	    else
	    {
		buildunary(0x80 | segword);
		buildimm(&source, TRUE);
	    }
	}
	else
	{
	    opcode |= direction | segword;
	    buildregular();
	}
    }
}

/* RCL, RCR, ROL, ROR, SAL, SAR, SHL, SHR */

PUBLIC void mgroup2()
{
    ++mcount;
    Ex(&target);
    buildsegword(&target);
    getshift(&source);
    if (mcount != 0x0)
    {
	buildunary(0xD0 | segword);
	if (source.base == CLREG)
	    opcode |= 0x2;
	else if (source.displ.offset != 0x1)
	{
	    opcode -= 0x10;
	    source.size = 0x1;
	    buildimm(&source, FALSE);
	}
    }
}

/* LLDT, LTR, SLDT, STR, VERR, VERW */

PUBLIC void mgroup6()
{
    ++mcount;
    Ew(&target);
    oprefix = 0x0;
    buildunary(0x0);
}

/* INVLPG, LGDT, LIDT, LMSW, SGDT, SIDT, SMSW */

PUBLIC void mgroup7()
{
    ++mcount;
    if (opcode == 0x20 || opcode == 0x30)
    {
	Ew(&target);
	oprefix = 0x0;
    }
    else
    {
	getindirect(&target);
	oprefix = 0x0;
	if (target.size != 0x0 && target.size != 0x6)
	    error(MISMATCHED_SIZE);	/* XXX - size 6 wrong for INVLPG? */
    }
    buildunary(0x1);
}

/* BT, BTR, BTS, BTC */

PUBLIC void mgroup8()
{
    ++mcount;
    Ev(&target);
    getcomma();
    /* Gv or Ib */
    getea(&source);
    notindirect(&source);
    notsegorspecreg(&source);
    if (mcount != 0x0)
    {
	if (source.base == NOREG)
	{
	    buildunary(0xBA);
	    source.size = 0x1;
	    buildimm(&source, TRUE);
	}
	else
	{
	    yes_samesize();
	    opcode += 0x83;
	    buildregular();
	}
    }
}

/* BSF, BSR, LAR, LSL (Intel manual opcode chart wrongly says GvEw for L*) */

PUBLIC void mGvEv()
{
    ++mcount;
    Gv(&source);
    getcomma();
    Ev(&target);
    yes_samesize();
    buildregular();
}

/* bound [r16,m16&16 r32,m32&32] */

PUBLIC void mGvMa()
{
    ++mcount;
    Gv(&source);
    getcomma();
    getindirect(&target);
    yes_samesize();
    buildregular();
}

/* LDS, LES, LFS, LGS, LSS */

PUBLIC void mGvMp()
{
    ++mcount;
    Gv(&source);
    getcomma();
    getindirect(&target);
    if (target.size != 0x0 && target.size != 0x2 + source.size)
	error(MISMATCHED_SIZE);
    buildregular();
}

/* IMUL */

PUBLIC void mimul()
{
    ++mcount;
    Ex(&target);
    if (sym != COMMA)
    {
	buildsegword(&target);
	buildunary(0xF6 | segword);
	return;
    }
    getcomma();
    notindirect(&target);
    source = target;		/* direction is swapped */
    getea(&target);
    notsegorspecreg(&target);
    yes_samesize();
    if (sym != COMMA && (target.indcount != 0x0 || target.base != NOREG))
    {
	page = PAGE1_OPCODE;
	++mcount;
	opcode = 0xAF;
	buildregular();
    }
    else
    {
	if (sym == COMMA)
	{
	    getsym();
	    getea(&source2);
	    yesimmed(&source2);
	}
	else
	{
	    source2 = target;
	    target = source;
	}
	source2.size = target.size;
	if (is8bitsignedoffset(source2.displ.offset))
	{
	    source2.size = 0x1;
	    opcode = 0x6B;
	}
	else
	{
	    source2.size = target.size;
	    opcode = 0x69;
	}
	buildregular();
	if (mcount != 0x0)
	    buildimm(&source2, FALSE);
    }
}

/* IN */

PUBLIC void min()
{
    ++mcount;
    if (opcode & WORDBIT)	/* inw; ind not supported */
	mnsize = 0x2;
    if (sym == EOLSYM && mnsize != 0x0)
	    target.size = mnsize;
    else
    {
	if (getaccumreg(&target))
	{
	    if (mnsize != 0x0 && regsize[target.base] != mnsize)
		error(MISMATCHED_SIZE);
	    getcomma();
	}
	else
	    target.size = regsize[target.base = mnsize < 0x2 ? ALREG : AXREG];
	opcode |= regsegword[target.base];
	if (!getdxreg(&source))
	{
	    getimmed(&source, 0x1);
	    opcode -= 0x8;
	}
    }
    if (target.size > 0x1 && target.size != defsize)
	oprefix = 0x66;
}

/* DEC, INC */

PUBLIC void mincdec()
{
    ++mcount;
    Ex(&target);
    buildsegword(&target);
    if (target.indcount == 0x0 && segword == WORDBIT)
	opcode |= 0x40 | rm[target.base];
    else
	buildunary(0xFE | segword);
}

/* CBW, CWD, CMPSW, INSW, IRET, LODSW, POPA, POPF, PUSHA, PUSHF */
/* MOVSW, OUTSW, SCASW, STOSW */

PUBLIC void minher16()
{
    minher();
    if (defsize != 0x2)
	oprefix = 0x66;
}

/* CWDE, CDQ, CMPSD, INSD, IRETD, LODSD, POPAD, POPFD, PUSHAD, PUSHFD */
/* MOVSD, OUTSD, SCASD, STOSD */

PUBLIC void minher32()
{
    minher();
    if (defsize != 0x4)
	oprefix = 0x66;
}

/* AAD, AAM */

PUBLIC void minhera()
{
    ++mcount;
    if (sym == EOLSYM)
    {
	target.displ.offset = 0xA;
	target.size = 0x1;
	buildimm(&target, FALSE);
    }
    else
	getimmed(&target, 0x1);
}

/* INT */

PUBLIC void mint()
{
    ++mcount;
    getimmed(&target, 0x1);
    if (!(immadr.data & (FORBIT | RELBIT | UNDBIT)) &&
	(opcode_t) immadr.offset == 0x3)
    {
	immcount = 0x0;
	opcode = 0xCC;
    }
}

/* JCC */

PUBLIC void mjcc()
{
    if (jumps_long && opcode < 0x80)	/* above 0x80 means loop - not long */
	mbcc();
    else
	mshort();
}

/* JCXZ, JECXZ */

PUBLIC void mjcxz()
{
    if (opcode != defsize)
    {
	aprefix = 0x67;
	++mcount;		/* quick fix - mshort() needs to know */
    }
    opcode = 0xE3;
    mshort();
    if (aprefix != 0x0)
	--mcount;		/* quick fix - main routine bumps it again */
}

/* LEA */

PUBLIC void mlea()
{
    Gv(&source);		/* back to front */
    getcomma();
    ++mcount;
    getindirect(&target);
    yes_samesize();
    buildregular();
}

/* MOV */

PUBLIC void mmov()
{
    getbinary();
    if (segword >= SEGMOV)
    {
	oprefix = 0x0;
	notimmed(&target);	/* target is actually the source */
	if (segword > SEGMOV)	/* special reg */
	    notindirect(&target);
    }
    if (mcount != 0x0)
    {
	if (target.base == NOREG && target.index == NOREG &&
	    (source.base == ALREG || source.base == AXREG ||
	     source.base == EAXREG))
	{
	    opcode = 0xA0 | (direction ^ TOREGBIT) | segword;
	    lastexp = target.displ;
	    if ((source.size = displsize(&target)) != defsize)
		aprefix = 0x67;
	    mcount += source.size;
	}
	else if (source.base == NOREG)
	{
	    if (target.indcount == 0x0)
		opcode = 0xB0 | (segword << 0x3) | rm[target.base];
	    else
	    {
		buildea(&target);
		opcode = 0xC6 | segword;
	    }
	    buildimm(&source, FALSE);
	}
	else
	{
	    if (isspecreg(source.base))
	    {
		page = PAGE1_OPCODE;
		++mcount;
		opcode = 0x0;
	    }
	    opcode |= direction | segword;
	    buildregular();
	}
    }
}

/* MOVSX, MOVZX */

PUBLIC void mmovx()
{
    ++mcount;
    Gv(&source);
    getcomma();
    Ex(&target);
    if (target.size == 0x0)
	kgerror(SIZE_UNK);
    if (target.size > 0x2)
	kgerror(ILL_SIZE);
    oprefix = 0x0;
    if (source.size != defsize)
	oprefix = 0x66;
    buildsegword(&target);
    opcode |= segword;
    buildregular();
}

/* NEG, NOT */

PUBLIC void mnegnot()
{
    ++mcount;
    Ex(&target);
    buildsegword(&target);
    buildunary(0xF6 | segword);
}

/* OUT */

PUBLIC void mout()
{
    ++mcount;
    if (opcode & WORDBIT)	/* outw; outd not supported */
	mnsize = 0x2;
    if (sym == EOLSYM && mnsize != 0x0)
	    source.size = mnsize;
    else
    {
	if (!getdxreg(&target))
	{
	    getimmed(&target, 0x1);
	    opcode -= 0x8;
	}
	if (sym == COMMA)
	{
	    getsym();
	    if (!getaccumreg(&source))
		kgerror(AL_AX_EAX_EXP);
	    else if (mnsize != 0x0 && regsize[source.base] != mnsize)
		error(MISMATCHED_SIZE);
	}
	else
	    source.size = regsize[source.base = mnsize < 0x2 ? ALREG : AXREG];
	opcode |= regsegword[source.base];
    }
    if (source.size > 0x1 && source.size != defsize)
	oprefix = 0x66;
}

/* POP, PUSH */

PUBLIC void mpushpop()
{
    opcode_t oldopcode;

    ++mcount;
    getea(&target);
    buildsegword(&target);
    notbytesize(&target);
    if ((oldopcode = opcode) == POP_OPCODE)
    {
	notimmed(&target);
	if (target.base == CSREG)
	    kgerror(ILL_SEG_REG);
    }
    if (mcount != 0x0)
    {
	if (target.indcount == 0x0)
	{
	    if (segword == SEGMOV)
	    {
		switch (target.base)
		{
		case CSREG:
		    opcode = 0x0E;
		    break;
		case DSREG:
		    opcode = 0x1E;
		    break;
		case ESREG:
		    opcode = 0x06;
		    break;
		case SSREG:
		    opcode = 0x16;
		    break;
		case FSREG:
		    opcode = 0xA0;
		    page = PAGE1_OPCODE;
		    ++mcount;
		    break;
		case GSREG:
		    opcode = 0xA8;
		    page = PAGE1_OPCODE;
		    ++mcount;
		    break;
		}
		if (oldopcode == POP_OPCODE)
		    ++opcode;
	    }
	    else if (target.base != NOREG)
	    {
		opcode = 0x50 | rm[target.base];
		if (oldopcode == POP_OPCODE)
		    opcode |= 0x8;
	    }
	    else
	    {
		opcode = 0x68;
		if (oldopcode == POP_OPCODE)
		    ++opcode;
		buildimm(&target, TRUE);
	    }
	}
	else
	{
	    buildea(&target);
	    if (oldopcode == PUSH_OPCODE)
		postb |= 0x6 << REG_SHIFT;
	}
    }
}

/* RET, RETF */

PUBLIC void mret()
{
    ++mcount;
    if (sym != EOLSYM)
    {
	--opcode;
	getimmed(&target, 0x2);
    }
}

/* SEG CS/DS/ES/FS/GS/SS */

PUBLIC void mseg()
{
    reg_pt reg;

    if (regsegword[reg = regchk()] != SEGMOV)
	error(SEG_REG_REQ);
    else
    {
	getsym();
	++mcount;
	opcode = (segoverride - CSREG)[reg];
    }
}

/* SETCC */

PUBLIC void msetcc()
{
    ++mcount;
    Eb(&target);
    if (mcount != 0x0)
	buildea(&target);
}

/* SHLD, SHRD */

PUBLIC void mshdouble()
{
    ++mcount;
    Ev(&target);
    getcomma();
    Gv(&source);
    yes_samesize();
    buildregular();
    getshift(&source2);
    lastexp = target.displ;	/* getshift() wiped it out */
    if (mcount != 0x0)
    {
	if (source2.base == CLREG)
	    opcode |= 0x1;
	else
	{
	    source2.size = 0x1;
	    buildimm(&source2, FALSE);
	}
    }
}

/*
  TEST
  Similar to the regular group1 operators.
  It does not allow the sign extended immediate byte forms
  and does not use the relevant direction bit.
*/

PUBLIC void mtest()
{
    getbinary();
    notsegorspecreg(&source);
    if (source.base == NOREG)
    {
	if (mcount != 0x0)
	{
	    if (target.indcount == 0x0
		&& (target.base == ALREG || target.base == AXREG
		    || target.base == EAXREG))
		opcode = 0xA8 | segword;
	    else
	    {
		buildea(&target);
		opcode = 0xF6 | segword;
	    }
	}
	buildimm(&source, FALSE);
    }
    else
    {
	opcode |= segword;
	buildregular();
    }
}

/*
  XCHG
  Similar to the regular group1 operators.
  It does not allow any of the immediate forms
  and does not use the irrelevant direction bit.
*/

PUBLIC void mxchg()
{
    getbinary();
    notimmed(&source);
    notsegorspecreg(&source);
    if (target.indcount == 0x0)
    {
	if (target.base == AXREG || target.base == EAXREG)
	{
	    opcode = 0x90 + rm[source.base];
	    return;
	}
	if (source.base == AXREG || source.base == EAXREG)
	{
	    opcode = 0x90 + rm[target.base];
	    return;
	}
    }
    opcode |= segword;
    buildregular();
}

PRIVATE void notbytesize(eap)
register struct ea_s *eap;
{
    if (eap->size == 0x1)
	kgerror(ILL_SIZE);
}

PRIVATE void notimmed(eap)
register struct ea_s *eap;
{
    if (eap->indcount == 0x0 && eap->base == NOREG)
	kgerror(ILL_IMM_MODE);
}

PRIVATE void notindirect(eap)
register struct ea_s *eap;
{
    if (eap->indcount != 0x0)
	kgerror(ILL_IND);
}

PRIVATE void notsegorspecreg(eap)
register struct ea_s *eap;
{
    if (regsegword[eap->base] >= SEGMOV)
	kgerror(ILLREG);
}

PRIVATE void yesimmed(eap)
register struct ea_s *eap;
{
    if (eap->indcount == 0x1)
	eap->indcount = 0x0;
    if (eap->indcount != 0x0 || eap->base != NOREG)
	kgerror(IMM_REQ);
}

PRIVATE void yes_samesize()
{
    if (target.size == 0x0)
	target.size = source.size;
    else if (source.size != 0x0 && target.size != source.size)
	kgerror(MISMATCHED_SIZE);
}

#endif /* I80386 */

#ifdef MC6809

/* 6809 opcode constants */

/* bits for indexed addressing */

#define INDIRECTBIT 0x10
#define INDEXBIT    0x80	/* except 5 bit offset */
#define PCRELBIT    0x04	/* PC relative (in certain cases) */
#define RRBITS      0x60	/* register select bits */

PRIVATE opcode_t rrindex[] =	/* register and index bits for indexed adr */
{
    0x60 | INDEXBIT,		/* S */
    0x40 | INDEXBIT,		/* U */
    0x00 | INDEXBIT,		/* X */
    0x20 | INDEXBIT,		/* Y */
    PCRELBIT | INDEXBIT,	/* PC */
};

PRIVATE opcode_t pushpull[] =	/* push/pull codes  */
{
    0x40,			/* S */
    0x40,			/* U */
    0x10,			/* X */
    0x20,			/* Y */
    0x80,			/* PC */
    0x02,			/* A */
    0x04,			/* B */
    0x01,			/* CC */
    0x08,			/* DP */
    0x06,			/* D */
};

PRIVATE opcode_t tfrexg1[] =	/* transfer/exchange codes for source reg */
{
    0x40,			/* S */
    0x30,			/* U */
    0x10,			/* X */
    0x20,			/* Y */
    0x50,			/* PC */
    0x80,			/* A */
    0x90,			/* B */
    0xA0,			/* CC */
    0xB0,			/* DP */
    0x00,			/* D */
};

PRIVATE opcode_t tfrexg2[] =	/* transfer/exchange codes for target reg */
{
    0x04,			/* S */
    0x03,			/* U */
    0x01,			/* X */
    0x02,			/* Y */
    0x05,			/* PC */
    0x08,			/* A */
    0x09,			/* B */
    0x0A,			/* CC */
    0x0B,			/* DP */
    0x00,			/* D */
};

FORWARD void checkpostinc P((void));
FORWARD void doaltind P((void));
FORWARD void do1altind P((void));
FORWARD void fixupind P((void));
FORWARD void getindexnopost P((void));
FORWARD void inderror P((error_pt errnum));
FORWARD reg_pt indreg P((reg_pt maxindex));
FORWARD void predec1 P((void));
FORWARD void sustack P((reg_pt stackreg));

PRIVATE void checkpostinc()
{
    if (sym == ADDOP)
    {
	if (postb & INDIRECTBIT)
	    inderror(ILLMOD);	/* single-inc indirect illegal */
	else
	{
	    lastexp.offset &= 0xFF00;	/* for printing if postbyte is 0: ,X+ */
	    getsym();
	}
    }
    else if (sym == POSTINCOP)
    {
	postb |= 0x1;
	getsym();
    }
    else
	postb |= 0x4;
    fixupind();
}

/* common code for all-mode ops, alterable-mode ops, indexed ops */

PRIVATE void doaltind()
{
    mcount += 0x2;
    if (sym == LBRACKET)
    {
	postb = INDIRECTBIT;
	getsym();
	do1altind();
	if (sym != RBRACKET)
	    error(RBEXP);
    }
    else
	do1altind();
}

PRIVATE void do1altind()
{
    bool_t byteflag;		/* set if direct or short indexed adr forced */
    char *oldlineptr;
    char *oldsymname;
    reg_pt reg;
    bool_t wordflag;		/* set if extended or long indexed adr forced*/

    if ((reg = regchk()) != NOREG)
    {
	switch (reg)
	{
	case AREG:
	    postb |= 0x86;
	    break;
	case BREG:
	    postb |= 0x85;
	    break;
	case DREG:
	    postb |= 0x8B;
	    break;
	default:
	    if (indreg(MAXINDREG) != NOREG)
		checkpostinc();
	    return;
	}
	getsym();
	if (sym != COMMA)
	    inderror(COMEXP);
	else
	    getindexnopost();
	return;
    }
    else if (sym == SUBOP)	/* could be -R or - in expression */
    {
	oldlineptr = lineptr;	/* save state */
	oldsymname = symname;
	getsym();
	reg = regchk();
	lineptr = oldlineptr;
	symname = oldsymname;
	if (reg != NOREG)
	{
	    predec1();		/* it's -R */
	    return;
	}
	sym = SUBOP;
    }
    else if (sym == COMMA)
    {
	postb |= INDEXBIT;
	getsym();
	if (sym == SUBOP)
	{
	    predec1();
	    return;
	}
	else if (sym != PREDECOP)
	{
	    if (indreg(MAXINDREG) != NOREG)
		checkpostinc();
	    return;
	}
    }
    if (sym == PREDECOP)
    {
	postb |= 0x83;
	getindexnopost();
	return;
    }

    /* should have expression */

    wordflag = byteflag = FALSE;
    if (sym == LESSTHAN)
    {
	/* context-sensitive, LESSTHAN means byte-sized here */
	byteflag = TRUE;
	getsym();
    }
    else if (sym == GREATERTHAN)
    {
	/* context-sensitive, GREATERTHAN means word-sized here */
	wordflag = TRUE;
	getsym();
    }
    expres();
    if (sym == COMMA)
    {				/* offset from register */
	getsym();
	if ((reg = indreg(PCREG)) == NOREG)
	    return;
	postb |= 0x8;		/* default 8 bit offset */
	if (reg == PCREG)
	{
	    reldata();
	    if (!(lastexp.data & (RELBIT | UNDBIT)))
	    {
		lastexp.offset = lastexp.offset - lc;
		if (page != 0x0)
		    lastexp.offset -= 0x4;	/* extra for instruction */
		else
		    lastexp.offset -= 0x3;	/* 3 byte instruction
						   assuming 8 bit offset */
	    }
	}
	if (byteflag)
	{
	    if (!(lastexp.data & (RELBIT | UNDBIT)) &&
		!is8bitsignedoffset(lastexp.offset))
		error(ABOUNDS);	/* forced short form is impossible */
	    ++mcount;
	}
	else if (wordflag || lastexp.data & (FORBIT | RELBIT | UNDBIT) ||
		 !is8bitsignedoffset(lastexp.offset))
	{			/* 16 bit offset */
	    if (postb & PCRELBIT && !(lastexp.data & RELBIT))
		--lastexp.offset;	/* instruction 1 longer than already
					   allowed */
	    postb |= 0x1;
	    mcount += 0x2;
	}
	else if (!(postb & PCRELBIT) &&
		 (offset_t) (lastexp.offset + 0x10) < 0x20 &&
		 !(postb & INDIRECTBIT && lastexp.offset != 0x0))
	{			/* 5 bit offset */
	    postb &= RRBITS | INDIRECTBIT;
	    if (lastexp.offset == 0x0)
		postb |= 0x84;	/* index with zero offset */
	    else
		postb |= (lastexp.offset & 0x1F);
	}
	else			/* 8 bit offset */
	    ++mcount;
	fixupind();
    }
    else if (postb & INDIRECTBIT)
    {				/* extended indirect */
	postb = 0x9F;
	mcount += 0x2;
	fixupind();
    }
    else if (postb & INDEXBIT)
	inderror(ILLMOD);	/* e.g. LEAX	 $10 */
    else
    {
	if (byteflag || !wordflag && !(lastexp.data & (FORBIT | RELBIT)) &&
	    (lastexp.offset >> 0x8) == dirpag)
	{			/* direct addressing */
	    if (opcode >= 0x80)
		opcode |= 0x10;
	}
	else			/* extended addressing */
	{
	    if (opcode < 0x80)
		opcode |= 0x70;
	    else
		opcode |= 0x30;
	    ++mcount;
	    if (pass2 && (opcode == JSR_OPCODE || opcode == JMP_OPCODE) &&
		!(lastexp.data & IMPBIT) &&
		lastexp.offset + (0x81 - 0x3) < 0x101)
				/* JSR or JMP could be done with BSR or BRA */
		error(SHORTB);
	}
    }
}

PRIVATE void fixupind()
{
    if ((opcode & 0x30) == 0x0)	/* change all but LEA opcodes */
    {
	if (opcode < 0x80)
	    opcode |= 0x60;
	else
	    opcode |= 0x20;
    }
}

PRIVATE void getindexnopost()
{
    getsym();
    if (indreg(MAXINDREG) != NOREG)
	fixupind();
}

PRIVATE void inderror(errnum)
error_pt errnum;
{
    error(errnum);
    if (postb & INDIRECTBIT)
	sym = RBRACKET;		/* fake right bracket to kill further errors */
    fixupind();
}

/* check current symbol is an index register (possibly excepting PC) */
/* if so, modify postbyte RR and INDEXBIT for it, get next sym, return TRUE */
/* otherwise generate error, return FALSE */

PRIVATE reg_pt indreg(maxindex)
reg_pt maxindex;
{
    reg_pt reg;

    if ((reg = regchk()) == NOREG)
	inderror(IREGEXP);
    else if (reg > maxindex)
    {
	inderror(ILLREG);
	reg = NOREG;
    }
    else
    {
	postb |= rrindex[reg];
	getsym();
    }
    return reg;
}

/* all-mode ops */

PUBLIC void mall()
{
    if (sym == IMMEDIATE)
	mimmed();
    else
	malter();
}

/* alterable mode ops */

PUBLIC void malter()
{
    postb = 0x0;		/* not yet indexed or indirect */
    doaltind();
}

/* indexed mode ops */

PUBLIC void mindex()
{
    postb = INDEXBIT;		/* indexed but not yet indirect */
    doaltind();
}

/* immediate ops */

PUBLIC void mimmed()
{
    opcode_t nybble;

    mcount += 0x2;
    if (sym != IMMEDIATE)
	error(ILLMOD);
    else
    {
	if (opcode >= 0x80 && ((nybble = opcode & 0xF) == 0x3 ||
			       nybble == 0xC || nybble >= 0xE))
	    ++mcount;		/* magic for long immediate */
	symexpres();
	if (pass2 && mcount <= 0x2)
	{
	    chkabs();
	    checkdatabounds();
	}
    }
}

/* long branches */

PUBLIC void mlong()
{
    mcount += 0x3;		/* may be 0x0 or 0x1 here */
    expres();
    if (pass2)
    {
	reldata();
	if (!(lastexp.data & (RELBIT | UNDBIT)))
	{
	    lastexp.offset = lastexp.offset - lc - lcjump;
	    if (!(lastexp.data & IMPBIT) &&
		lastexp.offset + 0x81 < 0x101)
		error(SHORTB);	/* -0x81 to 0x7F, warning */
	}
    }
}

/* PSHS and PULS */

PUBLIC void msstak()
{
    sustack(SREG);
}

/* TFR and EXG */

PUBLIC void mswap()
{
    reg_pt reg;

    mcount = 0x2;
    if ((reg = regchk()) == NOREG)
	error(REGEXP);
    else
    {
	postb = tfrexg1[reg];
	getsym();
	if (sym != COMMA)
	    error(COMEXP);
	else
	{
	    getsym();
	    if ((reg = regchk()) == NOREG)
		error(REGEXP);
	    else if ((postb |= tfrexg2[reg])
		     & 0x88 && (postb & 0x88) != 0x88)
		error(ILLREG);	/* registers not of same size */
	}
    }
}

/* PSHU and PULU */

PUBLIC void mustak()
{
    sustack(UREG);
}

PRIVATE void predec1()
{
    if (postb & INDIRECTBIT)
	inderror(ILLMOD);	/* single-dec indirect illegal */
    else
    {
	postb |= 0x82;
	getindexnopost();
    }
}

/* common routine for PSHS/PULS/PSHU/PULU */

PRIVATE void sustack(stackreg)
reg_pt stackreg;
{
    reg_pt reg;

    mcount = 0x2;
    while ((reg = regchk()) != NOREG)
    {
	if (reg == stackreg)
	{
	    error(ILLREG);	/* cannot stack self */
	    break;
	}
	postb |= pushpull[reg];
	getsym();
	if (sym != COMMA)
	    break;
	getsym();
    }
}

#endif /* MC6809 */

/* routines common to all processors */

PUBLIC void getcomma()
{
    if (sym != COMMA)
	error(COMEXP);
    else
	getsym();
}

/* inherent ops */

/* for I80386 */
/* AAA, AAS, CLC, CLD, CLI, CLTS, CMC, CMPSB, DAA, DAS, HLT, INTO, INSB, */
/* INVD, */
/* LAHF, LEAVE, LOCK, LODSB, MOVSB, NOP, OUTSB, REP, REPE, REPNE, REPNZ, */
/* REPZ, SAHF, SCASB, STC, STD, STI, STOSB, WAIT, WBINVD */

PUBLIC void minher()
{
    ++mcount;
}

/* short branches */

PUBLIC void mshort()
{
    nonimpexpres();
    mshort2();
}

PRIVATE void mshort2()
{
    mcount += 0x2;
    if (pass2)
    {
	reldata();
	if (lastexp.data & RELBIT)
	    showrelbad();
	else if (!(lastexp.data & UNDBIT))
	{
	    lastexp.offset = lastexp.offset - lc - mcount;
	    if (!is8bitsignedoffset(lastexp.offset))
		error(ABOUNDS);
	}
    }
}

/* check if current symbol is a register, return register number or NOREG */

PRIVATE reg_pt regchk()
{
    register struct sym_s *symptr;

    if (sym == IDENT)
    {
	if ((symptr = gsymptr)->type & MNREGBIT)
	{
	    if (symptr->data & REGBIT)
	    {
#ifdef I80386
		if (symptr->value_reg_or_op.reg == ST0REG && !fpreg_allowed)
		    error(FP_REG_NOT_ALLOWED);
#endif
		return symptr->value_reg_or_op.reg;
	    }
	}
	else if (!(symptr->type & (LABIT | MACBIT | VARBIT)))
	    symptr->data |= FORBIT;	/* show seen in advance */
    }
    return NOREG;
}

/* convert lastexp.data for PC relative */

PRIVATE void reldata()
{
    if ((lastexp.data ^ lcdata) & (IMPBIT | RELBIT | SEGM))
    {
	if ((lastexp.data ^ lcdata) & RELBIT)
	    showrelbad();	/* rel - abs is weird, abs - rel is bad */
	else
	{
	    pcrflag = OBJ_R_MASK;
	    lastexp.data = (lcdata & ~SEGM) | lastexp.data | RELBIT;
				/* segment is that of lastexp.data */
	}
    }
    else			/* same file, segment and relocation */
	lastexp.data = (lastexp.data | lcdata) & ~(RELBIT | SEGM);
}
