]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/mips-dis.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / opcodes / mips-dis.c
index 0bd5fef00d8a57e0928e5bcab2a7db71a06ea07c..97318529c87746ebd293e9d2c945ab5d329dfaf2 100644 (file)
@@ -1,7 +1,5 @@
 /* Print mips instructions for GDB, the GNU debugger, or for objdump.
-   Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2012
-   Free Software Foundation, Inc.
+   Copyright (C) 1989-2024 Free Software Foundation, Inc.
    Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
 
    This file is part of the GNU opcodes library.
    MA 02110-1301, USA.  */
 
 #include "sysdep.h"
-#include "dis-asm.h"
+#include "disassemble.h"
 #include "libiberty.h"
 #include "opcode/mips.h"
 #include "opintl.h"
+#include "elf-bfd.h"
+#include "elf/mips.h"
+#include "elfxx-mips.h"
 
 /* FIXME: These are needed to figure out if the code is mips16 or
    not. The low bit of the address is often a good indicator.  No
@@ -34,8 +35,6 @@
 
 #if !defined(EMBEDDED_ENV)
 #define SYMTAB_AVAILABLE 1
-#include "elf-bfd.h"
-#include "elf/mips.h"
 #endif
 
 /* Mips instructions are at maximum this many bytes long.  */
@@ -51,100 +50,6 @@ struct mips_cp0sel_name
   const char * const name;
 };
 
-/* The mips16 registers.  */
-static const unsigned int mips16_to_32_reg_map[] =
-{
-  16, 17, 2, 3, 4, 5, 6, 7
-};
-
-/* The microMIPS registers with type b.  */
-#define micromips_to_32_reg_b_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type c.  */
-#define micromips_to_32_reg_c_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type d.  */
-#define micromips_to_32_reg_d_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type e.  */
-#define micromips_to_32_reg_e_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type f.  */
-#define micromips_to_32_reg_f_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type g.  */
-#define micromips_to_32_reg_g_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type h.  */
-static const unsigned int micromips_to_32_reg_h_map[] =
-{
-  5, 5, 6, 4, 4, 4, 4, 4
-};
-
-/* The microMIPS registers with type i.  */
-static const unsigned int micromips_to_32_reg_i_map[] =
-{
-  6, 7, 7, 21, 22, 5, 6, 7
-};
-
-/* The microMIPS registers with type j: 32 registers.  */
-
-/* The microMIPS registers with type l.  */
-#define micromips_to_32_reg_l_map      mips16_to_32_reg_map
-
-/* The microMIPS registers with type m.  */
-static const unsigned int micromips_to_32_reg_m_map[] =
-{
-  0, 17, 2, 3, 16, 18, 19, 20
-};
-
-/* The microMIPS registers with type n.  */
-#define micromips_to_32_reg_n_map      micromips_to_32_reg_m_map
-
-/* The microMIPS registers with type p: 32 registers.  */
-
-/* The microMIPS registers with type q.  */
-static const unsigned int micromips_to_32_reg_q_map[] =
-{
-  0, 17, 2, 3, 4, 5, 6, 7
-};
-
-/* reg type s is $29.  */
-
-/* reg type t is the same as the last register.  */
-
-/* reg type y is $31.  */
-
-/* reg type z is $0.  */
-
-/* micromips imm B type.  */
-static const int micromips_imm_b_map[8] =
-{
-  1, 4, 8, 12, 16, 20, 24, -1
-};
-
-/* micromips imm C type.  */
-static const int micromips_imm_c_map[16] =
-{
-  128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
-};
-
-/* micromips imm D type: (-512..511)<<1.  */
-/* micromips imm E type: (-64..63)<<1.  */
-/* micromips imm F type: (0..63).  */
-/* micromips imm G type: (-1..14).  */
-/* micromips imm H type: (0..15)<<1.  */
-/* micromips imm I type: (-1..126).  */
-/* micromips imm J type: (0..15)<<2.  */
-/* micromips imm L type: (0..15).  */
-/* micromips imm M type: (1..8).  */
-/* micromips imm W type: (0..63)<<2.  */
-/* micromips imm X type: (-8..7).  */
-/* micromips imm Y type: (-258..-3, 2..257)<<2.  */
-
-#define mips16_reg_names(rn)   mips_gpr_names[mips16_to_32_reg_map[rn]]
-
-
 static const char * const mips_gpr_names_numeric[32] =
 {
   "$0",   "$1",   "$2",   "$3",   "$4",   "$5",   "$6",   "$7",
@@ -209,6 +114,26 @@ static const char * const mips_cp0_names_numeric[32] =
   "$24",  "$25",  "$26",  "$27",  "$28",  "$29",  "$30",  "$31"
 };
 
+static const char * const mips_cp1_names_numeric[32] =
+{
+  "$0",   "$1",   "$2",   "$3",   "$4",   "$5",   "$6",   "$7",
+  "$8",   "$9",   "$10",  "$11",  "$12",  "$13",  "$14",  "$15",
+  "$16",  "$17",  "$18",  "$19",  "$20",  "$21",  "$22",  "$23",
+  "$24",  "$25",  "$26",  "$27",  "$28",  "$29",  "$30",  "$31"
+};
+
+static const char * const mips_cp0_names_r3900[32] =
+{
+  "$0",           "$1",           "$2",           "c0_config",
+  "$4",           "$5",           "$6",           "c0_cache",
+  "c0_badvaddr",  "$9",           "$10",          "$11",
+  "c0_sr",        "c0_cause",     "c0_epc",       "c0_prid",
+  "c0_debug",     "c0_depc",      "$18",          "$19",
+  "$20",          "$21",          "$22",          "$23",
+  "$24",          "$25",          "$26",          "$27",
+  "$28",          "$29",          "$30",          "$31",
+};
+
 static const char * const mips_cp0_names_r3000[32] =
 {
   "c0_index",     "c0_random",    "c0_entrylo",   "$3",
@@ -245,18 +170,6 @@ static const char * const mips_cp0_names_r5900[32] =
   "c0_taglo",     "c0_taghi",     "c0_errorepc",  "$31"
 };
 
-static const struct mips_cp0sel_name mips_cp0sel_names_mipsr5900[] =
-{
-  { 24, 2, "c0_iab"                    },
-  { 24, 3, "c0_iabm"           },
-  { 24, 4, "c0_dab"                    },
-  { 24, 5, "c0_dabm"           },
-  { 24, 6, "c0_dvb"                    },
-  { 24, 7, "c0_dvbm"           },
-  { 25, 1, "c0_perfcnt,1"      },
-  { 25, 2, "c0_perfcnt,2"      }
-};
-
 static const char * const mips_cp0_names_mips3264[32] =
 {
   "c0_index",     "c0_random",    "c0_entrylo0",  "c0_entrylo1",
@@ -269,6 +182,30 @@ static const char * const mips_cp0_names_mips3264[32] =
   "c0_taglo",     "c0_taghi",     "c0_errorepc",  "c0_desave",
 };
 
+static const char * const mips_cp1_names_mips[32] =
+{
+  "c1_fir",       "$1",           "$2",           "$3",
+  "$4",           "$5",           "$6",           "$7",
+  "$8",           "$9",           "$10",          "$11",
+  "$12",          "$13",          "$14",          "$15",
+  "$16",          "$17",          "$18",          "$19",
+  "$20",          "$21",          "$22",          "$23",
+  "$24",          "$25",          "$26",          "$27",
+  "$28",          "$29",          "$30",          "c1_fcsr"
+};
+
+static const char * const mips_cp1_names_mips3264[32] =
+{
+  "c1_fir",       "c1_ufr",       "$2",           "$3",
+  "c1_unfr",      "$5",           "$6",           "$7",
+  "$8",           "$9",           "$10",          "$11",
+  "$12",          "$13",          "$14",          "$15",
+  "$16",          "$17",          "$18",          "$19",
+  "$20",          "$21",          "$22",          "$23",
+  "$24",          "c1_fccr",      "c1_fexr",      "$27",
+  "c1_fenr",      "$29",          "$30",          "c1_fcsr"
+};
+
 static const struct mips_cp0sel_name mips_cp0sel_names_mips3264[] =
 {
   { 16, 1, "c0_config1"                },
@@ -495,6 +432,15 @@ static const char * const mips_hwr_names_mips3264r2[32] =
   "$24",  "$25",  "$26",  "$27",  "$28",  "$29",  "$30",  "$31"
 };
 
