Logo Search packages:      
Sourcecode: linux86 version File versions  Download package

mops.c

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

#include "syshead.h"
#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==last_pass)

FORWARD void mshort2 P((void));
FORWARD reg_pt regchk P((void));
FORWARD void reldata P((void));
FORWARD void segadj 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((char * err_str));
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];
          }
      }
      needcpu(asize==4?3:0);
      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( last_pass == 1 )
          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;
        needcpu(eap->size==4?3:0);
      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)
          {
                needcpu(3);
            /* 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();
    }
    /* RDB */
    else if (!leading_immed && defsize <= 0x2)
      eap->indcount = 0x1;    /* compatibility kludge */
    if (!leading_displ)
      eap->displ = lastexp;

    needcpu(eap->size==4?3:0);
}

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(err_str)
char * err_str;
{
    error(err_str);
    sprefix = oprefix = aprefix = mcount = 0x0;
}

PRIVATE void lbranch(backamount)
int backamount;
{
    mcount += defsize + 0x1;
    segadj();
    if (pass2)
    {
      reldata();
      if (!(lastexp.data & (RELBIT | UNDBIT)))
      {
          lastexp.offset = lastexp.offset - lc - lcjump;
          if ( last_pass<2 && backamount != 0x0 && 
              !(lastexp.data & IMPBIT) &&
            lastexp.offset + backamount < 0x80 + backamount)
            warning(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
    {
#ifdef iscpu
      if (iscpu(3))
#else
      if (defsize != 0x2)
#endif
      {
          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()
{
    needcpu(4);
    ++mcount;
    Gd(&target);
    opcode |= rm[target.base];
}

/* BR, CALL, J, JMP */

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

    far_diff = 0x0;
    if (sym == IDENT && (symptr = gsymptr)->type & MNREGBIT &&
      symptr->data & SIZEBIT )
    {
        if(symptr->value_reg_or_op.op.routine == FAROP)
        {
          far_diff = 0x8;
          getsym();
        }
        if(symptr->value_reg_or_op.op.routine == WORDOP &&
         opcode == JMP_SHORT_OPCODE)
        {
         opcode = JMP_OPCODE;
         getsym();
      }
    }
    indirect = FALSE;

    if (asld_compatible && defsize <= 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)
    {
        int tsize = target.size?target.size:defsize;
      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)) &&
            tsize == 0x2 &&
            (offset_t) (lastexp.offset + 0x8000L) >= 0x18000L)
            datatoobig();
          mcount += tsize;
          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_diff;
          opcode = 0xFF;
      }
    }
    else if (opcode == JMP_SHORT_OPCODE)
    {
      if (jumps_long &&
          ((pass!=0 && !is8bitsignedoffset(lastexp.offset - lc - 2)) ||
           (last_pass==1)))
      {
          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
    {
        int tsize = target.size?target.size:defsize;
      getcomma();
      getea(&source);
      yesimmed(&source);
      if (mcount != 0x0)
      {
          lastexp = target.displ;
          if (!(lastexp.data & (FORBIT | RELBIT | UNDBIT)) &&
            tsize == 0x2 &&
            (offset_t) (lastexp.offset + 0x8000L) >= 0x18000L)
            {
            tsize=4;
              if( tsize != defsize ) oprefix = 0x66;
            /* datatoobig(); */
          }
          needcpu(tsize==4?3:0);
          mcount += tsize;
          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 */
    }
    needcpu(1);
}

/* 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)
      {
          needcpu(1);
          opcode -= 0x10;
          source.size = 0x1;
          buildimm(&source, FALSE);
      }
    }
}

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

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

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

PUBLIC void mgroup7()
{
    needcpu(2);   /* I think INVLPG is actually 386 */
    ++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()
{
    needcpu(3);
    ++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()
{
    needcpu(2);
    ++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))
    {
        needcpu(3);
      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;
    needcpu(3);
}

/* 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()
{
    /* First look for j* near */
    if (sym == IDENT && 
        gsymptr->type & MNREGBIT &&
      gsymptr->data & SIZEBIT &&
        gsymptr->value_reg_or_op.op.routine == WORDOP &&
      opcode < 0x80)
    {
        getsym();
        getea(&target);
        if (target.indcount >= 0x2 || target.base != NOREG)
         kgerror(REL_REQ);
        else
        {
            needcpu(3);
          page = PAGE1_OPCODE;
          ++mcount;
          opcode += 0x10;
          lbranch(0x84);
        }
    }
    else if (!jumps_long || opcode > 0x80) /* above 0x80 means loop, not long */
      mshort();
    else  /* mbcc */
    {
        getea(&target);
      lastexp = target.displ;

      if ( (pass!=0 && !is8bitsignedoffset(lastexp.offset - lc - 2)) ||
            last_pass==1)
      {
           if (target.indcount >= 0x2 || target.base != NOREG)
             kgerror(REL_REQ);

          aprefix = opcode ^ 0x1;   /* kludged storage for short branch
                                 over */
          oprefix = defsize + 0x1;
          mcount += 0x2;
          opcode = JMP_OPCODE;
          lbranch(0x83);
          mcount -= 0x2;
      }
      else
      {
         /* 8 bit */
          if (lastexp.data & IMPBIT)
          {
            error(NONIMPREQ);
            lastexp.data = FORBIT | UNDBIT;
          }
          mshort2();
      }
    }
}

/* 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;
          needcpu(source.size==4?3:0);
      }
      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))
          {
            needcpu(3);
            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()
{
    needcpu(3);
    ++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((char * err_str));
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 */
            warning(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(err_str)
char * err_str;
{
    error(err_str);
    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();
    segadj();
    if (pass2)
    {
      reldata();
      if (!(lastexp.data & (RELBIT | UNDBIT)))
      {
          lastexp.offset = lastexp.offset - lc - lcjump;
          if ( last_pass<2 && !(lastexp.data & IMPBIT) &&
            lastexp.offset + 0x81 < 0x101)
            warning(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)
          {
              int regno = symptr->value_reg_or_op.reg;
#ifdef I80386
            if (regno == ST0REG && !fpreg_allowed)
                error(FP_REG_NOT_ALLOWED);

            /* Check cpu */
                needcpu((regno==FSREG||regno==GSREG)?3:0);
                needcpu((regno>=EAXREG && regno<=ESPREG)?3:0);
                needcpu((regno>=CR0REG && regno<=TR7REG)?3:0);
#endif
            return regno;
          }
      }
      else
          if( last_pass == 1 )
              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);
}

PRIVATE void segadj()
{
    if ((lastexp.data & UNDBIT) && textseg >= 0 )
    {
        lastexp.sym->data &= ~SEGM;
        lastexp.sym->data |= (lcdata & SEGM);
    }
}

Generated by  Doxygen 1.6.0   Back to index