MIPS16: Simplify extended operand handling
[deliverable/binutils-gdb.git] / opcodes / mips-dis.c
index 19d0366e8f39819c7c44c160774780c842eb358f..a1152cd721a11cc3b10e017d1c3f407adefa6196 100644 (file)
@@ -658,7 +658,7 @@ const struct mips_arch_choice mips_arch_choices[] =
 
   /* This entry, mips16, is here only for ISA/processor selection; do
      not print its name.  */
-  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3, 0,
+  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64, 0,
     mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
     mips_hwr_names_numeric },
 };
@@ -764,6 +764,40 @@ 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;
+  return opcode_ases;
+}
+
 static void
 set_default_mips_dis_options (struct disassemble_info *info)
 {
@@ -785,19 +819,6 @@ set_default_mips_dis_options (struct disassemble_info *info)
   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.
@@ -818,6 +839,33 @@ set_default_mips_dis_options (struct disassemble_info *info)
       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
 }
 
@@ -1775,6 +1823,7 @@ print_mips16_insn_arg (struct disassemble_info *info,
   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;
 
@@ -1879,31 +1928,26 @@ print_mips16_insn_arg (struct disassemble_info *info,
          info->data_size = 1 << int_op->shift;
        }
 
-      if (operand->size == 26)
-       /* In this case INSN is the first two bytes of the instruction
-          and EXTEND is the second two bytes.  */
-       uval = ((insn & 0x1f) << 21) | ((insn & 0x3e0) << 11) | extend;
-      else
+      ext_size = 0;
+      if (use_extend)
        {
-         /* Calculate the full field value.  */
-         uval = mips_extract_operand (operand, insn);
-         if (use_extend)
+         ext_operand = decode_mips16_operand (type, TRUE);
+         if (ext_operand != operand)
            {
-             ext_operand = decode_mips16_operand (type, TRUE);
-             if (ext_operand != operand)
-               {
-                 operand = ext_operand;
-                 if (operand->size == 16)
-                   uval = (((extend & 0x1f) << 11) | (extend & 0x7e0)
-                           | (uval & 0x1f));
-                 else if (operand->size == 15)
-                   uval |= ((extend & 0xf) << 11) | (extend & 0x7f0);
-                 else
-                   uval = ((((extend >> 6) & 0x1f) | (extend & 0x20))
-                           & ((1U << operand->size) - 1));
-               }
+             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)
+       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);
 
       baseaddr = memaddr + 2;
       if (operand->type == OP_PCREL)
@@ -1914,36 +1958,36 @@ print_mips16_insn_arg (struct disassemble_info *info,
          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;
-            }
+           {
+             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;
+           }
        }
 
       print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
@@ -1968,6 +2012,15 @@ is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
   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
@@ -1976,13 +2029,13 @@ 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;
+  bfd_boolean have_second;
+  unsigned int second;
+  unsigned int first;
+  unsigned int full;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2023,44 +2076,26 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
       return -1;
     }
 
-  length = 2;
-
   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.  */
@@ -2068,37 +2103,39 @@ 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;
-
-         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;
-               }
+      enum match_kind match;
 
-             use_extend = FALSE;
+      if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+       continue;
 
-             memaddr += 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
+              && !(op->pinfo2 & INSN2_SHORT_ONLY)
+              && (second & op->mask) == op->match)
+       match = MATCH_FULL;
+      else
+       match = MATCH_NONE;
 
-             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 (match != MATCH_NONE)
+       {
+         const char *s;
 
          infprintf (is, "%s", op->name);
          if (op->args[0] != '\0')
@@ -2109,7 +2146,7 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
            {
              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;
@@ -2117,14 +2154,25 @@ 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 (info, &state, op, *s, memaddr, insn,
-                                    use_extend, extend, s[1] == '(');
+             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.  */
@@ -2141,17 +2189,15 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
          else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
            info->insn_type = dis_condbranch;
 
-         return length;
+         return match == MATCH_FULL ? 4 : 2;
        }
     }
 #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.  */
This page took 0.028964 seconds and 4 git commands to generate.