+static const char * const msa_control_names[32] =
+{
+  "msa_ir",    "msa_csr",      "msa_access",   "msa_save",
+  "msa_modify",        "msa_request",  "msa_map",      "msa_unmap",
+  "$8",   "$9",   "$10",  "$11",  "$12",  "$13",  "$14",  "$15",
+  "$16",  "$17",  "$18",  "$19",  "$20",  "$21",  "$22",  "$23",
+  "$24",  "$25",  "$26",  "$27",  "$28",  "$29",  "$30",  "$31"
+};
+
 struct mips_abi_choice
 {
   const char * name;
@@ -517,65 +463,95 @@ struct mips_arch_choice
   unsigned long bfd_mach;
   int processor;
   int isa;
+  int ase;
   const char * const *cp0_names;
   const struct mips_cp0sel_name *cp0sel_names;
   unsigned int cp0sel_names_len;
+  const char * const *cp1_names;
   const char * const *hwr_names;
 };
 
 const struct mips_arch_choice mips_arch_choices[] =
 {
-  { "numeric", 0, 0, 0, 0,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-
-  { "r3000",   1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1,
-    mips_cp0_names_r3000, NULL, 0, mips_hwr_names_numeric },
-  { "r3900",   1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r4000",   1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3,
-    mips_cp0_names_r4000, NULL, 0, mips_hwr_names_numeric },
-  { "r4010",   1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "vr4100",  1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "vr4111",  1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "vr4120",  1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r4300",   1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r4400",   1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3,
-    mips_cp0_names_r4000, NULL, 0, mips_hwr_names_numeric },
-  { "r4600",   1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r4650",   1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r5000",   1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "vr5400",  1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "vr5500",  1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r5900",   1, bfd_mach_mips5900, CPU_R5900, ISA_MIPS3,
-    mips_cp0_names_r5900, NULL, 0, mips_hwr_names_numeric },
-  { "r6000",   1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "rm7000",  1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "rm9000",  1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r8000",   1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r10000",  1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r12000",  1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r14000",  1, bfd_mach_mips14000, CPU_R14000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "r16000",  1, bfd_mach_mips16000, CPU_R16000, ISA_MIPS4,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
-  { "mips5",   1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+  { "numeric", 0, 0, 0, 0, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
+    mips_hwr_names_numeric },
+
+  { "r3000",   1, bfd_mach_mips3000, CPU_R3000, ISA_MIPS1, 0,
+    mips_cp0_names_r3000, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r3900",   1, bfd_mach_mips3900, CPU_R3900, ISA_MIPS1, 0,
+    mips_cp0_names_r3900, NULL, 0, mips_cp1_names_numeric,
+    mips_hwr_names_numeric },
+  { "r4000",   1, bfd_mach_mips4000, CPU_R4000, ISA_MIPS3, 0,
+    mips_cp0_names_r4000, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r4010",   1, bfd_mach_mips4010, CPU_R4010, ISA_MIPS2, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "allegrex",        1, bfd_mach_mips_allegrex, CPU_ALLEGREX, ISA_MIPS2, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
+    mips_hwr_names_numeric },
+  { "vr4100",  1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "vr4111",  1, bfd_mach_mips4111, CPU_R4111, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "vr4120",  1, bfd_mach_mips4120, CPU_VR4120, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r4300",   1, bfd_mach_mips4300, CPU_R4300, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r4400",   1, bfd_mach_mips4400, CPU_R4400, ISA_MIPS3, 0,
+    mips_cp0_names_r4000, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r4600",   1, bfd_mach_mips4600, CPU_R4600, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r4650",   1, bfd_mach_mips4650, CPU_R4650, ISA_MIPS3, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r5000",   1, bfd_mach_mips5000, CPU_R5000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "vr5400",  1, bfd_mach_mips5400, CPU_VR5400, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "vr5500",  1, bfd_mach_mips5500, CPU_VR5500, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r5900",   1, bfd_mach_mips5900, CPU_R5900, ISA_MIPS3, 0,
+    mips_cp0_names_r5900, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r6000",   1, bfd_mach_mips6000, CPU_R6000, ISA_MIPS2, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "rm7000",  1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "rm9000",  1, bfd_mach_mips7000, CPU_RM7000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r8000",   1, bfd_mach_mips8000, CPU_R8000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r10000",  1, bfd_mach_mips10000, CPU_R10000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r12000",  1, bfd_mach_mips12000, CPU_R12000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r14000",  1, bfd_mach_mips14000, CPU_R14000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "r16000",  1, bfd_mach_mips16000, CPU_R16000, ISA_MIPS4, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
+  { "mips5",   1, bfd_mach_mips5, CPU_MIPS5, ISA_MIPS5, 0,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
 
   /* For stock MIPS32, disassemble all applicable MIPS-specified ASEs.
      Note that MIPS-3D and MDMX are not applicable to MIPS32.  (See
@@ -583,80 +559,162 @@ const struct mips_arch_choice mips_arch_choices[] =
      MIPS32 Architecture_ (MIPS Document Number MD00082, Revision 0.95),
      page 1.  */
   { "mips32",  1, bfd_mach_mipsisa32, CPU_MIPS32,
-    ISA_MIPS32 | INSN_SMARTMIPS,
+    ISA_MIPS32,  ASE_SMARTMIPS,
     mips_cp0_names_mips3264,
     mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
-    mips_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "mips32r2",        1, bfd_mach_mipsisa32r2, CPU_MIPS32R2,
-    (ISA_MIPS32R2 | INSN_SMARTMIPS | INSN_DSP | INSN_DSPR2
-     | INSN_MIPS3D | INSN_MT | INSN_MCU),
+    ISA_MIPS32R2,
+    (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D
+     | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips32r3",        1, bfd_mach_mipsisa32r3, CPU_MIPS32R3,
+    ISA_MIPS32R3,
+    (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D
+     | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips32r5",        1, bfd_mach_mipsisa32r5, CPU_MIPS32R5,
+    ISA_MIPS32R5,
+    (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D
+     | ASE_MT | ASE_MCU | ASE_VIRT | ASE_MSA | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips32r6",        1, bfd_mach_mipsisa32r6, CPU_MIPS32R6,
+    ISA_MIPS32R6,
+    (ASE_EVA | ASE_MSA | ASE_VIRT | ASE_XPA | ASE_MCU | ASE_MT | ASE_DSP
+     | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC | ASE_GINV),
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
-    mips_hwr_names_mips3264r2 },
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
 
   /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs.  */
   { "mips64",  1, bfd_mach_mipsisa64, CPU_MIPS64,
-    ISA_MIPS64 | INSN_MIPS3D | INSN_MDMX,
+    ISA_MIPS64,  ASE_MIPS3D | ASE_MDMX,
     mips_cp0_names_mips3264,
     mips_cp0sel_names_mips3264, ARRAY_SIZE (mips_cp0sel_names_mips3264),
-    mips_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "mips64r2",        1, bfd_mach_mipsisa64r2, CPU_MIPS64R2,
-    (ISA_MIPS64R2 | INSN_MIPS3D | INSN_DSP | INSN_DSPR2
-     | INSN_DSP64 | INSN_MT | INSN_MDMX | INSN_MCU),
+    ISA_MIPS64R2,
+    (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT
+     | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips64r3",        1, bfd_mach_mipsisa64r3, CPU_MIPS64R3,
+    ISA_MIPS64R3,
+    (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT
+     | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips64r5",        1, bfd_mach_mipsisa64r5, CPU_MIPS64R5,
+    ISA_MIPS64R5,
+    (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT
+     | ASE_MCU | ASE_VIRT | ASE_VIRT64 | ASE_MSA | ASE_MSA64 | ASE_XPA),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "mips64r6",        1, bfd_mach_mipsisa64r6, CPU_MIPS64R6,
+    ISA_MIPS64R6,
+    (ASE_EVA | ASE_MSA | ASE_MSA64 | ASE_XPA | ASE_VIRT | ASE_VIRT64
+     | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC
+     | ASE_CRC64 | ASE_GINV),
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
-    mips_hwr_names_mips3264r2 },
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "interaptiv-mr2",  1, bfd_mach_mips_interaptiv_mr2, CPU_INTERAPTIV_MR2,
+    ISA_MIPS32R3,
+    ASE_MT | ASE_EVA | ASE_DSP | ASE_DSPR2 | ASE_MIPS16E2 | ASE_MIPS16E2_MT,
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
 
   { "sb1",     1, bfd_mach_mips_sb1, CPU_SB1,
-    ISA_MIPS64 | INSN_MIPS3D | INSN_SB1,
+    ISA_MIPS64 | INSN_SB1,  ASE_MIPS3D,
     mips_cp0_names_sb1,
     mips_cp0sel_names_sb1, ARRAY_SIZE (mips_cp0sel_names_sb1),
-    mips_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "loongson2e",   1, bfd_mach_mips_loongson_2e, CPU_LOONGSON_2E,
-    ISA_MIPS3 | INSN_LOONGSON_2E, mips_cp0_names_numeric, 
-    NULL, 0, mips_hwr_names_numeric },
+    ISA_MIPS3 | INSN_LOONGSON_2E, 0, mips_cp0_names_numeric,
+    NULL, 0, mips_cp1_names_mips, mips_hwr_names_numeric },
 
   { "loongson2f",   1, bfd_mach_mips_loongson_2f, CPU_LOONGSON_2F,
-    ISA_MIPS3 | INSN_LOONGSON_2F, mips_cp0_names_numeric, 
-    NULL, 0, mips_hwr_names_numeric },
+    ISA_MIPS3 | INSN_LOONGSON_2F, ASE_LOONGSON_MMI, mips_cp0_names_numeric,
+    NULL, 0, mips_cp1_names_mips, mips_hwr_names_numeric },
 
-  { "loongson3a",   1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A,
-    ISA_MIPS64 | INSN_LOONGSON_3A, mips_cp0_names_numeric, 
-    NULL, 0, mips_hwr_names_numeric },
+  /* The loongson3a is an alias of gs464 for compatibility */
+  { "loongson3a",   1, bfd_mach_mips_gs464, CPU_GS464,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+    mips_hwr_names_numeric },
 
-  { "octeon",   1, bfd_mach_mips_octeon, CPU_OCTEON,
-    ISA_MIPS64R2 | INSN_OCTEON, mips_cp0_names_numeric, NULL, 0,
+  { "gs464",   1, bfd_mach_mips_gs464, CPU_GS464,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+    mips_hwr_names_numeric },
+
+  { "gs464e",   1, bfd_mach_mips_gs464e, CPU_GS464E,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+    | ASE_LOONGSON_EXT2, mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
     mips_hwr_names_numeric },
 
+  { "gs264e",   1, bfd_mach_mips_gs264e, CPU_GS264E,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+    | ASE_LOONGSON_EXT2 | ASE_MSA | ASE_MSA64, mips_cp0_names_numeric, NULL,
+    0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
+
+  { "octeon",   1, bfd_mach_mips_octeon, CPU_OCTEON,
+    ISA_MIPS64R2 | INSN_OCTEON, 0, mips_cp0_names_numeric, NULL, 0,
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
+
   { "octeon+",   1, bfd_mach_mips_octeonp, CPU_OCTEONP,
-    ISA_MIPS64R2 | INSN_OCTEONP, mips_cp0_names_numeric,
-    NULL, 0, mips_hwr_names_numeric },
+    ISA_MIPS64R2 | INSN_OCTEONP, 0, mips_cp0_names_numeric,
+    NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon2",   1, bfd_mach_mips_octeon2, CPU_OCTEON2,
-    ISA_MIPS64R2 | INSN_OCTEON2, mips_cp0_names_numeric,
-    NULL, 0, mips_hwr_names_numeric },
+    ISA_MIPS64R2 | INSN_OCTEON2, 0, mips_cp0_names_numeric,
+    NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
+
+  { "octeon3",   1, bfd_mach_mips_octeon3, CPU_OCTEON3,
+    ISA_MIPS64R5 | INSN_OCTEON3, ASE_VIRT | ASE_VIRT64,
+    mips_cp0_names_numeric,
+    NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "xlr", 1, bfd_mach_mips_xlr, CPU_XLR,
-    ISA_MIPS64 | INSN_XLR,
+    ISA_MIPS64 | INSN_XLR, 0,
     mips_cp0_names_xlr,
     mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr),
-    mips_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   /* XLP is mostly like XLR, with the prominent exception it is being
      MIPS64R2.  */
   { "xlp", 1, bfd_mach_mips_xlr, CPU_XLR,
-    ISA_MIPS64R2 | INSN_XLR,
+    ISA_MIPS64R2 | INSN_XLR, 0,
     mips_cp0_names_xlr,
     mips_cp0sel_names_xlr, ARRAY_SIZE (mips_cp0sel_names_xlr),
-    mips_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   /* This entry, mips16, is here only for ISA/processor selection; do
      not print its name.  */
-  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64,
+    ASE_MIPS16E2 | ASE_MIPS16E2_MT,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
+    mips_hwr_names_numeric },
 };
 
 /* ISA and processor type to disassemble for, and register names to use.
@@ -664,12 +722,14 @@ const struct mips_arch_choice mips_arch_choices[] =
    values.  */
 static int mips_processor;
 static int mips_isa;
+static int mips_ase;
 static int micromips_ase;
 static const char * const *mips_gpr_names;
 static const char * const *mips_fpr_names;
 static const char * const *mips_cp0_names;
 static const struct mips_cp0sel_name *mips_cp0sel_names;
 static int mips_cp0sel_names_len;
+static const char * const *mips_cp1_names;
 static const char * const *mips_hwr_names;
 
 /* Other options */
@@ -758,6 +818,60 @@ is_micromips (Elf_Internal_Ehdr *header)
   return 0;
 }
 
+/* Convert ASE flags from .MIPS.abiflags to internal values.  */
+
+static unsigned long
+mips_convert_abiflags_ases (unsigned long afl_ases)
+{
+  unsigned long opcode_ases = 0;
+
+  if (afl_ases & AFL_ASE_DSP)
+    opcode_ases |= ASE_DSP;
+  if (afl_ases & AFL_ASE_DSPR2)
+    opcode_ases |= ASE_DSPR2;
+  if (afl_ases & AFL_ASE_EVA)
+    opcode_ases |= ASE_EVA;
+  if (afl_ases & AFL_ASE_MCU)
+    opcode_ases |= ASE_MCU;
+  if (afl_ases & AFL_ASE_MDMX)
+    opcode_ases |= ASE_MDMX;
+  if (afl_ases & AFL_ASE_MIPS3D)
+    opcode_ases |= ASE_MIPS3D;
+  if (afl_ases & AFL_ASE_MT)
+    opcode_ases |= ASE_MT;
+  if (afl_ases & AFL_ASE_SMARTMIPS)
+    opcode_ases |= ASE_SMARTMIPS;
+  if (afl_ases & AFL_ASE_VIRT)
+    opcode_ases |= ASE_VIRT;
+  if (afl_ases & AFL_ASE_MSA)
+    opcode_ases |= ASE_MSA;
+  if (afl_ases & AFL_ASE_XPA)
+    opcode_ases |= ASE_XPA;
+  if (afl_ases & AFL_ASE_DSPR3)
+    opcode_ases |= ASE_DSPR3;
+  if (afl_ases & AFL_ASE_MIPS16E2)
+    opcode_ases |= ASE_MIPS16E2;
+  return opcode_ases;
+}
+
+/* Calculate combination ASE flags from regular ASE flags.  */
+
+static unsigned long
+mips_calculate_combination_ases (int opcode_isa, unsigned long opcode_ases)
+{
+  unsigned long combination_ases = 0;
+
+  if ((opcode_ases & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT))
+    combination_ases |= ASE_XPA_VIRT;
+  if ((opcode_ases & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT))
+    combination_ases |= ASE_MIPS16E2_MT;
+  if ((opcode_ases & ASE_EVA)
+      && ((opcode_isa & INSN_ISA_MASK) == ISA_MIPS64R6
+         || (opcode_isa & INSN_ISA_MASK) == ISA_MIPS32R6))
+    combination_ases |= ASE_EVA_R6;
+  return combination_ases;
+}
+
 static void
 set_default_mips_dis_options (struct disassemble_info *info)
 {
@@ -769,45 +883,133 @@ set_default_mips_dis_options (struct disassemble_info *info)
   mips_isa = ISA_MIPS3;
   mips_processor = CPU_R3000;
   micromips_ase = 0;
+  mips_ase = 0;
   mips_gpr_names = mips_gpr_names_oldabi;
   mips_fpr_names = mips_fpr_names_numeric;
   mips_cp0_names = mips_cp0_names_numeric;
   mips_cp0sel_names = NULL;
   mips_cp0sel_names_len = 0;
+  mips_cp1_names = mips_cp1_names_numeric;
   mips_hwr_names = mips_hwr_names_numeric;
   no_aliases = 0;
 
-  /* Update settings according to the ELF file header flags.  */
-  if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
-    {
-      Elf_Internal_Ehdr *header;
-
-      header = elf_elfheader (info->section->owner);
-      /* If an ELF "newabi" binary, use the n32/(n)64 GPR names.  */
-      if (is_newabi (header))
-       mips_gpr_names = mips_gpr_names_newabi;
-      /* If a microMIPS binary, then don't use MIPS16 bindings.  */
-      micromips_ase = is_micromips (header);
-    }
-
   /* Set ISA, architecture, and cp0 register names as best we can.  */
 #if ! SYMTAB_AVAILABLE
   /* This is running out on a target machine, not in a host tool.
      FIXME: Where does mips_target_info come from?  */
   target_processor = mips_target_info.processor;
   mips_isa = mips_target_info.isa;
+  mips_ase = mips_target_info.ase;
 #else
   chosen_arch = choose_arch_by_number (info->mach);
   if (chosen_arch != NULL)
     {
       mips_processor = chosen_arch->processor;
       mips_isa = chosen_arch->isa;
+      mips_ase = chosen_arch->ase;
       mips_cp0_names = chosen_arch->cp0_names;
       mips_cp0sel_names = chosen_arch->cp0sel_names;
       mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+      mips_cp1_names = chosen_arch->cp1_names;
       mips_hwr_names = chosen_arch->hwr_names;
     }
+
+  /* Update settings according to the ELF file header flags.  */
+  if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
+    {
+      struct bfd *abfd = info->section->owner;
+      Elf_Internal_Ehdr *header = elf_elfheader (abfd);
+      Elf_Internal_ABIFlags_v0 *abiflags = NULL;
+
+      /* We won't ever get here if !HAVE_BFD_MIPS_ELF_GET_ABIFLAGS,
+        because we won't then have a MIPS/ELF BFD, however we need
+        to guard against a link error in a `--enable-targets=...'
+        configuration with a 32-bit host where the MIPS target is
+        a secondary, or with MIPS/ECOFF configurations.  */
+#ifdef HAVE_BFD_MIPS_ELF_GET_ABIFLAGS
+      abiflags = bfd_mips_elf_get_abiflags (abfd);
+#endif
+      /* If an ELF "newabi" binary, use the n32/(n)64 GPR names.  */
+      if (is_newabi (header))
+       mips_gpr_names = mips_gpr_names_newabi;
+      /* If a microMIPS binary, then don't use MIPS16 bindings.  */
+      micromips_ase = is_micromips (header);
+      /* OR in any extra ASE flags set in ELF file structures.  */
+      if (abiflags)
+       mips_ase |= mips_convert_abiflags_ases (abiflags->ases);
+      else if (header->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+       mips_ase |= ASE_MDMX;
+    }
 #endif
+  mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+}
+
+/* Parse an ASE disassembler option and set the corresponding global
+   ASE flag(s).  Return TRUE if successful, FALSE otherwise.  */
+
+static bool
+parse_mips_ase_option (const char *option)
+{
+  if (startswith (option, "msa"))
+    {
+      mips_ase |= ASE_MSA;
+      if ((mips_isa & INSN_ISA_MASK) == ISA_MIPS64R2
+          || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R3
+          || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5
+          || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6)
+         mips_ase |= ASE_MSA64;
+      return true;
+    }
+
+  if (startswith (option, "virt"))
+    {
+      mips_ase |= ASE_VIRT;
+      if (mips_isa & ISA_MIPS64R2
+         || mips_isa & ISA_MIPS64R3
+         || mips_isa & ISA_MIPS64R5
+         || mips_isa & ISA_MIPS64R6)
+       mips_ase |= ASE_VIRT64;
+      return true;
+    }
+
+  if (startswith (option, "xpa"))
+    {
+      mips_ase |= ASE_XPA;
+      return true;
+    }
+
+  if (startswith (option, "ginv"))
+    {
+      mips_ase |= ASE_GINV;
+      return true;
+    }
+
+  if (startswith (option, "loongson-mmi"))
+    {
+      mips_ase |= ASE_LOONGSON_MMI;
+      return true;
+    }
+
+  if (startswith (option, "loongson-cam"))
+    {
+      mips_ase |= ASE_LOONGSON_CAM;
+      return true;
+    }
+  
+  /* Put here for match ext2 frist */
+  if (startswith (option, "loongson-ext2"))
+    {
+      mips_ase |= ASE_LOONGSON_EXT2;
+      return true;
+    }
+
+  if (startswith (option, "loongson-ext"))
+    {
+      mips_ase |= ASE_LOONGSON_EXT;
+      return true;
+    }
+
+  return false;
 }
 
 static void
@@ -819,12 +1021,18 @@ parse_mips_dis_option (const char *option, unsigned int len)
   const struct mips_arch_choice *chosen_arch;
 
   /* Try to match options that are simple flags */
-  if (CONST_STRNEQ (option, "no-aliases"))
+  if (startswith (option, "no-aliases"))
     {
       no_aliases = 1;
       return;
     }
-  
+
+  if (parse_mips_ase_option (option))
+    {
+      mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+      return;
+    }
+
   /* Look for the = that delimits the end of the option name.  */
   for (i = 0; i < len; i++)
     if (option[i] == '=')
@@ -872,6 +1080,15 @@ parse_mips_dis_option (const char *option, unsigned int len)
       return;
     }
 
+  if (strncmp ("cp1-names", option, optionlen) == 0
+      && strlen ("cp1-names") == optionlen)
+    {
+      chosen_arch = choose_arch_by_name (val, vallen);
+      if (chosen_arch != NULL)
+       mips_cp1_names = chosen_arch->cp1_names;
+      return;
+    }
+
   if (strncmp ("hwr-names", option, optionlen) == 0
       && strlen ("hwr-names") == optionlen)
     {
@@ -900,6 +1117,7 @@ parse_mips_dis_option (const char *option, unsigned int len)
          mips_cp0_names = chosen_arch->cp0_names;
          mips_cp0sel_names = chosen_arch->cp0sel_names;
          mips_cp0sel_names_len = chosen_arch->cp0sel_names_len;
+         mips_cp1_names = chosen_arch->cp1_names;
          mips_hwr_names = chosen_arch->hwr_names;
        }
       return;
@@ -951,513 +1169,838 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names,
       return &names[i];
   return NULL;
 }
-\f
-/* Print insn arguments for 32/64-bit code.  */
 
-static void
-print_insn_args (const char *d,
-                int l,
-                bfd_vma pc,
-                struct disassemble_info *info,
-                const struct mips_opcode *opp)
-{
-  const fprintf_ftype infprintf = info->fprintf_func;
-  unsigned int lsb, msb, msbd;
-  void *is = info->stream;
-  int op;
+/* Print register REGNO, of type TYPE, for instruction OPCODE.  */
 
-  lsb = 0;
+static void
+print_reg (struct disassemble_info *info, const struct mips_opcode *opcode,
+          enum mips_reg_operand_type type, int regno)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
 
-#define GET_OP(insn, field) \
-  (((insn) >> OP_SH_##field) & OP_MASK_##field)
-#define GET_OP_S(insn, field) \
-  ((GET_OP (insn, field) ^ ((OP_MASK_##field >> 1) + 1)) \
-   - ((OP_MASK_##field >> 1) + 1))
-  for (; *d != '\0'; d++)
+  switch (type)
     {
-      switch (*d)
-       {
-       case ',':
-       case '(':
-       case ')':
-       case '[':
-       case ']':
-         infprintf (is, "%c", *d);
-         break;
+    case OP_REG_GP:
+      infprintf (info->stream, dis_style_register, "%s",
+                mips_gpr_names[regno]);
+      break;
 
-       case '+':
-         /* Extension character; switch for second char.  */
-         d++;
-         switch (*d)
-           {
-           case '\0':
-             /* xgettext:c-format */
-             infprintf (is,
-                        _("# internal error, "
-                          "incomplete extension sequence (+)"));
-             return;
+    case OP_REG_FP:
+      infprintf (info->stream, dis_style_register, "%s",
+                mips_fpr_names[regno]);
+      break;
 
-           case 'A':
-             lsb = GET_OP (l, SHAMT);
-             infprintf (is, "0x%x", lsb);
-             break;
-       
-           case 'B':
-             msb = GET_OP (l, INSMSB);
-             infprintf (is, "0x%x", msb - lsb + 1);
-             break;
-
-           case '1':
-             infprintf (is, "0x%x", GET_OP (l, UDI1));
-             break;
-             
-           case '2':
-             infprintf (is, "0x%x", GET_OP (l, UDI2));
-             break;
-             
-           case '3':
-             infprintf (is, "0x%x", GET_OP (l, UDI3));
-             break;
-      
-           case '4':
-             infprintf (is, "0x%x", GET_OP (l, UDI4));
-             break;
-             
-           case 'C':
-           case 'H':
-             msbd = GET_OP (l, EXTMSBD);
-             infprintf (is, "0x%x", msbd + 1);
-             break;
-
-           case 'D':
-             {
-               const struct mips_cp0sel_name *n;
-               unsigned int cp0reg, sel;
-
-               cp0reg = GET_OP (l, RD);
-               sel = GET_OP (l, SEL);
-
-               /* CP0 register including 'sel' code for mtcN (et al.), to be
-                  printed textually if known.  If not known, print both
-                  CP0 register name and sel numerically since CP0 register
-                  with sel 0 may have a name unrelated to register being
-                  printed.  */
-               n = lookup_mips_cp0sel_name(mips_cp0sel_names,
-                                           mips_cp0sel_names_len, cp0reg, sel);
-               if (n != NULL)
-                 infprintf (is, "%s", n->name);
-               else
-                 infprintf (is, "$%d,%d", cp0reg, sel);
-               break;
-             }
+    case OP_REG_CCC:
+      if (opcode->pinfo & (FP_D | FP_S))
+       infprintf (info->stream, dis_style_register, "$fcc%d", regno);
+      else
+       infprintf (info->stream, dis_style_register, "$cc%d", regno);
+      break;
 
-           case 'E':
-             lsb = GET_OP (l, SHAMT) + 32;
-             infprintf (is, "0x%x", lsb);
-             break;
-       
-           case 'F':
-             msb = GET_OP (l, INSMSB) + 32;
-             infprintf (is, "0x%x", msb - lsb + 1);
-             break;
-
-           case 'G':
-             msbd = GET_OP (l, EXTMSBD) + 32;
-             infprintf (is, "0x%x", msbd + 1);
-             break;
-
-           case 't': /* Coprocessor 0 reg name */
-             infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]);
-             break;
-
-           case 'T': /* Coprocessor 0 reg name */
-             {
-               const struct mips_cp0sel_name *n;
-               unsigned int cp0reg, sel;
-
-               cp0reg = GET_OP (l, RT);
-               sel = GET_OP (l, SEL);
-
-               /* CP0 register including 'sel' code for mftc0, to be
-                  printed textually if known.  If not known, print both
-                  CP0 register name and sel numerically since CP0 register
-                  with sel 0 may have a name unrelated to register being
-                  printed.  */
-               n = lookup_mips_cp0sel_name(mips_cp0sel_names,
-                                           mips_cp0sel_names_len, cp0reg, sel);
-               if (n != NULL)
-                 infprintf (is, "%s", n->name);
-               else
-                 infprintf (is, "$%d,%d", cp0reg, sel);
-               break;
-             }
+    case OP_REG_VEC:
+      if (opcode->membership & INSN_5400)
+       infprintf (info->stream, dis_style_register, "$f%d", regno);
+      else
+       infprintf (info->stream, dis_style_register, "$v%d", regno);
+      break;
 
-           case 'x':           /* bbit bit index */
-             infprintf (is, "0x%x", GET_OP (l, BBITIND));
-             break;
+    case OP_REG_ACC:
+      infprintf (info->stream, dis_style_register, "$ac%d", regno);
+      break;
 
-           case 'p':           /* cins, cins32, exts and exts32 position */
-             infprintf (is, "0x%x", GET_OP (l, CINSPOS));
-             break;
+    case OP_REG_COPRO:
+      if (opcode->name[strlen (opcode->name) - 1] == '0')
+       infprintf (info->stream, dis_style_register, "%s", mips_cp0_names[regno]);
+      else
+       infprintf (info->stream, dis_style_register, "$%d", regno);
+      break;
 
-           case 's':           /* cins and exts length-minus-one */
-             infprintf (is, "0x%x", GET_OP (l, CINSLM1));
-             break;
+    case OP_REG_CONTROL:
+      if (opcode->name[strlen (opcode->name) - 1] == '1')
+       infprintf (info->stream, dis_style_register, "%s", mips_cp1_names[regno]);
+      else
+       infprintf (info->stream, dis_style_register, "$%d", regno);
+      break;
 
-           case 'S':           /* cins32 and exts32 length-minus-one field */
-             infprintf (is, "0x%x", GET_OP (l, CINSLM1));
-             break;
+    case OP_REG_HW:
+      infprintf (info->stream, dis_style_register, "%s", mips_hwr_names[regno]);
+      break;
 
-           case 'Q':           /* seqi/snei immediate field */
-             infprintf (is, "%d", GET_OP_S (l, SEQI));
-             break;
+    case OP_REG_VF:
+      infprintf (info->stream, dis_style_register, "$vf%d", regno);
+      break;
 
-           case 'a':           /* 8-bit signed offset in bit 6 */
-             infprintf (is, "%d", GET_OP_S (l, OFFSET_A));
-             break;
+    case OP_REG_VI:
+      infprintf (info->stream, dis_style_register, "$vi%d", regno);
+      break;
 
-           case 'b':           /* 8-bit signed offset in bit 3 */
-             infprintf (is, "%d", GET_OP_S (l, OFFSET_B));
-             break;
+    case OP_REG_R5900_I:
+      infprintf (info->stream, dis_style_register, "$I");
+      break;
 
-           case 'c':           /* 9-bit signed offset in bit 6 */
-             /* Left shift 4 bits to print the real offset.  */
-             infprintf (is, "%d", GET_OP_S (l, OFFSET_C) << 4);
-             break;
+    case OP_REG_R5900_Q:
+      infprintf (info->stream, dis_style_register, "$Q");
+      break;
 
-           case 'z':
-             infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]);
-             break;
+    case OP_REG_R5900_R:
+      infprintf (info->stream, dis_style_register, "$R");
+      break;
 
-           case 'Z':
-             infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]);
-             break;
+    case OP_REG_R5900_ACC:
+      infprintf (info->stream, dis_style_register, "$ACC");
+      break;
 
-           default:
-             /* xgettext:c-format */
-             infprintf (is,
-                        _("# internal error, "
-                          "undefined extension sequence (+%c)"),
-                        *d);
-             return;
-           }
-         break;
+    case OP_REG_MSA:
+      infprintf (info->stream, dis_style_register, "$w%d", regno);
+      break;
 
-       case '2':
-         infprintf (is, "0x%x", GET_OP (l, BP));
-         break;
+    case OP_REG_MSA_CTRL:
+      infprintf (info->stream, dis_style_register, "%s",
+                msa_control_names[regno]);
+      break;
 
-       case '3':
-         infprintf (is, "0x%x", GET_OP (l, SA3));
-         break;
+    }
+}
+\f
+/* Used to track the state carried over from previous operands in
+   an instruction.  */
+struct mips_print_arg_state {
+  /* The value of the last OP_INT seen.  We only use this for OP_MSB,
+     where the value is known to be unsigned and small.  */
+  unsigned int last_int;
+
+  /* The type and number of the last OP_REG seen.  We only use this for
+     OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG.  */
+  enum mips_reg_operand_type last_reg_type;
+  unsigned int last_regno;
+  unsigned int dest_regno;
+  unsigned int seen_dest;
+};
 
-       case '4':
-         infprintf (is, "0x%x", GET_OP (l, SA4));
-         break;
+/* Initialize STATE for the start of an instruction.  */
 
-       case '5':
-         infprintf (is, "0x%x", GET_OP (l, IMM8));
-         break;
+static inline void
+init_print_arg_state (struct mips_print_arg_state *state)
+{
+  memset (state, 0, sizeof (*state));
+}
 
-       case '6':
-         infprintf (is, "0x%x", GET_OP (l, RS));
-         break;
+/* Print OP_VU0_SUFFIX or OP_VU0_MATCH_SUFFIX operand OPERAND,
+   whose value is given by UVAL.  */
 
-       case '7':
-         infprintf (is, "$ac%d", GET_OP (l, DSPACC));
-         break;
+static void
+print_vu0_channel (struct disassemble_info *info,
+                  const struct mips_operand *operand, unsigned int uval,
+                  enum disassembler_style style)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+
+  if (operand->size == 4)
+    infprintf (info->stream, style, "%s%s%s%s",
+                       uval & 8 ? "x" : "",
+                       uval & 4 ? "y" : "",
+                       uval & 2 ? "z" : "",
+                       uval & 1 ? "w" : "");
+  else if (operand->size == 2)
+    infprintf (info->stream, style, "%c", "xyzw"[uval]);
+  else
+    abort ();
+}
 
-       case '8':
-         infprintf (is, "0x%x", GET_OP (l, WRDSP));
-         break;
+/* Record information about a register operand.  */
 
-       case '9':
-         infprintf (is, "$ac%d", GET_OP (l, DSPACC_S));
-         break;
+static void
+mips_seen_register (struct mips_print_arg_state *state,
+                   unsigned int regno,
+                   enum mips_reg_operand_type reg_type)
+{
+  state->last_reg_type = reg_type;
+  state->last_regno = regno;
 
-       case '0': /* dsp 6-bit signed immediate in bit 20 */
-         infprintf (is, "%d", GET_OP_S (l, DSPSFT));
-         break;
+  if (!state->seen_dest)
+    {
+      state->seen_dest = 1;
+      state->dest_regno = regno;
+    }
+}
 
-       case ':': /* dsp 7-bit signed immediate in bit 19 */
-         infprintf (is, "%d", GET_OP_S (l, DSPSFT_7));
-         break;
+/* Print SAVE/RESTORE instruction operands according to the argument
+   register mask AMASK, the number of static registers saved NSREG,
+   the $ra, $s0 and $s1 register specifiers RA, S0 and S1 respectively,
+   and the frame size FRAME_SIZE.  */
 
-       case '~':
-         infprintf (is, "%d", GET_OP_S (l, OFFSET12));
-         break;
+static void
+mips_print_save_restore (struct disassemble_info *info, unsigned int amask,
+                        unsigned int nsreg, unsigned int ra,
+                        unsigned int s0, unsigned int s1,
+                        unsigned int frame_size)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  unsigned int nargs, nstatics, smask, i, j;
+  void *is = info->stream;
+  const char *sep;
 
-       case '\\':
-         infprintf (is, "0x%x", GET_OP (l, 3BITPOS));
-         break;
+  if (amask == MIPS_SVRS_ALL_ARGS)
+    {
+      nargs = 4;
+      nstatics = 0;
+    }
+  else if (amask == MIPS_SVRS_ALL_STATICS)
+    {
+      nargs = 0;
+      nstatics = 4;
+    }
+  else
+    {
+      nargs = amask >> 2;
+      nstatics = amask & 3;
+    }
 
-       case '\'':
-         infprintf (is, "0x%x", GET_OP (l, RDDSP));
-         break;
+  sep = "";
+  if (nargs > 0)
+    {
+      infprintf (is, dis_style_register, "%s", mips_gpr_names[4]);
+      if (nargs > 1)
+       infprintf (is, dis_style_register, "-%s", mips_gpr_names[4 + nargs - 1]);
+      sep = ",";
+    }
 
-       case '@': /* dsp 10-bit signed immediate in bit 16 */
-         infprintf (is, "%d", GET_OP_S (l, IMM10));
-         break;
+  infprintf (is, dis_style_text, "%s", sep);
+  infprintf (is, dis_style_immediate, "%d", frame_size);
 
-       case '!':
-         infprintf (is, "%d", GET_OP (l, MT_U));
-         break;
+  if (ra)                      /* $ra */
+    {
+      infprintf (is, dis_style_text, ",");
+      infprintf (is, dis_style_register, "%s", mips_gpr_names[31]);
+    }
 
-       case '$':
-         infprintf (is, "%d", GET_OP (l, MT_H));
-         break;
+  smask = 0;
+  if (s0)                      /* $s0 */
+    smask |= 1 << 0;
+  if (s1)                      /* $s1 */
+    smask |= 1 << 1;
+  if (nsreg > 0)               /* $s2-$s8 */
+    smask |= ((1 << nsreg) - 1) << 2;
 
-       case '*':
-         infprintf (is, "$ac%d", GET_OP (l, MTACC_T));
-         break;
+  for (i = 0; i < 9; i++)
+    if (smask & (1 << i))
+      {
+       infprintf (is, dis_style_text, ",");
+       infprintf (is, dis_style_register, "%s",
+                  mips_gpr_names[i == 8 ? 30 : (16 + i)]);
+       /* Skip over string of set bits.  */
+       for (j = i; smask & (2 << j); j++)
+         continue;
+       if (j > i)
+         {
+           infprintf (is, dis_style_text, "-");
+           infprintf (is, dis_style_register, "%s",
+                      mips_gpr_names[j == 8 ? 30 : (16 + j)]);
+         }
+       i = j + 1;
+      }
+  /* Statics $ax - $a3.  */
+  if (nstatics == 1)
+    {
+      infprintf (is, dis_style_text, ",");
+      infprintf (is, dis_style_register, "%s", mips_gpr_names[7]);
+    }
+  else if (nstatics > 0)
+    {
+      infprintf (is, dis_style_text, ",");
+      infprintf (is, dis_style_register, "%s",
+                mips_gpr_names[7 - nstatics + 1]);
+      infprintf (is, dis_style_text, "-");
+      infprintf (is, dis_style_register, "%s", mips_gpr_names[7]);
+    }
+}
 
-       case '&':
-         infprintf (is, "$ac%d", GET_OP (l, MTACC_D));
-         break;
 
-       case 'g':
-         /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2.  */
-         infprintf (is, "$%d", GET_OP (l, RD));
-         break;
+/* Print operand OPERAND of OPCODE, using STATE to track inter-operand state.
+   UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is
+   the base address for OP_PCREL operands.  */
 
-       case 's':
-       case 'b':
-       case 'r':
-       case 'v':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]);
-         break;
+static void
+print_insn_arg (struct disassemble_info *info,
+               struct mips_print_arg_state *state,
+               const struct mips_opcode *opcode,
+               const struct mips_operand *operand,
+               bfd_vma base_pc,
+               unsigned int uval)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  void *is = info->stream;
 
-       case 't':
-       case 'w':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]);
-         break;
+  switch (operand->type)
+    {
+    case OP_INT:
+      {
+       const struct mips_int_operand *int_op;
 
-       case 'i':
-       case 'u':
-         infprintf (is, "0x%x", GET_OP (l, IMMEDIATE));
-         break;
+       int_op = (const struct mips_int_operand *) operand;
+       uval = mips_decode_int_operand (int_op, uval);
+       state->last_int = uval;
+       if (int_op->print_hex)
+         infprintf (is, dis_style_immediate, "0x%x", uval);
+       else
+         infprintf (is, dis_style_immediate, "%d", uval);
+      }
+      break;
 
-       case 'j': /* Same as i, but sign-extended.  */
-       case 'o':
-         infprintf (is, "%d", GET_OP_S (l, DELTA));
-         break;
+    case OP_MAPPED_INT:
+      {
+       const struct mips_mapped_int_operand *mint_op;
 
-       case 'h':
-         infprintf (is, "0x%x", GET_OP (l, PREFX));
-         break;
+       mint_op = (const struct mips_mapped_int_operand *) operand;
+       uval = mint_op->int_map[uval];
+       state->last_int = uval;
+       if (mint_op->print_hex)
+         infprintf (is, dis_style_immediate, "0x%x", uval);
+       else
+         infprintf (is, dis_style_immediate, "%d", uval);
+      }
+      break;
 
-       case 'k':
-         infprintf (is, "0x%x", GET_OP (l, CACHE));
-         break;
+    case OP_MSB:
+      {
+       const struct mips_msb_operand *msb_op;
 
-       case 'a':
-         info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
-                         | (GET_OP (l, TARGET) << 2));
-         /* For gdb disassembler, force odd address on jalx.  */
-         if (info->flavour == bfd_target_unknown_flavour
-             && strcmp (opp->name, "jalx") == 0)
-           info->target |= 1;
-         (*info->print_address_func) (info->target, info);
-         break;
+       msb_op = (const struct mips_msb_operand *) operand;
+       uval += msb_op->bias;
+       if (msb_op->add_lsb)
+         uval -= state->last_int;
+       infprintf (is, dis_style_immediate, "0x%x", uval);
+      }
+      break;
 
-       case 'p':
-         /* Sign extend the displacement.  */
-         info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN;
-         (*info->print_address_func) (info->target, info);
-         break;
+    case OP_REG:
+    case OP_OPTIONAL_REG:
+      {
+       const struct mips_reg_operand *reg_op;
 
-       case 'd':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]);
-         break;
+       reg_op = (const struct mips_reg_operand *) operand;
+       uval = mips_decode_reg_operand (reg_op, uval);
+       print_reg (info, opcode, reg_op->reg_type, uval);
+
+       mips_seen_register (state, uval, reg_op->reg_type);
+      }
+      break;
+
+    case OP_REG_PAIR:
+      {
+       const struct mips_reg_pair_operand *pair_op;
+
+       pair_op = (const struct mips_reg_pair_operand *) operand;
+       print_reg (info, opcode, pair_op->reg_type,
+                  pair_op->reg1_map[uval]);
+       infprintf (is, dis_style_text, ",");
+       print_reg (info, opcode, pair_op->reg_type,
+                  pair_op->reg2_map[uval]);
+      }
+      break;
+
+    case OP_PCREL:
+      {
+       const struct mips_pcrel_operand *pcrel_op;
+
+       pcrel_op = (const struct mips_pcrel_operand *) operand;
+       info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval);
+
+       /* For jumps and branches clear the ISA bit except for
+          the GDB disassembler.  */
+       if (pcrel_op->include_isa_bit
+           && info->flavour != bfd_target_unknown_flavour)
+         info->target &= -2;
+
+       (*info->print_address_func) (info->target, info);
+      }
+      break;
+
+    case OP_PERF_REG:
+      infprintf (is, dis_style_register, "%d", uval);
+      break;
+
+    case OP_ADDIUSP_INT:
+      {
+       int sval;
+
+       sval = mips_signed_operand (operand, uval) * 4;
+       if (sval >= -8 && sval < 8)
+         sval ^= 0x400;
+       infprintf (is, dis_style_immediate, "%d", sval);
+       break;
+      }
+
+    case OP_CLO_CLZ_DEST:
+      {
+       unsigned int reg1, reg2;
+
+       reg1 = uval & 31;
+       reg2 = uval >> 5;
+       /* If one is zero use the other.  */
+       if (reg1 == reg2 || reg2 == 0)
+         infprintf (is, dis_style_register, "%s", mips_gpr_names[reg1]);
+       else if (reg1 == 0)
+         infprintf (is, dis_style_register, "%s", mips_gpr_names[reg2]);
+       else
+         {
+           /* Bogus, result depends on processor.  */
+           infprintf (is, dis_style_register, "%s", mips_gpr_names[reg1]);
+           infprintf (is, dis_style_text, " or ");
+           infprintf (is, dis_style_register, "%s", mips_gpr_names[reg2]);
+         }
+      }
+      break;
+
+    case OP_SAME_RS_RT:
+    case OP_CHECK_PREV:
+    case OP_NON_ZERO_REG:
+      {
+       print_reg (info, opcode, OP_REG_GP, uval & 31);
+       mips_seen_register (state, uval, OP_REG_GP);
+      }
+      break;
+
+    case OP_LWM_SWM_LIST:
+      if (operand->size == 2)
+       {
+         if (uval == 0)
+           {
+             infprintf (is, dis_style_register, "%s",
+                        mips_gpr_names[16]);
+             infprintf (is, dis_style_text, ",");
+             infprintf (is, dis_style_register, "%s",
+                        mips_gpr_names[31]);
+           }
+         else
+           {
+             infprintf (is, dis_style_register, "%s",
+                        mips_gpr_names[16]);
+             infprintf (is, dis_style_text, "-");
+             infprintf (is, dis_style_register, "%s",
+                        mips_gpr_names[16 + uval]);
+             infprintf (is, dis_style_text, ",");
+             infprintf (is, dis_style_register, "%s",
+                        mips_gpr_names[31]);
+           }
+       }
+      else
+       {
+         int s_reg_encode;
+
+         s_reg_encode = uval & 0xf;
+         if (s_reg_encode != 0)
+           {
+             if (s_reg_encode == 1)
+               infprintf (is, dis_style_register, "%s", mips_gpr_names[16]);
+             else if (s_reg_encode < 9)
+               {
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[16]);
+                 infprintf (is, dis_style_text, "-");
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[15 + s_reg_encode]);
+               }
+             else if (s_reg_encode == 9)
+               {
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[16]);
+                 infprintf (is, dis_style_text, "-");
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[23]);
+                 infprintf (is, dis_style_text, ",");
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[30]);
+               }
+             else
+               infprintf (is, dis_style_text, "UNKNOWN");
+           }
+
+         if (uval & 0x10) /* For ra.  */
+           {
+             if (s_reg_encode == 0)
+               infprintf (is, dis_style_register, "%s", mips_gpr_names[31]);
+             else
+               {
+                 infprintf (is, dis_style_text, ",");
+                 infprintf (is, dis_style_register, "%s",
+                            mips_gpr_names[31]);
+               }
+           }
+       }
+      break;
+
+    case OP_ENTRY_EXIT_LIST:
+      {
+       const char *sep;
+       unsigned int amask, smask;
 
-       case 'U':
+       sep = "";
+       amask = (uval >> 3) & 7;
+       if (amask > 0 && amask < 5)
          {
-           /* First check for both rd and rt being equal.  */
-           unsigned int reg = GET_OP (l, RD);
-           if (reg == GET_OP (l, RT))
-             infprintf (is, "%s", mips_gpr_names[reg]);
-           else
+           infprintf (is, dis_style_register, "%s", mips_gpr_names[4]);
+           if (amask > 1)
              {
-               /* If one is zero use the other.  */
-               if (reg == 0)
-                 infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]);
-               else if (GET_OP (l, RT) == 0)
-                 infprintf (is, "%s", mips_gpr_names[reg]);
-               else /* Bogus, result depends on processor.  */
-                 infprintf (is, "%s or %s",
-                            mips_gpr_names[reg],
-                            mips_gpr_names[GET_OP (l, RT)]);
+               infprintf (is, dis_style_text, "-");
+               infprintf (is, dis_style_register, "%s",
+                          mips_gpr_names[amask + 3]);
              }
