Mon Jun 8 12:20:30 1998 Alan Modra <alan@spri.levels.unisa.edu.au>
authorIan Lance Taylor <ian@airs.com>
Mon, 8 Jun 1998 17:06:00 +0000 (17:06 +0000)
committerIan Lance Taylor <ian@airs.com>
Mon, 8 Jun 1998 17:06:00 +0000 (17:06 +0000)
* config/tc-i386.c: REPNE renamed to REPNE_PREFIX_OPCODE, and
likewise for REPE.

* config/tc-i386.c (reloc): Add braces.

* config/tc-i386.c (struct _i386_insn): Rename bi to sib to be
consistent with Intel naming.
* config/tc-i386.h (base_index_byte): Rename to sib_byte.  Don't
use bitfields in sib_byte.
(modrm_byte): Don't use bitfields here either.

* config/tc-i386.c (current_templates): Add const.
(parse_register): Add const to return, param, and char *s.
(i386_operand): Add const to reg_entry *r.
* config/tc-i386.h (templates): Add const to start, end.

Inspired by code for 16 bit gas support from Martynas Kunigelis
<martynas@nm3.ktu.lt>:
* config/tc-i386.c (md_assemble): Add full support for 16 bit
modrm, and Jump, JumpByte, JumpDword, JumpInterSegment insns.
(uses_mem_addrmode): Remove.
(md_estimate_size_before_relax): Add support here too.
(md_relax_table): Rewrite interface to md_relax for 16 bit
support.
(BYTE, WORD, DWORD, UNKNOWN_SIZE): Remove.
(opcode_suffix_to_type): Remove.
(CODE16, SMALL, SMALL16, BIG, BIG16): Define.
(SIZE_FROM_RELAX_STATE): Modify to suit above.
(md_convert_frag): Likewise.
(i386_operand): Add support for 16 bit base/index regs,
immediates, and displacements.  Remove some unnecessary casts, and
localise end_of_operand_string, displacement_string_start,
displacement_string_end variables.  Add GCC_ASM_O_HACK.
* config/tc-i386.h (NO_BASE_REGISTER_16): Define.

* config/tc-i386.c (prefix_hash): Remove.
(md_begin): Rewrite without obstacks.  Remove prefix hash table
handling.  Rewrite lexical table handling.
(i386_print_statistics): Don't print prefix statistics.
(md_assemble): Rewrite instruction parser so that line is not
converted to lower case.  Don't do a hash_find for prefixes,
instead recognise them via opcode modifier.
(expecting_operand, paren_not_balanced): Localise variables.
* config/tc-i386.h (IsPrefix): Define.
(prefix_entry): Remove.

* config/tc-i386.h (PREFIX_SEPERATOR): Don't define.
* config/tc-i386.c (PREFIX_SEPARATOR): Define here instead, using
'\\' in case where comment_chars contains '/'.

* config/tc-i386.c (MATCH): Ensure given operand and template
match for JumpAbsolute.  Makes e.g. `ljmp table(%ebx)' invalid;
you must write `ljmp *table(%ebx)'.

From H.J. Lu <hjl@gnu.org>:
* config/tc-i386.c (BFD_RELOC_16, BFD_RELOC_16_PCREL): Define
as 0 ifndef BFD_ASSEMBLER.
(md_assemble): Allow immediate operands without suffix or
other reg operand to default in size to the current code size.

gas/ChangeLog
gas/config/tc-i386.c
gas/config/tc-i386.h

index 7ac200a..9f933e4 100644 (file)
@@ -1,3 +1,65 @@
+Mon Jun  8 12:20:30 1998  Alan Modra  <alan@spri.levels.unisa.edu.au>
+
+       * config/tc-i386.c: REPNE renamed to REPNE_PREFIX_OPCODE, and
+       likewise for REPE.
+
+       * config/tc-i386.c (reloc): Add braces.
+
+       * config/tc-i386.c (struct _i386_insn): Rename bi to sib to be
+       consistent with Intel naming.
+       * config/tc-i386.h (base_index_byte): Rename to sib_byte.  Don't
+       use bitfields in sib_byte.
+       (modrm_byte): Don't use bitfields here either.
+
+       * config/tc-i386.c (current_templates): Add const.
+       (parse_register): Add const to return, param, and char *s.
+       (i386_operand): Add const to reg_entry *r.
+       * config/tc-i386.h (templates): Add const to start, end.
+
+       Inspired by code for 16 bit gas support from Martynas Kunigelis
+       <martynas@nm3.ktu.lt>:
+       * config/tc-i386.c (md_assemble): Add full support for 16 bit
+       modrm, and Jump, JumpByte, JumpDword, JumpInterSegment insns.
+       (uses_mem_addrmode): Remove.
+       (md_estimate_size_before_relax): Add support here too.
+       (md_relax_table): Rewrite interface to md_relax for 16 bit
+       support.
+       (BYTE, WORD, DWORD, UNKNOWN_SIZE): Remove.
+       (opcode_suffix_to_type): Remove.
+       (CODE16, SMALL, SMALL16, BIG, BIG16): Define.
+       (SIZE_FROM_RELAX_STATE): Modify to suit above.
+       (md_convert_frag): Likewise.
+       (i386_operand): Add support for 16 bit base/index regs,
+       immediates, and displacements.  Remove some unnecessary casts, and
+       localise end_of_operand_string, displacement_string_start,
+       displacement_string_end variables.  Add GCC_ASM_O_HACK.
+       * config/tc-i386.h (NO_BASE_REGISTER_16): Define.
+
+       * config/tc-i386.c (prefix_hash): Remove.
+       (md_begin): Rewrite without obstacks.  Remove prefix hash table
+       handling.  Rewrite lexical table handling.
+       (i386_print_statistics): Don't print prefix statistics.
+       (md_assemble): Rewrite instruction parser so that line is not
+       converted to lower case.  Don't do a hash_find for prefixes,
+       instead recognise them via opcode modifier.
+       (expecting_operand, paren_not_balanced): Localise variables.
+       * config/tc-i386.h (IsPrefix): Define.
+       (prefix_entry): Remove.
+
+       * config/tc-i386.h (PREFIX_SEPERATOR): Don't define.
+       * config/tc-i386.c (PREFIX_SEPARATOR): Define here instead, using
+       '\\' in case where comment_chars contains '/'.
+
+       * config/tc-i386.c (MATCH): Ensure given operand and template
+       match for JumpAbsolute.  Makes e.g. `ljmp table(%ebx)' invalid;
+       you must write `ljmp *table(%ebx)'.
+
+       From H.J. Lu <hjl@gnu.org>:
+       * config/tc-i386.c (BFD_RELOC_16, BFD_RELOC_16_PCREL): Define
+       as 0 ifndef BFD_ASSEMBLER.
+       (md_assemble): Allow immediate operands without suffix or
+       other reg operand to default in size to the current code size.
+
 start-sanitize-v850e
 Mon Jun  8 09:45:00 1998  Catherine Moore  <clm@cygnus.com>
  
index 0a393e2..ea2a984 100644 (file)
@@ -30,8 +30,6 @@
 
 #include "as.h"
 #include "subsegs.h"
-
-#include "obstack.h"
 #include "opcode/i386.h"
 
 #ifndef TC_RELOC
@@ -120,11 +118,11 @@ struct _i386_insn
     unsigned int prefixes;
     unsigned char prefix[MAX_PREFIXES];
 
-    /* RM and BI are the modrm byte and the base index byte where the
+    /* RM and SIB are the modrm byte and the sib byte where the
        addressing modes of this insn are encoded.  */
 
     modrm_byte rm;
-    base_index_byte bi;
+    sib_byte sib;
   };
 
 typedef struct _i386_insn i386_insn;
@@ -133,8 +131,10 @@ typedef struct _i386_insn i386_insn;
    pre-processor is disabled, these aren't very useful */
 #if defined (TE_I386AIX) || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 const char comment_chars[] = "#/";
+#define PREFIX_SEPARATOR '\\'
 #else
 const char comment_chars[] = "#";
+#define PREFIX_SEPARATOR '/'
 #endif
 
 /* This array holds the chars that only start a comment at the beginning of
@@ -150,6 +150,7 @@ const char line_comment_chars[] = "";
 #else
 const char line_comment_chars[] = "/";
 #endif
+
 const char line_separator_chars[] = "";
 
 /* Chars that can be used to separate mant from exp in floating point nums */
@@ -194,7 +195,7 @@ static char *save_stack_p;  /* stack pointer */
 static i386_insn i;
 
 /* Possible templates for current insn.  */
-static templates *current_templates;
+static const templates *current_templates;
 
 /* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
 static expressionS disp_expressions[2], im_expressions[2];
@@ -215,10 +216,11 @@ static int flag_16bit_code;       /* 1 if we're writing 16-bit code, 0 if 32-bit */
 #define COND_JUMP 1            /* conditional jump */
 #define UNCOND_JUMP 2          /* unconditional jump */
 /* sizes */
