]> granicus.if.org Git - yasm/commitdiff
Correctly process 32-bit memory expressions in expr_checkea().
authorPeter Johnson <peter@tortall.net>
Mon, 29 Oct 2001 10:25:44 +0000 (10:25 -0000)
committerPeter Johnson <peter@tortall.net>
Mon, 29 Oct 2001 10:25:44 +0000 (10:25 -0000)
svn path=/trunk/yasm/; revision=303

libyasm/expr.c
modules/arch/x86/expr.c
modules/arch/x86/x86expr.c
src/arch/x86/expr.c
src/arch/x86/x86expr.c
src/expr.c

index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }
index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }
index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }
index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }
index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }
index 63604b906396bdc8b232c7b49b5fb5d20abb500b..4c2a6dc4915323251eeb505d620397464098ea46 100644 (file)
@@ -761,9 +761,9 @@ expr_checkea_get_reg32(ExprItem *ei, void *d)
     return ret;
 }
 
-typedef struct checkea_invalid16_data {
+typedef struct checkea_reg16_data {
     int bx, si, di, bp;                /* total multiplier for each reg */
-} checkea_invalid16_data;
+} checkea_reg16_data;
 
 /* Only works if ei->type == EXPR_REG (doesn't check).
  * Overwrites ei with intnum of 0 (to eliminate regs from the final expr).
@@ -771,7 +771,7 @@ typedef struct checkea_invalid16_data {
 static int *
 expr_checkea_get_reg16(ExprItem *ei, void *d)
 {
-    checkea_invalid16_data *data = d;
+    checkea_reg16_data *data = d;
     /* in order: ax,cx,dx,bx,sp,bp,si,di */
     static int *reg16[8] = {0,0,0,0,0,0,0,0};
     int *ret;
@@ -910,7 +910,7 @@ expr_checkea_distcheck_reg(expr **ep)
  * where the [...] parts are optional.
  *
  * Don't simplify out constant identities if we're looking for an indexreg: we
- * need the multiplier for determining what the indexreg is!
+ * may need the multiplier for determining what the indexreg is!
  *
  * Returns 0 if invalid register usage, 1 if unable to determine all values,
  * and 2 if all values successfully determined and saved in data.