+           sep = ",";
          }
-         break;
 
-       case 'z':
-         infprintf (is, "%s", mips_gpr_names[0]);
-         break;
+       smask = (uval >> 1) & 3;
+       if (smask == 3)
+         {
+           infprintf (is, dis_style_text, "%s??", sep);
+           sep = ",";
+         }
+       else if (smask > 0)
+         {
+           infprintf (is, dis_style_text, "%s", sep);
+           infprintf (is, dis_style_register, "%s", mips_gpr_names[16]);
+           if (smask > 1)
+             {
+               infprintf (is, dis_style_text, "-");
+               infprintf (is, dis_style_register, "%s",
+                          mips_gpr_names[smask + 15]);
+             }
+           sep = ",";
+         }
 
-       case '<':
-       case '1':
-         infprintf (is, "0x%x", GET_OP (l, SHAMT));
-         break;
+       if (uval & 1)
+         {
+           infprintf (is, dis_style_text, "%s", sep);
+           infprintf (is, dis_style_register, "%s", mips_gpr_names[31]);
+           sep = ",";
+         }
 
-       case 'c':
-         infprintf (is, "0x%x", GET_OP (l, CODE));
-         break;
+       if (amask == 5 || amask == 6)
+         {
+           infprintf (is, dis_style_text, "%s", sep);
+           infprintf (is, dis_style_register, "%s", mips_fpr_names[0]);
+           if (amask == 6)
+             {
+               infprintf (is, dis_style_text, "-");
+               infprintf (is, dis_style_register, "%s", mips_fpr_names[1]);
+             }
+         }
+      }
+      break;
 