-#define BYTE 0
-#define WORD 1
-#define DWORD 2
-#define UNKNOWN_SIZE 3
+#define CODE16 1
+#define SMALL  0
+#define SMALL16 (SMALL|CODE16)
+#define BIG    2
+#define BIG16  (BIG|CODE16)
 
 #ifndef INLINE
 #ifdef __GNUC__
@@ -231,7 +233,15 @@ static int flag_16bit_code;        /* 1 if we're writing 16-bit code, 0 if 32-bit */
 #define ENCODE_RELAX_STATE(type,size) \
   ((relax_substateT)((type<<2) | (size)))
 #define SIZE_FROM_RELAX_STATE(s) \
-    ( (((s) & 0x3) == BYTE ? 1 : (((s) & 0x3) == WORD ? 2 : 4)) )
+    ( (((s) & 0x3) == BIG ? 4 : (((s) & 0x3) == BIG16 ? 2 : 1)) )
+
+/* This table is used by relax_frag to promote short jumps to long
+   ones where necessary.  SMALL (short) jumps may be promoted to BIG
+   (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long).  We
+   don't allow a short jump in a 32 bit code segment to be promoted to
+   a 16 bit offset jump because it's slower (requires data size
+   prefix), and doesn't work, unless the destination is in the bottom
+   64k of the code segment (The top 16 bits of eip are zeroed).  */
 
 const relax_typeS md_relax_table[] =
 {
@@ -246,26 +256,23 @@ const relax_typeS md_relax_table[] =
   {1, 1, 0, 0},
   {1, 1, 0, 0},
 
-  /* For now we don't use word displacement jumps; they will not work
-     for destination addresses > 0xFFFF, since they clear the upper 16
-     bits of %eip.  */
-  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, DWORD)},
-  /* word conditionals add 3 bytes to frag:
-     2 opcode prefix; 1 displacement bytes */
-  {32767 + 2, -32768 + 2, 3, ENCODE_RELAX_STATE (COND_JUMP, DWORD)},
+  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG)},
+  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, BIG16)},
   /* dword conditionals adds 4 bytes to frag:
-     1 opcode prefix; 3 displacement bytes */
+     1 extra opcode byte, 3 extra displacement bytes.  */
   {0, 0, 4, 0},
-  {1, 1, 0, 0},
+  /* word conditionals add 2 bytes to frag:
+     1 extra opcode byte, 1 extra displacement byte.  */
+  {0, 0, 2, 0},
 
-  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD)},
-  /* word jmp adds 2 bytes to frag:
-     1 opcode prefix; 1 displacement bytes */
-  {32767 + 2, -32768 + 2, 2, ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD)},
+  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)},
+  {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)},
   /* dword jmp adds 3 bytes to frag:
-     0 opcode prefix; 3 displacement bytes */
+     0 extra opcode bytes, 3 extra displacement bytes.  */
   {0, 0, 3, 0},
-  {1, 1, 0, 0},
+  /* word jmp adds 1 byte to frag:
+     0 extra opcode bytes, 1 extra displacement byte.  */
+  {0, 0, 1, 0}
 
 };
 
@@ -358,7 +365,7 @@ i386_align_code (fragP, count)
 
 static char *output_invalid PARAMS ((int c));
 static int i386_operand PARAMS ((char *operand_string));
-static reg_entry *parse_register PARAMS ((char *reg_string));
+static const reg_entry *parse_register PARAMS ((const char *reg_string));
 #ifndef I386COFF
 static void s_bss PARAMS ((int));
 #endif
@@ -372,20 +379,6 @@ mode_from_disp_size (t)
   return (t & Disp8) ? 1 : (t & (Disp16|Disp32)) ? 2 : 0;
 }
 
-#if 0
-/* Not used.  */
-/* convert opcode suffix ('b' 'w' 'l' typically) into type specifier */
-
-static INLINE unsigned long
-opcode_suffix_to_type (s)
-     unsigned long s;
-{
-  return (s == BYTE_OPCODE_SUFFIX
-         ? Byte : (s == WORD_OPCODE_SUFFIX
-                   ? Word : DWord));
-}                              /* opcode_suffix_to_type() */
-#endif
-
 static INLINE int
 fits_in_signed_byte (num)
      long num;
@@ -461,8 +454,8 @@ add_prefix (prefix)
       q = SEG_PREFIX;
       break;
 
-    case REPNE:
-    case REPE:
+    case REPNE_PREFIX_OPCODE:
+    case REPE_PREFIX_OPCODE:
       ret = 2;
       /* fall thru */
     case LOCK_PREFIX_OPCODE:
@@ -524,15 +517,10 @@ const pseudo_typeS md_pseudo_table[] =
 /* for interface with expression () */
 extern char *input_line_pointer;
 
-/* obstack for constructing various things in md_begin */
-struct obstack o;
-
 /* hash table for opcode lookup */
 static struct hash_control *op_hash;
 /* hash table for register lookup */
 static struct hash_control *reg_hash;
-/* hash table for prefix lookup */
-static struct hash_control *prefix_hash;
 \f
 
 void
@@ -540,46 +528,40 @@ md_begin ()
 {
   const char *hash_err;
 
-  obstack_begin (&o, 4096);
-
   /* initialize op_hash hash table */
   op_hash = hash_new ();
 
   {
     register const template *optab;
     register templates *core_optab;
-    char *prev_name;
 
     optab = i386_optab;                /* setup for loop */
-    prev_name = optab->name;
-    obstack_grow (&o, optab, sizeof (template));
     core_optab = (templates *) xmalloc (sizeof (templates));
+    core_optab->start = optab;
 
-    for (optab++; optab < i386_optab_end; optab++)
+    while (1)
       {
-       if (!strcmp (optab->name, prev_name))
-         {
-           /* same name as before --> append to current template list */
-           obstack_grow (&o, optab, sizeof (template));
-         }
-       else
+       ++optab;
+       if (optab->name == NULL
+           || strcmp (optab->name, (optab - 1)->name) != 0)
          {
            /* different name --> ship out current template list;
               add to hash table; & begin anew */
-           /* Note: end must be set before start! since obstack_next_free
-              changes upon opstack_finish */
-           core_optab->end = (template *) obstack_next_free (&o);
-           core_optab->start = (template *) obstack_finish (&o);
-           hash_err = hash_insert (op_hash, prev_name, (char *) core_optab);
+           core_optab->end = optab;
+           hash_err = hash_insert (op_hash,
+                                   (optab - 1)->name,
+                                   (PTR) core_optab);
            if (hash_err)
              {
              hash_error:
-               as_fatal (_("Internal Error:  Can't hash %s: %s"), prev_name,
+               as_fatal (_("Internal Error:  Can't hash %s: %s"),
+                         (optab - 1)->name,
                          hash_err);
              }
-           prev_name = optab->name;
+           if (optab->name == NULL)
+             break;
            core_optab = (templates *) xmalloc (sizeof (templates));
-           obstack_grow (&o, optab, sizeof (template));
+           core_optab->start = optab;
          }
       }
   }
@@ -589,7 +571,9 @@ md_begin ()
   {
     register const reg_entry *regtab;
 
-    for (regtab = i386_regtab; regtab < i386_regtab_end; regtab++)
+    for (regtab = i386_regtab;
+        regtab < i386_regtab + sizeof (i386_regtab) / sizeof (i386_regtab[0]);
+        regtab++)
       {
        hash_err = hash_insert (reg_hash, regtab->reg_name, (PTR) regtab);
        if (hash_err)
@@ -597,21 +581,6 @@ md_begin ()
       }
   }
 
-  /* initialize reg_hash hash table */
-  prefix_hash = hash_new ();
-  {
-    register const prefix_entry *prefixtab;
-
-    for (prefixtab = i386_prefixtab;
-        prefixtab < i386_prefixtab_end; prefixtab++)
-      {
-       hash_err = hash_insert (prefix_hash, prefixtab->prefix_name,
-                               (PTR) prefixtab);
-       if (hash_err)
-         goto hash_error;
-      }
-  }
-
   /* fill in lexical tables:  opcode_chars, operand_chars, space_chars */
   {
     register int c;
@@ -619,41 +588,40 @@ md_begin ()
 
     for (c = 0; c < 256; c++)
       {
-       if (islower (c) || isdigit (c))
+       if (isdigit (c))
          {
+           digit_chars[c] = c;
            opcode_chars[c] = c;
            register_chars[c] = c;
+           operand_chars[c] = c;
          }
-       else if (isupper (c))
-         {
-           opcode_chars[c] = tolower (c);
-           register_chars[c] = opcode_chars[c];
-         }
-       else if (c == PREFIX_SEPERATOR)
+       else if (islower (c))
          {
            opcode_chars[c] = c;
+           register_chars[c] = c;
+           operand_chars[c] = c;
          }
-       else if (c == ')' || c == '(')
+       else if (isupper (c))
          {
-           register_chars[c] = c;
+           opcode_chars[c] = tolower (c);
+           register_chars[c] = opcode_chars[c];
+           operand_chars[c] = c;
          }
 
-       if (isupper (c) || islower (c) || isdigit (c))
-         operand_chars[c] = c;
-
-       if (isdigit (c) || c == '-')
-         digit_chars[c] = c;
-
-       if (isalpha (c) || c == '_' || c == '.' || isdigit (c))
+       if (isalpha (c) || isdigit (c))
          identifier_chars[c] = c;
+      }
 
 #ifdef LEX_AT
-       identifier_chars['@'] = '@';
+    identifier_chars['@'] = '@';
 #endif
-
-       if (c == ' ' || c == '\t')
-         space_chars[c] = c;
-      }
+    register_chars[')'] = ')';
+    register_chars['('] = '(';
+    digit_chars['-'] = '-';
+    identifier_chars['_'] = '_';
+    identifier_chars['.'] = '.';
+    space_chars[' '] = ' ';
+    space_chars['\t'] = '\t';
 
     for (p = operand_special_chars; *p != '\0'; p++)
       operand_chars[(unsigned char) *p] = *p;
@@ -675,7 +643,6 @@ i386_print_statistics (file)
 {
   hash_print_statistics (file, "i386 opcode", op_hash);
   hash_print_statistics (file, "i386 register", reg_hash);
-  hash_print_statistics (file, "i386 prefix", prefix_hash);
 }
 \f
 
@@ -838,24 +805,25 @@ reloc (size, pcrel, other)
   if (other != NO_RELOC) return other;
 
   if (pcrel)
-    switch (size)
-      {
-      case 1: return BFD_RELOC_8_PCREL;
-      case 2: return BFD_RELOC_16_PCREL;
-      case 4: return BFD_RELOC_32_PCREL;
-      }
-  else
-    switch (size)
-      {
-      case 1: return BFD_RELOC_8;
-      case 2: return BFD_RELOC_16;
-      case 4: return BFD_RELOC_32;
-      }
-
-  if (pcrel)
-    as_bad (_("Can not do %d byte pc-relative relocation"), size);
+    {
+      switch (size)
+       {
+       case 1: return BFD_RELOC_8_PCREL;
+       case 2: return BFD_RELOC_16_PCREL;
+       case 4: return BFD_RELOC_32_PCREL;
+       }
+      as_bad (_("Can not do %d byte pc-relative relocation"), size);
+    }
   else
-    as_bad (_("Can not do %d byte relocation"), size);
+    {
+      switch (size)
+       {
+       case 1: return BFD_RELOC_8;
+       case 2: return BFD_RELOC_16;
+       case 4: return BFD_RELOC_32;
+       }
+      as_bad (_("Can not do %d byte relocation"), size);
+    }
 
   return BFD_RELOC_NONE;
 }
@@ -886,7 +854,9 @@ tc_i386_fix_adjustable(fixP)
 }
 #else
 #define reloc(SIZE,PCREL,OTHER)        0
