]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/mips-dis.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / opcodes / mips-dis.c
index 7e3d1232325dfaa446bdc6fb232fb9756661944b..9db604ffb39f5d2d8d65bfef68334c74f4ee8b8c 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-2022 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,98 +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_map1[] =
-{
-  5, 5, 6, 4, 4, 4, 4, 4
-};
-static const unsigned int micromips_to_32_reg_h_map2[] =
-{
-  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",
@@ -207,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",
@@ -243,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",
@@ -267,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"                },
@@ -493,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;
@@ -519,62 +467,88 @@ struct mips_arch_choice
   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, 0,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_numeric, NULL, 0, mips_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips,
+    mips_hwr_names_numeric },
   { "vr4100",  1, bfd_mach_mips4100, CPU_VR4100, ISA_MIPS3, 0,
-    mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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_hwr_names_numeric },
+    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
@@ -585,66 +559,144 @@ const struct mips_arch_choice mips_arch_choices[] =
     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,
     (ASE_SMARTMIPS | ASE_DSP | ASE_DSPR2 | ASE_EVA | ASE_MIPS3D
-     | ASE_MT | ASE_MCU | ASE_VIRT),
+     | 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,  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,
     (ASE_MIPS3D | ASE_DSP | ASE_DSPR2 | ASE_DSP64 | ASE_EVA | ASE_MT
-     | ASE_MDMX | ASE_MCU | ASE_VIRT | ASE_VIRT64),
+     | 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_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_hwr_names_mips3264r2 },
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
 
   { "sb1",     1, bfd_mach_mips_sb1, CPU_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, 0, mips_cp0_names_numeric,
-    NULL, 0, mips_hwr_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, 0, 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 },
+
+  /* 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 },
+
+  { "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 },
 
-  { "loongson3a",   1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A,
-    ISA_MIPS64 | INSN_LOONGSON_3A, 0, mips_cp0_names_numeric,
-    NULL, 0, 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_hwr_names_numeric },
+    mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon+",   1, bfd_mach_mips_octeonp, CPU_OCTEONP,
     ISA_MIPS64R2 | INSN_OCTEONP, 0, mips_cp0_names_numeric,
-    NULL, 0, mips_hwr_names_numeric },
+    NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon2",   1, bfd_mach_mips_octeon2, CPU_OCTEON2,
     ISA_MIPS64R2 | INSN_OCTEON2, 0, mips_cp0_names_numeric,
-    NULL, 0, mips_hwr_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, 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.  */
@@ -652,12 +704,14 @@ const struct mips_arch_choice mips_arch_choices[] =
     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, 0,