-       case 'q':
-         infprintf (is, "0x%x", GET_OP (l, CODE2));
-         break;
+    case OP_SAVE_RESTORE_LIST:
+      /* Should be handled by the caller due to complex behavior.  */
+      abort ();
 
-       case 'C':
-         infprintf (is, "0x%x", GET_OP (l, COPZ));
-         break;
+    case OP_MDMX_IMM_REG:
+      {
+       unsigned int vsel;
 
-       case 'B':
-         infprintf (is, "0x%x", GET_OP (l, CODE20));
-         break;
+       vsel = uval >> 5;
+       uval &= 31;
+       if ((vsel & 0x10) == 0)
+         {
+           int fmt;
 
-       case 'J':
-         infprintf (is, "0x%x", GET_OP (l, CODE19));
-         break;
+           vsel &= 0x0f;
+           for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
+             if ((vsel & 1) == 0)
+               break;
+           print_reg (info, opcode, OP_REG_VEC, uval);
+           infprintf (is, dis_style_text, "[");
+           infprintf (is, dis_style_immediate, "%d", vsel >> 1);
+           infprintf (is, dis_style_text, "]");
+         }
+       else if ((vsel & 0x08) == 0)
+         print_reg (info, opcode, OP_REG_VEC, uval);
+       else
+         infprintf (is, dis_style_immediate, "0x%x", uval);
+      }
+      break;
 
-       case 'S':
-       case 'V':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]);
-         break;
+    case OP_REPEAT_PREV_REG:
+      print_reg (info, opcode, state->last_reg_type, state->last_regno);
+      break;
 
-       case 'T':
-       case 'W':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]);
-         break;
+    case OP_REPEAT_DEST_REG:
+      print_reg (info, opcode, state->last_reg_type, state->dest_regno);
+      break;
 
-       case 'D':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]);
-         break;
+    case OP_PC:
+      infprintf (is, dis_style_register, "$pc");
+      break;
 
-       case 'R':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]);
-         break;
+    case OP_REG28:
+      print_reg (info, opcode, OP_REG_GP, 28);
+      break;
 
-       case 'E':
-         /* Coprocessor register for lwcN instructions, et al.
+    case OP_VU0_SUFFIX:
+    case OP_VU0_MATCH_SUFFIX:
+      print_vu0_channel (info, operand, uval, dis_style_register);
+      break;
 
-            Note that there is no load/store cp0 instructions, and
-            that FPU (cp1) instructions disassemble this field using
-            'T' format.  Therefore, until we gain understanding of
-            cp2 register names, we can simply print the register
-            numbers.  */
-         infprintf (is, "$%d", GET_OP (l, RT));
-         break;
+    case OP_IMM_INDEX:
+      infprintf (is, dis_style_text, "[");
+      infprintf (is, dis_style_immediate, "%d", uval);
+      infprintf (is, dis_style_text, "]");
+      break;
 
-       case 'G':
-         /* Coprocessor register for mtcN instructions, et al.  Note
-            that FPU (cp1) instructions disassemble this field using
-            'S' format.  Therefore, we only need to worry about cp0,
-            cp2, and cp3.  */
-         op = GET_OP (l, OP);
-         if (op == OP_OP_COP0)
-           infprintf (is, "%s", mips_cp0_names[GET_OP (l, RD)]);
-         else
-           infprintf (is, "$%d", GET_OP (l, RD));
-         break;
+    case OP_REG_INDEX:
+      infprintf (is, dis_style_text, "[");
+      print_reg (info, opcode, OP_REG_GP, uval);
+      infprintf (is, dis_style_text, "]");
+      break;
+    }
+}
 
-       case 'K':
-         infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]);
-         break;
+/* Validate the arguments for INSN, which is described by OPCODE.
+   Use DECODE_OPERAND to get the encoding of each operand.  */
 
-       case 'N':
-         infprintf (is,
-                    (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d",
-                    GET_OP (l, BCC));
-         break;
+static bool
+validate_insn_args (const struct mips_opcode *opcode,
+                   const struct mips_operand *(*decode_operand) (const char *),
+                   unsigned int insn)
+{
+  struct mips_print_arg_state state;
+  const struct mips_operand *operand;
+  const char *s;
+  unsigned int uval;
 
-       case 'M':
-         infprintf (is, "$fcc%d", GET_OP (l, CCC));
+  init_print_arg_state (&state);
+  for (s = opcode->args; *s; ++s)
+    {
+      switch (*s)
+       {
+       case ',':
+       case '(':
+       case ')':
          break;
 
-       case 'P':
-         infprintf (is, "%d", GET_OP (l, PERFREG));
+       case '#':
+         ++s;
          break;
 
-       case 'e':
-         infprintf (is, "%d", GET_OP (l, VECBYTE));
-         break;
+       default:
+         operand = decode_operand (s);
 
-       case '%':
-         infprintf (is, "%d", GET_OP (l, VECALIGN));
-         break;
+         if (operand)
+           {
+             uval = mips_extract_operand (operand, insn);
+             switch (operand->type)
+               {
+               case OP_REG:
+               case OP_OPTIONAL_REG:
+                 {
+                   const struct mips_reg_operand *reg_op;
 
-       case 'H':
-         infprintf (is, "%d", GET_OP (l, SEL));
-         break;
+                   reg_op = (const struct mips_reg_operand *) operand;
+                   uval = mips_decode_reg_operand (reg_op, uval);
+                   mips_seen_register (&state, uval, reg_op->reg_type);
+                 }
+               break;
 
-       case 'O':
-         infprintf (is, "%d", GET_OP (l, ALN));
-         break;
+               case OP_SAME_RS_RT:
+                 {
+                   unsigned int reg1, reg2;
 
-       case 'Q':
-         {
-           unsigned int vsel = GET_OP (l, VSEL);
+                   reg1 = uval & 31;
+                   reg2 = uval >> 5;
 
-           if ((vsel & 0x10) == 0)
-             {
-               int fmt;
+                   if (reg1 != reg2 || reg1 == 0)
+                     return false;
+                 }
+               break;
 
-               vsel &= 0x0f;
-               for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
-                 if ((vsel & 1) == 0)
-                   break;
-               infprintf (is, "$v%d[%d]", GET_OP (l, FT), vsel >> 1);
-             }
-           else if ((vsel & 0x08) == 0)
-             {
-               infprintf (is, "$v%d", GET_OP (l, FT));
-             }
-           else
-             {
-               infprintf (is, "0x%x", GET_OP (l, FT));
-             }
-         }
-         break;
+               case OP_CHECK_PREV:
+                 {
+                   const struct mips_check_prev_operand *prev_op;
 
-       case 'X':
-         infprintf (is, "$v%d", GET_OP (l, FD));
-         break;
+                   prev_op = (const struct mips_check_prev_operand *) operand;
 
-       case 'Y':
-         infprintf (is, "$v%d", GET_OP (l, FS));
-         break;
+                   if (!prev_op->zero_ok && uval == 0)
+                     return false;
 
-       case 'Z':
-         infprintf (is, "$v%d", GET_OP (l, FT));
-         break;
+                   if (((prev_op->less_than_ok && uval < state.last_regno)
+                       || (prev_op->greater_than_ok && uval > state.last_regno)
+                       || (prev_op->equal_ok && uval == state.last_regno)))
+                     break;
 
-       default:
-         /* xgettext:c-format */
-         infprintf (is, _("# internal error, undefined modifier (%c)"), *d);
-         return;
+                   return false;
+                 }
+
+               case OP_NON_ZERO_REG:
+                 {
+                   if (uval == 0)
+                     return false;
+                 }
+               break;
+
+               case OP_INT:
+               case OP_MAPPED_INT:
+               case OP_MSB:
+               case OP_REG_PAIR:
+               case OP_PCREL:
+               case OP_PERF_REG:
+               case OP_ADDIUSP_INT:
+               case OP_CLO_CLZ_DEST:
+               case OP_LWM_SWM_LIST:
+               case OP_ENTRY_EXIT_LIST:
+               case OP_MDMX_IMM_REG:
+               case OP_REPEAT_PREV_REG:
+               case OP_REPEAT_DEST_REG:
+               case OP_PC:
+               case OP_REG28:
+               case OP_VU0_SUFFIX:
+               case OP_VU0_MATCH_SUFFIX:
+               case OP_IMM_INDEX:
+               case OP_REG_INDEX:
+               case OP_SAVE_RESTORE_LIST:
+                 break;
+               }
+           }
+         if (*s == 'm' || *s == '+' || *s == '-')
+           ++s;
        }
     }
+  return true;
 }
-\f
-/* Print the mips instruction at address MEMADDR in debugged memory,
-   on using INFO.  Returns length of the instruction, in bytes, which is
-   always INSNLEN.  BIGENDIAN must be 1 if this is big-endian code, 0 if
-   this is little-endian code.  */
 
-static int
-print_insn_mips (bfd_vma memaddr,
-                int word,
-                struct disassemble_info *info)
+/* Print the arguments for INSN, which is described by OPCODE.
+   Use DECODE_OPERAND to get the encoding of each operand.  Use BASE_PC
+   as the base of OP_PCREL operands, adjusting by LENGTH if the OP_PCREL
+   operand is for a branch or jump.  */
+
+static void
+print_insn_args (struct disassemble_info *info,
+                const struct mips_opcode *opcode,
+                const struct mips_operand *(*decode_operand) (const char *),
+                unsigned int insn, bfd_vma insn_pc, unsigned int length)
 {
-  static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
-  const fprintf_ftype infprintf = info->fprintf_func;
-  const struct mips_opcode *op;
-  static bfd_boolean init = 0;
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
   void *is = info->stream;
+  struct mips_print_arg_state state;
+  const struct mips_operand *operand;
+  const char *s;
 
-  /* Build a hash table to shorten the search time.  */
-  if (! init)
+  init_print_arg_state (&state);
+  for (s = opcode->args; *s; ++s)
+    {
+      switch (*s)
+       {
+       case ',':
+       case '(':
+       case ')':
+         infprintf (is, dis_style_text, "%c", *s);
+         break;
+
+       case '#':
+         ++s;
+         infprintf (is, dis_style_text, "%c%c", *s, *s);
+         break;
+
+       default:
+         operand = decode_operand (s);
+         if (!operand)
+           {
+             /* xgettext:c-format */
+             infprintf (is, dis_style_text,
+                        _("# internal error, undefined operand in `%s %s'"),
+                        opcode->name, opcode->args);
+             return;
+           }
+
+         if (operand->type == OP_SAVE_RESTORE_LIST)
+           {
+             /* Handle this case here because of the complex behavior.  */
+             unsigned int amask = (insn >> 15) & 0xf;
+             unsigned int nsreg = (insn >> 23) & 0x7;
+             unsigned int ra = insn & 0x1000;                  /* $ra */
+             unsigned int s0 = insn & 0x800;                   /* $s0 */
+             unsigned int s1 = insn & 0x400;                   /* $s1 */
+             unsigned int frame_size = (((insn >> 15) & 0xf0)
+                                        | ((insn >> 6) & 0x0f)) * 8;
+             mips_print_save_restore (info, amask, nsreg, ra, s0, s1,
+                                      frame_size);
+           }
+         else if (operand->type == OP_REG
+                  && s[1] == ','
+                  && s[2] == 'H'
+                  && opcode->name[strlen (opcode->name) - 1] == '0')
+           {
+             /* Coprocessor register 0 with sel field.  */
+             const struct mips_cp0sel_name *n;
+             unsigned int reg, sel;
+
+             reg = mips_extract_operand (operand, insn);
+             s += 2;
+             operand = decode_operand (s);
+             sel = mips_extract_operand (operand, insn);
+
+             /* CP0 register including 'sel' code for mftc0, to be
+                printed textually if known.  If not known, print both
+                CP0 register name and sel numerically since CP0 register
+                with sel 0 may have a name unrelated to register being
+                printed.  */
+             n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+                                          mips_cp0sel_names_len,
+                                          reg, sel);
+             if (n != NULL)
+               infprintf (is, dis_style_register, "%s", n->name);
+             else
+               {
+                 infprintf (is, dis_style_register, "$%d", reg);
+                 infprintf (is, dis_style_text, ",");
+                 infprintf (is, dis_style_immediate, "%d", sel);
+               }
+           }
+         else
+           {
+             bfd_vma base_pc = insn_pc;
+
+             /* Adjust the PC relative base so that branch/jump insns use
+                the following PC as the base but genuinely PC relative
+                operands use the current PC.  */
+             if (operand->type == OP_PCREL)
+               {
+                 const struct mips_pcrel_operand *pcrel_op;
+
+                 pcrel_op = (const struct mips_pcrel_operand *) operand;
+                 /* The include_isa_bit flag is sufficient to distinguish
+                    branch/jump from other PC relative operands.  */
+                 if (pcrel_op->include_isa_bit)
+                   base_pc += length;
+               }
+
+             print_insn_arg (info, &state, opcode, operand, base_pc,
+                             mips_extract_operand (operand, insn));
+           }
+         if (*s == 'm' || *s == '+' || *s == '-')
+           ++s;
+         break;
+       }
+    }
+}
+\f
+/* Print the mips instruction at address MEMADDR in debugged memory,
+   on using INFO.  Returns length of the instruction, in bytes, which is
+   always INSNLEN.  BIGENDIAN must be 1 if this is big-endian code, 0 if
+   this is little-endian code.  */
+
+static int
+print_insn_mips (bfd_vma memaddr,
+                int word,
+                struct disassemble_info *info)
+{
+#define GET_OP(insn, field)                    \
+  (((insn) >> OP_SH_##field) & OP_MASK_##field)
+  static const struct mips_opcode *mips_hash[OP_MASK_OP + 1];
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  const struct mips_opcode *op;
+  static bool init = 0;
+  void *is = info->stream;
+
+  /* Build a hash table to shorten the search time.  */
+  if (! init)
     {
       unsigned int i;
 
@@ -1493,22 +2036,21 @@ print_insn_mips (bfd_vma memaddr,
     {
       for (; op < &mips_opcodes[NUMOPCODES]; op++)
        {
-         if (op->pinfo != INSN_MACRO 
+         if (op->pinfo != INSN_MACRO
              && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
              && (word & op->mask) == op->match)
            {
-             const char *d;
-
-             /* We always allow to disassemble the jalx instruction.  */
-             if (!opcode_is_member (op, mips_isa, mips_processor)
-                 && strcmp (op->name, "jalx"))
+             /* We always disassemble the jalx instruction, except for MIPS r6.  */
+             if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor)
+                && (strcmp (op->name, "jalx")
+                    || (mips_isa & INSN_ISA_MASK) == ISA_MIPS32R6
+                    || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6))
                continue;
 
              /* Figure out instruction type and branch delay information.  */
              if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
                {
-                 if ((op->pinfo & (INSN_WRITE_GPR_31
-                                   | INSN_WRITE_GPR_D)) != 0)
+                 if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_1)) != 0)
                    info->insn_type = dis_jsr;
                  else
                    info->insn_type = dis_branch;
@@ -1524,1362 +2066,513 @@ print_insn_mips (bfd_vma memaddr,
                  info->branch_delay_insns = 1;
                }
              else if ((op->pinfo & (INSN_STORE_MEMORY
-                                    | INSN_LOAD_MEMORY_DELAY)) != 0)
+                                    | INSN_LOAD_MEMORY)) != 0)
                info->insn_type = dis_dref;
 
-             infprintf (is, "%s", op->name);
+             if (!validate_insn_args (op, decode_mips_operand, word))
+               continue;
 
-             d = op->args;
-             if (d != NULL && *d != '\0')
+             infprintf (is, dis_style_mnemonic, "%s", op->name);
+             if (op->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
                {
-                 infprintf (is, "\t");
-                 print_insn_args (d, word, memaddr, info, op);
+                 unsigned int uval;
+
+                 infprintf (is, dis_style_mnemonic, ".");
+                 uval = mips_extract_operand (&mips_vu0_channel_mask, word);
+                 print_vu0_channel (info, &mips_vu0_channel_mask, uval,
+                                    dis_style_mnemonic);
+               }
+
+             if (op->args[0])
+               {
+                 infprintf (is, dis_style_text, "\t");
+                 print_insn_args (info, op, decode_mips_operand, word,
+                                  memaddr, 4);
                }
 
              return INSNLEN;
            }
        }
     }