+#define BFD_RELOC_16           0
 #define BFD_RELOC_32           0
+#define BFD_RELOC_16_PCREL     0
 #define BFD_RELOC_32_PCREL     0
 #define BFD_RELOC_386_PLT32    0
 #define BFD_RELOC_386_GOT32    0
@@ -921,76 +891,72 @@ md_assemble (line)
      We assume that the scrubber has arranged it so that line[0] is the valid
      start of a (possibly prefixed) opcode. */
   {
+    char opcode[MAX_OPCODE_SIZE];
     char *l = line;
+    char *token_start = l;
+    char *opp;
 
-    /* 1 if operand is pending after ','. */
-    unsigned int expecting_operand = 0;
     /* Non-zero if we found a prefix only acceptable with string insns. */
     const char *expecting_string_instruction = NULL;
-    /* Non-zero if operand parens not balanced. */
-    unsigned int paren_not_balanced;
-    char *token_start = l;
 
-    while (!is_space_char (*l) && *l != END_OF_INSN)
+    while (1)
       {
-       if (!is_opcode_char (*l))
+       opp = opcode;
+       while ((*opp = opcode_chars[(unsigned char) *l]) != 0)
          {
-           as_bad (_("invalid character %s in opcode"), output_invalid (*l));
-           return;
+           opp++;
+           if (opp >= opcode + sizeof (opcode))
+             {
+               as_bad (_("no such 386 instruction: `%s'"), token_start);
+               return;
+             }
+           l++;
          }
-       else if (*l != PREFIX_SEPERATOR)
+       if (!is_space_char (*l)
+           && *l != END_OF_INSN
+           && *l != PREFIX_SEPARATOR)
          {
-           *l = opcode_chars[(unsigned char) *l];      /* fold case of opcodes */
-           l++;
+           as_bad (_("invalid character %s in opcode"),
+                   output_invalid (*l));
+           return;
          }
-       else
+       if (token_start == l)
          {
-           /* This opcode's got a prefix.  */
-           prefix_entry *prefix;
+           if (*l == PREFIX_SEPARATOR)
+             as_bad (_("expecting prefix; got nothing"));
+           else
+             as_bad (_("expecting opcode; got nothing"));
+           return;
+         }
 
-           if (l == token_start)
-             {
-               as_bad (_("expecting prefix; got nothing"));
-               return;
-             }
-           END_STRING_AND_SAVE (l);
-           prefix = (prefix_entry *) hash_find (prefix_hash, token_start);
-           if (!prefix)
-             {
-               as_bad (_("no such opcode prefix `%s'"), token_start);
-               RESTORE_END_STRING (l);
-               return;
-             }
-           RESTORE_END_STRING (l);
-           /* add prefix, checking for repeated prefixes */
-           switch (add_prefix (prefix->prefix_code))
+       /* Look up instruction (or prefix) via hash table.  */
+       current_templates = hash_find (op_hash, opcode);
+
+       if (*l != END_OF_INSN
+           && current_templates
+           && (current_templates->start->opcode_modifier & IsPrefix) != 0)
+         {
+           /* Add prefix, checking for repeated prefixes.  */
+           switch (add_prefix (current_templates->start->base_opcode))
              {
              case 0:
                return;
              case 2:
-               expecting_string_instruction = prefix->prefix_name;
+               expecting_string_instruction =
+                 current_templates->start->name;
                break;
              }
            /* Skip past PREFIX_SEPARATOR and reset token_start.  */
            token_start = ++l;
          }
-      }
-    END_STRING_AND_SAVE (l);
-    if (token_start == l)
-      {
-       as_bad (_("expecting opcode; got nothing"));
-       RESTORE_END_STRING (l);
-       return;
+       else
+         break;
       }
 
-    /* Lookup insn in hash; try intel & att naming conventions if appropriate;
-       that is:  we only use the opcode suffix 'b' 'w' or 'l' if we need to. */
-    current_templates = (templates *) hash_find (op_hash, token_start);
     if (!current_templates)
       {
-       int last_index = strlen (token_start) - 1;
-       char last_char = token_start[last_index];
-       switch (last_char)
+       /* See if we can get a match by trimming off a suffix.  */
+       switch (opp[-1])
          {
          case DWORD_OPCODE_SUFFIX:
          case WORD_OPCODE_SUFFIX:
@@ -999,19 +965,16 @@ md_assemble (line)
 #if LONG_OPCODE_SUFFIX != DWORD_OPCODE_SUFFIX
          case LONG_OPCODE_SUFFIX:
 #endif
-           token_start[last_index] = '\0';
-           current_templates = (templates *) hash_find (op_hash, token_start);
-           token_start[last_index] = last_char;
-           i.suffix = last_char;
+           i.suffix = opp[-1];
+           opp[-1] = '\0';
+           current_templates = hash_find (op_hash, opcode);
          }
        if (!current_templates)
          {
            as_bad (_("no such 386 instruction: `%s'"), token_start);
-           RESTORE_END_STRING (l);
            return;
          }
       }
-    RESTORE_END_STRING (l);
 
     /* check for rep/repne without a string instruction */
     if (expecting_string_instruction
@@ -1026,6 +989,13 @@ md_assemble (line)
     if (*l != END_OF_INSN)
       {
        /* parse operands */
+
+       /* 1 if operand is pending after ','. */
+       unsigned int expecting_operand = 0;
+
+       /* Non-zero if operand parens not balanced. */
+       unsigned int paren_not_balanced;
+
        do
          {
            /* skip optional white space before operand */
@@ -1123,9 +1093,8 @@ md_assemble (line)
 
 #define MATCH(overlap, given, template) \
   ((overlap) \
-   && (((overlap) & (JumpAbsolute|BaseIndex)) \
-       == ((given) & (JumpAbsolute|BaseIndex))))
-
+   && ((given) & BaseIndex) == ((overlap) & BaseIndex) \
+   && ((given) & JumpAbsolute) == ((template) & JumpAbsolute))
 
   /* If given types r0 and r1 are registers they must be of the same type
      unless the expected operand type register overlap is null.
@@ -1408,39 +1377,56 @@ md_assemble (line)
        && overlap0 != Imm8 && overlap0 != Imm8S
        && overlap0 != Imm16 && overlap0 != Imm32)
       {
-       if (!i.suffix)
+       if (i.suffix)
+         {
+          overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
+                       (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+         }
+       else if (overlap0 == (Imm16 | Imm32))
+         {
+          overlap0 =
+             (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+         }
+       else
          {
            as_bad (_("no opcode suffix given; can't determine immediate size"));
            return;
          }
-       overlap0 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
-                    (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
       }
     if ((overlap1 & (Imm8 | Imm8S | Imm16 | Imm32))
        && overlap1 != Imm8 && overlap1 != Imm8S
        && overlap1 != Imm16 && overlap1 != Imm32)
       {
-       if (!i.suffix)
+       if (i.suffix)
+         {
+          overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
+                       (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
+         }
+       else if (overlap1 == (Imm16 | Imm32))
+         {
+          overlap1 =
+             (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
+         }
+       else
          {
            as_bad (_("no opcode suffix given; can't determine immediate size"));
            return;
          }
-       overlap1 &= (i.suffix == BYTE_OPCODE_SUFFIX ? (Imm8 | Imm8S) :
-                    (i.suffix == WORD_OPCODE_SUFFIX ? Imm16 : Imm32));
       }
 
     i.types[0] = overlap0;
-    i.types[1] = overlap1;
-    i.types[2] = overlap2;
-
     if (overlap0 & ImplicitRegister)
       i.reg_operands--;
+    if (overlap0 & Imm1)
+      i.imm_operands = 0;      /* kludge for shift insns */
+
+    i.types[1] = overlap1;
     if (overlap1 & ImplicitRegister)
       i.reg_operands--;
+
+    i.types[2] = overlap2;
     if (overlap2 & ImplicitRegister)
       i.reg_operands--;
-    if (overlap0 & Imm1)
-      i.imm_operands = 0;      /* kludge for shift insns */
 
     /* Finalize opcode.  First, we change the opcode based on the operand
        size given by i.suffix: we never have to change things for byte insns,
@@ -1492,11 +1478,6 @@ md_assemble (line)
           This is only for optimizing out unnecessary segment overrides.  */
        const seg_entry *default_seg = 0;
 
-       /* True if this instruction uses a memory addressing mode,
-          and therefore may need an address-size prefix.  */
-       int uses_mem_addrmode = 0;
-
-
        /* If we found a reverse match we must alter the opcode
           direction bit.  found_reverse_match holds bits to change
           (different for int & float insns).  */
@@ -1612,24 +1593,63 @@ md_assemble (line)
                        if (! i.index_reg)
                          {
                            /* Operand is just <disp> */
-                           i.rm.regmem = NO_BASE_REGISTER;
-                           i.types[op] &= ~Disp;
-                           i.types[op] |= Disp32;
+                           if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+                             {
+                               i.rm.regmem = NO_BASE_REGISTER_16;
+                               i.types[op] &= ~Disp;
+                               i.types[op] |= Disp16;
+                             }
+                           else
+                             {
+                               i.rm.regmem = NO_BASE_REGISTER;
+                               i.types[op] &= ~Disp;
+                               i.types[op] |= Disp32;
+                             }
                          }
-                       else
+                       else /* ! i.base_reg && i.index_reg */
                          {
-                           i.bi.index = i.index_reg->reg_num;
-                           i.bi.base = NO_BASE_REGISTER;
-                           i.bi.scale = i.log2_scale_factor;
+                           i.sib.index = i.index_reg->reg_num;
+                           i.sib.base = NO_BASE_REGISTER;
+                           i.sib.scale = i.log2_scale_factor;
                            i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
                            i.types[op] &= ~Disp;
                            i.types[op] |= Disp32;      /* Must be 32 bit */
                          }
                      }
-                   else /* i.base_reg */
+                   else if (i.base_reg->reg_type & Reg16)
+                     {
+                       switch (i.base_reg->reg_num)
+                         {
+                         case 3: /* (%bx) */
+                           if (! i.index_reg)
+                             i.rm.regmem = 7;
+                           else /* (%bx,%si) -> 0, or (%bx,%di) -> 1 */
+                             i.rm.regmem = i.index_reg->reg_num - 6;
+                           break;
+                         case 5: /* (%bp) */
+                           default_seg = &ss;
+                           if (! i.index_reg)
+                             {
+                               i.rm.regmem = 6;
+                               if ((i.types[op] & Disp) == 0)
+                                 {
+                                   /* fake (%bp) into 0(%bp) */
+                                   i.types[op] |= Disp8;
+                                   fake_zero_displacement = 1;
+                                 }
+                             }
+                           else /* (%bp,%si) -> 2, or (%bp,%di) -> 3 */
+                             i.rm.regmem = i.index_reg->reg_num - 6 + 2;
+                           break;
+                         default: /* (%si) -> 4 or (%di) -> 5 */
+                           i.rm.regmem = i.base_reg->reg_num - 6 + 4;
+                         }
+                       i.rm.mode = mode_from_disp_size (i.types[op]);
+                     }
+                   else /* i.base_reg and 32 bit mode */
                      {
                        i.rm.regmem = i.base_reg->reg_num;
-                       i.bi.base = i.base_reg->reg_num;
+                       i.sib.base = i.base_reg->reg_num;
                        if (i.base_reg->reg_num == EBP_REG_NUM)
                          {
                            default_seg = &ss;
@@ -1643,7 +1663,7 @@ md_assemble (line)
                          {
                            default_seg = &ss;
                          }
-                       i.bi.scale = i.log2_scale_factor;
+                       i.sib.scale = i.log2_scale_factor;
                        if (! i.index_reg)
                          {
                            /* <disp>(%esp) becomes two byte modrm
@@ -1652,7 +1672,7 @@ md_assemble (line)
                               ie. ESCAPE_TO_TWO_BYTE_ADDRESSING.  Any
                               base register besides %esp will not use
                               the extra modrm byte.  */
-                           i.bi.index = NO_INDEX_REGISTER;
+                           i.sib.index = NO_INDEX_REGISTER;
 #if ! SCALE1_WHEN_NO_INDEX
                            /* Another case where we force the second
                               modrm byte.  */
@@ -1662,7 +1682,7 @@ md_assemble (line)
                          }
                        else
                          {
-                           i.bi.index = i.index_reg->reg_num;
+                           i.sib.index = i.index_reg->reg_num;
                            i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
                          }
                        i.rm.mode = mode_from_disp_size (i.types[op]);
@@ -1716,9 +1736,6 @@ md_assemble (line)
                if (i.tm.extension_opcode != None)
                  i.rm.reg = i.tm.extension_opcode;
              }
-
-           if (i.rm.mode != 3)
-             uses_mem_addrmode = 1;
          }
        else if (i.tm.opcode_modifier & (Seg2ShortForm | Seg3ShortForm))
          {
@@ -1731,10 +1748,6 @@ md_assemble (line)
          }
        else if ((i.tm.base_opcode & ~(D|W)) == MOV_AX_DISP32)
          {
-           /* This is a special non-modrm instruction
-              that addresses memory with a 32-bit displacement mode anyway,
-              and thus requires an address-size prefix if in 16-bit mode.  */
-           uses_mem_addrmode = 1;
            default_seg = &ds;
          }
        else if ((i.tm.opcode_modifier & IsString) != 0)
@@ -1744,15 +1757,6 @@ md_assemble (line)
            default_seg = &ds;
          }
 
-       /* GAS currently doesn't support 16-bit memory addressing modes at all,
-          so if we're writing 16-bit code and using a memory addressing mode,
-          always spew out an address size prefix.  */
-       if (uses_mem_addrmode && flag_16bit_code)
-         {
-           if (! add_prefix (ADDR_PREFIX_OPCODE))
-             return;
-         }
-
        /* If a segment was explicitly specified,
           and the specified segment is not the default,
           use an opcode prefix to select it.
@@ -1787,6 +1791,16 @@ md_assemble (line)
     if (i.tm.opcode_modifier & Jump)
       {
        unsigned long n = i.disps[0]->X_add_number;
+       int prefix = (i.prefix[DATA_PREFIX] != 0);
+       int code16 = 0;
+
+       if (prefix)
+         {
+           i.prefixes -= 1;
+           code16 = CODE16;
+         }
+       if (flag_16bit_code)
+         code16 ^= CODE16;
 
        if (i.prefixes != 0)
          as_warn (_("skipping prefixes on this instruction"));
@@ -1795,20 +1809,19 @@ md_assemble (line)
          {
            if (fits_in_signed_byte (n))
              {
-               p = frag_more (2);
                insn_size += 2;
+               p = frag_more (2);
                p[0] = i.tm.base_opcode;
                p[1] = n;
              }
            else
-             { /* It's an absolute word/dword displacement. */
-
+             {
                /* Use 16-bit jumps only for 16-bit code,
                   because text segments are limited to 64K anyway;
                   Use 32-bit jumps for 32-bit code, because they're faster,
                   and a 16-bit jump will clear the top 16 bits of %eip.  */
-               int jmp_size = flag_16bit_code ? 2 : 4;
-               if (flag_16bit_code && !fits_in_signed_word (n))
+               int jmp_size = code16 ? 2 : 4;
+               if (code16 && !fits_in_signed_word (n))
                  {
                    as_bad (_("16-bit jump out of range"));
                    return;
@@ -1817,43 +1830,45 @@ md_assemble (line)
                if (i.tm.base_opcode == JUMP_PC_RELATIVE)
                  {             /* pace */
                    /* unconditional jump */
-                   p = frag_more (1 + jmp_size);
-                   insn_size += 1 + jmp_size;
-                   p[0] = (char) 0xe9;
-                   md_number_to_chars (&p[1], (valueT) n, jmp_size);
+                   insn_size += prefix + 1 + jmp_size;
+                   p = frag_more (prefix + 1 + jmp_size);
+                   if (prefix)
+                     *p++ = DATA_PREFIX_OPCODE;
+                   *p++ = (char) 0xe9;
+                   md_number_to_chars (p, (valueT) n, jmp_size);
                  }
                else
                  {
                    /* conditional jump */
-                   p = frag_more (2 + jmp_size);
-                   insn_size += 2 + jmp_size;
-                   p[0] = TWO_BYTE_OPCODE_ESCAPE;
-                   p[1] = i.tm.base_opcode + 0x10;
-                   md_number_to_chars (&p[2], (valueT) n, jmp_size);
+                   insn_size += prefix + 2 + jmp_size;
+                   p = frag_more (prefix + 2 + jmp_size);
+                   if (prefix)
+                     *p++ = DATA_PREFIX_OPCODE;
+                   *p++ = TWO_BYTE_OPCODE_ESCAPE;
+                   *p++ = i.tm.base_opcode + 0x10;
+                   md_number_to_chars (p, (valueT) n, jmp_size);
                  }
              }
          }
        else
          {
-           if (flag_16bit_code)
-             {
-               FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
-               insn_size += 1;
-             }
+           int size = code16 ? 2 : 4;
 
            /* It's a symbol; end frag & setup for relax.
               Make sure there are more than 6 chars left in the current frag;
               if not we'll have to start a new one. */
-           frag_grow (7);
-           p = frag_more (1);
-           insn_size += 1;
-           p[0] = i.tm.base_opcode;
+           frag_grow (prefix + 1 + 2 + size);
+           insn_size += 1 + prefix;
+           p = frag_more (1 + prefix);
+           if (prefix)
+             *p++ = DATA_PREFIX_OPCODE;
+           *p = i.tm.base_opcode;
            frag_var (rs_machine_dependent,
-                     6,        /* 2 opcode/prefix + 4 displacement */
+                     prefix + 2 + size, /* 2 opcode/prefix + displacement */
                      1,
                      ((unsigned char) *p == JUMP_PC_RELATIVE
-                      ? ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE)
-                      : ENCODE_RELAX_STATE (COND_JUMP, BYTE)),
+                      ? ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL) | code16
+                      : ENCODE_RELAX_STATE (COND_JUMP, SMALL) | code16),
                      i.disps[0]->X_add_symbol,
                      (offsetT) n, p);
          }