-    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.
@@ -672,6 +726,7 @@ 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 */
@@ -760,6 +815,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)
 {
@@ -777,22 +886,10 @@ set_default_mips_dis_options (struct disassemble_info *info)
   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.
@@ -810,9 +907,106 @@ set_default_mips_dis_options (struct disassemble_info *info)
       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
@@ -824,20 +1018,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 (CONST_STRNEQ (option, "virt"))
+  if (parse_mips_ase_option (option))
     {
-      mips_ase |= ASE_VIRT;
-      if (mips_isa & ISA_MIPS64R2)
-       mips_ase |= ASE_VIRT64;
+      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] == '=')
@@ -885,6 +1077,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)
     {
@@ -913,6 +1114,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;
@@ -964,476 +1166,740 @@ lookup_mips_cp0sel_name (const struct mips_cp0sel_name *names,
       return &names[i];
   return NULL;
 }
-\f
-/* Print insn arguments for 32/64-bit code.  */
+
+/* Print register REGNO, of type TYPE, for instruction OPCODE.  */
 
 static void
-print_insn_args (const char *d,
-                int l,
-                bfd_vma pc,
-                struct disassemble_info *info,
-                const struct mips_opcode *opp)
+print_reg (struct disassemble_info *info, const struct mips_opcode *opcode,
+          enum mips_reg_operand_type type, int regno)
 {
-  const fprintf_ftype infprintf = info->fprintf_func;
-  unsigned int lsb, msb, msbd, cpreg;
-  void *is = info->stream;
-
-  lsb = 0;
-
-#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 ')':
-         infprintf (is, "%c", *d);
-         break;
+    case OP_REG_GP:
+      info->fprintf_func (info->stream, "%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:
+      info->fprintf_func (info->stream, "%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 '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 'J':           /* hypcall operand */
-             infprintf (is, "0x%x", GET_OP (l, CODE10));
-             break;
-
-           case 't': /* Coprocessor 0 reg name */
-             infprintf (is, "%s", mips_cp0_names[GET_OP (l, RT)]);
-             break;
-
-           case 'x':           /* bbit bit index */
-             infprintf (is, "0x%x", GET_OP (l, BBITIND));
-             break;
-
-           case 'p':           /* cins, cins32, exts and exts32 position */
-             infprintf (is, "0x%x", GET_OP (l, CINSPOS));
-             break;
-
-           case 's':           /* cins32 and exts32 length-minus-one */
-           case 'S':           /* cins and exts length-minus-one field */
-             infprintf (is, "0x%x", GET_OP (l, CINSLM1));
-             break;
-
-           case 'Q':           /* seqi/snei immediate field */
-             infprintf (is, "%d", GET_OP_S (l, SEQI));
-             break;
-
-           case 'a':           /* 8-bit signed offset in bit 6 */
-             infprintf (is, "%d", GET_OP_S (l, OFFSET_A));
-             break;
-
-           case 'b':           /* 8-bit signed offset in bit 3 */
-             infprintf (is, "%d", GET_OP_S (l, OFFSET_B));
-             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 'z':
-             infprintf (is, "%s", mips_gpr_names[GET_OP (l, RZ)]);
-             break;
-
-           case 'Z':
-             infprintf (is, "%s", mips_fpr_names[GET_OP (l, FZ)]);
-             break;
-
-           case 'i': /* JALX destination */
-             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)
-               info->target |= 1;
-             (*info->print_address_func) (info->target, info);
-             break;
-
-           case 'j':   /* 9-bit signed offset in bit 7.  */
-             infprintf (is, "%d", GET_OP_S (l, EVAOFFSET));
-             break;
-
-           default:
-             /* xgettext:c-format */
-             infprintf (is,
-                        _("# internal error, "
-                          "undefined extension sequence (+%c)"),
-                        *d);
-             return;
-           }
-         break;
+    case OP_REG_CCC:
+      if (opcode->pinfo & (FP_D | FP_S))
+       info->fprintf_func (info->stream, "$fcc%d", regno);
+      else
+       info->fprintf_func (info->stream, "$cc%d", regno);
+      break;
 
-       case '2':
-         infprintf (is, "0x%x", GET_OP (l, BP));
-         break;
+    case OP_REG_VEC:
+      if (opcode->membership & INSN_5400)
+       info->fprintf_func (info->stream, "$f%d", regno);
+      else
+       info->fprintf_func (info->stream, "$v%d", regno);
+      break;
 
-       case '3':
-         infprintf (is, "0x%x", GET_OP (l, SA3));
-         break;
+    case OP_REG_ACC:
+      info->fprintf_func (info->stream, "$ac%d", regno);
+      break;
 
-       case '4':
-         infprintf (is, "0x%x", GET_OP (l, SA4));
-         break;
+    case OP_REG_COPRO:
+      if (opcode->name[strlen (opcode->name) - 1] == '0')
+       info->fprintf_func (info->stream, "%s", mips_cp0_names[regno]);
+      else
+       info->fprintf_func (info->stream, "$%d", regno);
+      break;
 
-       case '5':
-         infprintf (is, "0x%x", GET_OP (l, IMM8));
-         break;
+    case OP_REG_CONTROL:
+      if (opcode->name[strlen (opcode->name) - 1] == '1')
+       info->fprintf_func (info->stream, "%s", mips_cp1_names[regno]);
+      else
+       info->fprintf_func (info->stream, "$%d", regno);
+      break;
 
-       case '6':
-         infprintf (is, "0x%x", GET_OP (l, RS));
-         break;
+    case OP_REG_HW:
+      info->fprintf_func (info->stream, "%s", mips_hwr_names[regno]);
+      break;
 
-       case '7':
-         infprintf (is, "$ac%d", GET_OP (l, DSPACC));
-         break;
+    case OP_REG_VF:
+      info->fprintf_func (info->stream, "$vf%d", regno);
+      break;
 
-       case '8':
-         infprintf (is, "0x%x", GET_OP (l, WRDSP));
-         break;
+    case OP_REG_VI:
+      info->fprintf_func (info->stream, "$vi%d", regno);
+      break;
 
-       case '9':
-         infprintf (is, "$ac%d", GET_OP (l, DSPACC_S));
-         break;
+    case OP_REG_R5900_I:
+      info->fprintf_func (info->stream, "$I");
+      break;
 
-       case '0': /* dsp 6-bit signed immediate in bit 20 */
-         infprintf (is, "%d", GET_OP_S (l, DSPSFT));
-         break;
+    case OP_REG_R5900_Q:
+      info->fprintf_func (info->stream, "$Q");
+      break;
 
-       case ':': /* dsp 7-bit signed immediate in bit 19 */
-         infprintf (is, "%d", GET_OP_S (l, DSPSFT_7));
-         break;
+    case OP_REG_R5900_R:
+      info->fprintf_func (info->stream, "$R");
+      break;
 
-       case '~':
-         infprintf (is, "%d", GET_OP_S (l, OFFSET12));
-         break;
+    case OP_REG_R5900_ACC:
+      info->fprintf_func (info->stream, "$ACC");
+      break;
 
-       case '\\':
-         infprintf (is, "0x%x", GET_OP (l, 3BITPOS));
-         break;
+    case OP_REG_MSA:
+      info->fprintf_func (info->stream, "$w%d", regno);
+      break;
 
-       case '\'':
-         infprintf (is, "0x%x", GET_OP (l, RDDSP));
-         break;
+    case OP_REG_MSA_CTRL:
+      info->fprintf_func (info->stream, "%s", msa_control_names[regno]);
+      break;
 
-       case '@': /* dsp 10-bit signed immediate in bit 16 */
-         infprintf (is, "%d", GET_OP_S (l, IMM10));
-         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 '!':
-         infprintf (is, "%d", GET_OP (l, MT_U));
-         break;
+/* Initialize STATE for the start of an instruction.  */
 
-       case '$':
-         infprintf (is, "%d", GET_OP (l, MT_H));
-         break;
+static inline void
+init_print_arg_state (struct mips_print_arg_state *state)
+{
+  memset (state, 0, sizeof (*state));
+}
 
-       case '*':
-         infprintf (is, "$ac%d", GET_OP (l, MTACC_T));
-         break;
+/* Print OP_VU0_SUFFIX or OP_VU0_MATCH_SUFFIX operand OPERAND,
+   whose value is given by UVAL.  */
 
-       case '&':
-         infprintf (is, "$ac%d", GET_OP (l, MTACC_D));
-         break;
+static void
+print_vu0_channel (struct disassemble_info *info,
+                  const struct mips_operand *operand, unsigned int uval)
+{
+  if (operand->size == 4)
+    info->fprintf_func (info->stream, "%s%s%s%s",
+                       uval & 8 ? "x" : "",
+                       uval & 4 ? "y" : "",
+                       uval & 2 ? "z" : "",
+                       uval & 1 ? "w" : "");
+  else if (operand->size == 2)
+    info->fprintf_func (info->stream, "%c", "xyzw"[uval]);
+  else
+    abort ();
+}
 
-       case 'g':
-         /* Coprocessor register for CTTC1, MTTC2, MTHC2, CTTC2.  */
-         infprintf (is, "$%d", GET_OP (l, RD));
-         break;
+/* Record information about a register operand.  */
 
-       case 's':
-       case 'b':
-       case 'r':
-       case 'v':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RS)]);
-         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 't':
-       case 'w':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RT)]);
-         break;
+  if (!state->seen_dest)
+    {
+      state->seen_dest = 1;
+      state->dest_regno = regno;
+    }
+}
 