-#undef GET_OP_S
 #undef GET_OP
 
   /* Handle undefined instructions.  */
   info->insn_type = dis_noninsn;
-  infprintf (is, "0x%x", word);
+  infprintf (is, dis_style_assembler_directive, ".word");
+  infprintf (is, dis_style_text, "\t");
+  infprintf (is, dis_style_immediate, "0x%x", word);
   return INSNLEN;
 }
 \f
 /* Disassemble an operand for a mips16 instruction.  */
 
 static void
-print_mips16_insn_arg (char type,
-                      const struct mips_opcode *op,
-                      int l,
-                      bfd_boolean use_extend,
-                      int extend,
-                      bfd_vma memaddr,
-                      struct disassemble_info *info)
-{
-  const fprintf_ftype infprintf = info->fprintf_func;
+print_mips16_insn_arg (struct disassemble_info *info,
+                      struct mips_print_arg_state *state,
+                      const struct mips_opcode *opcode,
+                      char type, bfd_vma memaddr,
+                      unsigned insn, bool use_extend,
+                      unsigned extend, bool is_offset)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
   void *is = info->stream;
+  const struct mips_operand *operand, *ext_operand;
+  unsigned short ext_size;
+  unsigned int uval;
+  bfd_vma baseaddr;
+
+  if (!use_extend)
+    extend = 0;
 
-#define GET_OP(insn, field) \
-  (((insn) >> MIPS16OP_SH_##field) & MIPS16OP_MASK_##field)
-#define GET_OP_S(insn, field) \
-  ((GET_OP (insn, field) ^ ((MIPS16OP_MASK_##field >> 1) + 1)) \
-   - ((MIPS16OP_MASK_##field >> 1) + 1))
   switch (type)
     {
     case ',':
     case '(':
     case ')':
-      infprintf (is, "%c", type);
-      break;
-
-    case 'y':
-    case 'w':
-      infprintf (is, "%s", mips16_reg_names (GET_OP (l, RY)));
-      break;
-
-    case 'x':
-    case 'v':
-      infprintf (is, "%s", mips16_reg_names (GET_OP (l, RX)));
-      break;
-
-    case 'z':
-      infprintf (is, "%s", mips16_reg_names (GET_OP (l, RZ)));
-      break;
-
-    case 'Z':
-      infprintf (is, "%s", mips16_reg_names (GET_OP (l, MOVE32Z)));
-      break;
-
-    case '0':
-      infprintf (is, "%s", mips_gpr_names[0]);
-      break;
-
-    case 'S':
-      infprintf (is, "%s", mips_gpr_names[29]);
-      break;
-
-    case 'P':
-      infprintf (is, "$pc");
-      break;
-
-    case 'R':
-      infprintf (is, "%s", mips_gpr_names[31]);
-      break;
-
-    case 'X':
-      infprintf (is, "%s", mips_gpr_names[GET_OP (l, REGR32)]);
-      break;
-
-    case 'Y':
-      infprintf (is, "%s", mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
-      break;
-
-    case '<':
-    case '>':
-    case '[':
-    case ']':
-    case '4':
-    case '5':
-    case 'H':
-    case 'W':
-    case 'D':
-    case 'j':
-    case '6':
-    case '8':
-    case 'V':
-    case 'C':
-    case 'U':
-    case 'k':
-    case 'K':
-    case 'p':
-    case 'q':
-    case 'A':
-    case 'B':
-    case 'E':
-      {
-       int immed, nbits, shift, signedp, extbits, pcrel, extu, branch;
-
-       shift = 0;
-       signedp = 0;
-       extbits = 16;
-       pcrel = 0;
-       extu = 0;
-       branch = 0;
-       switch (type)
-         {
-         case '<':
-           nbits = 3;
-           immed = GET_OP (l, RZ);
-           extbits = 5;
-           extu = 1;
-           break;
-         case '>':
-           nbits = 3;
-           immed = GET_OP (l, RX);
-           extbits = 5;
-           extu = 1;
-           break;
-         case '[':
-           nbits = 3;
-           immed = GET_OP (l, RZ);
-           extbits = 6;
-           extu = 1;
-           break;
-         case ']':
-           nbits = 3;
-           immed = GET_OP (l, RX);
-           extbits = 6;
-           extu = 1;
-           break;
-         case '4':
-           nbits = 4;
-           immed = GET_OP (l, IMM4);
-           signedp = 1;
-           extbits = 15;
-           break;
-         case '5':
-           nbits = 5;
-           immed = GET_OP (l, IMM5);
-           info->insn_type = dis_dref;
-           info->data_size = 1;
-           break;
-         case 'H':
-           nbits = 5;
-           shift = 1;
-           immed = GET_OP (l, IMM5);
-           info->insn_type = dis_dref;
-           info->data_size = 2;
-           break;
-         case 'W':
-           nbits = 5;
-           shift = 2;
-           immed = GET_OP (l, IMM5);
-           if ((op->pinfo & MIPS16_INSN_READ_PC) == 0
-               && (op->pinfo & MIPS16_INSN_READ_SP) == 0)
-             {
-               info->insn_type = dis_dref;
-               info->data_size = 4;
-             }
-           break;
-         case 'D':
-           nbits = 5;
-           shift = 3;
-           immed = GET_OP (l, IMM5);
-           info->insn_type = dis_dref;
-           info->data_size = 8;
-           break;
-         case 'j':
-           nbits = 5;
-           immed = GET_OP (l, IMM5);
-           signedp = 1;
-           break;
-         case '6':
-           nbits = 6;
-           immed = GET_OP (l, IMM6);
-           break;
-         case '8':
-           nbits = 8;
-           immed = GET_OP (l, IMM8);
-           break;
-         case 'V':
-           nbits = 8;
-           shift = 2;
-           immed = GET_OP (l, IMM8);
-           /* FIXME: This might be lw, or it might be addiu to $sp or
-               $pc.  We assume it's load.  */
-           info->insn_type = dis_dref;
-           info->data_size = 4;
-           break;
-         case 'C':
-           nbits = 8;
-           shift = 3;
-           immed = GET_OP (l, IMM8);
-           info->insn_type = dis_dref;
-           info->data_size = 8;
-           break;
-         case 'U':
-           nbits = 8;
-           immed = GET_OP (l, IMM8);
-           extu = 1;
-           break;
-         case 'k':
-           nbits = 8;
-           immed = GET_OP (l, IMM8);
-           signedp = 1;
-           break;
-         case 'K':
-           nbits = 8;
-           shift = 3;
-           immed = GET_OP (l, IMM8);
-           signedp = 1;
-           break;
-         case 'p':
-           nbits = 8;
-           immed = GET_OP (l, IMM8);
-           signedp = 1;
-           pcrel = 1;
-           branch = 1;
-           break;
-         case 'q':
-           nbits = 11;
-           immed = GET_OP (l, IMM11);
-           signedp = 1;
-           pcrel = 1;
-           branch = 1;
-           break;
-         case 'A':
-           nbits = 8;
-           shift = 2;
-           immed = GET_OP (l, IMM8);
-           pcrel = 1;
-           /* FIXME: This can be lw or la.  We assume it is lw.  */
-           info->insn_type = dis_dref;
-           info->data_size = 4;
-           break;
-         case 'B':
-           nbits = 5;
-           shift = 3;
-           immed = GET_OP (l, IMM5);
-           pcrel = 1;
-           info->insn_type = dis_dref;
-           info->data_size = 8;
-           break;
-         case 'E':
-           nbits = 5;
-           shift = 2;
-           immed = GET_OP (l, IMM5);
-           pcrel = 1;
-           break;
-         default:
-           abort ();
-         }
-
-       if (! use_extend)
-         {
-           if (signedp && immed >= (1 << (nbits - 1)))
-             immed -= 1 << nbits;
-           immed <<= shift;
-           if ((type == '<' || type == '>' || type == '[' || type == ']')
-               && immed == 0)
-             immed = 8;
-         }
-       else
-         {
-           if (extbits == 16)
-             immed |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
-           else if (extbits == 15)
-             immed |= ((extend & 0xf) << 11) | (extend & 0x7f0);
-           else
-             immed = ((extend >> 6) & 0x1f) | (extend & 0x20);
-           immed &= (1 << extbits) - 1;
-           if (! extu && immed >= (1 << (extbits - 1)))
-             immed -= 1 << extbits;
-         }
-
-       if (! pcrel)
-         infprintf (is, "%d", immed);
-       else
-         {
-           bfd_vma baseaddr;
-
-           if (branch)
-             {
-               immed *= 2;
-               baseaddr = memaddr + 2;
-             }
-           else if (use_extend)
-             baseaddr = memaddr - 2;
-           else
-             {
-               int status;
-               bfd_byte buffer[2];
-
-               baseaddr = memaddr;
-
-               /* If this instruction is in the delay slot of a jr
-                   instruction, the base address is the address of the
-                   jr instruction.  If it is in the delay slot of jalr
-                   instruction, the base address is the address of the
-                   jalr instruction.  This test is unreliable: we have
-                   no way of knowing whether the previous word is
-                   instruction or data.  */
-               status = (*info->read_memory_func) (memaddr - 4, buffer, 2,
-                                                   info);
-               if (status == 0
-                   && (((info->endian == BFD_ENDIAN_BIG
-                         ? bfd_getb16 (buffer)
-                         : bfd_getl16 (buffer))
-                        & 0xf800) == 0x1800))
-                 baseaddr = memaddr - 4;
-               else
-                 {
-                   status = (*info->read_memory_func) (memaddr - 2, buffer,
-                                                       2, info);
-                   if (status == 0
-                       && (((info->endian == BFD_ENDIAN_BIG
-                             ? bfd_getb16 (buffer)
-                             : bfd_getl16 (buffer))
-                            & 0xf81f) == 0xe800))
-                     baseaddr = memaddr - 2;
-                 }
-             }
-           info->target = (baseaddr & ~((1 << shift) - 1)) + immed;
-           if (pcrel && branch
-               && info->flavour == bfd_target_unknown_flavour)
-             /* For gdb disassembler, maintain odd address.  */
-             info->target |= 1;
-           (*info->print_address_func) (info->target, info);
-         }
-      }
-      break;
-
-    case 'a':
-      {
-       int jalx = l & 0x400;
-
-       if (! use_extend)
-         extend = 0;
-       l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
-       if (!jalx && info->flavour == bfd_target_unknown_flavour)
-         /* For gdb disassembler, maintain odd address.  */
-         l |= 1;
-      }
-      info->target = ((memaddr + 4) & ~(bfd_vma) 0x0fffffff) | l;
-      (*info->print_address_func) (info->target, info);
-      break;
-
-    case 'l':
-    case 'L':
-      {
-       int need_comma, amask, smask;
-
-       need_comma = 0;
-
-       l = GET_OP (l, IMM6);
-
-       amask = (l >> 3) & 7;
-
-       if (amask > 0 && amask < 5)
-         {
-           infprintf (is, "%s", mips_gpr_names[4]);
-           if (amask > 1)
-             infprintf (is, "-%s", mips_gpr_names[amask + 3]);
-           need_comma = 1;
-         }
-
-       smask = (l >> 1) & 3;
-       if (smask == 3)
-         {
-           infprintf (is, "%s??", need_comma ? "," : "");
-           need_comma = 1;
-         }
-       else if (smask > 0)
-         {
-           infprintf (is, "%s%s", need_comma ? "," : "", mips_gpr_names[16]);
-           if (smask > 1)
-             infprintf (is, "-%s", mips_gpr_names[smask + 15]);
-           need_comma = 1;
-         }
-
-       if (l & 1)
-         {
-           infprintf (is, "%s%s", need_comma ? "," : "", mips_gpr_names[31]);
-           need_comma = 1;
-         }
-
-       if (amask == 5 || amask == 6)
-         {
-           infprintf (is, "%s$f0", need_comma ? "," : "");
-           if (amask == 6)
-             infprintf (is, "-$f1");
-         }
-      }
-      break;
-
-    case 'm':
-    case 'M':
-      /* MIPS16e save/restore.  */
-      {
-      int need_comma = 0;
-      int amask, args, statics;
-      int nsreg, smask;
-      int framesz;
-      int i, j;
-
-      l = l & 0x7f;
-      if (use_extend)
-        l |= extend << 16;
-
-      amask = (l >> 16) & 0xf;
-      if (amask == MIPS16_ALL_ARGS)
-        {
-          args = 4;
-          statics = 0;
-        }
-      else if (amask == MIPS16_ALL_STATICS)
-        {
-          args = 0;
-          statics = 4;
-        }
-      else
-        {
-          args = amask >> 2;
-          statics = amask & 3;
-        }
-
-      if (args > 0) {
-         infprintf (is, "%s", mips_gpr_names[4]);
-          if (args > 1)
-           infprintf (is, "-%s", mips_gpr_names[4 + args - 1]);
-          need_comma = 1;
-      }
-
-      framesz = (((l >> 16) & 0xf0) | (l & 0x0f)) * 8;
-      if (framesz == 0 && !use_extend)
-        framesz = 128;
-
-      infprintf (is, "%s%d", need_comma ? "," : "", framesz);
-
-      if (l & 0x40)                   /* $ra */
-       infprintf (is, ",%s", mips_gpr_names[31]);
-
-      nsreg = (l >> 24) & 0x7;
-      smask = 0;
-      if (l & 0x20)                   /* $s0 */
-        smask |= 1 << 0;
-      if (l & 0x10)                   /* $s1 */
-        smask |= 1 << 1;
-      if (nsreg > 0)                  /* $s2-$s8 */
-        smask |= ((1 << nsreg) - 1) << 2;
-
-      /* Find first set static reg bit.  */
-      for (i = 0; i < 9; i++)
-        {
-          if (smask & (1 << i))
-            {
-             infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
-              /* Skip over string of set bits.  */
-              for (j = i; smask & (2 << j); j++)
-                continue;
-              if (j > i)
-               infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
-              i = j + 1;
-            }
-        }
-
-      /* Statics $ax - $a3.  */
-      if (statics == 1)
-       infprintf (is, ",%s", mips_gpr_names[7]);
-      else if (statics > 0) 
-       infprintf (is, ",%s-%s",
-                  mips_gpr_names[7 - statics + 1],
-                  mips_gpr_names[7]);
-      }
+      infprintf (is, dis_style_text, "%c", type);
       break;
 
     default:
-      /* xgettext:c-format */
-      infprintf (is,
-                _("# internal disassembler error, "
-                  "unrecognised modifier (%c)"),
-                type);
-      abort ();
-    }
-}
-
-/* Disassemble mips16 instructions.  */
-
-static int
-print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
-{
-  const fprintf_ftype infprintf = info->fprintf_func;
-  int status;
-  bfd_byte buffer[2];
-  int length;
-  int insn;
-  bfd_boolean use_extend;
-  int extend = 0;
-  const struct mips_opcode *op, *opend;
-  void *is = info->stream;
-
-  info->bytes_per_chunk = 2;
-  info->display_endian = info->endian;
-  info->insn_info_valid = 1;
-  info->branch_delay_insns = 0;
-  info->data_size = 0;
-  info->insn_type = dis_nonbranch;
-  info->target = 0;
-  info->target2 = 0;
-
-  status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-  if (status != 0)
-    {
-      (*info->memory_error_func) (status, memaddr, info);
-      return -1;
-    }
-
-  length = 2;
-
-  if (info->endian == BFD_ENDIAN_BIG)
-    insn = bfd_getb16 (buffer);
-  else
-    insn = bfd_getl16 (buffer);
-
-  /* Handle the extend opcode specially.  */
-  use_extend = FALSE;
-  if ((insn & 0xf800) == 0xf000)
-    {
-      use_extend = TRUE;
-      extend = insn & 0x7ff;
-
-      memaddr += 2;
-
-      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-      if (status != 0)
+      operand = decode_mips16_operand (type, false);
+      if (!operand)
        {
-         infprintf (is, "extend 0x%x", (unsigned int) extend);
-         (*info->memory_error_func) (status, memaddr, info);
-         return -1;
+         /* xgettext:c-format */
+         infprintf (is, dis_style_text, _("# internal error, undefined operand in `%s %s'"),
+                    opcode->name, opcode->args);
+         return;
        }
 
-      if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
-      else
-       insn = bfd_getl16 (buffer);
-
-      /* Check for an extend opcode followed by an extend opcode.  */
-      if ((insn & 0xf800) == 0xf000)
+      if (operand->type == OP_SAVE_RESTORE_LIST)
        {
-         infprintf (is, "extend 0x%x", (unsigned int) extend);
-         info->insn_type = dis_noninsn;
-         return length;
+         /* Handle this case here because of the complex interaction
+            with the EXTEND opcode.  */
+         unsigned int amask = extend & 0xf;
+         unsigned int nsreg = (extend >> 8) & 0x7;
+         unsigned int ra = insn & 0x40;                        /* $ra */
+         unsigned int s0 = insn & 0x20;                        /* $s0 */
+         unsigned int s1 = insn & 0x10;                        /* $s1 */
+         unsigned int frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
+         if (frame_size == 0 && !use_extend)
+           frame_size = 128;
+         mips_print_save_restore (info, amask, nsreg, ra, s0, s1, frame_size);
+         break;
        }
 
-      length += 2;
-    }
+      if (is_offset && operand->type == OP_INT)
+       {
+         const struct mips_int_operand *int_op;
 
-  /* FIXME: Should probably use a hash table on the major opcode here.  */
+         int_op = (const struct mips_int_operand *) operand;
+         info->insn_type = dis_dref;
+         info->data_size = 1 << int_op->shift;
+       }
 
-  opend = mips16_opcodes + bfd_mips16_num_opcodes;
-  for (op = mips16_opcodes; op < opend; op++)
-    {
-      if (op->pinfo != INSN_MACRO
-         && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
-         && (insn & op->mask) == op->match)
+      ext_size = 0;
+      if (use_extend)
        {
-         const char *s;
-
-         if (strchr (op->args, 'a') != NULL)
+         ext_operand = decode_mips16_operand (type, true);
+         if (ext_operand != operand
+             || (operand->type == OP_INT && operand->lsb == 0
+                 && mips_opcode_32bit_p (opcode)))
            {
-             if (use_extend)
-               {
-                 infprintf (is, "extend 0x%x", (unsigned int) extend);
-                 info->insn_type = dis_noninsn;
-                 return length - 2;
-               }
-
-             use_extend = FALSE;
-
-             memaddr += 2;
-
-             status = (*info->read_memory_func) (memaddr, buffer, 2,
-                                                 info);
-             if (status == 0)
-               {
-                 use_extend = TRUE;
-                 if (info->endian == BFD_ENDIAN_BIG)
-                   extend = bfd_getb16 (buffer);
-                 else
-                   extend = bfd_getl16 (buffer);
-                 length += 2;
-               }
+             ext_size = ext_operand->size;
+             operand = ext_operand;
            }
+       }
+      if (operand->size == 26)
+       uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
+      else if (ext_size == 16 || ext_size == 9)
+       uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+      else if (ext_size == 15)
+       uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf);
+      else if (ext_size == 6)
+       uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
+      else
+       uval = mips_extract_operand (operand, (extend << 16) | insn);
+      if (ext_size == 9)
+       uval &= (1U << ext_size) - 1;
 
-         infprintf (is, "%s", op->name);
-         if (op->args[0] != '\0')
-           infprintf (is, "\t");
-
-         for (s = op->args; *s != '\0'; s++)
-           {
-             if (*s == ','
-                 && s[1] == 'w'
-                 && GET_OP (insn, RX) == GET_OP (insn, RY))
-               {
-                 /* Skip the register and the comma.  */
-                 ++s;
-                 continue;
-               }
-             if (*s == ','
-                 && s[1] == 'v'
-                 && GET_OP (insn, RZ) == GET_OP (insn, RX))
-               {
-                 /* Skip the register and the comma.  */
-                 ++s;
-                 continue;
-               }
-             print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr,
-                                    info);
-           }
+      baseaddr = memaddr + 2;
+      if (operand->type == OP_PCREL)
+       {
+         const struct mips_pcrel_operand *pcrel_op;
 
-         /* Figure out branch instruction type and delay slot information.  */
-         if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
-           info->branch_delay_insns = 1;
-         if ((op->pinfo & (INSN_UNCOND_BRANCH_DELAY
-                           | MIPS16_INSN_UNCOND_BRANCH)) != 0)
+         pcrel_op = (const struct mips_pcrel_operand *) operand;
+         if (!pcrel_op->include_isa_bit && use_extend)
+           baseaddr = memaddr - 2;
+         else if (!pcrel_op->include_isa_bit)
            {
-             if ((op->pinfo & INSN_WRITE_GPR_31) != 0)
-               info->insn_type = dis_jsr;
+             bfd_byte buffer[2];
+
+             /* If this instruction is in the delay slot of a JAL/JALX
+                instruction, the base address is the address of the
+                JAL/JALX instruction.  If it is in the delay slot of
+                a JR/JALR instruction, the base address is the address
+                of the JR/JALR instruction.  This test is unreliable:
+                we have no way of knowing whether the previous word is
+                instruction or data.  */
+             if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
+                 && (((info->endian == BFD_ENDIAN_BIG
+                       ? bfd_getb16 (buffer)
+                       : bfd_getl16 (buffer))
+                      & 0xf800) == 0x1800))
+               baseaddr = memaddr - 4;
+             else if (info->read_memory_func (memaddr - 2, buffer, 2,
+                                              info) == 0
+                      && (((info->endian == BFD_ENDIAN_BIG
+                            ? bfd_getb16 (buffer)
+                            : bfd_getl16 (buffer))
+                           & 0xf89f) == 0xe800)
+                      && (((info->endian == BFD_ENDIAN_BIG
+                            ? bfd_getb16 (buffer)
+                            : bfd_getl16 (buffer))
+                           & 0x0060) != 0x0060))
+               baseaddr = memaddr - 2;
              else
-               info->insn_type = dis_branch;
+               baseaddr = memaddr;
            }
-         else if ((op->pinfo & MIPS16_INSN_COND_BRANCH) != 0)
-           info->insn_type = dis_condbranch;
-
-         return length;
        }
-    }
-#undef GET_OP_S
-#undef GET_OP
 
-  if (use_extend)
-    infprintf (is, "0x%x", extend | 0xf000);
-  infprintf (is, "0x%x", insn);
-  info->insn_type = dis_noninsn;
-
-  return length;
+      print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
+      break;
+    }
 }
 
-/* Disassemble microMIPS instructions.  */
 
-static int
-print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
+/* Check if the given address is the last word of a MIPS16 PLT entry.
+   This word is data and depending on the value it may interfere with
+   disassembly of further PLT entries.  We make use of the fact PLT
+   symbols are marked BSF_SYNTHETIC.  */
+static bool
+is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
 {
-  const fprintf_ftype infprintf = info->fprintf_func;
-  const struct mips_opcode *op, *opend;
-  unsigned int lsb, msbd, msb;
-  void *is = info->stream;
-  unsigned int regno;
-  bfd_byte buffer[2];
-  int lastregno = 0;
-  int higher;
-  int length;
-  int status;
-  int delta;
-  int immed;
-  int insn;
-
-  lsb = 0;
-
-  info->bytes_per_chunk = 2;
-  info->display_endian = info->endian;
-  info->insn_info_valid = 1;
-  info->branch_delay_insns = 0;
-  info->data_size = 0;
-  info->insn_type = dis_nonbranch;
-  info->target = 0;
-  info->target2 = 0;
-
-  status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-  if (status != 0)
-    {
-      (*info->memory_error_func) (status, memaddr, info);
-      return -1;
-    }
-
-  length = 2;
-
-  if (info->endian == BFD_ENDIAN_BIG)
-    insn = bfd_getb16 (buffer);
-  else
-    insn = bfd_getl16 (buffer);
-
-  if ((insn & 0xfc00) == 0x7c00)
-    {
-      /* This is a 48-bit microMIPS instruction.  */
-      higher = insn;
-
-      status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "micromips 0x%x", higher);
-         (*info->memory_error_func) (status, memaddr + 2, info);
-         return -1;
-       }
-      if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
-      else
-       insn = bfd_getl16 (buffer);
-      higher = (higher << 16) | insn;
-
-      status = (*info->read_memory_func) (memaddr + 4, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "micromips 0x%x", higher);
-         (*info->memory_error_func) (status, memaddr + 4, info);
-         return -1;
-       }
-      if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
-      else
-       insn = bfd_getl16 (buffer);
-      infprintf (is, "0x%x%04x (48-bit insn)", higher, insn);
-
-      info->insn_type = dis_noninsn;
-      return 6;
-    }
-  else if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
-    {
-      /* This is a 32-bit microMIPS instruction.  */
-      higher = insn;
-
-      status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "micromips 0x%x", higher);
-         (*info->memory_error_func) (status, memaddr + 2, info);
-         return -1;
-       }
-
-      if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
-      else
-       insn = bfd_getl16 (buffer);
-
-      insn = insn | (higher << 16);
-
-      length += 2;
-    }
-
-  /* FIXME: Should probably use a hash table on the major opcode here.  */
-
-#define GET_OP(insn, field) \
-  (((insn) >> MICROMIPSOP_SH_##field) & MICROMIPSOP_MASK_##field)
-#define GET_OP_S(insn, field) \
-  ((GET_OP (insn, field) ^ ((MICROMIPSOP_MASK_##field >> 1) + 1)) \
-   - ((MICROMIPSOP_MASK_##field >> 1) + 1))
-  opend = micromips_opcodes + bfd_micromips_num_opcodes;
-  for (op = micromips_opcodes; op < opend; op++)
-    {
-      if (op->pinfo != INSN_MACRO
-         && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
-         && (insn & op->mask) == op->match
-         && ((length == 2 && (op->mask & 0xffff0000) == 0)
-             || (length == 4 && (op->mask & 0xffff0000) != 0)))
-       {
-         const char *s;
-
-         infprintf (is, "%s", op->name);
-         if (op->args[0] != '\0')
-           infprintf (is, "\t");
-
-         for (s = op->args; *s != '\0'; s++)
-           {
-             switch (*s)
-               {
-               case ',':
-               case '(':
-               case ')':
-                 infprintf (is, "%c", *s);
-                 break;
-
-               case '.':
-                 infprintf (is, "%d", GET_OP_S (insn, OFFSET10));
-                 break;
-
-               case '1':
-                 infprintf (is, "0x%x", GET_OP (insn, STYPE));
-                 break;
-
-               case '2':
-                 infprintf (is, "0x%x", GET_OP (insn, BP));
-                 break;
-
-               case '3':
-                 infprintf (is, "0x%x", GET_OP (insn, SA3));
-                 break;
-
-               case '4':
-                 infprintf (is, "0x%x", GET_OP (insn, SA4));
-                 break;
-
-               case '5':
-                 infprintf (is, "0x%x", GET_OP (insn, IMM8));
-                 break;
-
-               case '6':
-                 infprintf (is, "0x%x", GET_OP (insn, RS));
-                 break;
-
-               case '7':
-                 infprintf (is, "$ac%d", GET_OP (insn, DSPACC));
-                 break;
-
-               case '8':
-                 infprintf (is, "0x%x", GET_OP (insn, WRDSP));
-                 break;
-
-               case '0': /* DSP 6-bit signed immediate in bit 16.  */
-                 delta = (GET_OP (insn, DSPSFT) ^ 0x20) - 0x20;
-                 infprintf (is, "%d", delta);
-                 break;
-
-               case '<':
-                 infprintf (is, "0x%x", GET_OP (insn, SHAMT));
-                 break;
-
-               case '\\':
-                 infprintf (is, "0x%x", GET_OP (insn, 3BITPOS));
-                 break;
-
-               case '^':
-                 infprintf (is, "0x%x", GET_OP (insn, RD));
-                 break;
-
-               case '|':
-                 infprintf (is, "0x%x", GET_OP (insn, TRAP));
-                 break;
-
-               case '~':
-                 infprintf (is, "%d", GET_OP_S (insn, OFFSET12));
-                 break;
-
-               case 'a':
-                 if (strcmp (op->name, "jalx") == 0)
-                   info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff)
-                                   | (GET_OP (insn, TARGET) << 2));
-                 else
-                   info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff)
-                                   | (GET_OP (insn, TARGET) << 1));
-                 /* For gdb disassembler, force odd address on jalx.  */
-                 if (info->flavour == bfd_target_unknown_flavour
-                     && strcmp (op->name, "jalx") == 0)
-                   info->target |= 1;
-                 (*info->print_address_func) (info->target, info);
-                 break;
-
-               case 'b':
-               case 'r':
-               case 's':
-               case 'v':
-                 infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS)]);
-                 break;
-
-               case 'c':
-                 infprintf (is, "0x%x", GET_OP (insn, CODE));
-                 break;
-
-               case 'd':
-                 infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RD)]);
-                 break;
-
-               case 'h':
-                 infprintf (is, "0x%x", GET_OP (insn, PREFX));
-                 break;
-
-               case 'i':
-               case 'u':
-                 infprintf (is, "0x%x", GET_OP (insn, IMMEDIATE));
-                 break;
-
-               case 'j': /* Same as i, but sign-extended.  */
-               case 'o':
-                 infprintf (is, "%d", GET_OP_S (insn, DELTA));
-                 break;
-
-               case 'k':
-                 infprintf (is, "0x%x", GET_OP (insn, CACHE));
-                 break;
-
-               case 'n':
-                 {
-                   int s_reg_encode;
-
-                   immed = GET_OP (insn, RT);
-                   s_reg_encode = immed & 0xf;
-                   if (s_reg_encode != 0)
-                     {
-                       if (s_reg_encode == 1)
-                         infprintf (is, "%s", mips_gpr_names[16]);
-                       else if (s_reg_encode < 9)
-                         infprintf (is, "%s-%s",
-                                  mips_gpr_names[16],
-                                  mips_gpr_names[15 + s_reg_encode]);
-                       else if (s_reg_encode == 9)
-                         infprintf (is, "%s-%s,%s",
-                                  mips_gpr_names[16],
-                                  mips_gpr_names[23],
-                                  mips_gpr_names[30]);
-                       else
-                         infprintf (is, "UNKNOWN");
-                     }
-
-                   if (immed & 0x10) /* For ra.  */
-                     {
-                       if (s_reg_encode == 0)
-                         infprintf (is, "%s", mips_gpr_names[31]);
-                       else
-                         infprintf (is, ",%s", mips_gpr_names[31]);
-                     }
-                   break;
-                 }
-
-               case 'p':
-                 /* Sign-extend the displacement.  */
-                 delta = GET_OP_S (insn, DELTA);
-                 info->target = (delta << 1) + memaddr + length;
-                 (*info->print_address_func) (info->target, info);
-                 break;
-
-               case 'q':
-                 infprintf (is, "0x%x", GET_OP (insn, CODE2));
-                 break;
-
-               case 't':
-               case 'w':
-                 infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RT)]);
-                 break;
-
-               case 'y':
-                 infprintf (is, "%s", mips_gpr_names[GET_OP (insn, RS3)]);
-                 break;
-
-               case 'z':
-                 infprintf (is, "%s", mips_gpr_names[0]);
-                 break;
-
-               case '@': /* DSP 10-bit signed immediate in bit 16.  */
-                 delta = (GET_OP (insn, IMM10) ^ 0x200) - 0x200;
-                 infprintf (is, "%d", delta);
-                 break;
-
-               case 'B':
-                 infprintf (is, "0x%x", GET_OP (insn, CODE10));
-                 break;
-
-               case 'C':
-                 infprintf (is, "0x%x", GET_OP (insn, COPZ));
-                 break;
-
-               case 'D':
-                 infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FD)]);
-                 break;
-
-               case 'E':
-                 /* Coprocessor register for lwcN instructions, et al.
-
-                   Note that there is no load/store cp0 instructions, and
-                   that FPU (cp1) instructions disassemble this field using
-                   'T' format.  Therefore, until we gain understanding of
-                   cp2 register names, we can simply print the register
-                   numbers.  */
-                 infprintf (is, "$%d", GET_OP (insn, RT));
-                 break;
-
-               case 'G':
-                 /* Coprocessor register for mtcN instructions, et al.  Note
-                    that FPU (cp1) instructions disassemble this field using
-                    'S' format.  Therefore, we only need to worry about cp0,
-                    cp2, and cp3.
-                    The microMIPS encoding does not have a coprocessor
-                    identifier field as such, so we must work out the
-                    coprocessor number by looking at the opcode.  */
-                 switch (insn
-                         & ~((MICROMIPSOP_MASK_RT << MICROMIPSOP_SH_RT)
-                             | (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS)))
-                   {
-                   case 0x000000fc:                            /* mfc0  */
-                   case 0x000002fc:                            /* mtc0  */
-                   case 0x580000fc:                            /* dmfc0 */
-                   case 0x580002fc:                            /* dmtc0 */
-                     infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]);
-                     break;
-                   default:
-                     infprintf (is, "$%d", GET_OP (insn, RS));
-                     break;
-                   }
-                 break;
-
-               case 'H':
-                 infprintf (is, "%d", GET_OP (insn, SEL));
-                 break;
-
-               case 'K':
-                 infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]);
-                 break;
+  if (info->symbols
+      && info->symbols[0]
+      && (info->symbols[0]->flags & BSF_SYNTHETIC)
+      && addr == bfd_asymbol_value (info->symbols[0]) + 12)
+    return true;
 