@@ -1867,45 +1882,58 @@ md_assemble (line)
          {
            if (i.prefix[ADDR_PREFIX])
              {
+               insn_size += 1;
                FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE);
+               i.prefixes -= 1;
+             }
+         }
+       else
+         {
+           int code16 = 0;
+
+           if (i.prefix[DATA_PREFIX])
+             {
                insn_size += 1;
+               FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
                i.prefixes -= 1;
+               code16 = CODE16;
              }
+           if (flag_16bit_code)
+             code16 ^= CODE16;
+
+           if (code16)
+             size = 2;
          }
 
        if (i.prefixes != 0)
          as_warn (_("skipping prefixes on this instruction"));
 
-       if (size == 4 && flag_16bit_code)
-         {
-           FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
-           insn_size += 1;
-         }
-
        if (fits_in_unsigned_byte (i.tm.base_opcode))
          {
-           FRAG_APPEND_1_CHAR (i.tm.base_opcode);
-           insn_size += 1;
+           insn_size += 1 + size;
+           p = frag_more (1 + size);
          }
        else
          {
-           p = frag_more (2);  /* opcode can be at most two bytes */
-           insn_size += 2;
-           /* put out high byte first: can't use md_number_to_chars! */
+           insn_size += 2 + size;      /* opcode can be at most two bytes */
+           p = frag_more (2 + size);
            *p++ = (i.tm.base_opcode >> 8) & 0xff;
-           *p = i.tm.base_opcode & 0xff;
          }