-       case 'i':
-       case 'u':
-         infprintf (is, "0x%x", GET_OP (l, IMMEDIATE));
-         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 'j': /* Same as i, but sign-extended.  */
-       case 'o':
-         infprintf (is, "%d", GET_OP_S (l, DELTA));
-         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_ftype infprintf = info->fprintf_func;
+  unsigned int nargs, nstatics, smask, i, j;
+  void *is = info->stream;
+  const char *sep;
 
-       case 'h':
-         infprintf (is, "0x%x", GET_OP (l, PREFX));
-         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 'k':
-         infprintf (is, "0x%x", GET_OP (l, CACHE));
-         break;
+  sep = "";
+  if (nargs > 0)
+    {
+      infprintf (is, "%s", mips_gpr_names[4]);
+      if (nargs > 1)
+       infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
+      sep = ",";
+    }
 
-       case 'a':
-         info->target = (((pc + 4) & ~(bfd_vma) 0x0fffffff)
-                         | (GET_OP (l, TARGET) << 2));
-         (*info->print_address_func) (info->target, info);
-         break;
+  infprintf (is, "%s%d", sep, frame_size);
 
-       case 'p':
-         /* Sign extend the displacement.  */
-         info->target = (GET_OP_S (l, DELTA) << 2) + pc + INSNLEN;
-         (*info->print_address_func) (info->target, info);
-         break;
+  if (ra)                      /* $ra */
+    infprintf (is, ",%s", mips_gpr_names[31]);
 