@@ -928,7 +928,6 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     e = *ep;
     switch (expr_checkea_distcheck_reg(ep)) {
        case 0:
-           ErrorAt(e->filename, e->line, _("invalid effective address"));
            return 0;
        case 2:
            /* Need to simplify again */
@@ -968,6 +967,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    if (!reg)
                        return 0;
                    (*reg)++;
+                   if (indexreg)
+                       *indexreg = reg-(int *)data;
                } else if (e->terms[i].type == EXPR_EXPR) {
                    /* Already ordered from ADD above, just grab the value.
                     * Sanity check for EXPR_INT.
@@ -982,8 +983,7 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
                    (*reg) +=
                        intnum_get_int(e->terms[i].data.expn->terms[1].data.intn);
                    if (indexreg)
-                       *indexreg =
-                           e->terms[i].data.expn->terms[0].data.reg.num;
+                       *indexreg = reg-(int *)data;
                }
            }
            break;
@@ -998,6 +998,8 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
            if (!reg)
                return 0;
            (*reg) += intnum_get_int(e->terms[1].data.intn);
+           if (indexreg)
+               *indexreg = reg-(int *)data;
            break;
        default:
            /* Should never get here! */
@@ -1013,6 +1015,123 @@ expr_checkea_getregusage(expr **ep, int *indexreg, void *data,
     return 2;
 }
 
+/* Calculate the displacement length, if possible.
+ * Takes several extra inputs so it can be used by both 32-bit and 16-bit
+ * expressions:
+ *  wordsize=2 for 16-bit, =4 for 32-bit.
+ *  noreg=1 if the *ModRM byte* has no registers used.
+ *  isbpreg=1 if BP/EBP is the *only* register used within the *ModRM byte*.
+ */
+static int
+checkea_calc_displen(expr **ep, int wordsize, int noreg, int isbpreg,
+                    unsigned char *displen, unsigned char *modrm,
+                    unsigned char *v_modrm)
+{
+    expr *e = *ep;
+    const intnum *intn;
+    long dispval;
+
+    *v_modrm = 0;      /* default to not yet valid */
+
+    switch (*displen) {
+       case 0:
+           /* the displacement length hasn't been forced, try to
+            * determine what it is.
+            */
+           if (noreg) {
+               /* no register in ModRM expression, so it must be disp16/32,
+                * and as the Mod bits are set to 0 by the caller, we're done
+                * with the ModRM byte.
+                */
+               *displen = wordsize;
+               *v_modrm = 1;
+           } else if (isbpreg) {
+               /* for BP/EBP, there *must* be a displacement value, but we
+                * may not know the size (8 or 16/32) for sure right now.
+                * We can't leave displen at 0, because that just means
+                * unknown displacement, including none.
+                */
+               *displen = 0xff;
+           }
+
+           intn = expr_get_intnum(ep);
+           if (!intn)
+               break;          /* expr still has unknown values */
+
+           /* make sure the displacement will fit in 16/32 bits if unsigned,
+            * and 8 bits if signed.
+            */
+           if (!intnum_check_size(intn, wordsize, 0) &&
+               !intnum_check_size(intn, 1, 1)) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+           /* don't try to find out what size displacement we have if
+            * displen is known.
+            */
+           if (*displen != 0 && *displen != 0xff)
+               break;
+
+           /* Don't worry about overflows here (it's already guaranteed
+            * to be 16/32 or 8 bits).
+            */
+           dispval = intnum_get_int(intn);
+
+           /* Figure out what size displacement we will have. */
+           if (*displen != 0xff && dispval == 0) {
+               /* if we know that the displacement is 0 right now,
+                * go ahead and delete the expr (making it so no
+                * displacement value is included in the output).
+                * The Mod bits of ModRM are set to 0 above, and
+                * we're done with the ModRM byte!
+                *
+                * Don't do this if we came from isbpreg check above, so
+                * check *displen.
+                */
+               expr_delete(e);
+               *ep = (expr *)NULL;
+           } else if (dispval >= -128 && dispval <= 127) {
+               /* It fits into a signed byte */
+               *displen = 1;
+               *modrm |= 0100;
+           } else {
+               /* It's a 16/32-bit displacement */
+               *displen = wordsize;
+               *modrm |= 0200;
+           }
+           *v_modrm = 1;       /* We're done with ModRM */
+
+           break;
+
+       /* If not 0, the displacement length was forced; set the Mod bits
+        * appropriately and we're done with the ModRM byte.  We assume
+        * that the user knows what they're doing if they do an explicit
+        * override, so we don't check for overflow (we'll just truncate
+        * when we output).
+        */
+       case 1:
+           *modrm |= 0100;
+           *v_modrm = 1;
+           break;
+       case 2:
+       case 4:
+           if (wordsize != *displen) {
+               ErrorAt(e->filename, e->line,
+                       _("invalid effective address (displacement size)"));
+               return 0;
+           }
+           *modrm |= 0200;
+           *v_modrm = 1;
+           break;
+       default:
+           /* we shouldn't ever get any other size! */
+           InternalError(_("strange EA displacement size"));
+    }
+
+    return 1;
+}
+
 static int
 expr_checkea_getregsize_callback(ExprItem *ei, void *d)
 {
@@ -1033,8 +1152,6 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
             unsigned char *n_sib)
 {
     expr *e = *ep;
-    const intnum *intn;
-    long dispval;
 
     if (*addrsize == 0) {
        /* we need to figure out the address size from what we know about:
@@ -1066,9 +1183,21 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
     }
 
     if (*addrsize == 32 && ((*n_modrm && !*v_modrm) || (*n_sib && !*v_sib))) {
+       int i;
+       typedef enum {
+           REG32_NONE = -1,
+           REG32_EAX = 0,
+           REG32_ECX = 1,
+           REG32_EDX = 2,
+           REG32_EBX = 3,
+           REG32_ESP = 4,
+           REG32_EBP = 5,
+           REG32_ESI = 6,
+           REG32_EDI = 7
+       } reg32type;
        int reg32mult[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-       /*int basereg = 0;*/    /* "base" register (for SIB) */
-       int indexreg = 0;   /* "index" register (for SIB) */
+       int basereg = REG32_NONE;       /* "base" register (for SIB) */
+       int indexreg = REG32_NONE;      /* "index" register (for SIB) */
        
        switch (expr_checkea_getregusage(ep, &indexreg, reg32mult,
                                         expr_checkea_get_reg32)) {
@@ -1083,6 +1212,153 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
                break;
        }
 
+       /* If indexreg mult is 0, discard it.
+        * This is possible because of the way indexreg is found in
+        * expr_checkea_getregusage().
+        */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] == 0)
+           indexreg = REG32_NONE;
+
+       /* Find a basereg (*1, but not indexreg), if there is one.
+        * Also, if an indexreg hasn't been assigned, try to find one.
+        * Meanwhile, check to make sure there's no negative register mults.
+        */
+       for (i=0; i<8; i++) {
+           if (reg32mult[i] < 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           if (i != indexreg && reg32mult[i] == 1)
+               basereg = i;
+           else if (indexreg == REG32_NONE && reg32mult[i] > 0)
+               indexreg = i;
+       }
+
+       /* Handle certain special cases of indexreg mults when basereg is
+        * empty.
+        */
+       if (indexreg != REG32_NONE && basereg == REG32_NONE)
+           switch (reg32mult[indexreg]) {
+               case 1:
+                   /* Only optimize this way if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       indexreg = -1;
+                   }
+                   break;
+               case 2:
+                   /* Only split if nosplit wasn't specified */
+                   if (!nosplit) {
+                       basereg = indexreg;
+                       reg32mult[indexreg] = 1;
+                   }
+                   break;
+               case 3:
+               case 5:
+               case 9:
+                   basereg = indexreg;
+                   reg32mult[indexreg]--;
+                   break;
+           }
+
+       /* Make sure there's no other registers than the basereg and indexreg
+        * we just found.
+        */
+       for (i=0; i<8; i++)
+           if (i != basereg && i != indexreg && reg32mult[i] != 0) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+
+       /* Check the index multiplier value for validity if present. */
+       if (indexreg != REG32_NONE && reg32mult[indexreg] != 1 &&
+           reg32mult[indexreg] != 2 && reg32mult[indexreg] != 4 &&
+           reg32mult[indexreg] != 8) {
+           ErrorAt(e->filename, e->line, _("invalid effective address"));
+           return 0;
+       }
+
+       /* ESP is not a legal indexreg. */
+       if (indexreg == REG32_ESP) {
+           /* If mult>1 or basereg is ESP also, there's no way to make it
+            * legal.
+            */
+           if (reg32mult[REG32_ESP] > 1 || basereg == REG32_ESP) {
+               ErrorAt(e->filename, e->line, _("invalid effective address"));
+               return 0;
+           }
+           /* If mult==1 and basereg is not ESP, swap indexreg w/basereg. */
+           indexreg = basereg;
+           basereg = REG32_ESP;
+       }
+
+       /* At this point, we know the base and index registers and that the
+        * memory expression is (essentially) valid.  Now build the ModRM and
+        * (optional) SIB bytes.
+        */
+
+       /* First determine R/M (Mod is later determined from disp size) */
+       *n_modrm = 1;   /* we always need ModRM */
+       if (basereg == REG32_NONE) {
+           /* disp32[index] */
+           *modrm |= 5;
+           /* we must have a SIB */
+           *n_sib = 1;
+       } else if (indexreg == REG32_NONE) {
+           /* basereg only */
+           *modrm |= basereg;
+           /* we don't need an SIB *unless* basereg is ESP */
+           if (basereg == REG32_ESP)
+               *n_sib = 1;
+           else {
+               *sib = 0;
+               *v_sib = 0;
+               *n_sib = 0;
+           }
+       } else {
+           /* both base AND index */
+           *modrm |= 4;
+           *n_sib = 1;
+       }
+
+       /* Determine SIB if needed */
+       if (*n_sib == 1) {
+           *sib = 0;   /* start with 0 */
+
+           /* Special case: no basereg (only happens in disp32[index] case) */
+           if (basereg == REG32_NONE)
+               *sib |= 5;
+           else
+               *sib |= basereg & 7;    /* &7 to sanity check */
+           
+           /* Put in indexreg, checking for none case */
+           if (indexreg == REG32_NONE)
+               *sib |= 040;
+               /* Any scale field is valid, just leave at 0. */
+           else {
+               *sib |= (indexreg & 7) << 3;    /* &7 to sanity check */
+               /* Set scale field, 1 case -> 0, so don't bother. */
+               switch (reg32mult[indexreg]) {
+                   case 2:
+                       *sib |= 0100;
+                       break;
+                   case 4:
+                       *sib |= 0200;
+                       break;
+                   case 8:
+                       *sib |= 0300;
+                       break;
+               }
+           }
+
+           *v_sib = 1; /* Done with SIB */
+       }
+
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 4, basereg == REG32_NONE,
+                                   basereg == REG32_EBP &&
+                                   indexreg == REG32_NONE, displen, modrm,
+                                   v_modrm);
     } else if (*addrsize == 16 && *n_modrm && !*v_modrm) {
        static const unsigned char modrm16[16] = {
            0006 /* disp16  */, 0007 /* [BX]    */, 0004 /* [SI]    */,
@@ -1092,7 +1368,7 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            0003 /* [BP+DI] */, 0377 /* invalid */, 0377 /* invalid */,
            0377 /* invalid */
        };
-       checkea_invalid16_data reg16mult = {0, 0, 0, 0};
+       checkea_reg16_data reg16mult = {0, 0, 0, 0};
        enum {
            HAVE_NONE = 0,
            HAVE_BX = 1<<0,
@@ -1142,107 +1418,13 @@ expr_checkea(expr **ep, unsigned char *addrsize, unsigned char bits,
            return 0;
        }
 
+       /* Set ModRM byte for registers */
        *modrm |= modrm16[havereg];
 
-       *v_modrm = 0;   /* default to not yet valid */
-
-       switch (*displen) {
-           case 0:
-               /* the displacement length hasn't been forced, try to
-                * determine what it is.
-                */
-               switch (havereg) {
-                   case HAVE_NONE:
-                       /* no register in expression, so it must be disp16, and
-                        * as the Mod bits are set to 0 above, we're done with
-                        * the ModRM byte.
-                        */
-                       *displen = 2;
-                       *v_modrm = 1;
-                       break;
-                   case HAVE_BP:
-                       /* for BP, there *must* be a displacement value, but we
-                        * may not know the size (8 or 16) for sure right now.
-                        * We can't leave displen at 0, because that just means
-                        * unknown displacement, including none.
-                        */
-                       *displen = 0xff;
-                       break;
-                   default:
-                       break;
-               }
-
-               intn = expr_get_intnum(ep);
-               if (!intn)
-                   break;          /* expr still has unknown values */
-
-               /* make sure the displacement will fit in 16 bits if unsigned,
-                * and 8 bits if signed.
-                */
-               if (!intnum_check_size(intn, 2, 0) &&
-                   !intnum_check_size(intn, 1, 1)) {
-                   ErrorAt(e->filename, e->line,
-                           _("invalid effective address"));
-                   return 0;
-               }
-
-               /* don't try to find out what size displacement we have if
-                * displen is known.
-                */
-               if (*displen != 0 && *displen != 0xff)
-                   break;
-
-               /* Don't worry about overflows here (it's already guaranteed
-                * to be 16 or 8 bits).
-                */
-               dispval = intnum_get_int(intn);
-
-               /* Figure out what size displacement we will have. */
-               if (*displen != 0xff && dispval == 0) {
-                   /* if we know that the displacement is 0 right now,
-                    * go ahead and delete the expr (making it so no
-                    * displacement value is included in the output).
-                    * The Mod bits of ModRM are set to 0 above, and
-                    * we're done with the ModRM byte!
-                    *
-                    * Don't do this if we came from HAVE_BP above, so
-                    * check *displen.
-                    */
-                   expr_delete(e);
-                   *ep = (expr *)NULL;
-               } else if (dispval >= -128 && dispval <= 127) {
-                   /* It fits into a signed byte */
-                   *displen = 1;
-                   *modrm |= 0100;
-               } else {
-                   /* It's a 16-bit displacement */
-                   *displen = 2;
-                   *modrm |= 0200;
-               }
-               *v_modrm = 1;   /* We're done with ModRM */
-
-               break;
-
-           /* If not 0, the displacement length was forced; set the Mod bits
-            * appropriately and we're done with the ModRM byte.  We assume
-            * that the user knows what they're doing if they do an explicit
-            * override, so we don't check for overflow (we'll just truncate
-            * when we output).
-            */
-           case 1:
-               *modrm |= 0100;
-               *v_modrm = 1;
-               break;
-           case 2:
-               *modrm |= 0200;
-               *v_modrm = 1;
-               break;
-           default:
-               /* any other size is an error */
-               ErrorAt(e->filename, e->line,
-                       _("invalid effective address (displacement size)"));
-               return 0;
-       }
+       /* Calculate displacement length (if possible) */
+       return checkea_calc_displen(ep, 2, havereg == HAVE_NONE,
+                                   havereg == HAVE_BP, displen, modrm,
+                                   v_modrm);
     }
     return 1;
 }