+       *p++ = i.tm.base_opcode & 0xff;
 
-       p = frag_more (size);
-       insn_size += size;
        if (i.disps[0]->X_op == O_constant)
          {
-           md_number_to_chars (p, (valueT) n, size);
            if (size == 1 && !fits_in_signed_byte (n))
              {
                as_bad (_("`%s' only takes byte displacement; %lu shortened to %d"),
                        i.tm.name, n, *p);
              }
+           else if (size == 2 && !fits_in_signed_word (n))
+             {
+               as_bad (_("16-bit jump out of range"));
+               return;
+             }
+           md_number_to_chars (p, (valueT) n, size);
          }
        else
          {
@@ -1916,26 +1944,52 @@ md_assemble (line)
       }
     else if (i.tm.opcode_modifier & JumpInterSegment)
       {
-       if (i.prefixes != 0)
-         as_warn (_("skipping prefixes on this instruction"));
+       int size;
+       int reloc_type;
+       int prefix = i.prefix[DATA_PREFIX] != 0;
+       int code16 = 0;
 
+       if (prefix)
+         {
+           code16 = CODE16;
+           i.prefixes -= 1;
+         }
        if (flag_16bit_code)
+         code16 ^= CODE16;
+
+       size = 4;
+       reloc_type = BFD_RELOC_32;
+       if (code16)
          {
-           FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
-           insn_size += 1;
+           size = 2;
+           reloc_type = BFD_RELOC_16;
          }
 
-       p = frag_more (1 + 2 + 4);      /* 1 opcode; 2 segment; 4 offset */
-       insn_size += 1 + 2 + 4;
-       p[0] = i.tm.base_opcode;
+       if (i.prefixes != 0)
+         as_warn (_("skipping prefixes on this instruction"));
+
+       insn_size += prefix + 1 + 2 + size;  /* 1 opcode; 2 segment; offset */
+       p = frag_more (prefix + 1 + 2 + size);
+       if (prefix)
+         *p++ = DATA_PREFIX_OPCODE;
+       *p++ = i.tm.base_opcode;
        if (i.imms[1]->X_op == O_constant)
-         md_number_to_chars (p + 1, (valueT) i.imms[1]->X_add_number, 4);
+         {
+           unsigned long n = i.imms[1]->X_add_number;
+           if (size == 2 && !fits_in_unsigned_word (n))
+             {
+               as_bad (_("16-bit jump out of range"));
+               return;
+             }
+           md_number_to_chars (p, (valueT) n, size);
+         }
        else
-         fix_new_exp (frag_now, p + 1 - frag_now->fr_literal, 4,
-                      i.imms[1], 0, BFD_RELOC_32);
+         fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+                      i.imms[1], 0, reloc_type);
        if (i.imms[0]->X_op != O_constant)