-       case 'd':
-         infprintf (is, "%s", mips_gpr_names[GET_OP (l, RD)]);
-         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 'U':
-         {
-           /* First check for both rd and rt being equal.  */
-           unsigned int reg;
-
-           reg = GET_OP (l, RD);
-           if (reg == GET_OP (l, RT))
-             infprintf (is, "%s", mips_gpr_names[reg]);
-           else
-             {
-               /* 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)]);
-             }
-         }
-         break;
+  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 (nstatics == 1)
+    infprintf (is, ",%s", mips_gpr_names[7]);
+  else if (nstatics > 0)
+    infprintf (is, ",%s-%s",
+              mips_gpr_names[7 - nstatics + 1],
+              mips_gpr_names[7]);
+}
 
-       case 'z':
-         infprintf (is, "%s", mips_gpr_names[0]);
-         break;
 
-       case '<':
-       case '1':
-         infprintf (is, "0x%x", GET_OP (l, SHAMT));
-         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 'c':
-         infprintf (is, "0x%x", GET_OP (l, CODE));
-         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_ftype infprintf = info->fprintf_func;
+  void *is = info->stream;
 
-       case 'q':
-         infprintf (is, "0x%x", GET_OP (l, CODE2));
-         break;
+  switch (operand->type)
+    {
+    case OP_INT:
+      {
+       const struct mips_int_operand *int_op;
 
-       case 'C':
-         infprintf (is, "0x%x", GET_OP (l, COPZ));
-         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, "0x%x", uval);
+       else
+         infprintf (is, "%d", uval);
+      }
+      break;
 
-       case 'B':
-         infprintf (is, "0x%x", GET_OP (l, CODE20));
-         break;
+    case OP_MAPPED_INT:
+      {
+       const struct mips_mapped_int_operand *mint_op;
 
-       case 'J':
-         infprintf (is, "0x%x", GET_OP (l, CODE19));
-         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, "0x%x", uval);
+       else
+         infprintf (is, "%d", uval);
+      }
+      break;
 
-       case 'S':
-       case 'V':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FS)]);
-         break;
+    case OP_MSB:
+      {
+       const struct mips_msb_operand *msb_op;
 
-       case 'T':
-       case 'W':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FT)]);
-         break;
+       msb_op = (const struct mips_msb_operand *) operand;
+       uval += msb_op->bias;
+       if (msb_op->add_lsb)
+         uval -= state->last_int;
+       infprintf (is, "0x%x", uval);
+      }
+      break;
 
-       case 'D':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FD)]);
-         break;
+    case OP_REG:
+    case OP_OPTIONAL_REG:
+      {
+       const struct mips_reg_operand *reg_op;
 
-       case 'R':
-         infprintf (is, "%s", mips_fpr_names[GET_OP (l, FR)]);
-         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);
 
-       case 'E':
-         cpreg = GET_OP (l, RT);
-         goto copro;
-
-       case 'G':
-         cpreg = GET_OP (l, RD);
-       copro:
-         /* 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.  */
-         if (opp->name[strlen (opp->name) - 1] == '0')
-           {
-             if (d[1] == ',' && d[2] == 'H')
-               {
-                 const struct mips_cp0sel_name *n;
-                 unsigned int sel;
+       mips_seen_register (state, uval, reg_op->reg_type);
+      }
+      break;
 
-                 sel = GET_OP (l, SEL);
+    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, ",");
+       print_reg (info, opcode, pair_op->reg_type,
+                  pair_op->reg2_map[uval]);
+      }
+      break;
 
-                 /* 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,
-                                              cpreg, sel);
-                 if (n != NULL)
-                   infprintf (is, "%s", n->name);
-                 else
-                   infprintf (is, "$%d,%d", cpreg, sel);
-                 d += 2;
-               }
+    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, "%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, "%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, "%s", mips_gpr_names[reg1]);
+       else if (reg1 == 0)
+         infprintf (is, "%s", mips_gpr_names[reg2]);
+       else
+         /* Bogus, result depends on processor.  */
+         infprintf (is, "%s or %s", mips_gpr_names[reg1],
+                    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, "%s,%s",
+                      mips_gpr_names[16],
+                      mips_gpr_names[31]);
+         else
+           infprintf (is, "%s-%s,%s",
+                      mips_gpr_names[16],
+                      mips_gpr_names[16 + uval],
+                      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, "%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, "%s", mips_cp0_names[cpreg]);
+               infprintf (is, "UNKNOWN");
            }
-         else
-           infprintf (is, "$%d", cpreg);
-         break;
 
-       case 'K':
-         infprintf (is, "%s", mips_hwr_names[GET_OP (l, RD)]);
-         break;
+         if (uval & 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 'N':
-         infprintf (is,
-                    (opp->pinfo & (FP_D | FP_S)) != 0 ? "$fcc%d" : "$cc%d",
-                    GET_OP (l, BCC));
-         break;
+    case OP_ENTRY_EXIT_LIST:
+      {
+       const char *sep;
+       unsigned int amask, smask;
 
-       case 'M':
-         infprintf (is, "$fcc%d", GET_OP (l, CCC));
-         break;
+       sep = "";
+       amask = (uval >> 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]);
+           sep = ",";
+         }
 
-       case 'P':
-         infprintf (is, "%d", GET_OP (l, PERFREG));
-         break;
+       smask = (uval >> 1) & 3;
+       if (smask == 3)
+         {
+           infprintf (is, "%s??", sep);
+           sep = ",";
+         }
+       else if (smask > 0)
+         {
+           infprintf (is, "%s%s", sep, mips_gpr_names[16]);
+           if (smask > 1)
+             infprintf (is, "-%s", mips_gpr_names[smask + 15]);
+           sep = ",";
+         }
 
-       case 'e':
-         infprintf (is, "%d", GET_OP (l, VECBYTE));
-         break;
+       if (uval & 1)
+         {
+           infprintf (is, "%s%s", sep, mips_gpr_names[31]);
+           sep = ",";
+         }
 
-       case '%':
-         infprintf (is, "%d", GET_OP (l, VECALIGN));
-         break;
+       if (amask == 5 || amask == 6)
+         {
+           infprintf (is, "%s%s", sep, mips_fpr_names[0]);
+           if (amask == 6)
+             infprintf (is, "-%s", mips_fpr_names[1]);
+         }
+      }
+      break;
 
-       case 'H':
-         infprintf (is, "%d", GET_OP (l, SEL));
-         break;
+    case OP_SAVE_RESTORE_LIST:
+      /* Should be handled by the caller due to complex behavior.  */
+      abort ();
 
-       case 'O':
-         infprintf (is, "%d", GET_OP (l, ALN));
-         break;
+    case OP_MDMX_IMM_REG:
+      {
+       unsigned int vsel;
 
-       case 'Q':
+       vsel = uval >> 5;
+       uval &= 31;
+       if ((vsel & 0x10) == 0)
          {
-           unsigned int vsel = GET_OP (l, VSEL);
-           char prefix;
-
-           prefix = opp->membership & INSN_5400 ? 'f' : 'v';
-           if ((vsel & 0x10) == 0)
-             {
-               int fmt;
-
-               vsel &= 0x0f;
-               for (fmt = 0; fmt < 3; fmt++, vsel >>= 1)
-                 if ((vsel & 1) == 0)
-                   break;
-               infprintf (is, "$%c%d[%d]", prefix, GET_OP (l, FT), vsel >> 1);
-             }
-           else if ((vsel & 0x08) == 0)
-             {
-               infprintf (is, "$%c%d", prefix, GET_OP (l, FT));
-             }
-           else
-             {
-               infprintf (is, "0x%x", GET_OP (l, FT));
-             }
+           int fmt;
+
+           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, "[%d]", vsel >> 1);
          }