-               case 'M':
-                 infprintf (is, "$fcc%d", GET_OP (insn, CCC));
-                 break;
-
-               case 'N':
-                 infprintf (is,
-                          (op->pinfo & (FP_D | FP_S)) != 0
-                          ? "$fcc%d" : "$cc%d",
-                          GET_OP (insn, BCC));
-                 break;
-
-               case 'R':
-                 infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FR)]);
-                 break;
-
-               case 'S':
-               case 'V':
-                 infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FS)]);
-                 break;
-
-               case 'T':
-                 infprintf (is, "%s", mips_fpr_names[GET_OP (insn, FT)]);
-                 break;
-
-               case '+':
-                 /* Extension character; switch for second char.  */
-                 s++;
-                 switch (*s)
-                   {
-                   case 'A':
-                     lsb = GET_OP (insn, EXTLSB);
-                     infprintf (is, "0x%x", lsb);
-                     break;
-
-                   case 'B':
-                     msb = GET_OP (insn, INSMSB);
-                     infprintf (is, "0x%x", msb - lsb + 1);
-                     break;
-
-                   case 'C':
-                   case 'H':
-                     msbd = GET_OP (insn, EXTMSBD);
-                     infprintf (is, "0x%x", msbd + 1);
-                     break;
-
-                   case 'D':
-                     {
-                       const struct mips_cp0sel_name *n;
-                       unsigned int cp0reg, sel;
-
-                       cp0reg = GET_OP (insn, RS);
-                       sel = GET_OP (insn, SEL);
-
-                       /* CP0 register including 'sel' code for mtcN
-                          (et al.), to be printed textually if known.
-                          If not known, print both CP0 register name and
-                          sel numerically since CP0 register with sel 0 may
-                          have a name unrelated to register being printed.  */
-                       n = lookup_mips_cp0sel_name (mips_cp0sel_names,
-                                                    mips_cp0sel_names_len,
-                                                    cp0reg, sel);
-                       if (n != NULL)
-                         infprintf (is, "%s", n->name);
-                       else
-                         infprintf (is, "$%d,%d", cp0reg, sel);
-                       break;
-                     }
-
-                   case 'E':
-                     lsb = GET_OP (insn, EXTLSB) + 32;
-                     infprintf (is, "0x%x", lsb);
-                     break;
-
-                   case 'F':
-                     msb = GET_OP (insn, INSMSB) + 32;
-                     infprintf (is, "0x%x", msb - lsb + 1);
-                     break;
-
-                   case 'G':
-                     msbd = GET_OP (insn, EXTMSBD) + 32;
-                     infprintf (is, "0x%x", msbd + 1);
-                     break;
-
-                   default:
-                     /* xgettext:c-format */
-                     infprintf (is,
-                              _("# internal disassembler error, "
-                                "unrecognized modifier (+%c)"),
-                              *s);
-                     abort ();
-                   }
-                 break;
-
-               case 'm':
-                 /* Extension character; switch for second char.  */
-                 s++;
-                 switch (*s)
-                   {
-                   case 'a':   /* global pointer.  */
-                     infprintf (is, "%s", mips_gpr_names[28]);
-                     break;
-
-                   case 'b':
-                     regno = micromips_to_32_reg_b_map[GET_OP (insn, MB)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  return false;
+}
 
-                   case 'c':
-                     regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+/* Whether none, a 32-bit or a 16-bit instruction match has been done.  */
 
-                   case 'd':
-                     regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+enum match_kind
+{
+  MATCH_NONE,
+  MATCH_FULL,
+  MATCH_SHORT
+};
 
-                   case 'e':
-                     regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+/* Disassemble mips16 instructions.  */
 
-                   case 'f':
-                     /* Save lastregno for "mt" to print out later.  */
-                     lastregno = micromips_to_32_reg_f_map[GET_OP (insn, MF)];
-                     infprintf (is, "%s", mips_gpr_names[lastregno]);
-                     break;
+static int
+print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  int status;
+  bfd_byte buffer[4];
+  const struct mips_opcode *op, *opend;
+  struct mips_print_arg_state state;
+  void *is = info->stream;
+  bool have_second;
+  bool extend_only;
+  unsigned int second;
+  unsigned int first;
+  unsigned int full;
 
-                   case 'g':
-                     regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  info->bytes_per_chunk = 2;
+  info->display_endian = info->endian;
+  info->insn_info_valid = 1;
+  info->branch_delay_insns = 0;
+  info->data_size = 0;
+  info->target = 0;
+  info->target2 = 0;
 
-                   case 'h':
-                     regno = micromips_to_32_reg_h_map[GET_OP (insn, MH)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+#define GET_OP(insn, field) \
+  (((insn) >> MIPS16OP_SH_##field) & MIPS16OP_MASK_##field)
+  /* Decode PLT entry's GOT slot address word.  */
+  if (is_mips16_plt_tail (info, memaddr))
+    {
+      info->insn_type = dis_noninsn;
+      status = (*info->read_memory_func) (memaddr, buffer, 4, info);
+      if (status == 0)
+       {
+         unsigned int gotslot;
 
-                   case 'i':
-                     regno = micromips_to_32_reg_i_map[GET_OP (insn, MI)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+         if (info->endian == BFD_ENDIAN_BIG)
+           gotslot = bfd_getb32 (buffer);
+         else
+           gotslot = bfd_getl32 (buffer);
+         infprintf (is, dis_style_assembler_directive, ".word");
+         infprintf (is, dis_style_text, "\t");
+         infprintf (is, dis_style_immediate, "0x%x", gotslot);
 
-                   case 'j':
-                     infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]);
-                     break;
+         return 4;
+       }
+    }
+  else
+    {
+      info->insn_type = dis_nonbranch;
+      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+    }
+  if (status != 0)
+    {
+      (*info->memory_error_func) (status, memaddr, info);
+      return -1;
+    }
 
-                   case 'l':
-                     regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  extend_only = false;
 
-                   case 'm':
-                     regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  if (info->endian == BFD_ENDIAN_BIG)
+    first = bfd_getb16 (buffer);
+  else
+    first = bfd_getl16 (buffer);
 
-                   case 'n':
-                     regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+  if (status == 0)
+    {
+      have_second = true;
+      if (info->endian == BFD_ENDIAN_BIG)
+       second = bfd_getb16 (buffer);
+      else
+       second = bfd_getl16 (buffer);
+      full = (first << 16) | second;
+    }
+  else
+    {
+      have_second = false;
+      second = 0;
+      full = first;
+    }
 
-                   case 'p':
-                     /* Save lastregno for "mt" to print out later.  */
-                     lastregno = GET_OP (insn, MP);
-                     infprintf (is, "%s", mips_gpr_names[lastregno]);
-                     break;
+  /* FIXME: Should probably use a hash table on the major opcode here.  */
 
-                   case 'q':
-                     regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
+  opend = mips16_opcodes + bfd_mips16_num_opcodes;
+  for (op = mips16_opcodes; op < opend; op++)
+    {
+      enum match_kind match;
 
-                   case 'r':   /* program counter.  */
-                     infprintf (is, "$pc");
-                     break;
+      if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+       continue;
 
-                   case 's':   /* stack pointer.  */
-                     lastregno = 29;
-                     infprintf (is, "%s", mips_gpr_names[29]);
-                     break;
+      if (op->pinfo == INSN_MACRO
+         || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+       match = MATCH_NONE;
+      else if (mips_opcode_32bit_p (op))
+       {
+         if (have_second
+             && (full & op->mask) == op->match)
+           match = MATCH_FULL;
+         else
+           match = MATCH_NONE;
+       }
+      else if ((first & op->mask) == op->match)
+       {
+         match = MATCH_SHORT;
+         second = 0;
+         full = first;
+       }
+      else if ((first & 0xf800) == 0xf000
+              && have_second
+              && !extend_only
+              && (second & op->mask) == op->match)
+       {
+         if (op->pinfo2 & INSN2_SHORT_ONLY)
+           {
+             match = MATCH_NONE;
+             extend_only = true;
+           }
+         else
+           match = MATCH_FULL;
+       }
+      else
+       match = MATCH_NONE;
 
-                   case 't':
-                     infprintf (is, "%s", mips_gpr_names[lastregno]);
-                     break;
+      if (match != MATCH_NONE)
+       {
+         const char *s;
 
-                   case 'z':   /* $0.  */
-                     infprintf (is, "%s", mips_gpr_names[0]);
-                     break;
+         infprintf (is, dis_style_mnemonic, "%s", op->name);
+         if (op->args[0] != '\0')
+           infprintf (is, dis_style_text, "\t");
 
-                   case 'A':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMA) << 2;
-                     infprintf (is, "%d", immed);
+         init_print_arg_state (&state);
+         for (s = op->args; *s != '\0'; s++)
+           {
+             if (*s == ','
+                 && s[1] == 'w'
+                 && GET_OP (full, RX) == GET_OP (full, RY))
+               {
+                 /* Skip the register and the comma.  */
+                 ++s;
+                 continue;
+               }
+             if (*s == ','
+                 && s[1] == 'v'
+                 && GET_OP (full, RZ) == GET_OP (full, RX))
+               {
+                 /* Skip the register and the comma.  */
+                 ++s;
+                 continue;
+               }
+             if (s[0] == 'N'
+                 && s[1] == ','
+                 && s[2] == 'O'
+                 && op->name[strlen (op->name) - 1] == '0')
+               {
+                 /* Coprocessor register 0 with sel field.  */
+                 const struct mips_cp0sel_name *n;
+                 const struct mips_operand *operand;
+                 unsigned int reg, sel;
+
+                 operand = decode_mips16_operand (*s, true);
+                 reg = mips_extract_operand (operand, (first << 16) | second);
+                 s += 2;
+                 operand = decode_mips16_operand (*s, true);
+                 sel = mips_extract_operand (operand, (first << 16) | second);
+
+                 /* CP0 register including 'sel' code for mftc0, to be
+                    printed textually if known.  If not known, print both
+                    CP0 register name and sel numerically since CP0 register
+                    with sel 0 may have a name unrelated to register being
+                    printed.  */
+                 n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+                                              mips_cp0sel_names_len,
+                                              reg, sel);
+                 if (n != NULL)
+                   infprintf (is, dis_style_register, "%s", n->name);
+                 else
+                   {
+                     infprintf (is, dis_style_register, "$%d", reg);
+                     infprintf (is, dis_style_text, ",");
+                     infprintf (is, dis_style_immediate, "%d", sel);
+                   }
+               }
+             else
+               switch (match)
+                 {
+                   case MATCH_FULL:
+                     print_mips16_insn_arg (info, &state, op, *s, memaddr + 2,
+                                            second, true, first, s[1] == '(');
                      break;
-
-                   case 'B':
-                     immed = micromips_imm_b_map[GET_OP (insn, IMMB)];
-                     infprintf (is, "%d", immed);
+                   case MATCH_SHORT:
+                     print_mips16_insn_arg (info, &state, op, *s, memaddr,
+                                            first, false, 0, s[1] == '(');
                      break;
-
-                   case 'C':
-                     immed = micromips_imm_c_map[GET_OP (insn, IMMC)];
-                     infprintf (is, "0x%x", immed);
+                   case MATCH_NONE:    /* Stop the compiler complaining.  */
                      break;
+                 }
+           }
 
-                   case 'D':
-                     /* Sign-extend the displacement.  */
-                     delta = GET_OP_S (insn, IMMD);
-                     info->target = (delta << 1) + memaddr + length;
-                     (*info->print_address_func) (info->target, info);
-                     break;
+         /* Figure out branch instruction type and delay slot information.  */
+         if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+           info->branch_delay_insns = 1;
+         if ((op->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0
+             || (op->pinfo2 & INSN2_UNCOND_BRANCH) != 0)
+           {
+             if ((op->pinfo & INSN_WRITE_GPR_31) != 0)
+               info->insn_type = dis_jsr;
+             else
+               info->insn_type = dis_branch;
+           }
+         else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
+           info->insn_type = dis_condbranch;
 
-                   case 'E':
-                     /* Sign-extend the displacement.  */
-                     delta = GET_OP_S (insn, IMME);
-                     info->target = (delta << 1) + memaddr + length;
-                     (*info->print_address_func) (info->target, info);
-                     break;
+         return match == MATCH_FULL ? 4 : 2;
+       }
+    }
+#undef GET_OP
 
-                   case 'F':
-                     immed = GET_OP (insn, IMMF);
-                     infprintf (is, "0x%x", immed);
-                     break;
+  infprintf (is, dis_style_assembler_directive, ".short");
+  infprintf (is, dis_style_text, "\t");
+  infprintf (is, dis_style_immediate, "0x%x", first);
+  info->insn_type = dis_noninsn;
 
-                   case 'G':
-                     immed = (insn >> MICROMIPSOP_SH_IMMG) + 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMG) - 1;
-                     infprintf (is, "%d", immed);
-                     break;
+  return 2;
+}
 
-                   case 'H':
-                     immed = GET_OP (insn, IMMH) << 1;
-                     infprintf (is, "%d", immed);
-                     break;
+/* Disassemble microMIPS instructions.  */
 
-                   case 'I':
-                     immed = (insn >> MICROMIPSOP_SH_IMMI) + 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMI) - 1;
-                     infprintf (is, "%d", immed);
-                     break;
+static int
+print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
+{
+  const fprintf_styled_ftype infprintf = info->fprintf_styled_func;
+  const struct mips_opcode *op, *opend;
+  void *is = info->stream;
+  bfd_byte buffer[2];
+  unsigned int higher;
+  unsigned int length;
+  int status;
+  unsigned int insn;
 
-                   case 'J':
-                     immed = GET_OP (insn, IMMJ) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
+  info->bytes_per_chunk = 2;
+  info->display_endian = info->endian;
+  info->insn_info_valid = 1;
+  info->branch_delay_insns = 0;
+  info->data_size = 0;
+  info->insn_type = dis_nonbranch;
+  info->target = 0;
+  info->target2 = 0;
 
-                   case 'L':
-                     immed = GET_OP (insn, IMML);
-                     infprintf (is, "%d", immed);
-                     break;
+  status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+  if (status != 0)
+    {
+      (*info->memory_error_func) (status, memaddr, info);
+      return -1;
+    }
 
-                   case 'M':
-                     immed = (insn >> MICROMIPSOP_SH_IMMM) - 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMM) + 1;
-                     infprintf (is, "%d", immed);
-                     break;
+  length = 2;
 