-         as_bad (_("can't handle non absolute segment in long call/jmp"));
-       md_number_to_chars (p + 5, (valueT) i.imms[0]->X_add_number, 2);
+         as_bad (_("can't handle non absolute segment in `%s'"),
+                 i.tm.name);
+       md_number_to_chars (p + size, (valueT) i.imms[0]->X_add_number, 2);
       }
     else
       {
@@ -1949,8 +2003,8 @@ md_assemble (line)
          {
            if (*q)
              {
-               p = frag_more (1);
                insn_size += 1;
+               p = frag_more (1);
                md_number_to_chars (p, (valueT) *q, 1);
              }
          }
@@ -1958,13 +2012,13 @@ md_assemble (line)
        /* Now the opcode; be careful about word order here! */
        if (fits_in_unsigned_byte (i.tm.base_opcode))
          {
-           FRAG_APPEND_1_CHAR (i.tm.base_opcode);
            insn_size += 1;
+           FRAG_APPEND_1_CHAR (i.tm.base_opcode);
          }
        else if (fits_in_unsigned_word (i.tm.base_opcode))
          {
-           p = frag_more (2);
            insn_size += 2;
+           p = frag_more (2);
            /* put out high byte first: can't use md_number_to_chars! */
            *p++ = (i.tm.base_opcode >> 8) & 0xff;
            *p = i.tm.base_opcode & 0xff;
@@ -1973,26 +2027,25 @@ md_assemble (line)
          {                     /* opcode is either 3 or 4 bytes */
            if (i.tm.base_opcode & 0xff000000)
              {
-               p = frag_more (4);
                insn_size += 4;
+               p = frag_more (4);
                *p++ = (i.tm.base_opcode >> 24) & 0xff;
              }
            else
              {
-               p = frag_more (3);
                insn_size += 3;
+               p = frag_more (3);
              }
            *p++ = (i.tm.base_opcode >> 16) & 0xff;
            *p++ = (i.tm.base_opcode >> 8) & 0xff;
            *p = (i.tm.base_opcode) & 0xff;
          }
 
-       /* Now the modrm byte and base index byte (if present). */
+       /* Now the modrm byte and sib byte (if present).  */
        if (i.tm.opcode_modifier & Modrm)
          {
-           p = frag_more (1);
            insn_size += 1;
-           /* md_number_to_chars (p, i.rm, 1); */
+           p = frag_more (1);
            md_number_to_chars (p,
                                (valueT) (i.rm.regmem << 0
                                          | i.rm.reg << 3
@@ -2000,16 +2053,18 @@ md_assemble (line)
                                1);
            /* If i.rm.regmem == ESP (4)
               && i.rm.mode != (Register mode)
+              && not 16 bit
               ==> need second modrm byte.  */
            if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING
-               && i.rm.mode != 3)
+               && i.rm.mode != 3
+               && !(i.base_reg && (i.base_reg->reg_type & Reg16) != 0))
              {
-               p = frag_more (1);
                insn_size += 1;
-               /* md_number_to_chars (p, i.bi, 1); */
-               md_number_to_chars (p, (valueT) (i.bi.base << 0
-                                                | i.bi.index << 3
-                                                | i.bi.scale << 6),
+               p = frag_more (1);
+               md_number_to_chars (p,
+                                   (valueT) (i.sib.base << 0
+                                             | i.sib.index << 3
+                                             | i.sib.scale << 6),
                                    1);
              }
          }
@@ -2026,37 +2081,44 @@ md_assemble (line)
                      {
                        if (i.types[n] & Disp8)
                          {
-                           p = frag_more (1);
                            insn_size += 1;
+                           p = frag_more (1);
                            md_number_to_chars (p,
                                                (valueT) i.disps[n]->X_add_number,
                                                1);
                          }
                        else if (i.types[n] & Disp16)
                          {
-                           p = frag_more (2);
                            insn_size += 2;
+                           p = frag_more (2);
                            md_number_to_chars (p,
                                                (valueT) i.disps[n]->X_add_number,
                                                2);
                          }
                        else
                          {     /* Disp32 */
-                           p = frag_more (4);
                            insn_size += 4;
+                           p = frag_more (4);
                            md_number_to_chars (p,
                                                (valueT) i.disps[n]->X_add_number,
                                                4);
                          }
                      }
-                   else
-                     {         /* not absolute_section */
-                       /* need a 32-bit fixup (don't support 8bit non-absolute disps) */
-                       p = frag_more (4);
+                   else if (i.types[n] & Disp32)
+                     {
                        insn_size += 4;
+                       p = frag_more (4);
                        fix_new_exp (frag_now, p - frag_now->fr_literal, 4,
-                                           i.disps[n], 0, 
-                                           TC_RELOC(i.disp_reloc[n], BFD_RELOC_32));
+                                    i.disps[n], 0,
+                                    TC_RELOC (i.disp_reloc[n], BFD_RELOC_32));
+                     }
+                   else
+                     { /* must be Disp16 */
+                       insn_size += 2;
+                       p = frag_more (2);
+                       fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+                                    i.disps[n], 0,
+                                    TC_RELOC (i.disp_reloc[n], BFD_RELOC_16));
                      }
                  }
              }
