2005-08-05 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / gas / config / tc-arm.c
index f78f10009806086f511991209ad01f9a6d9c81a1..6a587f3c9ce4a2fcb7449596fd60cef233c44123 100644 (file)
@@ -1,9 +1,12 @@
 /* tc-arm.c -- Assemble for the ARM
 /* tc-arm.c -- Assemble for the ARM
-   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005
    Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
        Modified by David Taylor (dtaylor@armltd.co.uk)
        Cirrus coprocessor mods by Aldy Hernandez (aldyh@redhat.com)
    Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
        Modified by David Taylor (dtaylor@armltd.co.uk)
        Cirrus coprocessor mods by Aldy Hernandez (aldyh@redhat.com)
+       Cirrus coprocessor fixes by Petko Manolov (petkan@nucleusys.com)
+       Cirrus coprocessor fixes by Vladimir Ivanov (vladitx@nucleusys.com)
 
    This file is part of GAS, the GNU Assembler.
 
 
    This file is part of GAS, the GNU Assembler.
 
 
    GAS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    GAS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with GAS; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA.  */
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 #include <string.h>
 
 #include <string.h>
-#define  NO_RELOC 0
+#define         NO_RELOC 0
 #include "as.h"
 #include "safe-ctype.h"
 
 #include "as.h"
 #include "safe-ctype.h"
 
 #include "symbols.h"
 #include "listing.h"
 
 #include "symbols.h"
 #include "listing.h"
 
+#include "opcode/arm.h"
+
 #ifdef OBJ_ELF
 #include "elf/arm.h"
 #include "dwarf2dbg.h"
 #ifdef OBJ_ELF
 #include "elf/arm.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 #endif
 
 #endif
 
-/* XXX Set this to 1 after the next binutils release */
+/* XXX Set this to 1 after the next binutils release */
 #define WARN_DEPRECATED 0
 
 #define WARN_DEPRECATED 0
 
-/* The following bitmasks control CPU extensions:  */
-#define ARM_EXT_V1      0x00000001     /* All processors (core set).  */
-#define ARM_EXT_V2      0x00000002     /* Multiply instructions.  */
-#define ARM_EXT_V2S     0x00000004     /* SWP instructions.       */
-#define ARM_EXT_V3      0x00000008     /* MSR MRS.                */
-#define ARM_EXT_V3M     0x00000010     /* Allow long multiplies.  */
-#define ARM_EXT_V4      0x00000020     /* Allow half word loads.  */
-#define ARM_EXT_V4T     0x00000040     /* Thumb v1.               */
-#define ARM_EXT_V5      0x00000080     /* Allow CLZ, etc.         */
-#define ARM_EXT_V5T     0x00000100     /* Thumb v2.               */
-#define ARM_EXT_V5ExP   0x00000200     /* DSP core set.           */
-#define ARM_EXT_V5E     0x00000400     /* DSP Double transfers.   */
-#define ARM_EXT_V5J     0x00000800     /* Jazelle extension.      */
-
-/* Co-processor space extensions.  */
-#define ARM_CEXT_XSCALE   0x00800000   /* Allow MIA etc.          */
-#define ARM_CEXT_MAVERICK 0x00400000   /* Use Cirrus/DSP coprocessor.  */
-
-/* Architectures are the sum of the base and extensions.  The ARM ARM (rev E)
-   defines the following: ARMv3, ARMv3M, ARMv4xM, ARMv4, ARMv4TxM, ARMv4T,
-   ARMv5xM, ARMv5, ARMv5TxM, ARMv5T, ARMv5TExP, ARMv5TE.  To these we add
-   three more to cover cores prior to ARM6.  Finally, there are cores which
-   implement further extensions in the co-processor space.  */
-#define ARM_ARCH_V1                      ARM_EXT_V1
-#define ARM_ARCH_V2    (ARM_ARCH_V1    | ARM_EXT_V2)
-#define ARM_ARCH_V2S   (ARM_ARCH_V2    | ARM_EXT_V2S)
-#define ARM_ARCH_V3    (ARM_ARCH_V2S   | ARM_EXT_V3)
-#define ARM_ARCH_V3M   (ARM_ARCH_V3    | ARM_EXT_V3M)
-#define ARM_ARCH_V4xM  (ARM_ARCH_V3    | ARM_EXT_V4)
-#define ARM_ARCH_V4    (ARM_ARCH_V3M   | ARM_EXT_V4)
-#define ARM_ARCH_V4TxM (ARM_ARCH_V4xM  | ARM_EXT_V4T)
-#define ARM_ARCH_V4T   (ARM_ARCH_V4    | ARM_EXT_V4T)
-#define ARM_ARCH_V5xM  (ARM_ARCH_V4xM  | ARM_EXT_V5)
-#define ARM_ARCH_V5    (ARM_ARCH_V4    | ARM_EXT_V5)
-#define ARM_ARCH_V5TxM (ARM_ARCH_V5xM  | ARM_EXT_V4T | ARM_EXT_V5T)
-#define ARM_ARCH_V5T   (ARM_ARCH_V5    | ARM_EXT_V4T | ARM_EXT_V5T)
-#define ARM_ARCH_V5TExP        (ARM_ARCH_V5T   | ARM_EXT_V5ExP)
-#define ARM_ARCH_V5TE  (ARM_ARCH_V5TExP | ARM_EXT_V5E)
-#define ARM_ARCH_V5TEJ (ARM_ARCH_V5TE  | ARM_EXT_V5J)
-
-/* Processors with specific extensions in the co-processor space.  */
-#define ARM_ARCH_XSCALE        (ARM_ARCH_V5TE  | ARM_CEXT_XSCALE)
-
-/* Some useful combinations:  */
-#define ARM_ANY                0x0000ffff      /* Any basic core.  */
-#define ARM_ALL                0x00ffffff      /* Any core + co-processor */
-#define CPROC_ANY      0x00ff0000      /* Any co-processor */
-#define FPU_ANY                0xff000000      /* Note this is ~ARM_ALL.  */
-
-
-#define FPU_FPA_EXT_V1  0x80000000     /* Base FPA instruction set.  */
-#define FPU_FPA_EXT_V2  0x40000000     /* LFM/SFM.                   */
-#define FPU_VFP_EXT_NONE 0x20000000    /* Use VFP word-ordering.     */
-#define FPU_VFP_EXT_V1xD 0x10000000    /* Base VFP instruction set.  */
-#define FPU_VFP_EXT_V1  0x08000000     /* Double-precision insns.    */
-#define FPU_VFP_EXT_V2  0x04000000     /* ARM10E VFPr1.              */
-#define FPU_NONE        0
-
-#define FPU_ARCH_FPE    FPU_FPA_EXT_V1
-#define FPU_ARCH_FPA   (FPU_ARCH_FPE | FPU_FPA_EXT_V2)
-
-#define FPU_ARCH_VFP       FPU_VFP_EXT_NONE
-#define FPU_ARCH_VFP_V1xD (FPU_VFP_EXT_V1xD | FPU_VFP_EXT_NONE)
-#define FPU_ARCH_VFP_V1   (FPU_ARCH_VFP_V1xD | FPU_VFP_EXT_V1)
-#define FPU_ARCH_VFP_V2          (FPU_ARCH_VFP_V1 | FPU_VFP_EXT_V2)
-
-/* Types of processor to assemble for.  */
+#ifdef OBJ_ELF
+/* Must be at least the size of the largest unwind opcode (currently two).  */
+#define ARM_OPCODE_CHUNK_SIZE 8
+
+/* This structure holds the unwinding state.  */
+
+static struct
+{
+  symbolS *      proc_start;
+  symbolS *      table_entry;
+  symbolS *      personality_routine;
+  int            personality_index;
+  /* The segment containing the function.  */
+  segT           saved_seg;
+  subsegT        saved_subseg;
+  /* Opcodes generated from this function.  */
+  unsigned char * opcodes;
+  int            opcode_count;
+  int            opcode_alloc;
+  /* The number of bytes pushed to the stack.  */
+  offsetT        frame_size;
+  /* We don't add stack adjustment opcodes immediately so that we can merge
+     multiple adjustments.  We can also omit the final adjustment
+     when using a frame pointer.  */
+  offsetT        pending_offset;
+  /* These two fields are set by both unwind_movsp and unwind_setfp.  They
+     hold the reg+offset to use when restoring sp from a frame pointer.         */
+  offsetT        fp_offset;
+  int            fp_reg;
+  /* Nonzero if an unwind_setfp directive has been seen.  */
+  unsigned       fp_used:1;
+  /* Nonzero if the last opcode restores sp from fp_reg.  */
+  unsigned       sp_restored:1;
+} unwind;
+
+/* Bit N indicates that an R_ARM_NONE relocation has been output for
+   __aeabi_unwind_cpp_prN already if set. This enables dependencies to be
+   emitted only once per section, to save unnecessary bloat.  */
+static unsigned int marked_pr_dependency = 0;
+
+#endif /* OBJ_ELF */
+
+enum arm_float_abi
+{
+  ARM_FLOAT_ABI_HARD,
+  ARM_FLOAT_ABI_SOFTFP,
+  ARM_FLOAT_ABI_SOFT
+};
+
+/* Types of processor to assemble for. */
 #define ARM_1          ARM_ARCH_V1
 #define ARM_2          ARM_ARCH_V2
 #define ARM_3          ARM_ARCH_V2S
 #define ARM_1          ARM_ARCH_V1
 #define ARM_2          ARM_ARCH_V2
 #define ARM_3          ARM_ARCH_V2S
 #define ARM_8          ARM_ARCH_V4
 #define ARM_9          ARM_ARCH_V4T
 #define ARM_STRONG     ARM_ARCH_V4
 #define ARM_8          ARM_ARCH_V4
 #define ARM_9          ARM_ARCH_V4T
 #define ARM_STRONG     ARM_ARCH_V4
-#define ARM_CPU_MASK   0x0000000f              /* XXX? */
+#define ARM_CPU_MASK   0x0000000f              /* XXX? */
 
 #ifndef CPU_DEFAULT
 #if defined __XSCALE__
 #define CPU_DEFAULT    (ARM_ARCH_XSCALE)
 #else
 #if defined __thumb__
 
 #ifndef CPU_DEFAULT
 #if defined __XSCALE__
 #define CPU_DEFAULT    (ARM_ARCH_XSCALE)
 #else
 #if defined __thumb__
-#define CPU_DEFAULT    (ARM_ARCH_V5T)
+#define CPU_DEFAULT    (ARM_ARCH_V5T)
 #else
 #else
-#define CPU_DEFAULT    ARM_ANY
+#define CPU_DEFAULT    ARM_ANY
 #endif
 #endif
 #endif
 
 #endif
 #endif
 #endif
 
-/* For backwards compatibility we default to the FPA.  */
 #ifndef FPU_DEFAULT
 #ifndef FPU_DEFAULT
-#define FPU_DEFAULT FPU_ARCH_FPA
-#endif
-
-#define streq(a, b)           (strcmp (a, b) == 0)
-#define skip_whitespace(str)  while (*(str) == ' ') ++(str)
+# ifdef TE_LINUX
+#  define FPU_DEFAULT FPU_ARCH_FPA
+# elif defined (TE_NetBSD)
+#  ifdef OBJ_ELF
+#   define FPU_DEFAULT FPU_ARCH_VFP    /* Soft-float, but VFP order.  */
+#  else
+    /* Legacy a.out format.  */
+#   define FPU_DEFAULT FPU_ARCH_FPA    /* Soft-float, but FPA order.  */
+#  endif
+# elif defined (TE_VXWORKS)
+#  define FPU_DEFAULT FPU_ARCH_VFP     /* Soft-float, VFP order.  */
+# else
+   /* For backwards compatibility, default to FPA.  */
+#  define FPU_DEFAULT FPU_ARCH_FPA
+# endif
+#endif /* ifndef FPU_DEFAULT */
+
+#define streq(a, b)          (strcmp (a, b) == 0)
 
 static unsigned long cpu_variant;
 
 static unsigned long cpu_variant;
-static int target_oabi = 0;
 
 /* Flags stored in private area of BFD structure.  */
 
 /* Flags stored in private area of BFD structure.  */
-static int uses_apcs_26      = FALSE;
-static int atpcs             = FALSE;
+static int uses_apcs_26             = FALSE;
+static int atpcs            = FALSE;
 static int support_interwork = FALSE;
 static int uses_apcs_float   = FALSE;
 static int support_interwork = FALSE;
 static int uses_apcs_float   = FALSE;
-static int pic_code          = FALSE;
+static int pic_code         = FALSE;
 
 /* Variables that we set while parsing command-line options.  Once all
    options have been read we re-process these values to set the real
 
 /* Variables that we set while parsing command-line options.  Once all
    options have been read we re-process these values to set the real
@@ -161,38 +161,17 @@ static int mcpu_fpu_opt = -1;
 static int march_cpu_opt = -1;
 static int march_fpu_opt = -1;
 static int mfpu_opt = -1;
 static int march_cpu_opt = -1;
 static int march_fpu_opt = -1;
 static int mfpu_opt = -1;
-
-/* This array holds the chars that always start a comment.  If the
-   pre-processor is disabled, these aren't very useful.  */
-const char comment_chars[] = "@";
-
-/* This array holds the chars that only start a comment at the beginning of
-   a line.  If the line seems to have the form '# 123 filename'
-   .line and .file directives will appear in the pre-processed output.  */
-/* Note that input_file.c hand checks for '#' at the beginning of the
-   first line of the input file.  This is because the compiler outputs
-   #NO_APP at the beginning of its output.  */
-/* Also note that comments like this one will always work.  */
-const char line_comment_chars[] = "#";
-
-const char line_separator_chars[] = ";";
-
-/* Chars that can be used to separate mant
-   from exp in floating point numbers.  */
-const char EXP_CHARS[] = "eE";
-
-/* Chars that mean this number is a floating point constant.  */
-/* As in 0f12.456  */
-/* or    0d1.2345e12  */
-
-const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
-
-/* Prefix characters that indicate the start of an immediate
-   value.  */
-#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
+static int mfloat_abi_opt = -1;
+#ifdef OBJ_ELF
+# ifdef EABI_DEFAULT
+static int meabi_flags = EABI_DEFAULT;
+# else
+static int meabi_flags = EF_ARM_EABI_UNKNOWN;
+# endif
+#endif
 
 #ifdef OBJ_ELF
 
 #ifdef OBJ_ELF
-/* Pre-defined "_GLOBAL_OFFSET_TABLE_"  */
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
 symbolS * GOT_symbol;
 #endif
 
 symbolS * GOT_symbol;
 #endif
 
@@ -205,76 +184,63 @@ const int md_reloc_size = 8;
       instructions.  */
 static int thumb_mode = 0;
 
       instructions.  */
 static int thumb_mode = 0;
 
-typedef struct arm_fix
-{
-  int thumb_mode;
-} arm_fix_data;
+/* If unified_syntax is true, we are processing the new unified
+   ARM/Thumb syntax.  Important differences from the old ARM mode:
+
+     - Immediate operands do not require a # prefix.
+     - Conditional affixes always appear at the end of the
+       instruction.  (For backward compatibility, those instructions
+       that formerly had them in the middle, continue to accept them
+       there.)
+     - The IT instruction may appear, and if it does is validated
+       against subsequent conditional affixes.  It does not generate
+       machine code.
+
+   Important differences from the old Thumb mode:
+
+     - Immediate operands do not require a # prefix.
+     - Most of the V6T2 instructions are only available in unified mode.
+     - The .N and .W suffixes are recognized and honored (it is an error
+       if they cannot be honored).
+     - All instructions set the flags if and only if they have an 's' affix.
+     - Conditional affixes may be used.  They are validated against
+       preceding IT instructions.  Unlike ARM mode, you cannot use a
+       conditional affix except in the scope of an IT instruction.  */
+
+static bfd_boolean unified_syntax = FALSE;
 
 struct arm_it
 {
 
 struct arm_it
 {
-  const char *  error;
+  const char * error;
   unsigned long instruction;
   unsigned long instruction;
-  int           size;
+  int          size;
+  int          size_req;
+  int          cond;
   struct
   {
     bfd_reloc_code_real_type type;
   struct
   {
     bfd_reloc_code_real_type type;
-    expressionS              exp;
-    int                      pc_rel;
+    expressionS                     exp;
+    int                             pc_rel;
   } reloc;
   } reloc;
-};
-
-struct arm_it inst;
-
-enum asm_shift_index
-{
-  SHIFT_LSL = 0,
-  SHIFT_LSR,
-  SHIFT_ASR,
-  SHIFT_ROR,
-  SHIFT_RRX
-};
-
-struct asm_shift_properties
-{
-  enum asm_shift_index index;
-  unsigned long        bit_field;
-  unsigned int         allows_0  : 1;
-  unsigned int         allows_32 : 1;
-};
-
-static const struct asm_shift_properties shift_properties [] =
-{
-  { SHIFT_LSL, 0,    1, 0},
-  { SHIFT_LSR, 0x20, 0, 1},
-  { SHIFT_ASR, 0x40, 0, 1},
-  { SHIFT_ROR, 0x60, 0, 0},
-  { SHIFT_RRX, 0x60, 0, 0}
-};
-
-struct asm_shift_name
-{
-  const char *                        name;
-  const struct asm_shift_properties * properties;
-};
 
 
-static const struct asm_shift_name shift_names [] =
-{
-  { "asl", shift_properties + SHIFT_LSL },
-  { "lsl", shift_properties + SHIFT_LSL },
-  { "lsr", shift_properties + SHIFT_LSR },
-  { "asr", shift_properties + SHIFT_ASR },
-  { "ror", shift_properties + SHIFT_ROR },
-  { "rrx", shift_properties + SHIFT_RRX },
-  { "ASL", shift_properties + SHIFT_LSL },
-  { "LSL", shift_properties + SHIFT_LSL },
-  { "LSR", shift_properties + SHIFT_LSR },
-  { "ASR", shift_properties + SHIFT_ASR },
-  { "ROR", shift_properties + SHIFT_ROR },
-  { "RRX", shift_properties + SHIFT_RRX }
+  struct
+  {
+    unsigned reg;
+    signed int imm;
+    unsigned present   : 1;  /* Operand present.  */
+    unsigned isreg     : 1;  /* Operand was a register.  */
+    unsigned immisreg  : 1;  /* .imm field is a second register.  */
+    unsigned hasreloc  : 1;  /* Operand has relocation suffix.  */
+    unsigned writeback : 1;  /* Operand has trailing !  */
+    unsigned preind    : 1;  /* Preindexed address.  */
+    unsigned postind   : 1;  /* Postindexed address.  */
+    unsigned negative  : 1;  /* Index register was negated.  */
+    unsigned shifted   : 1;  /* Shift applied to operation.  */
+    unsigned shift_kind : 3;  /* Shift operation (enum shift_kind).  */
+  } operands[6];
 };
 
 };
 
-#define NO_SHIFT_RESTRICT 1
-#define SHIFT_RESTRICT   0
+static struct arm_it inst;
 
 #define NUM_FLOAT_VALS 8
 
 
 #define NUM_FLOAT_VALS 8
 
@@ -283,7 +249,7 @@ const char * fp_const[] =
   "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
 };
 
   "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
 };
 
-/* Number of littlenums required to hold an extended precision number.  */
+/* Number of littlenums required to hold an extended precision number. */
 #define MAX_LITTLENUMS 6
 
 LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
 #define MAX_LITTLENUMS 6
 
 LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
@@ -291,217 +257,46 @@ LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
 #define FAIL   (-1)
 #define SUCCESS (0)
 
 #define FAIL   (-1)
 #define SUCCESS (0)
 
-/* Whether a Co-processor load/store operation accepts write-back forms.  */
-#define CP_WB_OK 1
-#define CP_NO_WB 0
-
 #define SUFF_S 1
 #define SUFF_D 2
 #define SUFF_E 3
 #define SUFF_P 4
 
 #define SUFF_S 1
 #define SUFF_D 2
 #define SUFF_E 3
 #define SUFF_P 4
 
-#define CP_T_X   0x00008000
-#define CP_T_Y   0x00400000
-#define CP_T_Pre 0x01000000
-#define CP_T_UD  0x00800000
-#define CP_T_WB  0x00200000
+#define CP_T_X  0x00008000
+#define CP_T_Y  0x00400000
 
 
-#define CONDS_BIT        0x00100000
-#define LOAD_BIT         0x00100000
+#define CONDS_BIT       0x00100000
+#define LOAD_BIT        0x00100000
 
 #define DOUBLE_LOAD_FLAG 0x00000001
 
 struct asm_cond
 {
 
 #define DOUBLE_LOAD_FLAG 0x00000001
 
 struct asm_cond
 {
-  const char *  template;
+  const char * template;
   unsigned long value;
 };
 
   unsigned long value;
 };
 
-#define COND_ALWAYS 0xe0000000
-#define COND_MASK   0xf0000000
-
-static const struct asm_cond conds[] =
-{
-  {"eq", 0x00000000},
-  {"ne", 0x10000000},
-  {"cs", 0x20000000}, {"hs", 0x20000000},
-  {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000},
-  {"mi", 0x40000000},
-  {"pl", 0x50000000},
-  {"vs", 0x60000000},
-  {"vc", 0x70000000},
-  {"hi", 0x80000000},
-  {"ls", 0x90000000},
-  {"ge", 0xa0000000},
-  {"lt", 0xb0000000},
-  {"gt", 0xc0000000},
-  {"le", 0xd0000000},
-  {"al", 0xe0000000},
-  {"nv", 0xf0000000}
-};
+#define COND_ALWAYS 0xE
 
 struct asm_psr
 {
   const char *template;
 
 struct asm_psr
 {
   const char *template;
-  bfd_boolean cpsr;
   unsigned long field;
 };
 
   unsigned long field;
 };
 
-/* The bit that distnguishes CPSR and SPSR.  */
+/* The bit that distinguishes CPSR and SPSR.  */
 #define SPSR_BIT   (1 << 22)
 
 #define SPSR_BIT   (1 << 22)
 
-/* How many bits to shift the PSR_xxx bits up by.  */
-#define PSR_SHIFT  16
-
-#define PSR_c   (1 << 0)
-#define PSR_x   (1 << 1)
-#define PSR_s   (1 << 2)
-#define PSR_f   (1 << 3)
-
-static const struct asm_psr psrs[] =
-{
-  {"CPSR",     TRUE,  PSR_c | PSR_f},
-  {"CPSR_all", TRUE,  PSR_c | PSR_f},
-  {"SPSR",     FALSE, PSR_c | PSR_f},
-  {"SPSR_all", FALSE, PSR_c | PSR_f},
-  {"CPSR_flg", TRUE,  PSR_f},
-  {"CPSR_f",    TRUE,  PSR_f},
-  {"SPSR_flg", FALSE, PSR_f},
-  {"SPSR_f",    FALSE, PSR_f},
-  {"CPSR_c",   TRUE,  PSR_c},
-  {"CPSR_ctl", TRUE,  PSR_c},
-  {"SPSR_c",   FALSE, PSR_c},
-  {"SPSR_ctl", FALSE, PSR_c},
-  {"CPSR_x",    TRUE,  PSR_x},
-  {"CPSR_s",    TRUE,  PSR_s},
-  {"SPSR_x",    FALSE, PSR_x},
-  {"SPSR_s",    FALSE, PSR_s},
-  /* Combinations of flags.  */
-  {"CPSR_fs",  TRUE, PSR_f | PSR_s},
-  {"CPSR_fx",  TRUE, PSR_f | PSR_x},
-  {"CPSR_fc",  TRUE, PSR_f | PSR_c},
-  {"CPSR_sf",  TRUE, PSR_s | PSR_f},
-  {"CPSR_sx",  TRUE, PSR_s | PSR_x},
-  {"CPSR_sc",  TRUE, PSR_s | PSR_c},
-  {"CPSR_xf",  TRUE, PSR_x | PSR_f},
-  {"CPSR_xs",  TRUE, PSR_x | PSR_s},
-  {"CPSR_xc",  TRUE, PSR_x | PSR_c},
-  {"CPSR_cf",  TRUE, PSR_c | PSR_f},
-  {"CPSR_cs",  TRUE, PSR_c | PSR_s},
-  {"CPSR_cx",  TRUE, PSR_c | PSR_x},
-  {"CPSR_fsx", TRUE, PSR_f | PSR_s | PSR_x},
-  {"CPSR_fsc", TRUE, PSR_f | PSR_s | PSR_c},
-  {"CPSR_fxs", TRUE, PSR_f | PSR_x | PSR_s},
-  {"CPSR_fxc", TRUE, PSR_f | PSR_x | PSR_c},
-  {"CPSR_fcs", TRUE, PSR_f | PSR_c | PSR_s},
-  {"CPSR_fcx", TRUE, PSR_f | PSR_c | PSR_x},
-  {"CPSR_sfx", TRUE, PSR_s | PSR_f | PSR_x},
-  {"CPSR_sfc", TRUE, PSR_s | PSR_f | PSR_c},
-  {"CPSR_sxf", TRUE, PSR_s | PSR_x | PSR_f},
-  {"CPSR_sxc", TRUE, PSR_s | PSR_x | PSR_c},
-  {"CPSR_scf", TRUE, PSR_s | PSR_c | PSR_f},
-  {"CPSR_scx", TRUE, PSR_s | PSR_c | PSR_x},
-  {"CPSR_xfs", TRUE, PSR_x | PSR_f | PSR_s},
-  {"CPSR_xfc", TRUE, PSR_x | PSR_f | PSR_c},
-  {"CPSR_xsf", TRUE, PSR_x | PSR_s | PSR_f},
-  {"CPSR_xsc", TRUE, PSR_x | PSR_s | PSR_c},
-  {"CPSR_xcf", TRUE, PSR_x | PSR_c | PSR_f},
-  {"CPSR_xcs", TRUE, PSR_x | PSR_c | PSR_s},
-  {"CPSR_cfs", TRUE, PSR_c | PSR_f | PSR_s},
-  {"CPSR_cfx", TRUE, PSR_c | PSR_f | PSR_x},
-  {"CPSR_csf", TRUE, PSR_c | PSR_s | PSR_f},
-  {"CPSR_csx", TRUE, PSR_c | PSR_s | PSR_x},
-  {"CPSR_cxf", TRUE, PSR_c | PSR_x | PSR_f},
-  {"CPSR_cxs", TRUE, PSR_c | PSR_x | PSR_s},
-  {"CPSR_fsxc",        TRUE, PSR_f | PSR_s | PSR_x | PSR_c},
-  {"CPSR_fscx",        TRUE, PSR_f | PSR_s | PSR_c | PSR_x},
-  {"CPSR_fxsc",        TRUE, PSR_f | PSR_x | PSR_s | PSR_c},
-  {"CPSR_fxcs",        TRUE, PSR_f | PSR_x | PSR_c | PSR_s},
-  {"CPSR_fcsx",        TRUE, PSR_f | PSR_c | PSR_s | PSR_x},
-  {"CPSR_fcxs",        TRUE, PSR_f | PSR_c | PSR_x | PSR_s},
-  {"CPSR_sfxc",        TRUE, PSR_s | PSR_f | PSR_x | PSR_c},
-  {"CPSR_sfcx",        TRUE, PSR_s | PSR_f | PSR_c | PSR_x},
-  {"CPSR_sxfc",        TRUE, PSR_s | PSR_x | PSR_f | PSR_c},
-  {"CPSR_sxcf",        TRUE, PSR_s | PSR_x | PSR_c | PSR_f},
-  {"CPSR_scfx",        TRUE, PSR_s | PSR_c | PSR_f | PSR_x},
-  {"CPSR_scxf",        TRUE, PSR_s | PSR_c | PSR_x | PSR_f},
-  {"CPSR_xfsc",        TRUE, PSR_x | PSR_f | PSR_s | PSR_c},
-  {"CPSR_xfcs",        TRUE, PSR_x | PSR_f | PSR_c | PSR_s},
-  {"CPSR_xsfc",        TRUE, PSR_x | PSR_s | PSR_f | PSR_c},
-  {"CPSR_xscf",        TRUE, PSR_x | PSR_s | PSR_c | PSR_f},
-  {"CPSR_xcfs",        TRUE, PSR_x | PSR_c | PSR_f | PSR_s},
-  {"CPSR_xcsf",        TRUE, PSR_x | PSR_c | PSR_s | PSR_f},
-  {"CPSR_cfsx",        TRUE, PSR_c | PSR_f | PSR_s | PSR_x},
-  {"CPSR_cfxs",        TRUE, PSR_c | PSR_f | PSR_x | PSR_s},
-  {"CPSR_csfx",        TRUE, PSR_c | PSR_s | PSR_f | PSR_x},
-  {"CPSR_csxf",        TRUE, PSR_c | PSR_s | PSR_x | PSR_f},
-  {"CPSR_cxfs",        TRUE, PSR_c | PSR_x | PSR_f | PSR_s},
-  {"CPSR_cxsf",        TRUE, PSR_c | PSR_x | PSR_s | PSR_f},
-  {"SPSR_fs",  FALSE, PSR_f | PSR_s},
-  {"SPSR_fx",  FALSE, PSR_f | PSR_x},
-  {"SPSR_fc",  FALSE, PSR_f | PSR_c},
-  {"SPSR_sf",  FALSE, PSR_s | PSR_f},
-  {"SPSR_sx",  FALSE, PSR_s | PSR_x},
-  {"SPSR_sc",  FALSE, PSR_s | PSR_c},
-  {"SPSR_xf",  FALSE, PSR_x | PSR_f},
-  {"SPSR_xs",  FALSE, PSR_x | PSR_s},
-  {"SPSR_xc",  FALSE, PSR_x | PSR_c},
-  {"SPSR_cf",  FALSE, PSR_c | PSR_f},
-  {"SPSR_cs",  FALSE, PSR_c | PSR_s},
-  {"SPSR_cx",  FALSE, PSR_c | PSR_x},
-  {"SPSR_fsx", FALSE, PSR_f | PSR_s | PSR_x},
-  {"SPSR_fsc", FALSE, PSR_f | PSR_s | PSR_c},
-  {"SPSR_fxs", FALSE, PSR_f | PSR_x | PSR_s},
-  {"SPSR_fxc", FALSE, PSR_f | PSR_x | PSR_c},
-  {"SPSR_fcs", FALSE, PSR_f | PSR_c | PSR_s},
-  {"SPSR_fcx", FALSE, PSR_f | PSR_c | PSR_x},
-  {"SPSR_sfx", FALSE, PSR_s | PSR_f | PSR_x},
-  {"SPSR_sfc", FALSE, PSR_s | PSR_f | PSR_c},
-  {"SPSR_sxf", FALSE, PSR_s | PSR_x | PSR_f},
-  {"SPSR_sxc", FALSE, PSR_s | PSR_x | PSR_c},
-  {"SPSR_scf", FALSE, PSR_s | PSR_c | PSR_f},
-  {"SPSR_scx", FALSE, PSR_s | PSR_c | PSR_x},
-  {"SPSR_xfs", FALSE, PSR_x | PSR_f | PSR_s},
-  {"SPSR_xfc", FALSE, PSR_x | PSR_f | PSR_c},
-  {"SPSR_xsf", FALSE, PSR_x | PSR_s | PSR_f},
-  {"SPSR_xsc", FALSE, PSR_x | PSR_s | PSR_c},
-  {"SPSR_xcf", FALSE, PSR_x | PSR_c | PSR_f},
-  {"SPSR_xcs", FALSE, PSR_x | PSR_c | PSR_s},
-  {"SPSR_cfs", FALSE, PSR_c | PSR_f | PSR_s},
-  {"SPSR_cfx", FALSE, PSR_c | PSR_f | PSR_x},
-  {"SPSR_csf", FALSE, PSR_c | PSR_s | PSR_f},
-  {"SPSR_csx", FALSE, PSR_c | PSR_s | PSR_x},
-  {"SPSR_cxf", FALSE, PSR_c | PSR_x | PSR_f},
-  {"SPSR_cxs", FALSE, PSR_c | PSR_x | PSR_s},
-  {"SPSR_fsxc",        FALSE, PSR_f | PSR_s | PSR_x | PSR_c},
-  {"SPSR_fscx",        FALSE, PSR_f | PSR_s | PSR_c | PSR_x},
-  {"SPSR_fxsc",        FALSE, PSR_f | PSR_x | PSR_s | PSR_c},
-  {"SPSR_fxcs",        FALSE, PSR_f | PSR_x | PSR_c | PSR_s},
-  {"SPSR_fcsx",        FALSE, PSR_f | PSR_c | PSR_s | PSR_x},
-  {"SPSR_fcxs",        FALSE, PSR_f | PSR_c | PSR_x | PSR_s},
-  {"SPSR_sfxc",        FALSE, PSR_s | PSR_f | PSR_x | PSR_c},
-  {"SPSR_sfcx",        FALSE, PSR_s | PSR_f | PSR_c | PSR_x},
-  {"SPSR_sxfc",        FALSE, PSR_s | PSR_x | PSR_f | PSR_c},
-  {"SPSR_sxcf",        FALSE, PSR_s | PSR_x | PSR_c | PSR_f},
-  {"SPSR_scfx",        FALSE, PSR_s | PSR_c | PSR_f | PSR_x},
-  {"SPSR_scxf",        FALSE, PSR_s | PSR_c | PSR_x | PSR_f},
-  {"SPSR_xfsc",        FALSE, PSR_x | PSR_f | PSR_s | PSR_c},
-  {"SPSR_xfcs",        FALSE, PSR_x | PSR_f | PSR_c | PSR_s},
-  {"SPSR_xsfc",        FALSE, PSR_x | PSR_s | PSR_f | PSR_c},
-  {"SPSR_xscf",        FALSE, PSR_x | PSR_s | PSR_c | PSR_f},
-  {"SPSR_xcfs",        FALSE, PSR_x | PSR_c | PSR_f | PSR_s},
-  {"SPSR_xcsf",        FALSE, PSR_x | PSR_c | PSR_s | PSR_f},
-  {"SPSR_cfsx",        FALSE, PSR_c | PSR_f | PSR_s | PSR_x},
-  {"SPSR_cfxs",        FALSE, PSR_c | PSR_f | PSR_x | PSR_s},
-  {"SPSR_csfx",        FALSE, PSR_c | PSR_s | PSR_f | PSR_x},
-  {"SPSR_csxf",        FALSE, PSR_c | PSR_s | PSR_x | PSR_f},
-  {"SPSR_cxfs",        FALSE, PSR_c | PSR_x | PSR_f | PSR_s},
-  {"SPSR_cxsf",        FALSE, PSR_c | PSR_x | PSR_s | PSR_f},
-};
+/* The individual PSR flag bits.  */
+#define PSR_c  (1 << 16)
+#define PSR_x  (1 << 17)
+#define PSR_s  (1 << 18)
+#define PSR_f  (1 << 19)
 
 
-enum vfp_dp_reg_pos
+struct reloc_entry
 {
 {
-  VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn
+  char *name;
+  bfd_reloc_code_real_type reloc;
 };
 
 enum vfp_sp_reg_pos
 };
 
 enum vfp_sp_reg_pos
@@ -514,1303 +309,101 @@ enum vfp_ldstm_type
   VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
 };
 
   VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
 };
 
-/* VFP system registers.  */
-struct vfp_reg
-{
-  const char *name;
-  unsigned long regno;
-};
-
-static const struct vfp_reg vfp_regs[] =
+/* ARM register categories.  This includes coprocessor numbers and various
+   architecture extensions' registers. */
+enum arm_reg_type
 {
 {
-  {"fpsid", 0x00000000},
-  {"FPSID", 0x00000000},
-  {"fpscr", 0x00010000},
-  {"FPSCR", 0x00010000},
-  {"fpexc", 0x00080000},
-  {"FPEXC", 0x00080000}
+  REG_TYPE_RN,
+  REG_TYPE_CP,
+  REG_TYPE_CN,
+  REG_TYPE_FN,
+  REG_TYPE_VFS,
+  REG_TYPE_VFD,
+  REG_TYPE_VFC,
+  REG_TYPE_MVF,
+  REG_TYPE_MVD,
+  REG_TYPE_MVFX,
+  REG_TYPE_MVDX,
+  REG_TYPE_MVAX,
+  REG_TYPE_DSPSC,
+  REG_TYPE_MMXWR,
+  REG_TYPE_MMXWC,
+  REG_TYPE_MMXWCG,
+  REG_TYPE_XSCALE,
 };
 
 /* Structure for a hash table entry for a register.  */
 struct reg_entry
 {
 };
 
 /* Structure for a hash table entry for a register.  */
 struct reg_entry
 {
-  const char * name;
-  int          number;
-};
-
-/* Some well known registers that we refer to directly elsewhere.  */
-#define REG_SP  13
-#define REG_LR  14
-#define REG_PC 15
-
-/* These are the standard names.  Users can add aliases with .req.  */
-/* Integer Register Numbers.  */
-static const struct reg_entry rn_table[] =
-{
-  {"r0",  0},  {"r1",  1},      {"r2",  2},      {"r3",  3},
-  {"r4",  4},  {"r5",  5},      {"r6",  6},      {"r7",  7},
-  {"r8",  8},  {"r9",  9},      {"r10", 10},     {"r11", 11},
-  {"r12", 12}, {"r13", REG_SP}, {"r14", REG_LR}, {"r15", REG_PC},
-  /* ATPCS Synonyms.  */
-  {"a1",  0},  {"a2",  1},      {"a3",  2},      {"a4",  3},
-  {"v1",  4},  {"v2",  5},      {"v3",  6},      {"v4",  7},
-  {"v5",  8},  {"v6",  9},      {"v7",  10},     {"v8",  11},
-  /* Well-known aliases.  */
-                                                {"wr",  7},
-              {"sb",  9},      {"sl",  10},     {"fp",  11},
-  {"ip",  12}, {"sp",  REG_SP}, {"lr",  REG_LR}, {"pc",  REG_PC},
-  {NULL, 0}
-};
-
-/* Co-processor Numbers.  */
-static const struct reg_entry cp_table[] =
-{
-  {"p0",  0},  {"p1",  1},  {"p2",  2},  {"p3", 3},
-  {"p4",  4},  {"p5",  5},  {"p6",  6},  {"p7", 7},
-  {"p8",  8},  {"p9",  9},  {"p10", 10}, {"p11", 11},
-  {"p12", 12}, {"p13", 13}, {"p14", 14}, {"p15", 15},
-  {NULL, 0}
-};
-
-/* Co-processor Register Numbers.  */
-static const struct reg_entry cn_table[] =
-{
-  {"c0",   0},  {"c1",   1},  {"c2",   2},  {"c3",   3},
-  {"c4",   4},  {"c5",   5},  {"c6",   6},  {"c7",   7},
-  {"c8",   8},  {"c9",   9},  {"c10",  10}, {"c11",  11},
-  {"c12",  12}, {"c13",  13}, {"c14",  14}, {"c15",  15},
-  /* Not really valid, but kept for back-wards compatibility.  */
-  {"cr0",  0},  {"cr1",  1},  {"cr2",  2},  {"cr3",  3},
-  {"cr4",  4},  {"cr5",  5},  {"cr6",  6},  {"cr7",  7},
-  {"cr8",  8},  {"cr9",  9},  {"cr10", 10}, {"cr11", 11},
-  {"cr12", 12}, {"cr13", 13}, {"cr14", 14}, {"cr15", 15},
-  {NULL, 0}
-};
-
-/* FPA Registers.  */
-static const struct reg_entry fn_table[] =
-{
-  {"f0", 0},   {"f1", 1},   {"f2", 2},   {"f3", 3},
-  {"f4", 4},   {"f5", 5},   {"f6", 6},   {"f7", 7},
-  {NULL, 0}
-};
-
-/* VFP SP Registers.  */
-static const struct reg_entry sn_table[] =
-{
-  {"s0",  0},  {"s1",  1},  {"s2",  2},         {"s3", 3},
-  {"s4",  4},  {"s5",  5},  {"s6",  6},         {"s7", 7},
-  {"s8",  8},  {"s9",  9},  {"s10", 10}, {"s11", 11},
-  {"s12", 12}, {"s13", 13}, {"s14", 14}, {"s15", 15},
-  {"s16", 16}, {"s17", 17}, {"s18", 18}, {"s19", 19},
-  {"s20", 20}, {"s21", 21}, {"s22", 22}, {"s23", 23},
-  {"s24", 24}, {"s25", 25}, {"s26", 26}, {"s27", 27},
-  {"s28", 28}, {"s29", 29}, {"s30", 30}, {"s31", 31},
-  {NULL, 0}
-};
-
-/* VFP DP Registers.  */
-static const struct reg_entry dn_table[] =
-{
-  {"d0",  0},  {"d1",  1},  {"d2",  2},         {"d3", 3},
-  {"d4",  4},  {"d5",  5},  {"d6",  6},         {"d7", 7},
-  {"d8",  8},  {"d9",  9},  {"d10", 10}, {"d11", 11},
-  {"d12", 12}, {"d13", 13}, {"d14", 14}, {"d15", 15},
-  {NULL, 0}
-};
-
-/* Maverick DSP coprocessor registers.  */
-static const struct reg_entry mav_mvf_table[] =
-{
-  {"mvf0",  0},  {"mvf1",  1},  {"mvf2",  2},  {"mvf3",  3},
-  {"mvf4",  4},  {"mvf5",  5},  {"mvf6",  6},  {"mvf7",  7},
-  {"mvf8",  8},  {"mvf9",  9},  {"mvf10", 10}, {"mvf11", 11},
-  {"mvf12", 12}, {"mvf13", 13}, {"mvf14", 14}, {"mvf15", 15},
-  {NULL, 0}
-};
-
-static const struct reg_entry mav_mvd_table[] =
-{
-  {"mvd0",  0},  {"mvd1",  1},  {"mvd2",  2},  {"mvd3",  3},
-  {"mvd4",  4},  {"mvd5",  5},  {"mvd6",  6},  {"mvd7",  7},
-  {"mvd8",  8},  {"mvd9",  9},  {"mvd10", 10}, {"mvd11", 11},
-  {"mvd12", 12}, {"mvd13", 13}, {"mvd14", 14}, {"mvd15", 15},
-  {NULL, 0}
-};
-
-static const struct reg_entry mav_mvfx_table[] =
-{
-  {"mvfx0",  0},  {"mvfx1",  1},  {"mvfx2",  2},  {"mvfx3",  3},
-  {"mvfx4",  4},  {"mvfx5",  5},  {"mvfx6",  6},  {"mvfx7",  7},
-  {"mvfx8",  8},  {"mvfx9",  9},  {"mvfx10", 10}, {"mvfx11", 11},
-  {"mvfx12", 12}, {"mvfx13", 13}, {"mvfx14", 14}, {"mvfx15", 15},
-  {NULL, 0}
-};
-
-static const struct reg_entry mav_mvdx_table[] =
-{
-  {"mvdx0",  0},  {"mvdx1",  1},  {"mvdx2",  2},  {"mvdx3",  3},
-  {"mvdx4",  4},  {"mvdx5",  5},  {"mvdx6",  6},  {"mvdx7",  7},
-  {"mvdx8",  8},  {"mvdx9",  9},  {"mvdx10", 10}, {"mvdx11", 11},
-  {"mvdx12", 12}, {"mvdx13", 13}, {"mvdx14", 14}, {"mvdx15", 15},
-  {NULL, 0}
-};
-
-static const struct reg_entry mav_mvax_table[] =
-{
-  {"mvax0", 0}, {"mvax1", 1}, {"mvax2", 2}, {"mvax3", 3},
-  {NULL, 0}
-};
-
-static const struct reg_entry mav_dspsc_table[] =
-{
-  {"dspsc", 0},
-  {NULL, 0}
-};
-
-struct reg_map
-{
-  const struct reg_entry *names;
-  int max_regno;
-  struct hash_control *htab;
-  const char *expected;
-};
-
-struct reg_map all_reg_maps[] =
-{
-  {rn_table,        15, NULL, N_("ARM register expected")},
-  {cp_table,        15, NULL, N_("bad or missing co-processor number")},
-  {cn_table,        15, NULL, N_("co-processor register expected")},
-  {fn_table,         7, NULL, N_("FPA register expected")},
-  {sn_table,       31, NULL, N_("VFP single precision register expected")},
-  {dn_table,       15, NULL, N_("VFP double precision register expected")},
-  {mav_mvf_table,   15, NULL, N_("Maverick MVF register expected")},
-  {mav_mvd_table,   15, NULL, N_("Maverick MVD register expected")},
-  {mav_mvfx_table,  15, NULL, N_("Maverick MVFX register expected")},
-  {mav_mvdx_table,  15, NULL, N_("Maverick MVFX register expected")},
-  {mav_mvax_table,   3, NULL, N_("Maverick MVAX register expected")},
-  {mav_dspsc_table,  0, NULL, N_("Maverick DSPSC register expected")},
+  const char   *name;
+  unsigned char number;
+  unsigned char type;
+  unsigned char builtin;
 };
 
 };
 
-/* Enumeration matching entries in table above.  */
-enum arm_reg_type
-{
-  REG_TYPE_RN = 0,
-#define REG_TYPE_FIRST REG_TYPE_RN
-  REG_TYPE_CP = 1,
-  REG_TYPE_CN = 2,
-  REG_TYPE_FN = 3,
-  REG_TYPE_SN = 4,
-  REG_TYPE_DN = 5,
-  REG_TYPE_MVF = 6,
-  REG_TYPE_MVD = 7,
-  REG_TYPE_MVFX = 8,
-  REG_TYPE_MVDX = 9,
-  REG_TYPE_MVAX = 10,
-  REG_TYPE_DSPSC = 11,
-
-  REG_TYPE_MAX = 12
+/* Diagnostics used when we don't get a register of the expected type. */
+const char *const reg_expected_msgs[] =
+{
+  N_("ARM register expected"),
+  N_("bad or missing co-processor number"),
+  N_("co-processor register expected"),
+  N_("FPA register expected"),
+  N_("VFP single precision register expected"),
+  N_("VFP double precision register expected"),
+  N_("VFP system register expected"),
+  N_("Maverick MVF register expected"),
+  N_("Maverick MVD register expected"),
+  N_("Maverick MVFX register expected"),
+  N_("Maverick MVDX register expected"),
+  N_("Maverick MVAX register expected"),
+  N_("Maverick DSPSC register expected"),
+  N_("iWMMXt data register expected"),
+  N_("iWMMXt control register expected"),
+  N_("iWMMXt scalar register expected"),
+  N_("XScale accumulator register expected"),
 };
 
 };
 
-/* Functions called by parser.  */
-/* ARM instructions.  */
-static void do_arit            PARAMS ((char *));
-static void do_cmp             PARAMS ((char *));
-static void do_mov             PARAMS ((char *));
-static void do_ldst            PARAMS ((char *));
-static void do_ldstt           PARAMS ((char *));
-static void do_ldmstm          PARAMS ((char *));
-static void do_branch          PARAMS ((char *));
-static void do_swi             PARAMS ((char *));
-
-/* Pseudo Op codes.  */
-static void do_adr             PARAMS ((char *));
-static void do_adrl            PARAMS ((char *));
-static void do_empty           PARAMS ((char *));
-
-/* ARM v2.  */
-static void do_mul             PARAMS ((char *));
-static void do_mla             PARAMS ((char *));
-
-/* ARM v2S.  */
-static void do_swap            PARAMS ((char *));
-
-/* ARM v3.  */
-static void do_msr             PARAMS ((char *));
-static void do_mrs             PARAMS ((char *));
-
-/* ARM v3M.  */
-static void do_mull            PARAMS ((char *));
-
-/* ARM v4.  */
-static void do_ldstv4          PARAMS ((char *));
-
-/* ARM v4T.  */
-static void do_bx               PARAMS ((char *));
-
-/* ARM v5T.  */
-static void do_blx             PARAMS ((char *));
-static void do_bkpt            PARAMS ((char *));
-static void do_clz             PARAMS ((char *));
-static void do_lstc2           PARAMS ((char *));
-static void do_cdp2            PARAMS ((char *));
-static void do_co_reg2         PARAMS ((char *));
-
-/* ARM v5TExP.  */
-static void do_smla            PARAMS ((char *));
-static void do_smlal           PARAMS ((char *));
-static void do_smul            PARAMS ((char *));
-static void do_qadd            PARAMS ((char *));
-
-/* ARM v5TE.  */
-static void do_pld             PARAMS ((char *));
-static void do_ldrd            PARAMS ((char *));
-static void do_co_reg2c                PARAMS ((char *));
-
-/* ARM v5TEJ.  */
-static void do_bxj             PARAMS ((char *));
-
-/* Coprocessor Instructions.  */
-static void do_cdp             PARAMS ((char *));
-static void do_lstc            PARAMS ((char *));
-static void do_co_reg          PARAMS ((char *));
-
-/* FPA instructions.  */
-static void do_fpa_ctrl                PARAMS ((char *));
-static void do_fpa_ldst                PARAMS ((char *));
-static void do_fpa_ldmstm      PARAMS ((char *));
-static void do_fpa_dyadic      PARAMS ((char *));
-static void do_fpa_monadic     PARAMS ((char *));
-static void do_fpa_cmp         PARAMS ((char *));
-static void do_fpa_from_reg    PARAMS ((char *));
-static void do_fpa_to_reg      PARAMS ((char *));
-
-/* VFP instructions.  */
-static void do_vfp_sp_monadic  PARAMS ((char *));
-static void do_vfp_dp_monadic  PARAMS ((char *));
-static void do_vfp_sp_dyadic   PARAMS ((char *));
-static void do_vfp_dp_dyadic   PARAMS ((char *));
-static void do_vfp_reg_from_sp  PARAMS ((char *));
-static void do_vfp_sp_from_reg  PARAMS ((char *));
-static void do_vfp_sp_reg2     PARAMS ((char *));
-static void do_vfp_reg_from_dp  PARAMS ((char *));
-static void do_vfp_reg2_from_dp PARAMS ((char *));
-static void do_vfp_dp_from_reg  PARAMS ((char *));
-static void do_vfp_dp_from_reg2 PARAMS ((char *));
-static void do_vfp_reg_from_ctrl PARAMS ((char *));
-static void do_vfp_ctrl_from_reg PARAMS ((char *));
-static void do_vfp_sp_ldst     PARAMS ((char *));
-static void do_vfp_dp_ldst     PARAMS ((char *));
-static void do_vfp_sp_ldstmia  PARAMS ((char *));
-static void do_vfp_sp_ldstmdb  PARAMS ((char *));
-static void do_vfp_dp_ldstmia  PARAMS ((char *));
-static void do_vfp_dp_ldstmdb  PARAMS ((char *));
-static void do_vfp_xp_ldstmia  PARAMS ((char *));
-static void do_vfp_xp_ldstmdb  PARAMS ((char *));
-static void do_vfp_sp_compare_z        PARAMS ((char *));
-static void do_vfp_dp_compare_z        PARAMS ((char *));
-static void do_vfp_dp_sp_cvt   PARAMS ((char *));
-static void do_vfp_sp_dp_cvt   PARAMS ((char *));
-
-/* XScale.  */
-static void do_xsc_mia         PARAMS ((char *));
-static void do_xsc_mar         PARAMS ((char *));
-static void do_xsc_mra         PARAMS ((char *));
-
-/* Maverick.  */
-static void do_mav_binops      PARAMS ((char *, int, enum arm_reg_type,
-                                        enum arm_reg_type));
-static void do_mav_binops_1a   PARAMS ((char *));
-static void do_mav_binops_1b   PARAMS ((char *));
-static void do_mav_binops_1c   PARAMS ((char *));
-static void do_mav_binops_1d   PARAMS ((char *));
-static void do_mav_binops_1e   PARAMS ((char *));
-static void do_mav_binops_1f   PARAMS ((char *));
-static void do_mav_binops_1g   PARAMS ((char *));
-static void do_mav_binops_1h   PARAMS ((char *));
-static void do_mav_binops_1i   PARAMS ((char *));
-static void do_mav_binops_1j   PARAMS ((char *));
-static void do_mav_binops_1k   PARAMS ((char *));
-static void do_mav_binops_1l   PARAMS ((char *));
-static void do_mav_binops_1m   PARAMS ((char *));
-static void do_mav_binops_1n   PARAMS ((char *));
-static void do_mav_binops_1o   PARAMS ((char *));
-static void do_mav_binops_2a   PARAMS ((char *));
-static void do_mav_binops_2b   PARAMS ((char *));
-static void do_mav_binops_2c   PARAMS ((char *));
-static void do_mav_binops_3a   PARAMS ((char *));
-static void do_mav_binops_3b   PARAMS ((char *));
-static void do_mav_binops_3c   PARAMS ((char *));
-static void do_mav_binops_3d   PARAMS ((char *));
-static void do_mav_triple      PARAMS ((char *, int, enum arm_reg_type,
-                                        enum arm_reg_type,
-                                        enum arm_reg_type));
-static void do_mav_triple_4a   PARAMS ((char *));
-static void do_mav_triple_4b   PARAMS ((char *));
-static void do_mav_triple_5a   PARAMS ((char *));
-static void do_mav_triple_5b   PARAMS ((char *));
-static void do_mav_triple_5c   PARAMS ((char *));
-static void do_mav_triple_5d   PARAMS ((char *));
-static void do_mav_triple_5e   PARAMS ((char *));
-static void do_mav_triple_5f   PARAMS ((char *));
-static void do_mav_triple_5g   PARAMS ((char *));
-static void do_mav_triple_5h   PARAMS ((char *));
-static void do_mav_quad                PARAMS ((char *, int, enum arm_reg_type,
-                                        enum arm_reg_type,
-                                        enum arm_reg_type,
-                                        enum arm_reg_type));
-static void do_mav_quad_6a     PARAMS ((char *));
-static void do_mav_quad_6b     PARAMS ((char *));
-static void do_mav_dspsc_1     PARAMS ((char *));
-static void do_mav_dspsc_2     PARAMS ((char *));
-static void do_mav_shift       PARAMS ((char *, enum arm_reg_type,
-                                        enum arm_reg_type));
-static void do_mav_shift_1     PARAMS ((char *));
-static void do_mav_shift_2     PARAMS ((char *));
-static void do_mav_ldst                PARAMS ((char *, enum arm_reg_type));
-static void do_mav_ldst_1      PARAMS ((char *));
-static void do_mav_ldst_2      PARAMS ((char *));
-static void do_mav_ldst_3      PARAMS ((char *));
-static void do_mav_ldst_4      PARAMS ((char *));
-
-static int mav_reg_required_here       PARAMS ((char **, int,
-                                                enum arm_reg_type));
-static int mav_parse_offset    PARAMS ((char **, int *));
-
-static void fix_new_arm                PARAMS ((fragS *, int, short, expressionS *,
-                                        int, int));
-static int arm_reg_parse       PARAMS ((char **, struct hash_control *));
-static enum arm_reg_type arm_reg_parse_any PARAMS ((char *));
-static const struct asm_psr * arm_psr_parse PARAMS ((char **));
-static void symbol_locate      PARAMS ((symbolS *, const char *, segT, valueT,
-                                        fragS *));
-static int add_to_lit_pool     PARAMS ((void));
-static unsigned validate_immediate PARAMS ((unsigned));
-static unsigned validate_immediate_twopart PARAMS ((unsigned int,
-                                                   unsigned int *));
-static int validate_offset_imm PARAMS ((unsigned int, int));
-static void opcode_select      PARAMS ((int));
-static void end_of_line                PARAMS ((char *));
-static int reg_required_here   PARAMS ((char **, int));
-static int psr_required_here   PARAMS ((char **));
-static int co_proc_number      PARAMS ((char **));
-static int cp_opc_expr         PARAMS ((char **, int, int));
-static int cp_reg_required_here        PARAMS ((char **, int));
-static int fp_reg_required_here        PARAMS ((char **, int));
-static int vfp_sp_reg_required_here PARAMS ((char **, enum vfp_sp_reg_pos));
-static int vfp_dp_reg_required_here PARAMS ((char **, enum vfp_dp_reg_pos));
-static void vfp_sp_ldstm       PARAMS ((char *, enum vfp_ldstm_type));
-static void vfp_dp_ldstm       PARAMS ((char *, enum vfp_ldstm_type));
-static long vfp_sp_reg_list    PARAMS ((char **, enum vfp_sp_reg_pos));
-static long vfp_dp_reg_list    PARAMS ((char **));
-static int vfp_psr_required_here PARAMS ((char **str));
-static const struct vfp_reg *vfp_psr_parse PARAMS ((char **str));
-static int cp_address_offset   PARAMS ((char **));
-static int cp_address_required_here    PARAMS ((char **, int));
-static int my_get_float_expression     PARAMS ((char **));
-static int skip_past_comma     PARAMS ((char **));
-static int walk_no_bignums     PARAMS ((symbolS *));
-static int negate_data_op      PARAMS ((unsigned long *, unsigned long));
-static int data_op2            PARAMS ((char **));
-static int fp_op2              PARAMS ((char **));
-static long reg_list           PARAMS ((char **));
-static void thumb_load_store   PARAMS ((char *, int, int));
-static int decode_shift                PARAMS ((char **, int));
-static int ldst_extend         PARAMS ((char **));
-static int ldst_extend_v4              PARAMS ((char **));
-static void thumb_add_sub      PARAMS ((char *, int));
-static void insert_reg         PARAMS ((const struct reg_entry *,
-                                        struct hash_control *));
-static void thumb_shift                PARAMS ((char *, int));
-static void thumb_mov_compare  PARAMS ((char *, int));
-static void build_arm_ops_hsh  PARAMS ((void));
-static void set_constant_flonums       PARAMS ((void));
-static valueT md_chars_to_number       PARAMS ((char *, int));
-static void build_reg_hsh      PARAMS ((struct reg_map *));
-static void insert_reg_alias   PARAMS ((char *, int, struct hash_control *));
-static int create_register_alias       PARAMS ((char *, char *));
-static void output_inst                PARAMS ((const char *));
-static int accum0_required_here PARAMS ((char **));
-static int ld_mode_required_here PARAMS ((char **));
-static void do_branch25         PARAMS ((char *));
-static symbolS * find_real_start PARAMS ((symbolS *));
-#ifdef OBJ_ELF
-static bfd_reloc_code_real_type        arm_parse_reloc PARAMS ((void));
-#endif
+/* Some well known registers that we refer to directly elsewhere.  */
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
 
 /* ARM instructions take 4bytes in the object file, Thumb instructions
    take 2:  */
 
 /* ARM instructions take 4bytes in the object file, Thumb instructions
    take 2:  */
-#define INSN_SIZE       4
-
-/* "INSN<cond> X,Y" where X:bit12, Y:bit16.  */
-#define MAV_MODE1      0x100c
-
-/* "INSN<cond> X,Y" where X:bit16, Y:bit12.  */
-#define MAV_MODE2      0x0c10
-
-/* "INSN<cond> X,Y" where X:0, Y:bit16.  */
-#define MAV_MODE3      0x1000
-
-/* "INSN<cond> X,Y,Z" where X:16, Y:0, Z:12.  */
-#define MAV_MODE4      0x0c0010
-
-/* "INSN<cond> X,Y,Z" where X:12, Y:16, Z:0.  */
-#define MAV_MODE5      0x00100c
-
-/* "INSN<cond> W,X,Y,Z" where W:5, X:12, Y:16, Z:0.  */
-#define MAV_MODE6      0x00100c05
+#define INSN_SIZE      4
 
 struct asm_opcode
 {
   /* Basic string to match.  */
 
 struct asm_opcode
 {
   /* Basic string to match.  */
-  const char * template;
-
-  /* Basic instruction code.  */
-  unsigned long value;
-
-  /* Offset into the template where the condition code (if any) will be.
-     If zero, then the instruction is never conditional.  */
-  unsigned cond_offset;
-
-  /* Which architecture variant provides this instruction.  */
-  unsigned long variant;
-
-  /* Function to call to parse args.  */
-  void (* parms) PARAMS ((char *));
-};
-
-static const struct asm_opcode insns[] =
-{
-  /* Core ARM Instructions.  */
-  {"and",        0xe0000000, 3,  ARM_EXT_V1,       do_arit},
-  {"ands",       0xe0100000, 3,  ARM_EXT_V1,       do_arit},
-  {"eor",        0xe0200000, 3,  ARM_EXT_V1,       do_arit},
-  {"eors",       0xe0300000, 3,  ARM_EXT_V1,       do_arit},
-  {"sub",        0xe0400000, 3,  ARM_EXT_V1,       do_arit},
-  {"subs",       0xe0500000, 3,  ARM_EXT_V1,       do_arit},
-  {"rsb",        0xe0600000, 3,  ARM_EXT_V1,       do_arit},
-  {"rsbs",       0xe0700000, 3,  ARM_EXT_V1,       do_arit},
-  {"add",        0xe0800000, 3,  ARM_EXT_V1,       do_arit},
-  {"adds",       0xe0900000, 3,  ARM_EXT_V1,       do_arit},
-  {"adc",        0xe0a00000, 3,  ARM_EXT_V1,       do_arit},
-  {"adcs",       0xe0b00000, 3,  ARM_EXT_V1,       do_arit},
-  {"sbc",        0xe0c00000, 3,  ARM_EXT_V1,       do_arit},
-  {"sbcs",       0xe0d00000, 3,  ARM_EXT_V1,       do_arit},
-  {"rsc",        0xe0e00000, 3,  ARM_EXT_V1,       do_arit},
-  {"rscs",       0xe0f00000, 3,  ARM_EXT_V1,       do_arit},
-  {"orr",        0xe1800000, 3,  ARM_EXT_V1,       do_arit},
-  {"orrs",       0xe1900000, 3,  ARM_EXT_V1,       do_arit},
-  {"bic",        0xe1c00000, 3,  ARM_EXT_V1,       do_arit},
-  {"bics",       0xe1d00000, 3,  ARM_EXT_V1,       do_arit},
-
-  {"tst",        0xe1100000, 3,  ARM_EXT_V1,       do_cmp},
-  {"tsts",       0xe1100000, 3,  ARM_EXT_V1,       do_cmp},
-  {"tstp",       0xe110f000, 3,  ARM_EXT_V1,       do_cmp},
-  {"teq",        0xe1300000, 3,  ARM_EXT_V1,       do_cmp},
-  {"teqs",       0xe1300000, 3,  ARM_EXT_V1,       do_cmp},
-  {"teqp",       0xe130f000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmp",        0xe1500000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmps",       0xe1500000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmpp",       0xe150f000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmn",        0xe1700000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmns",       0xe1700000, 3,  ARM_EXT_V1,       do_cmp},
-  {"cmnp",       0xe170f000, 3,  ARM_EXT_V1,       do_cmp},
-
-  {"mov",        0xe1a00000, 3,  ARM_EXT_V1,       do_mov},
-  {"movs",       0xe1b00000, 3,  ARM_EXT_V1,       do_mov},
-  {"mvn",        0xe1e00000, 3,  ARM_EXT_V1,       do_mov},
-  {"mvns",       0xe1f00000, 3,  ARM_EXT_V1,       do_mov},
-
-  {"ldr",        0xe4100000, 3,  ARM_EXT_V1,       do_ldst},
-  {"ldrb",       0xe4500000, 3,  ARM_EXT_V1,       do_ldst},
-  {"ldrt",       0xe4300000, 3,  ARM_EXT_V1,       do_ldstt},
-  {"ldrbt",      0xe4700000, 3,  ARM_EXT_V1,       do_ldstt},
-  {"str",        0xe4000000, 3,  ARM_EXT_V1,       do_ldst},
-  {"strb",       0xe4400000, 3,  ARM_EXT_V1,       do_ldst},
-  {"strt",       0xe4200000, 3,  ARM_EXT_V1,       do_ldstt},
-  {"strbt",      0xe4600000, 3,  ARM_EXT_V1,       do_ldstt},
-
-  {"stmia",      0xe8800000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmib",      0xe9800000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmda",      0xe8000000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmdb",      0xe9000000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmfd",      0xe9000000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmfa",      0xe9800000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmea",      0xe8800000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"stmed",      0xe8000000, 3,  ARM_EXT_V1,       do_ldmstm},
-
-  {"ldmia",      0xe8900000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmib",      0xe9900000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmda",      0xe8100000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmdb",      0xe9100000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmfd",      0xe8900000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmfa",      0xe8100000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmea",      0xe9100000, 3,  ARM_EXT_V1,       do_ldmstm},
-  {"ldmed",      0xe9900000, 3,  ARM_EXT_V1,       do_ldmstm},
-
-  {"swi",        0xef000000, 3,  ARM_EXT_V1,       do_swi},
-#ifdef TE_WINCE
-  /* XXX This is the wrong place to do this.  Think multi-arch.  */
-  {"bl",         0xeb000000, 2,  ARM_EXT_V1,       do_branch},
-  {"b",          0xea000000, 1,  ARM_EXT_V1,       do_branch},
-#else
-  {"bl",         0xebfffffe, 2,  ARM_EXT_V1,       do_branch},
-  {"b",          0xeafffffe, 1,  ARM_EXT_V1,       do_branch},
-#endif
-
-  /* Pseudo ops.  */
-  {"adr",        0xe28f0000, 3,  ARM_EXT_V1,       do_adr},
-  {"adrl",       0xe28f0000, 3,  ARM_EXT_V1,       do_adrl},
-  {"nop",        0xe1a00000, 3,  ARM_EXT_V1,       do_empty},
-
-  /* ARM 2 multiplies.  */
-  {"mul",        0xe0000090, 3,  ARM_EXT_V2,       do_mul},
-  {"muls",       0xe0100090, 3,  ARM_EXT_V2,       do_mul},
-  {"mla",        0xe0200090, 3,  ARM_EXT_V2,       do_mla},
-  {"mlas",       0xe0300090, 3,  ARM_EXT_V2,       do_mla},
-
-  /* Generic copressor instructions.  */
-  {"cdp",        0xee000000, 3,  ARM_EXT_V2,       do_cdp},
-  {"ldc",        0xec100000, 3,  ARM_EXT_V2,       do_lstc},
-  {"ldcl",       0xec500000, 3,  ARM_EXT_V2,       do_lstc},
-  {"stc",        0xec000000, 3,  ARM_EXT_V2,       do_lstc},
-  {"stcl",       0xec400000, 3,  ARM_EXT_V2,       do_lstc},
-  {"mcr",        0xee000010, 3,  ARM_EXT_V2,       do_co_reg},
-  {"mrc",        0xee100010, 3,  ARM_EXT_V2,       do_co_reg},
-
-  /* ARM 3 - swp instructions.  */
-  {"swp",        0xe1000090, 3,  ARM_EXT_V2S,      do_swap},
-  {"swpb",       0xe1400090, 3,  ARM_EXT_V2S,      do_swap},
-
-  /* ARM 6 Status register instructions.  */
-  {"mrs",        0xe10f0000, 3,  ARM_EXT_V3,       do_mrs},
-  {"msr",        0xe120f000, 3,  ARM_EXT_V3,       do_msr},
-  /* ScottB: our code uses     0xe128f000 for msr.
-     NickC:  but this is wrong because the bits 16 through 19 are
-             handled by the PSR_xxx defines above.  */
-
-  /* ARM 7M long multiplies.  */
-  {"smull",      0xe0c00090, 5,  ARM_EXT_V3M,      do_mull},
-  {"smulls",     0xe0d00090, 5,  ARM_EXT_V3M,      do_mull},
-  {"umull",      0xe0800090, 5,  ARM_EXT_V3M,      do_mull},
-  {"umulls",     0xe0900090, 5,  ARM_EXT_V3M,      do_mull},
-  {"smlal",      0xe0e00090, 5,  ARM_EXT_V3M,      do_mull},
-  {"smlals",     0xe0f00090, 5,  ARM_EXT_V3M,      do_mull},
-  {"umlal",      0xe0a00090, 5,  ARM_EXT_V3M,      do_mull},
-  {"umlals",     0xe0b00090, 5,  ARM_EXT_V3M,      do_mull},
-
-  /* ARM Architecture 4.  */
-  {"ldrh",       0xe01000b0, 3,  ARM_EXT_V4,       do_ldstv4},
-  {"ldrsh",      0xe01000f0, 3,  ARM_EXT_V4,       do_ldstv4},
-  {"ldrsb",      0xe01000d0, 3,  ARM_EXT_V4,       do_ldstv4},
-  {"strh",       0xe00000b0, 3,  ARM_EXT_V4,       do_ldstv4},
-
-  /* ARM Architecture 4T.  */
-  /* Note: bx (and blx) are required on V5, even if the processor does
-     not support Thumb.  */
-  {"bx",         0xe12fff10, 2,  ARM_EXT_V4T | ARM_EXT_V5, do_bx},
-
-  /*  ARM Architecture 5T.  */
-  /* Note: blx has 2 variants, so the .value is set dynamically.
-     Only one of the variants has conditional execution.  */
-  {"blx",        0xe0000000, 3,  ARM_EXT_V5,       do_blx},
-  {"clz",        0xe16f0f10, 3,  ARM_EXT_V5,       do_clz},
-  {"bkpt",       0xe1200070, 0,  ARM_EXT_V5,       do_bkpt},
-  {"ldc2",       0xfc100000, 0,  ARM_EXT_V5,       do_lstc2},
-  {"ldc2l",      0xfc500000, 0,  ARM_EXT_V5,       do_lstc2},
-  {"stc2",       0xfc000000, 0,  ARM_EXT_V5,       do_lstc2},
-  {"stc2l",      0xfc400000, 0,  ARM_EXT_V5,       do_lstc2},
-  {"cdp2",       0xfe000000, 0,  ARM_EXT_V5,       do_cdp2},
-  {"mcr2",       0xfe000010, 0,  ARM_EXT_V5,       do_co_reg2},
-  {"mrc2",       0xfe100010, 0,  ARM_EXT_V5,       do_co_reg2},
-
-  /*  ARM Architecture 5TExP.  */
-  {"smlabb",     0xe1000080, 6,  ARM_EXT_V5ExP,    do_smla},
-  {"smlatb",     0xe10000a0, 6,  ARM_EXT_V5ExP,    do_smla},
-  {"smlabt",     0xe10000c0, 6,  ARM_EXT_V5ExP,    do_smla},
-  {"smlatt",     0xe10000e0, 6,  ARM_EXT_V5ExP,    do_smla},
-
-  {"smlawb",     0xe1200080, 6,  ARM_EXT_V5ExP,    do_smla},
-  {"smlawt",     0xe12000c0, 6,  ARM_EXT_V5ExP,    do_smla},
-
-  {"smlalbb",    0xe1400080, 7,  ARM_EXT_V5ExP,    do_smlal},
-  {"smlaltb",    0xe14000a0, 7,  ARM_EXT_V5ExP,    do_smlal},
-  {"smlalbt",    0xe14000c0, 7,  ARM_EXT_V5ExP,    do_smlal},
-  {"smlaltt",    0xe14000e0, 7,  ARM_EXT_V5ExP,    do_smlal},
-
-  {"smulbb",     0xe1600080, 6,  ARM_EXT_V5ExP,    do_smul},
-  {"smultb",     0xe16000a0, 6,  ARM_EXT_V5ExP,    do_smul},
-  {"smulbt",     0xe16000c0, 6,  ARM_EXT_V5ExP,    do_smul},
-  {"smultt",     0xe16000e0, 6,  ARM_EXT_V5ExP,    do_smul},
-
-  {"smulwb",     0xe12000a0, 6,  ARM_EXT_V5ExP,    do_smul},
-  {"smulwt",     0xe12000e0, 6,  ARM_EXT_V5ExP,    do_smul},
-
-  {"qadd",       0xe1000050, 4,  ARM_EXT_V5ExP,    do_qadd},
-  {"qdadd",      0xe1400050, 5,  ARM_EXT_V5ExP,    do_qadd},
-  {"qsub",       0xe1200050, 4,  ARM_EXT_V5ExP,    do_qadd},
-  {"qdsub",      0xe1600050, 5,  ARM_EXT_V5ExP,    do_qadd},
-
-  /*  ARM Architecture 5TE.  */
-  {"pld",        0xf450f000, 0,  ARM_EXT_V5E,      do_pld},
-  {"ldrd",       0xe00000d0, 3,  ARM_EXT_V5E,      do_ldrd},
-  {"strd",       0xe00000f0, 3,  ARM_EXT_V5E,      do_ldrd},
-
-  {"mcrr",       0xec400000, 4,  ARM_EXT_V5E,      do_co_reg2c},
-  {"mrrc",       0xec500000, 4,  ARM_EXT_V5E,      do_co_reg2c},
-
-  /*  ARM Architecture 5TEJ.  */
-  {"bxj",       0xe12fff20, 3,  ARM_EXT_V5J,      do_bxj},
-
-  /* Core FPA instruction set (V1).  */
-  {"wfs",        0xee200110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
-  {"rfs",        0xee300110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
-  {"wfc",        0xee400110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
-  {"rfc",        0xee500110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
-
-  {"ldfs",       0xec100100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"ldfd",       0xec108100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"ldfe",       0xec500100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"ldfp",       0xec508100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-
-  {"stfs",       0xec000100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"stfd",       0xec008100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"stfe",       0xec400100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-  {"stfp",       0xec408100, 3,  FPU_FPA_EXT_V1,   do_fpa_ldst},
-
-  {"mvfs",       0xee008100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfsp",      0xee008120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfsm",      0xee008140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfsz",      0xee008160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfd",       0xee008180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfdp",      0xee0081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfdm",      0xee0081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfdz",      0xee0081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfe",       0xee088100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfep",      0xee088120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfem",      0xee088140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mvfez",      0xee088160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"mnfs",       0xee108100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfsp",      0xee108120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfsm",      0xee108140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfsz",      0xee108160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfd",       0xee108180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfdp",      0xee1081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfdm",      0xee1081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfdz",      0xee1081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfe",       0xee188100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfep",      0xee188120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfem",      0xee188140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"mnfez",      0xee188160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"abss",       0xee208100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"abssp",      0xee208120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"abssm",      0xee208140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"abssz",      0xee208160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absd",       0xee208180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absdp",      0xee2081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absdm",      0xee2081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absdz",      0xee2081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"abse",       0xee288100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absep",      0xee288120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absem",      0xee288140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"absez",      0xee288160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"rnds",       0xee308100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndsp",      0xee308120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndsm",      0xee308140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndsz",      0xee308160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndd",       0xee308180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rnddp",      0xee3081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rnddm",      0xee3081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rnddz",      0xee3081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rnde",       0xee388100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndep",      0xee388120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndem",      0xee388140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"rndez",      0xee388160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"sqts",       0xee408100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtsp",      0xee408120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtsm",      0xee408140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtsz",      0xee408160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtd",       0xee408180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtdp",      0xee4081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtdm",      0xee4081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtdz",      0xee4081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqte",       0xee488100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtep",      0xee488120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtem",      0xee488140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sqtez",      0xee488160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"logs",       0xee508100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logsp",      0xee508120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logsm",      0xee508140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logsz",      0xee508160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logd",       0xee508180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logdp",      0xee5081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logdm",      0xee5081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logdz",      0xee5081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"loge",       0xee588100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logep",      0xee588120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logem",      0xee588140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"logez",      0xee588160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"lgns",       0xee608100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnsp",      0xee608120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnsm",      0xee608140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnsz",      0xee608160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnd",       0xee608180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgndp",      0xee6081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgndm",      0xee6081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgndz",      0xee6081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgne",       0xee688100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnep",      0xee688120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnem",      0xee688140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"lgnez",      0xee688160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"exps",       0xee708100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expsp",      0xee708120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expsm",      0xee708140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expsz",      0xee708160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expd",       0xee708180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expdp",      0xee7081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expdm",      0xee7081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expdz",      0xee7081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expe",       0xee788100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expep",      0xee788120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expem",      0xee788140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"expdz",      0xee788160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"sins",       0xee808100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinsp",      0xee808120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinsm",      0xee808140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinsz",      0xee808160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sind",       0xee808180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sindp",      0xee8081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sindm",      0xee8081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sindz",      0xee8081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sine",       0xee888100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinep",      0xee888120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinem",      0xee888140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"sinez",      0xee888160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"coss",       0xee908100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cossp",      0xee908120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cossm",      0xee908140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cossz",      0xee908160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosd",       0xee908180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosdp",      0xee9081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosdm",      0xee9081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosdz",      0xee9081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cose",       0xee988100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosep",      0xee988120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosem",      0xee988140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"cosez",      0xee988160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"tans",       0xeea08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tansp",      0xeea08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tansm",      0xeea08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tansz",      0xeea08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tand",       0xeea08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tandp",      0xeea081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tandm",      0xeea081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tandz",      0xeea081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tane",       0xeea88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tanep",      0xeea88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tanem",      0xeea88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"tanez",      0xeea88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"asns",       0xeeb08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnsp",      0xeeb08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnsm",      0xeeb08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnsz",      0xeeb08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnd",       0xeeb08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asndp",      0xeeb081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asndm",      0xeeb081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asndz",      0xeeb081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asne",       0xeeb88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnep",      0xeeb88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnem",      0xeeb88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"asnez",      0xeeb88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"acss",       0xeec08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acssp",      0xeec08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acssm",      0xeec08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acssz",      0xeec08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsd",       0xeec08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsdp",      0xeec081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsdm",      0xeec081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsdz",      0xeec081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acse",       0xeec88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsep",      0xeec88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsem",      0xeec88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"acsez",      0xeec88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"atns",       0xeed08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnsp",      0xeed08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnsm",      0xeed08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnsz",      0xeed08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnd",       0xeed08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atndp",      0xeed081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atndm",      0xeed081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atndz",      0xeed081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atne",       0xeed88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnep",      0xeed88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnem",      0xeed88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"atnez",      0xeed88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"urds",       0xeee08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdsp",      0xeee08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdsm",      0xeee08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdsz",      0xeee08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdd",       0xeee08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urddp",      0xeee081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urddm",      0xeee081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urddz",      0xeee081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urde",       0xeee88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdep",      0xeee88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdem",      0xeee88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"urdez",      0xeee88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"nrms",       0xeef08100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmsp",      0xeef08120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmsm",      0xeef08140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmsz",      0xeef08160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmd",       0xeef08180, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmdp",      0xeef081a0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmdm",      0xeef081c0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmdz",      0xeef081e0, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrme",       0xeef88100, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmep",      0xeef88120, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmem",      0xeef88140, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-  {"nrmez",      0xeef88160, 3,  FPU_FPA_EXT_V1,   do_fpa_monadic},
-
-  {"adfs",       0xee000100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfsp",      0xee000120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfsm",      0xee000140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfsz",      0xee000160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfd",       0xee000180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfdp",      0xee0001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfdm",      0xee0001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfdz",      0xee0001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfe",       0xee080100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfep",      0xee080120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfem",      0xee080140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"adfez",      0xee080160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"sufs",       0xee200100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufsp",      0xee200120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufsm",      0xee200140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufsz",      0xee200160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufd",       0xee200180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufdp",      0xee2001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufdm",      0xee2001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufdz",      0xee2001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufe",       0xee280100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufep",      0xee280120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufem",      0xee280140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"sufez",      0xee280160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"rsfs",       0xee300100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfsp",      0xee300120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfsm",      0xee300140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfsz",      0xee300160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfd",       0xee300180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfdp",      0xee3001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfdm",      0xee3001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfdz",      0xee3001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfe",       0xee380100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfep",      0xee380120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfem",      0xee380140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rsfez",      0xee380160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"mufs",       0xee100100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufsp",      0xee100120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufsm",      0xee100140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufsz",      0xee100160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufd",       0xee100180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufdp",      0xee1001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufdm",      0xee1001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufdz",      0xee1001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufe",       0xee180100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufep",      0xee180120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufem",      0xee180140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"mufez",      0xee180160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"dvfs",       0xee400100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfsp",      0xee400120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfsm",      0xee400140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfsz",      0xee400160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfd",       0xee400180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfdp",      0xee4001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfdm",      0xee4001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfdz",      0xee4001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfe",       0xee480100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfep",      0xee480120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfem",      0xee480140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"dvfez",      0xee480160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"rdfs",       0xee500100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfsp",      0xee500120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfsm",      0xee500140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfsz",      0xee500160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfd",       0xee500180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfdp",      0xee5001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfdm",      0xee5001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfdz",      0xee5001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfe",       0xee580100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfep",      0xee580120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfem",      0xee580140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rdfez",      0xee580160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"pows",       0xee600100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powsp",      0xee600120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powsm",      0xee600140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powsz",      0xee600160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powd",       0xee600180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powdp",      0xee6001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powdm",      0xee6001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powdz",      0xee6001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powe",       0xee680100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powep",      0xee680120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powem",      0xee680140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"powez",      0xee680160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"rpws",       0xee700100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwsp",      0xee700120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwsm",      0xee700140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwsz",      0xee700160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwd",       0xee700180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwdp",      0xee7001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwdm",      0xee7001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwdz",      0xee7001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwe",       0xee780100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwep",      0xee780120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwem",      0xee780140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rpwez",      0xee780160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"rmfs",       0xee800100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfsp",      0xee800120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfsm",      0xee800140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfsz",      0xee800160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfd",       0xee800180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfdp",      0xee8001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfdm",      0xee8001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfdz",      0xee8001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfe",       0xee880100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfep",      0xee880120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfem",      0xee880140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"rmfez",      0xee880160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"fmls",       0xee900100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlsp",      0xee900120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlsm",      0xee900140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlsz",      0xee900160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmld",       0xee900180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmldp",      0xee9001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmldm",      0xee9001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmldz",      0xee9001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmle",       0xee980100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlep",      0xee980120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlem",      0xee980140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fmlez",      0xee980160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"fdvs",       0xeea00100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvsp",      0xeea00120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvsm",      0xeea00140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvsz",      0xeea00160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvd",       0xeea00180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvdp",      0xeea001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvdm",      0xeea001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvdz",      0xeea001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdve",       0xeea80100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvep",      0xeea80120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvem",      0xeea80140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"fdvez",      0xeea80160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"frds",       0xeeb00100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdsp",      0xeeb00120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdsm",      0xeeb00140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdsz",      0xeeb00160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdd",       0xeeb00180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frddp",      0xeeb001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frddm",      0xeeb001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frddz",      0xeeb001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frde",       0xeeb80100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdep",      0xeeb80120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdem",      0xeeb80140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"frdez",      0xeeb80160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"pols",       0xeec00100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polsp",      0xeec00120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polsm",      0xeec00140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polsz",      0xeec00160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"pold",       0xeec00180, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"poldp",      0xeec001a0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"poldm",      0xeec001c0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"poldz",      0xeec001e0, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"pole",       0xeec80100, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polep",      0xeec80120, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polem",      0xeec80140, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-  {"polez",      0xeec80160, 3,  FPU_FPA_EXT_V1,   do_fpa_dyadic},
-
-  {"cmf",        0xee90f110, 3,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-  {"cmfe",       0xeed0f110, 3,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-  {"cnf",        0xeeb0f110, 3,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-  {"cnfe",       0xeef0f110, 3,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-  /* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should
-     not be an optional suffix, but part of the instruction.  To be
-     compatible, we accept either.  */
-  {"cmfe",       0xeed0f110, 4,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-  {"cnfe",       0xeef0f110, 4,  FPU_FPA_EXT_V1,   do_fpa_cmp},
-
-  {"flts",       0xee000110, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltsp",      0xee000130, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltsm",      0xee000150, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltsz",      0xee000170, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltd",       0xee000190, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltdp",      0xee0001b0, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltdm",      0xee0001d0, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltdz",      0xee0001f0, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"flte",       0xee080110, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltep",      0xee080130, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltem",      0xee080150, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
-  {"fltez",      0xee080170, 3,  FPU_FPA_EXT_V1,   do_fpa_from_reg},
+  const char *template;
 
 
-  /* The implementation of the FIX instruction is broken on some
-     assemblers, in that it accepts a precision specifier as well as a
-     rounding specifier, despite the fact that this is meaningless.
-     To be more compatible, we accept it as well, though of course it
-     does not set any bits.  */
-  {"fix",        0xee100110, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixp",       0xee100130, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixm",       0xee100150, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixz",       0xee100170, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixsp",      0xee100130, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixsm",      0xee100150, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixsz",      0xee100170, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixdp",      0xee100130, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixdm",      0xee100150, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixdz",      0xee100170, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixep",      0xee100130, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixem",      0xee100150, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
-  {"fixez",      0xee100170, 3,  FPU_FPA_EXT_V1,   do_fpa_to_reg},
+  /* Parameters to instruction.         */
+  unsigned char operands[8];
 
 
-  /* Instructions that were new with the real FPA, call them V2.  */
-  {"lfm",        0xec100200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-  {"lfmfd",      0xec900200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-  {"lfmea",      0xed100200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-  {"sfm",        0xec000200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-  {"sfmfd",      0xed000200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-  {"sfmea",      0xec800200, 3,  FPU_FPA_EXT_V2,   do_fpa_ldmstm},
-
-  /* VFP V1xD (single precision).  */
-  /* Moves and type conversions.  */
-  {"fcpys",   0xeeb00a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fmrs",    0xee100a10, 4, FPU_VFP_EXT_V1xD, do_vfp_reg_from_sp},
-  {"fmsr",    0xee000a10, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_from_reg},
-  {"fmstat",  0xeef1fa10, 6, FPU_VFP_EXT_V1xD, do_empty},
-  {"fsitos",  0xeeb80ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fuitos",  0xeeb80a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"ftosis",  0xeebd0a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"ftosizs", 0xeebd0ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"ftouis",  0xeebc0a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"ftouizs", 0xeebc0ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fmrx",    0xeef00a10, 4, FPU_VFP_EXT_V1xD, do_vfp_reg_from_ctrl},
-  {"fmxr",    0xeee00a10, 4, FPU_VFP_EXT_V1xD, do_vfp_ctrl_from_reg},
-
-  /* Memory operations.  */
-  {"flds",    0xed100a00, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_ldst},
-  {"fsts",    0xed000a00, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_ldst},
-  {"fldmias", 0xec900a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
-  {"fldmfds", 0xec900a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
-  {"fldmdbs", 0xed300a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
-  {"fldmeas", 0xed300a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
-  {"fldmiax", 0xec900b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
-  {"fldmfdx", 0xec900b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
-  {"fldmdbx", 0xed300b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
-  {"fldmeax", 0xed300b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
-  {"fstmias", 0xec800a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
-  {"fstmeas", 0xec800a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
-  {"fstmdbs", 0xed200a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
-  {"fstmfds", 0xed200a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
-  {"fstmiax", 0xec800b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
-  {"fstmeax", 0xec800b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
-  {"fstmdbx", 0xed200b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
-  {"fstmfdx", 0xed200b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
+  /* Conditional tag - see opcode_lookup.  */
+  unsigned int tag : 4;
 
 
-  /* Monadic operations.  */
-  {"fabss",   0xeeb00ac0, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fnegs",   0xeeb10a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fsqrts",  0xeeb10ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-
-  /* Dyadic operations.  */
-  {"fadds",   0xee300a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fsubs",   0xee300a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fmuls",   0xee200a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fdivs",   0xee800a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fmacs",   0xee000a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fmscs",   0xee100a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fnmuls",  0xee200a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fnmacs",  0xee000a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-  {"fnmscs",  0xee100a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
+  /* Basic instruction code.  */
+  unsigned int avalue : 28;
 
 
-  /* Comparisons.  */
-  {"fcmps",   0xeeb40a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fcmpzs",  0xeeb50a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_compare_z},
-  {"fcmpes",  0xeeb40ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-  {"fcmpezs", 0xeeb50ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_compare_z},
+  /* Thumb-format instruction code.  */
+  unsigned int tvalue;
 
 
-  /* VFP V1 (Double precision).  */
-  /* Moves and type conversions.  */
-  {"fcpyd",   0xeeb00b40, 5, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-  {"fcvtds",  0xeeb70ac0, 6, FPU_VFP_EXT_V1,   do_vfp_dp_sp_cvt},
-  {"fcvtsd",  0xeeb70bc0, 6, FPU_VFP_EXT_V1,   do_vfp_sp_dp_cvt},
-  {"fmdhr",   0xee200b10, 5, FPU_VFP_EXT_V1,   do_vfp_dp_from_reg},
-  {"fmdlr",   0xee000b10, 5, FPU_VFP_EXT_V1,   do_vfp_dp_from_reg},
-  {"fmrdh",   0xee300b10, 5, FPU_VFP_EXT_V1,   do_vfp_reg_from_dp},
-  {"fmrdl",   0xee100b10, 5, FPU_VFP_EXT_V1,   do_vfp_reg_from_dp},
-  {"fsitod",  0xeeb80bc0, 6, FPU_VFP_EXT_V1,   do_vfp_dp_sp_cvt},
-  {"fuitod",  0xeeb80b40, 6, FPU_VFP_EXT_V1,   do_vfp_dp_sp_cvt},
-  {"ftosid",  0xeebd0b40, 6, FPU_VFP_EXT_V1,   do_vfp_sp_dp_cvt},
-  {"ftosizd", 0xeebd0bc0, 7, FPU_VFP_EXT_V1,   do_vfp_sp_dp_cvt},
-  {"ftouid",  0xeebc0b40, 6, FPU_VFP_EXT_V1,   do_vfp_sp_dp_cvt},
-  {"ftouizd", 0xeebc0bc0, 7, FPU_VFP_EXT_V1,   do_vfp_sp_dp_cvt},
-
-  /* Memory operations.  */
-  {"fldd",    0xed100b00, 4, FPU_VFP_EXT_V1,   do_vfp_dp_ldst},
-  {"fstd",    0xed000b00, 4, FPU_VFP_EXT_V1,   do_vfp_dp_ldst},
-  {"fldmiad", 0xec900b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmia},
-  {"fldmfdd", 0xec900b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmia},
-  {"fldmdbd", 0xed300b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmdb},
-  {"fldmead", 0xed300b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmdb},
-  {"fstmiad", 0xec800b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmia},
-  {"fstmead", 0xec800b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmia},
-  {"fstmdbd", 0xed200b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmdb},
-  {"fstmfdd", 0xed200b00, 7, FPU_VFP_EXT_V1,   do_vfp_dp_ldstmdb},
+  /* Which architecture variant provides this instruction.  */
+  unsigned long avariant;
+  unsigned long tvariant;
 
 
-  /* Monadic operations.  */
-  {"fabsd",   0xeeb00bc0, 5, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-  {"fnegd",   0xeeb10b40, 5, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-  {"fsqrtd",  0xeeb10bc0, 6, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-
-  /* Dyadic operations.  */
-  {"faddd",   0xee300b00, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fsubd",   0xee300b40, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fmuld",   0xee200b00, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fdivd",   0xee800b00, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fmacd",   0xee000b00, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fmscd",   0xee100b00, 5, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fnmuld",  0xee200b40, 6, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fnmacd",  0xee000b40, 6, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
-  {"fnmscd",  0xee100b40, 6, FPU_VFP_EXT_V1,   do_vfp_dp_dyadic},
+  /* Function to call to encode instruction in ARM format.  */
+  void (* aencode) (void);
 
 
-  /* Comparisons.  */
-  {"fcmpd",   0xeeb40b40, 5, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-  {"fcmpzd",  0xeeb50b40, 6, FPU_VFP_EXT_V1,   do_vfp_dp_compare_z},
-  {"fcmped",  0xeeb40bc0, 6, FPU_VFP_EXT_V1,   do_vfp_dp_monadic},
-  {"fcmpezd", 0xeeb50bc0, 7, FPU_VFP_EXT_V1,   do_vfp_dp_compare_z},
-
-  /* VFP V2.  */
-  {"fmsrr",   0xec400a10, 5, FPU_VFP_EXT_V2,   do_vfp_sp_reg2},
-  {"fmrrs",   0xec500a10, 5, FPU_VFP_EXT_V2,   do_vfp_sp_reg2},
-  {"fmdrr",   0xec400b10, 5, FPU_VFP_EXT_V2,   do_vfp_dp_from_reg2},
-  {"fmrrd",   0xec500b10, 5, FPU_VFP_EXT_V2,   do_vfp_reg2_from_dp},
-
-  /* Intel XScale extensions to ARM V5 ISA.  (All use CP0).  */
-  {"mia",        0xee200010, 3,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"miaph",      0xee280010, 5,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"miabb",      0xee2c0010, 5,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"miabt",      0xee2d0010, 5,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"miatb",      0xee2e0010, 5,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"miatt",      0xee2f0010, 5,  ARM_CEXT_XSCALE,   do_xsc_mia},
-  {"mar",        0xec400000, 3,  ARM_CEXT_XSCALE,   do_xsc_mar},
-  {"mra",        0xec500000, 3,  ARM_CEXT_XSCALE,   do_xsc_mra},
-
-  /* Cirrus Maverick instructions.  */
-  {"cfldrs",     0xec100400, 6,  ARM_CEXT_MAVERICK, do_mav_ldst_1},
-  {"cfldrd",     0xec500400, 6,  ARM_CEXT_MAVERICK, do_mav_ldst_2},
-  {"cfldr32",    0xec100500, 7,  ARM_CEXT_MAVERICK, do_mav_ldst_3},
-  {"cfldr64",    0xec500500, 7,  ARM_CEXT_MAVERICK, do_mav_ldst_4},
-  {"cfstrs",     0xec000400, 6,  ARM_CEXT_MAVERICK, do_mav_ldst_1},
-  {"cfstrd",     0xec400400, 6,  ARM_CEXT_MAVERICK, do_mav_ldst_2},
-  {"cfstr32",    0xec000500, 7,  ARM_CEXT_MAVERICK, do_mav_ldst_3},
-  {"cfstr64",    0xec400500, 7,  ARM_CEXT_MAVERICK, do_mav_ldst_4},
-  {"cfmvsr",     0xee000450, 6,  ARM_CEXT_MAVERICK, do_mav_binops_2a},
-  {"cfmvrs",     0xee100450, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1a},
-  {"cfmvdlr",    0xee000410, 7,  ARM_CEXT_MAVERICK, do_mav_binops_2b},
-  {"cfmvrdl",    0xee100410, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1b},
-  {"cfmvdhr",    0xee000430, 7,  ARM_CEXT_MAVERICK, do_mav_binops_2b},
-  {"cfmvrdh",    0xee100430, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1b},
-  {"cfmv64lr",   0xee000510, 8,  ARM_CEXT_MAVERICK, do_mav_binops_2c},
-  {"cfmvr64l",   0xee100510, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1c},
-  {"cfmv64hr",   0xee000530, 8,  ARM_CEXT_MAVERICK, do_mav_binops_2c},
-  {"cfmvr64h",   0xee100530, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1c},
-  {"cfmval32",   0xee100610, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3a},
-  {"cfmv32al",   0xee000610, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3b},
-  {"cfmvam32",   0xee100630, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3a},
-  {"cfmv32am",   0xee000630, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3b},
-  {"cfmvah32",   0xee100650, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3a},
-  {"cfmv32ah",   0xee000650, 8,  ARM_CEXT_MAVERICK, do_mav_binops_3b},
-  {"cfmva32",    0xee100670, 7,  ARM_CEXT_MAVERICK, do_mav_binops_3a},
-  {"cfmv32a",    0xee000670, 7,  ARM_CEXT_MAVERICK, do_mav_binops_3b},
-  {"cfmva64",    0xee100690, 7,  ARM_CEXT_MAVERICK, do_mav_binops_3c},
-  {"cfmv64a",    0xee000690, 7,  ARM_CEXT_MAVERICK, do_mav_binops_3d},
-  {"cfmvsc32",   0xee1006b0, 8,  ARM_CEXT_MAVERICK, do_mav_dspsc_1},
-  {"cfmv32sc",   0xee0006b0, 8,  ARM_CEXT_MAVERICK, do_mav_dspsc_2},
-  {"cfcpys",     0xee000400, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1d},
-  {"cfcpyd",     0xee000420, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1e},
-  {"cfcvtsd",    0xee000460, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1f},
-  {"cfcvtds",    0xee000440, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1g},
-  {"cfcvt32s",   0xee000480, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1h},
-  {"cfcvt32d",   0xee0004a0, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1i},
-  {"cfcvt64s",   0xee0004c0, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1j},
-  {"cfcvt64d",   0xee0004e0, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1k},
-  {"cfcvts32",   0xee100580, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1l},
-  {"cfcvtd32",   0xee1005a0, 8,  ARM_CEXT_MAVERICK, do_mav_binops_1m},
-  {"cftruncs32", 0xee1005c0, 10, ARM_CEXT_MAVERICK, do_mav_binops_1l},
-  {"cftruncd32", 0xee1005e0, 10, ARM_CEXT_MAVERICK, do_mav_binops_1m},
-  {"cfrshl32",   0xee000550, 8,  ARM_CEXT_MAVERICK, do_mav_triple_4a},
-  {"cfrshl64",   0xee000570, 8,  ARM_CEXT_MAVERICK, do_mav_triple_4b},
-  {"cfsh32",     0xee000500, 6,  ARM_CEXT_MAVERICK, do_mav_shift_1},
-  {"cfsh64",     0xee200500, 6,  ARM_CEXT_MAVERICK, do_mav_shift_2},
-  {"cfcmps",     0xee100490, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5a},
-  {"cfcmpd",     0xee1004b0, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5b},
-  {"cfcmp32",    0xee100590, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5c},
-  {"cfcmp64",    0xee1005b0, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5d},
-  {"cfabss",     0xee300400, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1d},
-  {"cfabsd",     0xee300420, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1e},
-  {"cfnegs",     0xee300440, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1d},
-  {"cfnegd",     0xee300460, 6,  ARM_CEXT_MAVERICK, do_mav_binops_1e},
-  {"cfadds",     0xee300480, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5e},
-  {"cfaddd",     0xee3004a0, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5f},
-  {"cfsubs",     0xee3004c0, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5e},
-  {"cfsubd",     0xee3004e0, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5f},
-  {"cfmuls",     0xee100400, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5e},
-  {"cfmuld",     0xee100420, 6,  ARM_CEXT_MAVERICK, do_mav_triple_5f},
-  {"cfabs32",    0xee300500, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1n},
-  {"cfabs64",    0xee300520, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1o},
-  {"cfneg32",    0xee300540, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1n},
-  {"cfneg64",    0xee300560, 7,  ARM_CEXT_MAVERICK, do_mav_binops_1o},
-  {"cfadd32",    0xee300580, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5g},
-  {"cfadd64",    0xee3005a0, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5h},
-  {"cfsub32",    0xee3005c0, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5g},
-  {"cfsub64",    0xee3005e0, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5h},
-  {"cfmul32",    0xee100500, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5g},
-  {"cfmul64",    0xee100520, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5h},
-  {"cfmac32",    0xee100540, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5g},
-  {"cfmsc32",    0xee100560, 7,  ARM_CEXT_MAVERICK, do_mav_triple_5g},
-  {"cfmadd32",   0xee000600, 8,  ARM_CEXT_MAVERICK, do_mav_quad_6a},
-  {"cfmsub32",   0xee100600, 8,  ARM_CEXT_MAVERICK, do_mav_quad_6a},
-  {"cfmadda32",  0xee200600, 9,  ARM_CEXT_MAVERICK, do_mav_quad_6b},
-  {"cfmsuba32",  0xee300600, 9,  ARM_CEXT_MAVERICK, do_mav_quad_6b},
+  /* Function to call to encode instruction in Thumb format.  */
+  void (* tencode) (void);
 };
 
 /* Defines for various bits that we will want to toggle.  */
 #define INST_IMMEDIATE 0x02000000
 #define OFFSET_REG     0x02000000
 };
 
 /* Defines for various bits that we will want to toggle.  */
 #define INST_IMMEDIATE 0x02000000
 #define OFFSET_REG     0x02000000
-#define HWOFFSET_IMM    0x00400000
+#define HWOFFSET_IMM   0x00400000
 #define SHIFT_BY_REG   0x00000010
 #define PRE_INDEX      0x01000000
 #define INDEX_UP       0x00800000
 #define SHIFT_BY_REG   0x00000010
 #define PRE_INDEX      0x01000000
 #define INDEX_UP       0x00800000
@@ -1841,36 +434,6 @@ static const struct asm_opcode insns[] =
 #define OPCODE_BIC     14
 #define OPCODE_MVN     15
 
 #define OPCODE_BIC     14
 #define OPCODE_MVN     15
 
-/* Thumb v1 (ARMv4T).  */
-static void do_t_nop           PARAMS ((char *));
-static void do_t_arit          PARAMS ((char *));
-static void do_t_add           PARAMS ((char *));
-static void do_t_asr           PARAMS ((char *));
-static void do_t_branch9       PARAMS ((char *));
-static void do_t_branch12      PARAMS ((char *));
-static void do_t_branch23      PARAMS ((char *));
-static void do_t_bx            PARAMS ((char *));
-static void do_t_compare       PARAMS ((char *));
-static void do_t_ldmstm                PARAMS ((char *));
-static void do_t_ldr           PARAMS ((char *));
-static void do_t_ldrb          PARAMS ((char *));
-static void do_t_ldrh          PARAMS ((char *));
-static void do_t_lds           PARAMS ((char *));
-static void do_t_lsl           PARAMS ((char *));
-static void do_t_lsr           PARAMS ((char *));
-static void do_t_mov           PARAMS ((char *));
-static void do_t_push_pop      PARAMS ((char *));
-static void do_t_str           PARAMS ((char *));
-static void do_t_strb          PARAMS ((char *));
-static void do_t_strh          PARAMS ((char *));
-static void do_t_sub           PARAMS ((char *));
-static void do_t_swi           PARAMS ((char *));
-static void do_t_adr           PARAMS ((char *));
-
-/* Thumb v2 (ARMv5T).  */
-static void do_t_blx           PARAMS ((char *));
-static void do_t_bkpt          PARAMS ((char *));
-
 #define T_OPCODE_MUL 0x4340
 #define T_OPCODE_TST 0x4200
 #define T_OPCODE_CMN 0x42c0
 #define T_OPCODE_MUL 0x4340
 #define T_OPCODE_TST 0x4200
 #define T_OPCODE_CMN 0x42c0
@@ -1891,7 +454,8 @@ static void do_t_bkpt              PARAMS ((char *));
 
 #define T_OPCODE_ASR_R 0x4100
 #define T_OPCODE_LSL_R 0x4080
 
 #define T_OPCODE_ASR_R 0x4100
 #define T_OPCODE_LSL_R 0x4080
-#define T_OPCODE_LSR_R  0x40c0
+#define T_OPCODE_LSR_R 0x40c0
+#define T_OPCODE_ROR_R 0x41c0
 #define T_OPCODE_ASR_I 0x1000
 #define T_OPCODE_LSL_I 0x0000
 #define T_OPCODE_LSR_I 0x0800
 #define T_OPCODE_ASR_I 0x1000
 #define T_OPCODE_LSL_I 0x0000
 #define T_OPCODE_LSR_I 0x0800
@@ -1921,186 +485,25 @@ static void do_t_bkpt           PARAMS ((char *));
 #define T_OPCODE_PUSH  0xb400
 #define T_OPCODE_POP   0xbc00
 
 #define T_OPCODE_PUSH  0xb400
 #define T_OPCODE_POP   0xbc00
 
-#define T_OPCODE_BRANCH 0xe7fe
-
-static int thumb_reg           PARAMS ((char ** str, int hi_lo));
+#define T_OPCODE_BRANCH 0xe000
 
 #define THUMB_SIZE     2       /* Size of thumb instruction.  */
 
 #define THUMB_SIZE     2       /* Size of thumb instruction.  */
-#define THUMB_REG_LO   0x1
-#define THUMB_REG_HI   0x2
-#define THUMB_REG_ANY  0x3
-
-#define THUMB_H1       0x0080
-#define THUMB_H2       0x0040
-
-#define THUMB_ASR 0
-#define THUMB_LSL 1
-#define THUMB_LSR 2
-
-#define THUMB_MOVE 0
-#define THUMB_COMPARE 1
-
-#define THUMB_LOAD 0
-#define THUMB_STORE 1
-
 #define THUMB_PP_PC_LR 0x0100
 #define THUMB_PP_PC_LR 0x0100
-
-/* These three are used for immediate shifts, do not alter.  */
-#define THUMB_WORD 2
-#define THUMB_HALFWORD 1
-#define THUMB_BYTE 0
-
-struct thumb_opcode
-{
-  /* Basic string to match.  */
-  const char * template;
-
-  /* Basic instruction code.  */
-  unsigned long value;
-
-  int size;
-
-  /* Which CPU variants this exists for.  */
-  unsigned long variant;
-
-  /* Function to call to parse args.  */
-  void (* parms) PARAMS ((char *));
-};
-
-static const struct thumb_opcode tinsns[] =
-{
-  /* Thumb v1 (ARMv4T).  */
-  {"adc",      0x4140,         2,      ARM_EXT_V4T, do_t_arit},
-  {"add",      0x0000,         2,      ARM_EXT_V4T, do_t_add},
-  {"and",      0x4000,         2,      ARM_EXT_V4T, do_t_arit},
-  {"asr",      0x0000,         2,      ARM_EXT_V4T, do_t_asr},
-  {"b",                T_OPCODE_BRANCH, 2,     ARM_EXT_V4T, do_t_branch12},
-  {"beq",      0xd0fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bne",      0xd1fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bcs",      0xd2fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bhs",      0xd2fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bcc",      0xd3fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bul",      0xd3fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"blo",      0xd3fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bmi",      0xd4fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bpl",      0xd5fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bvs",      0xd6fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bvc",      0xd7fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bhi",      0xd8fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bls",      0xd9fe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bge",      0xdafe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"blt",      0xdbfe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bgt",      0xdcfe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"ble",      0xddfe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bal",      0xdefe,         2,      ARM_EXT_V4T, do_t_branch9},
-  {"bic",      0x4380,         2,      ARM_EXT_V4T, do_t_arit},
-  {"bl",       0xf7fffffe,     4,      ARM_EXT_V4T, do_t_branch23},
-  {"bx",       0x4700,         2,      ARM_EXT_V4T, do_t_bx},
-  {"cmn",      T_OPCODE_CMN,   2,      ARM_EXT_V4T, do_t_arit},
-  {"cmp",      0x0000,         2,      ARM_EXT_V4T, do_t_compare},
-  {"eor",      0x4040,         2,      ARM_EXT_V4T, do_t_arit},
-  {"ldmia",    0xc800,         2,      ARM_EXT_V4T, do_t_ldmstm},
-  {"ldr",      0x0000,         2,      ARM_EXT_V4T, do_t_ldr},
-  {"ldrb",     0x0000,         2,      ARM_EXT_V4T, do_t_ldrb},
-  {"ldrh",     0x0000,         2,      ARM_EXT_V4T, do_t_ldrh},
-  {"ldrsb",    0x5600,         2,      ARM_EXT_V4T, do_t_lds},
-  {"ldrsh",    0x5e00,         2,      ARM_EXT_V4T, do_t_lds},
-  {"ldsb",     0x5600,         2,      ARM_EXT_V4T, do_t_lds},
-  {"ldsh",     0x5e00,         2,      ARM_EXT_V4T, do_t_lds},
-  {"lsl",      0x0000,         2,      ARM_EXT_V4T, do_t_lsl},
-  {"lsr",      0x0000,         2,      ARM_EXT_V4T, do_t_lsr},
-  {"mov",      0x0000,         2,      ARM_EXT_V4T, do_t_mov},
-  {"mul",      T_OPCODE_MUL,   2,      ARM_EXT_V4T, do_t_arit},
-  {"mvn",      T_OPCODE_MVN,   2,      ARM_EXT_V4T, do_t_arit},
-  {"neg",      T_OPCODE_NEG,   2,      ARM_EXT_V4T, do_t_arit},
-  {"orr",      0x4300,         2,      ARM_EXT_V4T, do_t_arit},
-  {"pop",      0xbc00,         2,      ARM_EXT_V4T, do_t_push_pop},
-  {"push",     0xb400,         2,      ARM_EXT_V4T, do_t_push_pop},
-  {"ror",      0x41c0,         2,      ARM_EXT_V4T, do_t_arit},
-  {"sbc",      0x4180,         2,      ARM_EXT_V4T, do_t_arit},
-  {"stmia",    0xc000,         2,      ARM_EXT_V4T, do_t_ldmstm},
-  {"str",      0x0000,         2,      ARM_EXT_V4T, do_t_str},
-  {"strb",     0x0000,         2,      ARM_EXT_V4T, do_t_strb},
-  {"strh",     0x0000,         2,      ARM_EXT_V4T, do_t_strh},
-  {"swi",      0xdf00,         2,      ARM_EXT_V4T, do_t_swi},
-  {"sub",      0x0000,         2,      ARM_EXT_V4T, do_t_sub},
-  {"tst",      T_OPCODE_TST,   2,      ARM_EXT_V4T, do_t_arit},
-  /* Pseudo ops:  */
-  {"adr",       0x0000,         2,      ARM_EXT_V4T, do_t_adr},
-  {"nop",       0x46C0,         2,      ARM_EXT_V4T, do_t_nop},      /* mov r8,r8  */
-  /* Thumb v2 (ARMv5T).  */
-  {"blx",      0,              0,      ARM_EXT_V5T, do_t_blx},
-  {"bkpt",     0xbe00,         2,      ARM_EXT_V5T, do_t_bkpt},
-};
-
-#define BAD_ARGS       _("bad arguments to instruction")
-#define BAD_PC                 _("r15 not allowed here")
-#define BAD_COND       _("instruction is not conditional")
-#define ERR_NO_ACCUM   _("acc0 expected")
-
-static struct hash_control * arm_ops_hsh   = NULL;
-static struct hash_control * arm_tops_hsh  = NULL;
-static struct hash_control * arm_cond_hsh  = NULL;
-static struct hash_control * arm_shift_hsh = NULL;
-static struct hash_control * arm_psr_hsh   = NULL;
-
-/* This table describes all the machine specific pseudo-ops the assembler
-   has to support.  The fields are:
-     pseudo-op name without dot
-     function to call to execute this pseudo-op
-     Integer arg to pass to the function.  */
-
-static void s_req PARAMS ((int));
-static void s_align PARAMS ((int));
-static void s_bss PARAMS ((int));
-static void s_even PARAMS ((int));
-static void s_ltorg PARAMS ((int));
-static void s_arm PARAMS ((int));
-static void s_thumb PARAMS ((int));
-static void s_code PARAMS ((int));
-static void s_force_thumb PARAMS ((int));
-static void s_thumb_func PARAMS ((int));
-static void s_thumb_set PARAMS ((int));
-#ifdef OBJ_ELF
-static void s_arm_elf_cons PARAMS ((int));
-#endif
-
-static int my_get_expression PARAMS ((expressionS *, char **));
-
-const pseudo_typeS md_pseudo_table[] =
-{
-  /* Never called becasue '.req' does not start line.  */
-  { "req",         s_req,         0 },
-  { "bss",         s_bss,         0 },
-  { "align",       s_align,       0 },
-  { "arm",         s_arm,         0 },
-  { "thumb",       s_thumb,       0 },
-  { "code",        s_code,        0 },
-  { "force_thumb", s_force_thumb, 0 },
-  { "thumb_func",  s_thumb_func,  0 },
-  { "thumb_set",   s_thumb_set,   0 },
-  { "even",        s_even,        0 },
-  { "ltorg",       s_ltorg,       0 },
-  { "pool",        s_ltorg,       0 },
-#ifdef OBJ_ELF
-  { "word",        s_arm_elf_cons, 4 },
-  { "long",        s_arm_elf_cons, 4 },
-  { "file",        (void (*) PARAMS ((int))) dwarf2_directive_file, 0 },
-  { "loc",         dwarf2_directive_loc,  0 },
-#else
-  { "word",        cons, 4},
-#endif
-  { "extend",      float_cons, 'x' },
-  { "ldouble",     float_cons, 'x' },
-  { "packed",      float_cons, 'p' },
-  { 0, 0, 0 }
-};
-
-/* Other internal functions.  */
-static int arm_parse_extension PARAMS ((char *, int *));
-static int arm_parse_cpu PARAMS ((char *));
-static int arm_parse_arch PARAMS ((char *));
-static int arm_parse_fpu PARAMS ((char *));
+#define THUMB_LOAD_BIT 0x0800
+
+#define BAD_ARGS       _("bad arguments to instruction")
+#define BAD_PC         _("r15 not allowed here")
+#define BAD_COND       _("instruction cannot be conditional")
+#define BAD_OVERLAP    _("registers may not be the same")
+#define BAD_HIREG      _("lo register required")
+#define BAD_THUMB32    _("instruction not supported in Thumb16 mode")
+
+static struct hash_control *arm_ops_hsh;
+static struct hash_control *arm_cond_hsh;
+static struct hash_control *arm_shift_hsh;
+static struct hash_control *arm_psr_hsh;
+static struct hash_control *arm_reg_hsh;
+static struct hash_control *arm_reloc_hsh;
 
 /* Stuff needed to resolve the label ambiguity
    As:
 
 /* Stuff needed to resolve the label ambiguity
    As:
@@ -2109,2288 +512,2395 @@ static int arm_parse_fpu PARAMS ((char *));
    may differ from:
      ...
      label:
    may differ from:
      ...
      label:
-              <insn>
+             <insn>
 */
 
 symbolS *  last_label_seen;
 static int label_is_thumb_function_name = FALSE;
 */
 
 symbolS *  last_label_seen;
 static int label_is_thumb_function_name = FALSE;
-
-/* Literal Pool stuff.  */
-
-#define MAX_LITERAL_POOL_SIZE 1024
-
+\f
 /* Literal pool structure.  Held on a per-section
    and per-sub-section basis.  */
 /* Literal pool structure.  Held on a per-section
    and per-sub-section basis.  */
+
+#define MAX_LITERAL_POOL_SIZE 1024
 typedef struct literal_pool
 {
 typedef struct literal_pool
 {
-  expressionS    literals [MAX_LITERAL_POOL_SIZE];
-  unsigned int   next_free_entry;
-  unsigned int   id;
-  symbolS *      symbol;
-  segT           section;
-  subsegT        sub_section;
+  expressionS   literals [MAX_LITERAL_POOL_SIZE];
+  unsigned int  next_free_entry;
+  unsigned int  id;
+  symbolS *     symbol;
+  segT          section;
+  subsegT       sub_section;
   struct literal_pool * next;
 } literal_pool;
 
 /* Pointer to a linked list of literal pools.  */
 literal_pool * list_of_pools = NULL;
 
   struct literal_pool * next;
 } literal_pool;
 
 /* Pointer to a linked list of literal pools.  */
 literal_pool * list_of_pools = NULL;
 
-static literal_pool * find_literal_pool PARAMS ((void));
-static literal_pool * find_or_make_literal_pool PARAMS ((void));
-
-static literal_pool *
-find_literal_pool ()
-{
-  literal_pool * pool;
+/* State variables for IT block handling.  */
+static bfd_boolean current_it_mask = 0;
+static int current_cc;
 
 
-  for (pool = list_of_pools; pool != NULL; pool = pool->next)
-    {
-      if (pool->section == now_seg
-         && pool->sub_section == now_subseg)
-       break;
-    }
+\f
+/* Pure syntax.         */
 
 
-  return pool;
-}
+/* This array holds the chars that always start a comment.  If the
+   pre-processor is disabled, these aren't very useful.         */
+const char comment_chars[] = "@";
 
 
-static literal_pool *
-find_or_make_literal_pool ()
-{
-  /* Next literal pool ID number.  */
-  static unsigned int latest_pool_num = 1;
-  literal_pool *      pool;
+/* This array holds the chars that only start a comment at the beginning of
+   a line.  If the line seems to have the form '# 123 filename'
+   .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+   first line of the input file.  This is because the compiler outputs
+   #NO_APP at the beginning of its output.  */
+/* Also note that comments like this one will always work.  */
+const char line_comment_chars[] = "#";
 
 
-  pool = find_literal_pool ();
+const char line_separator_chars[] = ";";
 
 
-  if (pool == NULL)
-    {
-      /* Create a new pool.  */
-      pool = (literal_pool *) xmalloc (sizeof (* pool));
-      if (! pool)
-       return NULL;
+/* Chars that can be used to separate mant
+   from exp in floating point numbers. */
+const char EXP_CHARS[] = "eE";
 
 
-      pool->next_free_entry = 0;
-      pool->section         = now_seg;
-      pool->sub_section     = now_subseg;
-      pool->next            = list_of_pools;
-      pool->symbol          = NULL;
+/* Chars that mean this number is a floating point constant.  */
+/* As in 0f12.456  */
+/* or   0d1.2345e12  */
 
 
-      /* Add it to the list.  */
-      list_of_pools = pool;
-    }
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
 
 
-  /* New pools, and emptied pools, will have a NULL symbol.  */
-  if (pool->symbol == NULL)
+/* Prefix characters that indicate the start of an immediate
+   value.  */
+#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
+
+/* Separator character handling.  */
+
+#define skip_whitespace(str)  do { if (*(str) == ' ') ++(str); } while (0)
+
+static inline int
+skip_past_char (char ** str, char c)
+{
+  if (**str == c)
     {
     {
-      pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
-                                   (valueT) 0, &zero_address_frag);
-      pool->id = latest_pool_num ++;
+      (*str)++;
+      return SUCCESS;
     }
     }
-
-  /* Done.  */
-  return pool;
+  else
+    return FAIL;
 }
 }
+#define skip_past_comma(str) skip_past_char (str, ',')
+
+/* Arithmetic expressions (possibly involving symbols).         */
+
+/* Return TRUE if anything in the expression is a bignum.  */
 
 
-/* Add the literal in the global 'inst'
-   structure to the relevent literal pool.  */
 static int
 static int
-add_to_lit_pool ()
+walk_no_bignums (symbolS * sp)
 {
 {
-  literal_pool * pool;
-  unsigned int entry;
-
-  pool = find_or_make_literal_pool ();
+  if (symbol_get_value_expression (sp)->X_op == O_big)
+    return 1;
 
 
-  /* Check if this literal value is already in the pool.  */
-  for (entry = 0; entry < pool->next_free_entry; entry ++)
+  if (symbol_get_value_expression (sp)->X_add_symbol)
     {
     {
-      if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
-         && (inst.reloc.exp.X_op == O_constant)
-         && (pool->literals[entry].X_add_number
-             == inst.reloc.exp.X_add_number)
-         && (pool->literals[entry].X_unsigned
-             == inst.reloc.exp.X_unsigned))
-       break;
-
-      if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
-          && (inst.reloc.exp.X_op == O_symbol)
-          && (pool->literals[entry].X_add_number
-             == inst.reloc.exp.X_add_number)
-          && (pool->literals[entry].X_add_symbol
-             == inst.reloc.exp.X_add_symbol)
-          && (pool->literals[entry].X_op_symbol
-             == inst.reloc.exp.X_op_symbol))
-        break;
+      return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
+             || (symbol_get_value_expression (sp)->X_op_symbol
+                 && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
     }
 
     }
 
-  /* Do we need to create a new entry?  */
-  if (entry == pool->next_free_entry)
+  return 0;
+}
+
+static int in_my_get_expression = 0;
+
+/* Third argument to my_get_expression.         */
+#define GE_NO_PREFIX 0
+#define GE_IMM_PREFIX 1
+#define GE_OPT_PREFIX 2
+
+static int
+my_get_expression (expressionS * ep, char ** str, int prefix_mode)
+{
+  char * save_in;
+  segT  seg;
+
+  /* In unified syntax, all prefixes are optional.  */
+  if (unified_syntax)
+    prefix_mode = GE_OPT_PREFIX;
+
+  switch (prefix_mode)
     {
     {
-      if (entry >= MAX_LITERAL_POOL_SIZE)
+    case GE_NO_PREFIX: break;
+    case GE_IMM_PREFIX:
+      if (!is_immediate_prefix (**str))
        {
        {
-         inst.error = _("literal pool overflow");
+         inst.error = _("immediate expression requires a # prefix");
          return FAIL;
        }
          return FAIL;
        }
-
-      pool->literals[entry] = inst.reloc.exp;
-      pool->next_free_entry += 1;
+      (*str)++;
+      break;
+    case GE_OPT_PREFIX:
+      if (is_immediate_prefix (**str))
+       (*str)++;
+      break;
+    default: abort ();
     }
 
     }
 
-  inst.reloc.exp.X_op         = O_symbol;
-  inst.reloc.exp.X_add_number = ((int) entry) * 4 - 8;
-  inst.reloc.exp.X_add_symbol = pool->symbol;
-
-  return SUCCESS;
-}
+  memset (ep, 0, sizeof (expressionS));
 
 
-/* Can't use symbol_new here, so have to create a symbol and then at
-   a later date assign it a value. Thats what these functions do.  */
+  save_in = input_line_pointer;
+  input_line_pointer = *str;
+  in_my_get_expression = 1;
+  seg = expression (ep);
+  in_my_get_expression = 0;
 
 
-static void
-symbol_locate (symbolP, name, segment, valu, frag)
-     symbolS *    symbolP;
-     const char * name;                /* It is copied, the caller can modify.  */
-     segT         segment;     /* Segment identifier (SEG_<something>).  */
-     valueT       valu;                /* Symbol value.  */
-     fragS *      frag;                /* Associated fragment.  */
-{
-  unsigned int name_length;
-  char * preserved_copy_of_name;
+  if (ep->X_op == O_illegal)
+    {
+      /* We found a bad expression in md_operand().  */
+      *str = input_line_pointer;
+      input_line_pointer = save_in;
+      if (inst.error == NULL)
+       inst.error = _("bad expression");
+      return 1;
+    }
 
 
-  name_length = strlen (name) + 1;   /* +1 for \0.  */
-  obstack_grow (&notes, name, name_length);
-  preserved_copy_of_name = obstack_finish (&notes);
-#ifdef STRIP_UNDERSCORE
-  if (preserved_copy_of_name[0] == '_')
-    preserved_copy_of_name++;
+#ifdef OBJ_AOUT
+  if (seg != absolute_section
+      && seg != text_section
+      && seg != data_section
+      && seg != bss_section
+      && seg != undefined_section)
+    {
+      inst.error = _("bad segment");
+      *str = input_line_pointer;
+      input_line_pointer = save_in;
+      return 1;
+    }
 #endif
 
 #endif
 
-#ifdef tc_canonicalize_symbol_name
-  preserved_copy_of_name =
-    tc_canonicalize_symbol_name (preserved_copy_of_name);
-#endif
+  /* Get rid of any bignums now, so that we don't generate an error for which
+     we can't establish a line number later on.         Big numbers are never valid
+     in instructions, which is where this routine is always called.  */
+  if (ep->X_op == O_big
+      || (ep->X_add_symbol
+         && (walk_no_bignums (ep->X_add_symbol)
+             || (ep->X_op_symbol
+                 && walk_no_bignums (ep->X_op_symbol)))))
+    {
+      inst.error = _("invalid constant");
+      *str = input_line_pointer;
+      input_line_pointer = save_in;
+      return 1;
+    }
 
 
-  S_SET_NAME (symbolP, preserved_copy_of_name);
+  *str = input_line_pointer;
+  input_line_pointer = save_in;
+  return 0;
+}
 
 
-  S_SET_SEGMENT (symbolP, segment);
-  S_SET_VALUE (symbolP, valu);
-  symbol_clear_list_pointers (symbolP);
+/* Turn a string in input_line_pointer into a floating point constant
+   of type TYPE, and store the appropriate bytes in *LITP.  The number
+   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
+   returned, or NULL on OK.
 
 
-  symbol_set_frag (symbolP, frag);
+   Note that fp constants aren't represent in the normal way on the ARM.
+   In big endian mode, things are as expected. However, in little endian
+   mode fp constants are big-endian word-wise, and little-endian byte-wise
+   within the words.  For example, (double) 1.1 in big endian mode is
+   the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
+   the byte sequence 99 99 f1 3f 9a 99 99 99.
 
 
-  /* Link to end of symbol chain.  */
-  {
-    extern int symbol_table_frozen;
-    if (symbol_table_frozen)
-      abort ();
-  }
+   ??? The format of 12 byte floats is uncertain according to gcc's arm.h.  */
 
 
-  symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+  int prec;
+  LITTLENUM_TYPE words[MAX_LITTLENUMS];
+  char *t;
+  int i;
 
 
-  obj_symbol_new_hook (symbolP);
+  switch (type)
+    {
+    case 'f':
+    case 'F':
+    case 's':
+    case 'S':
+      prec = 2;
+      break;
 
 
-#ifdef tc_symbol_new_hook
-  tc_symbol_new_hook (symbolP);
-#endif
+    case 'd':
+    case 'D':
+    case 'r':
+    case 'R':
+      prec = 4;
+      break;
 
 
-#ifdef DEBUG_SYMS
-  verify_symbol_chain (symbol_rootP, symbol_lastP);
-#endif /* DEBUG_SYMS  */
-}
+    case 'x':
+    case 'X':
+      prec = 6;
+      break;
 
 
-/* Check that an immediate is valid.
-   If so, convert it to the right format.  */
+    case 'p':
+    case 'P':
+      prec = 6;
+      break;
 
 
-static unsigned int
-validate_immediate (val)
-     unsigned int val;
-{
-  unsigned int a;
-  unsigned int i;
+    default:
+      *sizeP = 0;
+      return _("bad call to MD_ATOF()");
+    }
 
 
-#define rotate_left(v, n) (v << n | v >> (32 - n))
+  t = atof_ieee (input_line_pointer, type, words);
+  if (t)
+    input_line_pointer = t;
+  *sizeP = prec * 2;
 
 
-  for (i = 0; i < 32; i += 2)
-    if ((a = rotate_left (val, i)) <= 0xff)
-      return a | (i << 7); /* 12-bit pack: [shift-cnt,const].  */
+  if (target_big_endian)
+    {
+      for (i = 0; i < prec; i++)
+       {
+         md_number_to_chars (litP, (valueT) words[i], 2);
+         litP += 2;
+       }
+    }
+  else
+    {
+      if (cpu_variant & FPU_ARCH_VFP)
+       for (i = prec - 1; i >= 0; i--)
+         {
+           md_number_to_chars (litP, (valueT) words[i], 2);
+           litP += 2;
+         }
+      else
+       /* For a 4 byte float the order of elements in `words' is 1 0.
+          For an 8 byte float the order is 1 0 3 2.  */
+       for (i = 0; i < prec; i += 2)
+         {
+           md_number_to_chars (litP, (valueT) words[i + 1], 2);
+           md_number_to_chars (litP + 2, (valueT) words[i], 2);
+           litP += 4;
+         }
+    }
 
 
-  return FAIL;
+  return 0;
 }
 
 }
 
-/* Check to see if an immediate can be computed as two seperate immediate
-   values, added together.  We already know that this value cannot be
-   computed by just one ARM instruction.  */
-
-static unsigned int
-validate_immediate_twopart (val, highpart)
-     unsigned int   val;
-     unsigned int * highpart;
+/* We handle all bad expressions here, so that we can report the faulty
+   instruction in the error message.  */
+void
+md_operand (expressionS * expr)
 {
 {
-  unsigned int a;
-  unsigned int i;
+  if (in_my_get_expression)
+    expr->X_op = O_illegal;
+}
 
 
-  for (i = 0; i < 32; i += 2)
-    if (((a = rotate_left (val, i)) & 0xff) != 0)
-      {
-       if (a & 0xff00)
-         {
-           if (a & ~ 0xffff)
-             continue;
-           * highpart = (a  >> 8) | ((i + 24) << 7);
-         }
-       else if (a & 0xff0000)
-         {
-           if (a & 0xff000000)
-             continue;
-           * highpart = (a >> 16) | ((i + 16) << 7);
-         }
-       else
-         {
-           assert (a & 0xff000000);
-           * highpart = (a >> 24) | ((i + 8) << 7);
-         }
-
-       return (a & 0xff) | (i << 7);
-      }
-
-  return FAIL;
-}
+/* Immediate values.  */
 
 
+/* Generic immediate-value read function for use in directives.
+   Accepts anything that 'expression' can fold to a constant.
+   *val receives the number.  */
+#ifdef OBJ_ELF
 static int
 static int
-validate_offset_imm (val, hwse)
-     unsigned int val;
-     int hwse;
+immediate_for_directive (int *val)
 {
 {
-  if ((hwse && val > 255) || val > 4095)
-    return FAIL;
-  return val;
-}
+  expressionS exp;
+  exp.X_op = O_illegal;
 
 
-static void
-s_req (a)
-     int a ATTRIBUTE_UNUSED;
-{
-  as_bad (_("invalid syntax for .req directive"));
-}
+  if (is_immediate_prefix (*input_line_pointer))
+    {
+      input_line_pointer++;
+      expression (&exp);
+    }
 
 
-static void
-s_bss (ignore)
-     int ignore ATTRIBUTE_UNUSED;
-{
-  /* We don't support putting frags in the BSS segment, we fake it by
-     marking in_bss, then looking at s_skip for clues.  */
-  subseg_set (bss_section, 0);
-  demand_empty_rest_of_line ();
+  if (exp.X_op != O_constant)
+    {
+      as_bad (_("expected #constant"));
+      ignore_rest_of_line ();
+      return FAIL;
+    }
+  *val = exp.X_add_number;
+  return SUCCESS;
 }
 }
+#endif
 
 
-static void
-s_even (ignore)
-     int ignore ATTRIBUTE_UNUSED;
-{
-  /* Never make frag if expect extra pass.  */
-  if (!need_pass_2)
-    frag_align (1, 0, 0);
-
-  record_alignment (now_seg, 1);
+/* Register parsing.  */
 
 
-  demand_empty_rest_of_line ();
-}
+/* Generic register parser.  CCP points to what should be the
+   beginning of a register name.  If it is indeed a valid register
+   name, advance CCP over it and return the reg_entry structure;
+   otherwise return NULL.  Does not issue diagnostics. */
 
 
-static void
-s_ltorg (ignored)
-     int ignored ATTRIBUTE_UNUSED;
+static struct reg_entry *
+arm_reg_parse_multi (char **ccp)
 {
 {
-  unsigned int entry;
-  literal_pool * pool;
-  char sym_name[20];
-
-  pool = find_literal_pool ();
-  if (pool == NULL
-      || pool->symbol == NULL
-      || pool->next_free_entry == 0)
-    return;
-
-  /* Align pool as you have word accesses.
-     Only make a frag if we have to.  */
-  if (!need_pass_2)
-    frag_align (2, 0, 0);
-
-  record_alignment (now_seg, 2);
+  char *start = *ccp;
+  char *p;
+  struct reg_entry *reg;
 
 
-  sprintf (sym_name, "$$lit_\002%x", pool->id);
+#ifdef REGISTER_PREFIX
+  if (*start != REGISTER_PREFIX)
+    return FAIL;
+  start++;
+#endif
+#ifdef OPTIONAL_REGISTER_PREFIX
+  if (*start == OPTIONAL_REGISTER_PREFIX)
+    start++;
+#endif
 
 
-  symbol_locate (pool->symbol, sym_name, now_seg,
-                (valueT) frag_now_fix (), frag_now);
-  symbol_table_insert (pool->symbol);
+  p = start;
+  if (!ISALPHA (*p) || !is_name_beginner (*p))
+    return NULL;
 
 
-  ARM_SET_THUMB (pool->symbol, thumb_mode);
+  do
+    p++;
+  while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
 
 
-#if defined OBJ_COFF || defined OBJ_ELF
-  ARM_SET_INTERWORK (pool->symbol, support_interwork);
-#endif
+  reg = (struct reg_entry *) hash_find_n (arm_reg_hsh, start, p - start);
 
 
-  for (entry = 0; entry < pool->next_free_entry; entry ++)
-    /* First output the expression in the instruction to the pool.  */
-    emit_expr (&(pool->literals[entry]), 4); /* .word  */
+  if (!reg)
+    return NULL;
 
 
-  /* Mark the pool as empty.  */
-  pool->next_free_entry = 0;
-  pool->symbol = NULL;
+  *ccp = p;
+  return reg;
 }
 
 }
 
-/* Same as s_align_ptwo but align 0 => align 2.  */
+/* As above, but the register must be of type TYPE, and the return
+   value is the register number or NULL.  */
 
 
-static void
-s_align (unused)
-     int unused ATTRIBUTE_UNUSED;
+static int
+arm_reg_parse (char **ccp, enum arm_reg_type type)
 {
 {
-  register int temp;
-  register long temp_fill;
-  long max_alignment = 15;
+  char *start = *ccp;
+  struct reg_entry *reg = arm_reg_parse_multi (ccp);
 
 
-  temp = get_absolute_expression ();
-  if (temp > max_alignment)
-    as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
-  else if (temp < 0)
-    {
-      as_bad (_("alignment negative. 0 assumed."));
-      temp = 0;
-    }
+  if (reg && reg->type == type)
+    return reg->number;
 
 
-  if (*input_line_pointer == ',')
+  /* Alternative syntaxes are accepted for a few register classes.  */
+  switch (type)
     {
     {
-      input_line_pointer++;
-      temp_fill = get_absolute_expression ();
-    }
-  else
-    temp_fill = 0;
-
-  if (!temp)
-    temp = 2;
-
-  /* Only make a frag if we HAVE to.  */
-  if (temp && !need_pass_2)
-    frag_align (temp, (int) temp_fill, 0);
-  demand_empty_rest_of_line ();
+    case REG_TYPE_MVF:
+    case REG_TYPE_MVD:
+    case REG_TYPE_MVFX:
+    case REG_TYPE_MVDX:
+      /* Generic coprocessor register names are allowed for these.  */
+      if (reg->type == REG_TYPE_CN)
+       return reg->number;
+      break;
 
 
-  record_alignment (now_seg, temp);
-}
+    case REG_TYPE_CP:
+      /* For backward compatibility, a bare number is valid here.  */
+      {
+       unsigned long processor = strtoul (start, ccp, 10);
+       if (*ccp != start && processor <= 15)
+         return processor;
+      }
 
 
-static void
-s_force_thumb (ignore)
-     int ignore ATTRIBUTE_UNUSED;
-{
-  /* If we are not already in thumb mode go into it, EVEN if
-     the target processor does not support thumb instructions.
-     This is used by gcc/config/arm/lib1funcs.asm for example
-     to compile interworking support functions even if the
-     target processor should not support interworking.  */
-  if (! thumb_mode)
-    {
-      thumb_mode = 2;
+    case REG_TYPE_MMXWC:
+      /* WC includes WCG.  ??? I'm not sure this is true for all
+        instructions that take WC registers.  */
+      if (reg->type == REG_TYPE_MMXWCG)
+       return reg->number;
+      break;
 
 
-      record_alignment (now_seg, 1);
+    default:
+      break;
     }
 
     }
 
-  demand_empty_rest_of_line ();
+  *ccp = start;
+  return FAIL;
 }
 
 }
 
-static void
-s_thumb_func (ignore)
-     int ignore ATTRIBUTE_UNUSED;
+/* Parse an ARM register list.  Returns the bitmask, or FAIL.  */
+static long
+parse_reg_list (char ** strp)
 {
 {
-  if (! thumb_mode)
-    opcode_select (16);
-
-  /* The following label is the name/address of the start of a Thumb function.
-     We need to know this for the interworking support.  */
-  label_is_thumb_function_name = TRUE;
+  char * str = * strp;
+  long  range = 0;
+  int   another_range;
 
 
-  demand_empty_rest_of_line ();
-}
+  /* We come back here if we get ranges concatenated by '+' or '|'.  */
+  do
+    {
+      another_range = 0;
 
 
-/* Perform a .set directive, but also mark the alias as
-   being a thumb function.  */
+      if (*str == '{')
+       {
+         int in_range = 0;
+         int cur_reg = -1;
 
 
-static void
-s_thumb_set (equiv)
-     int equiv;
-{
-  /* XXX the following is a duplicate of the code for s_set() in read.c
-     We cannot just call that code as we need to get at the symbol that
-     is created.  */
-  register char *    name;
-  register char      delim;
-  register char *    end_name;
-  register symbolS * symbolP;
+         str++;
+         do
+           {
+             int reg;
 
 
-  /* Especial apologies for the random logic:
-     This just grew, and could be parsed much more simply!
-     Dean - in haste.  */
-  name      = input_line_pointer;
-  delim     = get_symbol_end ();
-  end_name  = input_line_pointer;
-  *end_name = delim;
+             if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+               {
+                 inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+                 return FAIL;
+               }
 
 
-  SKIP_WHITESPACE ();
+             if (in_range)
+               {
+                 int i;
 
 
-  if (*input_line_pointer != ',')
-    {
-      *end_name = 0;
-      as_bad (_("expected comma after name \"%s\""), name);
-      *end_name = delim;
-      ignore_rest_of_line ();
-      return;
-    }
+                 if (reg <= cur_reg)
+                   {
+                     inst.error = _("bad range in register list");
+                     return FAIL;
+                   }
 
 
-  input_line_pointer++;
-  *end_name = 0;
+                 for (i = cur_reg + 1; i < reg; i++)
+                   {
+                     if (range & (1 << i))
+                       as_tsktsk
+                         (_("Warning: duplicated register (r%d) in register list"),
+                          i);
+                     else
+                       range |= 1 << i;
+                   }
+                 in_range = 0;
+               }
 
 
-  if (name[0] == '.' && name[1] == '\0')
-    {
-      /* XXX - this should not happen to .thumb_set.  */
-      abort ();
-    }
+             if (range & (1 << reg))
+               as_tsktsk (_("Warning: duplicated register (r%d) in register list"),
+                          reg);
+             else if (reg <= cur_reg)
+               as_tsktsk (_("Warning: register range not in ascending order"));
 
 
-  if ((symbolP = symbol_find (name)) == NULL
-      && (symbolP = md_undefined_symbol (name)) == NULL)
-    {
-#ifndef NO_LISTING
-      /* When doing symbol listings, play games with dummy fragments living
-        outside the normal fragment chain to record the file and line info
-         for this symbol.  */
-      if (listing & LISTING_SYMBOLS)
-       {
-         extern struct list_info_struct * listing_tail;
-         fragS * dummy_frag = (fragS *) xmalloc (sizeof (fragS));
+             range |= 1 << reg;
+             cur_reg = reg;
+           }
+         while (skip_past_comma (&str) != FAIL
+                || (in_range = 1, *str++ == '-'));
+         str--;
 
 
-         memset (dummy_frag, 0, sizeof (fragS));
-         dummy_frag->fr_type = rs_fill;
-         dummy_frag->line = listing_tail;
-         symbolP = symbol_new (name, undefined_section, 0, dummy_frag);
-         dummy_frag->fr_symbol = symbolP;
+         if (*str++ != '}')
+           {
+             inst.error = _("missing `}'");
+             return FAIL;
+           }
        }
       else
        }
       else
-#endif
-       symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+       {
+         expressionS expr;
 
 
-#ifdef OBJ_COFF
-      /* "set" symbols are local unless otherwise specified.  */
-      SF_SET_LOCAL (symbolP);
-#endif /* OBJ_COFF  */
-    }                          /* Make a new symbol.  */
+         if (my_get_expression (&expr, &str, GE_NO_PREFIX))
+           return FAIL;
 
 
-  symbol_table_insert (symbolP);
+         if (expr.X_op == O_constant)
+           {
+             if (expr.X_add_number
+                 != (expr.X_add_number & 0x0000ffff))
+               {
+                 inst.error = _("invalid register mask");
+                 return FAIL;
+               }
 
 
-  * end_name = delim;
+             if ((range & expr.X_add_number) != 0)
+               {
+                 int regno = range & expr.X_add_number;
 
 
-  if (equiv
-      && S_IS_DEFINED (symbolP)
-      && S_GET_SEGMENT (symbolP) != reg_section)
-    as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+                 regno &= -regno;
+                 regno = (1 << regno) - 1;
+                 as_tsktsk
+                   (_("Warning: duplicated register (r%d) in register list"),
+                    regno);
+               }
 
 
-  pseudo_set (symbolP);
+             range |= expr.X_add_number;
+           }
+         else
+           {
+             if (inst.reloc.type != 0)
+               {
+                 inst.error = _("expression too complex");
+                 return FAIL;
+               }
 
 
-  demand_empty_rest_of_line ();
+             memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
+             inst.reloc.type = BFD_RELOC_ARM_MULTI;
+             inst.reloc.pc_rel = 0;
+           }
+       }
 
 
-  /* XXX Now we come to the Thumb specific bit of code.  */
+      if (*str == '|' || *str == '+')
+       {
+         str++;
+         another_range = 1;
+       }
+    }
+  while (another_range);
 
 
-  THUMB_SET_FUNC (symbolP, 1);
-  ARM_SET_THUMB (symbolP, 1);
-#if defined OBJ_ELF || defined OBJ_COFF
-  ARM_SET_INTERWORK (symbolP, support_interwork);
-#endif
+  *strp = str;
+  return range;
 }
 
 }
 
-static void
-opcode_select (width)
-     int width;
+/* Parse a VFP register list.  If the string is invalid return FAIL.
+   Otherwise return the number of registers, and set PBASE to the first
+   register.  Double precision registers are matched if DP is nonzero. */
+
+static int
+parse_vfp_reg_list (char **str, unsigned int *pbase, int dp)
 {
 {
-  switch (width)
+  int base_reg;
+  int new_base;
+  int regtype;
+  int max_regs;
+  int count = 0;
+  int warned = 0;
+  unsigned long mask = 0;
+  int i;
+
+  if (**str != '{')
+    return FAIL;
+
+  (*str)++;
+
+  if (dp)
     {
     {
-    case 16:
-      if (! thumb_mode)
+      regtype = REG_TYPE_VFD;
+      max_regs = 16;
+    }
+  else
+    {
+      regtype = REG_TYPE_VFS;
+      max_regs = 32;
+    }
+
+  base_reg = max_regs;
+
+  do
+    {
+      new_base = arm_reg_parse (str, regtype);
+      if (new_base == FAIL)
        {
        {
-         if (! (cpu_variant & ARM_EXT_V4T))
-           as_bad (_("selected processor does not support THUMB opcodes"));
+         inst.error = gettext (reg_expected_msgs[regtype]);
+         return FAIL;
+       }
 
 
-         thumb_mode = 1;
-         /* No need to force the alignment, since we will have been
-             coming from ARM mode, which is word-aligned.  */
-         record_alignment (now_seg, 1);
+      if (new_base < base_reg)
+       base_reg = new_base;
+
+      if (mask & (1 << new_base))
+       {
+         inst.error = _("invalid register list");
+         return FAIL;
        }
        }
-      break;
 
 
-    case 32:
-      if (thumb_mode)
+      if ((mask >> new_base) != 0 && ! warned)
        {
        {
-         if ((cpu_variant & ARM_ALL) == ARM_EXT_V4T)
-           as_bad (_("selected processor does not support ARM opcodes"));
+         as_tsktsk (_("register list not in ascending order"));
+         warned = 1;
+       }
 
 
-         thumb_mode = 0;
+      mask |= 1 << new_base;
+      count++;
 
 
-         if (!need_pass_2)
-           frag_align (2, 0, 0);
+      if (**str == '-') /* We have the start of a range expression */
+       {
+         int high_range;
 
 
-         record_alignment (now_seg, 1);
-       }
-      break;
+         (*str)++;
 
 
-    default:
-      as_bad (_("invalid instruction size selected (%d)"), width);
+         if ((high_range = arm_reg_parse (str, regtype)) == FAIL)
+           {
+             inst.error = gettext (reg_expected_msgs[regtype]);
+             return FAIL;
+           }
+
+         if (high_range <= new_base)
+           {
+             inst.error = _("register range not in ascending order");
+             return FAIL;
+           }
+
+         for (new_base++; new_base <= high_range; new_base++)
+           {
+             if (mask & (1 << new_base))
+               {
+                 inst.error = _("invalid register list");
+                 return FAIL;
+               }
+
+             mask |= 1 << new_base;
+             count++;
+           }
+       }
     }
     }
-}
+  while (skip_past_comma (str) != FAIL);
 
 
-static void
-s_arm (ignore)
-     int ignore ATTRIBUTE_UNUSED;
-{
-  opcode_select (32);
-  demand_empty_rest_of_line ();
-}
+  (*str)++;
 
 
-static void
-s_thumb (ignore)
-     int ignore ATTRIBUTE_UNUSED;
-{
-  opcode_select (16);
-  demand_empty_rest_of_line ();
-}
+  /* Sanity check -- should have raised a parse error above.  */
+  if (count == 0 || count > max_regs)
+    abort ();
 
 
-static void
-s_code (unused)
-     int unused ATTRIBUTE_UNUSED;
-{
-  register int temp;
+  *pbase = base_reg;
 
 
-  temp = get_absolute_expression ();
-  switch (temp)
+  /* Final test -- the registers must be consecutive.  */
+  mask >>= base_reg;
+  for (i = 0; i < count; i++)
     {
     {
-    case 16:
-    case 32:
-      opcode_select (temp);
-      break;
-
-    default:
-      as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
+      if ((mask & (1u << i)) == 0)
+       {
+         inst.error = _("non-contiguous register range");
+         return FAIL;
+       }
     }
     }
-}
-
-static void
-end_of_line (str)
-     char *str;
-{
-  skip_whitespace (str);
 
 
-  if (*str != '\0' && !inst.error)
-    inst.error = _("garbage following instruction");
+  return count;
 }
 
 }
 
+/* Parse an explicit relocation suffix on an expression.  This is
+   either nothing, or a word in parentheses.  Note that if !OBJ_ELF,
+   arm_reloc_hsh contains no entries, so this function can only
+   succeed if there is no () after the word.  Returns -1 on error,
+   BFD_RELOC_UNUSED if there wasn't any suffix.         */
 static int
 static int
-skip_past_comma (str)
-     char ** str;
+parse_reloc (char **str)
 {
 {
-  char * p = * str, c;
-  int comma = 0;
+  struct reloc_entry *r;
+  char *p, *q;
 
 
-  while ((c = *p) == ' ' || c == ',')
-    {
-      p++;
-      if (c == ',' && comma++)
-       return FAIL;
-    }
+  if (**str != '(')
+    return BFD_RELOC_UNUSED;
 
 
-  if (c == '\0')
-    return FAIL;
+  p = *str + 1;
+  q = p;
 
 
-  *str = p;
-  return comma ? SUCCESS : FAIL;
+  while (*q && *q != ')' && *q != ',')
+    q++;
+  if (*q != ')')
+    return -1;
+
+  if ((r = hash_find_n (arm_reloc_hsh, p, q - p)) == NULL)
+    return -1;
+
+  *str = q + 1;
+  return r->reloc;
 }
 
 }
 
-/* A standard register must be given at this point.
-   SHIFT is the place to put it in inst.instruction.
-   Restores input start point on error.
-   Returns the reg#, or FAIL.  */
+/* Directives: register aliases.  */
 
 
-static int
-reg_required_here (str, shift)
-     char ** str;
-     int     shift;
+static void
+insert_reg_alias (char *str, int number, int type)
 {
 {
-  static char buff [128]; /* XXX  */
-  int         reg;
-  char *      start = * str;
+  struct reg_entry *new;
+  const char *name;
 
 
-  if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_RN].htab)) != FAIL)
+  if ((new = hash_find (arm_reg_hsh, str)) != 0)
     {
     {
-      if (shift >= 0)
-       inst.instruction |= reg << shift;
-      return reg;
+      if (new->builtin)
+       as_warn (_("ignoring attempt to redefine built-in register '%s'"), str);
+
+      /* Only warn about a redefinition if it's not defined as the
+        same register.  */
+      else if (new->number != number || new->type != type)
+       as_warn (_("ignoring redefinition of register alias '%s'"), str);
+
+      return;
     }
 
     }
 
-  /* Restore the start point, we may have got a reg of the wrong class.  */
-  *str = start;
+  name = xstrdup (str);
+  new = xmalloc (sizeof (struct reg_entry));
 
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  sprintf (buff, _("register expected, not '%.100s'"), start);
-  inst.error = buff;
+  new->name = name;
+  new->number = number;
+  new->type = type;
+  new->builtin = FALSE;
 
 
-  return FAIL;
+  if (hash_insert (arm_reg_hsh, name, (PTR) new))
+    abort ();
 }
 
 }
 
-static const struct asm_psr *
-arm_psr_parse (ccp)
-     register char ** ccp;
+/* Look for the .req directive.         This is of the form:
+
+       new_register_name .req existing_register_name
+
+   If we find one, or if it looks sufficiently like one that we want to
+   handle any error here, return non-zero.  Otherwise return zero.  */
+
+static int
+create_register_alias (char * newname, char *p)
 {
 {
-  char * start = * ccp;
-  char   c;
-  char * p;
-  const struct asm_psr * psr;
+  struct reg_entry *old;
+  char *oldname, *nbuf;
+  size_t nlen;
 
 
-  p = start;
+  /* The input scrubber ensures that whitespace after the mnemonic is
+     collapsed to single spaces.  */
+  oldname = p;
+  if (strncmp (oldname, " .req ", 6) != 0)
+    return 0;
 
 
-  /* Skip to the end of the next word in the input stream.  */
-  do
+  oldname += 6;
+  if (*oldname == '\0')
+    return 0;
+
+  old = hash_find (arm_reg_hsh, oldname);
+  if (!old)
     {
     {
-      c = *p++;
+      as_warn (_("unknown register '%s' -- .req ignored"), oldname);
+      return 1;
     }
     }
-  while (ISALPHA (c) || c == '_');
 
 
-  /* Terminate the word.  */
-  *--p = 0;
-
-  /* CPSR's and SPSR's can now be lowercase.  This is just a convenience
-     feature for ease of use and backwards compatibility.  */
-  if (!strncmp (start, "cpsr", 4))
-    strncpy (start, "CPSR", 4);
-  else if (!strncmp (start, "spsr", 4))
-    strncpy (start, "SPSR", 4);
+  /* If TC_CASE_SENSITIVE is defined, then newname already points to
+     the desired alias name, and p points to its end.  If not, then
+     the desired alias name is in the global original_case_string.  */
+#ifdef TC_CASE_SENSITIVE
+  nlen = p - newname;
+#else
+  newname = original_case_string;
+  nlen = strlen (newname);
+#endif
 
 
-  /* Now locate the word in the psr hash table.  */
-  psr = (const struct asm_psr *) hash_find (arm_psr_hsh, start);
+  nbuf = alloca (nlen + 1);
+  memcpy (nbuf, newname, nlen);
+  nbuf[nlen] = '\0';
 
 
-  /* Restore the input stream.  */
-  *p = c;
+  /* Create aliases under the new name as stated; an all-lowercase
+     version of the new name; and an all-uppercase version of the new
+     name.  */
+  insert_reg_alias (nbuf, old->number, old->type);
 
 
-  /* If we found a valid match, advance the
-     stream pointer past the end of the word.  */
-  *ccp = p;
+  for (p = nbuf; *p; p++)
+    *p = TOUPPER (*p);
 
 
-  return psr;
-}
+  if (strncmp (nbuf, newname, nlen))
+    insert_reg_alias (nbuf, old->number, old->type);
 
 
-/* Parse the input looking for a PSR flag.  */
+  for (p = nbuf; *p; p++)
+    *p = TOLOWER (*p);
 
 
-static int
-psr_required_here (str)
-     char ** str;
-{
-  char * start = * str;
-  const struct asm_psr * psr;
+  if (strncmp (nbuf, newname, nlen))
+    insert_reg_alias (nbuf, old->number, old->type);
 
 
-  psr = arm_psr_parse (str);
+  return 1;
+}
 
 
-  if (psr)
-    {
-      /* If this is the SPSR that is being modified, set the R bit.  */
-      if (! psr->cpsr)
-       inst.instruction |= SPSR_BIT;
+/* Should never be called, as .req goes between the alias and the
+   register name, not at the beginning of the line.  */
+static void
+s_req (int a ATTRIBUTE_UNUSED)
+{
+  as_bad (_("invalid syntax for .req directive"));
+}
 
 
-      /* Set the psr flags in the MSR instruction.  */
-      inst.instruction |= psr->field << PSR_SHIFT;
+/* The .unreq directive deletes an alias which was previously defined
+   by .req.  For example:
 
 
-      return SUCCESS;
-    }
+       my_alias .req r11
+       .unreq my_alias   */
 
 
-  /* In the few cases where we might be able to accept
-     something else this error can be overridden.  */
-  inst.error = _("flag for {c}psr instruction expected");
+static void
+s_unreq (int a ATTRIBUTE_UNUSED)
+{
+  char * name;
+  char saved_char;
 
 
-  /* Restore the start point.  */
-  *str = start;
-  return FAIL;
-}
+  name = input_line_pointer;
 
 
-static int
-co_proc_number (str)
-     char **str;
-{
-  int processor, pchar;
-  char *start;
+  while (*input_line_pointer != 0
+        && *input_line_pointer != ' '
+        && *input_line_pointer != '\n')
+    ++input_line_pointer;
 
 
-  skip_whitespace (*str);
-  start = *str;
+  saved_char = *input_line_pointer;
+  *input_line_pointer = 0;
 
 
-  /* The data sheet seems to imply that just a number on its own is valid
-     here, but the RISC iX assembler seems to accept a prefix 'p'.  We will
-     accept either.  */
-  if ((processor = arm_reg_parse (str, all_reg_maps[REG_TYPE_CP].htab))
-      == FAIL)
+  if (!*name)
+    as_bad (_("invalid syntax for .unreq directive"));
+  else
     {
     {
-      *str = start;
+      struct reg_entry *reg = hash_find (arm_reg_hsh, name);
 
 
-      pchar = *(*str)++;
-      if (pchar >= '0' && pchar <= '9')
-       {
-         processor = pchar - '0';
-         if (**str >= '0' && **str <= '9')
-           {
-             processor = processor * 10 + *(*str)++ - '0';
-             if (processor > 15)
-               {
-                 inst.error = _("illegal co-processor number");
-                 return FAIL;
-               }
-           }
-       }
+      if (!reg)
+       as_bad (_("unknown register alias '%s'"), name);
+      else if (reg->builtin)
+       as_warn (_("ignoring attempt to undefine built-in register '%s'"),
+                name);
       else
        {
       else
        {
-         inst.error = _("bad or missing co-processor number");
-         return FAIL;
+         hash_delete (arm_reg_hsh, name);
+         free ((char *) reg->name);
+         free (reg);
        }
     }
 
        }
     }
 
-  inst.instruction |= processor << 8;
-  return SUCCESS;
+  *input_line_pointer = saved_char;
+  demand_empty_rest_of_line ();
 }
 
 }
 
-static int
-cp_opc_expr (str, where, length)
-     char ** str;
-     int where;
-     int length;
-{
-  expressionS expr;
+/* Directives: Instruction set selection.  */
 
 
-  skip_whitespace (* str);
+#ifdef OBJ_ELF
+/* This code is to handle mapping symbols as defined in the ARM ELF spec.
+   (See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0).
+   Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
+   and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped.  */
 
 
-  memset (&expr, '\0', sizeof (expr));
+static enum mstate mapstate = MAP_UNDEFINED;
 
 
-  if (my_get_expression (&expr, str))
-    return FAIL;
-  if (expr.X_op != O_constant)
-    {
-      inst.error = _("bad or missing expression");
-      return FAIL;
-    }
+static void
+mapping_state (enum mstate state)
+{
+  symbolS * symbolP;
+  const char * symname;
+  int type;
 
 
-  if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number)
-    {
-      inst.error = _("immediate co-processor expression too large");
-      return FAIL;
-    }
+  if (mapstate == state)
+    /* The mapping symbol has already been emitted.
+       There is nothing else to do.  */
+    return;
 
 
-  inst.instruction |= expr.X_add_number << where;
-  return SUCCESS;
-}
-
-static int
-cp_reg_required_here (str, where)
-     char ** str;
-     int     where;
-{
-  int    reg;
-  char * start = *str;
+  mapstate = state;
 
 
-  if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_CN].htab)) != FAIL)
+  switch (state)
     {
     {
-      inst.instruction |= reg << where;
-      return reg;
+    case MAP_DATA:
+      symname = "$d";
+      type = BSF_NO_FLAGS;
+      break;
+    case MAP_ARM:
+      symname = "$a";
+      type = BSF_NO_FLAGS;
+      break;
+    case MAP_THUMB:
+      symname = "$t";
+      type = BSF_NO_FLAGS;
+      break;
+    case MAP_UNDEFINED:
+      return;
+    default:
+      abort ();
     }
 
     }
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  inst.error = _("co-processor register expected");
-
-  /* Restore the start point.  */
-  *str = start;
-  return FAIL;
-}
+  seg_info (now_seg)->tc_segment_info_data.mapstate = state;
 
 
-static int
-fp_reg_required_here (str, where)
-     char ** str;
-     int     where;
-{
-  int    reg;
-  char * start = * str;
+  symbolP = symbol_new (symname, now_seg, (valueT) frag_now_fix (), frag_now);
+  symbol_table_insert (symbolP);
+  symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
 
 
-  if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_FN].htab)) != FAIL)
+  switch (state)
     {
     {
-      inst.instruction |= reg << where;
-      return reg;
-    }
+    case MAP_ARM:
+      THUMB_SET_FUNC (symbolP, 0);
+      ARM_SET_THUMB (symbolP, 0);
+      ARM_SET_INTERWORK (symbolP, support_interwork);
+      break;
 
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  inst.error = _("floating point register expected");
+    case MAP_THUMB:
+      THUMB_SET_FUNC (symbolP, 1);
+      ARM_SET_THUMB (symbolP, 1);
+      ARM_SET_INTERWORK (symbolP, support_interwork);
+      break;
 
 
-  /* Restore the start point.  */
-  *str = start;
-  return FAIL;
+    case MAP_DATA:
+    default:
+      return;
+    }
 }
 }
+#else
+#define mapping_state(x) /* nothing */
+#endif
 
 
-static int
-cp_address_offset (str)
-     char ** str;
+/* Find the real, Thumb encoded start of a Thumb function.  */
+
+static symbolS *
+find_real_start (symbolS * symbolP)
 {
 {
-  int offset;
+  char *       real_start;
+  const char * name = S_GET_NAME (symbolP);
+  symbolS *    new_target;
 
 
-  skip_whitespace (* str);
+  /* This definition must agree with the one in gcc/config/arm/thumb.c.         */
+#define STUB_NAME ".real_start_of"
 
 
-  if (! is_immediate_prefix (**str))
-    {
-      inst.error = _("immediate expression expected");
-      return FAIL;
-    }
+  if (name == NULL)
+    abort ();
 
 
-  (*str)++;
+  /* The compiler may generate BL instructions to local labels because
+     it needs to perform a branch to a far away location. These labels
+     do not have a corresponding ".real_start_of" label.  We check
+     both for S_IS_LOCAL and for a leading dot, to give a way to bypass
+     the ".real_start_of" convention for nonlocal branches.  */
+  if (S_IS_LOCAL (symbolP) || name[0] == '.')
+    return symbolP;
 
 
-  if (my_get_expression (& inst.reloc.exp, str))
-    return FAIL;
+  real_start = ACONCAT ((STUB_NAME, name, NULL));
+  new_target = symbol_find (real_start);
 
 
-  if (inst.reloc.exp.X_op == O_constant)
+  if (new_target == NULL)
     {
     {
-      offset = inst.reloc.exp.X_add_number;
-
-      if (offset & 3)
-       {
-         inst.error = _("co-processor address must be word aligned");
-         return FAIL;
-       }
-
-      if (offset > 1023 || offset < -1023)
-       {
-         inst.error = _("offset too large");
-         return FAIL;
-       }
-
-      if (offset >= 0)
-       inst.instruction |= INDEX_UP;
-      else
-       offset = -offset;
-
-      inst.instruction |= offset >> 2;
+      as_warn ("Failed to find real start of function: %s\n", name);
+      new_target = symbolP;
     }
     }
-  else
-    inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
 
 
-  return SUCCESS;
+  return new_target;
 }
 
 }
 
-static int
-cp_address_required_here (str, wb_ok)
-     char ** str;
-     int wb_ok;
+static void
+opcode_select (int width)
 {
 {
-  char * p = * str;
-  int    pre_inc = 0;
-  int    write_back = 0;
-
-  if (*p == '[')
+  switch (width)
     {
     {
-      int reg;
-
-      p++;
-      skip_whitespace (p);
-
-      if ((reg = reg_required_here (& p, 16)) == FAIL)
-       return FAIL;
-
-      skip_whitespace (p);
-
-      if (*p == ']')
+    case 16:
+      if (! thumb_mode)
        {
        {
-         p++;
-
-         if (wb_ok && skip_past_comma (& p) == SUCCESS)
-           {
-             /* [Rn], #expr  */
-             write_back = WRITE_BACK;
-
-             if (reg == REG_PC)
-               {
-                 inst.error = _("pc may not be used in post-increment");
-                 return FAIL;
-               }
+         if (! (cpu_variant & ARM_EXT_V4T))
+           as_bad (_("selected processor does not support THUMB opcodes"));
 
 
-             if (cp_address_offset (& p) == FAIL)
-               return FAIL;
-           }
-         else
-           pre_inc = PRE_INDEX | INDEX_UP;
+         thumb_mode = 1;
+         /* No need to force the alignment, since we will have been
+            coming from ARM mode, which is word-aligned.  */
+         record_alignment (now_seg, 1);
        }
        }
-      else
-       {
-         /* '['Rn, #expr']'[!]  */
-
-         if (skip_past_comma (& p) == FAIL)
-           {
-             inst.error = _("pre-indexed expression expected");
-             return FAIL;
-           }
-
-         pre_inc = PRE_INDEX;
-
-         if (cp_address_offset (& p) == FAIL)
-           return FAIL;
-
-         skip_whitespace (p);
+      mapping_state (MAP_THUMB);
+      break;
 
 
-         if (*p++ != ']')
-           {
-             inst.error = _("missing ]");
-             return FAIL;
-           }
+    case 32:
+      if (thumb_mode)
+       {
+         if ((cpu_variant & ARM_ALL) == ARM_EXT_V4T)
+           as_bad (_("selected processor does not support ARM opcodes"));
 
 
-         skip_whitespace (p);
+         thumb_mode = 0;
 
 
-         if (wb_ok && *p == '!')
-           {
-             if (reg == REG_PC)
-               {
-                 inst.error = _("pc may not be used with write-back");
-                 return FAIL;
-               }
+         if (!need_pass_2)
+           frag_align (2, 0, 0);
 
 
-             p++;
-             write_back = WRITE_BACK;
-           }
+         record_alignment (now_seg, 1);
        }
        }
-    }
-  else
-    {
-      if (my_get_expression (&inst.reloc.exp, &p))
-       return FAIL;
+      mapping_state (MAP_ARM);
+      break;
 
 
-      inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
-      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust.  */
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = PRE_INDEX;
+    default:
+      as_bad (_("invalid instruction size selected (%d)"), width);
     }
     }
-
-  inst.instruction |= write_back | pre_inc;
-  *str = p;
-  return SUCCESS;
 }
 
 static void
 }
 
 static void
-do_empty (str)
-     char * str;
+s_arm (int ignore ATTRIBUTE_UNUSED)
 {
 {
-  /* Do nothing really.  */
-  end_of_line (str);
-  return;
+  opcode_select (32);
+  demand_empty_rest_of_line ();
 }
 
 static void
 }
 
 static void
-do_mrs (str)
-     char *str;
+s_thumb (int ignore ATTRIBUTE_UNUSED)
 {
 {
-  int skip = 0;
+  opcode_select (16);
+  demand_empty_rest_of_line ();
+}
 
 
-  /* Only one syntax.  */
-  skip_whitespace (str);
+static void
+s_code (int unused ATTRIBUTE_UNUSED)
+{
+  int temp;
 
 
-  if (reg_required_here (&str, 12) == FAIL)
+  temp = get_absolute_expression ();
+  switch (temp)
     {
     {
-      inst.error = BAD_ARGS;
-      return;
-    }
+    case 16:
+    case 32:
+      opcode_select (temp);
+      break;
 
 
-  if (skip_past_comma (&str) == FAIL)
-    {
-      inst.error = _("comma expected after register name");
-      return;
+    default:
+      as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
     }
     }
+}
 
 
-  skip_whitespace (str);
-
-  if (   strcmp (str, "CPSR") == 0
-      || strcmp (str, "SPSR") == 0
-        /* Lower case versions for backwards compatability.  */
-      || strcmp (str, "cpsr") == 0
-      || strcmp (str, "spsr") == 0)
-    skip = 4;
-
-  /* This is for backwards compatability with older toolchains.  */
-  else if (   strcmp (str, "cpsr_all") == 0
-          || strcmp (str, "spsr_all") == 0)
-    skip = 8;
-  else
+static void
+s_force_thumb (int ignore ATTRIBUTE_UNUSED)
+{
+  /* If we are not already in thumb mode go into it, EVEN if
+     the target processor does not support thumb instructions.
+     This is used by gcc/config/arm/lib1funcs.asm for example
+     to compile interworking support functions even if the
+     target processor should not support interworking. */
+  if (! thumb_mode)
     {
     {
-      inst.error = _("CPSR or SPSR expected");
-      return;
+      thumb_mode = 2;
+      record_alignment (now_seg, 1);
     }
 
     }
 
-  if (* str == 's' || * str == 'S')
-    inst.instruction |= SPSR_BIT;
-  str += skip;
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb_func (int ignore ATTRIBUTE_UNUSED)
+{
+  s_thumb (0);
 
 
-  end_of_line (str);
+  /* The following label is the name/address of the start of a Thumb function.
+     We need to know this for the interworking support.         */
+  label_is_thumb_function_name = TRUE;
 }
 
 }
 
-/* Two possible forms:
-      "{C|S}PSR_<field>, Rm",
-      "{C|S}PSR_f, #expression".  */
+/* Perform a .set directive, but also mark the alias as
+   being a thumb function.  */
 
 static void
 
 static void
-do_msr (str)
-     char * str;
+s_thumb_set (int equiv)
 {
 {
-  skip_whitespace (str);
+  /* XXX the following is a duplicate of the code for s_set() in read.c
+     We cannot just call that code as we need to get at the symbol that
+     is created.  */
+  char *    name;
+  char     delim;
+  char *    end_name;
+  symbolS * symbolP;
 
 
-  if (psr_required_here (& str) == FAIL)
-    return;
+  /* Especial apologies for the random logic:
+     This just grew, and could be parsed much more simply!
+     Dean - in haste.  */
+  name     = input_line_pointer;
+  delim            = get_symbol_end ();
+  end_name  = input_line_pointer;
+  *end_name = delim;
 
 
-  if (skip_past_comma (& str) == FAIL)
+  if (*input_line_pointer != ',')
     {
     {
-      inst.error = _("comma missing after psr flags");
+      *end_name = 0;
+      as_bad (_("expected comma after name \"%s\""), name);
+      *end_name = delim;
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  skip_whitespace (str);
+  input_line_pointer++;
+  *end_name = 0;
 
 
-  if (reg_required_here (& str, 0) != FAIL)
+  if (name[0] == '.' && name[1] == '\0')
     {
     {
-      inst.error = NULL;
-      end_of_line (str);
-      return;
+      /* XXX - this should not happen to .thumb_set.  */
+      abort ();
     }
 
     }
 
-  if (! is_immediate_prefix (* str))
+  if ((symbolP = symbol_find (name)) == NULL
+      && (symbolP = md_undefined_symbol (name)) == NULL)
     {
     {
-      inst.error =
-       _("only a register or immediate value can follow a psr flag");
-      return;
-    }
+#ifndef NO_LISTING
+      /* When doing symbol listings, play games with dummy fragments living
+        outside the normal fragment chain to record the file and line info
+        for this symbol.  */
+      if (listing & LISTING_SYMBOLS)
+       {
+         extern struct list_info_struct * listing_tail;
+         fragS * dummy_frag = xmalloc (sizeof (fragS));
 
 
-  str ++;
-  inst.error = NULL;
+         memset (dummy_frag, 0, sizeof (fragS));
+         dummy_frag->fr_type = rs_fill;
+         dummy_frag->line = listing_tail;
+         symbolP = symbol_new (name, undefined_section, 0, dummy_frag);
+         dummy_frag->fr_symbol = symbolP;
+       }
+      else
+#endif
+       symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
 
 
-  if (my_get_expression (& inst.reloc.exp, & str))
-    {
-      inst.error =
-       _("only a register or immediate value can follow a psr flag");
-      return;
-    }
+#ifdef OBJ_COFF
+      /* "set" symbols are local unless otherwise specified.  */
+      SF_SET_LOCAL (symbolP);
+#endif /* OBJ_COFF  */
+    }                          /* Make a new symbol.  */
 
 
-#if 0  /* The first edition of the ARM architecture manual stated that
-         writing anything other than the flags with an immediate operation
-         had UNPREDICTABLE effects.  This constraint was removed in the
-         second edition of the specification.  */
-  if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
-      && inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
-    {
-      inst.error = _("immediate value cannot be used to set this field");
-      return;
-    }
-#endif
+  symbol_table_insert (symbolP);
 
 
-  inst.instruction |= INST_IMMEDIATE;
+  * end_name = delim;
 
 
-  if (inst.reloc.exp.X_add_symbol)
-    {
-      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-      inst.reloc.pc_rel = 0;
-    }
-  else
-    {
-      unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
+  if (equiv
+      && S_IS_DEFINED (symbolP)
+      && S_GET_SEGMENT (symbolP) != reg_section)
+    as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
 
 
-      if (value == (unsigned) FAIL)
-       {
-         inst.error = _("invalid constant");
-         return;
-       }
+  pseudo_set (symbolP);
 
 
-      inst.instruction |= value;
-    }
+  demand_empty_rest_of_line ();
 
 
-  inst.error = NULL;
-  end_of_line (str);
+  /* XXX Now we come to the Thumb specific bit of code.         */
+
+  THUMB_SET_FUNC (symbolP, 1);
+  ARM_SET_THUMB (symbolP, 1);
+#if defined OBJ_ELF || defined OBJ_COFF
+  ARM_SET_INTERWORK (symbolP, support_interwork);
+#endif
 }
 
 }
 
-/* Long Multiply Parser
-   UMULL RdLo, RdHi, Rm, Rs
-   SMULL RdLo, RdHi, Rm, Rs
-   UMLAL RdLo, RdHi, Rm, Rs
-   SMLAL RdLo, RdHi, Rm, Rs.  */
+/* Directives: Mode selection.  */
 
 
+/* .syntax [unified|divided] - choose the new unified syntax
+   (same for Arm and Thumb encoding, modulo slight differences in what
+   can be represented) or the old divergent syntax for each mode.  */
 static void
 static void
-do_mull (str)
-     char * str;
+s_syntax (int unused ATTRIBUTE_UNUSED)
 {
 {
-  int rdlo, rdhi, rm, rs;
+  char *name, delim;
 
 
-  /* Only one format "rdlo, rdhi, rm, rs".  */
-  skip_whitespace (str);
+  name = input_line_pointer;
+  delim = get_symbol_end ();
 
 
-  if ((rdlo = reg_required_here (&str, 12)) == FAIL)
+  if (!strcasecmp (name, "unified"))
+    unified_syntax = TRUE;
+  else if (!strcasecmp (name, "divided"))
+    unified_syntax = FALSE;
+  else
     {
     {
-      inst.error = BAD_ARGS;
+      as_bad (_("unrecognized syntax mode \"%s\""), name);
       return;
     }
       return;
     }
+  *input_line_pointer = delim;
+  demand_empty_rest_of_line ();
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rdhi = reg_required_here (&str, 16)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+/* Directives: sectioning and alignment.  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rm = reg_required_here (&str, 0)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+/* Same as s_align_ptwo but align 0 => align 2.         */
 
 
-  /* rdhi, rdlo and rm must all be different.  */
-  if (rdlo == rdhi || rdlo == rm || rdhi == rm)
-    as_tsktsk (_("rdhi, rdlo and rm must all be different"));
+static void
+s_align (int unused ATTRIBUTE_UNUSED)
+{
+  int temp;
+  long temp_fill;
+  long max_alignment = 15;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rs = reg_required_here (&str, 8)) == FAIL)
+  temp = get_absolute_expression ();
+  if (temp > max_alignment)
+    as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
+  else if (temp < 0)
     {
     {
-      inst.error = BAD_ARGS;
-      return;
+      as_bad (_("alignment negative. 0 assumed."));
+      temp = 0;
     }
 
     }
 
-  if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
+  if (*input_line_pointer == ',')
     {
     {
-      inst.error = BAD_PC;
-      return;
+      input_line_pointer++;
+      temp_fill = get_absolute_expression ();
     }
     }
+  else
+    temp_fill = 0;
 
 
-  end_of_line (str);
-  return;
+  if (!temp)
+    temp = 2;
+
+  /* Only make a frag if we HAVE to.  */
+  if (temp && !need_pass_2)
+    frag_align (temp, (int) temp_fill, 0);
+  demand_empty_rest_of_line ();
+
+  record_alignment (now_seg, temp);
 }
 
 static void
 }
 
 static void
-do_mul (str)
-     char * str;
+s_bss (int ignore ATTRIBUTE_UNUSED)
 {
 {
-  int rd, rm;
-
-  /* Only one format "rd, rm, rs".  */
-  skip_whitespace (str);
-
-  if ((rd = reg_required_here (&str, 16)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+  /* We don't support putting frags in the BSS segment, we fake it by
+     marking in_bss, then looking at s_skip for clues. */
+  subseg_set (bss_section, 0);
+  demand_empty_rest_of_line ();
+  mapping_state (MAP_DATA);
+}
 
 
-  if (rd == REG_PC)
-    {
-      inst.error = BAD_PC;
-      return;
-    }
+static void
+s_even (int ignore ATTRIBUTE_UNUSED)
+{
+  /* Never make frag if expect extra pass.  */
+  if (!need_pass_2)
+    frag_align (1, 0, 0);
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rm = reg_required_here (&str, 0)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+  record_alignment (now_seg, 1);
 
 
-  if (rm == REG_PC)
-    {
-      inst.error = BAD_PC;
-      return;
-    }
+  demand_empty_rest_of_line ();
+}
 
 
-  if (rm == rd)
-    as_tsktsk (_("rd and rm should be different in mul"));
+/* Directives: Literal pools.  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rm = reg_required_here (&str, 8)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+static literal_pool *
+find_literal_pool (void)
+{
+  literal_pool * pool;
 
 
-  if (rm == REG_PC)
+  for (pool = list_of_pools; pool != NULL; pool = pool->next)
     {
     {
-      inst.error = BAD_PC;
-      return;
+      if (pool->section == now_seg
+         && pool->sub_section == now_subseg)
+       break;
     }
 
     }
 
-  end_of_line (str);
-  return;
+  return pool;
 }
 
 }
 
-static void
-do_mla (str)
-     char * str;
+static literal_pool *
+find_or_make_literal_pool (void)
 {
 {
-  int rd, rm;
-
-  /* Only one format "rd, rm, rs, rn".  */
-  skip_whitespace (str);
-
-  if ((rd = reg_required_here (&str, 16)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (rd == REG_PC)
-    {
-      inst.error = BAD_PC;
-      return;
-    }
+  /* Next literal pool ID number.  */
+  static unsigned int latest_pool_num = 1;
+  literal_pool *      pool;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rm = reg_required_here (&str, 0)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
+  pool = find_literal_pool ();
 
 
-  if (rm == REG_PC)
+  if (pool == NULL)
     {
     {
-      inst.error = BAD_PC;
-      return;
-    }
+      /* Create a new pool.  */
+      pool = xmalloc (sizeof (* pool));
+      if (! pool)
+       return NULL;
 
 
-  if (rm == rd)
-    as_tsktsk (_("rd and rm should be different in mla"));
+      pool->next_free_entry = 0;
+      pool->section        = now_seg;
+      pool->sub_section            = now_subseg;
+      pool->next           = list_of_pools;
+      pool->symbol         = NULL;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (rd = reg_required_here (&str, 8)) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || (rm = reg_required_here (&str, 12)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
+      /* Add it to the list.  */
+      list_of_pools = pool;
     }
 
     }
 
-  if (rd == REG_PC || rm == REG_PC)
+  /* New pools, and emptied pools, will have a NULL symbol.  */
+  if (pool->symbol == NULL)
     {
     {
-      inst.error = BAD_PC;
-      return;
+      pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
+                                   (valueT) 0, &zero_address_frag);
+      pool->id = latest_pool_num ++;
     }
 
     }
 
-  end_of_line (str);
-  return;
+  /* Done.  */
+  return pool;
 }
 
 }
 
-/* Expects *str -> the characters "acc0", possibly with leading blanks.
-   Advances *str to the next non-alphanumeric.
-   Returns 0, or else FAIL (in which case sets inst.error).
-
-  (In a future XScale, there may be accumulators other than zero.
-  At that time this routine and its callers can be upgraded to suit.)  */
+/* Add the literal in the global 'inst'
+   structure to the relevent literal pool.  */
 
 static int
 
 static int
-accum0_required_here (str)
-     char ** str;
+add_to_lit_pool (void)
 {
 {
-  static char buff [128];      /* Note the address is taken.  Hence, static.  */
-  char * p = * str;
-  char   c;
-  int result = 0;              /* The accum number.  */
+  literal_pool * pool;
+  unsigned int entry;
 
 
-  skip_whitespace (p);
+  pool = find_or_make_literal_pool ();
 
 
-  *str = p;                    /* Advance caller's string pointer too.  */
-  c = *p++;
-  while (ISALNUM (c))
-    c = *p++;
+  /* Check if this literal value is already in the pool.  */
+  for (entry = 0; entry < pool->next_free_entry; entry ++)
+    {
+      if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+         && (inst.reloc.exp.X_op == O_constant)
+         && (pool->literals[entry].X_add_number
+             == inst.reloc.exp.X_add_number)
+         && (pool->literals[entry].X_unsigned
+             == inst.reloc.exp.X_unsigned))
+       break;
 
 
-  *--p = 0;                    /* Aap nul into input buffer at non-alnum.  */
+      if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+         && (inst.reloc.exp.X_op == O_symbol)
+         && (pool->literals[entry].X_add_number
+             == inst.reloc.exp.X_add_number)
+         && (pool->literals[entry].X_add_symbol
+             == inst.reloc.exp.X_add_symbol)
+         && (pool->literals[entry].X_op_symbol
+             == inst.reloc.exp.X_op_symbol))
+       break;
+    }
 
 
-  if (! ( streq (*str, "acc0") || streq (*str, "ACC0")))
+  /* Do we need to create a new entry? */
+  if (entry == pool->next_free_entry)
     {
     {
-      sprintf (buff, _("acc0 expected, not '%.100s'"), *str);
-      inst.error = buff;
-      result = FAIL;
+      if (entry >= MAX_LITERAL_POOL_SIZE)
+       {
+         inst.error = _("literal pool overflow");
+         return FAIL;
+       }
+
+      pool->literals[entry] = inst.reloc.exp;
+      pool->next_free_entry += 1;
     }
 
     }
 
-  *p = c;                      /* Unzap.  */
-  *str = p;                    /* Caller's string pointer to after match.  */
-  return result;
-}
+  inst.reloc.exp.X_op        = O_symbol;
+  inst.reloc.exp.X_add_number = ((int) entry) * 4;
+  inst.reloc.exp.X_add_symbol = pool->symbol;
 
 
-/* Expects **str -> after a comma. May be leading blanks.
-   Advances *str, recognizing a load  mode, and setting inst.instruction.
-   Returns rn, or else FAIL (in which case may set inst.error
-   and not advance str)
+  return SUCCESS;
+}
 
 
-   Note: doesn't know Rd, so no err checks that require such knowledge.  */
+/* Can't use symbol_new here, so have to create a symbol and then at
+   a later date assign it a value. Thats what these functions do.  */
 
 
-static int
-ld_mode_required_here (string)
-     char ** string;
+static void
+symbol_locate (symbolS *    symbolP,
+              const char * name,       /* It is copied, the caller can modify.  */
+              segT         segment,    /* Segment identifier (SEG_<something>).  */
+              valueT       valu,       /* Symbol value.  */
+              fragS *      frag)       /* Associated fragment.  */
 {
 {
-  char * str = * string;
-  int    rn;
-  int    pre_inc = 0;
+  unsigned int name_length;
+  char * preserved_copy_of_name;
 
 
-  skip_whitespace (str);
+  name_length = strlen (name) + 1;   /* +1 for \0.  */
+  obstack_grow (&notes, name, name_length);
+  preserved_copy_of_name = obstack_finish (&notes);
 
 
-  if (* str == '[')
-    {
-      str++;
+#ifdef tc_canonicalize_symbol_name
+  preserved_copy_of_name =
+    tc_canonicalize_symbol_name (preserved_copy_of_name);
+#endif
 
 
-      skip_whitespace (str);
+  S_SET_NAME (symbolP, preserved_copy_of_name);
 
 
-      if ((rn = reg_required_here (& str, 16)) == FAIL)
-       return FAIL;
+  S_SET_SEGMENT (symbolP, segment);
+  S_SET_VALUE (symbolP, valu);
+  symbol_clear_list_pointers (symbolP);
 
 
-      skip_whitespace (str);
+  symbol_set_frag (symbolP, frag);
 
 
-      if (* str == ']')
-       {
-         str ++;
+  /* Link to end of symbol chain.  */
+  {
+    extern int symbol_table_frozen;
 
 
-         if (skip_past_comma (& str) == SUCCESS)
-           {
-             /* [Rn],... (post inc) */
-             if (ldst_extend_v4 (&str) == FAIL)
-               return FAIL;
-           }
-         else        /* [Rn] */
-           {
-             skip_whitespace (str);
+    if (symbol_table_frozen)
+      abort ();
+  }
 
 
-             if (* str == '!')
-               {
-                 str ++;
-                 inst.instruction |= WRITE_BACK;
-               }
+  symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
 
 
-             inst.instruction |= INDEX_UP | HWOFFSET_IMM;
-             pre_inc = 1;
-           }
-       }
-      else       /* [Rn,...] */
-       {
-         if (skip_past_comma (& str) == FAIL)
-           {
-             inst.error = _("pre-indexed expression expected");
-             return FAIL;
-           }
+  obj_symbol_new_hook (symbolP);
 
 
-         pre_inc = 1;
+#ifdef tc_symbol_new_hook
+  tc_symbol_new_hook (symbolP);
+#endif
 
 
-         if (ldst_extend_v4 (&str) == FAIL)
-           return FAIL;
+#ifdef DEBUG_SYMS
+  verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS  */
+}
 
 
-         skip_whitespace (str);
 
 
-         if (* str ++ != ']')
-           {
-             inst.error = _("missing ]");
-             return FAIL;
-           }
+static void
+s_ltorg (int ignored ATTRIBUTE_UNUSED)
+{
+  unsigned int entry;
+  literal_pool * pool;
+  char sym_name[20];
 
 
-         skip_whitespace (str);
+  pool = find_literal_pool ();
+  if (pool == NULL
+      || pool->symbol == NULL
+      || pool->next_free_entry == 0)
+    return;
 
 
-         if (* str == '!')
-           {
-             str ++;
-             inst.instruction |= WRITE_BACK;
-           }
-       }
-    }
-  else if (* str == '=')       /* ldr's "r,=label" syntax */
-    /* We should never reach here, because <text> = <expression> is
-       caught gas/read.c read_a_source_file() as a .set operation.  */
-    return FAIL;
-  else                         /* PC +- 8 bit immediate offset.  */
-    {
-      if (my_get_expression (& inst.reloc.exp, & str))
-       return FAIL;
+  mapping_state (MAP_DATA);
 
 
-      inst.instruction            |= HWOFFSET_IMM;     /* The I bit.  */
-      inst.reloc.type              = BFD_RELOC_ARM_OFFSET_IMM8;
-      inst.reloc.exp.X_add_number -= 8;                /* PC rel adjust.  */
-      inst.reloc.pc_rel            = 1;
-      inst.instruction            |= (REG_PC << 16);
+  /* Align pool as you have word accesses.
+     Only make a frag if we have to.  */
+  if (!need_pass_2)
+    frag_align (2, 0, 0);
 
 
-      rn = REG_PC;
-      pre_inc = 1;
-    }
+  record_alignment (now_seg, 2);
 
 
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  * string = str;
+  sprintf (sym_name, "$$lit_\002%x", pool->id);
 
 
-  return rn;
-}
+  symbol_locate (pool->symbol, sym_name, now_seg,
+                (valueT) frag_now_fix (), frag_now);
+  symbol_table_insert (pool->symbol);
 
 
-/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
-   SMLAxy{cond} Rd,Rm,Rs,Rn
-   SMLAWy{cond} Rd,Rm,Rs,Rn
-   Error if any register is R15.  */
-
-static void
-do_smla (str)
-     char *        str;
-{
-  int rd, rm, rs, rn;
-
-  skip_whitespace (str);
+  ARM_SET_THUMB (pool->symbol, thumb_mode);
 
 
-  if ((rd = reg_required_here (& str, 16)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rm = reg_required_here (& str, 0)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rs = reg_required_here (& str, 8)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rn = reg_required_here (& str, 12)) == FAIL)
-    inst.error = BAD_ARGS;
+#if defined OBJ_COFF || defined OBJ_ELF
+  ARM_SET_INTERWORK (pool->symbol, support_interwork);
+#endif
 
 
-  else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
-    inst.error = BAD_PC;
+  for (entry = 0; entry < pool->next_free_entry; entry ++)
+    /* First output the expression in the instruction to the pool.  */
+    emit_expr (&(pool->literals[entry]), 4); /* .word  */
 
 
-  else
-    end_of_line (str);
+  /* Mark the pool as empty.  */
+  pool->next_free_entry = 0;
+  pool->symbol = NULL;
 }
 
 }
 
-/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
-   SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
-   Error if any register is R15.
-   Warning if Rdlo == Rdhi.  */
+#ifdef OBJ_ELF
+/* Forward declarations for functions below, in the MD interface
+   section.  */
+static void fix_new_arm (fragS *, int, short, expressionS *, int, int);
+static valueT create_unwind_entry (int);
+static void start_unwind_section (const segT, int);
+static void add_unwind_opcode (valueT, int);
+static void flush_pending_unwind (void);
+
+/* Directives: Data.  */
 
 static void
 
 static void
-do_smlal (str)
-     char *        str;
+s_arm_elf_cons (int nbytes)
 {
 {
-  int rdlo, rdhi, rm, rs;
+  expressionS exp;
 
 
-  skip_whitespace (str);
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
 
 
-  if ((rdlo = reg_required_here (& str, 12)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rdhi = reg_required_here (& str, 16)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rm = reg_required_here (& str, 0)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rs = reg_required_here (& str, 8)) == FAIL)
+  if (is_it_end_of_statement ())
     {
     {
-      inst.error = BAD_ARGS;
+      demand_empty_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  if (rdlo == REG_PC || rdhi == REG_PC || rm == REG_PC || rs == REG_PC)
+#ifdef md_cons_align
+  md_cons_align (nbytes);
+#endif
+
+  mapping_state (MAP_DATA);
+  do
     {
     {
-      inst.error = BAD_PC;
-      return;
-    }
+      int reloc;
+      char *base = input_line_pointer;
 
 
-  if (rdlo == rdhi)
-    as_tsktsk (_("rdhi and rdlo must be different"));
+      expression (& exp);
+
+      if (exp.X_op != O_symbol)
+       emit_expr (&exp, (unsigned int) nbytes);
+      else
+       {
+         char *before_reloc = input_line_pointer;
+         reloc = parse_reloc (&input_line_pointer);
+         if (reloc == -1)
+           {
+             as_bad (_("unrecognized relocation suffix"));
+             ignore_rest_of_line ();
+             return;
+           }
+         else if (reloc == BFD_RELOC_UNUSED)
+           emit_expr (&exp, (unsigned int) nbytes);
+         else
+           {
+             reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
+             int size = bfd_get_reloc_size (howto);
+
+             if (reloc == BFD_RELOC_ARM_PLT32)
+               {
+                 as_bad (_("(plt) is only valid on branch targets"));
+                 reloc = BFD_RELOC_UNUSED;
+                 size = 0;
+               }
+
+             if (size > nbytes)
+               as_bad (_("%s relocations do not fit in %d bytes"),
+                       howto->name, nbytes);
+             else
+               {
+                 /* We've parsed an expression stopping at O_symbol.
+                    But there may be more expression left now that we
+                    have parsed the relocation marker.  Parse it again.
+                    XXX Surely there is a cleaner way to do this.  */
+                 char *p = input_line_pointer;
+                 int offset;
+                 char *save_buf = alloca (input_line_pointer - base);
+                 memcpy (save_buf, base, input_line_pointer - base);
+                 memmove (base + (input_line_pointer - before_reloc),
+                          base, before_reloc - base);
+
+                 input_line_pointer = base + (input_line_pointer-before_reloc);
+                 expression (&exp);
+                 memcpy (base, save_buf, p - base);
+
+                 offset = nbytes - size;
+                 p = frag_more ((int) nbytes);
+                 fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+                              size, &exp, 0, reloc);
+               }
+           }
+       }
+    }
+  while (*input_line_pointer++ == ',');
 
 
-  end_of_line (str);
+  /* Put terminator back into stream.  */
+  input_line_pointer --;
+  demand_empty_rest_of_line ();
 }
 
 }
 
-/* ARM V5E (El Segundo) signed-multiply (argument parse)
-   SMULxy{cond} Rd,Rm,Rs
-   Error if any register is R15.  */
+
+/* Parse a .rel31 directive.  */
 
 static void
 
 static void
-do_smul (str)
-     char *        str;
+s_arm_rel31 (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rd, rm, rs;
+  expressionS exp;
+  char *p;
+  valueT highbit;
 
 
-  skip_whitespace (str);
+  highbit = 0;
+  if (*input_line_pointer == '1')
+    highbit = 0x80000000;
+  else if (*input_line_pointer != '0')
+    as_bad (_("expected 0 or 1"));
+
+  input_line_pointer++;
+  if (*input_line_pointer != ',')
+    as_bad (_("missing comma"));
+  input_line_pointer++;
 
 
-  if ((rd = reg_required_here (& str, 16)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rm = reg_required_here (& str, 0)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rs = reg_required_here (& str, 8)) == FAIL)
-    inst.error = BAD_ARGS;
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
 
 
-  else if (rd == REG_PC || rm == REG_PC || rs == REG_PC)
-    inst.error = BAD_PC;
+#ifdef md_cons_align
+  md_cons_align (4);
+#endif
 
 
-  else
-    end_of_line (str);
+  mapping_state (MAP_DATA);
+
+  expression (&exp);
+
+  p = frag_more (4);
+  md_number_to_chars (p, highbit, 4);
+  fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 1,
+              BFD_RELOC_ARM_PREL31);
+
+  demand_empty_rest_of_line ();
 }
 
 }
 
-/* ARM V5E (El Segundo) saturating-add/subtract (argument parse)
-   Q[D]{ADD,SUB}{cond} Rd,Rm,Rn
-   Error if any register is R15.  */
+/* Directives: AEABI stack-unwind tables.  */
+
+/* Parse an unwind_fnstart directive.  Simply records the current location.  */
 
 static void
 
 static void
-do_qadd (str)
-     char *        str;
+s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rd, rm, rn;
-
-  skip_whitespace (str);
+  demand_empty_rest_of_line ();
+  /* Mark the start of the function.  */
+  unwind.proc_start = expr_build_dot ();
 
 
-  if ((rd = reg_required_here (& str, 12)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rm = reg_required_here (& str, 0)) == FAIL
-      || skip_past_comma (& str) == FAIL
-      || (rn = reg_required_here (& str, 16)) == FAIL)
-    inst.error = BAD_ARGS;
+  /* Reset the rest of the unwind info.         */
+  unwind.opcode_count = 0;
+  unwind.table_entry = NULL;
+  unwind.personality_routine = NULL;
+  unwind.personality_index = -1;
+  unwind.frame_size = 0;
+  unwind.fp_offset = 0;
+  unwind.fp_reg = 13;
+  unwind.fp_used = 0;
+  unwind.sp_restored = 0;
+}
 
 
-  else if (rd == REG_PC || rm == REG_PC || rn == REG_PC)
-    inst.error = BAD_PC;
 
 
-  else
-    end_of_line (str);
-}
+/* Parse a handlerdata directive.  Creates the exception handling table entry
+   for the function.  */
 
 
-/* ARM V5E (el Segundo)
-   MCRRcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
-   MRRCcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+static void
+s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  if (unwind.table_entry)
+    as_bad (_("dupicate .handlerdata directive"));
 
 
-   These are equivalent to the XScale instructions MAR and MRA,
-   respectively, when coproc == 0, opcode == 0, and CRm == 0.
+  create_unwind_entry (1);
+}
 
 
-   Result unpredicatable if Rd or Rn is R15.  */
+/* Parse an unwind_fnend directive.  Generates the index table entry.  */
 
 static void
 
 static void
-do_co_reg2c (str)
-     char *        str;
+s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rd, rn;
+  long where;
+  char *ptr;
+  valueT val;
 
 
-  skip_whitespace (str);
+  demand_empty_rest_of_line ();
 
 
-  if (co_proc_number (& str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Add eh table entry.  */
+  if (unwind.table_entry == NULL)
+    val = create_unwind_entry (0);
+  else
+    val = 0;
 
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_opc_expr (& str, 4, 4) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Add index table entry.  This is two words.         */
+  start_unwind_section (unwind.saved_seg, 1);
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
 
 
-  if (skip_past_comma (& str) == FAIL
-      || (rd = reg_required_here (& str, 12)) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  ptr = frag_more (8);
+  where = frag_now_fix () - 8;
+
+  /* Self relative offset of the function start.  */
+  fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
+          BFD_RELOC_ARM_PREL31);
+
+  /* Indicate dependency on EHABI-defined personality routines to the
+     linker, if it hasn't been done already.  */
+  if (unwind.personality_index >= 0 && unwind.personality_index < 3
+      && !(marked_pr_dependency & (1 << unwind.personality_index)))
+    {
+      static const char *const name[] = {
+       "__aeabi_unwind_cpp_pr0",
+       "__aeabi_unwind_cpp_pr1",
+       "__aeabi_unwind_cpp_pr2"
+      };
+      symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+      fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+      marked_pr_dependency |= 1 << unwind.personality_index;
+      seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+       = marked_pr_dependency;
+    }
+
+  if (val)
+    /* Inline exception table entry.  */
+    md_number_to_chars (ptr + 4, val, 4);
+  else
+    /* Self relative offset of the table entry.         */
+    fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+            BFD_RELOC_ARM_PREL31);
 
 
-  if (skip_past_comma (& str) == FAIL
-      || (rn = reg_required_here (& str, 16)) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Restore the original section.  */
+  subseg_set (unwind.saved_seg, unwind.saved_subseg);
+}
 
 
-  /* Unpredictable result if rd or rn is R15.  */
-  if (rd == REG_PC || rn == REG_PC)
-    as_tsktsk
-      (_("Warning: instruction unpredictable when using r15"));
 
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 0) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* Parse an unwind_cantunwind directive.  */
+
+static void
+s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("personality routine specified for cantunwind frame"));
 
 
-  end_of_line (str);
+  unwind.personality_index = -2;
 }
 
 }
 
-/* ARM V5 count-leading-zeroes instruction (argument parse)
-     CLZ{<cond>} <Rd>, <Rm>
-     Condition defaults to COND_ALWAYS.
-     Error if Rd or Rm are R15.  */
+
+/* Parse a personalityindex directive. */
 
 static void
 
 static void
-do_clz (str)
-     char *        str;
+s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rd, rm;
+  expressionS exp;
 
 
-  skip_whitespace (str);
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personalityindex directive"));
 
 
-  if (((rd = reg_required_here (& str, 12)) == FAIL)
-      || (skip_past_comma (& str) == FAIL)
-      || ((rm = reg_required_here (& str, 0)) == FAIL))
-    inst.error = BAD_ARGS;
+  expression (&exp);
+
+  if (exp.X_op != O_constant
+      || exp.X_add_number < 0 || exp.X_add_number > 15)
+    {
+      as_bad (_("bad personality routine number"));
+      ignore_rest_of_line ();
+      return;
+    }
 
 
-  else if (rd == REG_PC || rm == REG_PC )
-    inst.error = BAD_PC;
+  unwind.personality_index = exp.X_add_number;
 
 
-  else
-    end_of_line (str);
+  demand_empty_rest_of_line ();
 }
 
 }
 
-/* ARM V5 (argument parse)
-     LDC2{L} <coproc>, <CRd>, <addressing mode>
-     STC2{L} <coproc>, <CRd>, <addressing mode>
-     Instruction is not conditional, and has 0xf in the codition field.
-     Otherwise, it's the same as LDC/STC.  */
+
+/* Parse a personality directive.  */
 
 static void
 
 static void
-do_lstc2 (str)
-     char *        str;
+s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  skip_whitespace (str);
+  char *name, *p, c;
 
 
-  if (co_proc_number (& str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-    }
-  else if (skip_past_comma (& str) == FAIL
-          || cp_reg_required_here (& str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-    }
-  else if (skip_past_comma (& str) == FAIL
-          || cp_address_required_here (&str, CP_WB_OK) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-    }
-  else
-    end_of_line (str);
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personality directive"));
+
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  p = input_line_pointer;
+  unwind.personality_routine = symbol_find_or_make (name);
+  *p = c;
+  demand_empty_rest_of_line ();
 }
 
 }
 
-/* ARM V5 (argument parse)
-     CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>, <opcode_2>
-     Instruction is not conditional, and has 0xf in the condition field.
-     Otherwise, it's the same as CDP.  */
+
+/* Parse a directive saving core registers.  */
 
 static void
 
 static void
-do_cdp2 (str)
-     char *        str;
+s_arm_unwind_save_core (void)
 {
 {
-  skip_whitespace (str);
+  valueT op;
+  long range;
+  int n;
 
 
-  if (co_proc_number (& str) == FAIL)
+  range = parse_reg_list (&input_line_pointer);
+  if (range == FAIL)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+      as_bad (_("expected register list"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_opc_expr (& str, 20,4) == FAIL)
+  demand_empty_rest_of_line ();
+
+  /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
+     into .unwind_save {..., sp...}.  We aren't bothered about the value of
+     ip because it is clobbered by calls.  */
+  if (unwind.sp_restored && unwind.fp_reg == 12
+      && (range & 0x3000) == 0x1000)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      unwind.opcode_count--;
+      unwind.sp_restored = 0;
+      range = (range | 0x2000) & ~0x1000;
+      unwind.pending_offset = 0;
     }
 
     }
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 12) == FAIL)
+  /* See if we can use the short opcodes.  These pop a block of upto 8
+     registers starting with r4, plus maybe r14.  */
+  for (n = 0; n < 8; n++)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Break at the first non-saved register.         */
+      if ((range & (1 << (n + 4))) == 0)
+       break;
     }
     }
-
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 16) == FAIL)
+  /* See if there are any other bits set.  */
+  if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Use the long form.  */
+      op = 0x8000 | ((range >> 4) & 0xfff);
+      add_unwind_opcode (op, 2);
     }
     }
-
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 0) == FAIL)
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Use the short form.  */
+      if (range & 0x4000)
+       op = 0xa8; /* Pop r14.  */
+      else
+       op = 0xa0; /* Do not pop r14.  */
+      op |= (n - 1);
+      add_unwind_opcode (op, 1);
     }
 
     }
 
-  if (skip_past_comma (& str) == SUCCESS)
+  /* Pop r0-r3.         */
+  if (range & 0xf)
     {
     {
-      if (cp_opc_expr (& str, 5, 3) == FAIL)
-       {
-         if (!inst.error)
-           inst.error = BAD_ARGS;
-         return;
-       }
+      op = 0xb100 | (range & 0xf);
+      add_unwind_opcode (op, 2);
     }
 
     }
 
-  end_of_line (str);
+  /* Record the number of bytes pushed.         */
+  for (n = 0; n < 16; n++)
+    {
+      if (range & (1 << n))
+       unwind.frame_size += 4;
+    }
 }
 
 }
 
-/* ARM V5 (argument parse)
-     MCR2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
-     MRC2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
-     Instruction is not conditional, and has 0xf in the condition field.
-     Otherwise, it's the same as MCR/MRC.  */
+
+/* Parse a directive saving FPA registers.  */
 
 static void
 
 static void
-do_co_reg2 (str)
-     char *        str;
+s_arm_unwind_save_fpa (int reg)
 {
 {
-  skip_whitespace (str);
+  expressionS exp;
+  int num_regs;
+  valueT op;
 
 
-  if (co_proc_number (& str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Get Number of registers to transfer.  */
+  if (skip_past_comma (&input_line_pointer) != FAIL)
+    expression (&exp);
+  else
+    exp.X_op = O_illegal;
 
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_opc_expr (& str, 21, 3) == FAIL)
+  if (exp.X_op != O_constant)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+      as_bad (_("expected , <constant>"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  if (skip_past_comma (& str) == FAIL
-      || reg_required_here (& str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  num_regs = exp.X_add_number;
 
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 16) == FAIL)
+  if (num_regs < 1 || num_regs > 4)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+      as_bad (_("number of registers must be in the range [1:4]"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  if (skip_past_comma (& str) == FAIL
-      || cp_reg_required_here (& str, 0) == FAIL)
+  demand_empty_rest_of_line ();
+
+  if (reg == 4)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Short form.  */
+      op = 0xb4 | (num_regs - 1);
+      add_unwind_opcode (op, 1);
     }
     }
-
-  if (skip_past_comma (& str) == SUCCESS)
+  else
     {
     {
-      if (cp_opc_expr (& str, 5, 3) == FAIL)
-       {
-         if (!inst.error)
-           inst.error = BAD_ARGS;
-         return;
-       }
+      /* Long form.  */
+      op = 0xc800 | (reg << 4) | (num_regs - 1);
+      add_unwind_opcode (op, 2);
     }
     }
-
-  end_of_line (str);
+  unwind.frame_size += num_regs * 12;
 }
 
 }
 
-/* ARM v5TEJ.  Jump to Jazelle code.  */
+
+/* Parse a directive saving VFP registers.  */
+
 static void
 static void
-do_bxj (str)
-     char * str;
+s_arm_unwind_save_vfp (void)
 {
 {
-  int reg;
-
-  skip_whitespace (str);
+  int count;
+  unsigned int reg;
+  valueT op;
 
 
-  if ((reg = reg_required_here (&str, 0)) == FAIL)
+  count = parse_vfp_reg_list (&input_line_pointer, &reg, 1);
+  if (count == FAIL)
     {
     {
-      inst.error = BAD_ARGS;
+      as_bad (_("expected register list"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  /* Note - it is not illegal to do a "bxj pc".  Useless, but not illegal.  */
-  if (reg == REG_PC)
-    as_tsktsk (_("use of r15 in bxj is not really useful"));
+  demand_empty_rest_of_line ();
 
 
-  end_of_line (str);
+  if (reg == 8)
+    {
+      /* Short form.  */
+      op = 0xb8 | (count - 1);
+      add_unwind_opcode (op, 1);
+    }
+  else
+    {
+      /* Long form.  */
+      op = 0xb300 | (reg << 4) | (count - 1);
+      add_unwind_opcode (op, 2);
+    }
+  unwind.frame_size += count * 8 + 4;
 }
 
 }
 
-/* THUMB V5 breakpoint instruction (argument parse)
-       BKPT <immed_8>.  */
+
+/* Parse a directive saving iWMMXt data registers.  */
 
 static void
 
 static void
-do_t_bkpt (str)
-     char * str;
+s_arm_unwind_save_mmxwr (void)
 {
 {
-  expressionS expr;
-  unsigned long number;
-
-  skip_whitespace (str);
-
-  /* Allow optional leading '#'.  */
-  if (is_immediate_prefix (*str))
-    str ++;
-
-  memset (& expr, '\0', sizeof (expr));
-  if (my_get_expression (& expr, & str)
-      || (expr.X_op != O_constant
-         /* As a convenience we allow 'bkpt' without an operand.  */
-         && expr.X_op != O_absent))
-    {
-      inst.error = _("bad expression");
-      return;
-    }
+  int reg;
+  int hi_reg;
+  int i;
+  unsigned mask = 0;
+  valueT op;
 
 
-  number = expr.X_add_number;
+  if (*input_line_pointer == '{')
+    input_line_pointer++;
 
 
-  /* Check it fits an 8 bit unsigned.  */
-  if (number != (number & 0xff))
+  do
     {
     {
-      inst.error = _("immediate value out of range");
-      return;
-    }
+      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
 
 
-  inst.instruction |= number;
+      if (reg == FAIL)
+       {
+         as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
+         goto error;
+       }
 
 
-  end_of_line (str);
-}
+      if (mask >> reg)
+       as_tsktsk (_("register list not in ascending order"));
+      mask |= 1 << reg;
 
 
-/* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
-   Expects inst.instruction is set for BLX(1).
-   Note: this is cloned from do_branch, and the reloc changed to be a
-       new one that can cope with setting one extra bit (the H bit).  */
+      if (*input_line_pointer == '-')
+       {
+         input_line_pointer++;
+         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
+         if (hi_reg == FAIL)
+           {
+             as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
+             goto error;
+           }
+         else if (reg >= hi_reg)
+           {
+             as_bad (_("bad register range"));
+             goto error;
+           }
+         for (; reg < hi_reg; reg++)
+           mask |= 1 << reg;
+       }
+    }
+  while (skip_past_comma (&input_line_pointer) != FAIL);
 
 
-static void
-do_branch25 (str)
-     char *        str;
-{
-  if (my_get_expression (& inst.reloc.exp, & str))
-    return;
+  if (*input_line_pointer == '}')
+    input_line_pointer++;
 
 
-#ifdef OBJ_ELF
-  {
-    char * save_in;
+  demand_empty_rest_of_line ();
 
 
-    /* ScottB: February 5, 1998 */
-    /* Check to see of PLT32 reloc required for the instruction.  */
+  /* Generate any deferred opcodes becuuse we're going to be looking at
+     the list. */
+  flush_pending_unwind ();
 
 
-    /* arm_parse_reloc() works on input_line_pointer.
-       We actually want to parse the operands to the branch instruction
-       passed in 'str'.  Save the input pointer and restore it later.  */
-    save_in = input_line_pointer;
-    input_line_pointer = str;
+  for (i = 0; i < 16; i++)
+    {
+      if (mask & (1 << i))
+       unwind.frame_size += 8;
+    }
 
 
-    if (inst.reloc.exp.X_op == O_symbol
-       && *str == '('
-       && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
-      {
-       inst.reloc.type   = BFD_RELOC_ARM_PLT32;
-       inst.reloc.pc_rel = 0;
-       /* Modify str to point to after parsed operands, otherwise
-          end_of_line() will complain about the (PLT) left in str.  */
-       str = input_line_pointer;
-      }
-    else
-      {
-       inst.reloc.type   = BFD_RELOC_ARM_PCREL_BLX;
-       inst.reloc.pc_rel = 1;
-      }
+  /* Attempt to combine with a previous opcode.         We do this because gcc
+     likes to output separate unwind directives for a single block of
+     registers.         */
+  if (unwind.opcode_count > 0)
+    {
+      i = unwind.opcodes[unwind.opcode_count - 1];
+      if ((i & 0xf8) == 0xc0)
+       {
+         i &= 7;
+         /* Only merge if the blocks are contiguous.  */
+         if (i < 6)
+           {
+             if ((mask & 0xfe00) == (1 << 9))
+               {
+                 mask |= ((1 << (i + 11)) - 1) & 0xfc00;
+                 unwind.opcode_count--;
+               }
+           }
+         else if (i == 6 && unwind.opcode_count >= 2)
+           {
+             i = unwind.opcodes[unwind.opcode_count - 2];
+             reg = i >> 4;
+             i &= 0xf;
 
 
-    input_line_pointer = save_in;
-  }
-#else
-  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BLX;
-  inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF */
+             op = 0xffff << (reg - 1);
+             if (reg > 0
+                 || ((mask & op) == (1u << (reg - 1))))
+               {
+                 op = (1 << (reg + i + 1)) - 1;
+                 op &= ~((1 << reg) - 1);
+                 mask |= op;
+                 unwind.opcode_count -= 2;
+               }
+           }
+       }
+    }
 
 
-  end_of_line (str);
-}
+  hi_reg = 15;
+  /* We want to generate opcodes in the order the registers have been
+     saved, ie. descending order.  */
+  for (reg = 15; reg >= -1; reg--)
+    {
+      /* Save registers in blocks.  */
+      if (reg < 0
+         || !(mask & (1 << reg)))
+       {
+         /* We found an unsaved reg.  Generate opcodes to save the
+            preceeding block.  */
+         if (reg != hi_reg)
+           {
+             if (reg == 9)
+               {
+                 /* Short form.  */
+                 op = 0xc0 | (hi_reg - 10);
+                 add_unwind_opcode (op, 1);
+               }
+             else
+               {
+                 /* Long form.  */
+                 op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
+                 add_unwind_opcode (op, 2);
+               }
+           }
+         hi_reg = reg - 1;
+       }
+    }
 
 
-/* ARM V5 branch-link-exchange instruction (argument parse)
-     BLX <target_addr>         ie BLX(1)
-     BLX{<condition>} <Rm>     ie BLX(2)
-   Unfortunately, there are two different opcodes for this mnemonic.
-   So, the insns[].value is not used, and the code here zaps values
-       into inst.instruction.
-   Also, the <target_addr> can be 25 bits, hence has its own reloc.  */
+  return;
+error:
+  ignore_rest_of_line ();
+}
 
 static void
 
 static void
-do_blx (str)
-     char *        str;
+s_arm_unwind_save_mmxwcg (void)
 {
 {
-  char * mystr = str;
-  int rm;
-
-  skip_whitespace (mystr);
-  rm = reg_required_here (& mystr, 0);
+  int reg;
+  int hi_reg;
+  unsigned mask = 0;
+  valueT op;
 
 
-  /* The above may set inst.error.  Ignore his opinion.  */
-  inst.error = 0;
+  if (*input_line_pointer == '{')
+    input_line_pointer++;
 
 
-  if (rm != FAIL)
-    {
-      /* Arg is a register.
-        Use the condition code our caller put in inst.instruction.
-        Pass ourselves off as a BX with a funny opcode.  */
-      inst.instruction |= 0x012fff30;
-      do_bx (str);
-    }
-  else
+  do
     {
     {
-      /* This must be is BLX <target address>, no condition allowed.  */
-      if (inst.instruction != COND_ALWAYS)
+      reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
+
+      if (reg == FAIL)
        {
        {
-         inst.error = BAD_COND;
-         return;
+         as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
+         goto error;
        }
 
        }
 
-      inst.instruction = 0xfafffffe;
+      reg -= 8;
+      if (mask >> reg)
+       as_tsktsk (_("register list not in ascending order"));
+      mask |= 1 << reg;
 
 
-      /* Process like a B/BL, but with a different reloc.
-        Note that B/BL expecte fffffe, not 0, offset in the opcode table.  */
-      do_branch25 (str);
+      if (*input_line_pointer == '-')
+       {
+         input_line_pointer++;
+         hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
+         if (hi_reg == FAIL)
+           {
+             as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
+             goto error;
+           }
+         else if (reg >= hi_reg)
+           {
+             as_bad (_("bad register range"));
+             goto error;
+           }
+         for (; reg < hi_reg; reg++)
+           mask |= 1 << reg;
+       }
     }
     }
-}
-
-/* ARM V5 Thumb BLX (argument parse)
-       BLX <target_addr>       which is BLX(1)
-       BLX <Rm>                which is BLX(2)
-   Unfortunately, there are two different opcodes for this mnemonic.
-   So, the tinsns[].value is not used, and the code here zaps values
-       into inst.instruction.  */
+  while (skip_past_comma (&input_line_pointer) != FAIL);
 
 
-static void
-do_t_blx (str)
-     char * str;
-{
-  char * mystr = str;
-  int rm;
+  if (*input_line_pointer == '}')
+    input_line_pointer++;
 
 
-  skip_whitespace (mystr);
-  inst.instruction = 0x4780;
+  demand_empty_rest_of_line ();
 
 
-  /* Note that this call is to the ARM register recognizer.  BLX(2)
-     uses the ARM register space, not the Thumb one, so a call to
-     thumb_reg() would be wrong.  */
-  rm = reg_required_here (& mystr, 3);
-  inst.error = 0;
+  /* Generate any deferred opcodes becuuse we're going to be looking at
+     the list. */
+  flush_pending_unwind ();
 
 
-  if (rm != FAIL)
-    {
-      /* It's BLX(2).  The .instruction was zapped with rm & is final.  */
-      inst.size = 2;
-    }
-  else
+  for (reg = 0; reg < 16; reg++)
     {
     {
-      /* No ARM register.  This must be BLX(1).  Change the .instruction.  */
-      inst.instruction = 0xf7ffeffe;
-      inst.size = 4;
-
-      if (my_get_expression (& inst.reloc.exp, & mystr))
-       return;
-
-      inst.reloc.type   = BFD_RELOC_THUMB_PCREL_BLX;
-      inst.reloc.pc_rel = 1;
+      if (mask & (1 << reg))
+       unwind.frame_size += 4;
     }
     }
-
-  end_of_line (mystr);
+  op = 0xc700 | mask;
+  add_unwind_opcode (op, 2);
+  return;
+error:
+  ignore_rest_of_line ();
 }
 
 }
 
-/* ARM V5 breakpoint instruction (argument parse)
-     BKPT <16 bit unsigned immediate>
-     Instruction is not conditional.
-       The bit pattern given in insns[] has the COND_ALWAYS condition,
-       and it is an error if the caller tried to override that.  */
+
+/* Parse an unwind_save directive.  */
 
 static void
 
 static void
-do_bkpt (str)
-     char *        str;
+s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  expressionS expr;
-  unsigned long number;
-
-  skip_whitespace (str);
+  char *peek;
+  struct reg_entry *reg;
+  bfd_boolean had_brace = FALSE;
 
 
-  /* Allow optional leading '#'.  */
-  if (is_immediate_prefix (* str))
-    str++;
+  /* Figure out what sort of save we have.  */
+  peek = input_line_pointer;
 
 
-  memset (& expr, '\0', sizeof (expr));
-
-  if (my_get_expression (& expr, & str)
-      || (expr.X_op != O_constant
-         /* As a convenience we allow 'bkpt' without an operand.  */
-         && expr.X_op != O_absent))
+  if (*peek == '{')
     {
     {
-      inst.error = _("bad expression");
-      return;
+      had_brace = TRUE;
+      peek++;
     }
 
     }
 
-  number = expr.X_add_number;
+  reg = arm_reg_parse_multi (&peek);
 
 
-  /* Check it fits a 16 bit unsigned.  */
-  if (number != (number & 0xffff))
+  if (!reg)
     {
     {
-      inst.error = _("immediate value out of range");
+      as_bad (_("register expected"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  /* Top 12 of 16 bits to bits 19:8.  */
-  inst.instruction |= (number & 0xfff0) << 4;
+  switch (reg->type)
+    {
+    case REG_TYPE_FN:
+      if (had_brace)
+       {
+         as_bad (_("FPA .unwind_save does not take a register list"));
+         ignore_rest_of_line ();
+         return;
+       }
+      s_arm_unwind_save_fpa (reg->number);
+      return;
 
 
-  /* Bottom 4 of 16 bits to bits 3:0.  */
-  inst.instruction |= number & 0xf;
+    case REG_TYPE_RN:    s_arm_unwind_save_core ();   return;
+    case REG_TYPE_VFD:    s_arm_unwind_save_vfp ();    return;
+    case REG_TYPE_MMXWR:  s_arm_unwind_save_mmxwr ();  return;
+    case REG_TYPE_MMXWCG: s_arm_unwind_save_mmxwcg (); return;
 
 
-  end_of_line (str);
+    default:
+      as_bad (_(".unwind_save does not support this kind of register"));
+      ignore_rest_of_line ();
+    }
 }
 
 }
 
-/* Xscale multiply-accumulate (argument parse)
-     MIAcc   acc0,Rm,Rs
-     MIAPHcc acc0,Rm,Rs
-     MIAxycc acc0,Rm,Rs.  */
+
+/* Parse an unwind_movsp directive.  */
 
 static void
 
 static void
-do_xsc_mia (str)
-     char * str;
+s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rs;
-  int rm;
+  int reg;
+  valueT op;
 
 
-  if (accum0_required_here (& str) == FAIL)
-    inst.error = ERR_NO_ACCUM;
+  reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+  if (reg == FAIL)
+    {
+      as_bad (_(reg_expected_msgs[REG_TYPE_RN]));
+      ignore_rest_of_line ();
+      return;
+    }
+  demand_empty_rest_of_line ();
 
 
-  else if (skip_past_comma (& str) == FAIL
-          || (rm = reg_required_here (& str, 0)) == FAIL)
-    inst.error = BAD_ARGS;
+  if (reg == REG_SP || reg == REG_PC)
+    {
+      as_bad (_("SP and PC not permitted in .unwind_movsp directive"));
+      return;
+    }
 
 
-  else if (skip_past_comma (& str) == FAIL
-          || (rs = reg_required_here (& str, 12)) == FAIL)
-    inst.error = BAD_ARGS;
+  if (unwind.fp_reg != REG_SP)
+    as_bad (_("unexpected .unwind_movsp directive"));
 
 
-  /* inst.instruction has now been zapped with both rm and rs.  */
-  else if (rm == REG_PC || rs == REG_PC)
-    inst.error = BAD_PC;       /* Undefined result if rm or rs is R15.  */
+  /* Generate opcode to restore the value.  */
+  op = 0x90 | reg;
+  add_unwind_opcode (op, 1);
 
 
-  else
-    end_of_line (str);
+  /* Record the information for later. */
+  unwind.fp_reg = reg;
+  unwind.fp_offset = unwind.frame_size;
+  unwind.sp_restored = 1;
 }
 
 }
 
-/* Xscale move-accumulator-register (argument parse)
-
-     MARcc   acc0,RdLo,RdHi.  */
+/* Parse an unwind_pad directive.  */
 
 static void
 
 static void
-do_xsc_mar (str)
-     char * str;
+s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rdlo, rdhi;
-
-  if (accum0_required_here (& str) == FAIL)
-    inst.error = ERR_NO_ACCUM;
+  int offset;
 
 
-  else if (skip_past_comma (& str) == FAIL
-          || (rdlo = reg_required_here (& str, 12)) == FAIL)
-    inst.error = BAD_ARGS;
+  if (immediate_for_directive (&offset) == FAIL)
+    return;
 
 
-  else if (skip_past_comma (& str) == FAIL
-          || (rdhi = reg_required_here (& str, 16)) == FAIL)
-    inst.error = BAD_ARGS;
+  if (offset & 3)
+    {
+      as_bad (_("stack increment must be multiple of 4"));
+      ignore_rest_of_line ();
+      return;
+    }
 
 
-  /* inst.instruction has now been zapped with both rdlo and rdhi.  */
-  else if (rdlo == REG_PC || rdhi == REG_PC)
-    inst.error = BAD_PC;       /* Undefined result if rdlo or rdhi is R15.  */
+  /* Don't generate any opcodes, just record the details for later.  */
+  unwind.frame_size += offset;
+  unwind.pending_offset += offset;
 
 
-  else
-    end_of_line (str);
+  demand_empty_rest_of_line ();
 }
 
 }
 
-/* Xscale move-register-accumulator (argument parse)
-
-     MRAcc   RdLo,RdHi,acc0.  */
+/* Parse an unwind_setfp directive.  */
 
 static void
 
 static void
-do_xsc_mra (str)
-     char * str;
+s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rdlo;
-  int rdhi;
+  int sp_reg;
+  int fp_reg;
+  int offset;
 
 
-  skip_whitespace (str);
+  fp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+  if (skip_past_comma (&input_line_pointer) == FAIL)
+    sp_reg = FAIL;
+  else
+    sp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
 
 
-  if ((rdlo = reg_required_here (& str, 12)) == FAIL)
-    inst.error = BAD_ARGS;
+  if (fp_reg == FAIL || sp_reg == FAIL)
+    {
+      as_bad (_("expected <reg>, <reg>"));
+      ignore_rest_of_line ();
+      return;
+    }
 
 
-  else if (skip_past_comma (& str) == FAIL
-          || (rdhi = reg_required_here (& str, 16)) == FAIL)
-    inst.error = BAD_ARGS;
+  /* Optional constant.         */
+  if (skip_past_comma (&input_line_pointer) != FAIL)
+    {
+      if (immediate_for_directive (&offset) == FAIL)
+       return;
+    }
+  else
+    offset = 0;
 
 
-  else if  (skip_past_comma (& str) == FAIL
-           || accum0_required_here (& str) == FAIL)
-    inst.error = ERR_NO_ACCUM;
+  demand_empty_rest_of_line ();
 
 
-  /* inst.instruction has now been zapped with both rdlo and rdhi.  */
-  else if (rdlo == rdhi)
-    inst.error = BAD_ARGS;     /* Undefined result if 2 writes to same reg.  */
+  if (sp_reg != 13 && sp_reg != unwind.fp_reg)
+    {
+      as_bad (_("register must be either sp or set by a previous"
+               "unwind_movsp directive"));
+      return;
+    }
 
 
-  else if (rdlo == REG_PC || rdhi == REG_PC)
-    inst.error = BAD_PC;       /* Undefined result if rdlo or rdhi is R15.  */
+  /* Don't generate any opcodes, just record the information for later.         */
+  unwind.fp_reg = fp_reg;
+  unwind.fp_used = 1;
+  if (sp_reg == 13)
+    unwind.fp_offset = unwind.frame_size - offset;
   else
   else
-    end_of_line (str);
+    unwind.fp_offset -= offset;
 }
 
 }
 
-/* ARMv5TE: Preload-Cache
-
-    PLD <addr_mode>
-
-  Syntactically, like LDR with B=1, W=0, L=1.  */
+/* Parse an unwind_raw directive.  */
 
 static void
 
 static void
-do_pld (str)
-     char * str;
+s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
 {
 {
-  int rd;
+  expressionS exp;
+  /* This is an arbitary limit.         */
+  unsigned char op[16];
+  int count;
 
 
-  skip_whitespace (str);
+  expression (&exp);
+  if (exp.X_op == O_constant
+      && skip_past_comma (&input_line_pointer) != FAIL)
+    {
+      unwind.frame_size += exp.X_add_number;
+      expression (&exp);
+    }
+  else
+    exp.X_op = O_illegal;
 
 
-  if (* str != '[')
+  if (exp.X_op != O_constant)
     {
     {
-      inst.error = _("'[' expected after PLD mnemonic");
+      as_bad (_("expected <offset>, <opcode>"));
+      ignore_rest_of_line ();
       return;
     }
 
       return;
     }
 
-  ++str;
-  skip_whitespace (str);
-
-  if ((rd = reg_required_here (& str, 16)) == FAIL)
-    return;
-
-  skip_whitespace (str);
-
-  if (*str == ']')
-    {
-      /* [Rn], ... ?  */
-      ++str;
-      skip_whitespace (str);
+  count = 0;
 
 
-      /* Post-indexed addressing is not allowed with PLD.  */
-      if (skip_past_comma (&str) == SUCCESS)
-       {
-         inst.error
-           = _("post-indexed expression used in preload instruction");
-         return;
-       }
-      else if (*str == '!') /* [Rn]! */
-       {
-         inst.error = _("writeback used in preload instruction");
-         ++str;
-       }
-      else /* [Rn] */
-       inst.instruction |= INDEX_UP | PRE_INDEX;
-    }
-  else /* [Rn, ...] */
+  /* Parse the opcode. */
+  for (;;)
     {
     {
-      if (skip_past_comma (& str) == FAIL)
+      if (count >= 16)
        {
        {
-         inst.error = _("pre-indexed expression expected");
-         return;
+         as_bad (_("unwind opcode too long"));
+         ignore_rest_of_line ();
        }
        }
-
-      if (ldst_extend (&str) == FAIL)
-       return;
-
-      skip_whitespace (str);
-
-      if (* str != ']')
+      if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
        {
        {
-         inst.error = _("missing ]");
+         as_bad (_("invalid unwind opcode"));
+         ignore_rest_of_line ();
          return;
        }
          return;
        }
+      op[count++] = exp.X_add_number;
 
 
-      ++ str;
-      skip_whitespace (str);
-
-      if (* str == '!') /* [Rn]! */
-       {
-         inst.error = _("writeback used in preload instruction");
-         ++ str;
-       }
+      /* Parse the next byte.  */
+      if (skip_past_comma (&input_line_pointer) == FAIL)
+       break;
 
 
-      inst.instruction |= PRE_INDEX;
+      expression (&exp);
     }
 
     }
 
-  end_of_line (str);
-}
+  /* Add the opcode bytes in reverse order.  */
+  while (count--)
+    add_unwind_opcode (op[count], 1);
 
 
-/* ARMv5TE load-consecutive (argument parse)
-   Mode is like LDRH.
+  demand_empty_rest_of_line ();
+}
+#endif /* OBJ_ELF */
 
 
-     LDRccD R, mode
-     STRccD R, mode.  */
+/* This table describes all the machine specific pseudo-ops the assembler
+   has to support.  The fields are:
+     pseudo-op name without dot
+     function to call to execute this pseudo-op
+     Integer arg to pass to the function.  */
 
 
-static void
-do_ldrd (str)
-     char * str;
+const pseudo_typeS md_pseudo_table[] =
 {
 {
-  int rd;
-  int rn;
+  /* Never called because '.req' does not start a line.         */
+  { "req",        s_req,         0 },
+  { "unreq",      s_unreq,       0 },
+  { "bss",        s_bss,         0 },
+  { "align",      s_align,       0 },
+  { "arm",        s_arm,         0 },
+  { "thumb",      s_thumb,       0 },
+  { "code",       s_code,        0 },
+  { "force_thumb", s_force_thumb, 0 },
+  { "thumb_func",  s_thumb_func,  0 },
+  { "thumb_set",   s_thumb_set,          0 },
+  { "even",       s_even,        0 },
+  { "ltorg",      s_ltorg,       0 },
+  { "pool",       s_ltorg,       0 },
+  { "syntax",     s_syntax,      0 },
+#ifdef OBJ_ELF
+  { "word",       s_arm_elf_cons, 4 },
+  { "long",       s_arm_elf_cons, 4 },
+  { "rel31",      s_arm_rel31,   0 },
+  { "fnstart",         s_arm_unwind_fnstart,   0 },
+  { "fnend",           s_arm_unwind_fnend,     0 },
+  { "cantunwind",      s_arm_unwind_cantunwind, 0 },
+  { "personality",     s_arm_unwind_personality, 0 },
+  { "personalityindex",        s_arm_unwind_personalityindex, 0 },
+  { "handlerdata",     s_arm_unwind_handlerdata, 0 },
+  { "save",            s_arm_unwind_save,      0 },
+  { "movsp",           s_arm_unwind_movsp,     0 },
+  { "pad",             s_arm_unwind_pad,       0 },
+  { "setfp",           s_arm_unwind_setfp,     0 },
+  { "unwind_raw",      s_arm_unwind_raw,       0 },
+#else
+  { "word",       cons, 4},
+#endif
+  { "extend",     float_cons, 'x' },
+  { "ldouble",    float_cons, 'x' },
+  { "packed",     float_cons, 'p' },
+  { 0, 0, 0 }
+};
+\f
+/* Parser functions used exclusively in instruction operands.  */
 
 
-  skip_whitespace (str);
+/* Generic immediate-value read function for use in insn parsing.
+   STR points to the beginning of the immediate (the leading #);
+   VAL receives the value; if the value is outside [MIN, MAX]
+   issue an error.  PREFIX_OPT is true if the immediate prefix is
+   optional.  */
 
 
-  if ((rd = reg_required_here (& str, 12)) == FAIL)
+static int
+parse_immediate (char **str, int *val, int min, int max,
+                bfd_boolean prefix_opt)
+{
+  expressionS exp;
+  my_get_expression (&exp, str, prefix_opt ? GE_OPT_PREFIX : GE_IMM_PREFIX);
+  if (exp.X_op != O_constant)
     {
     {
-      inst.error = BAD_ARGS;
-      return;
+      inst.error = _("constant expression required");
+      return FAIL;
     }
 
     }
 
-  if (skip_past_comma (& str) == FAIL
-      || (rn = ld_mode_required_here (& str)) == FAIL)
+  if (exp.X_add_number < min || exp.X_add_number > max)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.error = _("immediate value out of range");
+      return FAIL;
     }
 
     }
 
-  /* inst.instruction has now been zapped with Rd and the addressing mode.  */
-  if (rd & 1)          /* Unpredictable result if Rd is odd.  */
-    {
-      inst.error = _("destination register must be even");
-      return;
-    }
+  *val = exp.X_add_number;
+  return SUCCESS;
+}
 
 
-  if (rd == REG_LR)
-    {
-      inst.error = _("r14 not allowed here");
-      return;
-    }
+/* Returns the pseudo-register number of an FPA immediate constant,
+   or FAIL if there isn't a valid constant here.  */
+
+static int
+parse_fpa_immediate (char ** str)
+{
+  LITTLENUM_TYPE words[MAX_LITTLENUMS];
+  char *        save_in;
+  expressionS   exp;
+  int           i;
+  int           j;
 
 
-  if (((rd == rn) || (rd + 1 == rn))
-      && ((inst.instruction & WRITE_BACK)
-         || (!(inst.instruction & PRE_INDEX))))
-    as_warn (_("pre/post-indexing used when modified address register is destination"));
+  /* First try and match exact strings, this is to guarantee
+     that some formats will work even for cross assembly.  */
 
 
-  /* For an index-register load, the index register must not overlap the
-     destination (even if not write-back).  */
-  if ((inst.instruction & V4_STR_BIT) == 0
-      && (inst.instruction & HWOFFSET_IMM) == 0)
+  for (i = 0; fp_const[i]; i++)
     {
     {
-      int rm = inst.instruction & 0x0000000f;
+      if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
+       {
+         char *start = *str;
 
 
-      if (rm == rd || (rm == rd + 1))
-       as_warn (_("ldrd destination registers must not overlap index register"));
+         *str += strlen (fp_const[i]);
+         if (is_end_of_line[(unsigned char) **str])
+           return i + 8;
+         *str = start;
+       }
     }
 
     }
 
-  end_of_line (str);
-}
-
-/* Returns the index into fp_values of a floating point number,
-   or -1 if not in the table.  */
-
-static int
-my_get_float_expression (str)
-     char ** str;
-{
-  LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  char *         save_in;
-  expressionS    exp;
-  int            i;
-  int            j;
+  /* Just because we didn't get a match doesn't mean that the constant
+     isn't valid, just that it is in a format that we don't
+     automatically recognize.  Try parsing it with the standard
+     expression routines.  */
 
   memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
 
 
   memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
 
@@ -4409,7 +2919,7 @@ my_get_float_expression (str)
          if (j == MAX_LITTLENUMS)
            {
              *str = save_in;
          if (j == MAX_LITTLENUMS)
            {
              *str = save_in;
-             return i;
+             return i + 8;
            }
        }
     }
            }
        }
     }
@@ -4423,7 +2933,7 @@ my_get_float_expression (str)
       && exp.X_add_number < 0)
     {
       /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
       && exp.X_add_number < 0)
     {
       /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
-        Ditto for 15.  */
+        Ditto for 15.  */
       if (gen_to_words (words, 5, (long) 15) == 0)
        {
          for (i = 0; i < NUM_FLOAT_VALS; i++)
       if (gen_to_words (words, 5, (long) 15) == 0)
        {
          for (i = 0; i < NUM_FLOAT_VALS; i++)
@@ -4438,7 +2948,7 @@ my_get_float_expression (str)
                {
                  *str = input_line_pointer;
                  input_line_pointer = save_in;
                {
                  *str = input_line_pointer;
                  input_line_pointer = save_in;
-                 return i;
+                 return i + 8;
                }
            }
        }
                }
            }
        }
@@ -4446,1338 +2956,1700 @@ my_get_float_expression (str)
 
   *str = input_line_pointer;
   input_line_pointer = save_in;
 
   *str = input_line_pointer;
   input_line_pointer = save_in;
-  return -1;
+  inst.error = _("invalid FPA immediate expression");
+  return FAIL;
 }
 
 }
 
-/* Return TRUE if anything in the expression is a bignum.  */
+/* Shift operands.  */
+enum shift_kind
+{
+  SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX
+};
 
 
-static int
-walk_no_bignums (sp)
-     symbolS * sp;
+struct asm_shift_name
 {
 {
-  if (symbol_get_value_expression (sp)->X_op == O_big)
-    return 1;
+  const char     *name;
+  enum shift_kind  kind;
+};
 
 
-  if (symbol_get_value_expression (sp)->X_add_symbol)
-    {
-      return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
-             || (symbol_get_value_expression (sp)->X_op_symbol
-                 && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
-    }
+/* Third argument to parse_shift.  */
+enum parse_shift_mode
+{
+  NO_SHIFT_RESTRICT,           /* Any kind of shift is accepted.  */
+  SHIFT_IMMEDIATE,             /* Shift operand must be an immediate.  */
+  SHIFT_LSL_OR_ASR_IMMEDIATE,  /* Shift must be LSL or ASR immediate.  */
+  SHIFT_ASR_IMMEDIATE,         /* Shift must be ASR immediate.  */
+  SHIFT_LSL_IMMEDIATE,         /* Shift must be LSL immediate.  */
+};
 
 
-  return 0;
-}
+/* Parse a <shift> specifier on an ARM data processing instruction.
+   This has three forms:
 
 
-static int in_my_get_expression = 0;
+     (LSL|LSR|ASL|ASR|ROR) Rs
+     (LSL|LSR|ASL|ASR|ROR) #imm
+     RRX
+
+   Note that ASL is assimilated to LSL in the instruction encoding, and
+   RRX to ROR #0 (which cannot be written as such).  */
 
 static int
 
 static int
-my_get_expression (ep, str)
-     expressionS * ep;
-     char ** str;
+parse_shift (char **str, int i, enum parse_shift_mode mode)
 {
 {
-  char * save_in;
-  segT   seg;
+  const struct asm_shift_name *shift_name;
+  enum shift_kind shift;
+  char *s = *str;
+  char *p = s;
+  int reg;
 
 
-  save_in = input_line_pointer;
-  input_line_pointer = *str;
-  in_my_get_expression = 1;
-  seg = expression (ep);
-  in_my_get_expression = 0;
+  for (p = *str; ISALPHA (*p); p++)
+    ;
 
 
-  if (ep->X_op == O_illegal)
+  if (p == *str)
     {
     {
-      /* We found a bad expression in md_operand().  */
-      *str = input_line_pointer;
-      input_line_pointer = save_in;
-      return 1;
+      inst.error = _("shift expression expected");
+      return FAIL;
     }
 
     }
 
-#ifdef OBJ_AOUT
-  if (seg != absolute_section
-      && seg != text_section
-      && seg != data_section
-      && seg != bss_section
-      && seg != undefined_section)
-    {
-      inst.error = _("bad_segment");
-      *str = input_line_pointer;
-      input_line_pointer = save_in;
-      return 1;
-    }
-#endif
+  shift_name = hash_find_n (arm_shift_hsh, *str, p - *str);
 
 
-  /* Get rid of any bignums now, so that we don't generate an error for which
-     we can't establish a line number later on.  Big numbers are never valid
-     in instructions, which is where this routine is always called.  */
-  if (ep->X_op == O_big
-      || (ep->X_add_symbol
-         && (walk_no_bignums (ep->X_add_symbol)
-             || (ep->X_op_symbol
-                 && walk_no_bignums (ep->X_op_symbol)))))
+  if (shift_name == NULL)
     {
     {
-      inst.error = _("invalid constant");
-      *str = input_line_pointer;
-      input_line_pointer = save_in;
-      return 1;
+      inst.error = _("shift expression expected");
+      return FAIL;
     }
 
     }
 
-  *str = input_line_pointer;
-  input_line_pointer = save_in;
-  return 0;
-}
+  shift = shift_name->kind;
 
 
-/* We handle all bad expressions here, so that we can report the faulty
-   instruction in the error message.  */
-void
-md_operand (expr)
-     expressionS *expr;
-{
-  if (in_my_get_expression)
+  switch (mode)
     {
     {
-      expr->X_op = O_illegal;
-      if (inst.error == NULL)
-       inst.error = _("bad expression");
-    }
-}
+    case NO_SHIFT_RESTRICT:
+    case SHIFT_IMMEDIATE:   break;
 
 
-/* UNRESTRICT should be one if <shift> <register> is permitted for this
-   instruction.  */
+    case SHIFT_LSL_OR_ASR_IMMEDIATE:
+      if (shift != SHIFT_LSL && shift != SHIFT_ASR)
+       {
+         inst.error = _("'LSL' or 'ASR' required");
+         return FAIL;
+       }
+      break;
 
 
-static int
-decode_shift (str, unrestrict)
-     char ** str;
-     int     unrestrict;
-{
-  const struct asm_shift_name * shift;
-  char * p;
-  char   c;
+    case SHIFT_LSL_IMMEDIATE:
+      if (shift != SHIFT_LSL)
+       {
+         inst.error = _("'LSL' required");
+         return FAIL;
+       }
+      break;
 
 
-  skip_whitespace (* str);
+    case SHIFT_ASR_IMMEDIATE:
+      if (shift != SHIFT_ASR)
+       {
+         inst.error = _("'ASR' required");
+         return FAIL;
+       }
+      break;
 
 
-  for (p = * str; ISALPHA (* p); p ++)
-    ;
+    default: abort ();
+    }
 
 
-  if (p == * str)
+  if (shift != SHIFT_RRX)
     {
     {
-      inst.error = _("shift expression expected");
-      return FAIL;
+      /* Whitespace can appear here if the next thing is a bare digit. */
+      skip_whitespace (p);
+
+      if (mode == NO_SHIFT_RESTRICT
+         && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+       {
+         inst.operands[i].imm = reg;
+         inst.operands[i].immisreg = 1;
+       }
+      else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+       return FAIL;
     }
     }
+  inst.operands[i].shift_kind = shift;
+  inst.operands[i].shifted = 1;
+  *str = p;
+  return SUCCESS;
+}
 
 
-  c = * p;
-  * p = '\0';
-  shift = (const struct asm_shift_name *) hash_find (arm_shift_hsh, * str);
-  * p = c;
+/* Parse a <shifter_operand> for an ARM data processing instruction:
 
 
-  if (shift == NULL)
-    {
-      inst.error = _("shift expression expected");
-      return FAIL;
-    }
+      #<immediate>
+      #<immediate>, <rotate>
+      <Rm>
+      <Rm>, <shift>
+
+   where <shift> is defined by parse_shift above, and <rotate> is a
+   multiple of 2 between 0 and 30.  Validation of immediate operands
+   is deferred to md_apply_fix.  */
 
 
-  assert (shift->properties->index == shift_properties[shift->properties->index].index);
+static int
+parse_shifter_operand (char **str, int i)
+{
+  int value;
+  expressionS expr;
 
 
-  if (shift->properties->index == SHIFT_RRX)
+  if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL)
     {
     {
-      * str = p;
-      inst.instruction |= shift->properties->bit_field;
-      return SUCCESS;
-    }
+      inst.operands[i].reg = value;
+      inst.operands[i].isreg = 1;
 
 
-  skip_whitespace (p);
+      /* parse_shift will override this if appropriate */
+      inst.reloc.exp.X_op = O_constant;
+      inst.reloc.exp.X_add_number = 0;
 
 
-  if (unrestrict && reg_required_here (& p, 8) != FAIL)
-    {
-      inst.instruction |= shift->properties->bit_field | SHIFT_BY_REG;
-      * str = p;
-      return SUCCESS;
-    }
-  else if (! is_immediate_prefix (* p))
-    {
-      inst.error = (unrestrict
-                   ? _("shift requires register or #expression")
-                   : _("shift requires #expression"));
-      * str = p;
-      return FAIL;
-    }
+      if (skip_past_comma (str) == FAIL)
+       return SUCCESS;
 
 
-  inst.error = NULL;
-  p ++;
+      /* Shift operation on register.  */
+      return parse_shift (str, i, NO_SHIFT_RESTRICT);
+    }
 
 
-  if (my_get_expression (& inst.reloc.exp, & p))
+  if (my_get_expression (&inst.reloc.exp, str, GE_IMM_PREFIX))
     return FAIL;
 
     return FAIL;
 
-  /* Validate some simple #expressions.  */
-  if (inst.reloc.exp.X_op == O_constant)
+  if (skip_past_comma (str) == SUCCESS)
     {
     {
-      unsigned num = inst.reloc.exp.X_add_number;
-
-      /* Reject operations greater than 32.  */
-      if (num > 32
-         /* Reject a shift of 0 unless the mode allows it.  */
-         || (num == 0 && shift->properties->allows_0 == 0)
-         /* Reject a shift of 32 unless the mode allows it.  */
-         || (num == 32 && shift->properties->allows_32 == 0)
-         )
-       {
-         /* As a special case we allow a shift of zero for
-            modes that do not support it to be recoded as an
-            logical shift left of zero (ie nothing).  We warn
-            about this though.  */
-         if (num == 0)
-           {
-             as_warn (_("shift of 0 ignored."));
-             shift = & shift_names[0];
-             assert (shift->properties->index == SHIFT_LSL);
-           }
-         else
-           {
-             inst.error = _("invalid immediate shift");
-             return FAIL;
-           }
+      /* #x, y -- ie explicit rotation by Y.  */
+      if (my_get_expression (&expr, str, GE_NO_PREFIX))
+       return FAIL;
+
+      if (expr.X_op != O_constant || inst.reloc.exp.X_op != O_constant)
+       {
+         inst.error = _("constant expression expected");
+         return FAIL;
        }
 
        }
 
-      /* Shifts of 32 are encoded as 0, for those shifts that
-        support it.  */
-      if (num == 32)
-       num = 0;
+      value = expr.X_add_number;
+      if (value < 0 || value > 30 || value % 2 != 0)
+       {
+         inst.error = _("invalid rotation");
+         return FAIL;
+       }
+      if (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 255)
+       {
+         inst.error = _("invalid constant");
+         return FAIL;
+       }
 
 
-      inst.instruction |= (num << 7) | shift->properties->bit_field;
-    }
-  else
-    {
-      inst.reloc.type   = BFD_RELOC_ARM_SHIFT_IMM;
-      inst.reloc.pc_rel = 0;
-      inst.instruction |= shift->properties->bit_field;
+      /* Convert to decoded value.  md_apply_fix will put it back.  */
+      inst.reloc.exp.X_add_number
+       = (((inst.reloc.exp.X_add_number << (32 - value))
+           | (inst.reloc.exp.X_add_number >> value)) & 0xffffffff);
     }
 
     }
 
-  * str = p;
+  inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+  inst.reloc.pc_rel = 0;
   return SUCCESS;
 }
 
   return SUCCESS;
 }
 
-/* Do those data_ops which can take a negative immediate constant
-   by altering the instuction.  A bit of a hack really.
-        MOV <-> MVN
-        AND <-> BIC
-        ADC <-> SBC
-        by inverting the second operand, and
-        ADD <-> SUB
-        CMP <-> CMN
-        by negating the second operand.  */
+/* Parse all forms of an ARM address expression.  Information is written
+   to inst.operands[i] and/or inst.reloc.
 
 
-static int
-negate_data_op (instruction, value)
-     unsigned long * instruction;
-     unsigned long   value;
-{
-  int op, new_inst;
-  unsigned long negated, inverted;
+   Preindexed addressing (.preind=1):
 
 
-  negated = validate_immediate (-value);
-  inverted = validate_immediate (~value);
+   [Rn, #offset]       .reg=Rn .reloc.exp=offset
+   [Rn, +/-Rm]        .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+   [Rn, +/-Rm, shift]  .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+                      .shift_kind=shift .reloc.exp=shift_imm
 
 
-  op = (*instruction >> DATA_OP_SHIFT) & 0xf;
-  switch (op)
-    {
-      /* First negates.  */
-    case OPCODE_SUB:             /* ADD <-> SUB  */
-      new_inst = OPCODE_ADD;
-      value = negated;
-      break;
+   These three may have a trailing ! which causes .writeback to be set also.
 
 
-    case OPCODE_ADD:
-      new_inst = OPCODE_SUB;
-      value = negated;
-      break;
+   Postindexed addressing (.postind=1, .writeback=1):
 
 
-    case OPCODE_CMP:             /* CMP <-> CMN  */
-      new_inst = OPCODE_CMN;
-      value = negated;
-      break;
+   [Rn], #offset       .reg=Rn .reloc.exp=offset
+   [Rn], +/-Rm        .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+   [Rn], +/-Rm, shift  .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+                      .shift_kind=shift .reloc.exp=shift_imm
 
 
-    case OPCODE_CMN:
-      new_inst = OPCODE_CMP;
-      value = negated;
-      break;
+   Unindexed addressing (.preind=0, .postind=0):
 
 
-      /* Now Inverted ops.  */
-    case OPCODE_MOV:             /* MOV <-> MVN  */
-      new_inst = OPCODE_MVN;
-      value = inverted;
-      break;
+   [Rn], {option}      .reg=Rn .imm=option .immisreg=0
 
 
-    case OPCODE_MVN:
-      new_inst = OPCODE_MOV;
-      value = inverted;
-      break;
+   Other:
 
 
-    case OPCODE_AND:             /* AND <-> BIC  */
-      new_inst = OPCODE_BIC;
-      value = inverted;
-      break;
+   [Rn]{!}            shorthand for [Rn,#0]{!}
+   =immediate         .isreg=0 .reloc.exp=immediate
+   label              .reg=PC .reloc.pc_rel=1 .reloc.exp=label
 
 
-    case OPCODE_BIC:
-      new_inst = OPCODE_AND;
-      value = inverted;
-      break;
+  It is the caller's responsibility to check for addressing modes not
+  supported by the instruction, and to set inst.reloc.type.  */
 
 
-    case OPCODE_ADC:              /* ADC <-> SBC  */
-      new_inst = OPCODE_SBC;
-      value = inverted;
-      break;
+static int
+parse_address (char **str, int i)
+{
+  char *p = *str;
+  int reg;
 
 
-    case OPCODE_SBC:
-      new_inst = OPCODE_ADC;
-      value = inverted;
-      break;
+  if (skip_past_char (&p, '[') == FAIL)
+    {
+      if (skip_past_char (&p, '=') == FAIL)
+       {
+         /* bare address - translate to PC-relative offset */
+         inst.reloc.pc_rel = 1;
+         inst.operands[i].reg = REG_PC;
+         inst.operands[i].isreg = 1;
+         inst.operands[i].preind = 1;
+       }
+      /* else a load-constant pseudo op, no special treatment needed here */
 
 
-      /* We cannot do anything.  */
-    default:
+      if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+       return FAIL;
+
+      *str = p;
+      return SUCCESS;
+    }
+
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+    {
+      inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
       return FAIL;
     }
       return FAIL;
     }
+  inst.operands[i].reg = reg;
+  inst.operands[i].isreg = 1;
 
 
-  if (value == (unsigned) FAIL)
-    return FAIL;
+  if (skip_past_comma (&p) == SUCCESS)
+    {
+      inst.operands[i].preind = 1;
 
 
-  *instruction &= OPCODE_MASK;
-  *instruction |= new_inst << DATA_OP_SHIFT;
-  return value;
-}
+      if (*p == '+') p++;
+      else if (*p == '-') p++, inst.operands[i].negative = 1;
 
 
-static int
-data_op2 (str)
-     char ** str;
-{
-  int value;
-  expressionS expr;
+      if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+       {
+         inst.operands[i].imm = reg;
+         inst.operands[i].immisreg = 1;
 
 
-  skip_whitespace (* str);
+         if (skip_past_comma (&p) == SUCCESS)
+           if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+             return FAIL;
+       }
+      else
+       {
+         if (inst.operands[i].negative)
+           {
+             inst.operands[i].negative = 0;
+             p--;
+           }
+         if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+           return FAIL;
+       }
+    }
 
 
-  if (reg_required_here (str, 0) != FAIL)
+  if (skip_past_char (&p, ']') == FAIL)
     {
     {
-      if (skip_past_comma (str) == SUCCESS)
-       /* Shift operation on register.  */
-       return decode_shift (str, NO_SHIFT_RESTRICT);
-
-      return SUCCESS;
+      inst.error = _("']' expected");
+      return FAIL;
     }
     }
-  else
+
+  if (skip_past_char (&p, '!') == SUCCESS)
+    inst.operands[i].writeback = 1;
+
+  else if (skip_past_comma (&p) == SUCCESS)
     {
     {
-      /* Immediate expression.  */
-      if (is_immediate_prefix (**str))
+      if (skip_past_char (&p, '{') == SUCCESS)
        {
        {
-         (*str)++;
-         inst.error = NULL;
-
-         if (my_get_expression (&inst.reloc.exp, str))
+         /* [Rn], {expr} - unindexed, with option */
+         if (parse_immediate (&p, &inst.operands[i].imm,
+                              0, 255, TRUE) == FAIL)
            return FAIL;
 
            return FAIL;
 
-         if (inst.reloc.exp.X_add_symbol)
+         if (skip_past_char (&p, '}') == FAIL)
            {
            {
-             inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-             inst.reloc.pc_rel = 0;
+             inst.error = _("'}' expected at end of 'option' field");
+             return FAIL;
            }
            }
-         else
+         if (inst.operands[i].preind)
            {
            {
-             if (skip_past_comma (str) == SUCCESS)
-               {
-                 /* #x, y -- ie explicit rotation by Y.  */
-                 if (my_get_expression (&expr, str))
-                   return FAIL;
+             inst.error = _("cannot combine index with option");
+             return FAIL;
+           }
+         *str = p;
+         return SUCCESS;
+       }
+      else
+       {
+         inst.operands[i].postind = 1;
+         inst.operands[i].writeback = 1;
 
 
-                 if (expr.X_op != O_constant)
-                   {
-                     inst.error = _("constant expression expected");
-                     return FAIL;
-                   }
+         if (inst.operands[i].preind)
+           {
+             inst.error = _("cannot combine pre- and post-indexing");
+             return FAIL;
+           }
 
 
-                 /* Rotate must be a multiple of 2.  */
-                 if (((unsigned) expr.X_add_number) > 30
-                     || (expr.X_add_number & 1) != 0
-                     || ((unsigned) inst.reloc.exp.X_add_number) > 255)
-                   {
-                     inst.error = _("invalid constant");
-                     return FAIL;
-                   }
-                 inst.instruction |= INST_IMMEDIATE;
-                 inst.instruction |= inst.reloc.exp.X_add_number;
-                 inst.instruction |= expr.X_add_number << 7;
-                 return SUCCESS;
-               }
+         if (*p == '+') p++;
+         else if (*p == '-') p++, inst.operands[i].negative = 1;
 
 
-             /* Implicit rotation, select a suitable one.  */
-             value = validate_immediate (inst.reloc.exp.X_add_number);
+         if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+           {
+             inst.operands[i].imm = reg;
+             inst.operands[i].immisreg = 1;
 
 
-             if (value == FAIL)
+             if (skip_past_comma (&p) == SUCCESS)
+               if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+                 return FAIL;
+           }
+         else
+           {
+             if (inst.operands[i].negative)
                {
                {
-                 /* Can't be done.  Perhaps the code reads something like
-                    "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be OK.  */
-                 if ((value = negate_data_op (&inst.instruction,
-                                              inst.reloc.exp.X_add_number))
-                     == FAIL)
-                   {
-                     inst.error = _("invalid constant");
-                     return FAIL;
-                   }
+                 inst.operands[i].negative = 0;
+                 p--;
                }
                }
-
-             inst.instruction |= value;
+             if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+               return FAIL;
            }
            }
-
-         inst.instruction |= INST_IMMEDIATE;
-         return SUCCESS;
        }
        }
+    }
 
 
-      (*str)++;
-      inst.error = _("register or shift expression expected");
-      return FAIL;
+  /* If at this point neither .preind nor .postind is set, we have a
+     bare [Rn]{!}, which is shorthand for [Rn,#0]{!}.  */
+  if (inst.operands[i].preind == 0 && inst.operands[i].postind == 0)
+    {
+      inst.operands[i].preind = 1;
+      inst.reloc.exp.X_op = O_constant;
+      inst.reloc.exp.X_add_number = 0;
     }
     }
+  *str = p;
+  return SUCCESS;
 }
 
 }
 
+/* Miscellaneous. */
+
+/* Parse a PSR flag operand.  The value returned is FAIL on syntax error,
+   or a bitmask suitable to be or-ed into the ARM msr instruction.  */
 static int
 static int
-fp_op2 (str)
-     char ** str;
+parse_psr (char **str)
 {
 {
-  skip_whitespace (* str);
+  char *p;
+  unsigned long psr_field;
 
 
-  if (fp_reg_required_here (str, 0) != FAIL)
-    return SUCCESS;
+  /* CPSR's and SPSR's can now be lowercase.  This is just a convenience
+     feature for ease of use and backwards compatibility.  */
+  p = *str;
+  if (*p == 's' || *p == 'S')
+    psr_field = SPSR_BIT;
+  else if (*p == 'c' || *p == 'C')
+    psr_field = 0;
   else
   else
-    {
-      /* Immediate expression.  */
-      if (*((*str)++) == '#')
-       {
-         int i;
+    goto error;
 
 
-         inst.error = NULL;
+  p++;
+  if (strncasecmp (p, "PSR", 3) != 0)
+    goto error;
+  p += 3;
 
 
-         skip_whitespace (* str);
+  if (*p == '_')
+    {
+      /* A suffix follows.  */
+      const struct asm_psr *psr;
+      char *start;
 
 
-         /* First try and match exact strings, this is to guarantee
-            that some formats will work even for cross assembly.  */
+      p++;
+      start = p;
 
 
-         for (i = 0; fp_const[i]; i++)
-           {
-             if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
-               {
-                 char *start = *str;
+      do
+       p++;
+      while (ISALNUM (*p) || *p == '_');
 
 
-                 *str += strlen (fp_const[i]);
-                 if (is_end_of_line[(unsigned char) **str])
-                   {
-                     inst.instruction |= i + 8;
-                     return SUCCESS;
-                   }
-                 *str = start;
-               }
-           }
+      psr = hash_find_n (arm_psr_hsh, start, p - start);
+      if (!psr)
+       goto error;
 
 
-         /* Just because we didn't get a match doesn't mean that the
-            constant isn't valid, just that it is in a format that we
-            don't automatically recognize.  Try parsing it with
-            the standard expression routines.  */
-         if ((i = my_get_float_expression (str)) >= 0)
-           {
-             inst.instruction |= i + 8;
-             return SUCCESS;
-           }
+      psr_field |= psr->field;
+    }
+  else
+    {
+      if (ISALNUM (*p))
+       goto error;    /* Garbage after "[CS]PSR".  */
 
 
-         inst.error = _("invalid floating point immediate expression");
-         return FAIL;
-       }
-      inst.error =
-       _("floating point register or immediate expression expected");
-      return FAIL;
+      psr_field |= (PSR_c | PSR_f);
     }
     }
+  *str = p;
+  return psr_field;
+
+ error:
+  inst.error = _("flag for {c}psr instruction expected");
+  return FAIL;
 }
 
 }
 
-static void
-do_arit (str)
-     char * str;
+/* Parse the flags argument to CPSI[ED].  Returns FAIL on error, or a
+   value suitable for splatting into the AIF field of the instruction. */
+
+static int
+parse_cps_flags (char **str)
 {
 {
-  skip_whitespace (str);
+  int val = 0;
+  int saw_a_flag = 0;
+  char *s = *str;
 
 
-  if (reg_required_here (&str, 12) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 16) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || data_op2 (&str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  for (;;)
+    switch (*s++)
+      {
+      case '\0': case ',':
+       goto done;
 
 
-  end_of_line (str);
-  return;
-}
+      case 'a': case 'A': saw_a_flag = 1; val |= 0x4; break;
+      case 'i': case 'I': saw_a_flag = 1; val |= 0x2; break;
+      case 'f': case 'F': saw_a_flag = 1; val |= 0x1; break;
 
 
-static void
-do_adr (str)
-     char * str;
-{
-  /* This is a pseudo-op of the form "adr rd, label" to be converted
-     into a relative address of the form "add rd, pc, #label-.-8".  */
-  skip_whitespace (str);
+      default:
+       inst.error = _("unrecognized CPS flag");
+       return FAIL;
+      }
 
 
-  if (reg_required_here (&str, 12) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || my_get_expression (&inst.reloc.exp, &str))
+ done:
+  if (saw_a_flag == 0)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.error = _("missing CPS flags");
+      return FAIL;
     }
 
     }
 
-  /* Frag hacking will turn this into a sub instruction if the offset turns
-     out to be negative.  */
-  inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust.  */
-  inst.reloc.pc_rel = 1;
-
-  end_of_line (str);
+  *str = s - 1;
+  return val;
 }
 
 }
 
-static void
-do_adrl (str)
-     char * str;
-{
-  /* This is a pseudo-op of the form "adrl rd, label" to be converted
-     into a relative address of the form:
-     add rd, pc, #low(label-.-8)"
-     add rd, rd, #high(label-.-8)"  */
+/* Parse an endian specifier ("BE" or "LE", case insensitive);
+   returns 0 for big-endian, 1 for little-endian, FAIL for an error.  */
 
 
-  skip_whitespace (str);
+static int
+parse_endian_specifier (char **str)
+{
+  int little_endian;
+  char *s = *str;
 
 
-  if (reg_required_here (&str, 12) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || my_get_expression (&inst.reloc.exp, &str))
+  if (strncasecmp (s, "BE", 2))
+    little_endian = 0;
+  else if (strncasecmp (s, "LE", 2))
+    little_endian = 1;
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-
-      return;
+      inst.error = _("valid endian specifiers are be or le");
+      return FAIL;
     }
 
     }
 
-  end_of_line (str);
-  /* Frag hacking will turn this into a sub instruction if the offset turns
-     out to be negative.  */
-  inst.reloc.type              = BFD_RELOC_ARM_ADRL_IMMEDIATE;
-  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust  */
-  inst.reloc.pc_rel            = 1;
-  inst.size                    = INSN_SIZE * 2;
+  if (ISALNUM (s[2]) || s[2] == '_')
+    {
+      inst.error = _("valid endian specifiers are be or le");
+      return FAIL;
+    }
 
 
-  return;
+  *str = s + 2;
+  return little_endian;
 }
 
 }
 
-static void
-do_cmp (str)
-     char * str;
+/* Parse a rotation specifier: ROR #0, #8, #16, #24.  *val receives a
+   value suitable for poking into the rotate field of an sxt or sxta
+   instruction, or FAIL on error.  */
+
+static int
+parse_ror (char **str)
 {
 {
-  skip_whitespace (str);
+  int rot;
+  char *s = *str;
 
 
-  if (reg_required_here (&str, 16) == FAIL)
+  if (strncasecmp (s, "ROR", 3) == 0)
+    s += 3;
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.error = _("missing rotation field after comma");
+      return FAIL;
     }
 
     }
 
-  if (skip_past_comma (&str) == FAIL
-      || data_op2 (&str) == FAIL)
+  if (parse_immediate (&s, &rot, 0, 24, FALSE) == FAIL)
+    return FAIL;
+
+  switch (rot)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+    case  0: *str = s; return 0x0;
+    case  8: *str = s; return 0x1;
+    case 16: *str = s; return 0x2;
+    case 24: *str = s; return 0x3;
 
 
-  end_of_line (str);
-  return;
+    default:
+      inst.error = _("rotation can only be 0, 8, 16, or 24");
+      return FAIL;
+    }
 }
 
 }
 
-static void
-do_mov (str)
-     char * str;
+/* Parse a conditional code (from conds[] below).  The value returned is in the
+   range 0 .. 14, or FAIL.  */
+static int
+parse_cond (char **str)
 {
 {
-  skip_whitespace (str);
+  char *p, *q;
+  const struct asm_cond *c;
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  p = q = *str;
+  while (ISALPHA (*q))
+    q++;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || data_op2 (&str) == FAIL)
+  c = hash_find_n (arm_cond_hsh, p, q - p);
+  if (!c)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.error = _("condition required");
+      return FAIL;
     }
 
     }
 
-  end_of_line (str);
-  return;
+  *str = q;
+  return c->value;
 }
 
 }
 
+/* Parse the operands of a table branch instruction.  Similar to a memory
+   operand.  */
 static int
 static int
-ldst_extend (str)
-     char ** str;
+parse_tb (char **str)
 {
 {
-  int add = INDEX_UP;
-
-  switch (**str)
-    {
-    case '#':
-    case '$':
-      (*str)++;
-      if (my_get_expression (& inst.reloc.exp, str))
-       return FAIL;
+  char * p = *str;
+  int reg;
 
 
-      if (inst.reloc.exp.X_op == O_constant)
-       {
-         int value = inst.reloc.exp.X_add_number;
+  if (skip_past_char (&p, '[') == FAIL)
+    return FAIL;
 
 
-         if (value < -4095 || value > 4095)
-           {
-             inst.error = _("address offset too large");
-             return FAIL;
-           }
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+    {
+      inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+      return FAIL;
+    }
+  inst.operands[0].reg = reg;
 
 
-         if (value < 0)
-           {
-             value = -value;
-             add = 0;
-           }
+  if (skip_past_comma (&p) == FAIL)
+    return FAIL;
+  
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+    {
+      inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+      return FAIL;
+    }
+  inst.operands[0].imm = reg;
 
 
-         inst.instruction |= add | value;
-       }
-      else
+  if (skip_past_comma (&p) == SUCCESS)
+    {
+      if (parse_shift (&p, 0, SHIFT_LSL_IMMEDIATE) == FAIL)
+       return FAIL;
+      if (inst.reloc.exp.X_add_number != 1)
        {
        {
-         inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-         inst.reloc.pc_rel = 0;
+         inst.error = _("invalid shift");
+         return FAIL;
        }
        }
-      return SUCCESS;
-
-    case '-':
-      add = 0;
-      /* Fall through.  */
-
-    case '+':
-      (*str)++;
-      /* Fall through.  */
-
-    default:
-      if (reg_required_here (str, 0) == FAIL)
-       return FAIL;
-
-      inst.instruction |= add | OFFSET_REG;
-      if (skip_past_comma (str) == SUCCESS)
-       return decode_shift (str, SHIFT_RESTRICT);
-
-      return SUCCESS;
+      inst.operands[0].shifted = 1;
     }
     }
-}
-
-static void
-do_ldst (str)
-     char *        str;
-{
-  int pre_inc = 0;
-  int conflict_reg;
-  int value;
-
-  skip_whitespace (str);
 
 
-  if ((conflict_reg = reg_required_here (&str, 12)) == FAIL)
+  if (skip_past_char (&p, ']') == FAIL)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.error = _("']' expected");
+      return FAIL;
     }
     }
+  *str = p;
+  return SUCCESS;
+}
 
 
-  if (skip_past_comma (&str) == FAIL)
-    {
-      inst.error = _("address expected");
-      return;
-    }
+/* Matcher codes for parse_operands.  */
+enum operand_parse_code
+{
+  OP_stop,     /* end of line */
+
+  OP_RR,       /* ARM register */
+  OP_RRnpc,    /* ARM register, not r15 */
+  OP_RRnpcb,   /* ARM register, not r15, in square brackets */
+  OP_RRw,      /* ARM register, not r15, optional trailing ! */
+  OP_RCP,      /* Coprocessor number */
+  OP_RCN,      /* Coprocessor register */
+  OP_RF,       /* FPA register */
+  OP_RVS,      /* VFP single precision register */
+  OP_RVD,      /* VFP double precision register */
+  OP_RVC,      /* VFP control register */
+  OP_RMF,      /* Maverick F register */
+  OP_RMD,      /* Maverick D register */
+  OP_RMFX,     /* Maverick FX register */
+  OP_RMDX,     /* Maverick DX register */
+  OP_RMAX,     /* Maverick AX register */
+  OP_RMDS,     /* Maverick DSPSC register */
+  OP_RIWR,     /* iWMMXt wR register */
+  OP_RIWC,     /* iWMMXt wC register */
+  OP_RIWG,     /* iWMMXt wCG register */
+  OP_RXA,      /* XScale accumulator register */
+
+  OP_REGLST,   /* ARM register list */
+  OP_VRSLST,   /* VFP single-precision register list */
+  OP_VRDLST,   /* VFP double-precision register list */
+
+  OP_I7,       /* immediate value 0 .. 7 */
+  OP_I15,      /*                 0 .. 15 */
+  OP_I16,      /*                 1 .. 16 */
+  OP_I31,      /*                 0 .. 31 */
+  OP_I31w,     /*                 0 .. 31, optional trailing ! */
+  OP_I32,      /*                 1 .. 32 */
+  OP_I63s,     /*               -64 .. 63 */
+  OP_I255,     /*                 0 .. 255 */
+  OP_Iffff,    /*                 0 .. 65535 */
+
+  OP_I4b,      /* immediate, prefix optional, 1 .. 4 */
+  OP_I7b,      /*                             0 .. 7 */
+  OP_I15b,     /*                             0 .. 15 */
+  OP_I31b,     /*                             0 .. 31 */
+
+  OP_SH,       /* shifter operand */
+  OP_ADDR,     /* Memory address expression (any mode) */
+  OP_EXP,      /* arbitrary expression */
+  OP_EXPi,     /* same, with optional immediate prefix */
+  OP_EXPr,     /* same, with optional relocation suffix */
+
+  OP_CPSF,     /* CPS flags */
+  OP_ENDI,     /* Endianness specifier */
+  OP_PSR,      /* CPSR/SPSR mask for msr */
+  OP_COND,     /* conditional code */
+  OP_TB,       /* Table branch.  */
+
+  OP_RRnpc_I0, /* ARM register or literal 0 */
+  OP_RR_EXr,   /* ARM register or expression with opt. reloc suff. */
+  OP_RR_EXi,   /* ARM register or expression with imm prefix */
+  OP_RF_IF,    /* FPA register or immediate */
+  OP_RIWR_RIWC, /* iWMMXt R or C reg */
+
+  /* Optional operands.         */
+  OP_oI7b,      /* immediate, prefix optional, 0 .. 7 */
+  OP_oI31b,     /*                             0 .. 31 */
+  OP_oIffffb,   /*                             0 .. 65535 */
+  OP_oI255c,    /*       curly-brace enclosed, 0 .. 255 */
+
+  OP_oRR,       /* ARM register */
+  OP_oRRnpc,    /* ARM register, not the PC */
+  OP_oSHll,     /* LSL immediate */
+  OP_oSHar,     /* ASR immediate */
+  OP_oSHllar,   /* LSL or ASR immediate */
+  OP_oROR,      /* ROR 0/8/16/24 */
+
+  OP_FIRST_OPTIONAL = OP_oI7b
+};
 
 
-  if (*str == '[')
-    {
-      int reg;
+/* Generic instruction operand parser. This does no encoding and no
+   semantic validation; it merely squirrels values away in the inst
+   structure.  Returns SUCCESS or FAIL depending on whether the
+   specified grammar matched.  */
+static int
+parse_operands (char *str, const unsigned char *pattern)
+{
+  unsigned const char *upat = pattern;
+  char *backtrack_pos = 0;
+  const char *backtrack_error = 0;
+  int i, val, backtrack_index = 0;
+
+#define po_char_or_fail(chr) do {              \
+  if (skip_past_char (&str, chr) == FAIL)      \
+    goto bad_args;                             \
+} while (0)
+
+#define po_reg_or_fail(regtype) do {                   \
+  val = arm_reg_parse (&str, regtype);                 \
+  if (val == FAIL)                                     \
+    {                                                  \
+      inst.error = _(reg_expected_msgs[regtype]);      \
+      goto failure;                                    \
+    }                                                  \
+  inst.operands[i].reg = val;                          \
+  inst.operands[i].isreg = 1;                          \
+} while (0)
+
+#define po_reg_or_goto(regtype, label) do {    \
+  val = arm_reg_parse (&str, regtype);         \
+  if (val == FAIL)                             \
+    goto label;                                        \
+                                               \
+  inst.operands[i].reg = val;                  \
+  inst.operands[i].isreg = 1;                  \
+} while (0)
+
+#define po_imm_or_fail(min, max, popt) do {                    \
+  if (parse_immediate (&str, &val, min, max, popt) == FAIL)    \
+    goto failure;                                              \
+  inst.operands[i].imm = val;                                  \
+} while (0)
+
+#define po_misc_or_fail(expr) do {             \
+  if (expr)                                    \
+    goto failure;                              \
+} while (0)
 
 
-      str++;
+  skip_whitespace (str);
 
 
-      skip_whitespace (str);
+  for (i = 0; upat[i] != OP_stop; i++)
+    {
+      if (upat[i] >= OP_FIRST_OPTIONAL)
+       {
+         /* Remember where we are in case we need to backtrack.  */
+         assert (!backtrack_pos);
+         backtrack_pos = str;
+         backtrack_error = inst.error;
+         backtrack_index = i;
+       }
+
+      if (i > 0)
+       po_char_or_fail (',');
+
+      switch (upat[i])
+       {
+         /* Registers */
+       case OP_oRRnpc:
+       case OP_RRnpc:
+       case OP_oRR:
+       case OP_RR:    po_reg_or_fail (REG_TYPE_RN);      break;
+       case OP_RCP:   po_reg_or_fail (REG_TYPE_CP);      break;
+       case OP_RCN:   po_reg_or_fail (REG_TYPE_CN);      break;
+       case OP_RF:    po_reg_or_fail (REG_TYPE_FN);      break;
+       case OP_RVS:   po_reg_or_fail (REG_TYPE_VFS);     break;
+       case OP_RVD:   po_reg_or_fail (REG_TYPE_VFD);     break;
+       case OP_RVC:   po_reg_or_fail (REG_TYPE_VFC);     break;
+       case OP_RMF:   po_reg_or_fail (REG_TYPE_MVF);     break;
+       case OP_RMD:   po_reg_or_fail (REG_TYPE_MVD);     break;
+       case OP_RMFX:  po_reg_or_fail (REG_TYPE_MVFX);    break;
+       case OP_RMDX:  po_reg_or_fail (REG_TYPE_MVDX);    break;
+       case OP_RMAX:  po_reg_or_fail (REG_TYPE_MVAX);    break;
+       case OP_RMDS:  po_reg_or_fail (REG_TYPE_DSPSC);   break;
+       case OP_RIWR:  po_reg_or_fail (REG_TYPE_MMXWR);   break;
+       case OP_RIWC:  po_reg_or_fail (REG_TYPE_MMXWC);   break;
+       case OP_RIWG:  po_reg_or_fail (REG_TYPE_MMXWCG);  break;
+       case OP_RXA:   po_reg_or_fail (REG_TYPE_XSCALE);  break;
+
+       case OP_RRnpcb:
+         po_char_or_fail ('[');
+         po_reg_or_fail  (REG_TYPE_RN);
+         po_char_or_fail (']');
+         break;
 
 
-      if ((reg = reg_required_here (&str, 16)) == FAIL)
-       return;
+       case OP_RRw:
+         po_reg_or_fail (REG_TYPE_RN);
+         if (skip_past_char (&str, '!') == SUCCESS)
+           inst.operands[i].writeback = 1;
+         break;
+
+         /* Immediates */
+       case OP_I7:      po_imm_or_fail (  0,      7, FALSE);   break;
+       case OP_I15:     po_imm_or_fail (  0,     15, FALSE);   break;
+       case OP_I16:     po_imm_or_fail (  1,     16, FALSE);   break;
+       case OP_I31:     po_imm_or_fail (  0,     31, FALSE);   break;
+       case OP_I32:     po_imm_or_fail (  1,     32, FALSE);   break;
+       case OP_I63s:    po_imm_or_fail (-64,     63, FALSE);   break;
+       case OP_I255:    po_imm_or_fail (  0,    255, FALSE);   break;
+       case OP_Iffff:   po_imm_or_fail (  0, 0xffff, FALSE);   break;
+
+       case OP_I4b:     po_imm_or_fail (  1,      4, TRUE);    break;
+       case OP_oI7b:
+       case OP_I7b:     po_imm_or_fail (  0,      7, TRUE);    break;
+       case OP_I15b:    po_imm_or_fail (  0,     15, TRUE);    break;
+       case OP_oI31b:
+       case OP_I31b:    po_imm_or_fail (  0,     31, TRUE);    break;
+       case OP_oIffffb: po_imm_or_fail (  0, 0xffff, TRUE);    break;
+
+         /* Immediate variants */
+       case OP_oI255c:
+         po_char_or_fail ('{');
+         po_imm_or_fail (0, 255, TRUE);
+         po_char_or_fail ('}');
+         break;
 
 
-      /* Conflicts can occur on stores as well as loads.  */
-      conflict_reg = (conflict_reg == reg);
+       case OP_I31w:
+         /* The expression parser chokes on a trailing !, so we have
+            to find it first and zap it.  */
+         {
+           char *s = str;
+           while (*s && *s != ',')
+             s++;
+           if (s[-1] == '!')
+             {
+               s[-1] = '\0';
+               inst.operands[i].writeback = 1;
+             }
+           po_imm_or_fail (0, 31, TRUE);
+           if (str == s - 1)
+             str = s;
+         }
+         break;
 
 
-      skip_whitespace (str);
+         /* Expressions */
+       case OP_EXPi:   EXPi:
+         po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+                                             GE_OPT_PREFIX));
+         break;
 
 
-      if (*str == ']')
-       {
-         str ++;
+       case OP_EXP:
+         po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+                                             GE_NO_PREFIX));
+         break;
 
 
-         if (skip_past_comma (&str) == SUCCESS)
-           {
-             /* [Rn],... (post inc)  */
-             if (ldst_extend (&str) == FAIL)
-               return;
-             if (conflict_reg)
-               as_warn (_("%s register same as write-back base"),
-                        ((inst.instruction & LOAD_BIT)
-                         ? _("destination") : _("source")));
-           }
-         else
+       case OP_EXPr:   EXPr:
+         po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+                                             GE_NO_PREFIX));
+         if (inst.reloc.exp.X_op == O_symbol)
            {
            {
-             /* [Rn]  */
-             skip_whitespace (str);
-
-             if (*str == '!')
+             val = parse_reloc (&str);
+             if (val == -1)
                {
                {
-                 if (conflict_reg)
-                   as_warn (_("%s register same as write-back base"),
-                            ((inst.instruction & LOAD_BIT)
-                             ? _("destination") : _("source")));
-                 str++;
-                 inst.instruction |= WRITE_BACK;
+                 inst.error = _("unrecognized relocation suffix");
+                 goto failure;
+               }
+             else if (val != BFD_RELOC_UNUSED)
+               {
+                 inst.operands[i].imm = val;
+                 inst.operands[i].hasreloc = 1;
                }
                }
-
-             inst.instruction |= INDEX_UP;
-             pre_inc = 1;
-           }
-       }
-      else
-       {
-         /* [Rn,...]  */
-         if (skip_past_comma (&str) == FAIL)
-           {
-             inst.error = _("pre-indexed expression expected");
-             return;
            }
            }
+         break;
 
 
-         pre_inc = 1;
-         if (ldst_extend (&str) == FAIL)
-           return;
+         /* Register or expression */
+       case OP_RR_EXr:   po_reg_or_goto (REG_TYPE_RN, EXPr); break;
+       case OP_RR_EXi:   po_reg_or_goto (REG_TYPE_RN, EXPi); break;
 
 
-         skip_whitespace (str);
+         /* Register or immediate */
+       case OP_RRnpc_I0: po_reg_or_goto (REG_TYPE_RN, I0);   break;
+       I0:               po_imm_or_fail (0, 0, FALSE);       break;
 
 
-         if (*str++ != ']')
-           {
-             inst.error = _("missing ]");
-             return;
-           }
+       case OP_RF_IF:    po_reg_or_goto (REG_TYPE_FN, IF);   break;
+       IF:
+         if (!is_immediate_prefix (*str))
+           goto bad_args;
+         str++;
+         val = parse_fpa_immediate (&str);
+         if (val == FAIL)
+           goto failure;
+         /* FPA immediates are encoded as registers 8-15.
+            parse_fpa_immediate has already applied the offset.  */
+         inst.operands[i].reg = val;
+         inst.operands[i].isreg = 1;
+         break;
+
+         /* Two kinds of register */
+       case OP_RIWR_RIWC:
+         {
+           struct reg_entry *rege = arm_reg_parse_multi (&str);
+           if (rege->type != REG_TYPE_MMXWR
+               && rege->type != REG_TYPE_MMXWC
+               && rege->type != REG_TYPE_MMXWCG)
+             {
+               inst.error = _("iWMMXt data or control register expected");
+               goto failure;
+             }
+           inst.operands[i].reg = rege->number;
+           inst.operands[i].isreg = (rege->type == REG_TYPE_MMXWR);
+         }
+         break;
 
 
-         skip_whitespace (str);
+         /* Misc */
+       case OP_CPSF:    val = parse_cps_flags (&str);          break;
+       case OP_ENDI:    val = parse_endian_specifier (&str);   break;
+       case OP_oROR:    val = parse_ror (&str);                break;
+       case OP_PSR:     val = parse_psr (&str);                break;
+       case OP_COND:    val = parse_cond (&str);               break;
 
 
-         if (*str == '!')
+       case OP_TB:
+         po_misc_or_fail (parse_tb (&str));
+         break;
+
+         /* Register lists */
+       case OP_REGLST:
+         val = parse_reg_list (&str);
+         if (*str == '^')
            {
            {
-             if (conflict_reg)
-               as_warn (_("%s register same as write-back base"),
-                        ((inst.instruction & LOAD_BIT)
-                         ? _("destination") : _("source")));
+             inst.operands[1].writeback = 1;
              str++;
              str++;
-             inst.instruction |= WRITE_BACK;
            }
            }
-       }
-    }
-  else if (*str == '=')
-    {
-      if ((inst.instruction & LOAD_BIT) == 0)
-       {
-         inst.error = _("invalid pseudo operation");
-         return;
-       }
+         break;
 
 
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
+       case OP_VRSLST:
+         val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 0);
+         break;
 
 
-      skip_whitespace (str);
+       case OP_VRDLST:
+         val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 1);
+         break;
 
 
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
+         /* Addressing modes */
+       case OP_ADDR:
+         po_misc_or_fail (parse_address (&str, i));
+         break;
 
 
-      if (inst.reloc.exp.X_op != O_constant
-         && inst.reloc.exp.X_op != O_symbol)
-       {
-         inst.error = _("constant expression expected");
-         return;
-       }
+       case OP_SH:
+         po_misc_or_fail (parse_shifter_operand (&str, i));
+         break;
 
 
-      if (inst.reloc.exp.X_op == O_constant)
-       {
-         value = validate_immediate (inst.reloc.exp.X_add_number);
+       case OP_oSHll:
+         po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE));
+         break;
 
 
-         if (value != FAIL)
-           {
-             /* This can be done with a mov instruction.  */
-             inst.instruction &= LITERAL_MASK;
-             inst.instruction |= (INST_IMMEDIATE
-                                  | (OPCODE_MOV << DATA_OP_SHIFT));
-             inst.instruction |= value & 0xfff;
-             end_of_line (str);
-             return;
-           }
+       case OP_oSHar:
+         po_misc_or_fail (parse_shift (&str, i, SHIFT_ASR_IMMEDIATE));
+         break;
 
 
-         value = validate_immediate (~inst.reloc.exp.X_add_number);
+       case OP_oSHllar:
+         po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_OR_ASR_IMMEDIATE));
+         break;
 
 
-         if (value != FAIL)
-           {
-             /* This can be done with a mvn instruction.  */
-             inst.instruction &= LITERAL_MASK;
-             inst.instruction |= (INST_IMMEDIATE
-                                  | (OPCODE_MVN << DATA_OP_SHIFT));
-             inst.instruction |= value & 0xfff;
-             end_of_line (str);
-             return;
-           }
+       default:
+         as_fatal ("unhandled operand code %d", upat[i]);
        }
 
        }
 
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
+      /* Various value-based sanity checks and shared operations.  We
+        do not signal immediate failures for the register constraints;
+        this allows a syntax error to take precedence.  */
+      switch (upat[i])
        {
        {
-         if (!inst.error)
-           inst.error = _("literal pool insertion failed");
-         return;
-       }
+       case OP_oRRnpc:
+       case OP_RRnpc:
+       case OP_RRnpcb:
+       case OP_RRw:
+       case OP_RRnpc_I0:
+         if (inst.operands[i].isreg && inst.operands[i].reg == REG_PC)
+           inst.error = BAD_PC;
+         break;
 
 
-      /* Change the instruction exp to point to the pool.  */
-      inst.reloc.type = BFD_RELOC_ARM_LITERAL;
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
-  else
-    {
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
+       case OP_CPSF:
+       case OP_ENDI:
+       case OP_oROR:
+       case OP_PSR:
+       case OP_COND:
+       case OP_REGLST:
+       case OP_VRSLST:
+       case OP_VRDLST:
+         if (val == FAIL)
+           goto failure;
+         inst.operands[i].imm = val;
+         break;
 
 
-      inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-#ifndef TE_WINCE
-      /* PC rel adjust.  */
-      inst.reloc.exp.X_add_number -= 8;
-#endif
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
+       default:
+         break;
+       }
 
 
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  end_of_line (str);
-  return;
-}
+      /* If we get here, this operand was successfully parsed. */
+      inst.operands[i].present = 1;
+      continue;
 
 
-static void
-do_ldstt (str)
-     char *        str;
-{
-  int conflict_reg;
+    bad_args:
+      inst.error = BAD_ARGS;
 
 
-  skip_whitespace (str);
+    failure:
+      if (!backtrack_pos)
+       return FAIL;
 
 
-  if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+      /* Do not backtrack over a trailing optional argument that
+        absorbed some text.  We will only fail again, with the
+        'garbage following instruction' error message, which is
+        probably less helpful than the current one.  */
+      if (backtrack_index == i && backtrack_pos != str
+         && upat[i+1] == OP_stop)
+       return FAIL;
 
 
-  if (skip_past_comma (& str) == FAIL)
-    {
-      inst.error = _("address expected");
-      return;
+      /* Try again, skipping the optional argument at backtrack_pos.  */
+      str = backtrack_pos;
+      inst.error = backtrack_error;
+      inst.operands[backtrack_index].present = 0;
+      i = backtrack_index;
+      backtrack_pos = 0;
     }
 
     }
 
-  if (*str == '[')
-    {
-      int reg;
+  /* Check that we have parsed all the arguments.  */
+  if (*str != '\0' && !inst.error)
+    inst.error = _("garbage following instruction");
 
 
-      str++;
+  return inst.error ? FAIL : SUCCESS;
+}
 
 
-      skip_whitespace (str);
+#undef po_char_or_fail
+#undef po_reg_or_fail
+#undef po_reg_or_goto
+#undef po_imm_or_fail
+\f
+/* Shorthand macro for instruction encoding functions issuing errors.  */
+#define constraint(expr, err) do {             \
+  if (expr)                                    \
+    {                                          \
+      inst.error = err;                                \
+      return;                                  \
+    }                                          \
+} while (0)
 
 
-      if ((reg = reg_required_here (&str, 16)) == FAIL)
-       return;
+/* Functions for operand encoding.  ARM, then Thumb.  */
 
 
-      /* ldrt/strt always use post-indexed addressing, so if the base is
-        the same as Rd, we warn.  */
-      if (conflict_reg == reg)
-       as_warn (_("%s register same as write-back base"),
-                ((inst.instruction & LOAD_BIT)
-                 ? _("destination") : _("source")));
+#define rotate_left(v, n) (v << n | v >> (32 - n))
 
 
-      skip_whitespace (str);
+/* If VAL can be encoded in the immediate field of an ARM instruction,
+   return the encoded form.  Otherwise, return FAIL.  */
 
 
-      if (*str == ']')
-       {
-         str ++;
+static unsigned int
+encode_arm_immediate (unsigned int val)
+{
+  unsigned int a, i;
 
 
-         if (skip_past_comma (&str) == SUCCESS)
-           {
-             /* [Rn],... (post inc)  */
-             if (ldst_extend (&str) == FAIL)
-               return;
-           }
-         else
-           {
-             /* [Rn]  */
-             skip_whitespace (str);
+  for (i = 0; i < 32; i += 2)
+    if ((a = rotate_left (val, i)) <= 0xff)
+      return a | (i << 7); /* 12-bit pack: [shift-cnt,const].  */
 
 
-             /* Skip a write-back '!'.  */
-             if (*str == '!')
-               str++;
+  return FAIL;
+}
 
 
-             inst.instruction |= INDEX_UP;
-           }
-       }
-      else
-       {
-         inst.error = _("post-indexed expression expected");
-         return;
-       }
-    }
-  else
+/* If VAL can be encoded in the immediate field of a Thumb32 instruction,
+   return the encoded form.  Otherwise, return FAIL.  */
+static unsigned int
+encode_thumb32_immediate (unsigned int val)
+{
+  unsigned int a, i;
+
+  if (val <= 0xff)
+    return val;
+
+  for (i = 1; i <= 24; i++)
     {
     {
-      inst.error = _("post-indexed expression expected");
-      return;
+      a = val >> i;
+      if ((val & ~(0xff << i)) == 0)
+       return ((val >> i) & 0x7f) | ((32 - i) << 7);
     }
 
     }
 
-  end_of_line (str);
-  return;
+  a = val & 0xff;
+  if (val == ((a << 16) | a))
+    return 0x100 | a;
+  if (val == ((a << 24) | (a << 16) | (a << 8) | a))
+    return 0x300 | a;
+
+  a = val & 0xff00;
+  if (val == ((a << 16) | a))
+    return 0x200 | (a >> 8);
+
+  return FAIL;
 }
 }
+/* Encode a VFP SP register number into inst.instruction.  */
 
 
-static int
-ldst_extend_v4 (str)
-     char ** str;
+static void
+encode_arm_vfp_sp_reg (int reg, enum vfp_sp_reg_pos pos)
 {
 {
-  int add = INDEX_UP;
-
-  switch (**str)
+  switch (pos)
     {
     {
-    case '#':
-    case '$':
-      (*str)++;
-      if (my_get_expression (& inst.reloc.exp, str))
-       return FAIL;
+    case VFP_REG_Sd:
+      inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
+      break;
 
 
-      if (inst.reloc.exp.X_op == O_constant)
-       {
-         int value = inst.reloc.exp.X_add_number;
+    case VFP_REG_Sn:
+      inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
+      break;
 
 
-         if (value < -255 || value > 255)
-           {
-             inst.error = _("address offset too large");
-             return FAIL;
-           }
+    case VFP_REG_Sm:
+      inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
+      break;
 
 
-         if (value < 0)
-           {
-             value = -value;
-             add = 0;
-           }
+    default:
+      abort ();
+    }
+}
 
 
-         /* Halfword and signextension instructions have the
-             immediate value split across bits 11..8 and bits 3..0.  */
-         inst.instruction |= (add | HWOFFSET_IMM
-                              | ((value >> 4) << 8) | (value & 0xF));
-       }
-      else
+/* Encode a <shift> in an ARM-format instruction.  The immediate,
+   if any, is handled by md_apply_fix.  */
+static void
+encode_arm_shift (int i)
+{
+  if (inst.operands[i].shift_kind == SHIFT_RRX)
+    inst.instruction |= SHIFT_ROR << 5;
+  else
+    {
+      inst.instruction |= inst.operands[i].shift_kind << 5;
+      if (inst.operands[i].immisreg)
        {
        {
-         inst.instruction |= HWOFFSET_IMM;
-         inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-         inst.reloc.pc_rel = 0;
+         inst.instruction |= SHIFT_BY_REG;
+         inst.instruction |= inst.operands[i].imm << 8;
        }
        }
-      return SUCCESS;
-
-    case '-':
-      add = 0;
-      /* Fall through.  */
-
-    case '+':
-      (*str)++;
-      /* Fall through.  */
-
-    default:
-      if (reg_required_here (str, 0) == FAIL)
-       return FAIL;
+      else
+       inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+    }
+}
 
 
-      inst.instruction |= add;
-      return SUCCESS;
+static void
+encode_arm_shifter_operand (int i)
+{
+  if (inst.operands[i].isreg)
+    {
+      inst.instruction |= inst.operands[i].reg;
+      encode_arm_shift (i);
     }
     }
+  else
+    inst.instruction |= INST_IMMEDIATE;
 }
 
 }
 
-/* Halfword and signed-byte load/store operations.  */
+/* Subroutine of encode_arm_addr_mode_2 and encode_arm_addr_mode_3.  */
 static void
 static void
-do_ldstv4 (str)
-     char *        str;
+encode_arm_addr_mode_common (int i, bfd_boolean is_t)
 {
 {
-  int pre_inc = 0;
-  int conflict_reg;
-  int value;
+  assert (inst.operands[i].isreg);
+  inst.instruction |= inst.operands[i].reg << 16;
 
 
-  skip_whitespace (str);
+  if (inst.operands[i].preind)
+    {
+      if (is_t)
+       {
+         inst.error = _("instruction does not accept preindexed addressing");
+         return;
+       }
+      inst.instruction |= PRE_INDEX;
+      if (inst.operands[i].writeback)
+       inst.instruction |= WRITE_BACK;
 
 
-  if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
+    }
+  else if (inst.operands[i].postind)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      assert (inst.operands[i].writeback);
+      if (is_t)
+       inst.instruction |= WRITE_BACK;
     }
     }
-
-  if (skip_past_comma (& str) == FAIL)
+  else /* unindexed - only for coprocessor */
     {
     {
-      inst.error = _("address expected");
+      inst.error = _("instruction does not accept unindexed addressing");
       return;
     }
 
       return;
     }
 
-  if (*str == '[')
-    {
-      int reg;
-
-      str++;
-
-      skip_whitespace (str);
-
-      if ((reg = reg_required_here (&str, 16)) == FAIL)
-       return;
-
-      /* Conflicts can occur on stores as well as loads.  */
-      conflict_reg = (conflict_reg == reg);
+  if (((inst.instruction & WRITE_BACK) || !(inst.instruction & PRE_INDEX))
+      && (((inst.instruction & 0x000f0000) >> 16)
+         == ((inst.instruction & 0x0000f000) >> 12)))
+    as_warn ((inst.instruction & LOAD_BIT)
+            ? _("destination register same as write-back base")
+            : _("source register same as write-back base"));
+}
 
 
-      skip_whitespace (str);
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format mode 2 load or store instruction.         If is_t is true,
+   reject forms that cannot be used with a T instruction (i.e. not
+   post-indexed).  */
+static void
+encode_arm_addr_mode_2 (int i, bfd_boolean is_t)
+{
+  encode_arm_addr_mode_common (i, is_t);
 
 
-      if (*str == ']')
+  if (inst.operands[i].immisreg)
+    {
+      inst.instruction |= INST_IMMEDIATE;  /* yes, this is backwards */
+      inst.instruction |= inst.operands[i].imm;
+      if (!inst.operands[i].negative)
+       inst.instruction |= INDEX_UP;
+      if (inst.operands[i].shifted)
        {
        {
-         str ++;
-
-         if (skip_past_comma (&str) == SUCCESS)
-           {
-             /* [Rn],... (post inc)  */
-             if (ldst_extend_v4 (&str) == FAIL)
-               return;
-             if (conflict_reg)
-               as_warn (_("%s register same as write-back base"),
-                        ((inst.instruction & LOAD_BIT)
-                         ? _("destination") : _("source")));
-           }
+         if (inst.operands[i].shift_kind == SHIFT_RRX)
+           inst.instruction |= SHIFT_ROR << 5;
          else
            {
          else
            {
-             /* [Rn]  */
-             inst.instruction |= HWOFFSET_IMM;
-
-             skip_whitespace (str);
-
-             if (*str == '!')
-               {
-                 if (conflict_reg)
-                   as_warn (_("%s register same as write-back base"),
-                            ((inst.instruction & LOAD_BIT)
-                             ? _("destination") : _("source")));
-                 str++;
-                 inst.instruction |= WRITE_BACK;
-               }
-
-             inst.instruction |= INDEX_UP;
-             pre_inc = 1;
+             inst.instruction |= inst.operands[i].shift_kind << 5;
+             inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
            }
        }
            }
        }
-      else
-       {
-         /* [Rn,...]  */
-         if (skip_past_comma (&str) == FAIL)
-           {
-             inst.error = _("pre-indexed expression expected");
-             return;
-           }
+    }
+  else /* immediate offset in inst.reloc */
+    {
+      if (inst.reloc.type == BFD_RELOC_UNUSED)
+       inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+    }
+}
 
 
-         pre_inc = 1;
-         if (ldst_extend_v4 (&str) == FAIL)
-           return;
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format mode 3 load or store instruction.         Reject forms that
+   cannot be used with such instructions.  If is_t is true, reject
+   forms that cannot be used with a T instruction (i.e. not
+   post-indexed).  */
+static void
+encode_arm_addr_mode_3 (int i, bfd_boolean is_t)
+{
+  if (inst.operands[i].immisreg && inst.operands[i].shifted)
+    {
+      inst.error = _("instruction does not accept scaled register index");
+      return;
+    }
 
 
-         skip_whitespace (str);
+  encode_arm_addr_mode_common (i, is_t);
 
 
-         if (*str++ != ']')
-           {
-             inst.error = _("missing ]");
-             return;
-           }
+  if (inst.operands[i].immisreg)
+    {
+      inst.instruction |= inst.operands[i].imm;
+      if (!inst.operands[i].negative)
+       inst.instruction |= INDEX_UP;
+    }
+  else /* immediate offset in inst.reloc */
+    {
+      inst.instruction |= HWOFFSET_IMM;
+      if (inst.reloc.type == BFD_RELOC_UNUSED)
+       inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+    }
+}
 
 
-         skip_whitespace (str);
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format instruction.  Reject all forms which cannot be encoded
+   into a coprocessor load/store instruction.  If wb_ok is false,
+   reject use of writeback; if unind_ok is false, reject use of
+   unindexed addressing.  If reloc_override is not 0, use it instead
+   of BFD_ARM_CP_OFF_IMM.  */
 
 
-         if (*str == '!')
-           {
-             if (conflict_reg)
-               as_warn (_("%s register same as write-back base"),
-                        ((inst.instruction & LOAD_BIT)
-                         ? _("destination") : _("source")));
-             str++;
-             inst.instruction |= WRITE_BACK;
-           }
+static int
+encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
+{
+  inst.instruction |= inst.operands[i].reg << 16;
+
+  assert (!(inst.operands[i].preind && inst.operands[i].postind));
+
+  if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */
+    {
+      assert (!inst.operands[i].writeback);
+      if (!unind_ok)
+       {
+         inst.error = _("instruction does not support unindexed addressing");
+         return FAIL;
        }
        }
+      inst.instruction |= inst.operands[i].imm;
+      inst.instruction |= INDEX_UP;
+      return SUCCESS;
     }
     }
-  else if (*str == '=')
+
+  if (inst.operands[i].preind)
+    inst.instruction |= PRE_INDEX;
+
+  if (inst.operands[i].writeback)
     {
     {
-      if ((inst.instruction & LOAD_BIT) == 0)
+      if (inst.operands[i].reg == REG_PC)
        {
        {
-         inst.error = _("invalid pseudo operation");
-         return;
+         inst.error = _("pc may not be used with write-back");
+         return FAIL;
+       }
+      if (!wb_ok)
+       {
+         inst.error = _("instruction does not support writeback");
+         return FAIL;
        }
        }
+      inst.instruction |= WRITE_BACK;
+    }
 
 
-      /* XXX Does this work correctly for half-word/byte ops?  */
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
+  if (reloc_override)
+    inst.reloc.type = reloc_override;
+  else
+    inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+  return SUCCESS;
+}
 
 
-      skip_whitespace (str);
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+   Determine whether it can be performed with a move instruction; if
+   it can, convert inst.instruction to that move instruction and
+   return 1; if it can't, convert inst.instruction to a literal-pool
+   load and return 0.  If this is not a valid thing to do in the
+   current context, set inst.error and return 1.
 
 
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
+   inst.operands[i] describes the destination register.         */
 
 
-      if (inst.reloc.exp.X_op != O_constant
-         && inst.reloc.exp.X_op != O_symbol)
+static int
+move_or_literal_pool (int i, bfd_boolean thumb_p, bfd_boolean mode_3)
+{
+  if ((inst.instruction & (thumb_p ? THUMB_LOAD_BIT : LOAD_BIT)) == 0)
+    {
+      inst.error = _("invalid pseudo operation");
+      return 1;
+    }
+  if (inst.reloc.exp.X_op != O_constant && inst.reloc.exp.X_op != O_symbol)
+    {
+      inst.error = _("constant expression expected");
+      return 1;
+    }
+  if (inst.reloc.exp.X_op == O_constant)
+    {
+      if (thumb_p)
        {
        {
-         inst.error = _("constant expression expected");
-         return;
+         if ((inst.reloc.exp.X_add_number & ~0xFF) == 0)
+           {
+             /* This can be done with a mov(1) instruction.  */
+             inst.instruction  = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+             inst.instruction |= inst.reloc.exp.X_add_number;
+             return 1;
+           }
        }
        }
-
-      if (inst.reloc.exp.X_op == O_constant)
+      else
        {
        {
-         value = validate_immediate (inst.reloc.exp.X_add_number);
-
+         int value = encode_arm_immediate (inst.reloc.exp.X_add_number);
          if (value != FAIL)
            {
              /* This can be done with a mov instruction.  */
              inst.instruction &= LITERAL_MASK;
              inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
              inst.instruction |= value & 0xfff;
          if (value != FAIL)
            {
              /* This can be done with a mov instruction.  */
              inst.instruction &= LITERAL_MASK;
              inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
              inst.instruction |= value & 0xfff;
-             end_of_line (str);
-             return;
+             return 1;
            }
 
            }
 
-         value = validate_immediate (~ inst.reloc.exp.X_add_number);
-
+         value = encode_arm_immediate (~inst.reloc.exp.X_add_number);
          if (value != FAIL)
            {
              /* This can be done with a mvn instruction.  */
              inst.instruction &= LITERAL_MASK;
              inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
              inst.instruction |= value & 0xfff;
          if (value != FAIL)
            {
              /* This can be done with a mvn instruction.  */
              inst.instruction &= LITERAL_MASK;
              inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
              inst.instruction |= value & 0xfff;
-             end_of_line (str);
-             return;
+             return 1;
            }
        }
            }
        }
-
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
-       {
-         if (!inst.error)
-           inst.error = _("literal pool insertion failed");
-         return;
-       }
-
-      /* Change the instruction exp to point to the pool.  */
-      inst.instruction |= HWOFFSET_IMM;
-      inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
     }
     }
-  else
-    {
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
 
 
-      inst.instruction |= HWOFFSET_IMM;
-      inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-#ifndef TE_WINCE
-      /* PC rel adjust.  */
-      inst.reloc.exp.X_add_number -= 8;
-#endif
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
+  if (add_to_lit_pool () == FAIL)
+    {
+      inst.error = _("literal pool insertion failed");
+      return 1;
     }
     }
+  inst.operands[1].reg = REG_PC;
+  inst.operands[1].isreg = 1;
+  inst.operands[1].preind = 1;
+  inst.reloc.pc_rel = 1;
+  inst.reloc.type = (thumb_p
+                    ? BFD_RELOC_ARM_THUMB_OFFSET
+                    : (mode_3
+                       ? BFD_RELOC_ARM_HWLITERAL
+                       : BFD_RELOC_ARM_LITERAL));
+  return 0;
+}
 
 
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  end_of_line (str);
-  return;
+/* Functions for instruction encoding, sorted by subarchitecture.
+   First some generics; their names are taken from the conventional
+   bit positions for register arguments in ARM format instructions.  */
+
+static void
+do_noargs (void)
+{
 }
 
 }
 
-static long
-reg_list (strp)
-     char ** strp;
+static void
+do_rd (void)
 {
 {
-  char * str = * strp;
-  long   range = 0;
-  int    another_range;
+  inst.instruction |= inst.operands[0].reg << 12;
+}
 
 
-  /* We come back here if we get ranges concatenated by '+' or '|'.  */
-  do
-    {
-      another_range = 0;
+static void
+do_rd_rm (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+}
 
 
-      if (*str == '{')
-       {
-         int in_range = 0;
-         int cur_reg = -1;
+static void
+do_rd_rn (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+}
 
 
-         str++;
-         do
-           {
-             int reg;
+static void
+do_rn_rd (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg << 12;
+}
 
 
-             skip_whitespace (str);
+static void
+do_rd_rm_rn (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 16;
+}
 
 
-             if ((reg = reg_required_here (& str, -1)) == FAIL)
-               return FAIL;
+static void
+do_rd_rn_rm (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+}
 
 
-             if (in_range)
-               {
-                 int i;
+static void
+do_rm_rd_rn (void)
+{
+  inst.instruction |= inst.operands[0].reg;
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+}
 
 
-                 if (reg <= cur_reg)
-                   {
-                     inst.error = _("bad range in register list");
-                     return FAIL;
-                   }
+static void
+do_imm0 (void)
+{
+  inst.instruction |= inst.operands[0].imm;
+}
 
 
-                 for (i = cur_reg + 1; i < reg; i++)
-                   {
-                     if (range & (1 << i))
-                       as_tsktsk
-                         (_("Warning: duplicated register (r%d) in register list"),
-                          i);
-                     else
-                       range |= 1 << i;
-                   }
-                 in_range = 0;
-               }
+static void
+do_rd_cpaddr (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_cp_address (1, TRUE, TRUE, 0);
+}
 
 
-             if (range & (1 << reg))
-               as_tsktsk (_("Warning: duplicated register (r%d) in register list"),
-                          reg);
-             else if (reg <= cur_reg)
-               as_tsktsk (_("Warning: register range not in ascending order"));
+/* ARM instructions, in alphabetical order by function name (except
+   that wrapper functions appear immediately after the function they
+   wrap).  */
 
 
-             range |= 1 << reg;
-             cur_reg = reg;
-           }
-         while (skip_past_comma (&str) != FAIL
-                || (in_range = 1, *str++ == '-'));
-         str--;
-         skip_whitespace (str);
+/* This is a pseudo-op of the form "adr rd, label" to be converted
+   into a relative address of the form "add rd, pc, #label-.-8".  */
 
 
-         if (*str++ != '}')
-           {
-             inst.error = _("missing `}'");
-             return FAIL;
-           }
-       }
-      else
-       {
-         expressionS expr;
+static void
+do_adr (void)
+{
+  inst.instruction |= (inst.operands[0].reg << 12);  /* Rd */
 
 
-         if (my_get_expression (&expr, &str))
-           return FAIL;
+  /* Frag hacking will turn this into a sub instruction if the offset turns
+     out to be negative.  */
+  inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+  inst.reloc.pc_rel = 1;
+  inst.reloc.exp.X_add_number -= 8;
+}
 
 
-         if (expr.X_op == O_constant)
-           {
-             if (expr.X_add_number
-                 != (expr.X_add_number & 0x0000ffff))
-               {
-                 inst.error = _("invalid register mask");
-                 return FAIL;
-               }
+/* This is a pseudo-op of the form "adrl rd, label" to be converted
+   into a relative address of the form:
+   add rd, pc, #low(label-.-8)"
+   add rd, rd, #high(label-.-8)"  */
 
 
-             if ((range & expr.X_add_number) != 0)
-               {
-                 int regno = range & expr.X_add_number;
+static void
+do_adrl (void)
+{
+  inst.instruction |= (inst.operands[0].reg << 12);  /* Rd */
 
 
-                 regno &= -regno;
-                 regno = (1 << regno) - 1;
-                 as_tsktsk
-                   (_("Warning: duplicated register (r%d) in register list"),
-                    regno);
-               }
+  /* Frag hacking will turn this into a sub instruction if the offset turns
+     out to be negative.  */
+  inst.reloc.type             = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+  inst.reloc.pc_rel           = 1;
+  inst.size                   = INSN_SIZE * 2;
+  inst.reloc.exp.X_add_number -= 8;
+}
 
 
-             range |= expr.X_add_number;
-           }
-         else
-           {
-             if (inst.reloc.type != 0)
-               {
-                 inst.error = _("expression too complex");
-                 return FAIL;
-               }
+static void
+do_arit (void)
+{
+  if (!inst.operands[1].present)
+    inst.operands[1].reg = inst.operands[0].reg;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  encode_arm_shifter_operand (2);
+}
 
 
-             memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
-             inst.reloc.type = BFD_RELOC_ARM_MULTI;
-             inst.reloc.pc_rel = 0;
-           }
-       }
+static void
+do_bfc (void)
+{
+  unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+  constraint (msb > 32, _("bit-field extends past end of register"));
+  /* The instruction encoding stores the LSB and MSB,
+     not the LSB and width.  */
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].imm << 7;
+  inst.instruction |= (msb - 1) << 16;
+}
 
 
-      skip_whitespace (str);
+static void
+do_bfi (void)
+{
+  unsigned int msb;
 
 
-      if (*str == '|' || *str == '+')
-       {
-         str++;
-         another_range = 1;
-       }
-    }
-  while (another_range);
+  /* #0 in second position is alternative syntax for bfc, which is
+     the same instruction but with REG_PC in the Rm field.  */
+  if (!inst.operands[1].isreg)
+    inst.operands[1].reg = REG_PC;
 
 
-  *strp = str;
-  return range;
+  msb = inst.operands[2].imm + inst.operands[3].imm;
+  constraint (msb > 32, _("bit-field extends past end of register"));
+  /* The instruction encoding stores the LSB and MSB,
+     not the LSB and width.  */
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].imm << 7;
+  inst.instruction |= (msb - 1) << 16;
 }
 
 static void
 }
 
 static void
-do_ldmstm (str)
-     char * str;
+do_bfx (void)
 {
 {
-  int base_reg;
-  long range;
+  constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+             _("bit-field extends past end of register"));
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].imm << 7;
+  inst.instruction |= (inst.operands[3].imm - 1) << 16;
+}
 
 
-  skip_whitespace (str);
+/* ARM V5 breakpoint instruction (argument parse)
+     BKPT <16 bit unsigned immediate>
+     Instruction is not conditional.
+       The bit pattern given in insns[] has the COND_ALWAYS condition,
+       and it is an error if the caller tried to override that.  */
 
 
-  if ((base_reg = reg_required_here (&str, 16)) == FAIL)
-    return;
+static void
+do_bkpt (void)
+{
+  /* Top 12 of 16 bits to bits 19:8.  */
+  inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
+
+  /* Bottom 4 of 16 bits to bits 3:0.  */
+  inst.instruction |= inst.operands[0].imm & 0xf;
+}
 
 
-  if (base_reg == REG_PC)
+static void
+encode_branch (int default_reloc)
+{
+  if (inst.operands[0].hasreloc)
     {
     {
-      inst.error = _("r15 not allowed as base register");
-      return;
+      constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32,
+                 _("the only suffix valid here is '(plt)'"));
+      inst.reloc.type  = BFD_RELOC_ARM_PLT32;
     }
     }
-
-  skip_whitespace (str);
-
-  if (*str == '!')
+  else
     {
     {
-      inst.instruction |= WRITE_BACK;
-      str++;
+      inst.reloc.type = default_reloc;
     }
     }
+  inst.reloc.pc_rel = 1;
+}
+
+static void
+do_branch (void)
+{
+  encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
+}
+
+/* ARM V5 branch-link-exchange instruction (argument parse)
+     BLX <target_addr>         ie BLX(1)
+     BLX{<condition>} <Rm>     ie BLX(2)
+   Unfortunately, there are two different opcodes for this mnemonic.
+   So, the insns[].value is not used, and the code here zaps values
+       into inst.instruction.
+   Also, the <target_addr> can be 25 bits, hence has its own reloc.  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (range = reg_list (&str)) == FAIL)
+static void
+do_blx (void)
+{
+  if (inst.operands[0].isreg)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+      /* Arg is a register; the opcode provided by insns[] is correct.
+        It is not illegal to do "blx pc", just useless.  */
+      if (inst.operands[0].reg == REG_PC)
+       as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
 
 
-  if (*str == '^')
+      inst.instruction |= inst.operands[0].reg;
+    }
+  else
     {
     {
-      str++;
-      inst.instruction |= LDM_TYPE_2_OR_3;
+      /* Arg is an address; this instruction cannot be executed
+        conditionally, and the opcode must be adjusted.  */
+      constraint (inst.cond != COND_ALWAYS, BAD_COND);
+      inst.instruction = 0xfa000000;
+      encode_branch (BFD_RELOC_ARM_PCREL_BLX);
     }
     }
+}
+
+static void
+do_bx (void)
+{
+  if (inst.operands[0].reg == REG_PC)
+    as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
+
+  inst.instruction |= inst.operands[0].reg;
+}
+
+
+/* ARM v5TEJ.  Jump to Jazelle code.  */
+
+static void
+do_bxj (void)
+{
+  if (inst.operands[0].reg == REG_PC)
+    as_tsktsk (_("use of r15 in bxj is not really useful"));
+
+  inst.instruction |= inst.operands[0].reg;
+}
+
+/* Co-processor data operation:
+      CDP{cond} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}
+      CDP2     <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}  */
+static void
+do_cdp (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm << 20;
+  inst.instruction |= inst.operands[2].reg << 12;
+  inst.instruction |= inst.operands[3].reg << 16;
+  inst.instruction |= inst.operands[4].reg;
+  inst.instruction |= inst.operands[5].imm << 5;
+}
+
+static void
+do_cmp (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  encode_arm_shifter_operand (1);
+}
+
+/* Transfer between coprocessor and ARM registers.
+   MRC{cond} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
+   MRC2
+   MCR{cond}
+   MCR2
+
+   No special properties.  */
+
+static void
+do_co_reg (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm << 21;
+  inst.instruction |= inst.operands[2].reg << 12;
+  inst.instruction |= inst.operands[3].reg << 16;
+  inst.instruction |= inst.operands[4].reg;
+  inst.instruction |= inst.operands[5].imm << 5;
+}
+
+/* Transfer between coprocessor register and pair of ARM registers.
+   MCRR{cond} <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+   MCRR2
+   MRRC{cond}
+   MRRC2
+
+   Two XScale instructions are special cases of these:
+
+     MAR{cond} acc0, <RdLo>, <RdHi> == MCRR{cond} p0, #0, <RdLo>, <RdHi>, c0
+     MRA{cond} acc0, <RdLo>, <RdHi> == MRRC{cond} p0, #0, <RdLo>, <RdHi>, c0
+
+   Result unpredicatable if Rd or Rn is R15.  */
+
+static void
+do_co_reg2c (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm << 4;
+  inst.instruction |= inst.operands[2].reg << 12;
+  inst.instruction |= inst.operands[3].reg << 16;
+  inst.instruction |= inst.operands[4].reg;
+}
+
+static void
+do_cpsi (void)
+{
+  inst.instruction |= inst.operands[0].imm << 6;
+  inst.instruction |= inst.operands[1].imm;
+}
+
+static void
+do_it (void)
+{
+  /* There is no IT instruction in ARM mode.  We
+     process it but do not generate code for it.  */
+  inst.size = 0;
+}
+
+static void
+do_ldmstm (void)
+{
+  int base_reg = inst.operands[0].reg;
+  int range = inst.operands[1].imm;
 
 
-  if (inst.instruction & WRITE_BACK)
+  inst.instruction |= base_reg << 16;
+  inst.instruction |= range;
+
+  if (inst.operands[1].writeback)
+    inst.instruction |= LDM_TYPE_2_OR_3;
+
+  if (inst.operands[0].writeback)
     {
     {
+      inst.instruction |= WRITE_BACK;
       /* Check for unpredictable uses of writeback.  */
       if (inst.instruction & LOAD_BIT)
        {
       /* Check for unpredictable uses of writeback.  */
       if (inst.instruction & LOAD_BIT)
        {
-         /* Not allowed in LDM type 2.  */
+         /* Not allowed in LDM type 2.  */
          if ((inst.instruction & LDM_TYPE_2_OR_3)
              && ((range & (1 << REG_PC)) == 0))
            as_warn (_("writeback of base register is UNPREDICTABLE"));
          if ((inst.instruction & LDM_TYPE_2_OR_3)
              && ((range & (1 << REG_PC)) == 0))
            as_warn (_("writeback of base register is UNPREDICTABLE"));
@@ -5796,3915 +4668,5741 @@ do_ldmstm (str)
            as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
        }
     }
            as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
        }
     }
-
-  inst.instruction |= range;
-  end_of_line (str);
-  return;
 }
 
 }
 
+/* ARMv5TE load-consecutive (argument parse)
+   Mode is like LDRH.
+
+     LDRccD R, mode
+     STRccD R, mode.  */
+
 static void
 static void
-do_swi (str)
-     char * str;
+do_ldrd (void)
 {
 {
-  skip_whitespace (str);
+  constraint (inst.operands[0].reg % 2 != 0,
+             _("first destination register must be even"));
+  constraint (inst.operands[1].present
+             && inst.operands[1].reg != inst.operands[0].reg + 1,
+             _("can only load two consecutive registers"));
+  constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
+  constraint (!inst.operands[2].isreg, _("'[' expected"));
 
 
-  /* Allow optional leading '#'.  */
-  if (is_immediate_prefix (*str))
-    str++;
+  if (!inst.operands[1].present)
+    inst.operands[1].reg = inst.operands[0].reg + 1;
+  
+  if (inst.instruction & LOAD_BIT)
+    {
+      /* encode_arm_addr_mode_3 will diagnose overlap between the base
+        register and the first register written; we have to diagnose
+        overlap between the base and the second register written here.  */
 
 
-  if (my_get_expression (& inst.reloc.exp, & str))
-    return;
+      if (inst.operands[2].reg == inst.operands[1].reg
+         && (inst.operands[2].writeback || inst.operands[2].postind))
+       as_warn (_("base register written back, and overlaps "
+                  "second destination register"));
 
 
-  inst.reloc.type = BFD_RELOC_ARM_SWI;
-  inst.reloc.pc_rel = 0;
-  end_of_line (str);
+      /* For an index-register load, the index register must not overlap the
+        destination (even if not write-back).  */
+      else if (inst.operands[2].immisreg
+              && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
+                  || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
+       as_warn (_("index register overlaps destination register"));
+    }
 
 
-  return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
 }
 
 static void
 }
 
 static void
-do_swap (str)
-     char * str;
+do_ldrex (void)
 {
 {
-  int reg;
+  constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+             || inst.operands[1].postind || inst.operands[1].writeback
+             || inst.operands[1].immisreg || inst.operands[1].shifted
+             || inst.operands[1].negative,
+             _("instruction does not accept this addressing mode"));
 
 
-  skip_whitespace (str);
+  constraint (inst.operands[1].reg == REG_PC, BAD_PC);
 
 
-  if ((reg = reg_required_here (&str, 12)) == FAIL)
-    return;
+  constraint (inst.reloc.exp.X_op != O_constant
+             || inst.reloc.exp.X_add_number != 0,
+             _("offset must be zero in ARM encoding"));
 
 
-  if (reg == REG_PC)
-    {
-      inst.error = _("r15 not allowed in swap");
-      return;
-    }
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.reloc.type = BFD_RELOC_UNUSED;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (reg = reg_required_here (&str, 0)) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_ldrexd (void)
+{
+  constraint (inst.operands[0].reg % 2 != 0,
+             _("even register required"));
+  constraint (inst.operands[1].present
+             && inst.operands[1].reg != inst.operands[0].reg + 1,
+             _("can only load two consecutive registers"));
+  /* If op 1 were present and equal to PC, this function wouldn't
+     have been called in the first place.  */
+  constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
 
 
-  if (reg == REG_PC)
-    {
-      inst.error = _("r15 not allowed in swap");
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_ldst (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
       return;
       return;
-    }
+  encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || *str++ != '[')
+static void
+do_ldstt (void)
+{
+  /* ldrt/strt always use post-indexed addressing.  Turn [Rn] into [Rn]! and
+     reject [Rn,...].  */
+  if (inst.operands[1].preind)
     {
     {
-      inst.error = BAD_ARGS;
-      return;
-    }
+      constraint (inst.reloc.exp.X_op != O_constant ||
+                 inst.reloc.exp.X_add_number != 0,
+                 _("this instruction requires a post-indexed address"));
 
 
-  skip_whitespace (str);
+      inst.operands[1].preind = 0;
+      inst.operands[1].postind = 1;
+      inst.operands[1].writeback = 1;
+    }
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_addr_mode_2 (1, /*is_t=*/TRUE);
+}
 
 
-  if ((reg = reg_required_here (&str, 16)) == FAIL)
-    return;
+/* Halfword and signed-byte load/store operations.  */
 
 
-  if (reg == REG_PC)
-    {
-      inst.error = BAD_PC;
+static void
+do_ldstv4 (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE))
       return;
       return;
-    }
-
-  skip_whitespace (str);
+  encode_arm_addr_mode_3 (1, /*is_t=*/FALSE);
+}
 
 
-  if (*str++ != ']')
+static void
+do_ldsttv4 (void)
+{
+  /* ldrt/strt always use post-indexed addressing.  Turn [Rn] into [Rn]! and
+     reject [Rn,...].  */
+  if (inst.operands[1].preind)
     {
     {
-      inst.error = _("missing ]");
-      return;
+      constraint (inst.reloc.exp.X_op != O_constant ||
+                 inst.reloc.exp.X_add_number != 0,
+                 _("this instruction requires a post-indexed address"));
+
+      inst.operands[1].preind = 0;
+      inst.operands[1].postind = 1;
+      inst.operands[1].writeback = 1;
     }
     }
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_addr_mode_3 (1, /*is_t=*/TRUE);
+}
 
 
-  end_of_line (str);
-  return;
+/* Co-processor register load/store.
+   Format: <LDC|STC>{cond}[L] CP#,CRd,<address>         */
+static void
+do_lstc (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 12;
+  encode_arm_cp_address (2, TRUE, TRUE, 0);
 }
 
 static void
 }
 
 static void
-do_branch (str)
-     char * str;
+do_mlas (void)
 {
 {
-  if (my_get_expression (&inst.reloc.exp, &str))
-    return;
+  /* This restriction does not apply to mls (nor to mla in v6, but
+     that's hard to detect at present).         */
+  if (inst.operands[0].reg == inst.operands[1].reg
+      && !(inst.instruction & 0x00400000))
+    as_tsktsk (_("rd and rm should be different in mla"));
 
 
-#ifdef OBJ_ELF
-  {
-    char * save_in;
-
-    /* ScottB: February 5, 1998 - Check to see of PLT32 reloc
-       required for the instruction.  */
-
-    /* arm_parse_reloc () works on input_line_pointer.
-       We actually want to parse the operands to the branch instruction
-       passed in 'str'.  Save the input pointer and restore it later.  */
-    save_in = input_line_pointer;
-    input_line_pointer = str;
-    if (inst.reloc.exp.X_op == O_symbol
-       && *str == '('
-       && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
-      {
-       inst.reloc.type   = BFD_RELOC_ARM_PLT32;
-       inst.reloc.pc_rel = 0;
-       /* Modify str to point to after parsed operands, otherwise
-          end_of_line() will complain about the (PLT) left in str.  */
-       str = input_line_pointer;
-      }
-    else
-      {
-       inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
-       inst.reloc.pc_rel = 1;
-      }
-    input_line_pointer = save_in;
-  }
-#else
-  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
-  inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF  */
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 8;
+  inst.instruction |= inst.operands[3].reg << 12;
 
 
-  end_of_line (str);
-  return;
 }
 
 static void
 }
 
 static void
-do_bx (str)
-     char * str;
+do_mov (void)
 {
 {
-  int reg;
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_shifter_operand (1);
+}
 
 
-  skip_whitespace (str);
-
-  if ((reg = reg_required_here (&str, 0)) == FAIL)
-    {
-      inst.error = BAD_ARGS;
-      return;
-    }
-
-  /* Note - it is not illegal to do a "bx pc".  Useless, but not illegal.  */
-  if (reg == REG_PC)
-    as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
-
-  end_of_line (str);
-}
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>.         */
+static void
+do_mov16 (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  /* The value is in two pieces: 0:11, 16:19.  */
+  inst.instruction |= (inst.operands[1].imm & 0x00000fff);
+  inst.instruction |= (inst.operands[1].imm & 0x0000f000) << 4;
+}
 
 static void
 
 static void
-do_cdp (str)
-     char * str;
+do_mrs (void)
 {
 {
-  /* Co-processor data operation.
-     Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>}  */
-  skip_whitespace (str);
+  /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all.  */
+  constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+             != (PSR_c|PSR_f),
+             _("'CPSR' or 'SPSR' expected"));
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= (inst.operands[1].imm & SPSR_BIT);
+}
 
 
-  if (co_proc_number (&str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* Two possible forms:
+      "{C|S}PSR_<field>, Rm",
+      "{C|S}PSR_f, #expression".  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_opc_expr (&str, 20,4) == FAIL)
+static void
+do_msr (void)
+{
+  inst.instruction |= inst.operands[0].imm;
+  if (inst.operands[1].isreg)
+    inst.instruction |= inst.operands[1].reg;
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.instruction |= INST_IMMEDIATE;
+      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+      inst.reloc.pc_rel = 0;
     }
     }
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_mul (void)
+{
+  if (!inst.operands[2].present)
+    inst.operands[2].reg = inst.operands[0].reg;
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 8;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 16) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  if (inst.operands[0].reg == inst.operands[1].reg)
+    as_tsktsk (_("rd and rm should be different in mul"));
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 0) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* Long Multiply Parser
+   UMULL RdLo, RdHi, Rm, Rs
+   SMULL RdLo, RdHi, Rm, Rs
+   UMLAL RdLo, RdHi, Rm, Rs
+   SMLAL RdLo, RdHi, Rm, Rs.  */
 
 
-  if (skip_past_comma (&str) == SUCCESS)
-    {
-      if (cp_opc_expr (&str, 5, 3) == FAIL)
-       {
-         if (!inst.error)
-           inst.error = BAD_ARGS;
-         return;
-       }
-    }
+static void
+do_mull (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].reg << 8;
 
 
-  end_of_line (str);
-  return;
+  /* rdhi, rdlo and rm must all be different.  */
+  if (inst.operands[0].reg == inst.operands[1].reg
+      || inst.operands[0].reg == inst.operands[2].reg
+      || inst.operands[1].reg == inst.operands[2].reg)
+    as_tsktsk (_("rdhi, rdlo and rm must all be different"));
 }
 
 static void
 }
 
 static void
-do_lstc (str)
-     char * str;
+do_nop (void)
 {
 {
-  /* Co-processor register load/store.
-     Format: <LDC|STC{cond}[L] CP#,CRd,<address>  */
-
-  skip_whitespace (str);
-
-  if (co_proc_number (&str) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 12) == FAIL)
+  if (inst.operands[0].present)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (skip_past_comma (&str) == FAIL
-      || cp_address_required_here (&str, CP_WB_OK) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Architectural NOP hints are CPSR sets with no bits selected.  */
+      inst.instruction &= 0xf0000000;
+      inst.instruction |= 0x0320f000 + inst.operands[0].imm;
     }
     }
-
-  end_of_line (str);
-  return;
 }
 
 }
 
+/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
+   PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
+   Condition defaults to COND_ALWAYS.
+   Error if Rd, Rn or Rm are R15.  */
+
 static void
 static void
-do_co_reg (str)
-     char * str;
+do_pkhbt (void)
 {
 {
-  /* Co-processor register transfer.
-     Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>}  */
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  if (inst.operands[3].present)
+    encode_arm_shift (3);
+}
 
 
-  skip_whitespace (str);
+/* ARM V6 PKHTB (Argument Parse).  */
 
 
-  if (co_proc_number (&str) == FAIL)
+static void
+do_pkhtb (void)
+{
+  if (!inst.operands[3].present)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* If the shift specifier is omitted, turn the instruction
+        into pkhbt rd, rm, rn. */
+      inst.instruction &= 0xfff00010;
+      inst.instruction |= inst.operands[0].reg << 12;
+      inst.instruction |= inst.operands[1].reg;
+      inst.instruction |= inst.operands[2].reg << 16;
     }
     }
-
-  if (skip_past_comma (&str) == FAIL
-      || cp_opc_expr (&str, 21, 3) == FAIL)
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.instruction |= inst.operands[0].reg << 12;
+      inst.instruction |= inst.operands[1].reg << 16;
+      inst.instruction |= inst.operands[2].reg;
+      encode_arm_shift (3);
     }
     }
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* ARMv5TE: Preload-Cache
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 16) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+    PLD <addr_mode>
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_reg_required_here (&str, 0) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  Syntactically, like LDR with B=1, W=0, L=1.  */
 
 
-  if (skip_past_comma (&str) == SUCCESS)
-    {
-      if (cp_opc_expr (&str, 5, 3) == FAIL)
-       {
-         if (!inst.error)
-           inst.error = BAD_ARGS;
-         return;
-       }
-    }
+static void
+do_pld (void)
+{
+  constraint (!inst.operands[0].isreg,
+             _("'[' expected after PLD mnemonic"));
+  constraint (inst.operands[0].postind,
+             _("post-indexed expression used in preload instruction"));
+  constraint (inst.operands[0].writeback,
+             _("writeback used in preload instruction"));
+  constraint (!inst.operands[0].preind,
+             _("unindexed addressing used in preload instruction"));
+  inst.instruction |= inst.operands[0].reg;
+  encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_push_pop (void)
+{
+  inst.operands[1] = inst.operands[0];
+  memset (&inst.operands[0], 0, sizeof inst.operands[0]);
+  inst.operands[0].isreg = 1;
+  inst.operands[0].writeback = 1;
+  inst.operands[0].reg = REG_SP;
+  do_ldmstm ();
 }
 
 }
 
+/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
+   word at the specified address and the following word
+   respectively.
+   Unconditionally executed.
+   Error if Rn is R15. */
+
 static void
 static void
-do_fpa_ctrl (str)
-     char * str;
+do_rfe (void)
 {
 {
-  /* FP control registers.
-     Format: <WFS|RFS|WFC|RFC>{cond} Rn  */
+  inst.instruction |= inst.operands[0].reg << 16;
+  if (inst.operands[0].writeback)
+    inst.instruction |= WRITE_BACK;
+}
 
 
-  skip_whitespace (str);
+/* ARM V6 ssat (argument parse).  */
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_ssat (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= (inst.operands[1].imm - 1) << 16;
+  inst.instruction |= inst.operands[2].reg;
 
 
-  end_of_line (str);
-  return;
+  if (inst.operands[3].present)
+    encode_arm_shift (3);
 }
 
 }
 
+/* ARM V6 usat (argument parse).  */
+
 static void
 static void
-do_fpa_ldst (str)
-     char * str;
+do_usat (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].imm << 16;
+  inst.instruction |= inst.operands[2].reg;
 
 
-  if (fp_reg_required_here (&str, 12) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  if (inst.operands[3].present)
+    encode_arm_shift (3);
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_address_required_here (&str, CP_WB_OK) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* ARM V6 ssat16 (argument parse).  */
 
 
-  end_of_line (str);
+static void
+do_ssat16 (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= ((inst.operands[1].imm - 1) << 16);
+  inst.instruction |= inst.operands[2].reg;
 }
 
 static void
 }
 
 static void
-do_fpa_ldmstm (str)
-     char * str;
+do_usat16 (void)
 {
 {
-  int num_regs;
-
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].imm << 16;
+  inst.instruction |= inst.operands[2].reg;
+}
 
 
-  if (fp_reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* ARM V6 SETEND (argument parse).  Sets the E bit in the CPSR while
+   preserving the other bits.
 
 
-  /* Get Number of registers to transfer.  */
-  if (skip_past_comma (&str) == FAIL
-      || my_get_expression (&inst.reloc.exp, &str))
-    {
-      if (! inst.error)
-       inst.error = _("constant expression expected");
-      return;
-    }
+   setend <endian_specifier>, where <endian_specifier> is either
+   BE or LE.  */
 
 
-  if (inst.reloc.exp.X_op != O_constant)
-    {
-      inst.error = _("constant value required for number of registers");
-      return;
-    }
+static void
+do_setend (void)
+{
+  if (inst.operands[0].imm)
+    inst.instruction |= 0x200;
+}
 
 
-  num_regs = inst.reloc.exp.X_add_number;
+static void
+do_shift (void)
+{
+  unsigned int Rm = (inst.operands[1].present
+                    ? inst.operands[1].reg
+                    : inst.operands[0].reg);
 
 
-  if (num_regs < 1 || num_regs > 4)
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= Rm;
+  if (inst.operands[2].isreg)  /* Rd, {Rm,} Rs */
     {
     {
-      inst.error = _("number of registers must be in the range [1:4]");
-      return;
+      constraint (inst.operands[0].reg != Rm,
+                 _("source1 and dest must be same register"));
+      inst.instruction |= inst.operands[2].reg << 8;
+      inst.instruction |= SHIFT_BY_REG;
     }
     }
+  else
+    inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+}
 
 
-  switch (num_regs)
-    {
-    case 1:
-      inst.instruction |= CP_T_X;
-      break;
-    case 2:
-      inst.instruction |= CP_T_Y;
-      break;
-    case 3:
-      inst.instruction |= CP_T_Y | CP_T_X;
-      break;
-    case 4:
-      break;
-    default:
-      abort ();
-    }
+static void
+do_smi (void)
+{
+  inst.reloc.type = BFD_RELOC_ARM_SMI;
+  inst.reloc.pc_rel = 0;
+}
 
 
-  if (inst.instruction & (CP_T_Pre | CP_T_UD)) /* ea/fd format.  */
-    {
-      int reg;
-      int write_back;
-      int offset;
+static void
+do_swi (void)
+{
+  inst.reloc.type = BFD_RELOC_ARM_SWI;
+  inst.reloc.pc_rel = 0;
+}
 
 
-      /* The instruction specified "ea" or "fd", so we can only accept
-        [Rn]{!}.  The instruction does not really support stacking or
-        unstacking, so we have to emulate these by setting appropriate
-        bits and offsets.  */
-      if (skip_past_comma (&str) == FAIL
-         || *str != '[')
-       {
-         if (! inst.error)
-           inst.error = BAD_ARGS;
-         return;
-       }
+/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
+   SMLAxy{cond} Rd,Rm,Rs,Rn
+   SMLAWy{cond} Rd,Rm,Rs,Rn
+   Error if any register is R15.  */
 
 
-      str++;
-      skip_whitespace (str);
+static void
+do_smla (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 8;
+  inst.instruction |= inst.operands[3].reg << 12;
+}
 
 
-      if ((reg = reg_required_here (&str, 16)) == FAIL)
-       return;
+/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
+   SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
+   Error if any register is R15.
+   Warning if Rdlo == Rdhi.  */
 
 
-      skip_whitespace (str);
+static void
+do_smlal (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].reg << 8;
 
 
-      if (*str != ']')
-       {
-         inst.error = BAD_ARGS;
-         return;
-       }
+  if (inst.operands[0].reg == inst.operands[1].reg)
+    as_tsktsk (_("rdhi and rdlo must be different"));
+}
 
 
-      str++;
-      if (*str == '!')
-       {
-         write_back = 1;
-         str++;
-         if (reg == REG_PC)
-           {
-             inst.error =
-               _("r15 not allowed as base register with write-back");
-             return;
-           }
-       }
-      else
-       write_back = 0;
+/* ARM V5E (El Segundo) signed-multiply (argument parse)
+   SMULxy{cond} Rd,Rm,Rs
+   Error if any register is R15.  */
 
 
-      if (inst.instruction & CP_T_Pre)
-       {
-         /* Pre-decrement.  */
-         offset = 3 * num_regs;
-         if (write_back)
-           inst.instruction |= CP_T_WB;
-       }
-      else
-       {
-         /* Post-increment.  */
-         if (write_back)
-           {
-             inst.instruction |= CP_T_WB;
-             offset = 3 * num_regs;
-           }
-         else
-           {
-             /* No write-back, so convert this into a standard pre-increment
-                instruction -- aesthetically more pleasing.  */
-             inst.instruction |= CP_T_Pre | CP_T_UD;
-             offset = 0;
-           }
-       }
+static void
+do_smul (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 8;
+}
 
 
-      inst.instruction |= offset;
-    }
-  else if (skip_past_comma (&str) == FAIL
-          || cp_address_required_here (&str, CP_WB_OK) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* ARM V6 srs (argument parse).         */
 
 
-  end_of_line (str);
+static void
+do_srs (void)
+{
+  inst.instruction |= inst.operands[0].imm;
+  if (inst.operands[0].writeback)
+    inst.instruction |= WRITE_BACK;
 }
 
 }
 
+/* ARM V6 strex (argument parse).  */
+
 static void
 static void
-do_fpa_dyadic (str)
-     char * str;
+do_strex (void)
 {
 {
-  skip_whitespace (str);
+  constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+             || inst.operands[2].postind || inst.operands[2].writeback
+             || inst.operands[2].immisreg || inst.operands[2].shifted
+             || inst.operands[2].negative,
+             _("instruction does not accept this addressing mode"));
 
 
-  if (fp_reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  constraint (inst.operands[2].reg == REG_PC, BAD_PC);
 
 
-  if (skip_past_comma (&str) == FAIL
-      || fp_reg_required_here (&str, 16) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  constraint (inst.operands[0].reg == inst.operands[1].reg
+             || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
 
 
-  if (skip_past_comma (&str) == FAIL
-      || fp_op2 (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  constraint (inst.reloc.exp.X_op != O_constant
+             || inst.reloc.exp.X_add_number != 0,
+             _("offset must be zero in ARM encoding"));
 
 
-  end_of_line (str);
-  return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 16;
+  inst.reloc.type = BFD_RELOC_UNUSED;
 }
 
 static void
 }
 
 static void
-do_fpa_monadic (str)
-     char * str;
+do_strexd (void)
 {
 {
-  skip_whitespace (str);
-
-  if (fp_reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  constraint (inst.operands[1].reg % 2 != 0,
+             _("even register required"));
+  constraint (inst.operands[2].present
+             && inst.operands[2].reg != inst.operands[1].reg + 1,
+             _("can only store two consecutive registers"));
+  /* If op 2 were present and equal to PC, this function wouldn't
+     have been called in the first place.  */
+  constraint (inst.operands[1].reg == REG_LR, _("r14 not allowed here"));
 
 
-  if (skip_past_comma (&str) == FAIL
-      || fp_op2 (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  constraint (inst.operands[0].reg == inst.operands[1].reg
+             || inst.operands[0].reg == inst.operands[1].reg + 1
+             || inst.operands[0].reg == inst.operands[3].reg,
+             BAD_OVERLAP);
 
 
-  end_of_line (str);
-  return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[3].reg << 16;
 }
 
 }
 
+/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
+   extends it to 32-bits, and adds the result to a value in another
+   register.  You can specify a rotation by 0, 8, 16, or 24 bits
+   before extracting the 16-bit value.
+   SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
+   Condition defaults to COND_ALWAYS.
+   Error if any register uses R15.  */
+
 static void
 static void
-do_fpa_cmp (str)
-     char * str;
+do_sxtah (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].imm << 10;
+}
 
 
-  if (fp_reg_required_here (&str, 16) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* ARM V6 SXTH.
 
 
-  if (skip_past_comma (&str) == FAIL
-      || fp_op2 (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+   SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
+   Condition defaults to COND_ALWAYS.
+   Error if any register uses R15.  */
 
 
-  end_of_line (str);
-  return;
+static void
+do_sxth (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].imm << 10;
 }
 }
+\f
+/* VFP instructions.  In a logical order: SP variant first, monad
+   before dyad, arithmetic then move then load/store.  */
 
 static void
 
 static void
-do_fpa_from_reg (str)
-     char * str;
+do_vfp_sp_monadic (void)
 {
 {
-  skip_whitespace (str);
-
-  if (fp_reg_required_here (&str, 16) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+  encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_vfp_sp_dyadic (void)
+{
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+  encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn);
+  encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm);
 }
 
 static void
 }
 
 static void
-do_fpa_to_reg (str)
-     char * str;
+do_vfp_sp_compare_z (void)
 {
 {
-  skip_whitespace (str);
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+}
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+static void
+do_vfp_dp_sp_cvt (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || fp_reg_required_here (&str, 0) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_vfp_sp_dp_cvt (void)
+{
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+  inst.instruction |= inst.operands[1].reg;
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_vfp_reg_from_sp (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn);
 }
 
 }
 
-static int
-vfp_sp_reg_required_here (str, pos)
-     char **str;
-     enum vfp_sp_reg_pos pos;
+static void
+do_vfp_reg2_from_sp2 (void)
 {
 {
-  int    reg;
-  char *start = *str;
+  constraint (inst.operands[2].imm != 2,
+             _("only two consecutive VFP SP registers allowed here"));
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
 
 
-  if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab)) != FAIL)
-    {
-      switch (pos)
-       {
-       case VFP_REG_Sd:
-         inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
-         break;
+static void
+do_vfp_sp_from_reg (void)
+{
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sn);
+  inst.instruction |= inst.operands[1].reg << 12;
+}
 
 
-       case VFP_REG_Sn:
-         inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
-         break;
+static void
+do_vfp_sp2_from_reg2 (void)
+{
+  constraint (inst.operands[0].imm != 2,
+             _("only two consecutive VFP SP registers allowed here"));
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sm);
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+}
 
 
-       case VFP_REG_Sm:
-         inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
-         break;
+static void
+do_vfp_sp_ldst (void)
+{
+  encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+  encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
 
 
-       default:
-         abort ();
-       }
-      return reg;
-    }
+static void
+do_vfp_dp_ldst (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
 
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  inst.error = _(all_reg_maps[REG_TYPE_SN].expected);
 
 
-  /* Restore the start point.  */
-  *str = start;
-  return FAIL;
+static void
+vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+  if (inst.operands[0].writeback)
+    inst.instruction |= WRITE_BACK;
+  else
+    constraint (ldstm_type != VFP_LDSTMIA,
+               _("this addressing mode requires base-register writeback"));
+  inst.instruction |= inst.operands[0].reg << 16;
+  encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sd);
+  inst.instruction |= inst.operands[1].imm;
 }
 
 }
 
-static int
-vfp_dp_reg_required_here (str, pos)
-     char **str;
-     enum vfp_dp_reg_pos pos;
+static void
+vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type)
 {
 {
-  int   reg;
-  char *start = *str;
-
-  if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_DN].htab)) != FAIL)
-    {
-      switch (pos)
-       {
-       case VFP_REG_Dd:
-         inst.instruction |= reg << 12;
-         break;
+  int count;
 
 
-       case VFP_REG_Dn:
-         inst.instruction |= reg << 16;
-         break;
+  if (inst.operands[0].writeback)
+    inst.instruction |= WRITE_BACK;
+  else
+    constraint (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX,
+               _("this addressing mode requires base-register writeback"));
 
 
-       case VFP_REG_Dm:
-         inst.instruction |= reg << 0;
-         break;
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg << 12;
 
 
-       default:
-         abort ();
-       }
-      return reg;
-    }
+  count = inst.operands[1].imm << 1;
+  if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
+    count += 1;
 
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  inst.error = _(all_reg_maps[REG_TYPE_DN].expected);
+  inst.instruction |= count;
+}
 
 
-  /* Restore the start point.  */
-  *str = start;
-  return FAIL;
+static void
+do_vfp_sp_ldstmia (void)
+{
+  vfp_sp_ldstm (VFP_LDSTMIA);
 }
 
 static void
 }
 
 static void
-do_vfp_sp_monadic (str)
-     char *str;
+do_vfp_sp_ldstmdb (void)
 {
 {
-  skip_whitespace (str);
+  vfp_sp_ldstm (VFP_LDSTMDB);
+}
 
 
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
-    return;
+static void
+do_vfp_dp_ldstmia (void)
+{
+  vfp_dp_ldstm (VFP_LDSTMIA);
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_vfp_dp_ldstmdb (void)
+{
+  vfp_dp_ldstm (VFP_LDSTMDB);
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_vfp_xp_ldstmia (void)
+{
+  vfp_dp_ldstm (VFP_LDSTMIAX);
 }
 
 static void
 }
 
 static void
-do_vfp_dp_monadic (str)
-     char *str;
+do_vfp_xp_ldstmdb (void)
 {
 {
-  skip_whitespace (str);
+  vfp_dp_ldstm (VFP_LDSTMDBX);
+}
+\f
+/* FPA instructions.  Also in a logical order. */
 
 
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
-    return;
+static void
+do_fpa_cmp (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
+static void
+do_fpa_ldmstm (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  switch (inst.operands[1].imm)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+    case 1: inst.instruction |= CP_T_X;                 break;
+    case 2: inst.instruction |= CP_T_Y;                 break;
+    case 3: inst.instruction |= CP_T_Y | CP_T_X; break;
+    case 4:                                     break;
+    default: abort ();
     }
 
     }
 
-  end_of_line (str);
-  return;
-}
+  if (inst.instruction & (PRE_INDEX | INDEX_UP))
+    {
+      /* The instruction specified "ea" or "fd", so we can only accept
+        [Rn]{!}.  The instruction does not really support stacking or
+        unstacking, so we have to emulate these by setting appropriate
+        bits and offsets.  */
+      constraint (inst.reloc.exp.X_op != O_constant
+                 || inst.reloc.exp.X_add_number != 0,
+                 _("this instruction does not support indexing"));
 
 
-static void
-do_vfp_sp_dyadic (str)
-     char *str;
-{
-  skip_whitespace (str);
+      if ((inst.instruction & PRE_INDEX) || inst.operands[2].writeback)
+       inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm;
 
 
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
-    return;
+      if (!(inst.instruction & INDEX_UP))
+       inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      if (!(inst.instruction & PRE_INDEX) && inst.operands[2].writeback)
+       {
+         inst.operands[2].preind = 0;
+         inst.operands[2].postind = 1;
+       }
     }
 
     }
 
-  end_of_line (str);
-  return;
+  encode_arm_cp_address (2, TRUE, TRUE, 0);
 }
 }
+\f
+/* iWMMXt instructions: strictly in alphabetical order.         */
 
 static void
 
 static void
-do_vfp_dp_dyadic (str)
-     char *str;
+do_iwmmxt_tandorc (void)
 {
 {
-  skip_whitespace (str);
-
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
-    return;
-
-  if (skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  end_of_line (str);
-  return;
+  constraint (inst.operands[0].reg != REG_PC, _("only r15 allowed here"));
 }
 
 static void
 }
 
 static void
-do_vfp_reg_from_sp (str)
-     char *str;
+do_iwmmxt_textrc (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].imm;
+}
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+static void
+do_iwmmxt_textrm (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].imm;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_iwmmxt_tinsr (void)
+{
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].imm;
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_iwmmxt_tmia (void)
+{
+  inst.instruction |= inst.operands[0].reg << 5;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 12;
 }
 
 static void
 }
 
 static void
-do_vfp_sp_reg2 (str)
-     char *str;
+do_iwmmxt_waligni (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].imm << 20;
+}
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+static void
+do_iwmmxt_wmov (void)
+{
+  /* WMOV rD, rN is an alias for WOR rD, rN, rN.  */
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 16) == FAIL
-      || skip_past_comma (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_iwmmxt_wldstbh (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.reloc.exp.X_add_number *= 4;
+  encode_arm_cp_address (1, TRUE, FALSE, BFD_RELOC_ARM_CP_OFF_IMM_S2);
+}
 
 
-  /* We require exactly two consecutive SP registers.  */
-  if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+static void
+do_iwmmxt_wldstw (void)
+{
+  /* RIWR_RIWC clears .isreg for a control register.  */
+  if (!inst.operands[0].isreg)
     {
     {
-      if (! inst.error)
-       inst.error = _("only two consecutive VFP SP registers allowed here");
+      constraint (inst.cond != COND_ALWAYS, BAD_COND);
+      inst.instruction |= 0xf0000000;
     }
 
     }
 
-  end_of_line (str);
-  return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_cp_address (1, TRUE, TRUE, 0);
 }
 
 static void
 }
 
 static void
-do_vfp_sp_from_reg (str)
-     char *str;
+do_iwmmxt_wldstd (void)
 {
 {
-  skip_whitespace (str);
-
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL)
-    return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_arm_cp_address (1, TRUE, FALSE, 0);
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_iwmmxt_wshufh (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= ((inst.operands[2].imm & 0xf0) << 16);
+  inst.instruction |= (inst.operands[2].imm & 0x0f);
+}
 
 
-  end_of_line (str);
-  return;
+static void
+do_iwmmxt_wzero (void)
+{
+  /* WZERO reg is an alias for WANDN reg, reg, reg.  */
+  inst.instruction |= inst.operands[0].reg;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[0].reg << 16;
 }
 }
+\f
+/* Cirrus Maverick instructions.  Simple 2-, 3-, and 4-register
+   operations first, then control, shift, and load/store.  */
+
+/* Insns like "foo X,Y,Z".  */
 
 static void
 
 static void
-do_vfp_reg_from_dp (str)
-     char *str;
+do_mav_triple (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[0].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 12;
+}
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+/* Insns like "foo W,X,Y,Z".
+    where W=MVAX[0:3] and X,Y,Z=MVFX[0:15].  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_mav_quad (void)
+{
+  inst.instruction |= inst.operands[0].reg << 5;
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+  inst.instruction |= inst.operands[3].reg;
+}
 
 
-  end_of_line (str);
-  return;
+/* cfmvsc32<cond> DSPSC,MVDX[15:0].  */
+static void
+do_mav_dspsc (void)
+{
+  inst.instruction |= inst.operands[1].reg << 12;
 }
 
 }
 
+/* Maverick shift immediate instructions.
+   cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
+   cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0].  */
+
 static void
 static void
-do_vfp_reg2_from_dp (str)
-     char *str;
+do_mav_shift (void)
 {
 {
-  skip_whitespace (str);
+  int imm = inst.operands[2].imm;
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 16) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
+     Bits 5-7 of the insn should have bits 4-6 of the immediate.
+     Bit 4 should be 0.         */
+  imm = (imm & 0xf) | ((imm & 0x70) << 1);
 
 
-  end_of_line (str);
-  return;
+  inst.instruction |= imm;
 }
 }
+\f
+/* XScale instructions.         Also sorted arithmetic before move.  */
+
+/* Xscale multiply-accumulate (argument parse)
+     MIAcc   acc0,Rm,Rs
+     MIAPHcc acc0,Rm,Rs
+     MIAxycc acc0,Rm,Rs.  */
 
 static void
 
 static void
-do_vfp_dp_from_reg (str)
-     char *str;
+do_xsc_mia (void)
 {
 {
-  skip_whitespace (str);
-
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL)
-    return;
+  inst.instruction |= inst.operands[1].reg;
+  inst.instruction |= inst.operands[2].reg << 12;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+/* Xscale move-accumulator-register (argument parse)
 
 
-  end_of_line (str);
-  return;
-}
+     MARcc   acc0,RdLo,RdHi.  */
 
 static void
 
 static void
-do_vfp_dp_from_reg2 (str)
-     char *str;
+do_xsc_mar (void)
 {
 {
-  skip_whitespace (str);
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+}
 
 
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
-    return;
+/* Xscale move-register-accumulator (argument parse)
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 16))
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+     MRAcc   RdLo,RdHi,acc0.  */
 
 
-  end_of_line (str);
-  return;
+static void
+do_xsc_mra (void)
+{
+  constraint (inst.operands[0].reg == inst.operands[1].reg, BAD_OVERLAP);
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
 }
 }
+\f
+/* Encoding functions relevant only to Thumb.  */
 
 
-static const struct vfp_reg *
-vfp_psr_parse (str)
-     char **str;
-{
-  char *start = *str;
-  char  c;
-  char *p;
-  const struct vfp_reg *vreg;
+/* inst.operands[i] is a shifted-register operand; encode
+   it into inst.instruction in the format used by Thumb32.  */
 
 
-  p = start;
+static void
+encode_thumb32_shifted_operand (int i)
+{
+  unsigned int value = inst.reloc.exp.X_add_number;
+  unsigned int shift = inst.operands[i].shift_kind;
 
 
-  /* Find the end of the current token.  */
-  do
+  constraint (inst.operands[i].immisreg,
+             _("shift by register not allowed in thumb mode"));
+  inst.instruction |= inst.operands[i].reg;
+  if (shift == SHIFT_RRX)
+    inst.instruction |= SHIFT_ROR << 4;
+  else
     {
     {
-      c = *p++;
-    }
-  while (ISALPHA (c));
+      constraint (inst.reloc.exp.X_op != O_constant,
+                 _("expression too complex"));
 
 
-  /* Mark it.  */
-  *--p = 0;
+      constraint (value > 32
+                 || (value == 32 && (shift == SHIFT_LSL
+                                     || shift == SHIFT_ROR)),
+                 _("shift expression is too large"));
 
 
-  for (vreg = vfp_regs + 0;
-       vreg < vfp_regs + sizeof (vfp_regs) / sizeof (struct vfp_reg);
-       vreg++)
-    {
-      if (strcmp (start, vreg->name) == 0)
-       {
-         *p = c;
-         *str = p;
-         return vreg;
-       }
-    }
+      if (value == 0)
+       shift = SHIFT_LSL;
+      else if (value == 32)
+       value = 0;
 
 
-  *p = c;
-  return NULL;
+      inst.instruction |= shift << 4;
+      inst.instruction |= (value & 0x1c) << 10;
+      inst.instruction |= (value & 0x03) << 6;
+    }
 }
 
 }
 
-static int
-vfp_psr_required_here (str)
-     char **str;
+
+/* inst.operands[i] was set up by parse_address.  Encode it into a
+   Thumb32 format load or store instruction.  Reject forms that cannot
+   be used with such instructions.  If is_t is true, reject forms that
+   cannot be used with a T instruction; if is_d is true, reject forms
+   that cannot be used with a D instruction.  */
+
+static void
+encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
 {
 {
-  char *start = *str;
-  const struct vfp_reg *vreg;
+  bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
 
 
-  vreg = vfp_psr_parse (str);
+  constraint (!inst.operands[i].isreg,
+             _("Thumb does not support the ldr =N pseudo-operation"));
 
 
-  if (vreg)
+  inst.instruction |= inst.operands[i].reg << 16;
+  if (inst.operands[i].immisreg)
     {
     {
-      inst.instruction |= vreg->regno;
-      return SUCCESS;
-    }
+      constraint (is_pc, _("cannot use register index with PC-relative addressing"));
+      constraint (is_t || is_d, _("cannot use register index with this instruction"));
+      constraint (inst.operands[i].negative,
+                 _("Thumb does not support negative register indexing"));
+      constraint (inst.operands[i].postind,
+                 _("Thumb does not support register post-indexing"));
+      constraint (inst.operands[i].writeback,
+                 _("Thumb does not support register indexing with writeback"));
+      constraint (inst.operands[i].shifted && inst.operands[i].shift_kind != SHIFT_LSL,
+                 _("Thumb supports only LSL in shifted register indexing"));
 
 
-  inst.error = _("VFP system register expected");
-
-  *str = start;
-  return FAIL;
-}
+      inst.instruction |= inst.operands[1].imm;
+      if (inst.operands[i].shifted)
+       {
+         constraint (inst.reloc.exp.X_op != O_constant,
+                     _("expression too complex"));
+         constraint (inst.reloc.exp.X_add_number < 0
+                     || inst.reloc.exp.X_add_number > 3,
+                     _("shift out of range"));
+         inst.instruction |= inst.reloc.exp.X_add_number << 4;
+       }
+      inst.reloc.type = BFD_RELOC_UNUSED;
+    }
+  else if (inst.operands[i].preind)
+    {
+      constraint (is_pc && inst.operands[i].writeback,
+                 _("cannot use writeback with PC-relative addressing"));
+      constraint (is_t && inst.operands[1].writeback,
+                 _("cannot use writeback with this instruction"));
 
 
-static void
-do_vfp_reg_from_ctrl (str)
-     char *str;
+      if (is_d)
+       {
+         inst.instruction |= 0x01000000;
+         if (inst.operands[i].writeback)
+           inst.instruction |= 0x00200000;
+       }
+      else
+       {
+         inst.instruction |= 0x00000c00;
+         if (inst.operands[i].writeback)
+           inst.instruction |= 0x00000100;
+       }
+      inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+    }
+  else if (inst.operands[i].postind)
+    {
+      assert (inst.operands[i].writeback);
+      constraint (is_pc, _("cannot use post-indexing with PC-relative addressing"));
+      constraint (is_t, _("cannot use post-indexing with this instruction"));
+
+      if (is_d)
+       inst.instruction |= 0x00200000;
+      else
+       inst.instruction |= 0x00000900;
+      inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+    }
+  else /* unindexed - only for coprocessor */
+    inst.error = _("instruction does not accept unindexed addressing");
+}
+
+/* Table of Thumb instructions which exist in both 16- and 32-bit
+   encodings (the latter only in post-V6T2 cores).  The index is the
+   value used in the insns table below.  When there is more than one
+   possible 16-bit encoding for the instruction, this table always
+   holds variant (1).  */
+#define T16_32_TAB                             \
+  X(adc,   4140, eb400000),                    \
+  X(adcs,  4140, eb500000),                    \
+  X(add,   1c00, eb000000),                    \
+  X(adds,  1c00, eb100000),                    \
+  X(adr,   000f, f20f0000),                    \
+  X(and,   4000, ea000000),                    \
+  X(ands,  4000, ea100000),                    \
+  X(asr,   1000, fa40f000),                    \
+  X(asrs,  1000, fa50f000),                    \
+  X(bic,   4380, ea200000),                    \
+  X(bics,  4380, ea300000),                    \
+  X(cmn,   42c0, eb100f00),                    \
+  X(cmp,   2800, ebb00f00),                    \
+  X(cpsie, b660, f3af8400),                    \
+  X(cpsid, b670, f3af8600),                    \
+  X(cpy,   4600, ea4f0000),                    \
+  X(eor,   4040, ea800000),                    \
+  X(eors,  4040, ea900000),                    \
+  X(ldmia, c800, e8900000),                    \
+  X(ldr,   6800, f8500000),                    \
+  X(ldrb,  7800, f8100000),                    \
+  X(ldrh,  8800, f8300000),                    \
+  X(ldrsb, 5600, f9100000),                    \
+  X(ldrsh, 5e00, f9300000),                    \
+  X(lsl,   0000, fa00f000),                    \
+  X(lsls,  0000, fa10f000),                    \
+  X(lsr,   0800, fa20f000),                    \
+  X(lsrs,  0800, fa30f000),                    \
+  X(mov,   2000, ea4f0000),                    \
+  X(movs,  2000, ea5f0000),                    \
+  X(mul,   4340, fb00f000),                     \
+  X(muls,  4340, ffffffff), /* no 32b muls */  \
+  X(mvn,   43c0, ea6f0000),                    \
+  X(mvns,  43c0, ea7f0000),                    \
+  X(neg,   4240, f1c00000), /* rsb #0 */       \
+  X(negs,  4240, f1d00000), /* rsbs #0 */      \
+  X(orr,   4300, ea400000),                    \
+  X(orrs,  4300, ea500000),                    \
+  X(pop,   bc00, e8bd0000), /* ldmia sp!,... */        \
+  X(push,  b400, e92d0000), /* stmdb sp!,... */        \
+  X(rev,   ba00, fa90f080),                    \
+  X(rev16, ba40, fa90f090),                    \
+  X(revsh, bac0, fa90f0b0),                    \
+  X(ror,   41c0, fa60f000),                    \
+  X(rors,  41c0, fa70f000),                    \
+  X(sbc,   4180, eb600000),                    \
+  X(sbcs,  4180, eb700000),                    \
+  X(stmia, c000, e8800000),                    \
+  X(str,   6000, f8400000),                    \
+  X(strb,  7000, f8000000),                    \
+  X(strh,  8000, f8200000),                    \
+  X(sub,   1e00, eba00000),                    \
+  X(subs,  1e00, ebb00000),                    \
+  X(sxtb,  b240, fa4ff080),                    \
+  X(sxth,  b200, fa0ff080),                    \
+  X(tst,   4200, ea100f00),                    \
+  X(uxtb,  b2c0, fa5ff080),                    \
+  X(uxth,  b280, fa1ff080),                    \
+  X(nop,   bf00, f3af8000),                    \
+  X(yield, bf10, f3af8001),                    \
+  X(wfe,   bf20, f3af8002),                    \
+  X(wfi,   bf30, f3af8003),                    \
+  X(sev,   bf40, f3af9004), /* typo, 8004? */
+
+/* To catch errors in encoding functions, the codes are all offset by
+   0xF800, putting them in one of the 32-bit prefix ranges, ergo undefined
+   as 16-bit instructions.  */
+#define X(a,b,c) T_MNEM_##a
+enum t16_32_codes { T16_32_OFFSET = 0xF7FF, T16_32_TAB };
+#undef X
+
+#define X(a,b,c) 0x##b
+static const unsigned short thumb_op16[] = { T16_32_TAB };
+#define THUMB_OP16(n) (thumb_op16[(n) - (T16_32_OFFSET + 1)])
+#undef X
+
+#define X(a,b,c) 0x##c
+static const unsigned int thumb_op32[] = { T16_32_TAB };
+#define THUMB_OP32(n) (thumb_op32[(n) - (T16_32_OFFSET + 1)])
+#define THUMB_SETS_FLAGS(n) (THUMB_OP32 (n) & 0x00100000)
+#undef X
+#undef T16_32_TAB
+
+/* Thumb instruction encoders, in alphabetical order.  */
+
+/* ADDW or SUBW.  */
+static void
+do_t_add_sub_w (void)
+{
+  int Rd, Rn;
+
+  Rd = inst.operands[0].reg;
+  Rn = inst.operands[1].reg;
+
+  constraint (Rd == 15, _("PC not allowed as destination"));
+  inst.instruction |= (Rn << 16) | (Rd << 8);
+  inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+}
+
+/* Parse an add or subtract instruction.  We get here with inst.instruction
+   equalling any of THUMB_OPCODE_add, adds, sub, or subs.  */
+
+static void
+do_t_add_sub (void)
 {
 {
-  skip_whitespace (str);
+  int Rd, Rs, Rn;
 
 
-  if (reg_required_here (&str, 12) == FAIL)
-    return;
+  Rd = inst.operands[0].reg;
+  Rs = (inst.operands[1].present
+       ? inst.operands[1].reg    /* Rd, Rs, foo */
+       : inst.operands[0].reg);  /* Rd, foo -> Rd, Rd, foo */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_psr_required_here (&str) == FAIL)
+  if (unified_syntax)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  end_of_line (str);
-  return;
-}
+      if (!inst.operands[2].isreg)
+       {
+         /* ??? Convert large immediates to addw/subw.  */
+         /* ??? 16-bit adds with small immediates.  */
+         /* For an immediate, we always generate a 32-bit opcode;
+            section relaxation will shrink it later if possible.  */
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+         inst.instruction |= inst.operands[0].reg << 8;
+         inst.instruction |= inst.operands[1].reg << 16;
+         inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+       }
+      else
+       {
+         Rn = inst.operands[2].reg;
+         /* See if we can do this with a 16-bit instruction.  */
+         if (!inst.operands[2].shifted && inst.size_req != 4)
+           {
+             bfd_boolean narrow;
 
 
-static void
-do_vfp_ctrl_from_reg (str)
-     char *str;
-{
-  skip_whitespace (str);
+             if (inst.instruction == T_MNEM_adds
+                 || inst.instruction == T_MNEM_subs)
+               narrow = (current_it_mask == 0);
+             else
+               narrow = (current_it_mask != 0);
+             if (Rd > 7 || Rs > 7 || Rn > 7)
+               narrow = FALSE;
 
 
-  if (vfp_psr_required_here (&str) == FAIL)
-    return;
+             if (narrow)
+               {
+                 inst.instruction = ((inst.instruction == T_MNEM_adds
+                                      || inst.instruction == T_MNEM_add)
+                                     ? T_OPCODE_ADD_R3
+                                     : T_OPCODE_SUB_R3);
+                 inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+                 return;
+               }
 
 
-  if (skip_past_comma (&str) == FAIL
-      || reg_required_here (&str, 12) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+             if (inst.instruction == T_MNEM_add)
+               {
+                 if (Rd == Rs)
+                   {
+                     inst.instruction = T_OPCODE_ADD_HI;
+                     inst.instruction |= (Rd & 8) << 4;
+                     inst.instruction |= (Rd & 7);
+                     inst.instruction |= Rn << 3;
+                     return;
+                   }
+                 /* ... because addition is commutative! */
+                 else if (Rd == Rn)
+                   {
+                     inst.instruction = T_OPCODE_ADD_HI;
+                     inst.instruction |= (Rd & 8) << 4;
+                     inst.instruction |= (Rd & 7);
+                     inst.instruction |= Rs << 3;
+                     return;
+                   }
+               }
+           }
+         /* If we get here, it can't be done in 16 bits.  */
+         constraint (inst.operands[2].shifted && inst.operands[2].immisreg,
+                     _("shift must be constant"));
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= Rd << 8;
+         inst.instruction |= Rs << 16;
+         encode_thumb32_shifted_operand (2);
+       }
     }
     }
+  else
+    {
+      constraint (inst.instruction == T_MNEM_adds
+                 || inst.instruction == T_MNEM_subs,
+                 BAD_THUMB32);
 
 
-  end_of_line (str);
-  return;
-}
+      if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
+       {
+         constraint ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
+                     || (Rs > 7 && Rs != REG_SP && Rs != REG_PC),
+                     BAD_HIREG);
 
 
-static void
-do_vfp_sp_ldst (str)
-     char *str;
-{
-  skip_whitespace (str);
+         inst.instruction = (inst.instruction == T_MNEM_add
+                             ? 0x0000 : 0x8000);
+         inst.instruction |= (Rd << 4) | Rs;
+         inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+         return;
+       }
 
 
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+      Rn = inst.operands[2].reg;
+      constraint (inst.operands[2].shifted, _("unshifted register required"));
 
 
-  if (skip_past_comma (&str) == FAIL
-      || cp_address_required_here (&str, CP_NO_WB) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* We now have Rd, Rs, and Rn set to registers.  */
+      if (Rd > 7 || Rs > 7 || Rn > 7)
+       {
+         /* Can't do this for SUB.      */
+         constraint (inst.instruction == T_MNEM_sub, BAD_HIREG);
+         inst.instruction = T_OPCODE_ADD_HI;
+         inst.instruction |= (Rd & 8) << 4;
+         inst.instruction |= (Rd & 7);
+         if (Rs == Rd)
+           inst.instruction |= Rn << 3;
+         else if (Rn == Rd)
+           inst.instruction |= Rs << 3;
+         else
+           constraint (1, _("dest must overlap one source register"));
+       }
+      else
+       {
+         inst.instruction = (inst.instruction == T_MNEM_add
+                             ? T_OPCODE_ADD_R3 : T_OPCODE_SUB_R3);
+         inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+       }
     }
     }
-
-  end_of_line (str);
-  return;
 }
 
 static void
 }
 
 static void
-do_vfp_dp_ldst (str)
-     char *str;
+do_t_adr (void)
 {
 {
-  skip_whitespace (str);
-
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
+  if (unified_syntax && inst.size_req != 2)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* Always generate a 32-bit opcode;
+        section relaxation will shrink it later if possible.  */
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
+      inst.reloc.pc_rel = 1;
     }
     }
-
-  if (skip_past_comma (&str) == FAIL
-      || cp_address_required_here (&str, CP_NO_WB) == FAIL)
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+      inst.reloc.exp.X_add_number -= 4; /* PC relative adjust.  */
+      inst.reloc.pc_rel = 1;
 
 
-  end_of_line (str);
-  return;
+      inst.instruction |= inst.operands[0].reg << 4;
+    }
 }
 
 }
 
-/* Parse and encode a VFP SP register list, storing the initial
-   register in position POS and returning the range as the result.  If
-   the string is invalid return FAIL (an invalid range).  */
-static long
-vfp_sp_reg_list (str, pos)
-     char **str;
-     enum vfp_sp_reg_pos pos;
+/* Arithmetic instructions for which there is just one 16-bit
+   instruction encoding, and it allows only two low registers.
+   For maximal compatibility with ARM syntax, we allow three register
+   operands even when Thumb-32 instructions are not available, as long
+   as the first two are identical.  For instance, both "sbc r0,r1" and
+   "sbc r0,r0,r1" are allowed.  */
+static void
+do_t_arit3 (void)
 {
 {
-  long range = 0;
-  int base_reg = 0;
-  int new_base;
-  long base_bits = 0;
-  int count = 0;
-  long tempinst;
-  unsigned long mask = 0;
-  int warned = 0;
-
-  if (**str != '{')
-    return FAIL;
-
-  (*str)++;
-  skip_whitespace (*str);
+  int Rd, Rs, Rn;
 
 
-  tempinst = inst.instruction;
+  Rd = inst.operands[0].reg;
+  Rs = (inst.operands[1].present
+       ? inst.operands[1].reg    /* Rd, Rs, foo */
+       : inst.operands[0].reg);  /* Rd, foo -> Rd, Rd, foo */
+  Rn = inst.operands[2].reg;
 
 
-  do
+  if (unified_syntax)
     {
     {
-      inst.instruction = 0;
-
-      if ((new_base = vfp_sp_reg_required_here (str, pos)) == FAIL)
-       return FAIL;
-
-      if (count == 0 || base_reg > new_base)
-       {
-         base_reg = new_base;
-         base_bits = inst.instruction;
-       }
-
-      if (mask & (1 << new_base))
-       {
-         inst.error = _("invalid register list");
-         return FAIL;
-       }
-
-      if ((mask >> new_base) != 0 && ! warned)
+      if (!inst.operands[2].isreg)
        {
        {
-         as_tsktsk (_("register list not in ascending order"));
-         warned = 1;
+         /* For an immediate, we always generate a 32-bit opcode;
+            section relaxation will shrink it later if possible.  */
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+         inst.instruction |= Rd << 8;
+         inst.instruction |= Rs << 16;
+         inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
        }
        }
-
-      mask |= 1 << new_base;
-      count++;
-
-      skip_whitespace (*str);
-
-      if (**str == '-') /* We have the start of a range expression */
+      else
        {
        {
-         int high_range;
+         bfd_boolean narrow;
 
 
-         (*str)++;
+         /* See if we can do this with a 16-bit instruction.  */
+         if (THUMB_SETS_FLAGS (inst.instruction))
+           narrow = current_it_mask == 0;
+         else
+           narrow = current_it_mask != 0;
 
 
-         if ((high_range
-              = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab))
-             == FAIL)
-           {
-             inst.error = _(all_reg_maps[REG_TYPE_SN].expected);
-             return FAIL;
-           }
+         if (Rd > 7 || Rn > 7 || Rs > 7)
+           narrow = FALSE;
+         if (inst.operands[2].shifted)
+           narrow = FALSE;
+         if (inst.size_req == 4)
+           narrow = FALSE;
 
 
-         if (high_range <= new_base)
+         if (narrow
+             && Rd == Rs)
            {
            {
-             inst.error = _("register range not in ascending order");
-             return FAIL;
+             inst.instruction = THUMB_OP16 (inst.instruction);
+             inst.instruction |= Rd;
+             inst.instruction |= Rn << 3;
+             return;
            }
 
            }
 
-         for (new_base++; new_base <= high_range; new_base++)
-           {
-             if (mask & (1 << new_base))
-               {
-                 inst.error = _("invalid register list");
-                 return FAIL;
-               }
-
-             mask |= 1 << new_base;
-             count++;
-           }
+         /* If we get here, it can't be done in 16 bits.  */
+         constraint (inst.operands[2].shifted
+                     && inst.operands[2].immisreg,
+                     _("shift must be constant"));
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= Rd << 8;
+         inst.instruction |= Rs << 16;
+         encode_thumb32_shifted_operand (2);
        }
     }
        }
     }
-  while (skip_past_comma (str) != FAIL);
-
-  if (**str != '}')
+  else
     {
     {
-      inst.error = _("invalid register list");
-      return FAIL;
-    }
+      /* On its face this is a lie - the instruction does set the
+        flags.  However, the only supported mnemonic in this mode
+        says it doesn't.  */
+      constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
 
 
-  (*str)++;
-
-  range = count;
-
-  /* Sanity check -- should have raised a parse error above.  */
-  if (count == 0 || count > 32)
-    abort ();
+      constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+                 _("unshifted register required"));
+      constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+      constraint (Rd != Rs,
+                 _("dest and source1 must be the same register"));
 
 
-  /* Final test -- the registers must be consecutive.  */
-  while (count--)
-    {
-      if ((mask & (1 << base_reg++)) == 0)
-       {
-         inst.error = _("non-contiguous register range");
-         return FAIL;
-       }
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= Rd;
+      inst.instruction |= Rn << 3;
     }
     }
-
-  inst.instruction = tempinst | base_bits;
-  return range;
 }
 
 }
 
-static long
-vfp_dp_reg_list (str)
-     char **str;
+/* Similarly, but for instructions where the arithmetic operation is
+   commutative, so we can allow either of them to be different from
+   the destination operand in a 16-bit instruction.  For instance, all
+   three of "adc r0,r1", "adc r0,r0,r1", and "adc r0,r1,r0" are
+   accepted.  */
+static void
+do_t_arit3c (void)
 {
 {
-  long range = 0;
-  int base_reg = 0;
-  int new_base;
-  int count = 0;
-  long tempinst;
-  unsigned long mask = 0;
-  int warned = 0;
-
-  if (**str != '{')
-    return FAIL;
-
-  (*str)++;
-  skip_whitespace (*str);
+  int Rd, Rs, Rn;
 
 
-  tempinst = inst.instruction;
+  Rd = inst.operands[0].reg;
+  Rs = (inst.operands[1].present
+       ? inst.operands[1].reg    /* Rd, Rs, foo */
+       : inst.operands[0].reg);  /* Rd, foo -> Rd, Rd, foo */
+  Rn = inst.operands[2].reg;
 
 
-  do
+  if (unified_syntax)
     {
     {
-      inst.instruction = 0;
-
-      if ((new_base = vfp_dp_reg_required_here (str, VFP_REG_Dd)) == FAIL)
-       return FAIL;
-
-      if (count == 0 || base_reg > new_base)
-       {
-         base_reg = new_base;
-         range = inst.instruction;
-       }
-
-      if (mask & (1 << new_base))
-       {
-         inst.error = _("invalid register list");
-         return FAIL;
-       }
-
-      if ((mask >> new_base) != 0 && ! warned)
+      if (!inst.operands[2].isreg)
        {
        {
-         as_tsktsk (_("register list not in ascending order"));
-         warned = 1;
+         /* For an immediate, we always generate a 32-bit opcode;
+            section relaxation will shrink it later if possible.  */
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+         inst.instruction |= Rd << 8;
+         inst.instruction |= Rs << 16;
+         inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
        }
        }
-
-      mask |= 1 << new_base;
-      count++;
-
-      skip_whitespace (*str);
-
-      if (**str == '-') /* We have the start of a range expression */
+      else
        {
        {
-         int high_range;
-
-         (*str)++;
+         bfd_boolean narrow;
 
 
-         if ((high_range
-              = arm_reg_parse (str, all_reg_maps[REG_TYPE_DN].htab))
-             == FAIL)
-           {
-             inst.error = _(all_reg_maps[REG_TYPE_DN].expected);
-             return FAIL;
-           }
+         /* See if we can do this with a 16-bit instruction.  */
+         if (THUMB_SETS_FLAGS (inst.instruction))
+           narrow = current_it_mask == 0;
+         else
+           narrow = current_it_mask != 0;
 
 
-         if (high_range <= new_base)
-           {
-             inst.error = _("register range not in ascending order");
-             return FAIL;
-           }
+         if (Rd > 7 || Rn > 7 || Rs > 7)
+           narrow = FALSE;
+         if (inst.operands[2].shifted)
+           narrow = FALSE;
+         if (inst.size_req == 4)
+           narrow = FALSE;
 
 
-         for (new_base++; new_base <= high_range; new_base++)
+         if (narrow)
            {
            {
-             if (mask & (1 << new_base))
+             if (Rd == Rs)
                {
                {
-                 inst.error = _("invalid register list");
-                 return FAIL;
+                 inst.instruction = THUMB_OP16 (inst.instruction);
+                 inst.instruction |= Rd;
+                 inst.instruction |= Rn << 3;
+                 return;
+               }
+             if (Rd == Rn)
+               {
+                 inst.instruction = THUMB_OP16 (inst.instruction);
+                 inst.instruction |= Rd;
+                 inst.instruction |= Rs << 3;
+                 return;
                }
                }
-
-             mask |= 1 << new_base;
-             count++;
            }
            }
+
+         /* If we get here, it can't be done in 16 bits.  */
+         constraint (inst.operands[2].shifted
+                     && inst.operands[2].immisreg,
+                     _("shift must be constant"));
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= Rd << 8;
+         inst.instruction |= Rs << 16;
+         encode_thumb32_shifted_operand (2);
        }
     }
        }
     }
-  while (skip_past_comma (str) != FAIL);
-
-  if (**str != '}')
+  else
     {
     {
-      inst.error = _("invalid register list");
-      return FAIL;
-    }
-
-  (*str)++;
+      /* On its face this is a lie - the instruction does set the
+        flags.  However, the only supported mnemonic in this mode
+        says it doesn't.  */
+      constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
 
 
-  range |= 2 * count;
+      constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+                 _("unshifted register required"));
+      constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
 
 
-  /* Sanity check -- should have raised a parse error above.  */
-  if (count == 0 || count > 16)
-    abort ();
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= Rd;
 
 
-  /* Final test -- the registers must be consecutive.  */
-  while (count--)
-    {
-      if ((mask & (1 << base_reg++)) == 0)
-       {
-         inst.error = _("non-contiguous register range");
-         return FAIL;
-       }
+      if (Rd == Rs)
+       inst.instruction |= Rn << 3;
+      else if (Rd == Rn)
+       inst.instruction |= Rs << 3;
+      else
+       constraint (1, _("dest must overlap one source register"));
     }
     }
+}
 
 
-  inst.instruction = tempinst;
-  return range;
+static void
+do_t_bfc (void)
+{
+  unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+  constraint (msb > 32, _("bit-field extends past end of register"));
+  /* The instruction encoding stores the LSB and MSB,
+     not the LSB and width.  */
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= (inst.operands[1].imm & 0x1c) << 10;
+  inst.instruction |= (inst.operands[1].imm & 0x03) << 6;
+  inst.instruction |= msb - 1;
 }
 
 static void
 }
 
 static void
-vfp_sp_ldstm (str, ldstm_type)
-     char *str;
-     enum vfp_ldstm_type ldstm_type;
+do_t_bfi (void)
 {
 {
-  long range;
+  unsigned int msb;
 
 
-  skip_whitespace (str);
+  /* #0 in second position is alternative syntax for bfc, which is
+     the same instruction but with REG_PC in the Rm field.  */
+  if (!inst.operands[1].isreg)
+    inst.operands[1].reg = REG_PC;
 
 
-  if (reg_required_here (&str, 16) == FAIL)
-    return;
+  msb = inst.operands[2].imm + inst.operands[3].imm;
+  constraint (msb > 32, _("bit-field extends past end of register"));
+  /* The instruction encoding stores the LSB and MSB,
+     not the LSB and width.  */
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+  inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+  inst.instruction |= msb - 1;
+}
 
 
-  skip_whitespace (str);
+static void
+do_t_bfx (void)
+{
+  constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+             _("bit-field extends past end of register"));
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+  inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+  inst.instruction |= inst.operands[3].imm - 1;
+}
 
 
-  if (*str == '!')
-    {
-      inst.instruction |= WRITE_BACK;
-      str++;
-    }
-  else if (ldstm_type != VFP_LDSTMIA)
-    {
-      inst.error = _("this addressing mode requires base-register writeback");
-      return;
-    }
+/* ARM V5 Thumb BLX (argument parse)
+       BLX <target_addr>       which is BLX(1)
+       BLX <Rm>                which is BLX(2)
+   Unfortunately, there are two different opcodes for this mnemonic.
+   So, the insns[].value is not used, and the code here zaps values
+       into inst.instruction.
+
+   ??? How to take advantage of the additional two bits of displacement
+   available in Thumb32 mode?  Need new relocation?  */
 
 
-  if (skip_past_comma (&str) == FAIL
-      || (range = vfp_sp_reg_list (&str, VFP_REG_Sd)) == FAIL)
+static void
+do_t_blx (void)
+{
+  if (inst.operands[0].isreg)
+    /* We have a register, so this is BLX(2).  */
+    inst.instruction |= inst.operands[0].reg << 3;
+  else
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* No register.  This must be BLX(1).  */
+      inst.instruction = 0xf000e800;
+      inst.reloc.type  = BFD_RELOC_THUMB_PCREL_BLX;
+      inst.reloc.pc_rel = 1;
     }
     }
-
-  inst.instruction |= range;
-  end_of_line (str);
 }
 
 static void
 }
 
 static void
-vfp_dp_ldstm (str, ldstm_type)
-     char *str;
-     enum vfp_ldstm_type ldstm_type;
+do_t_branch (void)
 {
 {
-  long range;
-
-  skip_whitespace (str);
-
-  if (reg_required_here (&str, 16) == FAIL)
-    return;
-
-  skip_whitespace (str);
-
-  if (*str == '!')
+  if (unified_syntax && inst.size_req != 2)
     {
     {
-      inst.instruction |= WRITE_BACK;
-      str++;
+      if (inst.cond == COND_ALWAYS)
+       {
+         inst.instruction = 0xf000b000;
+         inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
+       }
+      else
+       {
+         assert (inst.cond != 0xF);
+         inst.instruction = (inst.cond << 22) | 0xf0008000;
+         inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
+       }
     }
     }
-  else if (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX)
+  else
     {
     {
-      inst.error = _("this addressing mode requires base-register writeback");
-      return;
+      if (inst.cond == COND_ALWAYS)
+       inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
+      else
+       {
+         inst.instruction = 0xd000 | (inst.cond << 8);
+         inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
+       }
     }
 
     }
 
-  if (skip_past_comma (&str) == FAIL
-      || (range = vfp_dp_reg_list (&str)) == FAIL)
+  inst.reloc.pc_rel = 1;
+}
+
+static void
+do_t_bkpt (void)
+{
+  if (inst.operands[0].present)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      constraint (inst.operands[0].imm > 255,
+                 _("immediate value out of range"));
+      inst.instruction |= inst.operands[0].imm;
     }
     }
+}
 
 
-  if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
-    range += 1;
+static void
+do_t_branch23 (void)
+{
+  inst.reloc.type   = BFD_RELOC_THUMB_PCREL_BRANCH23;
+  inst.reloc.pc_rel = 1;
 
 
-  inst.instruction |= range;
-  end_of_line (str);
+  /* If the destination of the branch is a defined symbol which does not have
+     the THUMB_FUNC attribute, then we must be calling a function which has
+     the (interfacearm) attribute.  We look for the Thumb entry point to that
+     function and change the branch to refer to that function instead. */
+  if (  inst.reloc.exp.X_op == O_symbol
+      && inst.reloc.exp.X_add_symbol != NULL
+      && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
+      && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
+    inst.reloc.exp.X_add_symbol =
+      find_real_start (inst.reloc.exp.X_add_symbol);
+}
+
+static void
+do_t_bx (void)
+{
+  inst.instruction |= inst.operands[0].reg << 3;
+  /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC.         The reloc
+     should cause the alignment to be checked once it is known.         This is
+     because BX PC only works if the instruction is word aligned.  */
 }
 
 static void
 }
 
 static void
-do_vfp_sp_ldstmia (str)
-     char *str;
+do_t_bxj (void)
 {
 {
-  vfp_sp_ldstm (str, VFP_LDSTMIA);
+  if (inst.operands[0].reg == REG_PC)
+    as_tsktsk (_("use of r15 in bxj is not really useful"));
+
+  inst.instruction |= inst.operands[0].reg << 16;
 }
 
 static void
 }
 
 static void
-do_vfp_sp_ldstmdb (str)
-     char *str;
+do_t_clz (void)
 {
 {
-  vfp_sp_ldstm (str, VFP_LDSTMDB);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[1].reg;
 }
 
 static void
 }
 
 static void
-do_vfp_dp_ldstmia (str)
-     char *str;
+do_t_cpsi (void)
 {
 {
-  vfp_dp_ldstm (str, VFP_LDSTMIA);
+  if (unified_syntax
+      && (inst.operands[1].present || inst.size_req == 4))
+    {
+      unsigned int imod = (inst.instruction & 0x0030) >> 4;
+      inst.instruction = 0xf3af8000;
+      inst.instruction |= imod << 9;
+      inst.instruction |= inst.operands[0].imm << 5;
+      if (inst.operands[1].present)
+       inst.instruction |= 0x100 | inst.operands[1].imm;
+    }
+  else
+    {
+      constraint (inst.operands[1].present,
+                 _("Thumb does not support the 2-argument "
+                   "form of this instruction"));
+      inst.instruction |= inst.operands[0].imm;
+    }
 }
 
 }
 
+/* THUMB CPY instruction (argument parse).  */
+
 static void
 static void
-do_vfp_dp_ldstmdb (str)
-     char *str;
+do_t_cpy (void)
 {
 {
-  vfp_dp_ldstm (str, VFP_LDSTMDB);
+  if (inst.size_req == 4)
+    {
+      inst.instruction = THUMB_OP32 (T_MNEM_mov);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.instruction |= inst.operands[1].reg;
+    }
+  else
+    {
+      inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+      inst.instruction |= (inst.operands[0].reg & 0x7);
+      inst.instruction |= inst.operands[1].reg << 3;
+    }
 }
 
 static void
 }
 
 static void
-do_vfp_xp_ldstmia (str)
-     char *str;
+do_t_czb (void)
 {
 {
-  vfp_dp_ldstm (str, VFP_LDSTMIAX);
+  constraint (inst.operands[0].reg > 7, BAD_HIREG);
+  inst.instruction |= inst.operands[0].reg;
+  inst.reloc.pc_rel = 1;
+  inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH7;
 }
 
 static void
 }
 
 static void
-do_vfp_xp_ldstmdb (str)
-     char *str;
+do_t_hint (void)
 {
 {
-  vfp_dp_ldstm (str, VFP_LDSTMDBX);
+  if (unified_syntax && inst.size_req == 4)
+    inst.instruction = THUMB_OP32 (inst.instruction);
+  else
+    inst.instruction = THUMB_OP16 (inst.instruction);
 }
 
 static void
 }
 
 static void
-do_vfp_sp_compare_z (str)
-     char *str;
+do_t_it (void)
 {
 {
-  skip_whitespace (str);
+  unsigned int cond = inst.operands[0].imm;
+
+  current_it_mask = (inst.instruction & 0xf) | 0x10;
+  current_cc = cond;
 
 
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
+  /* If the condition is a negative condition, invert the mask.  */
+  if ((cond & 0x1) == 0x0)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      unsigned int mask = inst.instruction & 0x000f;
+
+      if ((mask & 0x7) == 0)
+       /* no conversion needed */;
+      else if ((mask & 0x3) == 0)
+       mask ^= 0x8;
+      else if ((mask & 0x1) == 0)
+       mask ^= 0xC;
+      else
+       mask ^= 0xE;
+
+      inst.instruction &= 0xfff0;
+      inst.instruction |= mask;
     }
 
     }
 
-  end_of_line (str);
-  return;
+  inst.instruction |= cond << 4;
 }
 
 static void
 }
 
 static void
-do_vfp_dp_compare_z (str)
-     char *str;
+do_t_ldmstm (void)
 {
 {
-  skip_whitespace (str);
+  /* This really doesn't seem worth it.  */
+  constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+             _("expression too complex"));
+  constraint (inst.operands[1].writeback,
+             _("Thumb load/store multiple does not support {reglist}^"));
 
 
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
+  if (unified_syntax)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      /* See if we can use a 16-bit instruction.  */
+      if (inst.instruction < 0xffff /* not ldmdb/stmdb */
+         && inst.size_req != 4
+         && inst.operands[0].reg <= 7
+         && !(inst.operands[1].imm & ~0xff)
+         && (inst.instruction == T_MNEM_stmia
+             ? inst.operands[0].writeback
+             : (inst.operands[0].writeback
+                == !(inst.operands[1].imm & (1 << inst.operands[0].reg)))))
+       {
+         if (inst.instruction == T_MNEM_stmia
+             && (inst.operands[1].imm & (1 << inst.operands[0].reg))
+             && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+           as_warn (_("value stored for r%d is UNPREDICTABLE"),
+                    inst.operands[0].reg);
+
+         inst.instruction = THUMB_OP16 (inst.instruction);
+         inst.instruction |= inst.operands[0].reg << 8;
+         inst.instruction |= inst.operands[1].imm;
+       }
+      else
+       {
+         if (inst.operands[1].imm & (1 << 13))
+           as_warn (_("SP should not be in register list"));
+         if (inst.instruction == T_MNEM_stmia)
+           {
+             if (inst.operands[1].imm & (1 << 15))
+               as_warn (_("PC should not be in register list"));
+             if (inst.operands[1].imm & (1 << inst.operands[0].reg))
+               as_warn (_("value stored for r%d is UNPREDICTABLE"),
+                        inst.operands[0].reg);
+           }
+         else
+           {
+             if (inst.operands[1].imm & (1 << 14)
+                 && inst.operands[1].imm & (1 << 15))
+               as_warn (_("LR and PC should not both be in register list"));
+             if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+                 && inst.operands[0].writeback)
+               as_warn (_("base register should not be in register list "
+                          "when written back"));
+           }
+         if (inst.instruction < 0xffff)
+           inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= inst.operands[0].reg << 16;
+         inst.instruction |= inst.operands[1].imm;
+         if (inst.operands[0].writeback)
+           inst.instruction |= WRITE_BACK;
+       }
     }
     }
+  else
+    {
+      constraint (inst.operands[0].reg > 7
+                 || (inst.operands[1].imm & ~0xff), BAD_HIREG);
+      if (inst.instruction == T_MNEM_stmia)
+       {
+         if (!inst.operands[0].writeback)
+           as_warn (_("this instruction will write back the base register"));
+         if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+             && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+           as_warn (_("value stored for r%d is UNPREDICTABLE"),
+                    inst.operands[0].reg);
+       }
+      else
+       {
+         if (!inst.operands[0].writeback
+             && !(inst.operands[1].imm & (1 << inst.operands[0].reg)))
+           as_warn (_("this instruction will write back the base register"));
+         else if (inst.operands[0].writeback
+                  && (inst.operands[1].imm & (1 << inst.operands[0].reg)))
+           as_warn (_("this instruction will not write back the base register"));
+       }
 
 
-  end_of_line (str);
-  return;
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.instruction |= inst.operands[1].imm;
+    }
 }
 
 static void
 }
 
 static void
-do_vfp_dp_sp_cvt (str)
-     char *str;
+do_t_ldrex (void)
 {
 {
-  skip_whitespace (str);
+  constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+             || inst.operands[1].postind || inst.operands[1].writeback
+             || inst.operands[1].immisreg || inst.operands[1].shifted
+             || inst.operands[1].negative,
+             _("instruction does not accept this addressing mode"));
 
 
-  if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
-    return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
 
 
-  if (skip_past_comma (&str) == FAIL
-      || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
+static void
+do_t_ldrexd (void)
+{
+  if (!inst.operands[1].present)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      constraint (inst.operands[0].reg == REG_LR,
+                 _("r14 not allowed as first register "
+                   "when second register is omitted"));
+      inst.operands[1].reg = inst.operands[0].reg + 1;
     }
     }
+  constraint (inst.operands[0].reg == inst.operands[1].reg,
+             BAD_OVERLAP);
 
 
-  end_of_line (str);
-  return;
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 8;
+  inst.instruction |= inst.operands[2].reg << 16;
 }
 
 static void
 }
 
 static void
-do_vfp_sp_dp_cvt (str)
-     char *str;
+do_t_ldst (void)
 {
 {
-  skip_whitespace (str);
-
-  if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
-    return;
-
-  if (skip_past_comma (&str) == FAIL
-      || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
+  if (unified_syntax)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
+      /* Generation of 16-bit instructions for anything other than
+        Rd, [Rn, Ri] is deferred to section relaxation time.  */
+      if (inst.operands[1].isreg && inst.operands[1].immisreg
+         && !inst.operands[1].shifted && !inst.operands[1].postind
+         && !inst.operands[1].negative && inst.operands[0].reg <= 7
+         && inst.operands[1].reg <= 7 && inst.operands[1].imm <= 7
+         && inst.instruction <= 0xffff)
+       {
+         inst.instruction = THUMB_OP16 (inst.instruction);
+         goto op16;
+       }
+
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 12;
+      encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
       return;
     }
 
       return;
     }
 
-  end_of_line (str);
-  return;
-}
+  constraint (inst.operands[0].reg > 7, BAD_HIREG);
 
 
-/* Thumb specific routines.  */
+  if (inst.instruction == T_MNEM_ldrsh || inst.instruction == T_MNEM_ldrsb)
+    {
+      /* Only [Rn,Rm] is acceptable.  */
+      constraint (inst.operands[1].reg > 7 || inst.operands[1].imm > 7, BAD_HIREG);
+      constraint (!inst.operands[1].isreg || !inst.operands[1].immisreg
+                 || inst.operands[1].postind || inst.operands[1].shifted
+                 || inst.operands[1].negative,
+                 _("Thumb does not support this addressing mode"));
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      goto op16;
+    }
+     
+  inst.instruction = THUMB_OP16 (inst.instruction);
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
+      return;
 
 
-/* Parse and validate that a register is of the right form, this saves
-   repeated checking of this information in many similar cases.
-   Unlike the 32-bit case we do not insert the register into the opcode
-   here, since the position is often unknown until the full instruction
-   has been parsed.  */
+  constraint (!inst.operands[1].preind
+             || inst.operands[1].shifted
+             || inst.operands[1].writeback,
+             _("Thumb does not support this addressing mode"));
+  if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
+    {
+      constraint (inst.instruction & 0x0600,
+                 _("byte or halfword not valid for base register"));
+      constraint (inst.operands[1].reg == REG_PC
+                 && !(inst.instruction & THUMB_LOAD_BIT),
+                 _("r15 based store not allowed"));
+      constraint (inst.operands[1].immisreg,
+                 _("invalid base register for register offset"));
 
 
-static int
-thumb_reg (strp, hi_lo)
-     char ** strp;
-     int     hi_lo;
-{
-  int reg;
+      if (inst.operands[1].reg == REG_PC)
+       inst.instruction = T_OPCODE_LDR_PC;
+      else if (inst.instruction & THUMB_LOAD_BIT)
+       inst.instruction = T_OPCODE_LDR_SP;
+      else
+       inst.instruction = T_OPCODE_STR_SP;
 
 
-  if ((reg = reg_required_here (strp, -1)) == FAIL)
-    return FAIL;
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+      return;
+    }
 
 
-  switch (hi_lo)
+  constraint (inst.operands[1].reg > 7, BAD_HIREG);
+  if (!inst.operands[1].immisreg)
     {
     {
-    case THUMB_REG_LO:
-      if (reg > 7)
-       {
-         inst.error = _("lo register required");
-         return FAIL;
-       }
-      break;
+      /* Immediate offset.  */
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+      return;
+    }
 
 
-    case THUMB_REG_HI:
-      if (reg < 8)
-       {
-         inst.error = _("hi register required");
-         return FAIL;
-       }
-      break;
+  /* Register offset.  */
+  constraint (inst.operands[1].imm > 7, BAD_HIREG);
+  constraint (inst.operands[1].negative,
+             _("Thumb does not support this addressing mode"));
 
 
-    default:
-      break;
+ op16:
+  switch (inst.instruction)
+    {
+    case T_OPCODE_STR_IW: inst.instruction = T_OPCODE_STR_RW; break;
+    case T_OPCODE_STR_IH: inst.instruction = T_OPCODE_STR_RH; break;
+    case T_OPCODE_STR_IB: inst.instruction = T_OPCODE_STR_RB; break;
+    case T_OPCODE_LDR_IW: inst.instruction = T_OPCODE_LDR_RW; break;
+    case T_OPCODE_LDR_IH: inst.instruction = T_OPCODE_LDR_RH; break;
+    case T_OPCODE_LDR_IB: inst.instruction = T_OPCODE_LDR_RB; break;
+    case 0x5600 /* ldrsb */:
+    case 0x5e00 /* ldrsh */: break;
+    default: abort ();
     }
 
     }
 
-  return reg;
+  inst.instruction |= inst.operands[0].reg;
+  inst.instruction |= inst.operands[1].reg << 3;
+  inst.instruction |= inst.operands[1].imm << 6;
 }
 
 }
 
-/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
-   was SUB.  */
+static void
+do_t_ldstd (void)
+{
+  if (!inst.operands[1].present)
+    {
+      inst.operands[1].reg = inst.operands[0].reg + 1;
+      constraint (inst.operands[0].reg == REG_LR,
+                 _("r14 not allowed here"));
+    }
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 8;
+  encode_thumb32_addr_mode (2, /*is_t=*/FALSE, /*is_d=*/TRUE);
+                           
+}
 
 static void
 
 static void
-thumb_add_sub (str, subtract)
-     char * str;
-     int    subtract;
+do_t_ldstt (void)
 {
 {
-  int Rd, Rs, Rn = FAIL;
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_thumb32_addr_mode (1, /*is_t=*/TRUE, /*is_d=*/FALSE);
+}
 
 
-  skip_whitespace (str);
+static void
+do_t_mla (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].reg << 12;
+}
 
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
-      || skip_past_comma (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_t_mlal (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 8;
+  inst.instruction |= inst.operands[2].reg << 16;
+  inst.instruction |= inst.operands[3].reg;
+}
 
 
-  if (is_immediate_prefix (*str))
-    {
-      Rs = Rd;
-      str++;
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
-    }
-  else
+static void
+do_t_mov_cmp (void)
+{
+  if (unified_syntax)
     {
     {
-      if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-       return;
-
-      if (skip_past_comma (&str) == FAIL)
+      int r0off = (inst.instruction == T_MNEM_mov
+                  || inst.instruction == T_MNEM_movs) ? 8 : 16;
+      if (!inst.operands[1].isreg)
        {
        {
-         /* Two operand format, shuffle the registers
-            and pretend there are 3.  */
-         Rn = Rs;
-         Rs = Rd;
+         /* For an immediate, we always generate a 32-bit opcode;
+            section relaxation will shrink it later if possible.  */
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+         inst.instruction |= inst.operands[0].reg << r0off;
+         inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
        }
        }
-      else if (is_immediate_prefix (*str))
+      else if (inst.size_req == 4
+              || inst.operands[1].shifted
+              || (inst.instruction == T_MNEM_movs
+                  && (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)))
        {
        {
-         str++;
-         if (my_get_expression (&inst.reloc.exp, &str))
-           return;
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= inst.operands[0].reg << r0off;
+         encode_thumb32_shifted_operand (1);
        }
        }
-      else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-       return;
+      else
+       switch (inst.instruction)
+         {
+         case T_MNEM_mov:
+           inst.instruction = T_OPCODE_MOV_HR;
+           inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+           inst.instruction |= (inst.operands[0].reg & 0x7);
+           inst.instruction |= inst.operands[1].reg << 3;
+           break;
+
+         case T_MNEM_movs:
+           /* We know we have low registers at this point.
+              Generate ADD Rd, Rs, #0.  */
+           inst.instruction = T_OPCODE_ADD_I3;
+           inst.instruction |= inst.operands[0].reg;
+           inst.instruction |= inst.operands[1].reg << 3;
+           break;
+
+         case T_MNEM_cmp:
+           if (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7)
+             {
+               inst.instruction = T_OPCODE_CMP_LR;
+               inst.instruction |= inst.operands[0].reg;
+               inst.instruction |= inst.operands[1].reg << 3;
+             }
+           else
+             {
+               inst.instruction = T_OPCODE_CMP_HR;
+               inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+               inst.instruction |= (inst.operands[0].reg & 0x7);
+               inst.instruction |= inst.operands[1].reg << 3;
+             }
+           break;
+         }
+      return;
     }
 
     }
 
-  /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
-     for the latter case, EXPR contains the immediate that was found.  */
-  if (Rn != FAIL)
+  inst.instruction = THUMB_OP16 (inst.instruction);
+  if (inst.operands[1].isreg)
     {
     {
-      /* All register format.  */
-      if (Rd > 7 || Rs > 7 || Rn > 7)
+      if (inst.operands[0].reg < 8 && inst.operands[1].reg < 8)
        {
        {
-         if (Rs != Rd)
-           {
-             inst.error = _("dest and source1 must be the same register");
-             return;
-           }
-
-         /* Can't do this for SUB.  */
-         if (subtract)
-           {
-             inst.error = _("subtract valid only on lo regs");
-             return;
-           }
+         /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+            since a MOV instruction produces unpredictable results.  */
+         if (inst.instruction == T_OPCODE_MOV_I8)
+           inst.instruction = T_OPCODE_ADD_I3;
+         else
+           inst.instruction = T_OPCODE_CMP_LR;
 
 
-         inst.instruction = (T_OPCODE_ADD_HI
-                             | (Rd > 7 ? THUMB_H1 : 0)
-                             | (Rn > 7 ? THUMB_H2 : 0));
-         inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
+         inst.instruction |= inst.operands[0].reg;
+         inst.instruction |= inst.operands[1].reg << 3;
        }
       else
        {
        }
       else
        {
-         inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
-         inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+         if (inst.instruction == T_OPCODE_MOV_I8)
+           inst.instruction = T_OPCODE_MOV_HR;
+         else
+           inst.instruction = T_OPCODE_CMP_HR;
+         do_t_cpy ();
        }
     }
   else
     {
        }
     }
   else
     {
-      /* Immediate expression, now things start to get nasty.  */
+      constraint (inst.operands[0].reg > 7,
+                 _("only lo regs allowed with immediate"));
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+    }
+}
 
 
-      /* First deal with HI regs, only very restricted cases allowed:
-        Adjusting SP, and using PC or SP to get an address.  */
-      if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
-         || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
-       {
-         inst.error = _("invalid Hi register with immediate");
-         return;
-       }
+static void
+do_t_mov16 (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= (inst.operands[1].imm & 0xf000) << 4;
+  inst.instruction |= (inst.operands[1].imm & 0x0800) << 15;
+  inst.instruction |= (inst.operands[1].imm & 0x0700) << 4;
+  inst.instruction |= (inst.operands[1].imm & 0x00ff);
+}
 
 
-      if (inst.reloc.exp.X_op != O_constant)
+static void
+do_t_mvn_tst (void)
+{
+  if (unified_syntax)
+    {
+      int r0off = (inst.instruction == T_MNEM_mvn
+                  || inst.instruction == T_MNEM_mvns) ? 8 : 16;
+      if (!inst.operands[1].isreg)
        {
        {
-         /* Value isn't known yet, all we can do is store all the fragments
-            we know about in the instruction and let the reloc hacking
-            work it all out.  */
-         inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
-         inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+         /* For an immediate, we always generate a 32-bit opcode;
+            section relaxation will shrink it later if possible.  */
+         if (inst.instruction < 0xffff)
+           inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+         inst.instruction |= inst.operands[0].reg << r0off;
+         inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
        }
       else
        {
        }
       else
        {
-         int offset = inst.reloc.exp.X_add_number;
-
-         if (subtract)
-           offset = - offset;
-
-         if (offset < 0)
-           {
-             offset = - offset;
-             subtract = 1;
-
-             /* Quick check, in case offset is MIN_INT.  */
-             if (offset < 0)
-               {
-                 inst.error = _("immediate value out of range");
-                 return;
-               }
-           }
-         /* Note - you cannot convert a subtract of 0 into an
-            add of 0 because the carry flag is set differently.  */
-         else if (offset > 0)
-           subtract = 0;
-
-         if (Rd == REG_SP)
-           {
-             if (offset & ~0x1fc)
-               {
-                 inst.error = _("invalid immediate value for stack adjust");
-                 return;
-               }
-             inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
-             inst.instruction |= offset >> 2;
-           }
-         else if (Rs == REG_PC || Rs == REG_SP)
+         /* See if we can do this with a 16-bit instruction.  */
+         if (inst.instruction < 0xffff
+             && THUMB_SETS_FLAGS (inst.instruction)
+             && !inst.operands[1].shifted
+             && inst.operands[0].reg <= 7
+             && inst.operands[1].reg <= 7
+             && inst.size_req != 4)
            {
            {
-             if (subtract
-                 || (offset & ~0x3fc))
-               {
-                 inst.error = _("invalid immediate for address calculation");
-                 return;
-               }
-             inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
-                                 : T_OPCODE_ADD_SP);
-             inst.instruction |= (Rd << 8) | (offset >> 2);
-           }
-         else if (Rs == Rd)
-           {
-             if (offset & ~0xff)
-               {
-                 inst.error = _("immediate value out of range");
-                 return;
-               }
-             inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
-             inst.instruction |= (Rd << 8) | offset;
+             inst.instruction = THUMB_OP16 (inst.instruction);
+             inst.instruction |= inst.operands[0].reg;
+             inst.instruction |= inst.operands[1].reg << 3;
            }
          else
            {
            }
          else
            {
-             if (offset & ~0x7)
-               {
-                 inst.error = _("immediate value out of range");
-                 return;
-               }
-             inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
-             inst.instruction |= Rd | (Rs << 3) | (offset << 6);
+             constraint (inst.operands[1].shifted
+                         && inst.operands[1].immisreg,
+                         _("shift must be constant"));
+             if (inst.instruction < 0xffff)
+               inst.instruction = THUMB_OP32 (inst.instruction);
+             inst.instruction |= inst.operands[0].reg << r0off;
+             encode_thumb32_shifted_operand (1);
            }
        }
     }
            }
        }
     }
+  else
+    {
+      constraint (inst.instruction > 0xffff
+                 || inst.instruction == T_MNEM_mvns, BAD_THUMB32);
+      constraint (!inst.operands[1].isreg || inst.operands[1].shifted,
+                 _("unshifted register required"));
+      constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+                 BAD_HIREG);
 
 
-  end_of_line (str);
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+    }
 }
 
 static void
 }
 
 static void
-thumb_shift (str, shift)
-     char * str;
-     int    shift;
+do_t_mrs (void)
 {
 {
-  int Rd, Rs, Rn = FAIL;
+  /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all.  */
+  constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+             != (PSR_c|PSR_f),
+             _("'CPSR' or 'SPSR' expected"));
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= (inst.operands[1].imm & SPSR_BIT) >> 2;
+}
 
 
-  skip_whitespace (str);
+static void
+do_t_msr (void)
+{
+  constraint (!inst.operands[1].isreg,
+             _("Thumb encoding does not support an immediate here"));
+  inst.instruction |= (inst.operands[0].imm & SPSR_BIT) >> 2;
+  inst.instruction |= (inst.operands[0].imm & ~SPSR_BIT) >> 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+}
 
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+static void
+do_t_mul (void)
+{
+  if (!inst.operands[2].present)
+    inst.operands[2].reg = inst.operands[0].reg;
 
 
-  if (is_immediate_prefix (*str))
+  /* There is no 32-bit MULS and no 16-bit MUL. */
+  if (unified_syntax && inst.instruction == T_MNEM_mul)
     {
     {
-      /* Two operand immediate format, set Rs to Rd.  */
-      Rs = Rd;
-      str ++;
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.instruction |= inst.operands[1].reg << 16;
+      inst.instruction |= inst.operands[2].reg << 0;
     }
   else
     {
     }
   else
     {
-      if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-       return;
+      constraint (!unified_syntax
+                 && inst.instruction == T_MNEM_muls, BAD_THUMB32);
+      constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+                 BAD_HIREG);
 
 
-      if (skip_past_comma (&str) == FAIL)
-       {
-         /* Two operand format, shuffle the registers
-            and pretend there are 3.  */
-         Rn = Rs;
-         Rs = Rd;
-       }
-      else if (is_immediate_prefix (*str))
-       {
-         str++;
-         if (my_get_expression (&inst.reloc.exp, &str))
-           return;
-       }
-      else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-       return;
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg;
+
+      if (inst.operands[0].reg == inst.operands[1].reg)
+       inst.instruction |= inst.operands[2].reg << 3;
+      else if (inst.operands[0].reg == inst.operands[2].reg)
+       inst.instruction |= inst.operands[1].reg << 3;
+      else
+       constraint (1, _("dest must overlap one source register"));
     }
     }
+}
+
+static void
+do_t_mull (void)
+{
+  inst.instruction |= inst.operands[0].reg << 12;
+  inst.instruction |= inst.operands[1].reg << 8;
+  inst.instruction |= inst.operands[2].reg << 16;
+  inst.instruction |= inst.operands[3].reg;
 
 
-  /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
-     for the latter case, EXPR contains the immediate that was found.  */
+  if (inst.operands[0].reg == inst.operands[1].reg)
+    as_tsktsk (_("rdhi and rdlo must be different"));
+}
 
 
-  if (Rn != FAIL)
+static void
+do_t_nop (void)
+{
+  if (unified_syntax)
     {
     {
-      if (Rs != Rd)
+      if (inst.size_req == 4 || inst.operands[0].imm > 15)
        {
        {
-         inst.error = _("source1 and dest must be same register");
-         return;
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= inst.operands[0].imm;
        }
        }
-
-      switch (shift)
+      else
        {
        {
-       case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
-       case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
-       case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
+         inst.instruction = THUMB_OP16 (inst.instruction);
+         inst.instruction |= inst.operands[0].imm << 4;
        }
        }
-
-      inst.instruction |= Rd | (Rn << 3);
     }
   else
     {
     }
   else
     {
-      switch (shift)
-       {
-       case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
-       case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
-       case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
-       }
-
-      if (inst.reloc.exp.X_op != O_constant)
-       {
-         /* Value isn't known yet, create a dummy reloc and let reloc
-            hacking fix it up.  */
-         inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
-       }
-      else
-       {
-         unsigned shift_value = inst.reloc.exp.X_add_number;
-
-         if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
-           {
-             inst.error = _("invalid immediate for shift");
-             return;
-           }
-
-         /* Shifts of zero are handled by converting to LSL.  */
-         if (shift_value == 0)
-           inst.instruction = T_OPCODE_LSL_I;
-
-         /* Shifts of 32 are encoded as a shift of zero.  */
-         if (shift_value == 32)
-           shift_value = 0;
-
-         inst.instruction |= shift_value << 6;
-       }
-
-      inst.instruction |= Rd | (Rs << 3);
+      constraint (inst.operands[0].present,
+                 _("Thumb does not support NOP with hints"));
+      inst.instruction = 0x46c0;
     }
     }
-
-  end_of_line (str);
 }
 
 static void
 }
 
 static void
-thumb_mov_compare (str, move)
-     char * str;
-     int    move;
+do_t_neg (void)
 {
 {
-  int Rd, Rs = FAIL;
-
-  skip_whitespace (str);
-
-  if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
-      || skip_past_comma (&str) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
-
-  if (is_immediate_prefix (*str))
-    {
-      str++;
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
-    }
-  else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-    return;
-
-  if (Rs != FAIL)
+  if (unified_syntax)
     {
     {
-      if (Rs < 8 && Rd < 8)
+      if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7
+         || !THUMB_SETS_FLAGS (inst.instruction)
+         || inst.size_req == 4)
        {
        {
-         if (move == THUMB_MOVE)
-           /* A move of two lowregs is encoded as ADD Rd, Rs, #0
-              since a MOV instruction produces unpredictable results.  */
-           inst.instruction = T_OPCODE_ADD_I3;
-         else
-           inst.instruction = T_OPCODE_CMP_LR;
-         inst.instruction |= Rd | (Rs << 3);
+         inst.instruction = THUMB_OP32 (inst.instruction);
+         inst.instruction |= inst.operands[0].reg << 8;
+         inst.instruction |= inst.operands[1].reg << 16;
        }
       else
        {
        }
       else
        {
-         if (move == THUMB_MOVE)
-           inst.instruction = T_OPCODE_MOV_HR;
-         else
-           inst.instruction = T_OPCODE_CMP_HR;
-
-         if (Rd > 7)
-           inst.instruction |= THUMB_H1;
-
-         if (Rs > 7)
-           inst.instruction |= THUMB_H2;
-
-         inst.instruction |= (Rd & 7) | ((Rs & 7) << 3);
+         inst.instruction = THUMB_OP16 (inst.instruction);
+         inst.instruction |= inst.operands[0].reg;
+         inst.instruction |= inst.operands[1].reg << 3;
        }
     }
   else
     {
        }
     }
   else
     {
-      if (Rd > 7)
-       {
-         inst.error = _("only lo regs allowed with immediate");
-         return;
-       }
-
-      if (move == THUMB_MOVE)
-       inst.instruction = T_OPCODE_MOV_I8;
-      else
-       inst.instruction = T_OPCODE_CMP_I8;
-
-      inst.instruction |= Rd << 8;
+      constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+                 BAD_HIREG);
+      constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
 
 
-      if (inst.reloc.exp.X_op != O_constant)
-       inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
-      else
-       {
-         unsigned value = inst.reloc.exp.X_add_number;
-
-         if (value > 255)
-           {
-             inst.error = _("invalid immediate");
-             return;
-           }
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+    }
+}
 
 
-         inst.instruction |= value;
-       }
+static void
+do_t_pkhbt (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  if (inst.operands[3].present)
+    {
+      unsigned int val = inst.reloc.exp.X_add_number;
+      constraint (inst.reloc.exp.X_op != O_constant,
+                 _("expression too complex"));
+      inst.instruction |= (val & 0x1c) << 10;
+      inst.instruction |= (val & 0x03) << 6;
     }
     }
+}
 
 
-  end_of_line (str);
+static void
+do_t_pkhtb (void)
+{
+  if (!inst.operands[3].present)
+    inst.instruction &= ~0x00000020;
+  do_t_pkhbt ();
 }
 
 static void
 }
 
 static void
-thumb_load_store (str, load_store, size)
-     char * str;
-     int    load_store;
-     int    size;
+do_t_pld (void)
 {
 {
-  int Rd, Rb, Ro = FAIL;
+  encode_thumb32_addr_mode (0, /*is_t=*/FALSE, /*is_d=*/FALSE);
+}
 
 
-  skip_whitespace (str);
+static void
+do_t_push_pop (void)
+{
+  unsigned mask;
+  
+  constraint (inst.operands[0].writeback,
+             _("push/pop do not support {reglist}^"));
+  constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+             _("expression too complex"));
 
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL)
+  mask = inst.operands[0].imm;
+  if ((mask & ~0xff) == 0)
+    inst.instruction = THUMB_OP16 (inst.instruction);
+  else if ((inst.instruction == T_MNEM_push
+           && (mask & ~0xff) == 1 << REG_LR)
+          || (inst.instruction == T_MNEM_pop
+              && (mask & ~0xff) == 1 << REG_PC))
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= THUMB_PP_PC_LR;
+      mask &= 0xff;
     }
     }
-
-  if (*str == '[')
+  else if (unified_syntax)
     {
     {
-      str++;
-      if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-       return;
-
-      if (skip_past_comma (&str) != FAIL)
+      if (mask & (1 << 13))
+       inst.error =  _("SP not allowed in register list");
+      if (inst.instruction == T_MNEM_push)
        {
        {
-         if (is_immediate_prefix (*str))
-           {
-             str++;
-             if (my_get_expression (&inst.reloc.exp, &str))
-               return;
-           }
-         else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-           return;
+         if (mask & (1 << 15))
+           inst.error = _("PC not allowed in register list");
        }
       else
        {
        }
       else
        {
-         inst.reloc.exp.X_op = O_constant;
-         inst.reloc.exp.X_add_number = 0;
+         if (mask & (1 << 14)
+             && mask & (1 << 15))
+           inst.error = _("LR and PC should not both be in register list");
        }
        }
-
-      if (*str != ']')
+      if ((mask & (mask - 1)) == 0)
        {
        {
-         inst.error = _("expected ']'");
-         return;
+         /* Single register push/pop implemented as str/ldr.  */
+         if (inst.instruction == T_MNEM_push)
+           inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */
+         else
+           inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */
+         mask = ffs(mask) - 1;
+         mask <<= 12;
        }
        }
-      str++;
+      else
+       inst.instruction = THUMB_OP32 (inst.instruction);
     }
     }
-  else if (*str == '=')
+  else
     {
     {
-      if (load_store != THUMB_LOAD)
-       {
-         inst.error = _("invalid pseudo operation");
-         return;
-       }
-
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
-
-      skip_whitespace (str);
-
-      if (my_get_expression (& inst.reloc.exp, & str))
-       return;
-
-      end_of_line (str);
+      inst.error = _("invalid register list to push/pop instruction");
+      return;
+    }
 
 
-      if (   inst.reloc.exp.X_op != O_constant
-         && inst.reloc.exp.X_op != O_symbol)
-       {
-         inst.error = "Constant expression expected";
-         return;
-       }
+  inst.instruction |= mask;
+}
 
 
-      if (inst.reloc.exp.X_op == O_constant
-         && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
-       {
-         /* This can be done with a mov instruction.  */
+static void
+do_t_rbit (void)
+{
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+}
 
 
-         inst.instruction  = T_OPCODE_MOV_I8 | (Rd << 8);
-         inst.instruction |= inst.reloc.exp.X_add_number;
-         return;
-       }
+static void
+do_t_rev (void)
+{
+  if (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7
+      && inst.size_req != 4)
+    {
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+    }
+  else if (unified_syntax)
+    {
+      inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.instruction |= inst.operands[1].reg << 16;
+      inst.instruction |= inst.operands[1].reg;
+    }
+  else
+    inst.error = BAD_HIREG;
+}
 
 
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
-       {
-         if (!inst.error)
-           inst.error = "literal pool insertion failed";
-         return;
-       }
+static void
+do_t_rsb (void)
+{
+  int Rd, Rs;
 
 
-      inst.reloc.type   = BFD_RELOC_ARM_THUMB_OFFSET;
-      inst.reloc.pc_rel = 1;
-      inst.instruction  = T_OPCODE_LDR_PC | (Rd << 8);
-      /* Adjust ARM pipeline offset to Thumb.  */
-      inst.reloc.exp.X_add_number += 4;
+  Rd = inst.operands[0].reg;
+  Rs = (inst.operands[1].present
+       ? inst.operands[1].reg    /* Rd, Rs, foo */
+       : inst.operands[0].reg);  /* Rd, foo -> Rd, Rd, foo */
 
 
-      return;
+  inst.instruction |= Rd << 8;
+  inst.instruction |= Rs << 16;
+  if (!inst.operands[2].isreg)
+    {
+      inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+      inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
     }
   else
     }
   else
-    {
-      if (my_get_expression (&inst.reloc.exp, &str))
-       return;
+    encode_thumb32_shifted_operand (2);
+}
 
 
-      inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
-      inst.reloc.pc_rel = 1;
-      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset.  */
-      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
-      end_of_line (str);
-      return;
-    }
+static void
+do_t_setend (void)
+{
+  if (inst.operands[0].imm)
+    inst.instruction |= 0x8;
+}
 
 
-  if (Rb == REG_PC || Rb == REG_SP)
+static void
+do_t_shift (void)
+{
+  if (!inst.operands[1].present)
+    inst.operands[1].reg = inst.operands[0].reg;
+
+  if (unified_syntax)
     {
     {
-      if (size != THUMB_WORD)
-       {
-         inst.error = _("byte or halfword not valid for base register");
-         return;
-       }
-      else if (Rb == REG_PC && load_store != THUMB_LOAD)
-       {
-         inst.error = _("r15 based store not allowed");
-         return;
-       }
-      else if (Ro != FAIL)
+      if (inst.operands[0].reg > 7
+         || inst.operands[1].reg > 7
+         || !THUMB_SETS_FLAGS (inst.instruction)
+         || (!inst.operands[2].isreg && inst.instruction == T_MNEM_rors)
+         || (inst.operands[2].isreg && inst.operands[1].reg != inst.operands[0].reg)
+         || inst.size_req == 4)
        {
        {
-         inst.error = _("invalid base register for register offset");
-         return;
+         if (inst.operands[2].isreg)
+           {
+             inst.instruction = THUMB_OP32 (inst.instruction);
+             inst.instruction |= inst.operands[0].reg << 8;
+             inst.instruction |= inst.operands[1].reg << 16;
+             inst.instruction |= inst.operands[2].reg;
+           }
+         else
+           {
+             inst.operands[1].shifted = 1;
+             switch (inst.instruction)
+               {
+               case T_MNEM_asr:
+               case T_MNEM_asrs: inst.operands[1].shift_kind = SHIFT_ASR; break;
+               case T_MNEM_lsl:
+               case T_MNEM_lsls: inst.operands[1].shift_kind = SHIFT_LSL; break;
+               case T_MNEM_lsr:
+               case T_MNEM_lsrs: inst.operands[1].shift_kind = SHIFT_LSR; break;
+               case T_MNEM_ror:
+               case T_MNEM_rors: inst.operands[1].shift_kind = SHIFT_ROR; break;
+               default: abort ();
+               }
+             
+             inst.instruction = THUMB_OP32 (THUMB_SETS_FLAGS (inst.instruction)
+                                            ? T_MNEM_movs : T_MNEM_mov);
+             inst.instruction |= inst.operands[0].reg << 8;
+             encode_thumb32_shifted_operand (1);
+             /* Prevent the incorrect generation of an ARM_IMMEDIATE fixup.  */
+             inst.reloc.type = BFD_RELOC_UNUSED;
+           }
        }
        }
-
-      if (Rb == REG_PC)
-       inst.instruction = T_OPCODE_LDR_PC;
-      else if (load_store == THUMB_LOAD)
-       inst.instruction = T_OPCODE_LDR_SP;
       else
       else
-       inst.instruction = T_OPCODE_STR_SP;
-
-      inst.instruction |= Rd << 8;
-      if (inst.reloc.exp.X_op == O_constant)
        {
        {
-         unsigned offset = inst.reloc.exp.X_add_number;
-
-         if (offset & ~0x3fc)
+         if (inst.operands[2].isreg)
            {
            {
-             inst.error = _("invalid offset");
-             return;
+             switch (inst.instruction)
+               {
+               case T_MNEM_asrs: inst.instruction = T_OPCODE_ASR_R; break;
+               case T_MNEM_lsls: inst.instruction = T_OPCODE_LSL_R; break;
+               case T_MNEM_lsrs: inst.instruction = T_OPCODE_LSR_R; break;
+               case T_MNEM_rors: inst.instruction = T_OPCODE_ROR_R; break;
+               default: abort ();
+               }
+         
+             inst.instruction |= inst.operands[0].reg;
+             inst.instruction |= inst.operands[2].reg << 3;
+           }
+         else
+           {
+             switch (inst.instruction)
+               {
+               case T_MNEM_asrs: inst.instruction = T_OPCODE_ASR_I; break;
+               case T_MNEM_lsls: inst.instruction = T_OPCODE_LSL_I; break;
+               case T_MNEM_lsrs: inst.instruction = T_OPCODE_LSR_I; break;
+               default: abort ();
+               }
+             inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+             inst.instruction |= inst.operands[0].reg;
+             inst.instruction |= inst.operands[1].reg << 3;
            }
            }
-
-         inst.instruction |= offset >> 2;
        }
        }
-      else
-       inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
-    }
-  else if (Rb > 7)
-    {
-      inst.error = _("invalid base register in load/store");
-      return;
     }
     }
-  else if (Ro == FAIL)
+  else
     {
     {
-      /* Immediate offset.  */
-      if (size == THUMB_WORD)
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
-      else if (size == THUMB_HALFWORD)
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
-      else
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
-
-      inst.instruction |= Rd | (Rb << 3);
+      constraint (inst.operands[0].reg > 7
+                 || inst.operands[1].reg > 7, BAD_HIREG);
+      constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
 
 
-      if (inst.reloc.exp.X_op == O_constant)
+      if (inst.operands[2].isreg)  /* Rd, {Rs,} Rn */
        {
        {
-         unsigned offset = inst.reloc.exp.X_add_number;
+         constraint (inst.operands[2].reg > 7, BAD_HIREG);
+         constraint (inst.operands[0].reg != inst.operands[1].reg,
+                     _("source1 and dest must be same register"));
 
 
-         if (offset & ~(0x1f << size))
+         switch (inst.instruction)
            {
            {
-             inst.error = _("invalid offset");
-             return;
+           case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_R; break;
+           case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_R; break;
+           case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_R; break;
+           case T_MNEM_ror: inst.instruction = T_OPCODE_ROR_R; break;
+           default: abort ();
            }
            }
-         inst.instruction |= (offset >> size) << 6;
+         
+         inst.instruction |= inst.operands[0].reg;
+         inst.instruction |= inst.operands[2].reg << 3;
        }
       else
        }
       else
-       inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
-    }
-  else
-    {
-      /* Register offset.  */
-      if (size == THUMB_WORD)
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
-      else if (size == THUMB_HALFWORD)
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
-      else
-       inst.instruction = (load_store == THUMB_LOAD
-                           ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
-
-      inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
-    }
-
-  end_of_line (str);
-}
-
-/* A register must be given at this point.
-
-   Shift is the place to put it in inst.instruction.
-
-   Restores input start point on err.
-   Returns the reg#, or FAIL.  */
-
-static int
-mav_reg_required_here (str, shift, regtype)
-     char ** str;
-     int shift;
-     enum arm_reg_type regtype;
-{
-  int   reg;
-  char *start = *str;
-
-  if ((reg = arm_reg_parse (str, all_reg_maps[regtype].htab)) != FAIL)
-    {
-      if (shift >= 0)
-       inst.instruction |= reg << shift;
-
-      return reg;
+       {
+         switch (inst.instruction)
+           {
+           case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_I; break;
+           case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_I; break;
+           case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_I; break;
+           case T_MNEM_ror: inst.error = _("ror #imm not supported"); return;
+           default: abort ();
+           }
+         inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+         inst.instruction |= inst.operands[0].reg;
+         inst.instruction |= inst.operands[1].reg << 3;
+       }
     }
     }
-
-  /* Restore the start point.  */
-  *str = start;
-
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden.  */
-  inst.error = _(all_reg_maps[regtype].expected);
-
-  return FAIL;
 }
 
 }
 
-/* Cirrus Maverick Instructions.  */
-
-/* Wrapper functions.  */
-
 static void
 static void
-do_mav_binops_1a (str)
-     char * str;
+do_t_simd (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVF);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1b (str)
-     char * str;
+do_t_smi (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVD);
+  unsigned int value = inst.reloc.exp.X_add_number;
+  constraint (inst.reloc.exp.X_op != O_constant,
+             _("expression too complex"));
+  inst.reloc.type = BFD_RELOC_UNUSED;
+  inst.instruction |= (value & 0xf000) >> 12;
+  inst.instruction |= (value & 0x0ff0);
+  inst.instruction |= (value & 0x000f) << 16;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1c (str)
-     char * str;
+do_t_ssat (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVDX);
-}
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm - 1;
+  inst.instruction |= inst.operands[2].reg << 16;
 
 
-static void
-do_mav_binops_1d (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVF);
-}
+  if (inst.operands[3].present)
+    {
+      constraint (inst.reloc.exp.X_op != O_constant,
+                 _("expression too complex"));
 
 
-static void
-do_mav_binops_1e (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVD);
+      if (inst.reloc.exp.X_add_number != 0)
+       {
+         if (inst.operands[3].shift_kind == SHIFT_ASR)
+           inst.instruction |= 0x00200000;  /* sh bit */
+         inst.instruction |= (inst.reloc.exp.X_add_number & 0x1c) << 10;
+         inst.instruction |= (inst.reloc.exp.X_add_number & 0x03) << 6;
+       }
+      inst.reloc.type = BFD_RELOC_UNUSED;
+    }
 }
 
 static void
 }
 
 static void
-do_mav_binops_1f (str)
-     char * str;
+do_t_ssat16 (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVF);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm - 1;
+  inst.instruction |= inst.operands[2].reg << 16;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1g (str)
-     char * str;
+do_t_strex (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVD);
-}
+  constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+             || inst.operands[2].postind || inst.operands[2].writeback
+             || inst.operands[2].immisreg || inst.operands[2].shifted
+             || inst.operands[2].negative,
+             _("instruction does not accept this addressing mode"));
 
 
-static void
-do_mav_binops_1h (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVFX);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 16;
+  inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1i (str)
-     char * str;
+do_t_strexd (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVFX);
+  if (!inst.operands[2].present)
+    inst.operands[2].reg = inst.operands[1].reg + 1;
+
+  constraint (inst.operands[0].reg == inst.operands[1].reg
+             || inst.operands[0].reg == inst.operands[2].reg
+             || inst.operands[0].reg == inst.operands[3].reg
+             || inst.operands[1].reg == inst.operands[2].reg,
+             BAD_OVERLAP);
+
+  inst.instruction |= inst.operands[0].reg;
+  inst.instruction |= inst.operands[1].reg << 12;
+  inst.instruction |= inst.operands[2].reg << 8;
+  inst.instruction |= inst.operands[3].reg << 16;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1j (str)
-     char * str;
+do_t_sxtah (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVDX);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].reg << 16;
+  inst.instruction |= inst.operands[2].reg;
+  inst.instruction |= inst.operands[3].imm << 4;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1k (str)
-     char * str;
+do_t_sxth (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVDX);
+  if (inst.instruction <= 0xffff && inst.size_req != 4
+      && inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7
+      && (!inst.operands[2].present || inst.operands[2].imm == 0))
+    {
+      inst.instruction = THUMB_OP16 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg;
+      inst.instruction |= inst.operands[1].reg << 3;
+    }
+  else if (unified_syntax)
+    {
+      if (inst.instruction <= 0xffff)
+       inst.instruction = THUMB_OP32 (inst.instruction);
+      inst.instruction |= inst.operands[0].reg << 8;
+      inst.instruction |= inst.operands[1].reg;
+      inst.instruction |= inst.operands[2].imm << 4;
+    }
+  else
+    {
+      constraint (inst.operands[2].present && inst.operands[2].imm != 0,
+                 _("Thumb encoding does not support rotation"));
+      constraint (1, BAD_HIREG);
+    }
 }
 
 static void
 }
 
 static void
-do_mav_binops_1l (str)
-     char * str;
+do_t_swi (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVF);
+  inst.reloc.type = BFD_RELOC_ARM_SWI;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1m (str)
-     char * str;
+do_t_tb (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVD);
+  int half;
+
+  half = (inst.instruction & 0x10) != 0;
+  constraint (inst.operands[0].imm == 15,
+             _("PC is not a valid index register"));
+  constraint (!half && inst.operands[0].shifted,
+             _("instruction does not allow shifted index"));
+  constraint (half && !inst.operands[0].shifted,
+             _("instruction requires shifted index"));
+  inst.instruction |= (inst.operands[0].reg << 16) | inst.operands[0].imm;
 }
 
 static void
 }
 
 static void
-do_mav_binops_1n (str)
-     char * str;
+do_t_usat (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVFX);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm;
+  inst.instruction |= inst.operands[2].reg << 16;
+
+  if (inst.operands[3].present)
+    {
+      constraint (inst.reloc.exp.X_op != O_constant,
+                 _("expression too complex"));
+      if (inst.reloc.exp.X_add_number != 0)
+       {
+         if (inst.operands[3].shift_kind == SHIFT_ASR)
+           inst.instruction |= 0x00200000;  /* sh bit */
+
+         inst.instruction |= (inst.reloc.exp.X_add_number & 0x1c) << 10;
+         inst.instruction |= (inst.reloc.exp.X_add_number & 0x03) << 6;
+       }
+      inst.reloc.type = BFD_RELOC_UNUSED;
+    }
 }
 
 static void
 }
 
 static void
-do_mav_binops_1o (str)
-     char * str;
+do_t_usat16 (void)
 {
 {
-  do_mav_binops (str, MAV_MODE1, REG_TYPE_MVDX, REG_TYPE_MVDX);
+  inst.instruction |= inst.operands[0].reg << 8;
+  inst.instruction |= inst.operands[1].imm;
+  inst.instruction |= inst.operands[2].reg << 16;
 }
 }
+\f
+/* Overall per-instruction processing. */
 
 
-static void
-do_mav_binops_2a (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE2, REG_TYPE_MVF, REG_TYPE_RN);
-}
+/* We need to be able to fix up arbitrary expressions in some statements.
+   This is so that we can handle symbols that are an arbitrary distance from
+   the pc.  The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+   which returns part of an address in a form which will be valid for
+   a data instruction. We do this by pushing the expression into a symbol
+   in the expr_section, and creating a fix for that.  */
 
 static void
 
 static void
-do_mav_binops_2b (str)
-     char * str;
+fix_new_arm (fragS *      frag,
+            int           where,
+            short int     size,
+            expressionS * exp,
+            int           pc_rel,
+            int           reloc)
 {
 {
-  do_mav_binops (str, MAV_MODE2, REG_TYPE_MVD, REG_TYPE_RN);
-}
+  fixS *          new_fix;
 
 
-static void
-do_mav_binops_2c (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE2, REG_TYPE_MVDX, REG_TYPE_RN);
-}
+  switch (exp->X_op)
+    {
+    case O_constant:
+    case O_symbol:
+    case O_add:
+    case O_subtract:
+      new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+      break;
 
 
-static void
-do_mav_binops_3a (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE3, REG_TYPE_MVAX, REG_TYPE_MVFX);
-}
+    default:
+      new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
+                        pc_rel, reloc);
+      break;
+    }
 
 
-static void
-do_mav_binops_3b (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE3, REG_TYPE_MVFX, REG_TYPE_MVAX);
+  /* Mark whether the fix is to a THUMB instruction, or an ARM
+     instruction.  */
+  new_fix->tc_fix_data = thumb_mode;
 }
 
 static void
 }
 
 static void
-do_mav_binops_3c (str)
-     char * str;
+output_inst (const char * str)
 {
 {
-  do_mav_binops (str, MAV_MODE3, REG_TYPE_MVAX, REG_TYPE_MVDX);
-}
+  char * to = NULL;
 
 
-static void
-do_mav_binops_3d (str)
-     char * str;
-{
-  do_mav_binops (str, MAV_MODE3, REG_TYPE_MVDX, REG_TYPE_MVAX);
-}
+  if (inst.error)
+    {
+      as_bad ("%s -- `%s'", inst.error, str);
+      return;
+    }
+  if (inst.size == 0)
+    return;
 
 
-static void
-do_mav_triple_4a (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE4, REG_TYPE_MVFX, REG_TYPE_MVFX, REG_TYPE_RN);
-}
+  to = frag_more (inst.size);
 
 
-static void
-do_mav_triple_4b (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE4, REG_TYPE_MVDX, REG_TYPE_MVDX, REG_TYPE_RN);
-}
+  if (thumb_mode && (inst.size > THUMB_SIZE))
+    {
+      assert (inst.size == (2 * THUMB_SIZE));
+      md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
+      md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
+    }
+  else if (inst.size > INSN_SIZE)
+    {
+      assert (inst.size == (2 * INSN_SIZE));
+      md_number_to_chars (to, inst.instruction, INSN_SIZE);
+      md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
+    }
+  else
+    md_number_to_chars (to, inst.instruction, inst.size);
 
 
-static void
-do_mav_triple_5a (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVF, REG_TYPE_MVF);
-}
+  if (inst.reloc.type != BFD_RELOC_UNUSED)
+    fix_new_arm (frag_now, to - frag_now->fr_literal,
+                inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
+                inst.reloc.type);
 
 
-static void
-do_mav_triple_5b (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVD, REG_TYPE_MVD);
+#ifdef OBJ_ELF
+  dwarf2_emit_insn (inst.size);
+#endif
 }
 
 }
 
-static void
-do_mav_triple_5c (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+/* Tag values used in struct asm_opcode's tag field.  */
+enum opcode_tag
+{
+  OT_unconditional,    /* Instruction cannot be conditionalized.
+                          The ARM condition field is still 0xE.  */
+  OT_unconditionalF,   /* Instruction cannot be conditionalized
+                          and carries 0xF in its ARM condition field.  */
+  OT_csuffix,          /* Instruction takes a conditional suffix.  */
+  OT_cinfix3,          /* Instruction takes a conditional infix,
+                          beginning at character index 3.  (In
+                          unified mode, it becomes a suffix.)  */
+  OT_csuf_or_in3,      /* Instruction takes either a conditional
+                          suffix or an infix at character index 3.
+                          (In unified mode, a suffix only.  */
+  OT_odd_infix_unc,    /* This is the unconditional variant of an
+                          instruction that takes a conditional infix
+                          at an unusual position.  In unified mode,
+                          this variant will accept a suffix.  */
+  OT_odd_infix_0       /* Values greater than or equal to OT_odd_infix_0
+                          are the conditional variants of instructions that
+                          take conditional infixes in unusual positions.
+                          The infix appears at character index
+                          (tag - OT_odd_infix_0).  These are not accepted
+                          in unified mode.  */
+};
 
 
-static void
-do_mav_triple_5d (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVDX, REG_TYPE_MVDX);
-}
+/* Subroutine of md_assemble, responsible for looking up the primary
+   opcode from the mnemonic the user wrote.  STR points to the
+   beginning of the mnemonic.
+
+   This is not simply a hash table lookup, because of conditional
+   variants.  Most instructions have conditional variants, which are
+   expressed with a _conditional affix_ to the mnemonic.  If we were
+   to encode each conditional variant as a literal string in the opcode
+   table, it would have approximately 20,000 entries.
+
+   Most mnemonics take this affix as a suffix, and in unified syntax,
+   'most' is upgraded to 'all'.  However, in the divided syntax, some
+   instructions take the affix as an infix, notably the s-variants of
+   the arithmetic instructions.  Of those instructions, all but six
+   have the infix appear after the third character of the mnemonic.
+
+   Accordingly, the algorithm for looking up primary opcodes given
+   an identifier is:
+
+   1. Look up the identifier in the opcode table.
+      If we find a match, go to step U.
+
+   2. Look up the last two characters of the identifier in the
+      conditions table.  If we find a match, look up the first N-2
+      characters of the identifier in the opcode table.  If we
+      find a match, go to step CE.
+
+   3. Look up the fourth and fifth characters of the identifier in
+      the conditions table.  If we find a match, extract those
+      characters from the identifier, and look up the remaining
+      characters in the opcode table.  If we find a match, go
+      to step CM.
+
+   4. Fail.
+
+   U. Examine the tag field of the opcode structure, in case this is
+      one of the six instructions with its conditional infix in an
+      unusual place.  If it is, the tag tells us where to find the
+      infix; look it up in the conditions table and set inst.cond
+      accordingly.  Otherwise, this is an unconditional instruction.
+      Again set inst.cond accordingly.  Return the opcode structure.
+
+  CE. Examine the tag field to make sure this is an instruction that
+      should receive a conditional suffix.  If it is not, fail.
+      Otherwise, set inst.cond from the suffix we already looked up,
+      and return the opcode structure.
+
+  CM. Examine the tag field to make sure this is an instruction that
+      should receive a conditional infix after the third character.
+      If it is not, fail.  Otherwise, undo the edits to the current
+      line of input and proceed as for case CE.  */
+
+static const struct asm_opcode *
+opcode_lookup (char **str)
+{
+  char *end, *base;
+  char *affix;
+  const struct asm_opcode *opcode;
+  const struct asm_cond *cond;
+
+  /* Scan up to the end of the mnemonic, which must end in white space,
+     '.' (in unified mode only), or end of string.  */
+  for (base = end = *str; *end != '\0'; end++)
+    if (*end == ' ' || (unified_syntax && *end == '.'))
+      break;
 
 
-static void
-do_mav_triple_5e (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_MVF, REG_TYPE_MVF, REG_TYPE_MVF);
-}
+  if (end == base)
+    return 0;
 
 
-static void
-do_mav_triple_5f (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_MVD, REG_TYPE_MVD, REG_TYPE_MVD);
-}
+  /* Handle a possible width suffix.  */
+  if (end[0] == '.')
+    {
+      if (end[1] == 'w' && (end[2] == ' ' || end[2] == '\0'))
+       inst.size_req = 4;
+      else if (end[1] == 'n' && (end[2] == ' ' || end[2] == '\0'))
+       inst.size_req = 2;
+      else
+       return 0;
 
 
-static void
-do_mav_triple_5g (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_MVFX, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+      *str = end + 2;
+    }
+  else
+    *str = end;
 
 
-static void
-do_mav_triple_5h (str)
-     char * str;
-{
-  do_mav_triple (str, MAV_MODE5, REG_TYPE_MVDX, REG_TYPE_MVDX, REG_TYPE_MVDX);
-}
+  /* Look for unaffixed or special-case affixed mnemonic.  */
+  opcode = hash_find_n (arm_ops_hsh, base, end - base);
+  if (opcode)
+    {
+      /* step U */
+      if (opcode->tag < OT_odd_infix_0)
+       {
+         inst.cond = COND_ALWAYS;
+         return opcode;
+       }
 
 
-static void
-do_mav_quad_6a (str)
-     char * str;
-{
-  do_mav_quad (str, MAV_MODE6, REG_TYPE_MVAX, REG_TYPE_MVFX, REG_TYPE_MVFX,
-            REG_TYPE_MVFX);
-}
+      if (unified_syntax)
+       as_warn (_("conditional infixes are deprecated in unified syntax"));
+      affix = base + (opcode->tag - OT_odd_infix_0);
+      cond = hash_find_n (arm_cond_hsh, affix, 2);
+      assert (cond);
 
 
-static void
-do_mav_quad_6b (str)
-     char * str;
-{
-  do_mav_quad (str, MAV_MODE6, REG_TYPE_MVAX, REG_TYPE_MVAX, REG_TYPE_MVFX,
-            REG_TYPE_MVFX);
-}
+      inst.cond = cond->value;
+      return opcode;
+    }
 
 
-/* cfmvsc32<cond> DSPSC,MVFX[15:0].  */
-static void
-do_mav_dspsc_1 (str)
-     char * str;
-{
-  skip_whitespace (str);
+  /* Cannot have a conditional suffix on a mnemonic of less than two
+     characters.  */
+  if (end - base < 3)
+    return 0;
 
 
-  /* cfmvsc32.  */
-  if (mav_reg_required_here (&str, -1, REG_TYPE_DSPSC) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, 16, REG_TYPE_MVFX) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+  /* Look for suffixed mnemonic.  */
+  affix = end - 2;
+  cond = hash_find_n (arm_cond_hsh, affix, 2);
+  opcode = hash_find_n (arm_ops_hsh, base, affix - base);
+  if (opcode && cond)
+    {
+      /* step CE */
+      switch (opcode->tag)
+       {
+       case OT_cinfix3:
+       case OT_odd_infix_unc:
+         if (!unified_syntax)
+           return 0;
+         /* else fall through */
+
+       case OT_csuffix:
+       case OT_csuf_or_in3:
+         inst.cond = cond->value;
+         return opcode;
+
+       case OT_unconditional:
+       case OT_unconditionalF:
+         /* delayed diagnostic */
+         inst.error = BAD_COND;
+         inst.cond = COND_ALWAYS;
+         return opcode;
 
 
-      return;
+       default:
+         return 0;
+       }
     }
 
     }
 
-  end_of_line (str);
-}
-
-/* cfmv32sc<cond> MVFX[15:0],DSPSC.  */
-static void
-do_mav_dspsc_2 (str)
-     char * str;
-{
-  skip_whitespace (str);
+  /* Cannot have a usual-position infix on a mnemonic of less than
+     six characters (five would be a suffix).  */
+  if (end - base < 6)
+    return 0;
 
 
-  /* cfmv32sc.  */
-  if (mav_reg_required_here (&str, 0, REG_TYPE_MVFX) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, -1, REG_TYPE_DSPSC) == FAIL)
+  /* Look for infixed mnemonic in the usual position.  */
+  affix = base + 3;
+  cond = hash_find_n (arm_cond_hsh, affix, 2);
+  if (cond)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+      char save[2];
+      memcpy (save, affix, 2);
+      memmove (affix, affix + 2, (end - affix) - 2);
+      opcode = hash_find_n (arm_ops_hsh, base, (end - base) - 2);
+      memmove (affix + 2, affix, (end - affix) - 2);
+      memcpy (affix, save, 2);
+    }
+  if (opcode && (opcode->tag == OT_cinfix3 || opcode->tag == OT_csuf_or_in3))
+    {
+      /* step CM */
+      if (unified_syntax)
+       as_warn (_("conditional infixes are deprecated in unified syntax"));
 
 
-      return;
+      inst.cond = cond->value;
+      return opcode;
     }
 
     }
 
-  end_of_line (str);
+  return 0;
 }
 
 }
 
-static void
-do_mav_shift_1 (str)
-     char * str;
+void
+md_assemble (char *str)
 {
 {
-  do_mav_shift (str, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+  char *p = str;
+  const struct asm_opcode * opcode;
 
 
-static void
-do_mav_shift_2 (str)
-     char * str;
-{
-  do_mav_shift (str, REG_TYPE_MVDX, REG_TYPE_MVDX);
-}
+  /* Align the previous label if needed.  */
+  if (last_label_seen != NULL)
+    {
+      symbol_set_frag (last_label_seen, frag_now);
+      S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
+      S_SET_SEGMENT (last_label_seen, now_seg);
+    }
 
 
-static void
-do_mav_ldst_1 (str)
-     char * str;
-{
-  do_mav_ldst (str, REG_TYPE_MVF);
-}
+  memset (&inst, '\0', sizeof (inst));
+  inst.reloc.type = BFD_RELOC_UNUSED;
 
 
-static void
-do_mav_ldst_2 (str)
-     char * str;
-{
-  do_mav_ldst (str, REG_TYPE_MVD);
-}
+  opcode = opcode_lookup (&p);
+  if (!opcode)
+    {
+      /* It wasn't an instruction, but it might be a register alias of
+        the form alias .req reg.  */
+      if (!create_register_alias (str, p))
+       as_bad (_("bad instruction `%s'"), str);
 
 
-static void
-do_mav_ldst_3 (str)
-     char * str;
-{
-  do_mav_ldst (str, REG_TYPE_MVFX);
-}
+      return;
+    }
 
 
-static void
-do_mav_ldst_4 (str)
-     char * str;
-{
-  do_mav_ldst (str, REG_TYPE_MVDX);
-}
+  if (thumb_mode)
+    {
+      /* Check that this instruction is supported for this CPU.  */
+      if (thumb_mode == 1 && (opcode->tvariant & cpu_variant) == 0)
+       {
+         as_bad (_("selected processor does not support `%s'"), str);
+         return;
+       }
+      if (inst.cond != COND_ALWAYS && !unified_syntax
+         && opcode->tencode != do_t_branch)
+       {
+         as_bad (_("Thumb does not support conditional execution"));
+         return;
+       }
 
 
-/* Isnsn like "foo X,Y".  */
+      /* Check conditional suffixes.  */
+      if (current_it_mask)
+       {
+         int cond;
+         cond = current_cc ^ ((current_it_mask >> 4) & 1) ^ 1;
+         if (cond != inst.cond)
+           {
+             as_bad (_("incorrect condition in IT block"));
+             return;
+           }
+         current_it_mask <<= 1;
+         current_it_mask &= 0x1f;
+       }
+      else if (inst.cond != COND_ALWAYS && opcode->tencode != do_t_branch)
+       {
+         as_bad (_("thumb conditional instrunction not in IT block"));
+         return;
+       }
 
 
-static void
-do_mav_binops (str, mode, reg0, reg1)
-     char * str;
-     int mode;
-     enum arm_reg_type reg0;
-     enum arm_reg_type reg1;
-{
-  int shift0, shift1;
+      mapping_state (MAP_THUMB);
+      inst.instruction = opcode->tvalue;
 
 
-  shift0 = mode & 0xff;
-  shift1 = (mode >> 8) & 0xff;
+      if (!parse_operands (p, opcode->operands))
+       opcode->tencode ();
 
 
-  skip_whitespace (str);
+      /* Clear current_it_mask at the end of an IT block.  */
+      if (current_it_mask == 0x10)
+       current_it_mask = 0;
 
 
-  if (mav_reg_required_here (&str, shift0, reg0) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift1, reg1) == FAIL)
-    {
       if (!inst.error)
       if (!inst.error)
-       inst.error = BAD_ARGS;
+       {
+         assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
+         inst.size = (inst.instruction > 0xffff ? 4 : 2);
+         if (inst.size_req && inst.size_req != inst.size)
+           {
+             as_bad (_("cannot honor width suffix -- `%s'"), str);
+             return;
+           }
+       }
     }
   else
     }
   else
-    end_of_line (str);
+    {
+      /* Check that this instruction is supported for this CPU.  */
+      if ((opcode->avariant & cpu_variant) == 0)
+       {
+         as_bad (_("selected processor does not support `%s'"), str);
+         return;
+       }
+      if (inst.size_req)
+       {
+         as_bad (_("width suffixes are invalid in ARM mode -- `%s'"), str);
+         return;
+       }
+
+      mapping_state (MAP_ARM);
+      inst.instruction = opcode->avalue;
+      if (opcode->tag == OT_unconditionalF)
+       inst.instruction |= 0xF << 28;
+      else
+       inst.instruction |= inst.cond << 28;
+      inst.size = INSN_SIZE;
+      if (!parse_operands (p, opcode->operands))
+       opcode->aencode ();
+    }
+  output_inst (str);
 }
 
 }
 
-/* Isnsn like "foo X,Y,Z".  */
+/* Various frobbings of labels and their addresses.  */
 
 
-static void
-do_mav_triple (str, mode, reg0, reg1, reg2)
-     char * str;
-     int mode;
-     enum arm_reg_type reg0;
-     enum arm_reg_type reg1;
-     enum arm_reg_type reg2;
+void
+arm_start_line_hook (void)
 {
 {
-  int shift0, shift1, shift2;
+  last_label_seen = NULL;
+}
 
 
-  shift0 = mode & 0xff;
-  shift1 = (mode >> 8) & 0xff;
-  shift2 = (mode >> 16) & 0xff;
+void
+arm_frob_label (symbolS * sym)
+{
+  last_label_seen = sym;
 
 
-  skip_whitespace (str);
+  ARM_SET_THUMB (sym, thumb_mode);
 
 
-  if (mav_reg_required_here (&str, shift0, reg0) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift1, reg1) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift2, reg2) == FAIL)
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-    }
-  else
-    end_of_line (str);
-}
+#if defined OBJ_COFF || defined OBJ_ELF
+  ARM_SET_INTERWORK (sym, support_interwork);
+#endif
 
 
-/* Isnsn like "foo W,X,Y,Z".
-    where W=MVAX[0:3] and X,Y,Z=MVFX[0:15].  */
+  /* Note - do not allow local symbols (.Lxxx) to be labeled
+     as Thumb functions.  This is because these labels, whilst
+     they exist inside Thumb code, are not the entry points for
+     possible ARM->Thumb calls.         Also, these labels can be used
+     as part of a computed goto or switch statement.  eg gcc
+     can generate code that looks like this:
 
 
-static void
-do_mav_quad (str, mode, reg0, reg1, reg2, reg3)
-     char * str;
-     int mode;
-     enum arm_reg_type reg0;
-     enum arm_reg_type reg1;
-     enum arm_reg_type reg2;
-     enum arm_reg_type reg3;
-{
-  int shift0, shift1, shift2, shift3;
+               ldr  r2, [pc, .Laaa]
+               lsl  r3, r3, #2
+               ldr  r2, [r3, r2]
+               mov  pc, r2
 
 
-  shift0= mode & 0xff;
-  shift1 = (mode >> 8) & 0xff;
-  shift2 = (mode >> 16) & 0xff;
-  shift3 = (mode >> 24) & 0xff;
+       .Lbbb:  .word .Lxxx
+       .Lccc:  .word .Lyyy
+       ..etc...
+       .Laaa:  .word Lbbb
 
 
-  skip_whitespace (str);
+     The first instruction loads the address of the jump table.
+     The second instruction converts a table index into a byte offset.
+     The third instruction gets the jump address out of the table.
+     The fourth instruction performs the jump.
 
 
-  if (mav_reg_required_here (&str, shift0, reg0) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift1, reg1) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift2, reg2) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, shift3, reg3) == FAIL)
+     If the address stored at .Laaa is that of a symbol which has the
+     Thumb_Func bit set, then the linker will arrange for this address
+     to have the bottom bit set, which in turn would mean that the
+     address computation performed by the third instruction would end
+     up with the bottom bit set.  Since the ARM is capable of unaligned
+     word loads, the instruction would then load the incorrect address
+     out of the jump table, and chaos would ensue.  */
+  if (label_is_thumb_function_name
+      && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
+      && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
+      /* When the address of a Thumb function is taken the bottom
+        bit of that address should be set.  This will allow
+        interworking between Arm and Thumb functions to work
+        correctly.  */
+
+      THUMB_SET_FUNC (sym, 1);
+
+      label_is_thumb_function_name = FALSE;
     }
     }
-  else
-    end_of_line (str);
 }
 
 }
 
-/* Maverick shift immediate instructions.
-   cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
-   cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0].  */
-
-static void
-do_mav_shift (str, reg0, reg1)
-     char * str;
-     enum arm_reg_type reg0;
-     enum arm_reg_type reg1;
+int
+arm_data_in_code (void)
 {
 {
-  int error;
-  int imm, neg = 0;
-
-  skip_whitespace (str);
-
-  error = 0;
-
-  if (mav_reg_required_here (&str, 12, reg0) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || mav_reg_required_here (&str, 16, reg1) == FAIL
-      || skip_past_comma  (&str) == FAIL)
+  if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
     {
     {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      *input_line_pointer = '/';
+      input_line_pointer += 5;
+      *input_line_pointer = 0;
+      return 1;
     }
 
     }
 
-  /* Calculate the immediate operand.
-     The operand is a 7bit signed number.  */
-  skip_whitespace (str);
+  return 0;
+}
 
 
-  if (*str == '#')
-    ++str;
+char *
+arm_canonicalize_symbol_name (char * name)
+{
+  int len;
 
 
-  if (!ISDIGIT (*str) && *str != '-')
-    {
-      inst.error = _("expecting immediate, 7bit operand");
-      return;
-    }
+  if (thumb_mode && (len = strlen (name)) > 5
+      && streq (name + len - 5, "/data"))
+    *(name + len - 5) = 0;
 
 
-  if (*str == '-')
-    {
-      neg = 1;
-      ++str;
-    }
+  return name;
+}
+\f
+/* Table of all register names defined by default.  The user can
+   define additional names with .req.  Note that all register names
+   should appear in both upper and lowercase variants. Some registers
+   also have mixed-case names. */
 
 
-  for (imm = 0; *str && ISDIGIT (*str); ++str)
-    imm = imm * 10 + *str - '0';
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE }
+#define REGNUM(p,n,t) REGDEF(p##n, n, t)
+#define REGSET(p,t) \
+  REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
+  REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \
+  REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \
+  REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t)
 
 
-  if (imm > 64)
-    {
-      inst.error = _("immediate out of range");
-      return;
-    }
+static const struct reg_entry reg_names[] =
+{
+  /* ARM integer registers.  */
+  REGSET(r, RN), REGSET(R, RN),
 
 
-  /* Make negative imm's into 7bit signed numbers.  */
-  if (neg)
-    {
-      imm = -imm;
-      imm &= 0x0000007f;
-    }
+  /* ATPCS synonyms.  */
+  REGDEF(a1,0,RN), REGDEF(a2,1,RN), REGDEF(a3, 2,RN), REGDEF(a4, 3,RN),
+  REGDEF(v1,4,RN), REGDEF(v2,5,RN), REGDEF(v3, 6,RN), REGDEF(v4, 7,RN),
+  REGDEF(v5,8,RN), REGDEF(v6,9,RN), REGDEF(v7,10,RN), REGDEF(v8,11,RN),
 
 
-  /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
-     Bits 5-7 of the insn should have bits 4-6 of the immediate.
-     Bit 4 should be 0.  */
-  imm = (imm & 0xf) | ((imm & 0x70) << 1);
+  REGDEF(A1,0,RN), REGDEF(A2,1,RN), REGDEF(A3, 2,RN), REGDEF(A4, 3,RN),
+  REGDEF(V1,4,RN), REGDEF(V2,5,RN), REGDEF(V3, 6,RN), REGDEF(V4, 7,RN),
+  REGDEF(V5,8,RN), REGDEF(V6,9,RN), REGDEF(V7,10,RN), REGDEF(V8,11,RN),
 
 
-  inst.instruction |= imm;
-  end_of_line (str);
-}
+  /* Well-known aliases.  */
+  REGDEF(wr, 7,RN), REGDEF(sb, 9,RN), REGDEF(sl,10,RN), REGDEF(fp,11,RN),
+  REGDEF(ip,12,RN), REGDEF(sp,13,RN), REGDEF(lr,14,RN), REGDEF(pc,15,RN),
+
+  REGDEF(WR, 7,RN), REGDEF(SB, 9,RN), REGDEF(SL,10,RN), REGDEF(FP,11,RN),
+  REGDEF(IP,12,RN), REGDEF(SP,13,RN), REGDEF(LR,14,RN), REGDEF(PC,15,RN),
+
+  /* Coprocessor numbers.  */
+  REGSET(p, CP), REGSET(P, CP),
+
+  /* Coprocessor register numbers.  The "cr" variants are for backward
+     compatibility.  */
+  REGSET(c,  CN), REGSET(C, CN),
+  REGSET(cr, CN), REGSET(CR, CN),
+
+  /* FPA registers.  */
+  REGNUM(f,0,FN), REGNUM(f,1,FN), REGNUM(f,2,FN), REGNUM(f,3,FN),
+  REGNUM(f,4,FN), REGNUM(f,5,FN), REGNUM(f,6,FN), REGNUM(f,7, FN),
+
+  REGNUM(F,0,FN), REGNUM(F,1,FN), REGNUM(F,2,FN), REGNUM(F,3,FN),
+  REGNUM(F,4,FN), REGNUM(F,5,FN), REGNUM(F,6,FN), REGNUM(F,7, FN),
+
+  /* VFP SP registers. */
+  REGSET(s,VFS),
+  REGNUM(s,16,VFS), REGNUM(s,17,VFS), REGNUM(s,18,VFS), REGNUM(s,19,VFS),
+  REGNUM(s,20,VFS), REGNUM(s,21,VFS), REGNUM(s,22,VFS), REGNUM(s,23,VFS),
+  REGNUM(s,24,VFS), REGNUM(s,25,VFS), REGNUM(s,26,VFS), REGNUM(s,27,VFS),
+  REGNUM(s,28,VFS), REGNUM(s,29,VFS), REGNUM(s,30,VFS), REGNUM(s,31,VFS),
+
+  REGSET(S,VFS),
+  REGNUM(S,16,VFS), REGNUM(S,17,VFS), REGNUM(S,18,VFS), REGNUM(S,19,VFS),
+  REGNUM(S,20,VFS), REGNUM(S,21,VFS), REGNUM(S,22,VFS), REGNUM(S,23,VFS),
+  REGNUM(S,24,VFS), REGNUM(S,25,VFS), REGNUM(S,26,VFS), REGNUM(S,27,VFS),
+  REGNUM(S,28,VFS), REGNUM(S,29,VFS), REGNUM(S,30,VFS), REGNUM(S,31,VFS),
+
+  /* VFP DP Registers. */
+  REGSET(d,VFD), REGSET(D,VFS),
+
+  /* VFP control registers.  */
+  REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC),
+  REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC),
+
+  /* Maverick DSP coprocessor registers.  */
+  REGSET(mvf,MVF),  REGSET(mvd,MVD),  REGSET(mvfx,MVFX),  REGSET(mvdx,MVDX),
+  REGSET(MVF,MVF),  REGSET(MVD,MVD),  REGSET(MVFX,MVFX),  REGSET(MVDX,MVDX),
+
+  REGNUM(mvax,0,MVAX), REGNUM(mvax,1,MVAX),
+  REGNUM(mvax,2,MVAX), REGNUM(mvax,3,MVAX),
+  REGDEF(dspsc,0,DSPSC),
+
+  REGNUM(MVAX,0,MVAX), REGNUM(MVAX,1,MVAX),
+  REGNUM(MVAX,2,MVAX), REGNUM(MVAX,3,MVAX),
+  REGDEF(DSPSC,0,DSPSC),
+
+  /* iWMMXt data registers - p0, c0-15.         */
+  REGSET(wr,MMXWR), REGSET(wR,MMXWR), REGSET(WR, MMXWR),
+
+  /* iWMMXt control registers - p1, c0-3.  */
+  REGDEF(wcid, 0,MMXWC),  REGDEF(wCID,  0,MMXWC),  REGDEF(WCID,  0,MMXWC),
+  REGDEF(wcon, 1,MMXWC),  REGDEF(wCon,  1,MMXWC),  REGDEF(WCON,  1,MMXWC),
+  REGDEF(wcssf, 2,MMXWC),  REGDEF(wCSSF, 2,MMXWC),  REGDEF(WCSSF, 2,MMXWC),
+  REGDEF(wcasf, 3,MMXWC),  REGDEF(wCASF, 3,MMXWC),  REGDEF(WCASF, 3,MMXWC),
+
+  /* iWMMXt scalar (constant/offset) registers - p1, c8-11.  */
+  REGDEF(wcgr0, 8,MMXWCG),  REGDEF(wCGR0, 8,MMXWCG),  REGDEF(WCGR0, 8,MMXWCG),
+  REGDEF(wcgr1, 9,MMXWCG),  REGDEF(wCGR1, 9,MMXWCG),  REGDEF(WCGR1, 9,MMXWCG),
+  REGDEF(wcgr2,10,MMXWCG),  REGDEF(wCGR2,10,MMXWCG),  REGDEF(WCGR2,10,MMXWCG),
+  REGDEF(wcgr3,11,MMXWCG),  REGDEF(wCGR3,11,MMXWCG),  REGDEF(WCGR3,11,MMXWCG),
+
+  /* XScale accumulator registers.  */
+  REGNUM(acc,0,XSCALE), REGNUM(ACC,0,XSCALE),
+};
+#undef REGDEF
+#undef REGNUM
+#undef REGSET
 
 
-static int
-mav_parse_offset (str, negative)
-     char ** str;
-     int *negative;
+/* Table of all PSR suffixes.  Bare "CPSR" and "SPSR" are handled
+   within psr_required_here.  */
+static const struct asm_psr psrs[] =
 {
 {
-  char * p = *str;
-  int offset;
-
-  *negative = 0;
+  /* Backward compatibility notation.  Note that "all" is no longer
+     truly all possible PSR bits.  */
+  {"all",  PSR_c | PSR_f},
+  {"flg",  PSR_f},
+  {"ctl",  PSR_c},
+
+  /* Individual flags. */
+  {"f",           PSR_f},
+  {"c",           PSR_c},
+  {"x",           PSR_x},
+  {"s",           PSR_s},
+  /* Combinations of flags.  */
+  {"fs",   PSR_f | PSR_s},
+  {"fx",   PSR_f | PSR_x},
+  {"fc",   PSR_f | PSR_c},
+  {"sf",   PSR_s | PSR_f},
+  {"sx",   PSR_s | PSR_x},
+  {"sc",   PSR_s | PSR_c},
+  {"xf",   PSR_x | PSR_f},
+  {"xs",   PSR_x | PSR_s},
+  {"xc",   PSR_x | PSR_c},
+  {"cf",   PSR_c | PSR_f},
+  {"cs",   PSR_c | PSR_s},
+  {"cx",   PSR_c | PSR_x},
+  {"fsx",  PSR_f | PSR_s | PSR_x},
+  {"fsc",  PSR_f | PSR_s | PSR_c},
+  {"fxs",  PSR_f | PSR_x | PSR_s},
+  {"fxc",  PSR_f | PSR_x | PSR_c},
+  {"fcs",  PSR_f | PSR_c | PSR_s},
+  {"fcx",  PSR_f | PSR_c | PSR_x},
+  {"sfx",  PSR_s | PSR_f | PSR_x},
+  {"sfc",  PSR_s | PSR_f | PSR_c},
+  {"sxf",  PSR_s | PSR_x | PSR_f},
+  {"sxc",  PSR_s | PSR_x | PSR_c},
+  {"scf",  PSR_s | PSR_c | PSR_f},
+  {"scx",  PSR_s | PSR_c | PSR_x},
+  {"xfs",  PSR_x | PSR_f | PSR_s},
+  {"xfc",  PSR_x | PSR_f | PSR_c},
+  {"xsf",  PSR_x | PSR_s | PSR_f},
+  {"xsc",  PSR_x | PSR_s | PSR_c},
+  {"xcf",  PSR_x | PSR_c | PSR_f},
+  {"xcs",  PSR_x | PSR_c | PSR_s},
+  {"cfs",  PSR_c | PSR_f | PSR_s},
+  {"cfx",  PSR_c | PSR_f | PSR_x},
+  {"csf",  PSR_c | PSR_s | PSR_f},
+  {"csx",  PSR_c | PSR_s | PSR_x},
+  {"cxf",  PSR_c | PSR_x | PSR_f},
+  {"cxs",  PSR_c | PSR_x | PSR_s},
+  {"fsxc", PSR_f | PSR_s | PSR_x | PSR_c},
+  {"fscx", PSR_f | PSR_s | PSR_c | PSR_x},
+  {"fxsc", PSR_f | PSR_x | PSR_s | PSR_c},
+  {"fxcs", PSR_f | PSR_x | PSR_c | PSR_s},
+  {"fcsx", PSR_f | PSR_c | PSR_s | PSR_x},
+  {"fcxs", PSR_f | PSR_c | PSR_x | PSR_s},
+  {"sfxc", PSR_s | PSR_f | PSR_x | PSR_c},
+  {"sfcx", PSR_s | PSR_f | PSR_c | PSR_x},
+  {"sxfc", PSR_s | PSR_x | PSR_f | PSR_c},
+  {"sxcf", PSR_s | PSR_x | PSR_c | PSR_f},
+  {"scfx", PSR_s | PSR_c | PSR_f | PSR_x},
+  {"scxf", PSR_s | PSR_c | PSR_x | PSR_f},
+  {"xfsc", PSR_x | PSR_f | PSR_s | PSR_c},
+  {"xfcs", PSR_x | PSR_f | PSR_c | PSR_s},
+  {"xsfc", PSR_x | PSR_s | PSR_f | PSR_c},
+  {"xscf", PSR_x | PSR_s | PSR_c | PSR_f},
+  {"xcfs", PSR_x | PSR_c | PSR_f | PSR_s},
+  {"xcsf", PSR_x | PSR_c | PSR_s | PSR_f},
+  {"cfsx", PSR_c | PSR_f | PSR_s | PSR_x},
+  {"cfxs", PSR_c | PSR_f | PSR_x | PSR_s},
+  {"csfx", PSR_c | PSR_s | PSR_f | PSR_x},
+  {"csxf", PSR_c | PSR_s | PSR_x | PSR_f},
+  {"cxfs", PSR_c | PSR_x | PSR_f | PSR_s},
+  {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
+};
 
 
-  skip_whitespace (p);
+/* Table of all shift-in-operand names.         */
+static const struct asm_shift_name shift_names [] =
+{
+  { "asl", SHIFT_LSL },         { "ASL", SHIFT_LSL },
+  { "lsl", SHIFT_LSL },         { "LSL", SHIFT_LSL },
+  { "lsr", SHIFT_LSR },         { "LSR", SHIFT_LSR },
+  { "asr", SHIFT_ASR },         { "ASR", SHIFT_ASR },
+  { "ror", SHIFT_ROR },         { "ROR", SHIFT_ROR },
+  { "rrx", SHIFT_RRX },         { "RRX", SHIFT_RRX }
+};
 
 
-  if (*p == '#')
-    ++p;
+/* Table of all explicit relocation names.  */
+#ifdef OBJ_ELF
+static struct reloc_entry reloc_names[] =
+{
+  { "got",     BFD_RELOC_ARM_GOT32   },         { "GOT",     BFD_RELOC_ARM_GOT32   },
+  { "gotoff",  BFD_RELOC_ARM_GOTOFF  },         { "GOTOFF",  BFD_RELOC_ARM_GOTOFF  },
+  { "plt",     BFD_RELOC_ARM_PLT32   },         { "PLT",     BFD_RELOC_ARM_PLT32   },
+  { "target1", BFD_RELOC_ARM_TARGET1 },         { "TARGET1", BFD_RELOC_ARM_TARGET1 },
+  { "target2", BFD_RELOC_ARM_TARGET2 },         { "TARGET2", BFD_RELOC_ARM_TARGET2 },
+  { "sbrel",   BFD_RELOC_ARM_SBREL32 },         { "SBREL",   BFD_RELOC_ARM_SBREL32 },
+  { "tlsgd",   BFD_RELOC_ARM_TLS_GD32},  { "TLSGD",   BFD_RELOC_ARM_TLS_GD32},
+  { "tlsldm",  BFD_RELOC_ARM_TLS_LDM32}, { "TLSLDM",  BFD_RELOC_ARM_TLS_LDM32},
+  { "tlsldo",  BFD_RELOC_ARM_TLS_LDO32}, { "TLSLDO",  BFD_RELOC_ARM_TLS_LDO32},
+  { "gottpoff",BFD_RELOC_ARM_TLS_IE32},  { "GOTTPOFF",BFD_RELOC_ARM_TLS_IE32},
+  { "tpoff",   BFD_RELOC_ARM_TLS_LE32},  { "TPOFF",   BFD_RELOC_ARM_TLS_LE32}
+};
+#endif
 
 
-  if (*p == '-')
-    {
-      *negative = 1;
-      ++p;
-    }
+/* Table of all conditional affixes.  0xF is not defined as a condition code.  */
+static const struct asm_cond conds[] =
+{
+  {"eq", 0x0},
+  {"ne", 0x1},
+  {"cs", 0x2}, {"hs", 0x2},
+  {"cc", 0x3}, {"ul", 0x3}, {"lo", 0x3},
+  {"mi", 0x4},
+  {"pl", 0x5},
+  {"vs", 0x6},
+  {"vc", 0x7},
+  {"hi", 0x8},
+  {"ls", 0x9},
+  {"ge", 0xa},
+  {"lt", 0xb},
+  {"gt", 0xc},
+  {"le", 0xd},
+  {"al", 0xe}
+};
 
 
-  if (!ISDIGIT (*p))
-    {
-      inst.error = _("offset expected");
-      return 0;
-    }
+/* Table of ARM-format instructions.   */
+
+/* Macros for gluing together operand strings.  N.B. In all cases
+   other than OPS0, the trailing OP_stop comes from default
+   zero-initialization of the unspecified elements of the array.  */
+#define OPS0()           { OP_stop, }
+#define OPS1(a)                  { OP_##a, }
+#define OPS2(a,b)        { OP_##a,OP_##b, }
+#define OPS3(a,b,c)      { OP_##a,OP_##b,OP_##c, }
+#define OPS4(a,b,c,d)    { OP_##a,OP_##b,OP_##c,OP_##d, }
+#define OPS5(a,b,c,d,e)          { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e, }
+#define OPS6(a,b,c,d,e,f) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e,OP_##f, }
+
+/* These macros abstract out the exact format of the mnemonic table and
+   save some repeated characters.  */
+
+/* The normal sort of mnemonic; has a Thumb variant; takes a conditional suffix.  */
+#define TxCE(mnem, op, top, nops, ops, ae, te) \
+  { #mnem, OPS##nops ops, OT_csuffix, 0x##op, top, ARM_VARIANT, \
+    THUMB_VARIANT, do_##ae, do_##te }
+
+/* Two variants of the above - TCE for a numeric Thumb opcode, tCE for
+   a T_MNEM_xyz enumerator.  */
+#define TCE(mnem, aop, top, nops, ops, ae, te) \
+       TxCE(mnem, aop, 0x##top, nops, ops, ae, te)
+#define tCE(mnem, aop, top, nops, ops, ae, te) \
+       TxCE(mnem, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Second most common sort of mnemonic: has a Thumb variant, takes a conditional
+   infix after the third character.  */
+#define TxC3(mnem, op, top, nops, ops, ae, te) \
+  { #mnem, OPS##nops ops, OT_cinfix3, 0x##op, top, ARM_VARIANT, \
+    THUMB_VARIANT, do_##ae, do_##te }
+#define TC3(mnem, aop, top, nops, ops, ae, te) \
+       TxC3(mnem, aop, 0x##top, nops, ops, ae, te)
+#define tC3(mnem, aop, top, nops, ops, ae, te) \
+       TxC3(mnem, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Mnemonic with a conditional infix in an unusual place.  Each and every variant has to
+   appear in the condition table.  */
+#define TxCM_(m1, m2, m3, op, top, nops, ops, ae, te)  \
+  { #m1 #m2 #m3, OPS##nops ops, sizeof(#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof(#m1) - 1, \
+    0x##op, top, ARM_VARIANT, THUMB_VARIANT, do_##ae, do_##te }
+
+#define TxCM(m1, m2, op, top, nops, ops, ae, te)       \
+  TxCM_(m1,   , m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, eq, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, ne, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, cs, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, hs, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, cc, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, ul, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, lo, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, mi, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, pl, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, vs, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, vc, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, hi, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, ls, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, ge, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, lt, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, gt, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, le, m2, op, top, nops, ops, ae, te),       \
+  TxCM_(m1, al, m2, op, top, nops, ops, ae, te)
+
+#define TCM(m1,m2, aop, top, nops, ops, ae, te)                \
+       TxCM(m1,m2, aop, 0x##top, nops, ops, ae, te)
+#define tCM(m1,m2, aop, top, nops, ops, ae, te)                        \
+       TxCM(m1,m2, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Mnemonic that cannot be conditionalized.  The ARM condition-code
+   field is still 0xE.  */
+#define TUE(mnem, op, top, nops, ops, ae, te)                          \
+  { #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
+    THUMB_VARIANT, do_##ae, do_##te }
+
+/* Mnemonic that cannot be conditionalized, and bears 0xF in its ARM
+   condition code field.  */
+#define TUF(mnem, op, top, nops, ops, ae, te)                          \
+  { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0x##top, ARM_VARIANT, \
+    THUMB_VARIANT, do_##ae, do_##te }
+
+/* ARM-only variants of all the above.  */
+#define CE(mnem,  op, nops, ops, ae)   \
+  { #mnem, OPS##nops ops, OT_csuffix, 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define C3(mnem, op, nops, ops, ae)    \
+  { #mnem, OPS##nops ops, OT_cinfix3, 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define xCM_(m1, m2, m3, op, nops, ops, ae)    \
+  { #m1 #m2 #m3, OPS##nops ops, \
+    sizeof(#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof(#m1) - 1, \
+    0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define CM(m1, m2, op, nops, ops, ae)  \
+  xCM_(m1,   , m2, op, nops, ops, ae), \
+  xCM_(m1, eq, m2, op, nops, ops, ae), \
+  xCM_(m1, ne, m2, op, nops, ops, ae), \
+  xCM_(m1, cs, m2, op, nops, ops, ae), \
+  xCM_(m1, hs, m2, op, nops, ops, ae), \
+  xCM_(m1, cc, m2, op, nops, ops, ae), \
+  xCM_(m1, ul, m2, op, nops, ops, ae), \
+  xCM_(m1, lo, m2, op, nops, ops, ae), \
+  xCM_(m1, mi, m2, op, nops, ops, ae), \
+  xCM_(m1, pl, m2, op, nops, ops, ae), \
+  xCM_(m1, vs, m2, op, nops, ops, ae), \
+  xCM_(m1, vc, m2, op, nops, ops, ae), \
+  xCM_(m1, hi, m2, op, nops, ops, ae), \
+  xCM_(m1, ls, m2, op, nops, ops, ae), \
+  xCM_(m1, ge, m2, op, nops, ops, ae), \
+  xCM_(m1, lt, m2, op, nops, ops, ae), \
+  xCM_(m1, gt, m2, op, nops, ops, ae), \
+  xCM_(m1, le, m2, op, nops, ops, ae), \
+  xCM_(m1, al, m2, op, nops, ops, ae)
+
+#define UE(mnem, op, nops, ops, ae)    \
+  { #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define UF(mnem, op, nops, ops, ae)    \
+  { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define do_0 0
+
+/* Thumb-only, unconditional.  */
+#define UT(mnem,  op, nops, ops, te) TUE(mnem,  0, op, nops, ops, 0, te)
+
+/* ARM-only, takes either a suffix or a position-3 infix
+   (for an FPA corner case). */
+#define C3E(mnem, op, nops, ops, ae) \
+  { #mnem, OPS##nops ops, OT_csuf_or_in3, 0x##op, 0, ARM_VARIANT, 0, do_##ae, 0 }
 
 
-  for (offset = 0; *p && ISDIGIT (*p); ++p)
-    offset = offset * 10 + *p - '0';
+static const struct asm_opcode insns[] =
+{
+#define ARM_VARIANT ARM_EXT_V1 /* Core ARM Instructions.  */
+#define THUMB_VARIANT ARM_EXT_V4T
+ tCE(and,      0000000, and,      3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(ands,     0100000, ands,     3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(eor,      0200000, eor,      3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(eors,     0300000, eors,     3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(sub,      0400000, sub,      3, (RR, oRR, SH), arit, t_add_sub),
+ tC3(subs,     0500000, subs,     3, (RR, oRR, SH), arit, t_add_sub),
+ tCE(add,      0800000, add,      3, (RR, oRR, SH), arit, t_add_sub),
+ tC3(adds,     0900000, adds,     3, (RR, oRR, SH), arit, t_add_sub),
+ tCE(adc,      0a00000, adc,      3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(adcs,     0b00000, adcs,     3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(sbc,      0c00000, sbc,      3, (RR, oRR, SH), arit, t_arit3),
+ tC3(sbcs,     0d00000, sbcs,     3, (RR, oRR, SH), arit, t_arit3),
+ tCE(orr,      1800000, orr,      3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(orrs,     1900000, orrs,     3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(bic,      1c00000, bic,      3, (RR, oRR, SH), arit, t_arit3),
+ tC3(bics,     1d00000, bics,     3, (RR, oRR, SH), arit, t_arit3),
+
+ /* The p-variants of tst/cmp/cmn/teq (below) are the pre-V6 mechanism
+    for setting PSR flag bits.  They are obsolete in V6 and do not
+    have Thumb equivalents. */
+ tCE(tst,      1100000, tst,      2, (RR, SH),      cmp,  t_mvn_tst),
+ tC3(tsts,     1100000, tst,      2, (RR, SH),      cmp,  t_mvn_tst),
+  C3(tstp,     110f000,           2, (RR, SH),      cmp),
+ tCE(cmp,      1500000, cmp,      2, (RR, SH),      cmp,  t_mov_cmp),
+ tC3(cmps,     1500000, cmp,      2, (RR, SH),      cmp,  t_mov_cmp),
+  C3(cmpp,     150f000,           2, (RR, SH),      cmp),
+ tCE(cmn,      1700000, cmn,      2, (RR, SH),      cmp,  t_mvn_tst),
+ tC3(cmns,     1700000, cmn,      2, (RR, SH),      cmp,  t_mvn_tst),
+  C3(cmnp,     170f000,           2, (RR, SH),      cmp),
+
+ tCE(mov,      1a00000, mov,      2, (RR, SH),      mov,  t_mov_cmp),
+ tC3(movs,     1b00000, movs,     2, (RR, SH),      mov,  t_mov_cmp),
+ tCE(mvn,      1e00000, mvn,      2, (RR, SH),      mov,  t_mvn_tst),
+ tC3(mvns,     1f00000, mvns,     2, (RR, SH),      mov,  t_mvn_tst),
+
+ tCE(ldr,      4100000, ldr,      2, (RR, ADDR),    ldst, t_ldst),
+ tC3(ldrb,     4500000, ldrb,     2, (RR, ADDR),    ldst, t_ldst),
+ tCE(str,      4000000, str,      2, (RR, ADDR),    ldst, t_ldst),
+ tC3(strb,     4400000, strb,     2, (RR, ADDR),    ldst, t_ldst),
+
+ tC3(stmia,    8800000, stmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(stmea,    8800000, stmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(ldmia,    8900000, ldmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(ldmfd,    8900000, ldmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TCE(swi,      f000000, df00,     1, (EXPi),        swi, t_swi),
+ TCE(b,                a000000, e000,     1, (EXPr),        branch, t_branch),
+ TCE(bl,       b000000, f000f800, 1, (EXPr),        branch, t_branch23),
 
 
-  if (offset > 0xff)
-    {
-      inst.error = _("offset out of range");
-      return 0;
-    }
+  /* Pseudo ops.  */
+ tCE(adr,      28f0000, adr,      2, (RR, EXP),     adr,  t_adr),
+  C3(adrl,     28f0000,           2, (RR, EXP),     adrl),
+ tCE(nop,      1a00000, nop,      1, (oI255c),      nop,  t_nop),
+
+  /* Thumb-compatibility pseudo ops.  */
+ tCE(lsl,      1a00000, lsl,      3, (RR, oRR, SH), shift, t_shift),
+ tC3(lsls,     1b00000, lsls,     3, (RR, oRR, SH), shift, t_shift),
+ tCE(lsr,      1a00020, lsr,      3, (RR, oRR, SH), shift, t_shift),
+ tC3(lsrs,     1b00020, lsrs,     3, (RR, oRR, SH), shift, t_shift),
+ tCE(asr,      1a00040, asr,      3, (RR, oRR, SH), shift, t_shift),
+ tC3(asrs,      1b00040, asrs,     3, (RR, oRR, SH), shift, t_shift),
+ tCE(ror,      1a00060, ror,      3, (RR, oRR, SH), shift, t_shift),
+ tC3(rors,     1b00060, rors,     3, (RR, oRR, SH), shift, t_shift),
+ tCE(neg,      2600000, neg,      2, (RR, RR),      rd_rn, t_neg),
+ tC3(negs,     2700000, negs,     2, (RR, RR),      rd_rn, t_neg),
+ tCE(push,     92d0000, push,     1, (REGLST),      push_pop, t_push_pop),
+ tCE(pop,      8bd0000, pop,      1, (REGLST),      push_pop, t_push_pop),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6
+ TCE(cpy,       1a00000, 4600,     2, (RR, RR),      rd_rm, t_cpy),
+
+ /* V1 instructions with no Thumb analogue prior to V6T2.  */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(rsb,      0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TC3(rsbs,     0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TCE(teq,      1300000, ea900f00, 2, (RR, SH),      cmp,  t_mvn_tst),
+ TC3(teqs,     1300000, ea900f00, 2, (RR, SH),      cmp,  t_mvn_tst),
+  C3(teqp,     130f000,           2, (RR, SH),      cmp),
+
+ TC3(ldrt,     4300000, f8500e00, 2, (RR, ADDR),    ldstt, t_ldstt),
+ TC3(ldrbt,    4700000, f8300e00, 2, (RR, ADDR),    ldstt, t_ldstt),
+ TC3(strt,     4200000, f8400e00, 2, (RR, ADDR),    ldstt, t_ldstt),
+ TC3(strbt,    4600000, f8200e00, 2, (RR, ADDR),    ldstt, t_ldstt),
+
+ TC3(stmdb,    9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(stmfd,     9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TC3(ldmdb,    9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(ldmea,    9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ /* V1 instructions with no Thumb analogue at all.  */
+  CE(rsc,      0e00000,           3, (RR, oRR, SH), arit),
+  C3(rscs,     0f00000,           3, (RR, oRR, SH), arit),
+
+  C3(stmib,    9800000,           2, (RRw, REGLST), ldmstm),
+  C3(stmfa,    9800000,           2, (RRw, REGLST), ldmstm),
+  C3(stmda,    8000000,           2, (RRw, REGLST), ldmstm),
+  C3(stmed,    8000000,           2, (RRw, REGLST), ldmstm),
+  C3(ldmib,    9900000,           2, (RRw, REGLST), ldmstm),
+  C3(ldmed,    9900000,           2, (RRw, REGLST), ldmstm),
+  C3(ldmda,    8100000,           2, (RRw, REGLST), ldmstm),
+  C3(ldmfa,    8100000,           2, (RRw, REGLST), ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V2 /* ARM 2 - multiplies.  */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V4T
+ tCE(mul,      0000090, mul,      3, (RRnpc, RRnpc, oRR), mul, t_mul),
+ tC3(muls,     0100090, muls,     3, (RRnpc, RRnpc, oRR), mul, t_mul),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(mla,      0200090, fb000000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+  C3(mlas,     0300090,           4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas),
+
+  /* Generic coprocessor instructions. */
+ TCE(cdp,      e000000, ee000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp,    cdp),
+ TCE(ldc,      c100000, ec100000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TC3(ldcl,     c500000, ec500000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TCE(stc,      c000000, ec000000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TC3(stcl,     c400000, ec400000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TCE(mcr,      e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
+ TCE(mrc,      e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V2S /* ARM 3 - swp instructions.  */
+  CE(swp,      1000090,           3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+  C3(swpb,     1400090,           3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V3 /* ARM 6 Status register instructions.  */
+ TCE(mrs,      10f0000, f3ef8000, 2, (RR, PSR),     mrs, t_mrs),
+ TCE(msr,      120f000, f3808000, 2, (PSR, RR_EXi), msr, t_msr),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V3M         /* ARM 7M long multiplies.  */
+ TCE(smull,    0c00090, fb800000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+  CM(smull,s,  0d00090,           4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(umull,    0800090, fba00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+  CM(umull,s,  0900090,           4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(smlal,    0e00090, fbc00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+  CM(smlal,s,  0f00090,           4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(umlal,    0a00090, fbe00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+  CM(umlal,s,  0b00090,           4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V4 /* ARM Architecture 4.  */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V4T
+ tC3(ldrh,     01000b0, ldrh,     2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(strh,     00000b0, strh,     2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(ldrsh,    01000f0, ldrsh,    2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(ldrsb,    01000d0, ldrsb,    2, (RR, ADDR), ldstv4, t_ldst),
+ tCM(ld,sh,    01000f0, ldrsh,    2, (RR, ADDR), ldstv4, t_ldst),
+ tCM(ld,sb,    01000d0, ldrsb,    2, (RR, ADDR), ldstv4, t_ldst),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V4T|ARM_EXT_V5
+  /* ARM Architecture 4T.  */
+  /* Note: bx (and blx) are required on V5, even if the processor does
+     not support Thumb.         */
+ TCE(bx,       12fff10, 4700, 1, (RR), bx, t_bx),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5 /*  ARM Architecture 5T.         */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V5T
+  /* Note: blx has 2 variants; the .value coded here is for
+     BLX(2).  Only this variant has conditional execution.  */
+ TCE(blx,      12fff30, 4780, 1, (RR_EXr),                         blx,  t_blx),
+ TUE(bkpt,     1200070, be00, 1, (oIffffb),                        bkpt, t_bkpt),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(clz,      16f0f10, fab0f080, 2, (RRnpc, RRnpc),                   rd_rm,  t_clz),
+ TUF(ldc2,     c100000, fc100000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TUF(ldc2l,    c500000, fc500000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TUF(stc2,     c000000, fc000000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TUF(stc2l,    c400000, fc400000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TUF(cdp2,     e000000, fe000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp,    cdp),
+ TUF(mcr2,     e000010, fe000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
+ TUF(mrc2,     e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5ExP /*  ARM Architecture 5TExP.  */
+ TCE(smlabb,   1000080, fb100000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+ TCE(smlatb,   10000a0, fb100020, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+ TCE(smlabt,   10000c0, fb100010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+ TCE(smlatt,   10000e0, fb100030, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+
+ TCE(smlawb,   1200080, fb300000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+ TCE(smlawt,   12000c0, fb300010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smla, t_mla),
+
+ TCE(smlalbb,  1400080, fbc00080, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smlal, t_mlal),
+ TCE(smlaltb,  14000a0, fbc000a0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smlal, t_mlal),
+ TCE(smlalbt,  14000c0, fbc00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smlal, t_mlal),
+ TCE(smlaltt,  14000e0, fbc000b0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),   smlal, t_mlal),
+
+ TCE(smulbb,   1600080, fb10f000, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+ TCE(smultb,   16000a0, fb10f020, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+ TCE(smulbt,   16000c0, fb10f010, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+ TCE(smultt,   16000e0, fb10f030, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+
+ TCE(smulwb,   12000a0, fb30f000, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+ TCE(smulwt,   12000e0, fb30f010, 3, (RRnpc, RRnpc, RRnpc),        smul, t_simd),
+
+ TCE(qadd,     1000050, fa80f080, 3, (RRnpc, RRnpc, RRnpc),        rd_rm_rn, rd_rm_rn),
+ TCE(qdadd,    1400050, fa80f090, 3, (RRnpc, RRnpc, RRnpc),        rd_rm_rn, rd_rm_rn),
+ TCE(qsub,     1200050, fa80f0a0, 3, (RRnpc, RRnpc, RRnpc),        rd_rm_rn, rd_rm_rn),
+ TCE(qdsub,    1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc),        rd_rm_rn, rd_rm_rn),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5E /*  ARM Architecture 5TE.  */
+ TUF(pld,      450f000, f810f000, 1, (ADDR),                pld,  t_pld),
+ TC3(ldrd,     00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
+ TC3(strd,     00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
+
+ TCE(mcrr,     c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TCE(mrrc,     c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5J /*  ARM Architecture 5TEJ.  */
+ TCE(bxj,      12fff20, f3c08f00, 1, (RR),                       bxj, t_bxj),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6 /*  ARM V6.  */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6
+ TUF(cpsie,     1080000, b660,     2, (CPSF, oI31b),              cpsi,   t_cpsi),
+ TUF(cpsid,     10c0000, b670,     2, (CPSF, oI31b),              cpsi,   t_cpsi),
+ tCE(rev,       6bf0f30, rev,      2, (RRnpc, RRnpc),             rd_rm,  t_rev),
+ tCE(rev16,     6bf0fb0, rev16,    2, (RRnpc, RRnpc),             rd_rm,  t_rev),
+ tCE(revsh,     6ff0fb0, revsh,    2, (RRnpc, RRnpc),             rd_rm,  t_rev),
+ tCE(sxth,      6bf0070, sxth,     3, (RRnpc, RRnpc, oROR),       sxth,   t_sxth),
+ tCE(uxth,      6ff0070, uxth,     3, (RRnpc, RRnpc, oROR),       sxth,   t_sxth),
+ tCE(sxtb,      6af0070, sxtb,     3, (RRnpc, RRnpc, oROR),       sxth,   t_sxth),
+ tCE(uxtb,      6ef0070, uxtb,     3, (RRnpc, RRnpc, oROR),       sxth,   t_sxth),
+ TUF(setend,    1010000, b650,     1, (ENDI),                     setend, t_setend),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TUF(cps,      1020000, f3af8100, 1, (I31b),                     imm0, imm0),
+ TCE(ldrex,    1900f9f, e8500f00, 2, (RRnpc, ADDR),              ldrex, t_ldrex),
+ TUF(mcrr2,    c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TUF(mrrc2,    c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TCE(pkhbt,    6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll),   pkhbt, t_pkhbt),
+ TCE(pkhtb,    6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar),   pkhtb, t_pkhtb),
+ TCE(qadd16,   6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(qadd8,    6200f90, fa80f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(qaddsubx, 6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(qsub16,   6200f70, fad0f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(qsub8,    6200ff0, fac0f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(qsubaddx, 6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(sadd16,   6100f10, fa90f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(sadd8,    6100f90, fa80f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(saddsubx, 6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(shadd16,  6300f10, fa90f020, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(shadd8,   6300f90, fa80f020, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(shaddsubx, 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(shsub16,  6300f70, fad0f020, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(shsub8,   6300ff0, fac0f020, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(shsubaddx, 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(ssub16,   6100f70, fad0f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(ssub8,    6100ff0, fac0f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(ssubaddx, 6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uadd16,   6500f10, fa90f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uadd8,    6500f90, fa80f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uaddsubx, 6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uhadd16,  6700f10, fa90f060, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uhadd8,   6700f90, fa80f060, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uhaddsubx, 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(uhsub16,  6700f70, fad0f060, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uhsub8,   6700ff0, fac0f060, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uhsubaddx, 6700f50, fae0f060, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(uqadd16,  6600f10, fa90f050, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uqadd8,   6600f90, fa80f050, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uqaddsubx, 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(uqsub16,  6600f70, fad0f050, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uqsub8,   6600ff0, fac0f050, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(uqsubaddx, 6600f50, fae0f050, 3, (RRnpc, RRnpc, RRnpc),      rd_rn_rm, t_simd),
+ TCE(usub16,   6500f70, fad0f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(usub8,    6500ff0, fac0f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(usubaddx, 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TUF(rfeia,    8900a00, e990c000, 1, (RRw),                       rfe, rfe),
+  UF(rfeib,    9900a00,           1, (RRw),                       rfe),
+  UF(rfeda,    8100a00,           1, (RRw),                       rfe),
+ TUF(rfedb,    9100a00, e810c000, 1, (RRw),                       rfe, rfe),
+ TUF(rfefd,    8900a00, e990c000, 1, (RRw),                       rfe, rfe),
+  UF(rfefa,    9900a00,           1, (RRw),                       rfe),
+  UF(rfeea,    8100a00,           1, (RRw),                       rfe),
+ TUF(rfeed,    9100a00, e810c000, 1, (RRw),                       rfe, rfe),
+ TCE(sxtah,    6b00070, fa00f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtab16,  6800070, fa20f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtab,    6a00070, fa40f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtb16,   68f0070, fa2ff080, 3, (RRnpc, RRnpc, oROR),        sxth,  t_sxth),
+ TCE(uxtah,    6f00070, fa10f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtab16,  6c00070, fa30f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtab,    6e00070, fa50f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtb16,   6cf0070, fa3ff080, 3, (RRnpc, RRnpc, oROR),        sxth,  t_sxth),
+ TCE(sel,      68000b0, faa0f080, 3, (RRnpc, RRnpc, RRnpc),       rd_rn_rm, t_simd),
+ TCE(smlad,    7000010, fb200000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smladx,   7000030, fb200010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlald,   7400010, fbc000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlaldx,  7400030, fbc000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlsd,    7000050, fb400000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlsdx,   7000070, fb400010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlsld,   7400050, fbd000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlsldx,  7400070, fbd000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smmla,    7500010, fb500000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmlar,   7500030, fb500010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmls,    75000d0, fb600000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmlsr,   75000f0, fb600010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmul,    750f010, fb50f000, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TCE(smmulr,   750f030, fb50f010, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TCE(smuad,    700f010, fb20f000, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TCE(smuadx,   700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TCE(smusd,    700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TCE(smusdx,   700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc),       smul, t_simd),
+ TUF(srsia,    8cd0500, e980c000, 1, (I31w),                      srs,  srs),
+  UF(srsib,    9cd0500,           1, (I31w),                      srs),
+  UF(srsda,    84d0500,           1, (I31w),                      srs),
+ TUF(srsdb,    94d0500, e800c000, 1, (I31w),                      srs,  srs),
+ TCE(ssat,     6a00010, f3000000, 4, (RRnpc, I32, RRnpc, oSHllar),ssat,   t_ssat),
+ TCE(ssat16,   6a00f30, f3200000, 3, (RRnpc, I16, RRnpc),         ssat16, t_ssat16),
+ TCE(strex,    1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR),        strex,  t_strex),
+ TCE(umaal,    0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,  t_mlal),
+ TCE(usad8,    780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc),       smul,   t_simd),
+ TCE(usada8,   7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla,   t_mla),
+ TCE(usat,     6e00010, f3800000, 4, (RRnpc, I31, RRnpc, oSHllar),usat,   t_usat),
+ TCE(usat16,   6e00f30, f3a00000, 3, (RRnpc, I15, RRnpc),         usat16, t_usat16),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6K
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6K
+ tCE(yield,    320f001, yield,    0, (), noargs, t_hint),
+ tCE(wfe,      320f002, wfe,      0, (), noargs, t_hint),
+ tCE(wfi,      320f003, wfi,      0, (), noargs, t_hint),
+ tCE(sev,      320f004, sev,      0, (), noargs, t_hint),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(ldrexb,   1d00f9f, e8d00f4f, 2, (RRnpc, RRnpcb),                rd_rn,  rd_rn),
+ TCE(ldrexh,   1f00f9f, e8d00f5f, 2, (RRnpc, RRnpcb),                rd_rn,  rd_rn),
+ TCE(ldrexd,   1b00f9f, e8d0007f, 3, (RRnpc, oRRnpc, RRnpcb),        ldrexd, t_ldrexd),
+ TCE(strexb,   1c00f90, e8c00f40, 3, (RRnpc, RRnpc, ADDR),           strex,  rm_rd_rn),
+ TCE(strexh,   1e00f90, e8c00f50, 3, (RRnpc, RRnpc, ADDR),           strex,  rm_rd_rn),
+ TCE(strexd,   1a00f90, e8c00070, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb), strexd, t_strexd),
+ TUF(clrex,    57ff01f, f3bf8f2f, 0, (),                             noargs, noargs),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6Z
+ TCE(smi,      1600070, f7f08000, 1, (EXPi), smi, t_smi),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6T2
+ TCE(bfc,      7c0001f, f36f0000, 3, (RRnpc, I31, I32),           bfc, t_bfc),
+ TCE(bfi,      7c00010, f3600000, 4, (RRnpc, RRnpc_I0, I31, I32), bfi, t_bfi),
+ TCE(sbfx,     7a00050, f3400000, 4, (RR, RR, I31, I32),          bfx, t_bfx),
+ TCE(ubfx,     7e00050, f3c00000, 4, (RR, RR, I31, I32),          bfx, t_bfx),
+
+ TCE(mls,      0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+ TCE(movw,     3000000, f2400000, 2, (RRnpc, Iffff),               mov16, t_mov16),
+ TCE(movt,     3400000, f2c00000, 2, (RRnpc, Iffff),               mov16, t_mov16),
+ TCE(rbit,     3ff0f30, fa90f0a0, 2, (RR, RR),                     rd_rm, t_rbit),
+
+ TC3(ldrht,    03000b0, f8300e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(ldrsht,   03000f0, f9300e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(ldrsbt,   03000d0, f9100e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(strht,    02000b0, f8200e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+
+  UT(cbnz,      b900,    2, (RR, EXP), t_czb),
+  UT(cbz,       b100,    2, (RR, EXP), t_czb),
+ /* ARM does not really have an IT instruction.  */
+ TUE(it,        0, bf08, 1, (COND),    it, t_it),
+ TUE(itt,       0, bf0c, 1, (COND),    it, t_it),
+ TUE(ite,       0, bf04, 1, (COND),    it, t_it),
+ TUE(ittt,      0, bf0e, 1, (COND),    it, t_it),
+ TUE(itet,      0, bf06, 1, (COND),    it, t_it),
+ TUE(itte,      0, bf0a, 1, (COND),    it, t_it),
+ TUE(itee,      0, bf02, 1, (COND),    it, t_it),
+ TUE(itttt,     0, bf0f, 1, (COND),    it, t_it),
+ TUE(itett,     0, bf07, 1, (COND),    it, t_it),
+ TUE(ittet,     0, bf0b, 1, (COND),    it, t_it),
+ TUE(iteet,     0, bf03, 1, (COND),    it, t_it),
+ TUE(ittte,     0, bf0d, 1, (COND),    it, t_it),
+ TUE(itete,     0, bf05, 1, (COND),    it, t_it),
+ TUE(ittee,     0, bf09, 1, (COND),    it, t_it),
+ TUE(iteee,     0, bf01, 1, (COND),    it, t_it),
+
+ /* Thumb2 only instructions.  */
+#undef ARM_VARIANT
+#define ARM_VARIANT 0
+
+ TCE(addw,     0, f2000000, 3, (RR, RR, EXPi), 0, t_add_sub_w),
+ TCE(subw,     0, f2a00000, 3, (RR, RR, EXPi), 0, t_add_sub_w),
+ TCE(tbb,       0, e8d0f000, 1, (TB), 0, t_tb),
+ TCE(tbh,       0, e8d0f010, 1, (TB), 0, t_tb),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_FPA_EXT_V1  /* Core FPA instruction set (V1).  */
+  CE(wfs,      e200110, 1, (RR),            rd),
+  CE(rfs,      e300110, 1, (RR),            rd),
+  CE(wfc,      e400110, 1, (RR),            rd),
+  CE(rfc,      e500110, 1, (RR),            rd),
+
+  C3(ldfs,     c100100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(ldfd,     c108100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(ldfe,     c500100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(ldfp,     c508100, 2, (RF, ADDR),      rd_cpaddr),
+
+  C3(stfs,     c000100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(stfd,     c008100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(stfe,     c400100, 2, (RF, ADDR),      rd_cpaddr),
+  C3(stfp,     c408100, 2, (RF, ADDR),      rd_cpaddr),
+
+  C3(mvfs,     e008100, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfsp,    e008120, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfsm,    e008140, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfsz,    e008160, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfd,     e008180, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfdp,    e0081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfdm,    e0081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfdz,    e0081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfe,     e088100, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfep,    e088120, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfem,    e088140, 2, (RF, RF_IF),     rd_rm),
+  C3(mvfez,    e088160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(mnfs,     e108100, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfsp,    e108120, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfsm,    e108140, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfsz,    e108160, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfd,     e108180, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfdp,    e1081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfdm,    e1081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfdz,    e1081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfe,     e188100, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfep,    e188120, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfem,    e188140, 2, (RF, RF_IF),     rd_rm),
+  C3(mnfez,    e188160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(abss,     e208100, 2, (RF, RF_IF),     rd_rm),
+  C3(abssp,    e208120, 2, (RF, RF_IF),     rd_rm),
+  C3(abssm,    e208140, 2, (RF, RF_IF),     rd_rm),
+  C3(abssz,    e208160, 2, (RF, RF_IF),     rd_rm),
+  C3(absd,     e208180, 2, (RF, RF_IF),     rd_rm),
+  C3(absdp,    e2081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(absdm,    e2081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(absdz,    e2081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(abse,     e288100, 2, (RF, RF_IF),     rd_rm),
+  C3(absep,    e288120, 2, (RF, RF_IF),     rd_rm),
+  C3(absem,    e288140, 2, (RF, RF_IF),     rd_rm),
+  C3(absez,    e288160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(rnds,     e308100, 2, (RF, RF_IF),     rd_rm),
+  C3(rndsp,    e308120, 2, (RF, RF_IF),     rd_rm),
+  C3(rndsm,    e308140, 2, (RF, RF_IF),     rd_rm),
+  C3(rndsz,    e308160, 2, (RF, RF_IF),     rd_rm),
+  C3(rndd,     e308180, 2, (RF, RF_IF),     rd_rm),
+  C3(rnddp,    e3081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(rnddm,    e3081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(rnddz,    e3081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(rnde,     e388100, 2, (RF, RF_IF),     rd_rm),
+  C3(rndep,    e388120, 2, (RF, RF_IF),     rd_rm),
+  C3(rndem,    e388140, 2, (RF, RF_IF),     rd_rm),
+  C3(rndez,    e388160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(sqts,     e408100, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtsp,    e408120, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtsm,    e408140, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtsz,    e408160, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtd,     e408180, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtdp,    e4081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtdm,    e4081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtdz,    e4081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(sqte,     e488100, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtep,    e488120, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtem,    e488140, 2, (RF, RF_IF),     rd_rm),
+  C3(sqtez,    e488160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(logs,     e508100, 2, (RF, RF_IF),     rd_rm),
+  C3(logsp,    e508120, 2, (RF, RF_IF),     rd_rm),
+  C3(logsm,    e508140, 2, (RF, RF_IF),     rd_rm),
+  C3(logsz,    e508160, 2, (RF, RF_IF),     rd_rm),
+  C3(logd,     e508180, 2, (RF, RF_IF),     rd_rm),
+  C3(logdp,    e5081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(logdm,    e5081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(logdz,    e5081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(loge,     e588100, 2, (RF, RF_IF),     rd_rm),
+  C3(logep,    e588120, 2, (RF, RF_IF),     rd_rm),
+  C3(logem,    e588140, 2, (RF, RF_IF),     rd_rm),
+  C3(logez,    e588160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(lgns,     e608100, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnsp,    e608120, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnsm,    e608140, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnsz,    e608160, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnd,     e608180, 2, (RF, RF_IF),     rd_rm),
+  C3(lgndp,    e6081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(lgndm,    e6081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(lgndz,    e6081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(lgne,     e688100, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnep,    e688120, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnem,    e688140, 2, (RF, RF_IF),     rd_rm),
+  C3(lgnez,    e688160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(exps,     e708100, 2, (RF, RF_IF),     rd_rm),
+  C3(expsp,    e708120, 2, (RF, RF_IF),     rd_rm),
+  C3(expsm,    e708140, 2, (RF, RF_IF),     rd_rm),
+  C3(expsz,    e708160, 2, (RF, RF_IF),     rd_rm),
+  C3(expd,     e708180, 2, (RF, RF_IF),     rd_rm),
+  C3(expdp,    e7081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(expdm,    e7081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(expdz,    e7081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(expe,     e788100, 2, (RF, RF_IF),     rd_rm),
+  C3(expep,    e788120, 2, (RF, RF_IF),     rd_rm),
+  C3(expem,    e788140, 2, (RF, RF_IF),     rd_rm),
+  C3(expdz,    e788160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(sins,     e808100, 2, (RF, RF_IF),     rd_rm),
+  C3(sinsp,    e808120, 2, (RF, RF_IF),     rd_rm),
+  C3(sinsm,    e808140, 2, (RF, RF_IF),     rd_rm),
+  C3(sinsz,    e808160, 2, (RF, RF_IF),     rd_rm),
+  C3(sind,     e808180, 2, (RF, RF_IF),     rd_rm),
+  C3(sindp,    e8081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(sindm,    e8081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(sindz,    e8081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(sine,     e888100, 2, (RF, RF_IF),     rd_rm),
+  C3(sinep,    e888120, 2, (RF, RF_IF),     rd_rm),
+  C3(sinem,    e888140, 2, (RF, RF_IF),     rd_rm),
+  C3(sinez,    e888160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(coss,     e908100, 2, (RF, RF_IF),     rd_rm),
+  C3(cossp,    e908120, 2, (RF, RF_IF),     rd_rm),
+  C3(cossm,    e908140, 2, (RF, RF_IF),     rd_rm),
+  C3(cossz,    e908160, 2, (RF, RF_IF),     rd_rm),
+  C3(cosd,     e908180, 2, (RF, RF_IF),     rd_rm),
+  C3(cosdp,    e9081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(cosdm,    e9081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(cosdz,    e9081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(cose,     e988100, 2, (RF, RF_IF),     rd_rm),
+  C3(cosep,    e988120, 2, (RF, RF_IF),     rd_rm),
+  C3(cosem,    e988140, 2, (RF, RF_IF),     rd_rm),
+  C3(cosez,    e988160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(tans,     ea08100, 2, (RF, RF_IF),     rd_rm),
+  C3(tansp,    ea08120, 2, (RF, RF_IF),     rd_rm),
+  C3(tansm,    ea08140, 2, (RF, RF_IF),     rd_rm),
+  C3(tansz,    ea08160, 2, (RF, RF_IF),     rd_rm),
+  C3(tand,     ea08180, 2, (RF, RF_IF),     rd_rm),
+  C3(tandp,    ea081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(tandm,    ea081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(tandz,    ea081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(tane,     ea88100, 2, (RF, RF_IF),     rd_rm),
+  C3(tanep,    ea88120, 2, (RF, RF_IF),     rd_rm),
+  C3(tanem,    ea88140, 2, (RF, RF_IF),     rd_rm),
+  C3(tanez,    ea88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(asns,     eb08100, 2, (RF, RF_IF),     rd_rm),
+  C3(asnsp,    eb08120, 2, (RF, RF_IF),     rd_rm),
+  C3(asnsm,    eb08140, 2, (RF, RF_IF),     rd_rm),
+  C3(asnsz,    eb08160, 2, (RF, RF_IF),     rd_rm),
+  C3(asnd,     eb08180, 2, (RF, RF_IF),     rd_rm),
+  C3(asndp,    eb081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(asndm,    eb081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(asndz,    eb081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(asne,     eb88100, 2, (RF, RF_IF),     rd_rm),
+  C3(asnep,    eb88120, 2, (RF, RF_IF),     rd_rm),
+  C3(asnem,    eb88140, 2, (RF, RF_IF),     rd_rm),
+  C3(asnez,    eb88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(acss,     ec08100, 2, (RF, RF_IF),     rd_rm),
+  C3(acssp,    ec08120, 2, (RF, RF_IF),     rd_rm),
+  C3(acssm,    ec08140, 2, (RF, RF_IF),     rd_rm),
+  C3(acssz,    ec08160, 2, (RF, RF_IF),     rd_rm),
+  C3(acsd,     ec08180, 2, (RF, RF_IF),     rd_rm),
+  C3(acsdp,    ec081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(acsdm,    ec081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(acsdz,    ec081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(acse,     ec88100, 2, (RF, RF_IF),     rd_rm),
+  C3(acsep,    ec88120, 2, (RF, RF_IF),     rd_rm),
+  C3(acsem,    ec88140, 2, (RF, RF_IF),     rd_rm),
+  C3(acsez,    ec88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(atns,     ed08100, 2, (RF, RF_IF),     rd_rm),
+  C3(atnsp,    ed08120, 2, (RF, RF_IF),     rd_rm),
+  C3(atnsm,    ed08140, 2, (RF, RF_IF),     rd_rm),
+  C3(atnsz,    ed08160, 2, (RF, RF_IF),     rd_rm),
+  C3(atnd,     ed08180, 2, (RF, RF_IF),     rd_rm),
+  C3(atndp,    ed081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(atndm,    ed081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(atndz,    ed081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(atne,     ed88100, 2, (RF, RF_IF),     rd_rm),
+  C3(atnep,    ed88120, 2, (RF, RF_IF),     rd_rm),
+  C3(atnem,    ed88140, 2, (RF, RF_IF),     rd_rm),
+  C3(atnez,    ed88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(urds,     ee08100, 2, (RF, RF_IF),     rd_rm),
+  C3(urdsp,    ee08120, 2, (RF, RF_IF),     rd_rm),
+  C3(urdsm,    ee08140, 2, (RF, RF_IF),     rd_rm),
+  C3(urdsz,    ee08160, 2, (RF, RF_IF),     rd_rm),
+  C3(urdd,     ee08180, 2, (RF, RF_IF),     rd_rm),
+  C3(urddp,    ee081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(urddm,    ee081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(urddz,    ee081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(urde,     ee88100, 2, (RF, RF_IF),     rd_rm),
+  C3(urdep,    ee88120, 2, (RF, RF_IF),     rd_rm),
+  C3(urdem,    ee88140, 2, (RF, RF_IF),     rd_rm),
+  C3(urdez,    ee88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(nrms,     ef08100, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmsp,    ef08120, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmsm,    ef08140, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmsz,    ef08160, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmd,     ef08180, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmdp,    ef081a0, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmdm,    ef081c0, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmdz,    ef081e0, 2, (RF, RF_IF),     rd_rm),
+  C3(nrme,     ef88100, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmep,    ef88120, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmem,    ef88140, 2, (RF, RF_IF),     rd_rm),
+  C3(nrmez,    ef88160, 2, (RF, RF_IF),     rd_rm),
+
+  C3(adfs,     e000100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfsp,    e000120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfsm,    e000140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfsz,    e000160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfd,     e000180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfdp,    e0001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfdm,    e0001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfdz,    e0001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfe,     e080100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfep,    e080120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfem,    e080140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(adfez,    e080160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(sufs,     e200100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufsp,    e200120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufsm,    e200140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufsz,    e200160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufd,     e200180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufdp,    e2001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufdm,    e2001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufdz,    e2001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufe,     e280100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufep,    e280120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufem,    e280140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(sufez,    e280160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(rsfs,     e300100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfsp,    e300120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfsm,    e300140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfsz,    e300160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfd,     e300180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfdp,    e3001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfdm,    e3001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfdz,    e3001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfe,     e380100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfep,    e380120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfem,    e380140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rsfez,    e380160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(mufs,     e100100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufsp,    e100120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufsm,    e100140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufsz,    e100160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufd,     e100180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufdp,    e1001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufdm,    e1001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufdz,    e1001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufe,     e180100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufep,    e180120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufem,    e180140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(mufez,    e180160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(dvfs,     e400100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfsp,    e400120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfsm,    e400140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfsz,    e400160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfd,     e400180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfdp,    e4001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfdm,    e4001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfdz,    e4001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfe,     e480100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfep,    e480120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfem,    e480140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(dvfez,    e480160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(rdfs,     e500100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfsp,    e500120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfsm,    e500140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfsz,    e500160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfd,     e500180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfdp,    e5001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfdm,    e5001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfdz,    e5001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfe,     e580100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfep,    e580120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfem,    e580140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rdfez,    e580160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(pows,     e600100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powsp,    e600120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powsm,    e600140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powsz,    e600160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powd,     e600180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powdp,    e6001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powdm,    e6001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powdz,    e6001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powe,     e680100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powep,    e680120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powem,    e680140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(powez,    e680160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(rpws,     e700100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwsp,    e700120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwsm,    e700140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwsz,    e700160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwd,     e700180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwdp,    e7001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwdm,    e7001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwdz,    e7001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwe,     e780100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwep,    e780120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwem,    e780140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rpwez,    e780160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(rmfs,     e800100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfsp,    e800120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfsm,    e800140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfsz,    e800160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfd,     e800180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfdp,    e8001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfdm,    e8001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfdz,    e8001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfe,     e880100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfep,    e880120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfem,    e880140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(rmfez,    e880160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(fmls,     e900100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlsp,    e900120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlsm,    e900140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlsz,    e900160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmld,     e900180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmldp,    e9001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmldm,    e9001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmldz,    e9001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmle,     e980100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlep,    e980120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlem,    e980140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fmlez,    e980160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(fdvs,     ea00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvsp,    ea00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvsm,    ea00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvsz,    ea00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvd,     ea00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvdp,    ea001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvdm,    ea001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvdz,    ea001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdve,     ea80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvep,    ea80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvem,    ea80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(fdvez,    ea80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(frds,     eb00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdsp,    eb00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdsm,    eb00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdsz,    eb00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdd,     eb00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frddp,    eb001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frddm,    eb001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frddz,    eb001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frde,     eb80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdep,    eb80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdem,    eb80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(frdez,    eb80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  C3(pols,     ec00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polsp,    ec00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polsm,    ec00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polsz,    ec00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(pold,     ec00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(poldp,    ec001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(poldm,    ec001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(poldz,    ec001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(pole,     ec80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polep,    ec80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polem,    ec80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+  C3(polez,    ec80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+  CE(cmf,      e90f110, 2, (RF, RF_IF),     fpa_cmp),
+ C3E(cmfe,     ed0f110, 2, (RF, RF_IF),     fpa_cmp),
+  CE(cnf,      eb0f110, 2, (RF, RF_IF),     fpa_cmp),
+ C3E(cnfe,     ef0f110, 2, (RF, RF_IF),     fpa_cmp),
+
+  C3(flts,     e000110, 2, (RF, RR),        rn_rd),
+  C3(fltsp,    e000130, 2, (RF, RR),        rn_rd),
+  C3(fltsm,    e000150, 2, (RF, RR),        rn_rd),
+  C3(fltsz,    e000170, 2, (RF, RR),        rn_rd),
+  C3(fltd,     e000190, 2, (RF, RR),        rn_rd),
+  C3(fltdp,    e0001b0, 2, (RF, RR),        rn_rd),
+  C3(fltdm,    e0001d0, 2, (RF, RR),        rn_rd),
+  C3(fltdz,    e0001f0, 2, (RF, RR),        rn_rd),
+  C3(flte,     e080110, 2, (RF, RR),        rn_rd),
+  C3(fltep,    e080130, 2, (RF, RR),        rn_rd),
+  C3(fltem,    e080150, 2, (RF, RR),        rn_rd),
+  C3(fltez,    e080170, 2, (RF, RR),        rn_rd),
 
 
-  *str = p;
+  /* The implementation of the FIX instruction is broken on some
+     assemblers, in that it accepts a precision specifier as well as a
+     rounding specifier, despite the fact that this is meaningless.
+     To be more compatible, we accept it as well, though of course it
+     does not set any bits.  */
+  CE(fix,      e100110, 2, (RR, RF),        rd_rm),
+  C3(fixp,     e100130, 2, (RR, RF),        rd_rm),
+  C3(fixm,     e100150, 2, (RR, RF),        rd_rm),
+  C3(fixz,     e100170, 2, (RR, RF),        rd_rm),
+  C3(fixsp,    e100130, 2, (RR, RF),        rd_rm),
+  C3(fixsm,    e100150, 2, (RR, RF),        rd_rm),
+  C3(fixsz,    e100170, 2, (RR, RF),        rd_rm),
+  C3(fixdp,    e100130, 2, (RR, RF),        rd_rm),
+  C3(fixdm,    e100150, 2, (RR, RF),        rd_rm),
+  C3(fixdz,    e100170, 2, (RR, RF),        rd_rm),
+  C3(fixep,    e100130, 2, (RR, RF),        rd_rm),
+  C3(fixem,    e100150, 2, (RR, RF),        rd_rm),
+  C3(fixez,    e100170, 2, (RR, RF),        rd_rm),
 
 
-  return *negative ? -offset : offset;
-}
+  /* Instructions that were new with the real FPA, call them V2.  */
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_FPA_EXT_V2
+  CE(lfm,      c100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+  C3(lfmfd,    c900200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+  C3(lfmea,    d100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+  CE(sfm,      c000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+  C3(sfmfd,    d000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+  C3(sfmea,    c800200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V1xD  /* VFP V1xD (single precision).  */
+  /* Moves and type conversions.  */
+  CE(fcpys,    eb00a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fmrs,     e100a10, 2, (RR, RVS),        vfp_reg_from_sp),
+  CE(fmsr,     e000a10, 2, (RVS, RR),        vfp_sp_from_reg),
+  CE(fmstat,   ef1fa10, 0, (),               noargs),
+  CE(fsitos,   eb80ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fuitos,   eb80a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(ftosis,   ebd0a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(ftosizs,  ebd0ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(ftouis,   ebc0a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(ftouizs,  ebc0ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fmrx,     ef00a10, 2, (RR, RVC),        rd_rn),
+  CE(fmxr,     ee00a10, 2, (RVC, RR),        rn_rd),
+
+  /* Memory operations.         */
+  CE(flds,     d100a00, 2, (RVS, ADDR),      vfp_sp_ldst),
+  CE(fsts,     d000a00, 2, (RVS, ADDR),      vfp_sp_ldst),
+  CE(fldmias,  c900a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
+  CE(fldmfds,  c900a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
+  CE(fldmdbs,  d300a00, 2, (RRw, VRSLST),    vfp_sp_ldstmdb),
+  CE(fldmeas,  d300a00, 2, (RRw, VRSLST),    vfp_sp_ldstmdb),
+  CE(fldmiax,  c900b00, 2, (RRw, VRDLST),    vfp_xp_ldstmia),
+  CE(fldmfdx,  c900b00, 2, (RRw, VRDLST),    vfp_xp_ldstmia),
+  CE(fldmdbx,  d300b00, 2, (RRw, VRDLST),    vfp_xp_ldstmdb),
+  CE(fldmeax,  d300b00, 2, (RRw, VRDLST),    vfp_xp_ldstmdb),
+  CE(fstmias,  c800a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
+  CE(fstmeas,  c800a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
+  CE(fstmdbs,  d200a00, 2, (RRw, VRSLST),    vfp_sp_ldstmdb),
+  CE(fstmfds,  d200a00, 2, (RRw, VRSLST),    vfp_sp_ldstmdb),
+  CE(fstmiax,  c800b00, 2, (RRw, VRDLST),    vfp_xp_ldstmia),
+  CE(fstmeax,  c800b00, 2, (RRw, VRDLST),    vfp_xp_ldstmia),
+  CE(fstmdbx,  d200b00, 2, (RRw, VRDLST),    vfp_xp_ldstmdb),
+  CE(fstmfdx,  d200b00, 2, (RRw, VRDLST),    vfp_xp_ldstmdb),
 
 
-/* Maverick load/store instructions.
-  <insn><cond> CRd,[Rn,<offset>]{!}.
-  <insn><cond> CRd,[Rn],<offset>.  */
+  /* Monadic operations.  */
+  CE(fabss,    eb00ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fnegs,    eb10a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fsqrts,   eb10ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+
+  /* Dyadic operations.         */
+  CE(fadds,    e300a00, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fsubs,    e300a40, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fmuls,    e200a00, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fdivs,    e800a00, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fmacs,    e000a00, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fmscs,    e100a00, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fnmuls,   e200a40, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fnmacs,   e000a40, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
+  CE(fnmscs,   e100a40, 3, (RVS, RVS, RVS),  vfp_sp_dyadic),
 
 
-static void
-do_mav_ldst (str, reg0)
-     char * str;
-     enum arm_reg_type reg0;
-{
-  int offset, negative;
+  /* Comparisons.  */
+  CE(fcmps,    eb40a40, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fcmpzs,   eb50a40, 1, (RVS),            vfp_sp_compare_z),
+  CE(fcmpes,   eb40ac0, 2, (RVS, RVS),       vfp_sp_monadic),
+  CE(fcmpezs,  eb50ac0, 1, (RVS),            vfp_sp_compare_z),
 
 
-  skip_whitespace (str);
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V1 /* VFP V1 (Double precision).  */
+  /* Moves and type conversions.  */
+  CE(fcpyd,    eb00b40, 2, (RVD, RVD),       rd_rm),
+  CE(fcvtds,   eb70ac0, 2, (RVD, RVS),       vfp_dp_sp_cvt),
+  CE(fcvtsd,   eb70bc0, 2, (RVS, RVD),       vfp_sp_dp_cvt),
+  CE(fmdhr,    e200b10, 2, (RVD, RR),        rn_rd),
+  CE(fmdlr,    e000b10, 2, (RVD, RR),        rn_rd),
+  CE(fmrdh,    e300b10, 2, (RR, RVD),        rd_rn),
+  CE(fmrdl,    e100b10, 2, (RR, RVD),        rd_rn),
+  CE(fsitod,   eb80bc0, 2, (RVD, RVS),       vfp_dp_sp_cvt),
+  CE(fuitod,   eb80b40, 2, (RVD, RVS),       vfp_dp_sp_cvt),
+  CE(ftosid,   ebd0b40, 2, (RVS, RVD),       vfp_sp_dp_cvt),
+  CE(ftosizd,  ebd0bc0, 2, (RVS, RVD),       vfp_sp_dp_cvt),
+  CE(ftouid,   ebc0b40, 2, (RVS, RVD),       vfp_sp_dp_cvt),
+  CE(ftouizd,  ebc0bc0, 2, (RVS, RVD),       vfp_sp_dp_cvt),
+
+  /* Memory operations.         */
+  CE(fldd,     d100b00, 2, (RVD, ADDR),      vfp_dp_ldst),
+  CE(fstd,     d000b00, 2, (RVD, ADDR),      vfp_dp_ldst),
+  CE(fldmiad,  c900b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
+  CE(fldmfdd,  c900b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
+  CE(fldmdbd,  d300b00, 2, (RRw, VRDLST),    vfp_dp_ldstmdb),
+  CE(fldmead,  d300b00, 2, (RRw, VRDLST),    vfp_dp_ldstmdb),
+  CE(fstmiad,  c800b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
+  CE(fstmead,  c800b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
+  CE(fstmdbd,  d200b00, 2, (RRw, VRDLST),    vfp_dp_ldstmdb),
+  CE(fstmfdd,  d200b00, 2, (RRw, VRDLST),    vfp_dp_ldstmdb),
 
 
-  if (mav_reg_required_here (&str, 12, reg0) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || *str++ != '['
-      || reg_required_here (&str, 16) == FAIL)
-    goto fail_ldst;
+  /* Monadic operations.  */
+  CE(fabsd,    eb00bc0, 2, (RVD, RVD),       rd_rm),
+  CE(fnegd,    eb10b40, 2, (RVD, RVD),       rd_rm),
+  CE(fsqrtd,   eb10bc0, 2, (RVD, RVD),       rd_rm),
+
+  /* Dyadic operations.         */
+  CE(faddd,    e300b00, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fsubd,    e300b40, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fmuld,    e200b00, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fdivd,    e800b00, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fmacd,    e000b00, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fmscd,    e100b00, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fnmuld,   e200b40, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fnmacd,   e000b40, 3, (RVD, RVD, RVD),  rd_rn_rm),
+  CE(fnmscd,   e100b40, 3, (RVD, RVD, RVD),  rd_rn_rm),
 
 
-  if (skip_past_comma (&str) == SUCCESS)
-    {
-      /* You are here: "<offset>]{!}".  */
-      inst.instruction |= PRE_INDEX;
+  /* Comparisons.  */
+  CE(fcmpd,    eb40b40, 2, (RVD, RVD),       rd_rm),
+  CE(fcmpzd,   eb50b40, 1, (RVD),            rd),
+  CE(fcmped,   eb40bc0, 2, (RVD, RVD),       rd_rm),
+  CE(fcmpezd,  eb50bc0, 1, (RVD),            rd),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V2
+  CE(fmsrr,    c400a10, 3, (VRSLST, RR, RR), vfp_sp2_from_reg2),
+  CE(fmrrs,    c500a10, 3, (RR, RR, VRSLST), vfp_reg2_from_sp2),
+  CE(fmdrr,    c400b10, 3, (RVD, RR, RR),    rm_rd_rn),
+  CE(fmrrd,    c500b10, 3, (RR, RR, RVD),    rd_rn_rm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_XSCALE /* Intel XScale extensions.         */
+  CE(mia,      e200010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(miaph,    e280010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(miabb,    e2c0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(miabt,    e2d0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(miatb,    e2e0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(miatt,    e2f0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+  CE(mar,      c400000, 3, (RXA, RRnpc, RRnpc), xsc_mar),
+  CE(mra,      c500000, 3, (RRnpc, RRnpc, RXA), xsc_mra),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_IWMMXT /* Intel Wireless MMX technology.  */
+  CE(tandcb,   e13f130, 1, (RR),                   iwmmxt_tandorc),
+  CE(tandch,   e53f130, 1, (RR),                   iwmmxt_tandorc),
+  CE(tandcw,   e93f130, 1, (RR),                   iwmmxt_tandorc),
+  CE(tbcstb,   e400010, 2, (RIWR, RR),             rn_rd),
+  CE(tbcsth,   e400050, 2, (RIWR, RR),             rn_rd),
+  CE(tbcstw,   e400090, 2, (RIWR, RR),             rn_rd),
+  CE(textrcb,  e130170, 2, (RR, I7),               iwmmxt_textrc),
+  CE(textrch,  e530170, 2, (RR, I7),               iwmmxt_textrc),
+  CE(textrcw,  e930170, 2, (RR, I7),               iwmmxt_textrc),
+  CE(textrmub, e100070, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(textrmuh, e500070, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(textrmuw, e900070, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(textrmsb, e100078, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(textrmsh, e500078, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(textrmsw, e900078, 3, (RR, RIWR, I7),         iwmmxt_textrm),
+  CE(tinsrb,   e600010, 3, (RIWR, RR, I7),         iwmmxt_tinsr),
+  CE(tinsrh,   e600050, 3, (RIWR, RR, I7),         iwmmxt_tinsr),
+  CE(tinsrw,   e600090, 3, (RIWR, RR, I7),         iwmmxt_tinsr),
+  CE(tmcr,     e000110, 2, (RIWC, RR),             rn_rd),
+  CE(tmcrr,    c400000, 3, (RIWR, RR, RR),         rm_rd_rn),
+  CE(tmia,     e200010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmiaph,   e280010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmiabb,   e2c0010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmiabt,   e2d0010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmiatb,   e2e0010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmiatt,   e2f0010, 3, (RIWR, RR, RR),         iwmmxt_tmia),
+  CE(tmovmskb, e100030, 2, (RR, RIWR),             rd_rn),
+  CE(tmovmskh, e500030, 2, (RR, RIWR),             rd_rn),
+  CE(tmovmskw, e900030, 2, (RR, RIWR),             rd_rn),
+  CE(tmrc,     e100110, 2, (RR, RIWC),             rd_rn),
+  CE(tmrrc,    c500000, 3, (RR, RR, RIWR),         rd_rn_rm),
+  CE(torcb,    e13f150, 1, (RR),                   iwmmxt_tandorc),
+  CE(torch,    e53f150, 1, (RR),                   iwmmxt_tandorc),
+  CE(torcw,    e93f150, 1, (RR),                   iwmmxt_tandorc),
+  CE(waccb,    e0001c0, 2, (RIWR, RIWR),           rd_rn),
+  CE(wacch,    e4001c0, 2, (RIWR, RIWR),           rd_rn),
+  CE(waccw,    e8001c0, 2, (RIWR, RIWR),           rd_rn),
+  CE(waddbss,  e300180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddb,    e000180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddbus,  e100180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddhss,  e700180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddh,    e400180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddhus,  e500180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddwss,  eb00180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddw,    e800180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waddwus,  e900180, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(waligni,  e000020, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_waligni),
+  CE(walignr0, e800020, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(walignr1, e900020, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(walignr2, ea00020, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(walignr3, eb00020, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wand,     e200000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wandn,    e300000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wavg2b,   e800000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wavg2br,  e900000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wavg2h,   ec00000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wavg2hr,  ed00000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpeqb,  e000060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpeqh,  e400060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpeqw,  e800060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtub, e100060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtuh, e500060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtuw, e900060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtsb, e300060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtsh, e700060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wcmpgtsw, eb00060, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wldrb,    c100000, 2, (RIWR, ADDR),           iwmmxt_wldstbh),
+  CE(wldrh,    c500000, 2, (RIWR, ADDR),           iwmmxt_wldstbh),
+  CE(wldrw,    c100100, 2, (RIWR_RIWC, ADDR),      iwmmxt_wldstw),
+  CE(wldrd,    c500100, 2, (RIWR, ADDR),           iwmmxt_wldstd),
+  CE(wmacs,    e600100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmacsz,   e700100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmacu,    e400100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmacuz,   e500100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmadds,   ea00100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaddu,   e800100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxsb,   e200160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxsh,   e600160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxsw,   ea00160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxub,   e000160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxuh,   e400160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmaxuw,   e800160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminsb,   e300160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminsh,   e700160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminsw,   eb00160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminub,   e100160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminuh,   e500160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wminuw,   e900160, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmov,     e000000, 2, (RIWR, RIWR),           iwmmxt_wmov),
+  CE(wmulsm,   e300100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmulsl,   e200100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmulum,   e100100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wmulul,   e000100, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wor,      e000000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackhss, e700080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackhus, e500080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackwss, eb00080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackwus, e900080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackdss, ef00080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wpackdus, ed00080, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wrorh,    e700040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wrorhg,   e700148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wrorw,    eb00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wrorwg,   eb00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wrord,    ef00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wrordg,   ef00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsadb,    e000120, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsadbz,   e100120, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsadh,    e400120, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsadhz,   e500120, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wshufh,   e0001e0, 3, (RIWR, RIWR, I255),     iwmmxt_wshufh),
+  CE(wsllh,    e500040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsllhg,   e500148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsllw,    e900040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsllwg,   e900148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wslld,    ed00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wslldg,   ed00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsrah,    e400040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsrahg,   e400148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsraw,    e800040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsrawg,   e800148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsrad,    ec00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsradg,   ec00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsrlh,    e600040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsrlhg,   e600148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsrlw,    ea00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsrlwg,   ea00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wsrld,    ee00040, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsrldg,   ee00148, 3, (RIWR, RIWR, RIWG),     rd_rn_rm),
+  CE(wstrb,    c000000, 2, (RIWR, ADDR),           iwmmxt_wldstbh),
+  CE(wstrh,    c400000, 2, (RIWR, ADDR),           iwmmxt_wldstbh),
+  CE(wstrw,    c000100, 2, (RIWR_RIWC, ADDR),      iwmmxt_wldstw),
+  CE(wstrd,    c400100, 2, (RIWR, ADDR),           iwmmxt_wldstd),
+  CE(wsubbss,  e3001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubb,    e0001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubbus,  e1001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubhss,  e7001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubh,    e4001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubhus,  e5001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubwss,  eb001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubw,    e8001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wsubwus,  e9001a0, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wunpckehub,e0000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckehuh,e4000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckehuw,e8000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckehsb,e2000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckehsh,e6000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckehsw,ea000c0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckihb, e1000c0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wunpckihh, e5000c0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wunpckihw, e9000c0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wunpckelub,e0000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckeluh,e4000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckeluw,e8000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckelsb,e2000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckelsh,e6000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckelsw,ea000e0, 2, (RIWR, RIWR),          rd_rn),
+  CE(wunpckilb, e1000e0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wunpckilh, e5000e0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wunpckilw, e9000e0, 3, (RIWR, RIWR, RIWR),            rd_rn_rm),
+  CE(wxor,     e100000, 3, (RIWR, RIWR, RIWR),     rd_rn_rm),
+  CE(wzero,    e300000, 1, (RIWR),                 iwmmxt_wzero),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_MAVERICK /* Cirrus Maverick instructions. */
+  CE(cfldrs,   c100400, 2, (RMF, ADDR),              rd_cpaddr),
+  CE(cfldrd,   c500400, 2, (RMD, ADDR),              rd_cpaddr),
+  CE(cfldr32,  c100500, 2, (RMFX, ADDR),             rd_cpaddr),
+  CE(cfldr64,  c500500, 2, (RMDX, ADDR),             rd_cpaddr),
+  CE(cfstrs,   c000400, 2, (RMF, ADDR),              rd_cpaddr),
+  CE(cfstrd,   c400400, 2, (RMD, ADDR),              rd_cpaddr),
+  CE(cfstr32,  c000500, 2, (RMFX, ADDR),             rd_cpaddr),
+  CE(cfstr64,  c400500, 2, (RMDX, ADDR),             rd_cpaddr),
+  CE(cfmvsr,   e000450, 2, (RMF, RR),                rn_rd),
+  CE(cfmvrs,   e100450, 2, (RR, RMF),                rd_rn),
+  CE(cfmvdlr,  e000410, 2, (RMD, RR),                rn_rd),
+  CE(cfmvrdl,  e100410, 2, (RR, RMD),                rd_rn),
+  CE(cfmvdhr,  e000430, 2, (RMD, RR),                rn_rd),
+  CE(cfmvrdh,  e100430, 2, (RR, RMD),                rd_rn),
+  CE(cfmv64lr, e000510, 2, (RMDX, RR),               rn_rd),
+  CE(cfmvr64l, e100510, 2, (RR, RMDX),               rd_rn),
+  CE(cfmv64hr, e000530, 2, (RMDX, RR),               rn_rd),
+  CE(cfmvr64h, e100530, 2, (RR, RMDX),               rd_rn),
+  CE(cfmval32, e200440, 2, (RMAX, RMFX),             rd_rn),
+  CE(cfmv32al, e100440, 2, (RMFX, RMAX),             rd_rn),
+  CE(cfmvam32, e200460, 2, (RMAX, RMFX),             rd_rn),
+  CE(cfmv32am, e100460, 2, (RMFX, RMAX),             rd_rn),
+  CE(cfmvah32, e200480, 2, (RMAX, RMFX),             rd_rn),
+  CE(cfmv32ah, e100480, 2, (RMFX, RMAX),             rd_rn),
+  CE(cfmva32,  e2004a0, 2, (RMAX, RMFX),             rd_rn),
+  CE(cfmv32a,  e1004a0, 2, (RMFX, RMAX),             rd_rn),
+  CE(cfmva64,  e2004c0, 2, (RMAX, RMDX),             rd_rn),
+  CE(cfmv64a,  e1004c0, 2, (RMDX, RMAX),             rd_rn),
+  CE(cfmvsc32, e2004e0, 2, (RMDS, RMDX),             mav_dspsc),
+  CE(cfmv32sc, e1004e0, 2, (RMDX, RMDS),             rd),
+  CE(cfcpys,   e000400, 2, (RMF, RMF),               rd_rn),
+  CE(cfcpyd,   e000420, 2, (RMD, RMD),               rd_rn),
+  CE(cfcvtsd,  e000460, 2, (RMD, RMF),               rd_rn),
+  CE(cfcvtds,  e000440, 2, (RMF, RMD),               rd_rn),
+  CE(cfcvt32s, e000480, 2, (RMF, RMFX),              rd_rn),
+  CE(cfcvt32d, e0004a0, 2, (RMD, RMFX),              rd_rn),
+  CE(cfcvt64s, e0004c0, 2, (RMF, RMDX),              rd_rn),
+  CE(cfcvt64d, e0004e0, 2, (RMD, RMDX),              rd_rn),
+  CE(cfcvts32, e100580, 2, (RMFX, RMF),              rd_rn),
+  CE(cfcvtd32, e1005a0, 2, (RMFX, RMD),              rd_rn),
+  CE(cftruncs32,e1005c0, 2, (RMFX, RMF),             rd_rn),
+  CE(cftruncd32,e1005e0, 2, (RMFX, RMD),             rd_rn),
+  CE(cfrshl32, e000550, 3, (RMFX, RMFX, RR),         mav_triple),
+  CE(cfrshl64, e000570, 3, (RMDX, RMDX, RR),         mav_triple),
+  CE(cfsh32,   e000500, 3, (RMFX, RMFX, I63s),       mav_shift),
+  CE(cfsh64,   e200500, 3, (RMDX, RMDX, I63s),       mav_shift),
+  CE(cfcmps,   e100490, 3, (RR, RMF, RMF),           rd_rn_rm),
+  CE(cfcmpd,   e1004b0, 3, (RR, RMD, RMD),           rd_rn_rm),
+  CE(cfcmp32,  e100590, 3, (RR, RMFX, RMFX),         rd_rn_rm),
+  CE(cfcmp64,  e1005b0, 3, (RR, RMDX, RMDX),         rd_rn_rm),
+  CE(cfabss,   e300400, 2, (RMF, RMF),               rd_rn),
+  CE(cfabsd,   e300420, 2, (RMD, RMD),               rd_rn),
+  CE(cfnegs,   e300440, 2, (RMF, RMF),               rd_rn),
+  CE(cfnegd,   e300460, 2, (RMD, RMD),               rd_rn),
+  CE(cfadds,   e300480, 3, (RMF, RMF, RMF),          rd_rn_rm),
+  CE(cfaddd,   e3004a0, 3, (RMD, RMD, RMD),          rd_rn_rm),
+  CE(cfsubs,   e3004c0, 3, (RMF, RMF, RMF),          rd_rn_rm),
+  CE(cfsubd,   e3004e0, 3, (RMD, RMD, RMD),          rd_rn_rm),
+  CE(cfmuls,   e100400, 3, (RMF, RMF, RMF),          rd_rn_rm),
+  CE(cfmuld,   e100420, 3, (RMD, RMD, RMD),          rd_rn_rm),
+  CE(cfabs32,  e300500, 2, (RMFX, RMFX),             rd_rn),
+  CE(cfabs64,  e300520, 2, (RMDX, RMDX),             rd_rn),
+  CE(cfneg32,  e300540, 2, (RMFX, RMFX),             rd_rn),
+  CE(cfneg64,  e300560, 2, (RMDX, RMDX),             rd_rn),
+  CE(cfadd32,  e300580, 3, (RMFX, RMFX, RMFX),       rd_rn_rm),
+  CE(cfadd64,  e3005a0, 3, (RMDX, RMDX, RMDX),       rd_rn_rm),
+  CE(cfsub32,  e3005c0, 3, (RMFX, RMFX, RMFX),       rd_rn_rm),
+  CE(cfsub64,  e3005e0, 3, (RMDX, RMDX, RMDX),       rd_rn_rm),
+  CE(cfmul32,  e100500, 3, (RMFX, RMFX, RMFX),       rd_rn_rm),
+  CE(cfmul64,  e100520, 3, (RMDX, RMDX, RMDX),       rd_rn_rm),
+  CE(cfmac32,  e100540, 3, (RMFX, RMFX, RMFX),       rd_rn_rm),
+  CE(cfmsc32,  e100560, 3, (RMFX, RMFX, RMFX),       rd_rn_rm),
+  CE(cfmadd32, e000600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+  CE(cfmsub32, e100600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+  CE(cfmadda32, e200600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+  CE(cfmsuba32, e300600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+};
+#undef ARM_VARIANT
+#undef THUMB_VARIANT
+#undef TCE
+#undef TCM
+#undef TUE
+#undef TUF
+#undef TCC
+#undef CE
+#undef CM
+#undef UE
+#undef UF
+#undef UT
+#undef OPS0
+#undef OPS1
+#undef OPS2
+#undef OPS3
+#undef OPS4
+#undef OPS5
+#undef OPS6
+#undef do_0
+\f
+/* MD interface: bits in the object file.  */
 
 
-      offset = mav_parse_offset (&str, &negative);
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+   for use in the a.out file, and stores them in the array pointed to by buf.
+   This knows about the endian-ness of the target machine and does
+   THE RIGHT THING, whatever it is.  Possible values for n are 1 (byte)
+   2 (short) and 4 (long)  Floating numbers are put out as a series of
+   LITTLENUMS (shorts, here at least). */
 
 
-      if (inst.error)
-       return;
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
+}
 
 
-      if (*str++ != ']')
-       {
-         inst.error = _("missing ]");
-         return;
-       }
+static valueT
+md_chars_to_number (char * buf, int n)
+{
+  valueT result = 0;
+  unsigned char * where = (unsigned char *) buf;
 
 
-      if (*str == '!')
+  if (target_big_endian)
+    {
+      while (n--)
        {
        {
-         inst.instruction |= WRITE_BACK;
-         ++str;
+         result <<= 8;
+         result |= (*where++ & 255);
        }
     }
   else
     {
        }
     }
   else
     {
-      /* You are here: "], <offset>".  */
-      if (*str++ != ']')
+      while (n--)
        {
        {
-         inst.error = _("missing ]");
-         return;
+         result <<= 8;
+         result |= (where[n] & 255);
        }
        }
-
-      if (skip_past_comma (&str) == FAIL
-         || (offset = mav_parse_offset (&str, &negative), inst.error))
-       goto fail_ldst;
-
-      inst.instruction |= CP_T_WB; /* Post indexed, set bit W.  */
     }
 
     }
 
-  if (negative)
-    offset = -offset;
-  else
-    inst.instruction |= CP_T_UD; /* Postive, so set bit U.  */
+  return result;
+}
 
 
-  inst.instruction |= offset >> 2;
-  end_of_line (str);
-  return;
+/* MD interface: Sections.  */
 
 
-fail_ldst:
-  if (!inst.error)
-     inst.error = BAD_ARGS;
-  return;
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+                              segT    segtype ATTRIBUTE_UNUSED)
+{
+  as_fatal (_("md_estimate_size_before_relax\n"));
+  return 1;
 }
 
 }
 
-static void
-do_t_nop (str)
-     char * str;
+/* Round up a section size to the appropriate boundary.         */
+
+valueT
+md_section_align (segT  segment ATTRIBUTE_UNUSED,
+                 valueT size)
 {
 {
-  /* Do nothing.  */
-  end_of_line (str);
-  return;
+#ifdef OBJ_ELF
+  return size;
+#else
+  /* Round all sects to multiple of 4. */
+  return (size + 3) & ~3;
+#endif
 }
 
 }
 
-/* Handle the Format 4 instructions that do not have equivalents in other
-   formats.  That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL,
-   BIC and MVN.  */
+/* This is called from HANDLE_ALIGN in write.c.         Fill in the contents
+   of an rs_align_code fragment.  */
 
 
-static void
-do_t_arit (str)
-     char * str;
+void
+arm_handle_align (fragS * fragP)
 {
 {
-  int Rd, Rs, Rn;
+  static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+  static char const thumb_noop[2] = { 0xc0, 0x46 };
+  static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+  static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
 
 
-  skip_whitespace (str);
+  int bytes, fix, noop_size;
+  char * p;
+  const char * noop;
+
+  if (fragP->fr_type != rs_align_code)
+    return;
+
+  bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+  p = fragP->fr_literal + fragP->fr_fix;
+  fix = 0;
+
+  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
 
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+  if (fragP->tc_frag_data)
     {
     {
-      inst.error = BAD_ARGS;
-      return;
+      if (target_big_endian)
+       noop = thumb_bigend_noop;
+      else
+       noop = thumb_noop;
+      noop_size = sizeof (thumb_noop);
     }
     }
-
-  if (skip_past_comma (&str) != FAIL)
+  else
     {
     {
-      /* Three operand format not allowed for TST, CMN, NEG and MVN.
-        (It isn't allowed for CMP either, but that isn't handled by this
-        function.)  */
-      if (inst.instruction == T_OPCODE_TST
-         || inst.instruction == T_OPCODE_CMN
-         || inst.instruction == T_OPCODE_NEG
-         || inst.instruction == T_OPCODE_MVN)
-       {
-         inst.error = BAD_ARGS;
-         return;
-       }
-
-      if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-       return;
+      if (target_big_endian)
+       noop = arm_bigend_noop;
+      else
+       noop = arm_noop;
+      noop_size = sizeof (arm_noop);
+    }
 
 
-      if (Rs != Rd)
-       {
-         inst.error = _("dest and source1 must be the same register");
-         return;
-       }
-      Rs = Rn;
+  if (bytes & (noop_size - 1))
+    {
+      fix = bytes & (noop_size - 1);
+      memset (p, 0, fix);
+      p += fix;
+      bytes -= fix;
     }
 
     }
 
-  if (inst.instruction == T_OPCODE_MUL
-      && Rs == Rd)
-    as_tsktsk (_("Rs and Rd must be different in MUL"));
+  while (bytes >= noop_size)
+    {
+      memcpy (p, noop, noop_size);
+      p += noop_size;
+      bytes -= noop_size;
+      fix += noop_size;
+    }
 
 
-  inst.instruction |= Rd | (Rs << 3);
-  end_of_line (str);
+  fragP->fr_fix += fix;
+  fragP->fr_var = noop_size;
 }
 
 }
 
-static void
-do_t_add (str)
-     char * str;
+/* Called from md_do_align.  Used to create an alignment
+   frag in a code section.  */
+
+void
+arm_frag_align_code (int n, int max)
 {
 {
-  thumb_add_sub (str, 0);
+  char * p;
+
+  /* We assume that there will never be a requirement
+     to support alignments greater than 32 bytes.  */
+  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+    as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+
+  p = frag_var (rs_align_code,
+               MAX_MEM_FOR_RS_ALIGN_CODE,
+               1,
+               (relax_substateT) max,
+               (symbolS *) NULL,
+               (offsetT) n,
+               (char *) NULL);
+  *p = 0;
 }
 
 }
 
-static void
-do_t_asr (str)
-     char * str;
+/* Perform target specific initialisation of a frag.  */
+
+void
+arm_init_frag (fragS * fragP)
 {
 {
-  thumb_shift (str, THUMB_ASR);
+  /* Record whether this frag is in an ARM or a THUMB area.  */
+  fragP->tc_frag_data = thumb_mode;
 }
 
 }
 
-static void
-do_t_branch9 (str)
-     char * str;
+#ifdef OBJ_ELF
+/* When we change sections we need to issue a new mapping symbol.  */
+
+void
+arm_elf_change_section (void)
 {
 {
-  if (my_get_expression (&inst.reloc.exp, &str))
+  flagword flags;
+  segment_info_type *seginfo;
+
+  /* Link an unlinked unwind index table section to the .text section. */
+  if (elf_section_type (now_seg) == SHT_ARM_EXIDX
+      && elf_linked_to_section (now_seg) == NULL)
+    elf_linked_to_section (now_seg) = text_section;
+
+  if (!SEG_NORMAL (now_seg))
     return;
     return;
-  inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
-  inst.reloc.pc_rel = 1;
-  end_of_line (str);
+
+  flags = bfd_get_section_flags (stdoutput, now_seg);
+
+  /* We can ignore sections that only contain debug info.  */
+  if ((flags & SEC_ALLOC) == 0)
+    return;
+
+  seginfo = seg_info (now_seg);
+  mapstate = seginfo->tc_segment_info_data.mapstate;
+  marked_pr_dependency = seginfo->tc_segment_info_data.marked_pr_dependency;
 }
 
 }
 
-static void
-do_t_branch12 (str)
-     char * str;
+int
+arm_elf_section_type (const char * str, size_t len)
 {
 {
-  if (my_get_expression (&inst.reloc.exp, &str))
-    return;
-  inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
-  inst.reloc.pc_rel = 1;
-  end_of_line (str);
+  if (len == 5 && strncmp (str, "exidx", 5) == 0)
+    return SHT_ARM_EXIDX;
+
+  return -1;
 }
 }
+\f
+/* Code to deal with unwinding tables. */
 
 
-/* Find the real, Thumb encoded start of a Thumb function.  */
+static void add_unwind_adjustsp (offsetT);
 
 
-static symbolS *
-find_real_start (symbolP)
-     symbolS * symbolP;
-{
-  char *       real_start;
-  const char * name = S_GET_NAME (symbolP);
-  symbolS *    new_target;
+/* Cenerate and deferred unwind frame offset.  */
 
 
-  /* This definiton must agree with the one in gcc/config/arm/thumb.c.  */
-#define STUB_NAME ".real_start_of"
+static void
+flush_pending_unwind (void)
+{
+  offsetT offset;
 
 
-  if (name == NULL)
-    abort ();
+  offset = unwind.pending_offset;
+  unwind.pending_offset = 0;
+  if (offset != 0)
+    add_unwind_adjustsp (offset);
+}
 
 
-  /* Names that start with '.' are local labels, not function entry points.
-     The compiler may generate BL instructions to these labels because it
-     needs to perform a branch to a far away location.  */
-  if (name[0] == '.')
-    return symbolP;
+/* Add an opcode to this list for this function.  Two-byte opcodes should
+   be passed as op[0] << 8 | op[1].  The list of opcodes is built in reverse
+   order.  */
 
 
-  real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
-  sprintf (real_start, "%s%s", STUB_NAME, name);
+static void
+add_unwind_opcode (valueT op, int length)
+{
+  /* Add any deferred stack adjustment.         */
+  if (unwind.pending_offset)
+    flush_pending_unwind ();
 
 
-  new_target = symbol_find (real_start);
+  unwind.sp_restored = 0;
 
 
-  if (new_target == NULL)
+  if (unwind.opcode_count + length > unwind.opcode_alloc)
     {
     {
-      as_warn ("Failed to find real start of function: %s\n", name);
-      new_target = symbolP;
+      unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
+      if (unwind.opcodes)
+       unwind.opcodes = xrealloc (unwind.opcodes,
+                                  unwind.opcode_alloc);
+      else
+       unwind.opcodes = xmalloc (unwind.opcode_alloc);
+    }
+  while (length > 0)
+    {
+      length--;
+      unwind.opcodes[unwind.opcode_count] = op & 0xff;
+      op >>= 8;
+      unwind.opcode_count++;
     }
     }
-
-  free (real_start);
-
-  return new_target;
 }
 
 }
 
+/* Add unwind opcodes to adjust the stack pointer.  */
+
 static void
 static void
-do_t_branch23 (str)
-     char * str;
+add_unwind_adjustsp (offsetT offset)
 {
 {
-  if (my_get_expression (& inst.reloc.exp, & str))
-    return;
+  valueT op;
 
 
-  inst.reloc.type   = BFD_RELOC_THUMB_PCREL_BRANCH23;
-  inst.reloc.pc_rel = 1;
-  end_of_line (str);
+  if (offset > 0x200)
+    {
+      /* We need at most 5 bytes to hold a 32-bit value in a uleb128.  */
+      char bytes[5];
+      int n;
+      valueT o;
 
 
-  /* If the destination of the branch is a defined symbol which does not have
-     the THUMB_FUNC attribute, then we must be calling a function which has
-     the (interfacearm) attribute.  We look for the Thumb entry point to that
-     function and change the branch to refer to that function instead.  */
-  if (   inst.reloc.exp.X_op == O_symbol
-      && inst.reloc.exp.X_add_symbol != NULL
-      && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
-      && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
-    inst.reloc.exp.X_add_symbol =
-      find_real_start (inst.reloc.exp.X_add_symbol);
+      /* Long form: 0xb2, uleb128.  */
+      /* This might not fit in a word so add the individual bytes,
+        remembering the list is built in reverse order.  */
+      o = (valueT) ((offset - 0x204) >> 2);
+      if (o == 0)
+       add_unwind_opcode (0, 1);
+
+      /* Calculate the uleb128 encoding of the offset. */
+      n = 0;
+      while (o)
+       {
+         bytes[n] = o & 0x7f;
+         o >>= 7;
+         if (o)
+           bytes[n] |= 0x80;
+         n++;
+       }
+      /* Add the insn. */
+      for (; n; n--)
+       add_unwind_opcode (bytes[n - 1], 1);
+      add_unwind_opcode (0xb2, 1);
+    }
+  else if (offset > 0x100)
+    {
+      /* Two short opcodes.  */
+      add_unwind_opcode (0x3f, 1);
+      op = (offset - 0x104) >> 2;
+      add_unwind_opcode (op, 1);
+    }
+  else if (offset > 0)
+    {
+      /* Short opcode. */
+      op = (offset - 4) >> 2;
+      add_unwind_opcode (op, 1);
+    }
+  else if (offset < 0)
+    {
+      offset = -offset;
+      while (offset > 0x100)
+       {
+         add_unwind_opcode (0x7f, 1);
+         offset -= 0x100;
+       }
+      op = ((offset - 4) >> 2) | 0x40;
+      add_unwind_opcode (op, 1);
+    }
 }
 
 }
 
+/* Finish the list of unwind opcodes for this function.         */
 static void
 static void
-do_t_bx (str)
-     char * str;
+finish_unwind_opcodes (void)
 {
 {
-  int reg;
-
-  skip_whitespace (str);
+  valueT op;
 
 
-  if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
-    return;
-
-  /* This sets THUMB_H2 from the top bit of reg.  */
-  inst.instruction |= reg << 3;
-
-  /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC.  The reloc
-     should cause the alignment to be checked once it is known.  This is
-     because BX PC only works if the instruction is word aligned.  */
+  if (unwind.fp_used)
+    {
+      /* Adjust sp as neccessary.  */
+      unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
+      flush_pending_unwind ();
 
 
-  end_of_line (str);
+      /* After restoring sp from the frame pointer.  */
+      op = 0x90 | unwind.fp_reg;
+      add_unwind_opcode (op, 1);
+    }
+  else
+    flush_pending_unwind ();
 }
 
 }
 
-static void
-do_t_compare (str)
-     char * str;
-{
-  thumb_mov_compare (str, THUMB_COMPARE);
-}
+
+/* Start an exception table entry.  If idx is nonzero this is an index table
+   entry.  */
 
 static void
 
 static void
-do_t_ldmstm (str)
-     char * str;
+start_unwind_section (const segT text_seg, int idx)
 {
 {
-  int Rb;
-  long range;
+  const char * text_name;
+  const char * prefix;
+  const char * prefix_once;
+  const char * group_name;
+  size_t prefix_len;
+  size_t text_len;
+  char * sec_name;
+  size_t sec_name_len;
+  int type;
+  int flags;
+  int linkonce;
 
 
-  skip_whitespace (str);
-
-  if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-    return;
-
-  if (*str != '!')
-    as_warn (_("inserted missing '!': load/store multiple always writes back base register"));
-  else
-    str++;
-
-  if (skip_past_comma (&str) == FAIL
-      || (range = reg_list (&str)) == FAIL)
+  if (idx)
     {
     {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
+      prefix = ELF_STRING_ARM_unwind;
+      prefix_once = ELF_STRING_ARM_unwind_once;
+      type = SHT_ARM_EXIDX;
     }
     }
-
-  if (inst.reloc.type != BFD_RELOC_NONE)
+  else
     {
     {
-      /* This really doesn't seem worth it.  */
-      inst.reloc.type = BFD_RELOC_NONE;
-      inst.error = _("expression too complex");
-      return;
+      prefix = ELF_STRING_ARM_unwind_info;
+      prefix_once = ELF_STRING_ARM_unwind_info_once;
+      type = SHT_PROGBITS;
     }
 
     }
 
-  if (range & ~0xff)
+  text_name = segment_name (text_seg);
+  if (streq (text_name, ".text"))
+    text_name = "";
+
+  if (strncmp (text_name, ".gnu.linkonce.t.",
+              strlen (".gnu.linkonce.t.")) == 0)
     {
     {
-      inst.error = _("only lo-regs valid in load/store multiple");
-      return;
+      prefix = prefix_once;
+      text_name += strlen (".gnu.linkonce.t.");
     }
 
     }
 
-  inst.instruction |= (Rb << 8) | range;
-  end_of_line (str);
-}
-
-static void
-do_t_ldr (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
-}
-
-static void
-do_t_ldrb (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
-}
-
-static void
-do_t_ldrh (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
-}
-
-static void
-do_t_lds (str)
-     char * str;
-{
-  int Rd, Rb, Ro;
+  prefix_len = strlen (prefix);
+  text_len = strlen (text_name);
+  sec_name_len = prefix_len + text_len;
+  sec_name = xmalloc (sec_name_len + 1);
+  memcpy (sec_name, prefix, prefix_len);
+  memcpy (sec_name + prefix_len, text_name, text_len);
+  sec_name[prefix_len + text_len] = '\0';
 
 
-  skip_whitespace (str);
+  flags = SHF_ALLOC;
+  linkonce = 0;
+  group_name = 0;
 
 
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || *str++ != '['
-      || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || skip_past_comma (&str) == FAIL
-      || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL
-      || *str++ != ']')
+  /* Handle COMDAT group.  */
+  if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
     {
     {
-      if (! inst.error)
-       inst.error = _("syntax: ldrs[b] Rd, [Rb, Ro]");
-      return;
+      group_name = elf_group_name (text_seg);
+      if (group_name == NULL)
+       {
+         as_bad ("Group section `%s' has no group signature",
+                 segment_name (text_seg));
+         ignore_rest_of_line ();
+         return;
+       }
+      flags |= SHF_GROUP;
+      linkonce = 1;
     }
 
     }
 
-  inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
-  end_of_line (str);
-}
+  obj_elf_change_section (sec_name, type, flags, 0, group_name, linkonce, 0);
 
 
-static void
-do_t_lsl (str)
-     char * str;
-{
-  thumb_shift (str, THUMB_LSL);
+  /* Set the setion link for index tables.  */
+  if (idx)
+    elf_linked_to_section (now_seg) = text_seg;
 }
 
 }
 
-static void
-do_t_lsr (str)
-     char * str;
-{
-  thumb_shift (str, THUMB_LSR);
-}
 
 
-static void
-do_t_mov (str)
-     char * str;
-{
-  thumb_mov_compare (str, THUMB_MOVE);
-}
+/* Start an unwind table entry.         HAVE_DATA is nonzero if we have additional
+   personality routine data.  Returns zero, or the index table value for
+   and inline entry.  */
 
 
-static void
-do_t_push_pop (str)
-     char * str;
+static valueT
+create_unwind_entry (int have_data)
 {
 {
-  long range;
+  int size;
+  addressT where;
+  char *ptr;
+  /* The current word of data. */
+  valueT data;
+  /* The number of bytes left in this word.  */
+  int n;
 
 
-  skip_whitespace (str);
+  finish_unwind_opcodes ();
 
 
-  if ((range = reg_list (&str)) == FAIL)
-    {
-      if (! inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+  /* Remember the current text section.         */
+  unwind.saved_seg = now_seg;
+  unwind.saved_subseg = now_subseg;
 
 
-  if (inst.reloc.type != BFD_RELOC_NONE)
-    {
-      /* This really doesn't seem worth it.  */
-      inst.reloc.type = BFD_RELOC_NONE;
-      inst.error = _("expression too complex");
-      return;
-    }
+  start_unwind_section (now_seg, 0);
 
 
-  if (range & ~0xff)
+  if (unwind.personality_routine == NULL)
     {
     {
-      if ((inst.instruction == T_OPCODE_PUSH
-          && (range & ~0xff) == 1 << REG_LR)
-         || (inst.instruction == T_OPCODE_POP
-             && (range & ~0xff) == 1 << REG_PC))
+      if (unwind.personality_index == -2)
        {
        {
-         inst.instruction |= THUMB_PP_PC_LR;
-         range &= 0xff;
+         if (have_data)
+           as_bad (_("handerdata in cantunwind frame"));
+         return 1; /* EXIDX_CANTUNWIND.  */
        }
        }
-      else
+
+      /* Use a default personality routine if none is specified.  */
+      if (unwind.personality_index == -1)
        {
        {
-         inst.error = _("invalid register list to push/pop instruction");
-         return;
+         if (unwind.opcode_count > 3)
+           unwind.personality_index = 1;
+         else
+           unwind.personality_index = 0;
        }
        }
-    }
-
-  inst.instruction |= range;
-  end_of_line (str);
-}
 
 
-static void
-do_t_str (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_WORD);
-}
+      /* Space for the personality routine entry.  */
+      if (unwind.personality_index == 0)
+       {
+         if (unwind.opcode_count > 3)
+           as_bad (_("too many unwind opcodes for personality routine 0"));
 
 
-static void
-do_t_strb (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
-}
+         if (!have_data)
+           {
+             /* All the data is inline in the index table.  */
+             data = 0x80;
+             n = 3;
+             while (unwind.opcode_count > 0)
+               {
+                 unwind.opcode_count--;
+                 data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+                 n--;
+               }
 
 
-static void
-do_t_strh (str)
-     char * str;
-{
-  thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
-}
+             /* Pad with "finish" opcodes.  */
+             while (n--)
+               data = (data << 8) | 0xb0;
 
 
-static void
-do_t_sub (str)
-     char * str;
-{
-  thumb_add_sub (str, 1);
-}
+             return data;
+           }
+         size = 0;
+       }
+      else
+       /* We get two opcodes "free" in the first word.  */
+       size = unwind.opcode_count - 2;
+    }
+  else
+    /* An extra byte is required for the opcode count. */
+    size = unwind.opcode_count + 1;
 
 
-static void
-do_t_swi (str)
-     char * str;
-{
-  skip_whitespace (str);
+  size = (size + 3) >> 2;
+  if (size > 0xff)
+    as_bad (_("too many unwind opcodes"));
 
 
-  if (my_get_expression (&inst.reloc.exp, &str))
-    return;
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
+  unwind.table_entry = expr_build_dot ();
 
 
-  inst.reloc.type = BFD_RELOC_ARM_SWI;
-  end_of_line (str);
-  return;
-}
+  /* Allocate the table entry. */
+  ptr = frag_more ((size << 2) + 4);
+  where = frag_now_fix () - ((size << 2) + 4);
 
 
-static void
-do_t_adr (str)
-     char * str;
-{
-  int reg;
+  switch (unwind.personality_index)
+    {
+    case -1:
+      /* ??? Should this be a PLT generating relocation?  */
+      /* Custom personality routine.  */
+      fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+              BFD_RELOC_ARM_PREL31);
 
 
-  /* This is a pseudo-op of the form "adr rd, label" to be converted
-     into a relative address of the form "add rd, pc, #label-.-4".  */
-  skip_whitespace (str);
+      where += 4;
+      ptr += 4;
 
 
-  /* Store Rd in temporary location inside instruction.  */
-  if ((reg = reg_required_here (&str, 4)) == FAIL
-      || (reg > 7)  /* For Thumb reg must be r0..r7.  */
-      || skip_past_comma (&str) == FAIL
-      || my_get_expression (&inst.reloc.exp, &str))
-    {
-      if (!inst.error)
-       inst.error = BAD_ARGS;
-      return;
-    }
+      /* Set the first byte to the number of additional words. */
+      data = size - 1;
+      n = 3;
+      break;
 
 
-  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
-  inst.reloc.exp.X_add_number -= 4; /* PC relative adjust.  */
-  inst.reloc.pc_rel = 1;
-  inst.instruction |= REG_PC; /* Rd is already placed into the instruction.  */
+    /* ABI defined personality routines.  */
+    case 0:
+      /* Three opcodes bytes are packed into the first word.  */
+      data = 0x80;
+      n = 3;
+      break;
 
 
-  end_of_line (str);
-}
+    case 1:
+    case 2:
+      /* The size and first two opcode bytes go in the first word.  */
+      data = ((0x80 + unwind.personality_index) << 8) | size;
+      n = 2;
+      break;
 
 
-static void
-insert_reg (r, htab)
-     const struct reg_entry *r;
-     struct hash_control *htab;
-{
-  int    len  = strlen (r->name) + 2;
-  char * buf  = (char *) xmalloc (len);
-  char * buf2 = (char *) xmalloc (len);
-  int    i    = 0;
+    default:
+      /* Should never happen.  */
+      abort ();
+    }
 
 
-#ifdef REGISTER_PREFIX
-  buf[i++] = REGISTER_PREFIX;
-#endif
+  /* Pack the opcodes into words (MSB first), reversing the list at the same
+     time.  */
+  while (unwind.opcode_count > 0)
+    {
+      if (n == 0)
+       {
+         md_number_to_chars (ptr, data, 4);
+         ptr += 4;
+         n = 4;
+         data = 0;
+       }
+      unwind.opcode_count--;
+      n--;
+      data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+    }
 
 
-  strcpy (buf + i, r->name);
+  /* Finish off the last word. */
+  if (n < 4)
+    {
+      /* Pad with "finish" opcodes.  */
+      while (n--)
+       data = (data << 8) | 0xb0;
 
 
-  for (i = 0; buf[i]; i++)
-    buf2[i] = TOUPPER (buf[i]);
+      md_number_to_chars (ptr, data, 4);
+    }
 
 
-  buf2[i] = '\0';
+  if (!have_data)
+    {
+      /* Add an empty descriptor if there is no user-specified data.   */
+      ptr = frag_more (4);
+      md_number_to_chars (ptr, 0, 4);
+    }
 
 
-  hash_insert (htab, buf,  (PTR) r);
-  hash_insert (htab, buf2, (PTR) r);
+  return 0;
 }
 
 }
 
-static void
-build_reg_hsh (map)
-     struct reg_map *map;
+/* Convert REGNAME to a DWARF-2 register number.  */
+
+int
+tc_arm_regname_to_dw2regnum (const char *regname)
 {
 {
-  const struct reg_entry *r;
+  int reg = arm_reg_parse ((char **) &regname, REG_TYPE_RN);
 
 
-  if ((map->htab = hash_new ()) == NULL)
-    as_fatal (_("virtual memory exhausted"));
+  if (reg == FAIL)
+    return -1;
 
 
-  for (r = map->names; r->name != NULL; r++)
-    insert_reg (r, map->htab);
+  return reg;
 }
 
 }
 
-static void
-insert_reg_alias (str, regnum, htab)
-     char *str;
-     int regnum;
-     struct hash_control *htab;
-{
-  struct reg_entry *new =
-    (struct reg_entry *) xmalloc (sizeof (struct reg_entry));
-  char *name = xmalloc (strlen (str) + 1);
-  strcpy (name, str);
-
-  new->name = name;
-  new->number = regnum;
+/* Initialize the DWARF-2 unwind information for this procedure.  */
 
 
-  hash_insert (htab, name, (PTR) new);
+void
+tc_arm_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa (REG_SP, 0);
 }
 }
+#endif /* OBJ_ELF */
 
 
-/* Look for the .req directive.  This is of the form:
 
 
-       newname .req existing_name
+/* MD interface: Symbol and relocation handling.  */
 
 
-   If we find one, or if it looks sufficiently like one that we want to
-   handle any error here, return non-zero.  Otherwise return zero.  */
-static int
-create_register_alias (newname, p)
-     char *newname;
-     char *p;
-{
-  char *q;
-  char c;
+/* Return the address within the segment that a PC-relative fixup is
+   relative to.  For ARM, PC-relative fixups applied to instructions
+   are generally relative to the location of the fixup plus 8 bytes.
+   Thumb branches are offset by 4, and Thumb loads relative to PC
+   require special handling.  */
 
 
-  q = p;
-  skip_whitespace (q);
+long
+md_pcrel_from_section (fixS * fixP, segT seg)
+{
+  offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
 
 
-  c = *p;
-  *p = '\0';
+  /* If this is pc-relative and we are going to emit a relocation
+     then we just want to put out any pipeline compensation that the linker
+     will need.  Otherwise we want to use the calculated base.  */
+  if (fixP->fx_pcrel 
+      && ((fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+         || arm_force_relocation (fixP)))
+    base = 0;
 
 
-  if (*q && !strncmp (q, ".req ", 5))
+  switch (fixP->fx_r_type)
     {
     {
-      char *copy_of_str;
-      char *r;
+      /* PC relative addressing on the Thumb is slightly odd as the
+        bottom two bits of the PC are forced to zero for the
+        calculation.  This happens *after* application of the
+        pipeline offset.  However, Thumb adrl already adjusts for
+        this, so we need not do it again.  */
+    case BFD_RELOC_ARM_THUMB_ADD:
+      return base & ~3;
 
 
-#ifdef IGNORE_OPCODE_CASE
-      newname = original_case_string;
-#endif
-      copy_of_str = newname;
+    case BFD_RELOC_ARM_THUMB_OFFSET:
+    case BFD_RELOC_ARM_T32_OFFSET_IMM:
+    case BFD_RELOC_ARM_T32_ADD_PC12:
+      return (base + 4) & ~3;
 
 
-      q += 4;
-      skip_whitespace (q);
+      /* Thumb branches are simply offset by +4.  */
+    case BFD_RELOC_THUMB_PCREL_BRANCH7:
+    case BFD_RELOC_THUMB_PCREL_BRANCH9:
+    case BFD_RELOC_THUMB_PCREL_BRANCH12:
+    case BFD_RELOC_THUMB_PCREL_BRANCH20:
+    case BFD_RELOC_THUMB_PCREL_BRANCH23:
+    case BFD_RELOC_THUMB_PCREL_BRANCH25:
+    case BFD_RELOC_THUMB_PCREL_BLX:
+      return base + 4;
 
 
-      for (r = q; *r != '\0'; r++)
-       if (*r == ' ')
-         break;
+      /* ARM mode branches are offset by +8.  However, the Windows CE
+        loader expects the relocation not to take this into account.  */
+    case BFD_RELOC_ARM_PCREL_BRANCH:
+    case BFD_RELOC_ARM_PCREL_BLX:
+    case BFD_RELOC_ARM_PLT32:
+#ifdef TE_WINCE
+      return base;
+#else
+      return base + 8;
+#endif
 
 
-      if (r != q)
-       {
-         enum arm_reg_type new_type, old_type;
-         int old_regno;
-         char d = *r;
+      /* ARM mode loads relative to PC are also offset by +8.  Unlike
+        branches, the Windows CE loader *does* expect the relocation
+        to take this into account.  */
+    case BFD_RELOC_ARM_OFFSET_IMM:
+    case BFD_RELOC_ARM_OFFSET_IMM8:
+    case BFD_RELOC_ARM_HWLITERAL:
+    case BFD_RELOC_ARM_LITERAL:
+    case BFD_RELOC_ARM_CP_OFF_IMM:
+      return base + 8;
 
 
-         *r = '\0';
-         old_type = arm_reg_parse_any (q);
-         *r = d;
 
 
-         new_type = arm_reg_parse_any (newname);
+      /* Other PC-relative relocations are un-offset.  */
+    default:
+      return base;
+    }
+}
 
 
-         if (new_type == REG_TYPE_MAX)
-           {
-             if (old_type != REG_TYPE_MAX)
-               {
-                 old_regno = arm_reg_parse (&q, all_reg_maps[old_type].htab);
-                 insert_reg_alias (newname, old_regno,
-                                   all_reg_maps[old_type].htab);
-               }
-             else
-               as_warn (_("register '%s' does not exist\n"), q);
-           }
-         else if (old_type == REG_TYPE_MAX)
-           {
-             as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"),
-                      copy_of_str, q);
-           }
-         else
-           {
-             /* Do not warn about redefinitions to the same alias.  */
-             if (new_type != old_type
-                 || (arm_reg_parse (&q, all_reg_maps[old_type].htab)
-                     != arm_reg_parse (&q, all_reg_maps[new_type].htab)))
-               as_warn (_("ignoring redefinition of register alias '%s'"),
-                        copy_of_str);
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
+   Otherwise we have no need to default values of symbols.  */
 
 
-           }
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+  if (name[0] == '_' && name[1] == 'G'
+      && streq (name, GLOBAL_OFFSET_TABLE_NAME))
+    {
+      if (!GOT_symbol)
+       {
+         if (symbol_find (name))
+           as_bad ("GOT already in the symbol table");
+
+         GOT_symbol = symbol_new (name, undefined_section,
+                                  (valueT) 0, & zero_address_frag);
        }
        }
-      else
-       as_warn (_("ignoring incomplete .req pseuso op"));
 
 
-      *p = c;
-      return 1;
+      return GOT_symbol;
     }
     }
-  *p = c;
+#endif
+
   return 0;
 }
 
   return 0;
 }
 
-static void
-set_constant_flonums ()
-{
-  int i;
-
-  for (i = 0; i < NUM_FLOAT_VALS; i++)
-    if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
-      abort ();
-}
+/* Subroutine of md_apply_fix.  Check to see if an immediate can be
+   computed as two separate immediate values, added together.  We
+   already know that this value cannot be computed by just one ARM
+   instruction.         */
 
 
-/* Iterate over the base tables to create the instruction patterns.  */
-static void
-build_arm_ops_hsh ()
+static unsigned int
+validate_immediate_twopart (unsigned int   val,
+                           unsigned int * highpart)
 {
 {
+  unsigned int a;
   unsigned int i;
   unsigned int i;
-  unsigned int j;
-  static struct obstack insn_obstack;
 
 
-  obstack_begin (&insn_obstack, 4000);
+  for (i = 0; i < 32; i += 2)
+    if (((a = rotate_left (val, i)) & 0xff) != 0)
+      {
+       if (a & 0xff00)
+         {
+           if (a & ~ 0xffff)
+             continue;
+           * highpart = (a  >> 8) | ((i + 24) << 7);
+         }
+       else if (a & 0xff0000)
+         {
+           if (a & 0xff000000)
+             continue;
+           * highpart = (a >> 16) | ((i + 16) << 7);
+         }
+       else
+         {
+           assert (a & 0xff000000);
+           * highpart = (a >> 24) | ((i + 8) << 7);
+         }
 
 
-  for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
-    {
-      const struct asm_opcode *insn = insns + i;
+       return (a & 0xff) | (i << 7);
+      }
 
 
-      if (insn->cond_offset != 0)
-       {
-         /* Insn supports conditional execution.  Build the varaints
-            and insert them in the hash table.  */
-         for (j = 0; j < sizeof (conds) / sizeof (struct asm_cond); j++)
-           {
-             unsigned len = strlen (insn->template);
-             struct asm_opcode *new;
-             char *template;
-
-             new = obstack_alloc (&insn_obstack, sizeof (struct asm_opcode));
-             /* All condition codes are two characters.  */
-             template = obstack_alloc (&insn_obstack, len + 3);
-
-             strncpy (template, insn->template, insn->cond_offset);
-             strcpy (template + insn->cond_offset, conds[j].template);
-             if (len > insn->cond_offset)
-               strcpy (template + insn->cond_offset + 2,
-                       insn->template + insn->cond_offset);
-             new->template = template;
-             new->cond_offset = 0;
-             new->variant = insn->variant;
-             new->parms = insn->parms;
-             new->value = (insn->value & ~COND_MASK) | conds[j].value;
-
-             hash_insert (arm_ops_hsh, new->template, (PTR) new);
-           }
-       }
-      /* Finally, insert the unconditional insn in the table directly;
-        no need to build a copy.  */
-      hash_insert (arm_ops_hsh, insn->template, (PTR) insn);
-    }
+  return FAIL;
 }
 
 }
 
-void
-md_begin ()
+static int
+validate_offset_imm (unsigned int val, int hwse)
 {
 {
-  unsigned mach;
-  unsigned int i;
-
-  if (   (arm_ops_hsh = hash_new ()) == NULL
-      || (arm_tops_hsh = hash_new ()) == NULL
-      || (arm_cond_hsh = hash_new ()) == NULL
-      || (arm_shift_hsh = hash_new ()) == NULL
-      || (arm_psr_hsh = hash_new ()) == NULL)
-    as_fatal (_("virtual memory exhausted"));
-
-  build_arm_ops_hsh ();
-  for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++)
-    hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i));
-  for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
-    hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
-  for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
-    hash_insert (arm_shift_hsh, shift_names[i].name, (PTR) (shift_names + i));
-  for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
-    hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
-
-  for (i = (int) REG_TYPE_FIRST; i < (int) REG_TYPE_MAX; i++)
-    build_reg_hsh (all_reg_maps + i);
-
-  set_constant_flonums ();
-
-  /* Set the cpu variant based on the command-line options.  We prefer
-     -mcpu= over -march= if both are set (as for GCC); and we prefer
-     -mfpu= over any other way of setting the floating point unit.
-     Use of legacy options with new options are faulted.  */
-  if (legacy_cpu != -1)
-    {
-      if (mcpu_cpu_opt != -1 || march_cpu_opt != -1)
-       as_bad (_("use of old and new-style options to set CPU type"));
+  if ((hwse && val > 255) || val > 4095)
+    return FAIL;
+  return val;
+}
 
 
-      mcpu_cpu_opt = legacy_cpu;
-    }
-  else if (mcpu_cpu_opt == -1)
-    mcpu_cpu_opt = march_cpu_opt;
+/* Subroutine of md_apply_fix.  Do those data_ops which can take a
+   negative immediate constant by altering the instruction.  A bit of
+   a hack really.
+       MOV <-> MVN
+       AND <-> BIC
+       ADC <-> SBC
+       by inverting the second operand, and
+       ADD <-> SUB
+       CMP <-> CMN
+       by negating the second operand.  */
 
 
-  if (legacy_fpu != -1)
-    {
-      if (mfpu_opt != -1)
-       as_bad (_("use of old and new-style options to set FPU type"));
+static int
+negate_data_op (unsigned long * instruction,
+               unsigned long   value)
+{
+  int op, new_inst;
+  unsigned long negated, inverted;
 
 
-      mfpu_opt = legacy_fpu;
-    }
-  else if (mfpu_opt == -1)
-    {
-      if (mcpu_fpu_opt != -1)
-       mfpu_opt = mcpu_fpu_opt;
-      else
-       mfpu_opt = march_fpu_opt;
-    }
+  negated = encode_arm_immediate (-value);
+  inverted = encode_arm_immediate (~value);
 
 
-  if (mfpu_opt == -1)
+  op = (*instruction >> DATA_OP_SHIFT) & 0xf;
+  switch (op)
     {
     {
-      if (mcpu_cpu_opt == -1)
-       mfpu_opt = FPU_DEFAULT;
-      else if (mcpu_cpu_opt & ARM_EXT_V5)
-       mfpu_opt = FPU_ARCH_VFP_V2;
-      else
-       mfpu_opt = FPU_ARCH_FPA;
-    }
-
-  if (mcpu_cpu_opt == -1)
-    mcpu_cpu_opt = CPU_DEFAULT;
-
-  cpu_variant = mcpu_cpu_opt | mfpu_opt;
-
-#if defined OBJ_COFF || defined OBJ_ELF
-  {
-    unsigned int flags = 0;
+      /* First negates.         */
+    case OPCODE_SUB:            /* ADD <-> SUB  */
+      new_inst = OPCODE_ADD;
+      value = negated;
+      break;
 
 
-    /* Set the flags in the private structure.  */
-    if (uses_apcs_26)      flags |= F_APCS26;
-    if (support_interwork) flags |= F_INTERWORK;
-    if (uses_apcs_float)   flags |= F_APCS_FLOAT;
-    if (pic_code)          flags |= F_PIC;
-    if ((cpu_variant & FPU_ANY) == FPU_NONE
-       || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only.  */
-      flags |= F_SOFT_FLOAT;
-    /* Using VFP conventions (even if soft-float).  */
-    if (cpu_variant & FPU_VFP_EXT_NONE) flags |= F_VFP_FLOAT;
+    case OPCODE_ADD:
+      new_inst = OPCODE_SUB;
+      value = negated;
+      break;
 
 
+    case OPCODE_CMP:            /* CMP <-> CMN  */
+      new_inst = OPCODE_CMN;
+      value = negated;
+      break;
 
 
-    bfd_set_private_flags (stdoutput, flags);
+    case OPCODE_CMN:
+      new_inst = OPCODE_CMP;
+      value = negated;
+      break;
 
 
-    /* We have run out flags in the COFF header to encode the
-       status of ATPCS support, so instead we create a dummy,
-       empty, debug section called .arm.atpcs.  */
-    if (atpcs)
-      {
-       asection * sec;
+      /* Now Inverted ops.  */
+    case OPCODE_MOV:            /* MOV <-> MVN  */
+      new_inst = OPCODE_MVN;
+      value = inverted;
+      break;
 
 
-       sec = bfd_make_section (stdoutput, ".arm.atpcs");
+    case OPCODE_MVN:
+      new_inst = OPCODE_MOV;
+      value = inverted;
+      break;
 
 
-       if (sec != NULL)
-         {
-           bfd_set_section_flags
-             (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
-           bfd_set_section_size (stdoutput, sec, 0);
-           bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
-         }
-      }
-  }
-#endif
+    case OPCODE_AND:            /* AND <-> BIC  */
+      new_inst = OPCODE_BIC;
+      value = inverted;
+      break;
 
 
-  /* Record the CPU type as well.  */
-  switch (cpu_variant & ARM_CPU_MASK)
-    {
-    case ARM_2:
-      mach = bfd_mach_arm_2;
+    case OPCODE_BIC:
+      new_inst = OPCODE_AND;
+      value = inverted;
       break;
 
       break;
 
-    case ARM_3:                /* Also ARM_250.  */
-      mach = bfd_mach_arm_2a;
+    case OPCODE_ADC:             /* ADC <-> SBC  */
+      new_inst = OPCODE_SBC;
+      value = inverted;
       break;
 
       break;
 
-    case ARM_6:                        /* Also ARM_7.  */
-      mach = bfd_mach_arm_3;
+    case OPCODE_SBC:
+      new_inst = OPCODE_ADC;
+      value = inverted;
       break;
 
       break;
 
+      /* We cannot do anything.         */
     default:
     default:
-      mach = bfd_mach_arm_4;
-      break;
+      return FAIL;
     }
 
     }
 
-  /* Catch special cases.  */
-  if (cpu_variant & ARM_CEXT_XSCALE)
-    mach = bfd_mach_arm_XScale;
-  else if (cpu_variant & ARM_EXT_V5E)
-    mach = bfd_mach_arm_5TE;
-  else if (cpu_variant & ARM_EXT_V5)
-    {
-      if (cpu_variant & ARM_EXT_V4T)
-       mach = bfd_mach_arm_5T;
-      else
-       mach = bfd_mach_arm_5;
-    }
-  else if (cpu_variant & ARM_EXT_V4)
-    {
-      if (cpu_variant & ARM_EXT_V4T)
-       mach = bfd_mach_arm_4T;
-      else
-       mach = bfd_mach_arm_4;
-    }
-  else if (cpu_variant & ARM_EXT_V3M)
-    mach = bfd_mach_arm_3M;
+  if (value == (unsigned) FAIL)
+    return FAIL;
 
 
-  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+  *instruction &= OPCODE_MASK;
+  *instruction |= new_inst << DATA_OP_SHIFT;
+  return value;
 }
 
 }
 
-/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
-   for use in the a.out file, and stores them in the array pointed to by buf.
-   This knows about the endian-ness of the target machine and does
-   THE RIGHT THING, whatever it is.  Possible values for n are 1 (byte)
-   2 (short) and 4 (long)  Floating numbers are put out as a series of
-   LITTLENUMS (shorts, here at least).  */
-
 void
 void
-md_number_to_chars (buf, val, n)
-     char * buf;
-     valueT val;
-     int    n;
+md_apply_fix (fixS *   fixP,
+              valueT * valP,
+              segT     seg)
 {
 {
-  if (target_big_endian)
-    number_to_chars_bigendian (buf, val, n);
-  else
-    number_to_chars_littleendian (buf, val, n);
-}
+  offsetT       value = * valP;
+  offsetT       newval;
+  unsigned int  newimm;
+  unsigned long         temp;
+  int           sign;
+  char *        buf = fixP->fx_where + fixP->fx_frag->fr_literal;
 
 
-static valueT
-md_chars_to_number (buf, n)
-     char * buf;
-     int    n;
-{
-  valueT result = 0;
-  unsigned char * where = (unsigned char *) buf;
+  assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
 
 
-  if (target_big_endian)
+  /* Note whether this will delete the relocation.  */
+  if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+    fixP->fx_done = 1;
+
+  /* On a 64-bit host, silently truncate 'value' to 32 bits for
+     consistency with the behavior on 32-bit hosts.  Remember value
+     for emit_reloc.  */
+  value &= 0xffffffff;
+  value ^= 0x80000000;
+  value -= 0x80000000; 
+
+  *valP = value;
+  fixP->fx_addnumber = value;
+
+  /* Same treatment for fixP->fx_offset.  */
+  fixP->fx_offset &= 0xffffffff;
+  fixP->fx_offset ^= 0x80000000;
+  fixP->fx_offset -= 0x80000000;
+
+  switch (fixP->fx_r_type)
     {
     {
-      while (n--)
+    case BFD_RELOC_NONE:
+      /* This will need to go in the object file.  */
+      fixP->fx_done = 0;
+      break;
+
+    case BFD_RELOC_ARM_IMMEDIATE:
+      /* We claim that this fixup has been processed here,
+        even if in fact we generate an error because we do
+        not have a reloc for it, so tc_gen_reloc will reject it.  */
+      fixP->fx_done = 1;
+
+      if (fixP->fx_addsy
+         && ! S_IS_DEFINED (fixP->fx_addsy))
        {
        {
-         result <<= 8;
-         result |= (*where++ & 255);
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("undefined symbol %s used as an immediate value"),
+                       S_GET_NAME (fixP->fx_addsy));
+         break;
        }
        }
-    }
-  else
-    {
-      while (n--)
+
+      newimm = encode_arm_immediate (value);
+      temp = md_chars_to_number (buf, INSN_SIZE);
+
+      /* If the instruction will fail, see if we can fix things up by
+        changing the opcode.  */
+      if (newimm == (unsigned int) FAIL
+         && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
        {
        {
-         result <<= 8;
-         result |= (where[n] & 255);
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid constant (%lx) after fixup"),
+                       (unsigned long) value);
+         break;
        }
        }
-    }
 
 
-  return result;
-}
+      newimm |= (temp & 0xfffff000);
+      md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+      break;
 
 
-/* Turn a string in input_line_pointer into a floating point constant
-   of type TYPE, and store the appropriate bytes in *LITP.  The number
-   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
-   returned, or NULL on OK.
+    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+      {
+       unsigned int highpart = 0;
+       unsigned int newinsn  = 0xe1a00000; /* nop.  */
 
 
-   Note that fp constants aren't represent in the normal way on the ARM.
-   In big endian mode, things are as expected.  However, in little endian
-   mode fp constants are big-endian word-wise, and little-endian byte-wise
-   within the words.  For example, (double) 1.1 in big endian mode is
-   the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
-   the byte sequence 99 99 f1 3f 9a 99 99 99.
-
-   ??? The format of 12 byte floats is uncertain according to gcc's arm.h.  */
-
-char *
-md_atof (type, litP, sizeP)
-     char   type;
-     char * litP;
-     int *  sizeP;
-{
-  int prec;
-  LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  char *t;
-  int i;
-
-  switch (type)
-    {
-    case 'f':
-    case 'F':
-    case 's':
-    case 'S':
-      prec = 2;
-      break;
-
-    case 'd':
-    case 'D':
-    case 'r':
-    case 'R':
-      prec = 4;
-      break;
-
-    case 'x':
-    case 'X':
-      prec = 6;
-      break;
-
-    case 'p':
-    case 'P':
-      prec = 6;
-      break;
-
-    default:
-      *sizeP = 0;
-      return _("bad call to MD_ATOF()");
-    }
-
-  t = atof_ieee (input_line_pointer, type, words);
-  if (t)
-    input_line_pointer = t;
-  *sizeP = prec * 2;
-
-  if (target_big_endian)
-    {
-      for (i = 0; i < prec; i++)
-       {
-         md_number_to_chars (litP, (valueT) words[i], 2);
-         litP += 2;
-       }
-    }
-  else
-    {
-      if (cpu_variant & FPU_ARCH_VFP)
-       for (i = prec - 1; i >= 0; i--)
-         {
-           md_number_to_chars (litP, (valueT) words[i], 2);
-           litP += 2;
-         }
-      else
-       /* For a 4 byte float the order of elements in `words' is 1 0.
-          For an 8 byte float the order is 1 0 3 2.  */
-       for (i = 0; i < prec; i += 2)
-         {
-           md_number_to_chars (litP, (valueT) words[i + 1], 2);
-           md_number_to_chars (litP + 2, (valueT) words[i], 2);
-           litP += 4;
-         }
-    }
-
-  return 0;
-}
-
-/* The knowledge of the PC's pipeline offset is built into the insns
-   themselves.  */
-
-long
-md_pcrel_from (fixP)
-     fixS * fixP;
-{
-  if (fixP->fx_addsy
-      && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section
-      && fixP->fx_subsy == NULL)
-    return 0;
-
-  if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
-    {
-      /* PC relative addressing on the Thumb is slightly odd
-        as the bottom two bits of the PC are forced to zero
-        for the calculation.  */
-      return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
-    }
-
-#ifdef TE_WINCE
-  /* The pattern was adjusted to accomodate CE's off-by-one fixups,
-     so we un-adjust here to compensate for the accomodation.  */
-  return fixP->fx_where + fixP->fx_frag->fr_address + 8;
-#else
-  return fixP->fx_where + fixP->fx_frag->fr_address;
-#endif
-}
-
-/* Round up a section size to the appropriate boundary.  */
-
-valueT
-md_section_align (segment, size)
-     segT   segment ATTRIBUTE_UNUSED;
-     valueT size;
-{
-#ifdef OBJ_ELF
-  return size;
-#else
-  /* Round all sects to multiple of 4.  */
-  return (size + 3) & ~3;
-#endif
-}
-
-/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
-   Otherwise we have no need to default values of symbols.  */
-
-symbolS *
-md_undefined_symbol (name)
-     char * name ATTRIBUTE_UNUSED;
-{
-#ifdef OBJ_ELF
-  if (name[0] == '_' && name[1] == 'G'
-      && streq (name, GLOBAL_OFFSET_TABLE_NAME))
-    {
-      if (!GOT_symbol)
-       {
-         if (symbol_find (name))
-           as_bad ("GOT already in the symbol table");
-
-         GOT_symbol = symbol_new (name, undefined_section,
-                                  (valueT) 0, & zero_address_frag);
-       }
-
-      return GOT_symbol;
-    }
-#endif
-
-  return 0;
-}
-
-/* arm_reg_parse () := if it looks like a register, return its token and
-   advance the pointer.  */
-
-static int
-arm_reg_parse (ccp, htab)
-     register char ** ccp;
-     struct hash_control *htab;
-{
-  char * start = * ccp;
-  char   c;
-  char * p;
-  struct reg_entry * reg;
-
-#ifdef REGISTER_PREFIX
-  if (*start != REGISTER_PREFIX)
-    return FAIL;
-  p = start + 1;
-#else
-  p = start;
-#ifdef OPTIONAL_REGISTER_PREFIX
-  if (*p == OPTIONAL_REGISTER_PREFIX)
-    p++, start++;
-#endif
-#endif
-  if (!ISALPHA (*p) || !is_name_beginner (*p))
-    return FAIL;
-
-  c = *p++;
-  while (ISALPHA (c) || ISDIGIT (c) || c == '_')
-    c = *p++;
-
-  *--p = 0;
-  reg = (struct reg_entry *) hash_find (htab, start);
-  *p = c;
-
-  if (reg)
-    {
-      *ccp = p;
-      return reg->number;
-    }
-
-  return FAIL;
-}
-
-/* Search for the following register name in each of the possible reg name
-   tables.  Return the classification if found, or REG_TYPE_MAX if not
-   present.  */
-static enum arm_reg_type
-arm_reg_parse_any (cp)
-     char *cp;
-{
-  int i;
-
-  for (i = (int) REG_TYPE_FIRST; i < (int) REG_TYPE_MAX; i++)
-    if (arm_reg_parse (&cp, all_reg_maps[i].htab) != FAIL)
-      return (enum arm_reg_type) i;
-
-  return REG_TYPE_MAX;
-}
-
-void
-md_apply_fix3 (fixP, valP, seg)
-     fixS *   fixP;
-     valueT * valP;
-     segT     seg;
-{
-  offsetT        value = * valP;
-  offsetT        newval;
-  unsigned int   newimm;
-  unsigned long  temp;
-  int            sign;
-  char *         buf = fixP->fx_where + fixP->fx_frag->fr_literal;
-  arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
-
-  assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
-
-  /* Note whether this will delete the relocation.  */
-#if 0
-  /* Patch from REarnshaw to JDavis (disabled for the moment, since it
-     doesn't work fully.)  */
-  if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy))
-      && !fixP->fx_pcrel)
-#else
-  if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
-#endif
-    fixP->fx_done = 1;
-
-  /* If this symbol is in a different section then we need to leave it for
-     the linker to deal with.  Unfortunately, md_pcrel_from can't tell,
-     so we have to undo it's effects here.  */
-  if (fixP->fx_pcrel)
-    {
-      if (fixP->fx_addsy != NULL
-         && S_IS_DEFINED (fixP->fx_addsy)
-         && S_GET_SEGMENT (fixP->fx_addsy) != seg)
-       {
-         if (target_oabi
-             && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
-                 || fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
-                 ))
-           value = 0;
-         else
-           value += md_pcrel_from (fixP);
-       }
-    }
-
-  /* Remember value for emit_reloc.  */
-  fixP->fx_addnumber = value;
-
-  switch (fixP->fx_r_type)
-    {
-    case BFD_RELOC_ARM_IMMEDIATE:
-      newimm = validate_immediate (value);
-      temp = md_chars_to_number (buf, INSN_SIZE);
-
-      /* If the instruction will fail, see if we can fix things up by
-        changing the opcode.  */
-      if (newimm == (unsigned int) FAIL
-         && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
-       {
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("invalid constant (%lx) after fixup"),
-                       (unsigned long) value);
-         break;
-       }
-
-      newimm |= (temp & 0xfffff000);
-      md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
-      fixP->fx_done = 1;
-      break;
-
-    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
-      {
-       unsigned int highpart = 0;
-       unsigned int newinsn  = 0xe1a00000; /* nop.  */
-
-       newimm = validate_immediate (value);
+       newimm = encode_arm_immediate (value);
        temp = md_chars_to_number (buf, INSN_SIZE);
 
        /* If the instruction will fail, see if we can fix things up by
        temp = md_chars_to_number (buf, INSN_SIZE);
 
        /* If the instruction will fail, see if we can fix things up by
-          changing the opcode.  */
+          changing the opcode.  */
        if (newimm == (unsigned int) FAIL
            && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
          {
            /* No ?  OK - try using two ADD instructions to generate
        if (newimm == (unsigned int) FAIL
            && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
          {
            /* No ?  OK - try using two ADD instructions to generate
-               the value.  */
+              the value.  */
            newimm = validate_immediate_twopart (value, & highpart);
 
            /* Yes - then make sure that the second instruction is
            newimm = validate_immediate_twopart (value, & highpart);
 
            /* Yes - then make sure that the second instruction is
-               also an add.  */
+              also an add.  */
            if (newimm != (unsigned int) FAIL)
              newinsn = temp;
            /* Still No ?  Try using a negated value.  */
            if (newimm != (unsigned int) FAIL)
              newinsn = temp;
            /* Still No ?  Try using a negated value.  */
@@ -9736,6 +10434,7 @@ md_apply_fix3 (fixP, valP, seg)
       break;
 
     case BFD_RELOC_ARM_OFFSET_IMM:
       break;
 
     case BFD_RELOC_ARM_OFFSET_IMM:
+    case BFD_RELOC_ARM_LITERAL:
       sign = value >= 0;
 
       if (value < 0)
       sign = value >= 0;
 
       if (value < 0)
@@ -9743,9 +10442,13 @@ md_apply_fix3 (fixP, valP, seg)
 
       if (validate_offset_imm (value, 0) == FAIL)
        {
 
       if (validate_offset_imm (value, 0) == FAIL)
        {
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("bad immediate value for offset (%ld)"),
-                       (long) value);
+         if (fixP->fx_r_type == BFD_RELOC_ARM_LITERAL)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("invalid literal constant: pool needs to be closer"));
+         else
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("bad immediate value for offset (%ld)"),
+                         (long) value);
          break;
        }
 
          break;
        }
 
@@ -9779,53 +10482,223 @@ md_apply_fix3 (fixP, valP, seg)
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
-    case BFD_RELOC_ARM_LITERAL:
-      sign = value >= 0;
-
-      if (value < 0)
-       value = - value;
-
-      if (validate_offset_imm (value, 0) == FAIL)
-       {
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("invalid literal constant: pool needs to be closer"));
-         break;
-       }
+    case BFD_RELOC_ARM_T32_OFFSET_U8:
+      if (value < 0 || value > 1020 || value % 4 != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("bad immediate value for offset (%ld)"), (long) value);
+      value /= 4;
 
 
-      newval = md_chars_to_number (buf, INSN_SIZE);
-      newval &= 0xff7ff000;
-      newval |= value | (sign ? INDEX_UP : 0);
-      md_number_to_chars (buf, newval, INSN_SIZE);
+      newval = md_chars_to_number (buf+2, THUMB_SIZE);
+      newval |= value;
+      md_number_to_chars (buf+2, newval, THUMB_SIZE);
       break;
 
       break;
 
-    case BFD_RELOC_ARM_SHIFT_IMM:
-      newval = md_chars_to_number (buf, INSN_SIZE);
-      if (((unsigned long) value) > 32
-         || (value == 32
-             && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
+    case BFD_RELOC_ARM_T32_OFFSET_IMM:
+      /* This is a complicated relocation used for all varieties of Thumb32
+        load/store instruction with immediate offset:
+
+        1110 100P u1WL NNNN XXXX YYYY iiii iiii - +/-(U) pre/post(P) 8-bit,
+                                                  *4, optional writeback(W)
+                                                  (doubleword load/store)
+
+        1111 100S uTTL 1111 XXXX iiii iiii iiii - +/-(U) 12-bit PC-rel
+        1111 100S 0TTL NNNN XXXX 1Pu1 iiii iiii - +/-(U) pre/post(P) 8-bit
+        1111 100S 0TTL NNNN XXXX 1110 iiii iiii - positive 8-bit (T instruction)
+        1111 100S 1TTL NNNN XXXX iiii iiii iiii - positive 12-bit
+        1111 100S 0TTL NNNN XXXX 1100 iiii iiii - negative 8-bit
+
+        Uppercase letters indicate bits that are already encoded at
+        this point.  Lowercase letters are our problem.  For the
+        second block of instructions, the secondary opcode nybble
+        (bits 8..11) is present, and bit 23 is zero, even if this is
+        a PC-relative operation.  */
+      newval = md_chars_to_number (buf, THUMB_SIZE);
+      newval <<= 16;
+      newval |= md_chars_to_number (buf+THUMB_SIZE, THUMB_SIZE);
+
+      if ((newval & 0xf0000000) == 0xe0000000)
        {
        {
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("shift expression is too large"));
-         break;
+         /* Doubleword load/store: 8-bit offset, scaled by 4.  */
+         if (value >= 0)
+           newval |= (1 << 23);
+         else
+           value = -value;
+         if (value % 4 != 0)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset not a multiple of 4"));
+             break;
+           }
+         value /= 4;
+         if (value >= 0xff)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset out of range"));
+             break;
+           }
+         newval &= ~0xff;
        }
        }
-
-      if (value == 0)
-       /* Shifts of zero must be done as lsl.  */
-       newval &= ~0x60;
-      else if (value == 32)
-       value = 0;
-      newval &= 0xfffff07f;
-      newval |= (value & 0x1f) << 7;
-      md_number_to_chars (buf, newval, INSN_SIZE);
-      break;
-
-    case BFD_RELOC_ARM_SWI:
-      if (arm_data->thumb_mode)
+      else if ((newval & 0x000f0000) == 0x000f0000)
+       {
+         /* PC-relative, 12-bit offset.  */
+         if (value >= 0)
+           newval |= (1 << 23);
+         else
+           value = -value;
+         if (value >= 0xfff)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset out of range"));
+             break;
+           }
+         newval &= ~0xfff;
+       }
+      else if ((newval & 0x00000100) == 0x00000100)
+       {
+         /* Writeback: 8-bit, +/- offset.  */
+         if (value >= 0)
+           newval |= (1 << 9);
+         else
+           value = -value;
+         if (value >= 0xff)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset out of range"));
+             break;
+           }
+         newval &= ~0xff;
+       }
+      else if ((newval & 0x00000f00) == 0x00000e00)
+       {
+         /* T-instruction: positive 8-bit offset.  */
+         if (value < 0 || value >= 0xff)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset out of range"));
+             break;
+           }
+         newval &= ~0xff;
+         newval |= value;
+       }
+      else
+       {
+         /* Positive 12-bit or negative 8-bit offset.  */
+         int limit;
+         if (value >= 0)
+           {
+             newval |= (1 << 23);
+             limit = 0xfff;
+           }
+         else
+           {
+             value = -value;
+             limit = 0xff;
+           }
+         if (value > limit)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("offset out of range"));
+             break;
+           }
+         newval &= ~limit;
+       }
+
+      newval |= value;
+      md_number_to_chars (buf, (newval >> 16) & 0xffff, THUMB_SIZE);
+      md_number_to_chars (buf + THUMB_SIZE, newval & 0xffff, THUMB_SIZE);
+      break;
+
+    case BFD_RELOC_ARM_SHIFT_IMM:
+      newval = md_chars_to_number (buf, INSN_SIZE);
+      if (((unsigned long) value) > 32
+         || (value == 32
+             && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("shift expression is too large"));
+         break;
+       }
+
+      if (value == 0)
+       /* Shifts of zero must be done as lsl.  */
+       newval &= ~0x60;
+      else if (value == 32)
+       value = 0;
+      newval &= 0xfffff07f;
+      newval |= (value & 0x1f) << 7;
+      md_number_to_chars (buf, newval, INSN_SIZE);
+      break;
+
+    case BFD_RELOC_ARM_T32_IMMEDIATE:
+    case BFD_RELOC_ARM_T32_IMM12:
+    case BFD_RELOC_ARM_T32_ADD_PC12:
+      /* We claim that this fixup has been processed here,
+        even if in fact we generate an error because we do
+        not have a reloc for it, so tc_gen_reloc will reject it.  */
+      fixP->fx_done = 1;
+
+      if (fixP->fx_addsy
+         && ! S_IS_DEFINED (fixP->fx_addsy))
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("undefined symbol %s used as an immediate value"),
+                       S_GET_NAME (fixP->fx_addsy));
+         break;
+       }
+
+      newval = md_chars_to_number (buf, THUMB_SIZE);
+      newval <<= 16;
+      newval |= md_chars_to_number (buf+2, THUMB_SIZE);
+
+      /* FUTURE: Implement analogue of negate_data_op for T32.  */
+      if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE)
+       newimm = encode_thumb32_immediate (value);
+      else
+       {
+         /* 12 bit immediate for addw/subw.  */
+         if (value < 0)
+           {
+             value = -value;
+             newval ^= 0x00a00000;
+           }
+         if (value > 0xfff)
+           newimm = (unsigned int) FAIL;
+         else
+           newimm = value;
+       }
+
+      if (newimm == (unsigned int)FAIL)
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid constant (%lx) after fixup"),
+                       (unsigned long) value);
+         break;
+       }
+
+      newval |= (newimm & 0x800) << 15;
+      newval |= (newimm & 0x700) << 4;
+      newval |= (newimm & 0x0ff);
+
+      md_number_to_chars (buf,   (valueT) ((newval >> 16) & 0xffff), THUMB_SIZE);
+      md_number_to_chars (buf+2, (valueT) (newval & 0xffff), THUMB_SIZE);
+      break;
+
+    case BFD_RELOC_ARM_SMI:
+      if (((unsigned long) value) > 0xffff)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("invalid smi expression"));
+      newval = md_chars_to_number (buf, INSN_SIZE);
+      newval |= (value & 0xf) | ((value & 0xfff0) << 4);
+      md_number_to_chars (buf, newval, INSN_SIZE);
+      break;
+
+    case BFD_RELOC_ARM_SWI:
+      if (fixP->tc_fix_data != 0)
        {
          if (((unsigned long) value) > 0xff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid swi expression"));
        {
          if (((unsigned long) value) > 0xff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid swi expression"));
-         newval = md_chars_to_number (buf, THUMB_SIZE) & 0xff00;
+         newval = md_chars_to_number (buf, THUMB_SIZE);
          newval |= value;
          md_number_to_chars (buf, newval, THUMB_SIZE);
        }
          newval |= value;
          md_number_to_chars (buf, newval, THUMB_SIZE);
        }
@@ -9834,7 +10707,7 @@ md_apply_fix3 (fixP, valP, seg)
          if (((unsigned long) value) > 0x00ffffff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid swi expression"));
          if (((unsigned long) value) > 0x00ffffff)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid swi expression"));
-         newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000;
+         newval = md_chars_to_number (buf, INSN_SIZE);
          newval |= value;
          md_number_to_chars (buf, newval, INSN_SIZE);
        }
          newval |= value;
          md_number_to_chars (buf, newval, INSN_SIZE);
        }
@@ -9849,212 +10722,246 @@ md_apply_fix3 (fixP, valP, seg)
       break;
 
     case BFD_RELOC_ARM_PCREL_BRANCH:
       break;
 
     case BFD_RELOC_ARM_PCREL_BRANCH:
-      newval = md_chars_to_number (buf, INSN_SIZE);
-
-      /* Sign-extend a 24-bit number.  */
-#define SEXT24(x)      ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000)
-
 #ifdef OBJ_ELF
 #ifdef OBJ_ELF
-      if (! target_oabi)
-       value = fixP->fx_offset;
+    case BFD_RELOC_ARM_PLT32:
 #endif
 
       /* We are going to store value (shifted right by two) in the
 #endif
 
       /* We are going to store value (shifted right by two) in the
-        instruction, in a 24 bit, signed field.  Thus we need to check
-        that none of the top 8 bits of the shifted value (top 7 bits of
-         the unshifted, unsigned value) are set, or that they are all set.  */
-      if ((value & ~ ((offsetT) 0x1ffffff)) != 0
-         && ((value & ~ ((offsetT) 0x1ffffff)) != ~ ((offsetT) 0x1ffffff)))
-       {
-#ifdef OBJ_ELF
-         /* Normally we would be stuck at this point, since we cannot store
-            the absolute address that is the destination of the branch in the
-            24 bits of the branch instruction.  If however, we happen to know
-            that the destination of the branch is in the same section as the
-            branch instruciton itself, then we can compute the relocation for
-            ourselves and not have to bother the linker with it.
-
-            FIXME: The tests for OBJ_ELF and ! target_oabi are only here
-            because I have not worked out how to do this for OBJ_COFF or
-            target_oabi.  */
-         if (! target_oabi
-             && fixP->fx_addsy != NULL
-             && S_IS_DEFINED (fixP->fx_addsy)
-             && S_GET_SEGMENT (fixP->fx_addsy) == seg)
-           {
-             /* Get pc relative value to go into the branch.  */
-             value = * valP;
-
-             /* Permit a backward branch provided that enough bits
-                are set.  Allow a forwards branch, provided that
-                enough bits are clear.  */
-             if (   (value & ~ ((offsetT) 0x1ffffff)) == ~ ((offsetT) 0x1ffffff)
-                 || (value & ~ ((offsetT) 0x1ffffff)) == 0)
-               fixP->fx_done = 1;
-           }
+        instruction, in a 24 bit, signed field.  Bits 0 and 1 must be
+        clear, and bits 26 through 32 either all clear or all set. */
+      if (value & 0x00000003)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("misaligned branch destination"));
+      if ((value & (offsetT)0xfe000000) != (offsetT)0
+         && (value & (offsetT)0xfe000000) != (offsetT)0xfe000000)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
 
 
-         if (! fixP->fx_done)
-#endif
-           as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("GAS can't handle same-section branch dest >= 0x04000000"));
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         newval = md_chars_to_number (buf, INSN_SIZE);
+         newval |= (value >> 2) & 0x00ffffff;
+         md_number_to_chars (buf, newval, INSN_SIZE);
        }
        }
+      break;
 
 
-      value >>= 2;
-      value += SEXT24 (newval);
-
-      if (    (value & ~ ((offsetT) 0xffffff)) != 0
-         && ((value & ~ ((offsetT) 0xffffff)) != ~ ((offsetT) 0xffffff)))
+    case BFD_RELOC_ARM_PCREL_BLX:
+      /* BLX allows bit 1 to be set in the branch destination, since
+        it targets a Thumb instruction which is only required to be
+        aligned modulo 2.  Other constraints are as for B/BL.  */
+      if (value & 0x00000001)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("misaligned BLX destination"));
+      if ((value & (offsetT)0xfe000000) != (offsetT)0
+         && (value & (offsetT)0xfe000000) != (offsetT)0xfe000000)
        as_bad_where (fixP->fx_file, fixP->fx_line,
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("out of range branch"));
+                     _("branch out of range"));
 
 
-      newval = (value & 0x00ffffff) | (newval & 0xff000000);
-      md_number_to_chars (buf, newval, INSN_SIZE);
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         offsetT hbit;
+         hbit   = (value >> 1) & 1;
+         value  = (value >> 2) & 0x00ffffff;
+
+         newval = md_chars_to_number (buf, INSN_SIZE);
+         newval |= value | hbit << 24;
+         md_number_to_chars (buf, newval, INSN_SIZE);
+       }
       break;
 
       break;
 
-    case BFD_RELOC_ARM_PCREL_BLX:
-      {
-       offsetT hbit;
-       newval = md_chars_to_number (buf, INSN_SIZE);
+    case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CZB */
+      /* CZB can only branch forward.  */
+      if (value & ~0x7e)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
 
 
-#ifdef OBJ_ELF
-       if (! target_oabi)
-         value = fixP->fx_offset;
-#endif
-       hbit   = (value >> 1) & 1;
-       value  = (value >> 2) & 0x00ffffff;
-       value  = (value + (newval & 0x00ffffff)) & 0x00ffffff;
-       newval = value | (newval & 0xfe000000) | (hbit << 24);
-       md_number_to_chars (buf, newval, INSN_SIZE);
-      }
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         newval = md_chars_to_number (buf, THUMB_SIZE);
+         newval |= ((value & 0x2e) << 2) | ((value & 0x40) << 3);
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+       }
       break;
 
       break;
 
-    case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch.  */
-      newval = md_chars_to_number (buf, THUMB_SIZE);
-      {
-       addressT diff = (newval & 0xff) << 1;
-       if (diff & 0x100)
-         diff |= ~0xff;
+    case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
+      if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
 
 
-       value += diff;
-       if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("branch out of range"));
-       newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
-      }
-      md_number_to_chars (buf, newval, THUMB_SIZE);
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         newval = md_chars_to_number (buf, THUMB_SIZE);
+         newval |= (value & 0x1ff) >> 1;
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+       }
       break;
 
     case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch.  */
       break;
 
     case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch.  */
-      newval = md_chars_to_number (buf, THUMB_SIZE);
-      {
-       addressT diff = (newval & 0x7ff) << 1;
-       if (diff & 0x800)
-         diff |= ~0x7ff;
+      if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
 
 
-       value += diff;
-       if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("branch out of range"));
-       newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
-      }
-      md_number_to_chars (buf, newval, THUMB_SIZE);
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         newval = md_chars_to_number (buf, THUMB_SIZE);
+         newval |= (value & 0xfff) >> 1;
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+       }
+      break;
+
+    case BFD_RELOC_THUMB_PCREL_BRANCH20:
+      if ((value & ~0x1fffff) && ((value & ~0x1fffff) != ~0x1fffff))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("conditional branch out of range"));
+
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         offsetT newval2;
+         addressT S, J1, J2, lo, hi;
+
+         S  = (value & 0x00100000) >> 20;
+         J2 = (value & 0x00080000) >> 19;
+         J1 = (value & 0x00040000) >> 18;
+         hi = (value & 0x0003f000) >> 12;
+         lo = (value & 0x00000ffe) >> 1;
+
+         newval   = md_chars_to_number (buf, THUMB_SIZE);
+         newval2  = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+         newval  |= (S << 10) | hi;
+         newval2 |= (J1 << 13) | (J2 << 11) | lo;
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+         md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+       }
       break;
 
     case BFD_RELOC_THUMB_PCREL_BLX:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
       break;
 
     case BFD_RELOC_THUMB_PCREL_BLX:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
-      {
-       offsetT newval2;
-       addressT diff;
-
-       newval  = md_chars_to_number (buf, THUMB_SIZE);
-       newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
-       diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
-       if (diff & 0x400000)
-         diff |= ~0x3fffff;
-#ifdef OBJ_ELF
-       value = fixP->fx_offset;
-#endif
-       value += diff;
+      if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
 
 
-       if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("branch with link out of range"));
-
-       newval  = (newval  & 0xf800) | ((value & 0x7fffff) >> 12);
-       newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
-       if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
-         /* For a BLX instruction, make sure that the relocation is rounded up
-            to a word boundary.  This follows the semantics of the instruction
-            which specifies that bit 1 of the target address will come from bit
-            1 of the base address.  */
-         newval2 = (newval2 + 1) & ~ 1;
-       md_number_to_chars (buf, newval, THUMB_SIZE);
-       md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
-      }
+      if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+       /* For a BLX instruction, make sure that the relocation is rounded up
+          to a word boundary.  This follows the semantics of the instruction
+          which specifies that bit 1 of the target address will come from bit
+          1 of the base address.  */
+       value = (value + 1) & ~ 1;
+
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         offsetT newval2;
+
+         newval   = md_chars_to_number (buf, THUMB_SIZE);
+         newval2  = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+         newval  |= (value & 0x7fffff) >> 12;
+         newval2 |= (value & 0xfff) >> 1;
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+         md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+       }
       break;
 
       break;
 
-    case BFD_RELOC_8:
-      if (fixP->fx_done || fixP->fx_pcrel)
-       md_number_to_chars (buf, value, 1);
-#ifdef OBJ_ELF
-      else if (!target_oabi)
+    case BFD_RELOC_THUMB_PCREL_BRANCH25:
+      if ((value & ~0x1ffffff) && ((value & ~0x1ffffff) != ~0x1ffffff))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
+
+      if (fixP->fx_done || !seg->use_rela_p)
        {
        {
-         value = fixP->fx_offset;
-         md_number_to_chars (buf, value, 1);
+         offsetT newval2;
+         addressT S, I1, I2, lo, hi;
+
+         S  = (value & 0x01000000) >> 24;
+         I1 = (value & 0x00800000) >> 23;
+         I2 = (value & 0x00400000) >> 22;
+         hi = (value & 0x003ff000) >> 12;
+         lo = (value & 0x00000ffe) >> 1;
+
+         I1 = !(I1 ^ S);
+         I2 = !(I2 ^ S);
+
+         newval   = md_chars_to_number (buf, THUMB_SIZE);
+         newval2  = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+         newval  |= (S << 10) | hi;
+         newval2 |= (I1 << 13) | (I2 << 11) | lo;
+         md_number_to_chars (buf, newval, THUMB_SIZE);
+         md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
        }
        }
-#endif
+      break;
+
+    case BFD_RELOC_8:
+      if (fixP->fx_done || !seg->use_rela_p)
+       md_number_to_chars (buf, value, 1);
       break;
 
     case BFD_RELOC_16:
       break;
 
     case BFD_RELOC_16:
-      if (fixP->fx_done || fixP->fx_pcrel)
+      if (fixP->fx_done || !seg->use_rela_p)
        md_number_to_chars (buf, value, 2);
        md_number_to_chars (buf, value, 2);
-#ifdef OBJ_ELF
-      else if (!target_oabi)
-       {
-         value = fixP->fx_offset;
-         md_number_to_chars (buf, value, 2);
-       }
-#endif
       break;
 
 #ifdef OBJ_ELF
       break;
 
 #ifdef OBJ_ELF
+    case BFD_RELOC_ARM_TLS_GD32:
+    case BFD_RELOC_ARM_TLS_LE32:
+    case BFD_RELOC_ARM_TLS_IE32:
+    case BFD_RELOC_ARM_TLS_LDM32:
+    case BFD_RELOC_ARM_TLS_LDO32:
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* fall through */
+
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
-      md_number_to_chars (buf, 0, 4);
+    case BFD_RELOC_ARM_TARGET2:
+      if (fixP->fx_done || !seg->use_rela_p)
+       md_number_to_chars (buf, 0, 4);
       break;
 #endif
 
     case BFD_RELOC_RVA:
     case BFD_RELOC_32:
       break;
 #endif
 
     case BFD_RELOC_RVA:
     case BFD_RELOC_32:
-      if (fixP->fx_done || fixP->fx_pcrel)
+    case BFD_RELOC_ARM_TARGET1:
+    case BFD_RELOC_ARM_ROSEGREL32:
+    case BFD_RELOC_ARM_SBREL32:
+    case BFD_RELOC_32_PCREL:
+      if (fixP->fx_done || !seg->use_rela_p)
        md_number_to_chars (buf, value, 4);
        md_number_to_chars (buf, value, 4);
-#ifdef OBJ_ELF
-      else if (!target_oabi)
-       {
-         value = fixP->fx_offset;
-         md_number_to_chars (buf, value, 4);
-       }
-#endif
       break;
 
 #ifdef OBJ_ELF
       break;
 
 #ifdef OBJ_ELF
-    case BFD_RELOC_ARM_PLT32:
-      /* It appears the instruction is fully prepared at this point.  */
+    case BFD_RELOC_ARM_PREL31:
+      if (fixP->fx_done || !seg->use_rela_p)
+       {
+         newval = md_chars_to_number (buf, 4) & 0x80000000;
+         if ((value ^ (value >> 1)) & 0x40000000)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("rel31 relocation overflow"));
+           }
+         newval |= value & 0x7fffffff;
+         md_number_to_chars (buf, newval, 4);
+       }
       break;
 #endif
 
     case BFD_RELOC_ARM_CP_OFF_IMM:
       break;
 #endif
 
     case BFD_RELOC_ARM_CP_OFF_IMM:
-      sign = value >= 0;
       if (value < -1023 || value > 1023 || (value & 3))
        as_bad_where (fixP->fx_file, fixP->fx_line,
       if (value < -1023 || value > 1023 || (value & 3))
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("illegal value for co-processor offset"));
+                     _("co-processor offset out of range"));
+    cp_off_common:
+      sign = value >= 0;
       if (value < 0)
        value = -value;
       newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
       newval |= (value >> 2) | (sign ? INDEX_UP : 0);
       if (value < 0)
        value = -value;
       newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
       newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+      if (value == 0)
+       newval &= ~WRITE_BACK;
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
+    case BFD_RELOC_ARM_CP_OFF_IMM_S2:
+      if (value < -255 || value > 255)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("co-processor offset out of range"));
+      goto cp_off_common;
+
     case BFD_RELOC_ARM_THUMB_OFFSET:
       newval = md_chars_to_number (buf, THUMB_SIZE);
       /* Exactly what ranges, and where the offset is inserted depends
     case BFD_RELOC_ARM_THUMB_OFFSET:
       newval = md_chars_to_number (buf, THUMB_SIZE);
       /* Exactly what ranges, and where the offset is inserted depends
@@ -10064,24 +10971,20 @@ md_apply_fix3 (fixP, valP, seg)
        {
        case 4: /* PC load.  */
          /* Thumb PC loads are somewhat odd, bit 1 of the PC is
        {
        case 4: /* PC load.  */
          /* Thumb PC loads are somewhat odd, bit 1 of the PC is
-            forced to zero for these loads, so we will need to round
-            up the offset if the instruction address is not word
-            aligned (since the final address produced must be, and
-            we can only describe word-aligned immediate offsets).  */
-
-         if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
+            forced to zero for these loads; md_pcrel_from has already
+            compensated for this.  */
+         if (value & 3)
            as_bad_where (fixP->fx_file, fixP->fx_line,
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("invalid offset, target not word aligned (0x%08X)"),
-                         (unsigned int) (fixP->fx_frag->fr_address
-                                         + fixP->fx_where + value));
+                         _("invalid offset, target not word aligned (0x%08lX)"),
+                         (((unsigned int) fixP->fx_frag->fr_address
+                           + (unsigned int) fixP->fx_where) & ~3) + value);
 
 
-         if ((value + 2) & ~0x3fe)
+         if (value & ~0x3fc)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid offset, value too big (0x%08lX)"),
                          (long) value);
 
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid offset, value too big (0x%08lX)"),
                          (long) value);
 
-         /* Round up, since pc will be rounded down.  */
-         newval |= (value + 2) >> 2;
+         newval |= value >> 2;
          break;
 
        case 9: /* SP load/store.  */
          break;
 
        case 9: /* SP load/store.  */
@@ -10108,7 +11011,7 @@ md_apply_fix3 (fixP, valP, seg)
          newval |= value << 6;
          break;
 
          newval |= value << 6;
          break;
 
-       case 8: /* Halfword load/store.  */
+       case 8: /* Halfword load/store.  */
          if (value & ~0x3e)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid offset, value too big (0x%08lX)"),
          if (value & ~0x3e)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("invalid offset, value too big (0x%08lX)"),
@@ -10127,15 +11030,15 @@ md_apply_fix3 (fixP, valP, seg)
 
     case BFD_RELOC_ARM_THUMB_ADD:
       /* This is a complicated relocation, since we use it for all of
 
     case BFD_RELOC_ARM_THUMB_ADD:
       /* This is a complicated relocation, since we use it for all of
-         the following immediate relocations:
+        the following immediate relocations:
 
            3bit ADD/SUB
            8bit ADD/SUB
            9bit ADD/SUB SP word-aligned
           10bit ADD PC/SP word-aligned
 
 
            3bit ADD/SUB
            8bit ADD/SUB
            9bit ADD/SUB SP word-aligned
           10bit ADD PC/SP word-aligned
 
-         The type of instruction being processed is encoded in the
-         instruction field:
+        The type of instruction being processed is encoded in the
+        instruction field:
 
           0x8000  SUB
           0x00F0  Rd
 
           0x8000  SUB
           0x00F0  Rd
@@ -10145,7 +11048,24 @@ md_apply_fix3 (fixP, valP, seg)
       {
        int rd = (newval >> 4) & 0xf;
        int rs = newval & 0xf;
       {
        int rd = (newval >> 4) & 0xf;
        int rs = newval & 0xf;
-       int subtract = newval & 0x8000;
+       int subtract = !!(newval & 0x8000);
+
+       /* Check for HI regs, only very restricted cases allowed:
+          Adjusting SP, and using PC or SP to get an address.  */
+       if ((rd > 7 && (rd != REG_SP || rs != REG_SP))
+           || (rs > 7 && rs != REG_SP && rs != REG_PC))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid Hi register with immediate"));
+
+       /* If value is negative, choose the opposite instruction.  */
+       if (value < 0)
+         {
+           value = -value;
+           subtract = !subtract;
+           if (value < 0)
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("immediate value out of range"));
+         }
 
        if (rd == REG_SP)
          {
 
        if (rd == REG_SP)
          {
@@ -10157,8 +11077,7 @@ md_apply_fix3 (fixP, valP, seg)
          }
        else if (rs == REG_PC || rs == REG_SP)
          {
          }
        else if (rs == REG_PC || rs == REG_SP)
          {
-           if (subtract ||
-               value & ~0x3fc)
+           if (subtract || value & ~0x3fc)
              as_bad_where (fixP->fx_file, fixP->fx_line,
                            _("invalid immediate for address calculation (value = 0x%08lX)"),
                            (unsigned long) value);
              as_bad_where (fixP->fx_file, fixP->fx_line,
                            _("invalid immediate for address calculation (value = 0x%08lX)"),
                            (unsigned long) value);
@@ -10170,7 +11089,7 @@ md_apply_fix3 (fixP, valP, seg)
          {
            if (value & ~0xff)
              as_bad_where (fixP->fx_file, fixP->fx_line,
          {
            if (value & ~0xff)
              as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("invalid 8bit immediate"));
+                           _("immediate value out of range"));
            newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
            newval |= (rd << 8) | value;
          }
            newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
            newval |= (rd << 8) | value;
          }
@@ -10178,7 +11097,7 @@ md_apply_fix3 (fixP, valP, seg)
          {
            if (value & ~0x7)
              as_bad_where (fixP->fx_file, fixP->fx_line,
          {
            if (value & ~0x7)
              as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("invalid 3bit immediate"));
+                           _("immediate value out of range"));
            newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
            newval |= rd | (rs << 3) | (value << 6);
          }
            newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
            newval |= rd | (rs << 3) | (value << 6);
          }
@@ -10188,29 +11107,27 @@ md_apply_fix3 (fixP, valP, seg)
 
     case BFD_RELOC_ARM_THUMB_IMM:
       newval = md_chars_to_number (buf, THUMB_SIZE);
 
     case BFD_RELOC_ARM_THUMB_IMM:
       newval = md_chars_to_number (buf, THUMB_SIZE);
-      switch (newval >> 11)
-       {
-       case 0x04: /* 8bit immediate MOV.  */
-       case 0x05: /* 8bit immediate CMP.  */
-         if (value < 0 || value > 255)
-           as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("invalid immediate: %ld is too large"),
-                         (long) value);
-         newval |= value;
-         break;
-
-       default:
-         abort ();
-       }
+      if (value < 0 || value > 255)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("invalid immediate: %ld is too large"),
+                     (long) value);
+      newval |= value;
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
     case BFD_RELOC_ARM_THUMB_SHIFT:
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
     case BFD_RELOC_ARM_THUMB_SHIFT:
-      /* 5bit shift value (0..31).  */
-      if (value < 0 || value > 31)
+      /* 5bit shift value (0..32).  LSL cannot take 32.         */
+      newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf83f;
+      temp = newval & 0xf800;
+      if (value < 0 || value > 32 || (value == 32 && temp == T_OPCODE_LSL_I))
        as_bad_where (fixP->fx_file, fixP->fx_line,
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("illegal Thumb shift value: %ld"), (long) value);
-      newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
+                     _("invalid shift value: %ld"), (long) value);
+      /* Shifts of zero must be encoded as LSL.         */
+      if (value == 0)
+       newval = (newval & 0x003f) | T_OPCODE_LSL_I;
+      /* Shifts of 32 are encoded as zero.  */
+      else if (value == 32)
+       value = 0;
       newval |= value << 6;
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
       newval |= value << 6;
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
@@ -10220,7 +11137,7 @@ md_apply_fix3 (fixP, valP, seg)
       fixP->fx_done = 0;
       return;
 
       fixP->fx_done = 0;
       return;
 
-    case BFD_RELOC_NONE:
+    case BFD_RELOC_UNUSED:
     default:
       as_bad_where (fixP->fx_file, fixP->fx_line,
                    _("bad relocation fixup type (%d)"), fixP->fx_r_type);
     default:
       as_bad_where (fixP->fx_file, fixP->fx_line,
                    _("bad relocation fixup type (%d)"), fixP->fx_r_type);
@@ -10231,28 +11148,21 @@ md_apply_fix3 (fixP, valP, seg)
    format.  */
 
 arelent *
    format.  */
 
 arelent *
-tc_gen_reloc (section, fixp)
-     asection * section ATTRIBUTE_UNUSED;
-     fixS * fixp;
+tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
+             fixS *     fixp)
 {
   arelent * reloc;
   bfd_reloc_code_real_type code;
 
 {
   arelent * reloc;
   bfd_reloc_code_real_type code;
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
+  reloc = xmalloc (sizeof (arelent));
 
 
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
-  /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
-#ifndef OBJ_ELF
-  if (fixp->fx_pcrel == 0)
-    reloc->addend = fixp->fx_offset;
-  else
-    reloc->addend = fixp->fx_offset = reloc->address;
-#else  /* OBJ_ELF */
+  if (fixp->fx_pcrel)
+    fixp->fx_offset = reloc->address;
   reloc->addend = fixp->fx_offset;
   reloc->addend = fixp->fx_offset;
-#endif
 
   switch (fixp->fx_r_type)
     {
 
   switch (fixp->fx_r_type)
     {
@@ -10277,12 +11187,16 @@ tc_gen_reloc (section, fixp)
          break;
        }
 
          break;
        }
 
+    case BFD_RELOC_NONE:
     case BFD_RELOC_ARM_PCREL_BRANCH:
     case BFD_RELOC_ARM_PCREL_BLX:
     case BFD_RELOC_RVA:
     case BFD_RELOC_ARM_PCREL_BRANCH:
     case BFD_RELOC_ARM_PCREL_BLX:
     case BFD_RELOC_RVA:
+    case BFD_RELOC_THUMB_PCREL_BRANCH7:
     case BFD_RELOC_THUMB_PCREL_BRANCH9:
     case BFD_RELOC_THUMB_PCREL_BRANCH12:
     case BFD_RELOC_THUMB_PCREL_BRANCH9:
     case BFD_RELOC_THUMB_PCREL_BRANCH12:
+    case BFD_RELOC_THUMB_PCREL_BRANCH20:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
+    case BFD_RELOC_THUMB_PCREL_BRANCH25:
     case BFD_RELOC_THUMB_PCREL_BLX:
     case BFD_RELOC_VTABLE_ENTRY:
     case BFD_RELOC_VTABLE_INHERIT:
     case BFD_RELOC_THUMB_PCREL_BLX:
     case BFD_RELOC_VTABLE_ENTRY:
     case BFD_RELOC_VTABLE_INHERIT:
@@ -10301,6 +11215,23 @@ tc_gen_reloc (section, fixp)
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
     case BFD_RELOC_ARM_PLT32:
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
     case BFD_RELOC_ARM_PLT32:
+    case BFD_RELOC_ARM_TARGET1:
+    case BFD_RELOC_ARM_ROSEGREL32:
+    case BFD_RELOC_ARM_SBREL32:
+    case BFD_RELOC_ARM_PREL31:
+    case BFD_RELOC_ARM_TARGET2:
+    case BFD_RELOC_ARM_TLS_LE32:
+    case BFD_RELOC_ARM_TLS_LDO32:
+      code = fixp->fx_r_type;
+      break;
+
+    case BFD_RELOC_ARM_TLS_GD32:
+    case BFD_RELOC_ARM_TLS_IE32:
+    case BFD_RELOC_ARM_TLS_LDM32:
+      /* BFD will include the symbol's address in the addend.
+        But we don't want that, so subtract it out again here.  */
+      if (!S_IS_COMMON (fixp->fx_addsy))
+       reloc->addend -= (*reloc->sym_ptr_ptr)->value;
       code = fixp->fx_r_type;
       break;
 #endif
       code = fixp->fx_r_type;
       break;
 #endif
@@ -10316,6 +11247,16 @@ tc_gen_reloc (section, fixp)
       return NULL;
 
     case BFD_RELOC_ARM_OFFSET_IMM:
       return NULL;
 
     case BFD_RELOC_ARM_OFFSET_IMM:
+      if (fixp->fx_addsy != NULL
+         && !S_IS_DEFINED (fixp->fx_addsy)
+         && S_IS_LOCAL (fixp->fx_addsy))
+       {
+         as_bad_where (fixp->fx_file, fixp->fx_line,
+                       _("undefined local label `%s'"),
+                       S_GET_NAME (fixp->fx_addsy));
+         return NULL;
+       }
+
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("internal_relocation (type: OFFSET_IMM) not fixed up"));
       return NULL;
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("internal_relocation (type: OFFSET_IMM) not fixed up"));
       return NULL;
@@ -10326,16 +11267,18 @@ tc_gen_reloc (section, fixp)
 
        switch (fixp->fx_r_type)
          {
 
        switch (fixp->fx_r_type)
          {
+         case BFD_RELOC_NONE:             type = "NONE";         break;
          case BFD_RELOC_ARM_OFFSET_IMM8:  type = "OFFSET_IMM8";  break;
          case BFD_RELOC_ARM_OFFSET_IMM8:  type = "OFFSET_IMM8";  break;
-         case BFD_RELOC_ARM_SHIFT_IMM:    type = "SHIFT_IMM";    break;
-         case BFD_RELOC_ARM_SWI:          type = "SWI";          break;
-         case BFD_RELOC_ARM_MULTI:        type = "MULTI";        break;
-         case BFD_RELOC_ARM_CP_OFF_IMM:   type = "CP_OFF_IMM";   break;
-         case BFD_RELOC_ARM_THUMB_ADD:    type = "THUMB_ADD";    break;
+         case BFD_RELOC_ARM_SHIFT_IMM:    type = "SHIFT_IMM";    break;
+         case BFD_RELOC_ARM_SMI:          type = "SMI";          break;
+         case BFD_RELOC_ARM_SWI:          type = "SWI";          break;
+         case BFD_RELOC_ARM_MULTI:        type = "MULTI";        break;
+         case BFD_RELOC_ARM_CP_OFF_IMM:   type = "CP_OFF_IMM";   break;
+         case BFD_RELOC_ARM_THUMB_ADD:    type = "THUMB_ADD";    break;
          case BFD_RELOC_ARM_THUMB_SHIFT:  type = "THUMB_SHIFT";  break;
          case BFD_RELOC_ARM_THUMB_SHIFT:  type = "THUMB_SHIFT";  break;
-         case BFD_RELOC_ARM_THUMB_IMM:    type = "THUMB_IMM";    break;
+         case BFD_RELOC_ARM_THUMB_IMM:    type = "THUMB_IMM";    break;
          case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
          case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
-         default:                         type = _("<unknown>"); break;
+         default:                         type = _("<unknown>"); break;
          }
        as_bad_where (fixp->fx_file, fixp->fx_line,
                      _("cannot represent %s relocation in this object file format"),
          }
        as_bad_where (fixp->fx_file, fixp->fx_line,
                      _("cannot represent %s relocation in this object file format"),
@@ -10372,206 +11315,529 @@ tc_gen_reloc (section, fixp)
   return reloc;
 }
 
   return reloc;
 }
 
-int
-md_estimate_size_before_relax (fragP, segtype)
-     fragS * fragP ATTRIBUTE_UNUSED;
-     segT    segtype ATTRIBUTE_UNUSED;
-{
-  as_fatal (_("md_estimate_size_before_relax\n"));
-  return 1;
-}
+/* This fix_new is called by cons via TC_CONS_FIX_NEW. */
 
 
-static void
-output_inst (str)
-     const char *str;
+void
+cons_fix_new_arm (fragS *      frag,
+                 int           where,
+                 int           size,
+                 expressionS * exp)
 {
 {
-  char * to = NULL;
+  bfd_reloc_code_real_type type;
+  int pcrel = 0;
 
 
-  if (inst.error)
+  /* Pick a reloc.
+     FIXME: @@ Should look at CPU word size.  */
+  switch (size)
     {
     {
-      as_bad ("%s -- `%s'", inst.error, str);
-      return;
+    case 1:
+      type = BFD_RELOC_8;
+      break;
+    case 2:
+      type = BFD_RELOC_16;
+      break;
+    case 4:
+    default:
+      type = BFD_RELOC_32;
+      break;
+    case 8:
+      type = BFD_RELOC_64;
+      break;
     }
 
     }
 
-  to = frag_more (inst.size);
+  fix_new_exp (frag, where, (int) size, exp, pcrel, type);
+}
 
 
-  if (thumb_mode && (inst.size > THUMB_SIZE))
-    {
-      assert (inst.size == (2 * THUMB_SIZE));
-      md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
-      md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
-    }
-  else if (inst.size > INSN_SIZE)
+#if defined OBJ_COFF || defined OBJ_ELF
+void
+arm_validate_fix (fixS * fixP)
+{
+  /* If the destination of the branch is a defined symbol which does not have
+     the THUMB_FUNC attribute, then we must be calling a function which has
+     the (interfacearm) attribute.  We look for the Thumb entry point to that
+     function and change the branch to refer to that function instead. */
+  if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
+      && fixP->fx_addsy != NULL
+      && S_IS_DEFINED (fixP->fx_addsy)
+      && ! THUMB_IS_FUNC (fixP->fx_addsy))
     {
     {
-      assert (inst.size == (2 * INSN_SIZE));
-      md_number_to_chars (to, inst.instruction, INSN_SIZE);
-      md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
+      fixP->fx_addsy = find_real_start (fixP->fx_addsy);
     }
     }
-  else
-    md_number_to_chars (to, inst.instruction, inst.size);
+}
+#endif
 
 
-  if (inst.reloc.type != BFD_RELOC_NONE)
-    fix_new_arm (frag_now, to - frag_now->fr_literal,
-                inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
-                inst.reloc.type);
+int
+arm_force_relocation (struct fix * fixp)
+{
+#if defined (OBJ_COFF) && defined (TE_PE)
+  if (fixp->fx_r_type == BFD_RELOC_RVA)
+    return 1;
+#endif
+
+  /* Resolve these relocations even if the symbol is extern or weak.  */
+  if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
+      || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
+      || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+    return 0;
+
+  return generic_force_reloc (fixp);
+}
+
+#ifdef OBJ_COFF
+/* This is a little hack to help the gas/arm/adrl.s test.  It prevents
+   local labels from being added to the output symbol table when they
+   are used with the ADRL pseudo op.  The ADRL relocation should always
+   be resolved before the binbary is emitted, so it is safe to say that
+   it is adjustable.  */
+
+bfd_boolean
+arm_fix_adjustable (fixS * fixP)
+{
+  if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+    return 1;
+  return 0;
+}
+#endif
 
 #ifdef OBJ_ELF
 
 #ifdef OBJ_ELF
-  dwarf2_emit_insn (inst.size);
+/* Relocations against Thumb function names must be left unadjusted,
+   so that the linker can use this information to correctly set the
+   bottom bit of their addresses.  The MIPS version of this function
+   also prevents relocations that are mips-16 specific, but I do not
+   know why it does this.
+
+   FIXME:
+   There is one other problem that ought to be addressed here, but
+   which currently is not:  Taking the address of a label (rather
+   than a function) and then later jumping to that address.  Such
+   addresses also ought to have their bottom bit set (assuming that
+   they reside in Thumb code), but at the moment they will not.         */
+
+bfd_boolean
+arm_fix_adjustable (fixS * fixP)
+{
+  if (fixP->fx_addsy == NULL)
+    return 1;
+
+  if (THUMB_IS_FUNC (fixP->fx_addsy)
+      && fixP->fx_subsy == NULL)
+    return 0;
+
+  /* We need the symbol name for the VTABLE entries.  */
+  if (  fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 0;
+
+  /* Don't allow symbols to be discarded on GOT related relocs.         */
+  if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
+      || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
+      || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
+    return 0;
+
+  return 1;
+}
+
+const char *
+elf32_arm_target_format (void)
+{
+#ifdef TE_SYMBIAN
+  return (target_big_endian
+         ? "elf32-bigarm-symbian"
+         : "elf32-littlearm-symbian");
+#elif defined (TE_VXWORKS)
+  return (target_big_endian
+         ? "elf32-bigarm-vxworks"
+         : "elf32-littlearm-vxworks");
+#else
+  if (target_big_endian)
+    return "elf32-bigarm";
+  else
+    return "elf32-littlearm";
 #endif
 }
 
 void
 #endif
 }
 
 void
-md_assemble (str)
-     char * str;
+armelf_frob_symbol (symbolS * symp,
+                   int *     puntp)
 {
 {
-  char  c;
-  char *p;
-  char *start;
-
-  /* Align the instruction.
-     This may not be the right thing to do but ...  */
-#if 0
-  arm_align (2, 0);
+  elf_frob_symbol (symp, puntp);
+}
 #endif
 
 #endif
 
-  /* Align the previous label if needed.  */
-  if (last_label_seen != NULL)
-    {
-      symbol_set_frag (last_label_seen, frag_now);
-      S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
-      S_SET_SEGMENT (last_label_seen, now_seg);
-    }
-
-  memset (&inst, '\0', sizeof (inst));
-  inst.reloc.type = BFD_RELOC_NONE;
+/* MD interface: Finalization. */
 
 
-  skip_whitespace (str);
+/* A good place to do this, although this was probably not intended
+   for this kind of use.  We need to dump the literal pool before
+   references are made to a null symbol pointer.  */
 
 
-  /* Scan up to the end of the op-code, which must end in white space or
-     end of string.  */
-  for (start = p = str; *p != '\0'; p++)
-    if (*p == ' ')
-      break;
+void
+arm_cleanup (void)
+{
+  literal_pool * pool;
 
 
-  if (p == str)
+  for (pool = list_of_pools; pool; pool = pool->next)
     {
     {
-      as_bad (_("no operator -- statement `%s'\n"), str);
-      return;
+      /* Put it at the end of the relevent section.  */
+      subseg_set (pool->section, pool->sub_section);
+#ifdef OBJ_ELF
+      arm_elf_change_section ();
+#endif
+      s_ltorg (0);
     }
     }
+}
 
 
-  if (thumb_mode)
-    {
-      const struct thumb_opcode * opcode;
+/* Adjust the symbol table.  This marks Thumb symbols as distinct from
+   ARM ones.  */
 
 
-      c = *p;
-      *p = '\0';
-      opcode = (const struct thumb_opcode *) hash_find (arm_tops_hsh, str);
-      *p = c;
+void
+arm_adjust_symtab (void)
+{
+#ifdef OBJ_COFF
+  symbolS * sym;
 
 
-      if (opcode)
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+    {
+      if (ARM_IS_THUMB (sym))
        {
        {
-         /* Check that this instruction is supported for this CPU.  */
-         if (thumb_mode == 1 && (opcode->variant & cpu_variant) == 0)
+         if (THUMB_IS_FUNC (sym))
            {
            {
-             as_bad (_("selected processor does not support `%s'"), str);
-             return;
-           }
+             /* Mark the symbol as a Thumb function.  */
+             if (   S_GET_STORAGE_CLASS (sym) == C_STAT
+                 || S_GET_STORAGE_CLASS (sym) == C_LABEL)  /* This can happen!  */
+               S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
 
 
-         inst.instruction = opcode->value;
-         inst.size = opcode->size;
-         (*opcode->parms) (p);
-         output_inst (str);
-         return;
+             else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
+               S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
+             else
+               as_bad (_("%s: unexpected function type: %d"),
+                       S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+           }
+         else switch (S_GET_STORAGE_CLASS (sym))
+           {
+           case C_EXT:
+             S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
+             break;
+           case C_STAT:
+             S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
+             break;
+           case C_LABEL:
+             S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
+             break;
+           default:
+             /* Do nothing.  */
+             break;
+           }
        }
        }
+
+      if (ARM_IS_INTERWORK (sym))
+       coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
     }
     }
-  else
+#endif
+#ifdef OBJ_ELF
+  symbolS * sym;
+  char     bind;
+
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
     {
     {
-      const struct asm_opcode * opcode;
+      if (ARM_IS_THUMB (sym))
+       {
+         elf_symbol_type * elf_sym;
 
 
-      c = *p;
-      *p = '\0';
-      opcode = (const struct asm_opcode *) hash_find (arm_ops_hsh, str);
-      *p = c;
+         elf_sym = elf_symbol (symbol_get_bfdsym (sym));
+         bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
 
 
-      if (opcode)
-       {
-         /* Check that this instruction is supported for this CPU.  */
-         if ((opcode->variant & cpu_variant) == 0)
+         if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name))
            {
            {
-             as_bad (_("selected processor does not support `%s'"), str);
-             return;
+             /* If it's a .thumb_func, declare it as so,
+                otherwise tag label as .code 16.  */
+             if (THUMB_IS_FUNC (sym))
+               elf_sym->internal_elf_sym.st_info =
+                 ELF_ST_INFO (bind, STT_ARM_TFUNC);
+             else
+               elf_sym->internal_elf_sym.st_info =
+                 ELF_ST_INFO (bind, STT_ARM_16BIT);
            }
            }
-
-         inst.instruction = opcode->value;
-         inst.size = INSN_SIZE;
-         (*opcode->parms) (p);
-         output_inst (str);
-         return;
        }
     }
        }
     }
+#endif
+}
 
 
-  /* It wasn't an instruction, but it might be a register alias of the form
-     alias .req reg.  */
-  if (create_register_alias (str, p))
-    return;
+/* MD interface: Initialization.  */
+
+static void
+set_constant_flonums (void)
+{
+  int i;
 
 
-  as_bad (_("bad instruction `%s'"), start);
+  for (i = 0; i < NUM_FLOAT_VALS; i++)
+    if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
+      abort ();
 }
 
 }
 
-/* md_parse_option
-      Invocation line includes a switch not recognized by the base assembler.
-      See if it's a processor-specific option.
+void
+md_begin (void)
+{
+  unsigned mach;
+  unsigned int i;
 
 
-      This routine is somewhat complicated by the need for backwards
-      compatibility (since older releases of gcc can't be changed).
-      The new options try to make the interface as compatible as
-      possible with GCC.
+  if (  (arm_ops_hsh = hash_new ()) == NULL
+      || (arm_cond_hsh = hash_new ()) == NULL
+      || (arm_shift_hsh = hash_new ()) == NULL
+      || (arm_psr_hsh = hash_new ()) == NULL
+      || (arm_reg_hsh = hash_new ()) == NULL
+      || (arm_reloc_hsh = hash_new ()) == NULL)
+    as_fatal (_("virtual memory exhausted"));
 
 
-      New options (supported) are:
+  for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
+    hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i));
+  for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
+    hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
+  for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
+    hash_insert (arm_shift_hsh, shift_names[i].name, (PTR) (shift_names + i));
+  for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
+    hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
+  for (i = 0; i < sizeof (reg_names) / sizeof (struct reg_entry); i++)
+    hash_insert (arm_reg_hsh, reg_names[i].name, (PTR) (reg_names + i));
+#ifdef OBJ_ELF
+  for (i = 0; i < sizeof (reloc_names) / sizeof (struct reloc_entry); i++)
+    hash_insert (arm_reloc_hsh, reloc_names[i].name, (PTR) (reloc_names + i));
+#endif
 
 
-             -mcpu=<cpu name>           Assemble for selected processor
-             -march=<architecture name> Assemble for selected architecture
-             -mfpu=<fpu architecture>   Assemble for selected FPU.
-             -EB/-mbig-endian           Big-endian
-             -EL/-mlittle-endian        Little-endian
-             -k                         Generate PIC code
-             -mthumb                    Start in Thumb mode
-             -mthumb-interwork          Code supports ARM/Thumb interworking
+  set_constant_flonums ();
 
 
-      For now we will also provide support for:
+  /* Set the cpu variant based on the command-line options.  We prefer
+     -mcpu= over -march= if both are set (as for GCC); and we prefer
+     -mfpu= over any other way of setting the floating point unit.
+     Use of legacy options with new options are faulted.  */
+  if (legacy_cpu != -1)
+    {
+      if (mcpu_cpu_opt != -1 || march_cpu_opt != -1)
+       as_bad (_("use of old and new-style options to set CPU type"));
 
 
-             -mapcs-32                  32-bit Program counter
-             -mapcs-26                  26-bit Program counter
-             -macps-float               Floats passed in FP registers
-             -mapcs-reentrant           Reentrant code
-             -matpcs
-      (sometime these will probably be replaced with -mapcs=<list of options>
-      and -matpcs=<list of options>)
+      mcpu_cpu_opt = legacy_cpu;
+    }
+  else if (mcpu_cpu_opt == -1)
+    mcpu_cpu_opt = march_cpu_opt;
 
 
-      The remaining options are only supported for back-wards compatibility.
-      Cpu variants, the arm part is optional:
-              -m[arm]1                Currently not supported.
-              -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
-              -m[arm]3                Arm 3 processor
-              -m[arm]6[xx],           Arm 6 processors
-              -m[arm]7[xx][t][[d]m]   Arm 7 processors
-              -m[arm]8[10]            Arm 8 processors
-              -m[arm]9[20][tdmi]      Arm 9 processors
-              -mstrongarm[110[0]]     StrongARM processors
-              -mxscale                XScale processors
-              -m[arm]v[2345[t[e]]]    Arm architectures
-              -mall                   All (except the ARM1)
-      FP variants:
-              -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
-              -mfpe-old               (No float load/store multiples)
-             -mvfpxd                 VFP Single precision
-             -mvfp                   All VFP
-              -mno-fpu                Disable all floating point instructions
+  if (legacy_fpu != -1)
+    {
+      if (mfpu_opt != -1)
+       as_bad (_("use of old and new-style options to set FPU type"));
 
 
-      The following CPU names are recognized:
-             arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620,
-             arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700,
-             arm700i, arm710 arm710t, arm720, arm720t, arm740t, arm710c,
+      mfpu_opt = legacy_fpu;
+    }
+  else if (mfpu_opt == -1)
+    {
+#if !(defined (TE_LINUX) || defined (TE_NetBSD) || defined (TE_VXWORKS))
+      /* Some environments specify a default FPU.  If they don't, infer it
+        from the processor.  */
+      if (mcpu_fpu_opt != -1)
+       mfpu_opt = mcpu_fpu_opt;
+      else
+       mfpu_opt = march_fpu_opt;
+#else
+      mfpu_opt = FPU_DEFAULT;
+#endif
+    }
+
+  if (mfpu_opt == -1)
+    {
+      if (mcpu_cpu_opt == -1)
+       mfpu_opt = FPU_DEFAULT;
+      else if (mcpu_cpu_opt & ARM_EXT_V5)
+       mfpu_opt = FPU_ARCH_VFP_V2;
+      else
+       mfpu_opt = FPU_ARCH_FPA;
+    }
+
+  if (mcpu_cpu_opt == -1)
+    mcpu_cpu_opt = CPU_DEFAULT;
+
+  cpu_variant = mcpu_cpu_opt | mfpu_opt;
+
+#if defined OBJ_COFF || defined OBJ_ELF
+  {
+    unsigned int flags = 0;
+
+#if defined OBJ_ELF
+    flags = meabi_flags;
+
+    switch (meabi_flags)
+      {
+      case EF_ARM_EABI_UNKNOWN:
+#endif
+       /* Set the flags in the private structure.  */
+       if (uses_apcs_26)      flags |= F_APCS26;
+       if (support_interwork) flags |= F_INTERWORK;
+       if (uses_apcs_float)   flags |= F_APCS_FLOAT;
+       if (pic_code)          flags |= F_PIC;
+       if ((cpu_variant & FPU_ANY) == FPU_NONE
+            || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only.  */
+         flags |= F_SOFT_FLOAT;
+
+       switch (mfloat_abi_opt)
+         {
+         case ARM_FLOAT_ABI_SOFT:
+         case ARM_FLOAT_ABI_SOFTFP:
+           flags |= F_SOFT_FLOAT;
+           break;
+
+         case ARM_FLOAT_ABI_HARD:
+           if (flags & F_SOFT_FLOAT)
+             as_bad (_("hard-float conflicts with specified fpu"));
+           break;
+         }
+
+       /* Using VFP conventions (even if soft-float).  */
+       if (cpu_variant & FPU_VFP_EXT_NONE)
+         flags |= F_VFP_FLOAT;
+
+#if defined OBJ_ELF
+       if (cpu_variant & FPU_ARCH_MAVERICK)
+           flags |= EF_ARM_MAVERICK_FLOAT;
+       break;
+
+      case EF_ARM_EABI_VER4:
+       /* No additional flags to set.  */
+       break;
+
+      default:
+       abort ();
+      }
+#endif
+    bfd_set_private_flags (stdoutput, flags);
+
+    /* We have run out flags in the COFF header to encode the
+       status of ATPCS support, so instead we create a dummy,
+       empty, debug section called .arm.atpcs. */
+    if (atpcs)
+      {
+       asection * sec;
+
+       sec = bfd_make_section (stdoutput, ".arm.atpcs");
+
+       if (sec != NULL)
+         {
+           bfd_set_section_flags
+             (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
+           bfd_set_section_size (stdoutput, sec, 0);
+           bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
+         }
+      }
+  }
+#endif
+
+  /* Record the CPU type as well.  */
+  switch (cpu_variant & ARM_CPU_MASK)
+    {
+    case ARM_2:
+      mach = bfd_mach_arm_2;
+      break;
+
+    case ARM_3:                        /* Also ARM_250.  */
+      mach = bfd_mach_arm_2a;
+      break;
+
+    case ARM_6:                        /* Also ARM_7.  */
+      mach = bfd_mach_arm_3;
+      break;
+
+    default:
+      mach = bfd_mach_arm_unknown;
+      break;
+    }
+
+  /* Catch special cases.  */
+  if (cpu_variant & ARM_CEXT_IWMMXT)
+    mach = bfd_mach_arm_iWMMXt;
+  else if (cpu_variant & ARM_CEXT_XSCALE)
+    mach = bfd_mach_arm_XScale;
+  else if (cpu_variant & ARM_CEXT_MAVERICK)
+    mach = bfd_mach_arm_ep9312;
+  else if (cpu_variant & ARM_EXT_V5E)
+    mach = bfd_mach_arm_5TE;
+  else if (cpu_variant & ARM_EXT_V5)
+    {
+      if (cpu_variant & ARM_EXT_V4T)
+       mach = bfd_mach_arm_5T;
+      else
+       mach = bfd_mach_arm_5;
+    }
+  else if (cpu_variant & ARM_EXT_V4)
+    {
+      if (cpu_variant & ARM_EXT_V4T)
+       mach = bfd_mach_arm_4T;
+      else
+       mach = bfd_mach_arm_4;
+    }
+  else if (cpu_variant & ARM_EXT_V3M)
+    mach = bfd_mach_arm_3M;
+
+  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+}
+
+/* Command line processing.  */
+
+/* md_parse_option
+      Invocation line includes a switch not recognized by the base assembler.
+      See if it's a processor-specific option.
+
+      This routine is somewhat complicated by the need for backwards
+      compatibility (since older releases of gcc can't be changed).
+      The new options try to make the interface as compatible as
+      possible with GCC.
+
+      New options (supported) are:
+
+             -mcpu=<cpu name>           Assemble for selected processor
+             -march=<architecture name> Assemble for selected architecture
+             -mfpu=<fpu architecture>   Assemble for selected FPU.
+             -EB/-mbig-endian           Big-endian
+             -EL/-mlittle-endian        Little-endian
+             -k                         Generate PIC code
+             -mthumb                    Start in Thumb mode
+             -mthumb-interwork          Code supports ARM/Thumb interworking
+
+      For now we will also provide support for:
+
+             -mapcs-32                  32-bit Program counter
+             -mapcs-26                  26-bit Program counter
+             -macps-float               Floats passed in FP registers
+             -mapcs-reentrant           Reentrant code
+             -matpcs
+      (sometime these will probably be replaced with -mapcs=<list of options>
+      and -matpcs=<list of options>)
+
+      The remaining options are only supported for back-wards compatibility.
+      Cpu variants, the arm part is optional:
+             -m[arm]1                Currently not supported.
+             -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
+             -m[arm]3                Arm 3 processor
+             -m[arm]6[xx],           Arm 6 processors
+             -m[arm]7[xx][t][[d]m]   Arm 7 processors
+             -m[arm]8[10]            Arm 8 processors
+             -m[arm]9[20][tdmi]      Arm 9 processors
+             -mstrongarm[110[0]]     StrongARM processors
+             -mxscale                XScale processors
+             -m[arm]v[2345[t[e]]]    Arm architectures
+             -mall                   All (except the ARM1)
+      FP variants:
+             -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
+             -mfpe-old               (No float load/store multiples)
+             -mvfpxd                 VFP Single precision
+             -mvfp                   All VFP
+             -mno-fpu                Disable all floating point instructions
+
+      The following CPU names are recognized:
+             arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620,
+             arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700,
+             arm700i, arm710 arm710t, arm720, arm720t, arm740t, arm710c,
              arm7100, arm7500, arm7500fe, arm7tdmi, arm8, arm810, arm9,
              arm920, arm920t, arm940t, arm946, arm966, arm9tdmi, arm9e,
              arm10t arm10e, arm1020t, arm1020e, arm10200e,
              arm7100, arm7500, arm7500fe, arm7tdmi, arm8, arm810, arm9,
              arm920, arm920t, arm940t, arm946, arm966, arm9tdmi, arm9e,
              arm10t arm10e, arm1020t, arm1020e, arm10200e,
@@ -10609,18 +11875,17 @@ struct arm_option_table
 {
   char *option;                /* Option name to match.  */
   char *help;          /* Help information.  */
 {
   char *option;                /* Option name to match.  */
   char *help;          /* Help information.  */
-  int  *var;           /* Variable to change.  */
-  int   value;         /* What to change it to.  */
+  int  *var;           /* Variable to change.  */
+  int  value;          /* What to change it to.  */
   char *deprecated;    /* If non-null, print this message.  */
 };
 
 struct arm_option_table arm_opts[] =
 {
   char *deprecated;    /* If non-null, print this message.  */
 };
 
 struct arm_option_table arm_opts[] =
 {
-  {"k",      N_("generate PIC code"),      &pic_code,    1, NULL},
-  {"mthumb", N_("assemble Thumb code"),    &thumb_mode,  1, NULL},
+  {"k",             N_("generate PIC code"),      &pic_code,    1, NULL},
+  {"mthumb", N_("assemble Thumb code"),           &thumb_mode,  1, NULL},
   {"mthumb-interwork", N_("support ARM/Thumb interworking"),
    &support_interwork, 1, NULL},
   {"mthumb-interwork", N_("support ARM/Thumb interworking"),
    &support_interwork, 1, NULL},
-  {"moabi",  N_("use old ABI (ELF only)"), &target_oabi, 1, NULL},
   {"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
   {"mapcs-26", N_("code uses 26-bit program counter"), &uses_apcs_26, 1, NULL},
   {"mapcs-float", N_("floating point args are in fp regs"), &uses_apcs_float,
   {"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
   {"mapcs-26", N_("code uses 26-bit program counter"), &uses_apcs_26, 1, NULL},
   {"mapcs-float", N_("floating point args are in fp regs"), &uses_apcs_float,
@@ -10628,10 +11893,10 @@ struct arm_option_table arm_opts[] =
   {"mapcs-reentrant", N_("re-entrant code"), &pic_code, 1, NULL},
   {"matpcs", N_("code is ATPCS conformant"), &atpcs, 1, NULL},
   {"mbig-endian", N_("assemble for big-endian"), &target_big_endian, 1, NULL},
   {"mapcs-reentrant", N_("re-entrant code"), &pic_code, 1, NULL},
   {"matpcs", N_("code is ATPCS conformant"), &atpcs, 1, NULL},
   {"mbig-endian", N_("assemble for big-endian"), &target_big_endian, 1, NULL},
-  {"mlittle-endian", N_("assemble for little-endian"), &target_big_endian, 1,
+  {"mlittle-endian", N_("assemble for little-endian"), &target_big_endian, 0,
    NULL},
 
    NULL},
 
-  /* These are recognized by the assembler, but have no affect on code.  */
+  /* These are recognized by the assembler, but have no affect on code.         */
   {"mapcs-frame", N_("use frame pointer"), NULL, 0, NULL},
   {"mapcs-stack-check", N_("use stack size checking"), NULL, 0, NULL},
 
   {"mapcs-frame", N_("use frame pointer"), NULL, 0, NULL},
   {"mapcs-stack-check", N_("use stack size checking"), NULL, 0, NULL},
 
@@ -10713,7 +11978,8 @@ struct arm_option_table arm_opts[] =
   {"mstrongarm1110", NULL, &legacy_cpu, ARM_ARCH_V4,
    N_("use -mcpu=strongarm1110")},
   {"mxscale",   NULL, &legacy_cpu, ARM_ARCH_XSCALE, N_("use -mcpu=xscale")},
   {"mstrongarm1110", NULL, &legacy_cpu, ARM_ARCH_V4,
    N_("use -mcpu=strongarm1110")},
   {"mxscale",   NULL, &legacy_cpu, ARM_ARCH_XSCALE, N_("use -mcpu=xscale")},
-  {"mall",      NULL, &legacy_cpu, ARM_ANY,      N_("use -mcpu=all")},
+  {"miwmmxt",   NULL, &legacy_cpu, ARM_ARCH_IWMMXT, N_("use -mcpu=iwmmxt")},
+  {"mall",      NULL, &legacy_cpu, ARM_ANY,      N_("use -mcpu=all")},
 
   /* Architecture variants -- don't add any more to this list either.  */
   {"mv2",       NULL, &legacy_cpu, ARM_ARCH_V2,  N_("use -march=armv2")},
 
   /* Architecture variants -- don't add any more to this list either.  */
   {"mv2",       NULL, &legacy_cpu, ARM_ARCH_V2,  N_("use -march=armv2")},
@@ -10735,7 +12001,7 @@ struct arm_option_table arm_opts[] =
   {"mv5e",      NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
   {"marmv5e",   NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
 
   {"mv5e",      NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
   {"marmv5e",   NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
 
-  /* Floating point variants -- don't add any more to this list either.  */
+  /* Floating point variants -- don't add any more to this list either.         */
   {"mfpe-old", NULL, &legacy_fpu, FPU_ARCH_FPE, N_("use -mfpu=fpe")},
   {"mfpa10",   NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa10")},
   {"mfpa11",   NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa11")},
   {"mfpe-old", NULL, &legacy_fpu, FPU_ARCH_FPE, N_("use -mfpu=fpe")},
   {"mfpa10",   NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa10")},
   {"mfpa11",   NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa11")},
@@ -10748,10 +12014,10 @@ struct arm_option_table arm_opts[] =
 struct arm_cpu_option_table
 {
   char *name;
 struct arm_cpu_option_table
 {
   char *name;
-  int   value;
+  int  value;
   /* For some CPUs we assume an FPU unless the user explicitly sets
   /* For some CPUs we assume an FPU unless the user explicitly sets
-     -mfpu=...  */
-  int   default_fpu;
+     -mfpu=... */
+  int  default_fpu;
 };
 
 /* This list should, at a minimum, contain all the cpu names
 };
 
 /* This list should, at a minimum, contain all the cpu names
@@ -10788,6 +12054,7 @@ static struct arm_cpu_option_table arm_cpus[] =
   {"arm7500fe",                ARM_ARCH_V3,     FPU_ARCH_FPA},
   {"arm7t",            ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm7tdmi",         ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm7500fe",                ARM_ARCH_V3,     FPU_ARCH_FPA},
   {"arm7t",            ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm7tdmi",         ARM_ARCH_V4T,    FPU_ARCH_FPA},
+  {"arm7tdmi-s",       ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm8",             ARM_ARCH_V4,     FPU_ARCH_FPA},
   {"arm810",           ARM_ARCH_V4,     FPU_ARCH_FPA},
   {"strongarm",                ARM_ARCH_V4,     FPU_ARCH_FPA},
   {"arm8",             ARM_ARCH_V4,     FPU_ARCH_FPA},
   {"arm810",           ARM_ARCH_V4,     FPU_ARCH_FPA},
   {"strongarm",                ARM_ARCH_V4,     FPU_ARCH_FPA},
@@ -10802,12 +12069,14 @@ static struct arm_cpu_option_table arm_cpus[] =
   {"arm940t",          ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm9tdmi",         ARM_ARCH_V4T,    FPU_ARCH_FPA},
   /* For V5 or later processors we default to using VFP; but the user
   {"arm940t",          ARM_ARCH_V4T,    FPU_ARCH_FPA},
   {"arm9tdmi",         ARM_ARCH_V4T,    FPU_ARCH_FPA},
   /* For V5 or later processors we default to using VFP; but the user
-     should really set the FPU type explicitly.  */
+     should really set the FPU type explicitly.         */
   {"arm9e-r0",         ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
   {"arm9e-r0",         ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
-  {"arm9e",            ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
+  {"arm9e",            ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm926ej",         ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
   {"arm926ej",         ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
+  {"arm926ejs",                ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
+  {"arm926ej-s",       ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
   {"arm946e-r0",       ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
   {"arm946e-r0",       ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
-  {"arm946e",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
+  {"arm946e",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm966e-r0",       ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
   {"arm966e",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm10t",           ARM_ARCH_V5T,    FPU_ARCH_VFP_V1},
   {"arm966e-r0",       ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
   {"arm966e",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm10t",           ARM_ARCH_V5T,    FPU_ARCH_VFP_V1},
@@ -10815,19 +12084,31 @@ static struct arm_cpu_option_table arm_cpus[] =
   {"arm1020",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm1020t",         ARM_ARCH_V5T,    FPU_ARCH_VFP_V1},
   {"arm1020e",         ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm1020",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm1020t",         ARM_ARCH_V5T,    FPU_ARCH_VFP_V1},
   {"arm1020e",         ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
+  {"arm1026ejs",       ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
+  {"arm1026ej-s",      ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
+  {"arm1136js",                ARM_ARCH_V6,     FPU_NONE},
+  {"arm1136j-s",       ARM_ARCH_V6,     FPU_NONE},
+  {"arm1136jfs",       ARM_ARCH_V6,     FPU_ARCH_VFP_V2},
+  {"arm1136jf-s",      ARM_ARCH_V6,     FPU_ARCH_VFP_V2},
+  {"mpcore",           ARM_ARCH_V6K,    FPU_ARCH_VFP_V2},
+  {"mpcorenovfp",      ARM_ARCH_V6K,    FPU_NONE},
+  {"arm1176jz-s",      ARM_ARCH_V6ZK,   FPU_NONE},
+  {"arm1176jzf-s",     ARM_ARCH_V6ZK,   FPU_ARCH_VFP_V2},
   /* ??? XSCALE is really an architecture.  */
   {"xscale",           ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
   /* ??? XSCALE is really an architecture.  */
   {"xscale",           ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
+  /* ??? iwmmxt is not a processor.  */
+  {"iwmmxt",           ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2},
   {"i80200",           ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
   /* Maverick */
   {"i80200",           ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
   /* Maverick */
-  {"ep9312",           ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_NONE},
+  {"ep9312",           ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_ARCH_MAVERICK},
   {NULL, 0, 0}
 };
 
 struct arm_arch_option_table
 {
   char *name;
   {NULL, 0, 0}
 };
 
 struct arm_arch_option_table
 {
   char *name;
-  int   value;
-  int   default_fpu;
+  int  value;
+  int  default_fpu;
 };
 
 /* This list should, at a minimum, contain all the architecture names
 };
 
 /* This list should, at a minimum, contain all the architecture names
@@ -10850,34 +12131,39 @@ static struct arm_arch_option_table arm_archs[] =
   {"armv5txm",         ARM_ARCH_V5TxM,  FPU_ARCH_VFP},
   {"armv5te",          ARM_ARCH_V5TE,   FPU_ARCH_VFP},
   {"armv5texp",                ARM_ARCH_V5TExP, FPU_ARCH_VFP},
   {"armv5txm",         ARM_ARCH_V5TxM,  FPU_ARCH_VFP},
   {"armv5te",          ARM_ARCH_V5TE,   FPU_ARCH_VFP},
   {"armv5texp",                ARM_ARCH_V5TExP, FPU_ARCH_VFP},
-  {"armv5tej",         ARM_ARCH_V5TEJ,  FPU_ARCH_VFP},
+  {"armv5tej",         ARM_ARCH_V5TEJ,  FPU_ARCH_VFP},
+  {"armv6",            ARM_ARCH_V6,     FPU_ARCH_VFP},
+  {"armv6j",           ARM_ARCH_V6,     FPU_ARCH_VFP},
+  {"armv6k",           ARM_ARCH_V6K,    FPU_ARCH_VFP},
+  {"armv6z",           ARM_ARCH_V6Z,    FPU_ARCH_VFP},
+  {"armv6zk",          ARM_ARCH_V6ZK,   FPU_ARCH_VFP},
+  {"armv6t2",          ARM_ARCH_V6T2,   FPU_ARCH_VFP},
+  {"armv6kt2",         ARM_ARCH_V6KT2,  FPU_ARCH_VFP},
+  {"armv6zt2",         ARM_ARCH_V6ZT2,  FPU_ARCH_VFP},
+  {"armv6zkt2",                ARM_ARCH_V6ZKT2, FPU_ARCH_VFP},
   {"xscale",           ARM_ARCH_XSCALE, FPU_ARCH_VFP},
   {"xscale",           ARM_ARCH_XSCALE, FPU_ARCH_VFP},
+  {"iwmmxt",           ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
   {NULL, 0, 0}
 };
 
 /* ISA extensions in the co-processor space.  */
   {NULL, 0, 0}
 };
 
 /* ISA extensions in the co-processor space.  */
-struct arm_arch_extension_table
+struct arm_option_value_table
 {
   char *name;
   int value;
 };
 
 {
   char *name;
   int value;
 };
 
-static struct arm_arch_extension_table arm_extensions[] =
+static struct arm_option_value_table arm_extensions[] =
 {
   {"maverick",         ARM_CEXT_MAVERICK},
   {"xscale",           ARM_CEXT_XSCALE},
 {
   {"maverick",         ARM_CEXT_MAVERICK},
   {"xscale",           ARM_CEXT_XSCALE},
+  {"iwmmxt",           ARM_CEXT_IWMMXT},
   {NULL,               0}
 };
 
   {NULL,               0}
 };
 
-struct arm_fpu_option_table
-{
-  char *name;
-  int   value;
-};
-
 /* This list should, at a minimum, contain all the fpu names
    recognized by GCC.  */
 /* This list should, at a minimum, contain all the fpu names
    recognized by GCC.  */
-static struct arm_fpu_option_table arm_fpus[] =
+static struct arm_option_value_table arm_fpus[] =
 {
   {"softfpa",          FPU_NONE},
   {"fpe",              FPU_ARCH_FPE},
 {
   {"softfpa",          FPU_NONE},
   {"fpe",              FPU_ARCH_FPE},
@@ -10896,26 +12182,45 @@ static struct arm_fpu_option_table arm_fpus[] =
   {"vfpxd",            FPU_ARCH_VFP_V1xD},
   {"arm1020t",         FPU_ARCH_VFP_V1},
   {"arm1020e",         FPU_ARCH_VFP_V2},
   {"vfpxd",            FPU_ARCH_VFP_V1xD},
   {"arm1020t",         FPU_ARCH_VFP_V1},
   {"arm1020e",         FPU_ARCH_VFP_V2},
+  {"arm1136jfs",       FPU_ARCH_VFP_V2},
+  {"arm1136jf-s",      FPU_ARCH_VFP_V2},
+  {"maverick",         FPU_ARCH_MAVERICK},
+  {NULL, 0}
+};
+
+static struct arm_option_value_table arm_float_abis[] =
+{
+  {"hard",     ARM_FLOAT_ABI_HARD},
+  {"softfp",   ARM_FLOAT_ABI_SOFTFP},
+  {"soft",     ARM_FLOAT_ABI_SOFT},
+  {NULL, 0}
+};
+
+#ifdef OBJ_ELF
+/* We only know how to output GNU and ver 4 (AAELF) formats.  */
+static struct arm_option_value_table arm_eabis[] =
+{
+  {"gnu",      EF_ARM_EABI_UNKNOWN},
+  {"4",                EF_ARM_EABI_VER4},
   {NULL, 0}
 };
   {NULL, 0}
 };
+#endif
 
 struct arm_long_option_table
 {
 
 struct arm_long_option_table
 {
-  char *option;                /* Substring to match.  */
-  char *help;          /* Help information.  */
-  int (*func) PARAMS ((char *subopt)); /* Function to decode sub-option.  */
-  char *deprecated;    /* If non-null, print this message.  */
+  char * option;               /* Substring to match.  */
+  char * help;                 /* Help information.  */
+  int (* func) (char * subopt);        /* Function to decode sub-option.  */
+  char * deprecated;           /* If non-null, print this message.  */
 };
 
 static int
 };
 
 static int
-arm_parse_extension (str, opt_p)
-     char *str;
-     int *opt_p;
+arm_parse_extension (char * str, int * opt_p)
 {
   while (str != NULL && *str != 0)
     {
 {
   while (str != NULL && *str != 0)
     {
-      struct arm_arch_extension_table *opt;
-      char *ext;
+      struct arm_option_value_table * opt;
+      char * ext;
       int optlen;
 
       if (*str != '+')
       int optlen;
 
       if (*str != '+')
@@ -10958,11 +12263,10 @@ arm_parse_extension (str, opt_p)
 }
 
 static int
 }
 
 static int
-arm_parse_cpu (str)
-     char *str;
+arm_parse_cpu (char * str)
 {
 {
-  struct arm_cpu_option_table *opt;
-  char *ext = strchr (str, '+');
+  struct arm_cpu_option_table * opt;
+  char * ext = strchr (str, '+');
   int optlen;
 
   if (ext != NULL)
   int optlen;
 
   if (ext != NULL)
@@ -10985,745 +12289,209 @@ arm_parse_cpu (str)
        if (ext != NULL)
          return arm_parse_extension (ext, &mcpu_cpu_opt);
 
        if (ext != NULL)
          return arm_parse_extension (ext, &mcpu_cpu_opt);
 
-       return 1;
-      }
-
-  as_bad (_("unknown cpu `%s'"), str);
-  return 0;
-}
-
-static int
-arm_parse_arch (str)
-     char *str;
-{
-  struct arm_arch_option_table *opt;
-  char *ext = strchr (str, '+');
-  int optlen;
-
-  if (ext != NULL)
-    optlen = ext - str;
-  else
-    optlen = strlen (str);
-
-  if (optlen == 0)
-    {
-      as_bad (_("missing architecture name `%s'"), str);
-      return 0;
-    }
-
-
-  for (opt = arm_archs; opt->name != NULL; opt++)
-    if (strcmp (opt->name, str) == 0)
-      {
-       march_cpu_opt = opt->value;
-       march_fpu_opt = opt->default_fpu;
-
-       if (ext != NULL)
-         return arm_parse_extension (ext, &march_cpu_opt);
-
-       return 1;
-      }
-
-  as_bad (_("unknown architecture `%s'\n"), str);
-  return 0;
-}
-
-static int
-arm_parse_fpu (str)
-     char *str;
-{
-  struct arm_fpu_option_table *opt;
-
-  for (opt = arm_fpus; opt->name != NULL; opt++)
-    if (strcmp (opt->name, str) == 0)
-      {
-       mfpu_opt = opt->value;
-       return 1;
-      }
-
-  as_bad (_("unknown floating point format `%s'\n"), str);
-  return 0;
-}
-
-struct arm_long_option_table arm_long_opts[] =
-{
-  {"mcpu=", N_("<cpu name>\t  assemble for CPU <cpu name>"),
-   arm_parse_cpu, NULL},
-  {"march=", N_("<arch name>\t  assemble for architecture <arch name>"),
-   arm_parse_arch, NULL},
-  {"mfpu=", N_("<fpu name>\t  assemble for FPU architecture <fpu name>"),
-   arm_parse_fpu, NULL},
-  {NULL, NULL, 0, NULL}
-};
-
-int
-md_parse_option (c, arg)
-     int    c;
-     char * arg;
-{
-  struct arm_option_table *opt;
-  struct arm_long_option_table *lopt;
-
-  switch (c)
-    {
-#ifdef OPTION_EB
-    case OPTION_EB:
-      target_big_endian = 1;
-      break;
-#endif
-
-#ifdef OPTION_EL
-    case OPTION_EL:
-      target_big_endian = 0;
-      break;
-#endif
-
-    case 'a':
-      /* Listing option.  Just ignore these, we don't support additional
-        ones.  */
-      return 0;
-
-    default:
-      for (opt = arm_opts; opt->option != NULL; opt++)
-       {
-         if (c == opt->option[0]
-             && ((arg == NULL && opt->option[1] == 0)
-                 || strcmp (arg, opt->option + 1) == 0))
-           {
-#if WARN_DEPRECATED
-             /* If the option is deprecated, tell the user.  */
-             if (opt->deprecated != NULL)
-               as_tsktsk (_("option `-%c%s' is deprecated: %s"), c,
-                          arg ? arg : "", _(opt->deprecated));
-#endif
-
-             if (opt->var != NULL)
-               *opt->var = opt->value;
-
-             return 1;
-           }
-       }
-
-      for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
-       {
-         /* These options are expected to have an argument.  */
-         if (c == lopt->option[0]
-             && arg != NULL
-             && strncmp (arg, lopt->option + 1,
-                         strlen (lopt->option + 1)) == 0)
-           {
-#if WARN_DEPRECATED
-             /* If the option is deprecated, tell the user.  */
-             if (lopt->deprecated != NULL)
-               as_tsktsk (_("option `-%c%s' is deprecated: %s"), c, arg,
-                          _(lopt->deprecated));
-#endif
-
-             /* Call the sup-option parser.  */
-             return (*lopt->func)(arg + strlen (lopt->option) - 1);
-           }
-       }
-
-      as_bad (_("unrecognized option `-%c%s'"), c, arg ? arg : "");
-      return 0;
-    }
-
-  return 1;
-}
-
-void
-md_show_usage (fp)
-     FILE * fp;
-{
-  struct arm_option_table *opt;
-  struct arm_long_option_table *lopt;
-
-  fprintf (fp, _(" ARM-specific assembler options:\n"));
-
-  for (opt = arm_opts; opt->option != NULL; opt++)
-    if (opt->help != NULL)
-      fprintf (fp, "  -%-23s%s\n", opt->option, _(opt->help));
-
-  for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
-    if (lopt->help != NULL)
-      fprintf (fp, "  -%s%s\n", lopt->option, _(lopt->help));
-
-#ifdef OPTION_EB
-  fprintf (fp, _("\
-  -EB                     assemble code for a big-endian cpu\n"));
-#endif
-
-#ifdef OPTION_EL
-  fprintf (fp, _("\
-  -EL                     assemble code for a little-endian cpu\n"));
-#endif
-}
-
-/* We need to be able to fix up arbitrary expressions in some statements.
-   This is so that we can handle symbols that are an arbitrary distance from
-   the pc.  The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
-   which returns part of an address in a form which will be valid for
-   a data instruction.  We do this by pushing the expression into a symbol
-   in the expr_section, and creating a fix for that.  */
-
-static void
-fix_new_arm (frag, where, size, exp, pc_rel, reloc)
-     fragS *       frag;
-     int           where;
-     short int     size;
-     expressionS * exp;
-     int           pc_rel;
-     int           reloc;
-{
-  fixS *           new_fix;
-  arm_fix_data *   arm_data;
-
-  switch (exp->X_op)
-    {
-    case O_constant:
-    case O_symbol:
-    case O_add:
-    case O_subtract:
-      new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
-      break;
-
-    default:
-      new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
-                        pc_rel, reloc);
-      break;
-    }
-
-  /* Mark whether the fix is to a THUMB instruction, or an ARM
-     instruction.  */
-  arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data));
-  new_fix->tc_fix_data = (PTR) arm_data;
-  arm_data->thumb_mode = thumb_mode;
-
-  return;
-}
-
-/* This fix_new is called by cons via TC_CONS_FIX_NEW.  */
-
-void
-cons_fix_new_arm (frag, where, size, exp)
-     fragS *       frag;
-     int           where;
-     int           size;
-     expressionS * exp;
-{
-  bfd_reloc_code_real_type type;
-  int pcrel = 0;
-
-  /* Pick a reloc.
-     FIXME: @@ Should look at CPU word size.  */
-  switch (size)
-    {
-    case 1:
-      type = BFD_RELOC_8;
-      break;
-    case 2:
-      type = BFD_RELOC_16;
-      break;
-    case 4:
-    default:
-      type = BFD_RELOC_32;
-      break;
-    case 8:
-      type = BFD_RELOC_64;
-      break;
-    }
-
-  fix_new_exp (frag, where, (int) size, exp, pcrel, type);
-}
-
-/* A good place to do this, although this was probably not intended
-   for this kind of use.  We need to dump the literal pool before
-   references are made to a null symbol pointer.  */
-
-void
-arm_cleanup ()
-{
-  literal_pool * pool;
-
-  for (pool = list_of_pools; pool; pool = pool->next)
-    {
-      /* Put it at the end of the relevent section.  */
-      subseg_set (pool->section, pool->sub_section);
-      s_ltorg (0);
-    }
-}
-
-void
-arm_start_line_hook ()
-{
-  last_label_seen = NULL;
-}
-
-void
-arm_frob_label (sym)
-     symbolS * sym;
-{
-  last_label_seen = sym;
-
-  ARM_SET_THUMB (sym, thumb_mode);
-
-#if defined OBJ_COFF || defined OBJ_ELF
-  ARM_SET_INTERWORK (sym, support_interwork);
-#endif
-
-  /* Note - do not allow local symbols (.Lxxx) to be labeled
-     as Thumb functions.  This is because these labels, whilst
-     they exist inside Thumb code, are not the entry points for
-     possible ARM->Thumb calls.  Also, these labels can be used
-     as part of a computed goto or switch statement.  eg gcc
-     can generate code that looks like this:
-
-                ldr  r2, [pc, .Laaa]
-                lsl  r3, r3, #2
-                ldr  r2, [r3, r2]
-                mov  pc, r2
-
-       .Lbbb:  .word .Lxxx
-       .Lccc:  .word .Lyyy
-       ..etc...
-       .Laaa:   .word Lbbb
-
-     The first instruction loads the address of the jump table.
-     The second instruction converts a table index into a byte offset.
-     The third instruction gets the jump address out of the table.
-     The fourth instruction performs the jump.
-
-     If the address stored at .Laaa is that of a symbol which has the
-     Thumb_Func bit set, then the linker will arrange for this address
-     to have the bottom bit set, which in turn would mean that the
-     address computation performed by the third instruction would end
-     up with the bottom bit set.  Since the ARM is capable of unaligned
-     word loads, the instruction would then load the incorrect address
-     out of the jump table, and chaos would ensue.  */
-  if (label_is_thumb_function_name
-      && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
-      && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
-    {
-      /* When the address of a Thumb function is taken the bottom
-        bit of that address should be set.  This will allow
-        interworking between Arm and Thumb functions to work
-        correctly.  */
-
-      THUMB_SET_FUNC (sym, 1);
-
-      label_is_thumb_function_name = FALSE;
-    }
-}
-
-/* Adjust the symbol table.  This marks Thumb symbols as distinct from
-   ARM ones.  */
-
-void
-arm_adjust_symtab ()
-{
-#ifdef OBJ_COFF
-  symbolS * sym;
-
-  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
-    {
-      if (ARM_IS_THUMB (sym))
-       {
-         if (THUMB_IS_FUNC (sym))
-           {
-             /* Mark the symbol as a Thumb function.  */
-             if (   S_GET_STORAGE_CLASS (sym) == C_STAT
-                 || S_GET_STORAGE_CLASS (sym) == C_LABEL)  /* This can happen!  */
-               S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
-
-             else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
-               S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
-             else
-               as_bad (_("%s: unexpected function type: %d"),
-                       S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
-           }
-         else switch (S_GET_STORAGE_CLASS (sym))
-           {
-           case C_EXT:
-             S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
-             break;
-           case C_STAT:
-             S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
-             break;
-           case C_LABEL:
-             S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
-             break;
-           default:
-             /* Do nothing.  */
-             break;
-           }
-       }
-
-      if (ARM_IS_INTERWORK (sym))
-       coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
-    }
-#endif
-#ifdef OBJ_ELF
-  symbolS * sym;
-  char      bind;
-
-  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
-    {
-      if (ARM_IS_THUMB (sym))
-       {
-         elf_symbol_type * elf_sym;
-
-         elf_sym = elf_symbol (symbol_get_bfdsym (sym));
-         bind = ELF_ST_BIND (elf_sym);
-
-         /* If it's a .thumb_func, declare it as so,
-            otherwise tag label as .code 16.  */
-         if (THUMB_IS_FUNC (sym))
-           elf_sym->internal_elf_sym.st_info =
-             ELF_ST_INFO (bind, STT_ARM_TFUNC);
-         else
-           elf_sym->internal_elf_sym.st_info =
-             ELF_ST_INFO (bind, STT_ARM_16BIT);
-       }
-    }
-#endif
-}
-
-int
-arm_data_in_code ()
-{
-  if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
-    {
-      *input_line_pointer = '/';
-      input_line_pointer += 5;
-      *input_line_pointer = 0;
-      return 1;
-    }
-
-  return 0;
-}
-
-char *
-arm_canonicalize_symbol_name (name)
-     char * name;
-{
-  int len;
-
-  if (thumb_mode && (len = strlen (name)) > 5
-      && streq (name + len - 5, "/data"))
-    *(name + len - 5) = 0;
-
-  return name;
-}
-
-void
-arm_validate_fix (fixP)
-     fixS * fixP;
-{
-  /* If the destination of the branch is a defined symbol which does not have
-     the THUMB_FUNC attribute, then we must be calling a function which has
-     the (interfacearm) attribute.  We look for the Thumb entry point to that
-     function and change the branch to refer to that function instead.  */
-  if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
-      && fixP->fx_addsy != NULL
-      && S_IS_DEFINED (fixP->fx_addsy)
-      && ! THUMB_IS_FUNC (fixP->fx_addsy))
-    {
-      fixP->fx_addsy = find_real_start (fixP->fx_addsy);
-    }
-}
-
-#ifdef OBJ_COFF
-/* This is a little hack to help the gas/arm/adrl.s test.  It prevents
-   local labels from being added to the output symbol table when they
-   are used with the ADRL pseudo op.  The ADRL relocation should always
-   be resolved before the binbary is emitted, so it is safe to say that
-   it is adjustable.  */
-
-bfd_boolean
-arm_fix_adjustable (fixP)
-   fixS * fixP;
-{
-  if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
-    return 1;
-  return 0;
-}
-#endif
-#ifdef OBJ_ELF
-/* Relocations against Thumb function names must be left unadjusted,
-   so that the linker can use this information to correctly set the
-   bottom bit of their addresses.  The MIPS version of this function
-   also prevents relocations that are mips-16 specific, but I do not
-   know why it does this.
-
-   FIXME:
-   There is one other problem that ought to be addressed here, but
-   which currently is not:  Taking the address of a label (rather
-   than a function) and then later jumping to that address.  Such
-   addresses also ought to have their bottom bit set (assuming that
-   they reside in Thumb code), but at the moment they will not.  */
-
-bfd_boolean
-arm_fix_adjustable (fixP)
-   fixS * fixP;
-{
-  if (fixP->fx_addsy == NULL)
-    return 1;
-
-  if (THUMB_IS_FUNC (fixP->fx_addsy)
-      && fixP->fx_subsy == NULL)
-    return 0;
-
-  /* We need the symbol name for the VTABLE entries.  */
-  if (   fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
-    return 0;
-
-  /* Don't allow symbols to be discarded on GOT related relocs.  */
-  if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
-      || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
-      || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF)
-    return 0;
+       return 1;
+      }
 
 
-  return 1;
+  as_bad (_("unknown cpu `%s'"), str);
+  return 0;
 }
 
 }
 
-const char *
-elf32_arm_target_format ()
+static int
+arm_parse_arch (char * str)
 {
 {
-  if (target_big_endian)
-    {
-      if (target_oabi)
-       return "elf32-bigarm-oabi";
-      else
-       return "elf32-bigarm";
-    }
+  struct arm_arch_option_table *opt;
+  char *ext = strchr (str, '+');
+  int optlen;
+
+  if (ext != NULL)
+    optlen = ext - str;
   else
   else
+    optlen = strlen (str);
+
+  if (optlen == 0)
     {
     {
-      if (target_oabi)
-       return "elf32-littlearm-oabi";
-      else
-       return "elf32-littlearm";
+      as_bad (_("missing architecture name `%s'"), str);
+      return 0;
     }
     }
-}
 
 
-void
-armelf_frob_symbol (symp, puntp)
-     symbolS * symp;
-     int *     puntp;
-{
-  elf_frob_symbol (symp, puntp);
+
+  for (opt = arm_archs; opt->name != NULL; opt++)
+    if (streq (opt->name, str))
+      {
+       march_cpu_opt = opt->value;
+       march_fpu_opt = opt->default_fpu;
+
+       if (ext != NULL)
+         return arm_parse_extension (ext, &march_cpu_opt);
+
+       return 1;
+      }
+
+  as_bad (_("unknown architecture `%s'\n"), str);
+  return 0;
 }
 
 }
 
-int
-arm_force_relocation (fixp)
-     struct fix * fixp;
-{
-  if (   fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
-      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
-      || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
-      || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
-      || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX
-      || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23)
-    return 1;
+static int
+arm_parse_fpu (char * str)
+{
+  struct arm_option_value_table * opt;
 
 
-  /* Resolve these relocations even if the symbol is extern or weak.  */
-  if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
-      || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
-    return 0;
+  for (opt = arm_fpus; opt->name != NULL; opt++)
+    if (streq (opt->name, str))
+      {
+       mfpu_opt = opt->value;
+       return 1;
+      }
 
 
-  return S_FORCE_RELOC (fixp->fx_addsy);
+  as_bad (_("unknown floating point format `%s'\n"), str);
+  return 0;
 }
 
 }
 
-static bfd_reloc_code_real_type
-arm_parse_reloc ()
+static int
+arm_parse_float_abi (char * str)
 {
 {
-  char         id [16];
-  char *       ip;
-  unsigned int i;
-  static struct
-  {
-    char * str;
-    int    len;
-    bfd_reloc_code_real_type reloc;
-  }
-  reloc_map[] =
-  {
-#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
-    MAP ("(got)",    BFD_RELOC_ARM_GOT32),
-    MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
-    /* ScottB: Jan 30, 1998 - Added support for parsing "var(PLT)"
-       branch instructions generated by GCC for PLT relocs.  */
-    MAP ("(plt)",    BFD_RELOC_ARM_PLT32),
-    { NULL, 0,         BFD_RELOC_UNUSED }
-#undef MAP
-  };
-
-  for (i = 0, ip = input_line_pointer;
-       i < sizeof (id) && (ISALNUM (*ip) || ISPUNCT (*ip));
-       i++, ip++)
-    id[i] = TOLOWER (*ip);
-
-  for (i = 0; reloc_map[i].str; i++)
-    if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
-      break;
+  struct arm_option_value_table * opt;
 
 
-  input_line_pointer += reloc_map[i].len;
+  for (opt = arm_float_abis; opt->name != NULL; opt++)
+    if (streq (opt->name, str))
+      {
+       mfloat_abi_opt = opt->value;
+       return 1;
+      }
 
 
-  return reloc_map[i].reloc;
+  as_bad (_("unknown floating point abi `%s'\n"), str);
+  return 0;
 }
 
 }
 
-static void
-s_arm_elf_cons (nbytes)
-     int nbytes;
+#ifdef OBJ_ELF
+static int
+arm_parse_eabi (char * str)
 {
 {
-  expressionS exp;
+  struct arm_option_value_table *opt;
 
 
-#ifdef md_flush_pending_output
-  md_flush_pending_output ();
+  for (opt = arm_eabis; opt->name != NULL; opt++)
+    if (streq (opt->name, str))
+      {
+       meabi_flags = opt->value;
+       return 1;
+      }
+  as_bad (_("unknown EABI `%s'\n"), str);
+  return 0;
+}
 #endif
 
 #endif
 
-  if (is_it_end_of_statement ())
-    {
-      demand_empty_rest_of_line ();
-      return;
-    }
-
-#ifdef md_cons_align
-  md_cons_align (nbytes);
+struct arm_long_option_table arm_long_opts[] =
+{
+  {"mcpu=", N_("<cpu name>\t  assemble for CPU <cpu name>"),
+   arm_parse_cpu, NULL},
+  {"march=", N_("<arch name>\t  assemble for architecture <arch name>"),
+   arm_parse_arch, NULL},
+  {"mfpu=", N_("<fpu name>\t  assemble for FPU architecture <fpu name>"),
+   arm_parse_fpu, NULL},
+  {"mfloat-abi=", N_("<abi>\t  assemble for floating point ABI <abi>"),
+   arm_parse_float_abi, NULL},
+#ifdef OBJ_ELF
+  {"meabi=", N_("<ver>\t  assemble for eabi version <ver>"),
+   arm_parse_eabi, NULL},
 #endif
 #endif
+  {NULL, NULL, 0, NULL}
+};
 
 
-  do
+int
+md_parse_option (int c, char * arg)
+{
+  struct arm_option_table *opt;
+  struct arm_long_option_table *lopt;
+
+  switch (c)
     {
     {
-      bfd_reloc_code_real_type reloc;
+#ifdef OPTION_EB
+    case OPTION_EB:
+      target_big_endian = 1;
+      break;
+#endif
 
 
-      expression (& exp);
+#ifdef OPTION_EL
+    case OPTION_EL:
+      target_big_endian = 0;
+      break;
+#endif
 
 
-      if (exp.X_op == O_symbol
-         && * input_line_pointer == '('
-         && (reloc = arm_parse_reloc ()) != BFD_RELOC_UNUSED)
-       {
-         reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
-         int size = bfd_get_reloc_size (howto);
+    case 'a':
+      /* Listing option.  Just ignore these, we don't support additional
+        ones.  */
+      return 0;
 
 
-         if (size > nbytes)
-           as_bad ("%s relocations do not fit in %d bytes",
-                   howto->name, nbytes);
-         else
+    default:
+      for (opt = arm_opts; opt->option != NULL; opt++)
+       {
+         if (c == opt->option[0]
+             && ((arg == NULL && opt->option[1] == 0)
+                 || streq (arg, opt->option + 1)))
            {
            {
-             register char *p = frag_more ((int) nbytes);
-             int offset = nbytes - size;
+#if WARN_DEPRECATED
+             /* If the option is deprecated, tell the user.  */
+             if (opt->deprecated != NULL)
+               as_tsktsk (_("option `-%c%s' is deprecated: %s"), c,
+                          arg ? arg : "", _(opt->deprecated));
+#endif
+
+             if (opt->var != NULL)
+               *opt->var = opt->value;
 
 
-             fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
-                          &exp, 0, reloc);
+             return 1;
            }
        }
            }
        }
-      else
-       emit_expr (&exp, (unsigned int) nbytes);
-    }
-  while (*input_line_pointer++ == ',');
-
-  /* Put terminator back into stream.  */
-  input_line_pointer --;
-  demand_empty_rest_of_line ();
-}
-
-#endif /* OBJ_ELF */
-
-/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
-   of an rs_align_code fragment.  */
-
-void
-arm_handle_align (fragP)
-     fragS *fragP;
-{
-  static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
-  static char const thumb_noop[2] = { 0xc0, 0x46 };
-  static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
-  static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
-
-  int bytes, fix, noop_size;
-  char * p;
-  const char * noop;
-
-  if (fragP->fr_type != rs_align_code)
-    return;
-
-  bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
-  p = fragP->fr_literal + fragP->fr_fix;
-  fix = 0;
-
-  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
-    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
 
 
-  if (fragP->tc_frag_data)
-    {
-      if (target_big_endian)
-       noop = thumb_bigend_noop;
-      else
-       noop = thumb_noop;
-      noop_size = sizeof (thumb_noop);
-    }
-  else
-    {
-      if (target_big_endian)
-       noop = arm_bigend_noop;
-      else
-       noop = arm_noop;
-      noop_size = sizeof (arm_noop);
-    }
+      for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
+       {
+         /* These options are expected to have an argument.  */
+         if (c == lopt->option[0]
+             && arg != NULL
+             && strncmp (arg, lopt->option + 1,
+                         strlen (lopt->option + 1)) == 0)
+           {
+#if WARN_DEPRECATED
+             /* If the option is deprecated, tell the user.  */
+             if (lopt->deprecated != NULL)
+               as_tsktsk (_("option `-%c%s' is deprecated: %s"), c, arg,
+                          _(lopt->deprecated));
+#endif
 
 
-  if (bytes & (noop_size - 1))
-    {
-      fix = bytes & (noop_size - 1);
-      memset (p, 0, fix);
-      p += fix;
-      bytes -= fix;
-    }
+             /* Call the sup-option parser.  */
+             return lopt->func (arg + strlen (lopt->option) - 1);
+           }
+       }
 
 
-  while (bytes >= noop_size)
-    {
-      memcpy (p, noop, noop_size);
-      p += noop_size;
-      bytes -= noop_size;
-      fix += noop_size;
+      return 0;
     }
 
     }
 
-  fragP->fr_fix += fix;
-  fragP->fr_var = noop_size;
+  return 1;
 }
 
 }
 
-/* Called from md_do_align.  Used to create an alignment
-   frag in a code section.  */
-
 void
 void
-arm_frag_align_code (n, max)
-     int n;
-     int max;
+md_show_usage (FILE * fp)
 {
 {
-  char * p;
+  struct arm_option_table *opt;
+  struct arm_long_option_table *lopt;
 
 
-  /* We assume that there will never be a requirment
-     to support alignments greater than 32 bytes.  */
-  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
-    as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+  fprintf (fp, _(" ARM-specific assembler options:\n"));
 
 
-  p = frag_var (rs_align_code,
-               MAX_MEM_FOR_RS_ALIGN_CODE,
-               1,
-               (relax_substateT) max,
-               (symbolS *) NULL,
-               (offsetT) n,
-               (char *) NULL);
-  *p = 0;
+  for (opt = arm_opts; opt->option != NULL; opt++)
+    if (opt->help != NULL)
+      fprintf (fp, "  -%-23s%s\n", opt->option, _(opt->help));
 
 
-}
+  for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
+    if (lopt->help != NULL)
+      fprintf (fp, "  -%s%s\n", lopt->option, _(lopt->help));
 
 
-/* Perform target specific initialisation of a frag.  */
+#ifdef OPTION_EB
+  fprintf (fp, _("\
+  -EB                     assemble code for a big-endian cpu\n"));
+#endif
 
 
-void
-arm_init_frag (fragP)
-     fragS *fragP;
-{
-  /* Record whether this frag is in an ARM or a THUMB area.  */
-  fragP->tc_frag_data = thumb_mode;
+#ifdef OPTION_EL
+  fprintf (fp, _("\
+  -EL                     assemble code for a little-endian cpu\n"));
+#endif
 }
 }
This page took 0.264561 seconds and 4 git commands to generate.