-                   case 'N':
-                     immed = GET_OP (insn, IMMN);
-                     if (immed == 0)
-                       infprintf (is, "%s,%s",
-                                mips_gpr_names[16],
-                                mips_gpr_names[31]);
-                     else
-                       infprintf (is, "%s-%s,%s",
-                                mips_gpr_names[16],
-                                mips_gpr_names[16 + immed],
-                                mips_gpr_names[31]);
-                     break;
+  if (info->endian == BFD_ENDIAN_BIG)
+    insn = bfd_getb16 (buffer);
+  else
+    insn = bfd_getl16 (buffer);
 
-                   case 'O':
-                     immed = GET_OP (insn, IMMO);
-                     infprintf (is, "0x%x", immed);
-                     break;
+  if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
+    {
+      /* This is a 32-bit microMIPS instruction.  */
+      higher = insn;
 
-                   case 'P':
-                     immed = GET_OP (insn, IMMP) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
+      status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+      if (status != 0)
+       {
+         infprintf (is, dis_style_text, "micromips 0x%x", higher);
+         (*info->memory_error_func) (status, memaddr + 2, info);
+         return -1;
+       }
 
-                   case 'Q':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMQ) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
+      if (info->endian == BFD_ENDIAN_BIG)
+       insn = bfd_getb16 (buffer);
+      else
+       insn = bfd_getl16 (buffer);
 