@@ -2075,24 +2137,24 @@ md_assemble (line)
                      {
                        if (i.types[n] & (Imm8 | Imm8S))
                          {
-                           p = frag_more (1);
                            insn_size += 1;
+                           p = frag_more (1);
                            md_number_to_chars (p,
                                                (valueT) i.imms[n]->X_add_number,
                                                1);
                          }
                        else if (i.types[n] & Imm16)
                          {
-                           p = frag_more (2);
                            insn_size += 2;
+                           p = frag_more (2);
                            md_number_to_chars (p,
                                                (valueT) i.imms[n]->X_add_number,
                                                2);
                          }
                        else
                          {
-                           p = frag_more (4);
                            insn_size += 4;
+                           p = frag_more (4);
                            md_number_to_chars (p,
                                                (valueT) i.imms[n]->X_add_number,
                                                4);
@@ -2113,9 +2175,9 @@ md_assemble (line)
                          size = 2;
                        else
                          size = 4;
-                       r_type = reloc (size, 0, i.disp_reloc[0]);
-                       p = frag_more (size);
                        insn_size += size;
+                       p = frag_more (size);
+                       r_type = reloc (size, 0, i.disp_reloc[0]);
 #ifdef BFD_ASSEMBLER
                        if (r_type == BFD_RELOC_32
                            && GOT_symbol
@@ -2155,13 +2217,6 @@ i386_operand (operand_string)
 {
   register char *op_string = operand_string;
 
-  /* Address of '\0' at end of operand_string. */
-  char *end_of_operand_string = operand_string + strlen (operand_string);
-
-  /* Start and end of displacement string expression (if found). */
-  char *displacement_string_start = NULL;
-  char *displacement_string_end = NULL;
-
   /* We check for an absolute prefix (differentiating,
      for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
   if (*op_string == ABSOLUTE_PREFIX)
@@ -2173,7 +2228,7 @@ i386_operand (operand_string)
   /* Check if operand is a register. */
   if (*op_string == REGISTER_PREFIX)
     {
-      register reg_entry *r;
+      register const reg_entry *r;
       if (!(r = parse_register (op_string)))
        {
          as_bad (_("bad register name `%s'"), op_string);
@@ -2186,22 +2241,22 @@ i386_operand (operand_string)
          switch (r->reg_num)
            {
            case 0:
-             i.seg[i.mem_operands] = (seg_entry *) & es;
+             i.seg[i.mem_operands] = &es;
              break;
            case 1:
-             i.seg[i.mem_operands] = (seg_entry *) & cs;
+             i.seg[i.mem_operands] = &cs;
              break;
            case 2:
-             i.seg[i.mem_operands] = (seg_entry *) & ss;
+             i.seg[i.mem_operands] = &ss;
              break;
            case 3:
-             i.seg[i.mem_operands] = (seg_entry *) & ds;
+             i.seg[i.mem_operands] = &ds;
              break;
            case 4:
-             i.seg[i.mem_operands] = (seg_entry *) & fs;
+             i.seg[i.mem_operands] = &fs;
              break;
            case 5:
-             i.seg[i.mem_operands] = (seg_entry *) & gs;
+             i.seg[i.mem_operands] = &gs;
              break;
            }
          op_string += 4;       /* skip % <x> s : */
@@ -2268,7 +2323,7 @@ i386_operand (operand_string)
       else if (exp->X_op == O_constant)
        {
          i.types[this_operand] |=
-           smallest_imm_type ((unsigned long) exp->X_add_number);
+           smallest_imm_type ((long) exp->X_add_number);
        }
 #ifdef OBJ_AOUT
       else if (exp_seg != text_section
@@ -2287,8 +2342,9 @@ i386_operand (operand_string)
 #endif
       else
        {
-         /* this is an address ==> 32bit */
-         i.types[this_operand] |= Imm32;
+         /* This is an address.  */
+         i.types[this_operand] |=
+           (flag_16bit_code ^ (i.prefix[DATA_PREFIX] != 0)) ? Imm16 : Imm32;
        }
       /* shorten this type of this operand if the instruction wants
        * fewer bits than are present in the immediate.  The bit field
@@ -2309,9 +2365,14 @@ i386_operand (operand_string)
           || *op_string == '(')
     {
       /* This is a memory reference of some sort. */
+      char *end_of_operand_string;
       register char *base_string;
       int found_base_index_form;
 
+      /* Start and end of displacement string expression (if found). */
+      char *displacement_string_start;
+      char *displacement_string_end;
+
     do_memory_reference:
       if ((i.mem_operands == 1
           && (current_templates->start->opcode_modifier & IsString) == 0)
@@ -2326,8 +2387,9 @@ i386_operand (operand_string)
         looking for an ')' at the end of the operand, searching
         for the '(' matching it, and finding a REGISTER_PREFIX or ','
         after it. */
-      base_string = end_of_operand_string - 1;
       found_base_index_form = 0;
+      end_of_operand_string = op_string + strlen (op_string);
+      base_string = end_of_operand_string - 1;
       if (*base_string == ')')
        {
          unsigned int parens_balanced = 1;
@@ -2363,6 +2425,7 @@ i386_operand (operand_string)
          i.types[this_operand] |= BaseIndex;
 
          /* If there is a displacement set-up for it to be parsed later. */
+         displacement_string_start = NULL;
          if (base_string != op_string + 1)
            {
              displacement_string_start = op_string;
@@ -2483,6 +2546,11 @@ i386_operand (operand_string)
          register expressionS *exp;
          segT exp_seg = 0;
          char *save_input_line_pointer;
+         int bigdisp = Disp32;
+
+         if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
+            bigdisp = Disp16;
+         i.types[this_operand] |= bigdisp;
 
          exp = &disp_expressions[i.disp_operands];
          i.disps[this_operand] = exp;
@@ -2491,7 +2559,51 @@ i386_operand (operand_string)
          save_input_line_pointer = input_line_pointer;
          input_line_pointer = displacement_string_start;
          END_STRING_AND_SAVE (displacement_string_end);
+#ifndef GCC_ASM_O_HACK
+#define GCC_ASM_O_HACK 0
+#endif
+#if GCC_ASM_O_HACK
+         END_STRING_AND_SAVE (displacement_string_end + 1);
+         if ((i.types[this_operand] & BaseIndex) != 0
+             && displacement_string_end[-1] == '+')
+           {
+            /* This hack is to avoid a warning when using the "o"
+               constraint within gcc asm statements.
+               For instance:
+
+               #define _set_tssldt_desc(n,addr,limit,type) \
+               __asm__ __volatile__ ( \
+               "movw %w2,%0\n\t" \
+               "movw %w1,2+%0\n\t" \
+               "rorl $16,%1\n\t" \
+               "movb %b1,4+%0\n\t" \
+               "movb %4,5+%0\n\t" \
+               "movb $0,6+%0\n\t" \
+               "movb %h1,7+%0\n\t" \
+               "rorl $16,%1" \
+               : "=o"(*(n)) : "q" (addr), "ri"(limit), "i"(type))
+
+               This works great except that the output assembler ends
+               up looking a bit weird if it turns out that there is
+               no offset.  You end up producing code that looks like:
+
+               #APP
+                       movw $235,(%eax)
+                       movw %dx,2+(%eax)
+                       rorl $16,%edx
+                       movb %dl,4+(%eax)
+                       movb $137,5+(%eax)
+                       movb $0,6+(%eax)
+                       movb %dh,7+(%eax)
+                       rorl $16,%edx
+               #NO_APP
+
+               So here we provide the missing zero.
+            */
 
+            *displacement_string_end = '0';
+           }
+#endif
 #ifndef LEX_AT
          {
            /*
@@ -2539,6 +2651,10 @@ i386_operand (operand_string)
                else
                  as_bad (_("Bad reloc specifier `%s' in expression"), cp + 1);
 
+               /* GOT relocations are not supported in 16 bit mode */
+               if (flag_16bit_code)
+                 as_bad (_("GOT relocations not supported in 16 bit mode"));
+
                input_line_pointer = tmpbuf;
              }
          }
@@ -2565,6 +2681,9 @@ i386_operand (operand_string)
          if (*input_line_pointer)
            as_bad (_("Ignoring junk `%s' after expression"),
                    input_line_pointer);
+#if GCC_ASM_O_HACK
+         RESTORE_END_STRING (displacement_string_end + 1);
+#endif
          RESTORE_END_STRING (displacement_string_end);
          input_line_pointer = save_input_line_pointer;
 
@@ -2574,33 +2693,28 @@ i386_operand (operand_string)
              /* missing expr becomes absolute 0 */
              as_bad (_("missing or invalid displacement `%s' taken as 0"),
                      operand_string);
-             i.types[this_operand] |= Disp;
              exp->X_op = O_constant;
              exp->X_add_number = 0;
              exp->X_add_symbol = (symbolS *) 0;
              exp->X_op_symbol = (symbolS *) 0;
+             i.types[this_operand] |= Disp8;
            }
          else
 #endif
          if (exp->X_op == O_constant)
            {
-             i.types[this_operand] |= SMALLEST_DISP_TYPE (exp->X_add_number);
-           }
-         else if (exp_seg == text_section
-                  || exp_seg == data_section
-                  || exp_seg == bss_section
-                  || exp_seg == undefined_section)
-           {
-             i.types[this_operand] |= Disp32;
+             if (fits_in_signed_byte (exp->X_add_number))
+               i.types[this_operand] |= Disp8;
            }
-         else
+#ifdef OBJ_AOUT
+         else if (exp_seg != text_section
+                  && exp_seg != data_section
+                  && exp_seg != bss_section
+                  && exp_seg != undefined_section)
            {
-#ifndef OBJ_AOUT
-             i.types[this_operand] |= Disp32;
-#else
              goto seg_unimplemented;
-#endif
            }
+#endif
        }
 
       /* Special case for (%dx) while doing input/output op.  */
@@ -2615,15 +2729,36 @@ i386_operand (operand_string)
          return 1;
        }
       /* Make sure the memory operand we've been dealt is valid.  */
-      if ((i.base_reg
-          && (i.base_reg->reg_type & Reg32) == 0)
-         || (i.index_reg
-             && ((i.index_reg->reg_type & (Reg32|BaseIndex))
-                 != (Reg32|BaseIndex))))
+      if (flag_16bit_code ^ (i.prefix[ADDR_PREFIX] != 0))
        {
-         as_bad (_("`%s' is not a valid %s bit base/index expression"),
-                 operand_string, "32");
-         return 0;
+         if ((i.base_reg
+              && ((i.base_reg->reg_type & (Reg16|BaseIndex))
+                  != (Reg16|BaseIndex)))
+             || (i.index_reg
+                 && (((i.index_reg->reg_type & (Reg16|BaseIndex))
+                      != (Reg16|BaseIndex))
+                     || ! (i.base_reg
+                           && i.base_reg->reg_num < 6
+                           && i.index_reg->reg_num >= 6
+                           && i.log2_scale_factor == 0))))
+           {
+             as_bad (_("`%s' is not a valid %s bit base/index expression"),
+                     operand_string, "16");
+             return 0;
+           }
+       }
+      else
+       {
+         if ((i.base_reg
+              && (i.base_reg->reg_type & Reg32) == 0)
+             || (i.index_reg
+                 && ((i.index_reg->reg_type & (Reg32|BaseIndex))
+                     != (Reg32|BaseIndex))))
+           {
+             as_bad (_("`%s' is not a valid %s bit base/index expression"),
+                     operand_string, "32");
+             return 0;
+           }
        }
       i.mem_operands++;
     }
@@ -2665,12 +2800,16 @@ md_estimate_size_before_relax (fragP, segment)
   if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
     {
       /* symbol is undefined in this segment */
+      int code16 = fragP->fr_subtype & CODE16;
+      int size = code16 ? 2 : 4;
+      int pcrel_reloc = code16 ? BFD_RELOC_16_PCREL : BFD_RELOC_32_PCREL;
+
       switch (opcode[0])
        {
        case JUMP_PC_RELATIVE:  /* make jmp (0xeb) a dword displacement jump */
          opcode[0] = 0xe9;     /* dword disp jmp */
-         fragP->fr_fix += 4;
-         fix_new (fragP, old_fr_fix, 4,
+         fragP->fr_fix += size;
+         fix_new (fragP, old_fr_fix, size,
                   fragP->fr_symbol,
                   fragP->fr_offset, 1,
                   (GOT_symbol && /* Not quite right - we should switch on
@@ -2680,8 +2819,8 @@ md_estimate_size_before_relax (fragP, segment)
                                     get it right all of the time, but I
                                     think it does not matter that much, as
                                     this will be right most of the time. ERY*/
-                   S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)?
-                  BFD_RELOC_386_PLT32 : BFD_RELOC_32_PCREL);
+                   S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+                  ? BFD_RELOC_386_PLT32 : pcrel_reloc);
          break;
 
        default:
@@ -2689,15 +2828,15 @@ md_estimate_size_before_relax (fragP, segment)
                           the dword-displacement jump 0x0f8N */
          opcode[1] = opcode[0] + 0x10;
          opcode[0] = TWO_BYTE_OPCODE_ESCAPE;   /* two-byte escape */
-         fragP->fr_fix += 1 + 4;       /* we've added an opcode byte */
-         fix_new (fragP, old_fr_fix + 1, 4,
+         fragP->fr_fix += 1 + size;    /* we've added an opcode byte */
+         fix_new (fragP, old_fr_fix + 1, size,
                   fragP->fr_symbol,
-                  fragP->fr_offset, 1, 
+                  fragP->fr_offset, 1,
                   (GOT_symbol &&  /* Not quite right - we should switch on
                                     presence of @PLT, but I cannot see how
                                     to get to that from here.  ERY */
-                   S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)?
-                  BFD_RELOC_386_PLT32 : BFD_RELOC_32_PCREL);
+                   S_GET_SEGMENT(fragP->fr_symbol) == undefined_section)
+                  ? BFD_RELOC_386_PLT32 : pcrel_reloc);
          break;
        }
       frag_wane (fragP);
@@ -2753,38 +2892,38 @@ md_convert_frag (abfd, sec, fragP)
 
   switch (fragP->fr_subtype)
     {
-    case ENCODE_RELAX_STATE (COND_JUMP, BYTE):
-    case ENCODE_RELAX_STATE (UNCOND_JUMP, BYTE):
+    case ENCODE_RELAX_STATE (COND_JUMP, SMALL):
+    case ENCODE_RELAX_STATE (COND_JUMP, SMALL16):
+    case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL):
+    case ENCODE_RELAX_STATE (UNCOND_JUMP, SMALL16):
       /* don't have to change opcode */
       extension = 1;           /* 1 opcode + 1 displacement */
       where_to_put_displacement = &opcode[1];
       break;
 
-    case ENCODE_RELAX_STATE (COND_JUMP, WORD):
-      opcode[1] = TWO_BYTE_OPCODE_ESCAPE;
-      opcode[2] = opcode[0] + 0x10;
-      opcode[0] = DATA_PREFIX_OPCODE;
-      extension = 4;           /* 3 opcode + 2 displacement */
-      where_to_put_displacement = &opcode[3];
+    case ENCODE_RELAX_STATE (COND_JUMP, BIG):
+      extension = 5;           /* 2 opcode + 4 displacement */
+      opcode[1] = opcode[0] + 0x10;
+      opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+      where_to_put_displacement = &opcode[2];
       break;
 
-    case ENCODE_RELAX_STATE (UNCOND_JUMP, WORD):
-      opcode[1] = 0xe9;
-      opcode[0] = DATA_PREFIX_OPCODE;
-      extension = 3;           /* 2 opcode + 2 displacement */
-      where_to_put_displacement = &opcode[2];
+    case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG):
+      extension = 4;           /* 1 opcode + 4 displacement */
+      opcode[0] = 0xe9;
+      where_to_put_displacement = &opcode[1];
       break;
 
-    case ENCODE_RELAX_STATE (COND_JUMP, DWORD):
+    case ENCODE_RELAX_STATE (COND_JUMP, BIG16):
+      extension = 3;           /* 2 opcode + 2 displacement */
       opcode[1] = opcode[0] + 0x10;
       opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
-      extension = 5;           /* 2 opcode + 4 displacement */
       where_to_put_displacement = &opcode[2];
       break;
 
-    case ENCODE_RELAX_STATE (UNCOND_JUMP, DWORD):
+    case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16):
+      extension = 2;           /* 1 opcode + 2 displacement */
       opcode[0] = 0xe9;
-      extension = 4;           /* 1 opcode + 4 displacement */
       where_to_put_displacement = &opcode[1];
       break;
 
@@ -2903,7 +3042,7 @@ md_apply_fix3 (fixP, valp, seg)
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
       && fixP->fx_addsy)
-    switch(fixP->fx_r_type) {
+    switch (fixP->fx_r_type) {
     case BFD_RELOC_386_PLT32:
       /* Make the jump instruction point to the address of the operand.  At
         runtime we merely add the offset to the actual PLT entry. */
@@ -3051,23 +3190,22 @@ output_invalid (c)
 }
 
 /* reg_string starts *before* REGISTER_PREFIX */
-static reg_entry *
+static const reg_entry *
 parse_register (reg_string)
-     char *reg_string;
+     const char *reg_string;
 {
-  register char *s = reg_string;
+  register const char *s = reg_string;
   register char *p;
   char reg_name_given[MAX_REG_NAME_SIZE];
 
   s++;                         /* skip REGISTER_PREFIX */
-  for (p = reg_name_given; is_register_char (*s); p++, s++)
+  p = reg_name_given;
+  while ((*p++ = register_chars[(unsigned char) *s++]) != '\0')
     {
-      *p = register_chars[(unsigned char) *s];
       if (p >= reg_name_given + MAX_REG_NAME_SIZE)
-       return (reg_entry *) 0;
+       return (const reg_entry *) NULL;
     }
-  *p = '\0';
-  return (reg_entry *) hash_find (reg_hash, reg_name_given);
+  return (const reg_entry *) hash_find (reg_hash, reg_name_given);
 }
 \f
 #ifdef OBJ_ELF
@@ -3078,7 +3216,7 @@ CONST char *md_shortopts = "m";
 struct option md_longopts[] = {
   {NULL, no_argument, NULL, 0}
 };
-size_t md_longopts_size = sizeof(md_longopts);
+size_t md_longopts_size = sizeof (md_longopts);
 
 int
 md_parse_option (c, arg)
@@ -3154,11 +3292,11 @@ md_undefined_symbol (name)
        if (*name == '_' && *(name+1) == 'G'
            && strcmp(name, GLOBAL_OFFSET_TABLE_NAME) == 0)
          {
-           if(!GOT_symbol)
+           if (!GOT_symbol)
              {
-               if(symbol_find(name)) 
+               if (symbol_find (name))
                  as_bad (_("GOT already in symbol table"));
-               GOT_symbol = symbol_new (name, undefined_section, 
+               GOT_symbol = symbol_new (name, undefined_section,
                                         (valueT) 0, &zero_address_frag);
              };
            return GOT_symbol;
index 34c8bb7..11a4d32 100644 (file)
@@ -179,7 +179,6 @@ extern int tc_coff_sizemachdep PARAMS ((fragS *frag));
 #define REGISTER_PREFIX '%'
 #define IMMEDIATE_PREFIX '$'
 #define ABSOLUTE_PREFIX '*'
-#define PREFIX_SEPERATOR '/'
 
 #define TWO_BYTE_OPCODE_ESCAPE 0x0f
 #define NOP_OPCODE (char) 0x90
@@ -194,6 +193,7 @@ extern int tc_coff_sizemachdep PARAMS ((fragS *frag));
 #define NO_INDEX_REGISTER ESP_REG_NUM
 /* index_base_byte.base for no base register addressing */
 #define NO_BASE_REGISTER EBP_REG_NUM
+#define NO_BASE_REGISTER_16 6
 
 /* these are the opcode suffixes, making movl --> mov, for example */
 #define DWORD_OPCODE_SUFFIX 'l'
@@ -322,6 +322,7 @@ typedef struct
 #define FWait        0x100000  /* instruction needs FWAIT */
 #define IsString      0x200000 /* quick test for string instructions */
 #define regKludge     0x400000 /* fake an extra reg operand for clr, imul */
+#define IsPrefix      0x800000 /* opcode is a prefix */
 #define Ugh        0x80000000  /* deprecated fp insn, gets a warning */
 
   /* operand_types[i] describes the type of operand i.  This is made
@@ -341,8 +342,8 @@ template;
   */
 typedef struct
   {
-    template *start;
-    template *end;
+    const template *start;
+    const template *end;
   } templates;
 
 /* these are for register name --> number & type hash lookup */
@@ -352,7 +353,6 @@ typedef struct
     unsigned int reg_type;
     unsigned int reg_num;
   }
-
 reg_entry;
 
 typedef struct
@@ -360,37 +360,25 @@ typedef struct
     char *seg_name;
     unsigned int seg_prefix;
   }
-
 seg_entry;
 
-/* these are for prefix name --> prefix code hash lookup */
-typedef struct
-  {
-    char *prefix_name;
-    unsigned char prefix_code;
-  }
-
-prefix_entry;
-
 /* 386 operand encoding bytes:  see 386 book for details of this. */
 typedef struct
   {
-    unsigned regmem:3;         /* codes register or memory operand */
-    unsigned reg:3;            /* codes register operand (or extended opcode) */
-    unsigned mode:2;           /* how to interpret regmem & reg */
+    unsigned int regmem;       /* codes register or memory operand */
+    unsigned int reg;          /* codes register operand (or extended opcode) */
+    unsigned int mode;         /* how to interpret regmem & reg */
   }
-
 modrm_byte;
 
 /* 386 opcode byte to code indirect addressing. */
 typedef struct
   {
-    unsigned base:3;
-    unsigned index:3;
-    unsigned scale:2;
+    unsigned base;
+    unsigned index;
+    unsigned scale;
   }
-
-base_index_byte;
+sib_byte;
 
 /* The name of the global offset table generated by the compiler. Allow
    this to be overridden if need be. */