+       else if ((vsel & 0x08) == 0)
+         print_reg (info, opcode, OP_REG_VEC, uval);
+       else
+         infprintf (is, "0x%x", uval);
+      }
+      break;
+
+    case OP_REPEAT_PREV_REG:
+      print_reg (info, opcode, state->last_reg_type, state->last_regno);
+      break;
+
+    case OP_REPEAT_DEST_REG:
+      print_reg (info, opcode, state->last_reg_type, state->dest_regno);
+      break;
+
+    case OP_PC:
+      infprintf (is, "$pc");
+      break;
+
+    case OP_REG28:
+      print_reg (info, opcode, OP_REG_GP, 28);
+      break;
+
+    case OP_VU0_SUFFIX:
+    case OP_VU0_MATCH_SUFFIX:
+      print_vu0_channel (info, operand, uval);
+      break;
+
+    case OP_IMM_INDEX:
+      infprintf (is, "[%d]", uval);
+      break;
+
+    case OP_REG_INDEX:
+      infprintf (is, "[");
+      print_reg (info, opcode, OP_REG_GP, uval);
+      infprintf (is, "]");
+      break;
+    }
+}
+
+/* Validate the arguments for INSN, which is described by OPCODE.
+   Use DECODE_OPERAND to get the encoding of each operand.  */
+
+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;
+
+  init_print_arg_state (&state);
+  for (s = opcode->args; *s; ++s)
+    {
+      switch (*s)
+       {
+       case ',':
+       case '(':
+       case ')':
          break;
 
-       case 'X':
-         infprintf (is, "$v%d", GET_OP (l, FD));
+       case '#':
+         ++s;
          break;
 
-       case 'Y':
-         infprintf (is, "$v%d", GET_OP (l, FS));
+       default:
+         operand = decode_operand (s);
+
+         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;
+
+                   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 OP_SAME_RS_RT:
+                 {
+                   unsigned int reg1, reg2;
+
+                   reg1 = uval & 31;
+                   reg2 = uval >> 5;
+
+                   if (reg1 != reg2 || reg1 == 0)
+                     return false;
+                 }
+               break;
+
+               case OP_CHECK_PREV:
+                 {
+                   const struct mips_check_prev_operand *prev_op;
+
+                   prev_op = (const struct mips_check_prev_operand *) operand;
+
+                   if (!prev_op->zero_ok && uval == 0)
+                     return false;
+
+                   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;
+
+                   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;
+}
+
+/* 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)
+{
+  const fprintf_ftype infprintf = info->fprintf_func;
+  void *is = info->stream;
+  struct mips_print_arg_state state;
+  const struct mips_operand *operand;
+  const char *s;
+
+  init_print_arg_state (&state);
+  for (s = opcode->args; *s; ++s)
+    {
+      switch (*s)
+       {
+       case ',':
+       case '(':
+       case ')':
+         infprintf (is, "%c", *s);
          break;
 
-       case 'Z':
-         infprintf (is, "$v%d", GET_OP (l, FT));
+       case '#':
+         ++s;
+         infprintf (is, "%c%c", *s, *s);
          break;
 
        default:
-         /* xgettext:c-format */
-         infprintf (is, _("# internal error, undefined modifier (%c)"), *d);
-         return;
+         operand = decode_operand (s);
+         if (!operand)
+           {
+             /* xgettext:c-format */
+             infprintf (is,
+                        _("# 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, "%s", n->name);
+             else
+               infprintf (is, "$%d,%d", reg, 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;
        }
     }
 }
@@ -1448,10 +1914,12 @@ 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_ftype infprintf = info->fprintf_func;
   const struct mips_opcode *op;
-  static bfd_boolean init = 0;
+  static bool init = 0;
   void *is = info->stream;
 
   /* Build a hash table to shorten the search time.  */
@@ -1491,22 +1959,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.  */
+             /* 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"))
+                && (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;
@@ -1522,23 +1989,33 @@ 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;
 
+             if (!validate_insn_args (op, decode_mips_operand, word))
+               continue;
+
              infprintf (is, "%s", op->name);
+             if (op->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
+               {
+                 unsigned int uval;
 
-             d = op->args;
-             if (d != NULL && *d != '\0')
+                 infprintf (is, ".");
+                 uval = mips_extract_operand (&mips_vu0_channel_mask, word);
+                 print_vu0_channel (info, &mips_vu0_channel_mask, uval);
+               }
+
+             if (op->args[0])
                {
                  infprintf (is, "\t");
-                 print_insn_args (d, word, memaddr, info, op);
+                 print_insn_args (info, op, decode_mips_operand, word,
+                                  memaddr, 4);
                }
 
              return INSNLEN;
            }
        }
     }
-#undef GET_OP_S
 #undef GET_OP
 
   /* Handle undefined instructions.  */
@@ -1550,22 +2027,23 @@ print_insn_mips (bfd_vma memaddr,
 /* 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)
+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_ftype infprintf = info->fprintf_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 ',':
@@ -1574,457 +2052,109 @@ print_mips16_insn_arg (char type,
       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;
+    default:
+      operand = decode_mips16_operand (type, false);
+      if (!operand)
+       {
+         /* xgettext:c-format */
+         infprintf (is, _("# internal error, undefined operand in `%s %s'"),
+                    opcode->name, opcode->args);
+         return;
+       }
 
-    case 'X':
-      infprintf (is, "%s", mips_gpr_names[GET_OP (l, REGR32)]);
-      break;
+      if (operand->type == OP_SAVE_RESTORE_LIST)
+       {
+         /* 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;
+       }
 
-    case 'Y':
-      infprintf (is, "%s", mips_gpr_names[MIPS16OP_EXTRACT_REG32R (l)]);
-      break;
+      if (is_offset && operand->type == OP_INT)
+       {
+         const struct mips_int_operand *int_op;
 
-    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 ();
-         }
+         int_op = (const struct mips_int_operand *) operand;
+         info->insn_type = dis_dref;
+         info->data_size = 1 << int_op->shift;
+       }
 
-       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;
-         }
+      ext_size = 0;
+      if (use_extend)
+       {
+         ext_operand = decode_mips16_operand (type, true);
+         if (ext_operand != operand
+             || (operand->type == OP_INT && operand->lsb == 0
+                 && mips_opcode_32bit_p (opcode)))
+           {
+             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;
 
-       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 + 2;
+      if (operand->type == OP_PCREL)
+       {
+         const struct mips_pcrel_operand *pcrel_op;
 
+         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)
+           {
+             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
                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':
-    case 'i':
-      {
-       if (! use_extend)
-         extend = 0;
-       l = ((l & 0x1f) << 23) | ((l & 0x3e0) << 13) | (extend << 2);
-       if (type == 'a' && 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]);
-      }
+      print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
       break;
-
-    default:
-      /* xgettext:c-format */
-      infprintf (is,
-                _("# internal disassembler error, "
-                  "unrecognised modifier (%c)"),
-                type);
-      abort ();
     }
 }
 
@@ -2033,18 +2163,27 @@ print_mips16_insn_arg (char type,
    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 bfd_boolean
+static bool
 is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
 {
   if (info->symbols
       && info->symbols[0]
       && (info->symbols[0]->flags & BSF_SYNTHETIC)
       && addr == bfd_asymbol_value (info->symbols[0]) + 12)
-    return TRUE;
+    return true;
 
-  return FALSE;
+  return false;
 }
 
+/* Whether none, a 32-bit or a 16-bit instruction match has been done.  */
+
+enum match_kind
+{
+  MATCH_NONE,
+  MATCH_FULL,
+  MATCH_SHORT
+};
+
 /* Disassemble mips16 instructions.  */
 
 static int
@@ -2053,12 +2192,14 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   const fprintf_ftype infprintf = info->fprintf_func;
   int status;
   bfd_byte buffer[4];
-  int length;
-  int insn;
-  bfd_boolean use_extend;
-  int extend = 0;
   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;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2068,6 +2209,8 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   info->target = 0;
   info->target2 = 0;
 
+#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))
     {
@@ -2097,44 +2240,28 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
       return -1;
     }
 
-  length = 2;
+  extend_only = false;
 
   if (info->endian == BFD_ENDIAN_BIG)
-    insn = bfd_getb16 (buffer);
+    first = bfd_getb16 (buffer);
   else
-    insn = bfd_getl16 (buffer);
+    first = bfd_getl16 (buffer);
 
-  /* Handle the extend opcode specially.  */
-  use_extend = FALSE;
-  if ((insn & 0xf800) == 0xf000)
+  status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+  if (status == 0)
     {
-      use_extend = TRUE;
-      extend = insn & 0x7ff;
-
-      memaddr += 2;
-
-      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "extend 0x%x", (unsigned int) extend);
-         (*info->memory_error_func) (status, memaddr, info);
-         return -1;
-       }
-
+      have_second = true;
       if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
+       second = bfd_getb16 (buffer);
       else
-       insn = bfd_getl16 (buffer);
-
-      /* Check for an extend opcode followed by an extend opcode.  */
-      if ((insn & 0xf800) == 0xf000)
-       {
-         infprintf (is, "extend 0x%x", (unsigned int) extend);
-         info->insn_type = dis_noninsn;
-         return length;
-       }
-
-      length += 2;
+       second = bfd_getl16 (buffer);
+      full = (first << 16) | second;
+    }
+  else
+    {
+      have_second = false;
+      second = 0;
+      full = first;
     }
 
   /* FIXME: Should probably use a hash table on the major opcode here.  */
@@ -2142,47 +2269,58 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   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)
-       {
-         const char *s;
+      enum match_kind match;
 
-         if (op->args[0] == 'a' || op->args[0] == 'i')
-           {
-             if (use_extend)
-               {
-                 infprintf (is, "extend 0x%x", (unsigned int) extend);
-                 info->insn_type = dis_noninsn;
-                 return length - 2;
-               }
-
-             use_extend = FALSE;
+      if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+       continue;
 
-             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;
-               }
+      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;
+
+      if (match != MATCH_NONE)
+       {
+         const char *s;
 
          infprintf (is, "%s", op->name);
          if (op->args[0] != '\0')
            infprintf (is, "\t");
 
+         init_print_arg_state (&state);
          for (s = op->args; *s != '\0'; s++)
            {
              if (*s == ','
                  && s[1] == 'w'
-                 && GET_OP (insn, RX) == GET_OP (insn, RY))
+                 && GET_OP (full, RX) == GET_OP (full, RY))
                {
                  /* Skip the register and the comma.  */
                  ++s;
@@ -2190,42 +2328,80 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
                }
              if (*s == ','
                  && s[1] == 'v'
-                 && GET_OP (insn, RZ) == GET_OP (insn, RX))
+                 && GET_OP (full, RZ) == GET_OP (full, RX))
                {
                  /* Skip the register and the comma.  */
                  ++s;
                  continue;
                }
-             print_mips16_insn_arg (*s, op, insn, use_extend, extend, memaddr,
-                                    info);
+             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, "%s", n->name);
+                 else
+                   infprintf (is, "$%d,%d", reg, sel);
+               }
+             else
+               switch (match)
+                 {
+                   case MATCH_FULL:
+                     print_mips16_insn_arg (info, &state, op, *s, memaddr + 2,
+                                            second, true, first, s[1] == '(');
+                     break;
+                   case MATCH_SHORT:
+                     print_mips16_insn_arg (info, &state, op, *s, memaddr,
+                                            first, false, 0, s[1] == '(');
+                     break;
+                   case MATCH_NONE:    /* Stop the compiler complaining.  */
+                     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
-                           | MIPS16_INSN_UNCOND_BRANCH)) != 0)
+         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->pinfo & MIPS16_INSN_COND_BRANCH) != 0)
+         else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
            info->insn_type = dis_condbranch;
 