-                   case 'U':
-                     immed = GET_OP (insn, IMMU) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
+      insn = insn | (higher << 16);
 
-                   case 'W':
-                     immed = GET_OP (insn, IMMW) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
+      length += 2;
+    }
 
-                   case 'X':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMX);
-                     infprintf (is, "%d", immed);
-                     break;
+  /* FIXME: Should probably use a hash table on the major opcode here.  */
 
-                   case 'Y':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMY) << 2;
-                     if ((unsigned int) (immed + 8) < 16)
-                       immed ^= 0x400;
-                     infprintf (is, "%d", immed);
-                     break;
+  opend = micromips_opcodes + bfd_micromips_num_opcodes;
+  for (op = micromips_opcodes; op < opend; op++)
+    {
+      if (op->pinfo != INSN_MACRO
+         && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
+         && (insn & op->mask) == op->match
+         && ((length == 2 && (op->mask & 0xffff0000) == 0)
+             || (length == 4 && (op->mask & 0xffff0000) != 0)))
+       {
+         if (!validate_insn_args (op, decode_micromips_operand, insn))
+           continue;
 
-                   default:
-                     /* xgettext:c-format */
-                     infprintf (is,
-                              _("# internal disassembler error, "
-                                "unrecognized modifier (m%c)"),
-                              *s);
-                     abort ();
-                   }
-                 break;
+         infprintf (is, dis_style_mnemonic, "%s", op->name);
 
-               default:
-                 /* xgettext:c-format */
-                 infprintf (is,
-                          _("# internal disassembler error, "
-                            "unrecognized modifier (%c)"),
-                          *s);
-                 abort ();
-               }
+         if (op->args[0])
+           {
+             infprintf (is, dis_style_text, "\t");
+             print_insn_args (info, op, decode_micromips_operand, insn,
+                              memaddr + 1, length);
            }
 
          /* Figure out instruction type and branch delay information.  */
@@ -2889,7 +2582,7 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
          if (((op->pinfo & INSN_UNCOND_BRANCH_DELAY)
               | (op->pinfo2 & INSN2_UNCOND_BRANCH)) != 0)
            {
-             if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_GPR_T)) != 0)
+             if ((op->pinfo & (INSN_WRITE_GPR_31 | INSN_WRITE_1)) != 0)
                info->insn_type = dis_jsr;
              else
                info->insn_type = dis_branch;
@@ -2903,50 +2596,58 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
                info->insn_type = dis_condbranch;
            }
          else if ((op->pinfo
-                   & (INSN_STORE_MEMORY | INSN_LOAD_MEMORY_DELAY)) != 0)
+                   & (INSN_STORE_MEMORY | INSN_LOAD_MEMORY)) != 0)
            info->insn_type = dis_dref;
 
          return length;
        }
     }
-#undef GET_OP_S
-#undef GET_OP
 
-  infprintf (is, "0x%x", insn);
+  infprintf (is, dis_style_assembler_directive, ".short");
+  infprintf (is, dis_style_text, "\t");
+  if (length != 2)
+    {
+      infprintf (is, dis_style_immediate, "0x%x", (insn >> 16) & 0xffff);
+      infprintf (is, dis_style_text, ", ");
+    }
+  infprintf (is, dis_style_immediate, "0x%x", (insn & 0xffff));
+
   info->insn_type = dis_noninsn;
 
   return length;
 }
 
 /* Return 1 if a symbol associated with the location being disassembled
-   indicates a compressed (MIPS16 or microMIPS) mode.  We iterate over
-   all the symbols at the address being considered assuming if at least
-   one of them indicates code compression, then such code has been
-   genuinely produced here (other symbols could have been derived from
-   function symbols defined elsewhere or could define data).  Otherwise,
-   return 0.  */
-
-static bfd_boolean
-is_compressed_mode_p (struct disassemble_info *info)
-{
-  elf_symbol_type *symbol;
-  int pos;
+   indicates a compressed mode, either MIPS16 or microMIPS, according to
+   MICROMIPS_P.  We iterate over all the symbols at the address being
+   considered assuming if at least one of them indicates code compression,
+   then such code has been genuinely produced here (other symbols could
+   have been derived from function symbols defined elsewhere or could
+   define data).  Otherwise, return 0.  */
+
+static bool
+is_compressed_mode_p (struct disassemble_info *info, bool micromips_p)
+{
   int i;
-
-  for (i = 0; i < info->num_symbols; i++)
-    {
-      pos = info->symtab_pos + i;
-
-      if (bfd_asymbol_flavour (info->symtab[pos]) != bfd_target_elf_flavour)
-       continue;
-
-      symbol = (elf_symbol_type *) info->symtab[pos];
-      if ((!micromips_ase
-          && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
-         || (micromips_ase
-             && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
-           return 1;
-    }
+  int l;
+
+  for (i = info->symtab_pos, l = i + info->num_symbols; i < l; i++)
+    if (((info->symtab[i])->flags & BSF_SYNTHETIC) != 0
+       && ((!micromips_p
+            && ELF_ST_IS_MIPS16 ((*info->symbols)->udata.i))
+           || (micromips_p
+               && ELF_ST_IS_MICROMIPS ((*info->symbols)->udata.i))))
+      return 1;
+    else if (bfd_asymbol_flavour (info->symtab[i]) == bfd_target_elf_flavour
+             && info->symtab[i]->section == info->section)
+      {
+       elf_symbol_type *symbol = (elf_symbol_type *) info->symtab[i];
+       if ((!micromips_p
+            && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
+           || (micromips_p
+               && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
+         return 1;
+      }
 
   return 0;
 }
@@ -2962,7 +2663,6 @@ _print_insn_mips (bfd_vma memaddr,
                  struct disassemble_info *info,
                  enum bfd_endian endianness)
 {
-  int (*print_insn_compr) (bfd_vma, struct disassemble_info *);
   bfd_byte buffer[INSNLEN];
   int status;
 
@@ -2974,18 +2674,23 @@ _print_insn_mips (bfd_vma memaddr,
   if (info->mach == bfd_mach_mips_micromips)
     return print_insn_micromips (memaddr, info);
 
-  print_insn_compr = !micromips_ase ? print_insn_mips16 : print_insn_micromips;
-
 #if 1
   /* FIXME: If odd address, this is CLEARLY a compressed instruction.  */
   /* Only a few tools will work this way.  */
   if (memaddr & 0x01)
-    return print_insn_compr (memaddr, info);
+    {
+      if (micromips_ase)
+       return print_insn_micromips (memaddr, info);
+      else
+       return print_insn_mips16 (memaddr, info);
+    }
 #endif
 
 #if SYMTAB_AVAILABLE
-  if (is_compressed_mode_p (info))
-    return print_insn_compr (memaddr, info);
+  if (is_compressed_mode_p (info, true))
+    return print_insn_micromips (memaddr, info);
+  if (is_compressed_mode_p (info, false))
+    return print_insn_mips16 (memaddr, info);
 #endif
 
   status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
@@ -3019,55 +2724,196 @@ print_insn_little_mips (bfd_vma memaddr, struct disassemble_info *info)
   return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
 }
 \f
-void
-print_mips_disassembler_options (FILE *stream)
+/* Indices into option argument vector for options accepting an argument.
+   Use MIPS_OPTION_ARG_NONE for options accepting no argument.  */
+typedef enum
 {
-  unsigned int i;
+  MIPS_OPTION_ARG_NONE = -1,
+  MIPS_OPTION_ARG_ABI,
+  MIPS_OPTION_ARG_ARCH,
+  MIPS_OPTION_ARG_SIZE
+} mips_option_arg_t;
+
+/* Valid MIPS disassembler options.  */
+static struct
+{
+  const char *name;
+  const char *description;
+  mips_option_arg_t arg;
+} mips_options[] =
+{
+  { "no-aliases", N_("Use canonical instruction forms.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "msa",        N_("Recognize MSA instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "virt",       N_("Recognize the virtualization ASE instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "xpa",        N_("Recognize the eXtended Physical Address (XPA) ASE\n\
+                  instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "ginv",       N_("Recognize the Global INValidate (GINV) ASE "
+                    "instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-mmi",
+                 N_("Recognize the Loongson MultiMedia extensions "
+                    "Instructions (MMI) ASE instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-cam",
+                 N_("Recognize the Loongson Content Address Memory (CAM) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-ext",
+                 N_("Recognize the Loongson EXTensions (EXT) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-ext2",
+                 N_("Recognize the Loongson EXTensions R2 (EXT2) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "gpr-names=", N_("Print GPR names according to specified ABI.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "fpr-names=", N_("Print FPR names according to specified ABI.\n\
+                  Default: numeric.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "cp0-names=", N_("Print CP0 register names according to specified "
+                    "architecture.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ARCH },
+  { "hwr-names=", N_("Print HWR names according to specified architecture.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ARCH },
+  { "reg-names=", N_("Print GPR and FPR names according to specified ABI.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "reg-names=", N_("Print CP0 register and HWR names according to "
+                    "specified\n\
+                  architecture."),
+                 MIPS_OPTION_ARG_ARCH }
+};
 
-  fprintf (stream, _("\n\
-The following MIPS specific disassembler options are supported for use\n\
-with the -M switch (multiple options should be separated by commas):\n"));
+/* Build the structure representing valid MIPS disassembler options.
+   This is done dynamically for maintenance ease purpose; a static
+   initializer would be unreadable.  */
 
-  fprintf (stream, _("\n\
-  gpr-names=ABI            Print GPR names according to  specified ABI.\n\
-                           Default: based on binary being disassembled.\n"));
+const disasm_options_and_args_t *
+disassembler_options_mips (void)
+{
+  static disasm_options_and_args_t *opts_and_args;
 
-  fprintf (stream, _("\n\
-  fpr-names=ABI            Print FPR names according to specified ABI.\n\
-                           Default: numeric.\n"));
+  if (opts_and_args == NULL)
+    {
+      size_t num_options = ARRAY_SIZE (mips_options);
+      size_t num_args = MIPS_OPTION_ARG_SIZE;
+      disasm_option_arg_t *args;
+      disasm_options_t *opts;
+      size_t i;
+      size_t j;
+
+      args = XNEWVEC (disasm_option_arg_t, num_args + 1);
+
+      args[MIPS_OPTION_ARG_ABI].name = "ABI";
+      args[MIPS_OPTION_ARG_ABI].values
+       = XNEWVEC (const char *, ARRAY_SIZE (mips_abi_choices) + 1);
+      for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+       args[MIPS_OPTION_ARG_ABI].values[i] = mips_abi_choices[i].name;
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_ABI].values[i] = NULL;
+
+      args[MIPS_OPTION_ARG_ARCH].name = "ARCH";
+      args[MIPS_OPTION_ARG_ARCH].values
+       = XNEWVEC (const char *, ARRAY_SIZE (mips_arch_choices) + 1);
+      for (i = 0, j = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+       if (*mips_arch_choices[i].name != '\0')
+         args[MIPS_OPTION_ARG_ARCH].values[j++] = mips_arch_choices[i].name;
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_ARCH].values[j] = NULL;
+
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_SIZE].name = NULL;
+      args[MIPS_OPTION_ARG_SIZE].values = NULL;
+
+      opts_and_args = XNEW (disasm_options_and_args_t);
+      opts_and_args->args = args;
+
+      opts = &opts_and_args->options;
+      opts->name = XNEWVEC (const char *, num_options + 1);
+      opts->description = XNEWVEC (const char *, num_options + 1);
+      opts->arg = XNEWVEC (const disasm_option_arg_t *, num_options + 1);
+      for (i = 0; i < num_options; i++)
+       {
+         opts->name[i] = mips_options[i].name;
+         opts->description[i] = _(mips_options[i].description);
+         if (mips_options[i].arg != MIPS_OPTION_ARG_NONE)
+           opts->arg[i] = &args[mips_options[i].arg];
+         else
+           opts->arg[i] = NULL;
+       }
+      /* The array we return must be NULL terminated.  */
+      opts->name[i] = NULL;
+      opts->description[i] = NULL;
+      opts->arg[i] = NULL;
+    }
 
-  fprintf (stream, _("\n\
-  cp0-names=ARCH           Print CP0 register names according to\n\
-                           specified architecture.\n\
-                           Default: based on binary being disassembled.\n"));
+  return opts_and_args;
+}
 
-  fprintf (stream, _("\n\
-  hwr-names=ARCH           Print HWR names according to specified \n\
-                          architecture.\n\
-                           Default: based on binary being disassembled.\n"));
+void
+print_mips_disassembler_options (FILE *stream)
+{
+  const disasm_options_and_args_t *opts_and_args;
+  const disasm_option_arg_t *args;
+  const disasm_options_t *opts;
+  size_t max_len = 0;
+  size_t i;
+  size_t j;
 
-  fprintf (stream, _("\n\
-  reg-names=ABI            Print GPR and FPR names according to\n\
-                           specified ABI.\n"));
+  opts_and_args = disassembler_options_mips ();
+  opts = &opts_and_args->options;
+  args = opts_and_args->args;
 
   fprintf (stream, _("\n\
-  reg-names=ARCH           Print CP0 register and HWR names according to\n\
-                           specified architecture.\n"));
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n\n"));
 
-  fprintf (stream, _("\n\
-  For the options above, the following values are supported for \"ABI\":\n\
-   "));
-  for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
-    fprintf (stream, " %s", mips_abi_choices[i].name);
-  fprintf (stream, _("\n"));
+  /* Compute the length of the longest option name.  */
+  for (i = 0; opts->name[i] != NULL; i++)
+    {
+      size_t len = strlen (opts->name[i]);
 
-  fprintf (stream, _("\n\
-  For the options above, The following values are supported for \"ARCH\":\n\
-   "));
-  for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
-    if (*mips_arch_choices[i].name != '\0')
-      fprintf (stream, " %s", mips_arch_choices[i].name);
-  fprintf (stream, _("\n"));
+      if (opts->arg[i] != NULL)
+       len += strlen (opts->arg[i]->name);
+      if (max_len < len)
+       max_len = len;
+    }
+
+  for (i = 0, max_len++; opts->name[i] != NULL; i++)
+    {
+      fprintf (stream, "  %s", opts->name[i]);
+      if (opts->arg[i] != NULL)
+       fprintf (stream, "%s", opts->arg[i]->name);
+      if (opts->description[i] != NULL)
+       {
+         size_t len = strlen (opts->name[i]);
+
+         if (opts->arg[i] != NULL)
+           len += strlen (opts->arg[i]->name);
+         fprintf (stream,
+                  "%*c %s", (int) (max_len - len), ' ', opts->description[i]);
+       }
+      fprintf (stream, _("\n"));
+    }
+
+  for (i = 0; args[i].name != NULL; i++)
+    {
+      if (args[i].values == NULL)
+       continue;
+      fprintf (stream, _("\n\
+  For the options above, the following values are supported for \"%s\":\n   "),
+              args[i].name);
+      for (j = 0; args[i].values[j] != NULL; j++)
+       fprintf (stream, " %s", args[i].values[j]);
+      fprintf (stream, _("\n"));
+    }
 
   fprintf (stream, _("\n"));
 }