-         return length;
+         return match == MATCH_FULL ? 4 : 2;
        }
     }
-#undef GET_OP_S
 #undef GET_OP
 
-  if (use_extend)
-    infprintf (is, "0x%x", extend | 0xf000);
-  infprintf (is, "0x%x", insn);
+  infprintf (is, "0x%x", first);
   info->insn_type = dis_noninsn;
 
-  return length;
+  return 2;
 }
 
 /* Disassemble microMIPS instructions.  */
@@ -2235,19 +2411,12 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
 {
   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;
+  unsigned int higher;
+  unsigned int length;
   int status;
-  int delta;
-  int immed;
-  int insn;
-
-  lsb = 0;
+  unsigned int insn;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2255,58 +2424,24 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
   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->target = 0;
+  info->target2 = 0;
 
-      info->insn_type = dis_noninsn;
-      return 6;
+  status = (*info->read_memory_func) (memaddr, buffer, 2, info);
+  if (status != 0)
+    {
+      (*info->memory_error_func) (status, memaddr, info);
+      return -1;
     }
-  else if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
+
+  length = 2;
+
+  if (info->endian == BFD_ENDIAN_BIG)
+    insn = bfd_getb16 (buffer);
+  else
+    insn = bfd_getl16 (buffer);
+
+  if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
     {
       /* This is a 32-bit microMIPS instruction.  */
       higher = insn;
@@ -2331,11 +2466,6 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
 
   /* 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++)
     {
@@ -2345,569 +2475,16 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
          && ((length == 2 && (op->mask & 0xffff0000) == 0)
              || (length == 4 && (op->mask & 0xffff0000) != 0)))
        {
-         const char *s;
+         if (!validate_insn_args (op, decode_micromips_operand, insn))
+           continue;
 
          infprintf (is, "%s", op->name);
-         if (op->args[0] != '\0')
-           infprintf (is, "\t");
 
-         for (s = op->args; *s != '\0'; s++)
+         if (op->args[0])
            {
-             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':
-                 info->target = (((memaddr + 4) & ~(bfd_vma) 0x07ffffff)
-                                 | (GET_OP (insn, TARGET) << 1));
-                 /* For gdb disassembler, maintain odd address.  */
-                 if (info->flavour == bfd_target_unknown_flavour)
-                   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.  */
-                 if (op->name[strlen (op->name) - 1] == '0')
-                   {
-                     if (s[1] == ',' && s[2] == 'H')
-                       {
-                         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);
-                         s += 2;
-                       }
-                     else
-                       infprintf (is, "%s", mips_cp0_names[GET_OP (insn, RS)]);
-                   }
-                 else
-                   infprintf (is, "$%d", GET_OP (insn, RS));
-                 break;
-
-               case 'H':
-                 infprintf (is, "%d", GET_OP (insn, SEL));
-                 break;
-
-               case 'K':
-                 infprintf (is, "%s", mips_hwr_names[GET_OP (insn, RS)]);
-                 break;
-
-               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 '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;
-
-                   case 'i':
-                     info->target = (((memaddr + 4) & ~(bfd_vma) 0x0fffffff)
-                                     | (GET_OP (insn, TARGET) << 2));
-                     (*info->print_address_func) (info->target, info);
-                     break;
-
-                   case 'j':   /* 9-bit signed offset in bit 0. */
-                     delta = GET_OP_S (insn, EVAOFFSET);
-                     infprintf (is, "%d", delta);
-                     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;
-
-                   case 'c':
-                     regno = micromips_to_32_reg_c_map[GET_OP (insn, MC)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'd':
-                     regno = micromips_to_32_reg_d_map[GET_OP (insn, MD)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'e':
-                     regno = micromips_to_32_reg_e_map[GET_OP (insn, ME)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   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;
-
-                   case 'g':
-                     regno = micromips_to_32_reg_g_map[GET_OP (insn, MG)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'h':
-                     regno = micromips_to_32_reg_h_map1[GET_OP (insn, MH)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     regno = micromips_to_32_reg_h_map2[GET_OP (insn, MH)];
-                     infprintf (is, ",%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'j':
-                     infprintf (is, "%s", mips_gpr_names[GET_OP (insn, MJ)]);
-                     break;
-
-                   case 'l':
-                     regno = micromips_to_32_reg_l_map[GET_OP (insn, ML)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'm':
-                     regno = micromips_to_32_reg_m_map[GET_OP (insn, MM)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'n':
-                     regno = micromips_to_32_reg_n_map[GET_OP (insn, MN)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'p':
-                     /* Save lastregno for "mt" to print out later.  */
-                     lastregno = GET_OP (insn, MP);
-                     infprintf (is, "%s", mips_gpr_names[lastregno]);
-                     break;
-
-                   case 'q':
-                     regno = micromips_to_32_reg_q_map[GET_OP (insn, MQ)];
-                     infprintf (is, "%s", mips_gpr_names[regno]);
-                     break;
-
-                   case 'r':   /* program counter.  */
-                     infprintf (is, "$pc");
-                     break;
-
-                   case 's':   /* stack pointer.  */
-                     lastregno = 29;
-                     infprintf (is, "%s", mips_gpr_names[29]);
-                     break;
-
-                   case 't':
-                     infprintf (is, "%s", mips_gpr_names[lastregno]);
-                     break;
-
-                   case 'z':   /* $0.  */
-                     infprintf (is, "%s", mips_gpr_names[0]);
-                     break;
-
-                   case 'A':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMA) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'B':
-                     immed = micromips_imm_b_map[GET_OP (insn, IMMB)];
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'C':
-                     immed = micromips_imm_c_map[GET_OP (insn, IMMC)];
-                     infprintf (is, "0x%x", immed);
-                     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;
-
-                   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;
-
-                   case 'F':
-                     immed = GET_OP (insn, IMMF);
-                     infprintf (is, "0x%x", immed);
-                     break;
-
-                   case 'G':
-                     immed = (insn >> MICROMIPSOP_SH_IMMG) + 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMG) - 1;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'H':
-                     immed = GET_OP (insn, IMMH) << 1;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'I':
-                     immed = (insn >> MICROMIPSOP_SH_IMMI) + 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMI) - 1;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'J':
-                     immed = GET_OP (insn, IMMJ) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'L':
-                     immed = GET_OP (insn, IMML);
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'M':
-                     immed = (insn >> MICROMIPSOP_SH_IMMM) - 1;
-                     immed = (immed & MICROMIPSOP_MASK_IMMM) + 1;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   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;
-
-                   case 'O':
-                     immed = GET_OP (insn, IMMO);
-                     infprintf (is, "0x%x", immed);
-                     break;
-
-                   case 'P':
-                     immed = GET_OP (insn, IMMP) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'Q':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMQ) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'U':
-                     immed = GET_OP (insn, IMMU) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'W':
-                     immed = GET_OP (insn, IMMW) << 2;
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   case 'X':
-                     /* Sign-extend the immediate.  */
-                     immed = GET_OP_S (insn, IMMX);
-                     infprintf (is, "%d", immed);
-                     break;
-
-                   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;
-
-                   default:
-                     /* xgettext:c-format */
-                     infprintf (is,
-                              _("# internal disassembler error, "
-                                "unrecognized modifier (m%c)"),
-                              *s);
-                     abort ();
-                   }
-                 break;
-
-               default:
-                 /* xgettext:c-format */
-                 infprintf (is,
-                          _("# internal disassembler error, "
-                            "unrecognized modifier (%c)"),
-                          *s);
-                 abort ();
-               }
+             infprintf (is, "\t");
+             print_insn_args (info, op, decode_micromips_operand, insn,
+                              memaddr + 1, length);
            }
 
          /* Figure out instruction type and branch delay information.  */
@@ -2917,7 +2494,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;
@@ -2931,14 +2508,12 @@ 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);
   info->insn_type = dis_noninsn;
@@ -2947,33 +2522,33 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info)
 }
 
 /* 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)
+   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;
   int l;
 
   for (i = info->symtab_pos, l = i + info->num_symbols; i < l; i++)
     if (((info->symtab[i])->flags & BSF_SYNTHETIC) != 0
-       && ((!micromips_ase
+       && ((!micromips_p
             && ELF_ST_IS_MIPS16 ((*info->symbols)->udata.i))
-           || (micromips_ase
+           || (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_ase
+       if ((!micromips_p
             && ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
-           || (micromips_ase
+           || (micromips_p
                && ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
          return 1;
       }
@@ -2992,7 +2567,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;
 
@@ -3004,18 +2578,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);
@@ -3049,58 +2628,194 @@ 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\
-  virt            Recognize the virtualization ASE instructions.\n"));
+const disasm_options_and_args_t *
+disassembler_options_mips (void)
+{
+  static disasm_options_and_args_t *opts_and_args;
 
-  fprintf (stream, _("\n\
-  gpr-names=ABI            Print GPR names according to  specified ABI.\n\
-                           Default: based on binary being disassembled.\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\
-  fpr-names=ABI            Print FPR names according to specified ABI.\n\
-                           Default: numeric.\n"));
+  return opts_and_args;
+}
 
-  fprintf (stream, _("\n\
-  cp0-names=ARCH           Print CP0 register names according to\n\
-                           specified 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\
-  hwr-names=ARCH           Print HWR names according to specified \n\
-                          architecture.\n\
-                           Default: based on binary being disassembled.\n"));
+  opts_and_args = disassembler_options_mips ();
+  opts = &opts_and_args->options;
+  args = opts_and_args->args;
 
   fprintf (stream, _("\n\
-  reg-names=ABI            Print GPR and FPR names according to\n\
-                           specified ABI.\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\
-  reg-names=ARCH           Print CP0 register and HWR names according to\n\
-                           specified architecture.\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 \"ABI\":\n\
-   "));
-  for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
-    fprintf (stream, " %s", mips_abi_choices[i].name);
-  fprintf (stream, _("\n"));
+      if (opts->arg[i] != NULL)
+       len += strlen (opts->arg[i]->name);
+      if (max_len < len)
+       max_len = len;
+    }
 
-  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"));
+  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++)
+    {
+      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"));
 }