]> oss.titaniummirror.com Git - msp430-binutils.git/blobdiff - opcodes/ppc-dis.c
Merge commit 'upstream/2.20'
[msp430-binutils.git] / opcodes / ppc-dis.c
index c03dd54bd91b4d0e4a427cccf25d9a47f8d1ee8b..ac88f7698eac2da4ccc8d4417a47191b6d0e4c46 100644 (file)
@@ -1,6 +1,6 @@
 /* ppc-dis.c -- Disassemble PowerPC instructions
-   Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+   2008, 2009 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support
 
    This file is part of the GNU opcodes library.
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include "sysdep.h"
 #include "dis-asm.h"
+#include "opintl.h"
 #include "opcode/ppc.h"
 
 /* This file provides several disassembler functions, all of which use
    are provided because this file handles disassembly for the PowerPC
    in both big and little endian mode and also for the POWER (RS/6000)
    chip.  */
+static int print_insn_powerpc (bfd_vma, struct disassemble_info *, int,
+                              ppc_cpu_t);
 
-static int print_insn_powerpc (bfd_vma, struct disassemble_info *, int, int);
+struct dis_private
+{
+  /* Stash the result of parsing disassembler_options here.  */
+  ppc_cpu_t dialect;
+};
+
+#define POWERPC_DIALECT(INFO) \
+  (((struct dis_private *) ((INFO)->private_data))->dialect)
+
+struct ppc_mopt {
+  const char *opt;
+  ppc_cpu_t cpu;
+  ppc_cpu_t sticky;
+};
+
+struct ppc_mopt ppc_opts[] = {
+  { "403",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_403
+               | PPC_OPCODE_32),
+    0 },
+  { "405",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_403
+               | PPC_OPCODE_405 | PPC_OPCODE_32),
+    0 },
+  { "440",     (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32
+               | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI),
+    0 },
+  { "464",     (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32
+               | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI),
+    0 },
+  { "476",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ISEL
+               | PPC_OPCODE_440 | PPC_OPCODE_476 | PPC_OPCODE_POWER4
+               | PPC_OPCODE_POWER5),
+    0 },
+  { "601",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_601
+               | PPC_OPCODE_32),
+    0 },
+  { "603",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32),
+    0 },
+  { "604",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32),
+    0 },
+  { "620",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64),
+    0 },
+  { "7400",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC
+               | PPC_OPCODE_32),
+    0 },
+  { "7410",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC
+               | PPC_OPCODE_32),
+    0 },
+  { "7450",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC
+               | PPC_OPCODE_32),
+    0 },
+  { "7455",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ALTIVEC
+               | PPC_OPCODE_32),
+    0 },
+  { "750cl",   (PPC_OPCODE_PPC | PPC_OPCODE_PPCPS)
+    , 0 },
+  { "altivec", (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC),
+    PPC_OPCODE_ALTIVEC },
+  { "any",     0,
+    PPC_OPCODE_ANY },
+  { "booke",   (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32),
+    0 },
+  { "booke32", (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_32),
+    0 },
+  { "cell",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64
+               | PPC_OPCODE_POWER4 | PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC),
+    0 },
+  { "com",     (PPC_OPCODE_COMMON | PPC_OPCODE_32),
+    0 },
+  { "e300",    (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32
+               | PPC_OPCODE_E300),
+    0 },
+  { "e500",    (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_SPE
+               | PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK
+               | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI
+               | PPC_OPCODE_E500MC),
+    0 },
+  { "e500mc",  (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_ISEL
+               | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI
+               | PPC_OPCODE_E500MC),
+    0 },
+  { "e500x2",  (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_SPE
+               | PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK
+               | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI
+               | PPC_OPCODE_E500MC),
+    0 },
+  { "efs",     (PPC_OPCODE_PPC | PPC_OPCODE_EFS),
+    0 },
+  { "power4",  (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64
+               | PPC_OPCODE_POWER4),
+    0 },
+  { "power5",  (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64
+               | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5),
+    0 },
+  { "power6",  (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64
+               | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6
+               | PPC_OPCODE_ALTIVEC),
+    0 },
+  { "power7",  (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ISEL
+               | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5
+               | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 | PPC_OPCODE_ALTIVEC
+               | PPC_OPCODE_VSX),
+    0 },
+  { "ppc",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32),
+    0 },
+  { "ppc32",   (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_32),
+    0 },
+  { "ppc64",   (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64),
+    0 },
+  { "ppc64bridge", (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_64_BRIDGE
+                   | PPC_OPCODE_64),
+    0 },
+  { "a2",      (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_ISEL
+               | PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_CACHELCK
+               | PPC_OPCODE_64 | PPC_OPCODE_A2),
+    0 },
+  { "ppcps",   (PPC_OPCODE_PPC | PPC_OPCODE_PPCPS),
+    0 },
+  { "pwr",     (PPC_OPCODE_POWER | PPC_OPCODE_32),
+    0 },
+  { "pwr2",    (PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_32),
+    0 },
+  { "pwrx",    (PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_32),
+    0 },
+  { "spe",     (PPC_OPCODE_PPC | PPC_OPCODE_EFS),
+    PPC_OPCODE_SPE },
+  { "vsx",     (PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC),
+    PPC_OPCODE_VSX },
+};
+
+/* Handle -m and -M options that set cpu type, and .machine arg.  */
+
+ppc_cpu_t
+ppc_parse_cpu (ppc_cpu_t ppc_cpu, const char *arg)
+{
+  /* Sticky bits.  */
+  ppc_cpu_t retain_flags = ppc_cpu & (PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX
+                                     | PPC_OPCODE_SPE | PPC_OPCODE_ANY);
+  unsigned int i;
+
+  for (i = 0; i < sizeof (ppc_opts) / sizeof (ppc_opts[0]); i++)
+    if (strcmp (ppc_opts[i].opt, arg) == 0)
+      {
+       if (ppc_opts[i].sticky)
+         {
+           retain_flags |= ppc_opts[i].sticky;
+           if ((ppc_cpu & ~(PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX
+                            | PPC_OPCODE_SPE | PPC_OPCODE_ANY)) != 0)
+             break;
+         }
+       ppc_cpu = ppc_opts[i].cpu;
+       break;
+      }
+  if (i >= sizeof (ppc_opts) / sizeof (ppc_opts[0]))
+    return 0;
 
-/* Determine which set of machines to disassemble for.  PPC403/601 or
-   BookE.  For convenience, also disassemble instructions supported
-   by the AltiVec vector unit.  */
+  ppc_cpu |= retain_flags;
+  return ppc_cpu;
+}
+
+/* Determine which set of machines to disassemble for.  */
 
 static int
-powerpc_dialect (struct disassemble_info *info)
+powerpc_init_dialect (struct disassemble_info *info)
 {
-  int dialect = PPC_OPCODE_PPC;
-
-  if (BFD_DEFAULT_TARGET_SIZE == 64)
-    dialect |= PPC_OPCODE_64;
-
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "booke") != NULL)
-    dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_BOOKE64;
-  else if ((info->mach == bfd_mach_ppc_e500)
-          || (info->disassembler_options
-              && strstr (info->disassembler_options, "e500") != NULL))
-    dialect |= (PPC_OPCODE_BOOKE
-               | PPC_OPCODE_SPE | PPC_OPCODE_ISEL
-               | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK
-               | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK
-               | PPC_OPCODE_RFMCI);
-  else if (info->disassembler_options
-          && strstr (info->disassembler_options, "efs") != NULL)
-    dialect |= PPC_OPCODE_EFS;
-  else if (info->disassembler_options
-          && strstr (info->disassembler_options, "e300") != NULL)
-    dialect |= PPC_OPCODE_E300 | PPC_OPCODE_CLASSIC | PPC_OPCODE_COMMON;
-  else if (info->disassembler_options
-          && strstr (info->disassembler_options, "440") != NULL)
-    dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_32
-      | PPC_OPCODE_440 | PPC_OPCODE_ISEL | PPC_OPCODE_RFMCI;
-  else
-    dialect |= (PPC_OPCODE_403 | PPC_OPCODE_601 | PPC_OPCODE_CLASSIC
-               | PPC_OPCODE_COMMON | PPC_OPCODE_ALTIVEC);
+  ppc_cpu_t dialect = 0;
+  char *arg;
+  struct dis_private *priv = calloc (sizeof (*priv), 1);
 
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "power4") != NULL)
-    dialect |= PPC_OPCODE_POWER4;
+  if (priv == NULL)
+    return FALSE;
 
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "power5") != NULL)
-    dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5;
+  arg = info->disassembler_options;
+  while (arg != NULL)
+    {
+      ppc_cpu_t new_cpu = 0;
+      char *end = strchr (arg, ',');
 
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "cell") != NULL)
-    dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC;
+      if (end != NULL)
+       *end = 0;
 
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "power6") != NULL)
-    dialect |= PPC_OPCODE_POWER4 | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC;
+      if ((new_cpu = ppc_parse_cpu (dialect, arg)) != 0)
+       dialect = new_cpu;
+      else if (strcmp (arg, "32") == 0)
+       {
+         dialect &= ~PPC_OPCODE_64;
+         dialect |= PPC_OPCODE_32;
+       }
+      else if (strcmp (arg, "64") == 0)
+       {
+         dialect |= PPC_OPCODE_64;
+         dialect &= ~PPC_OPCODE_32;
+       }
+      else
+       fprintf (stderr, _("warning: ignoring unknown -M%s option\n"), arg);
 
-  if (info->disassembler_options
-      && strstr (info->disassembler_options, "any") != NULL)
-    dialect |= PPC_OPCODE_ANY;
+      if (end != NULL)
+       *end++ = ',';
+      arg = end;
+    }
 
-  if (info->disassembler_options)
+  if ((dialect & ~(PPC_OPCODE_32 | PPC_OPCODE_64)) == 0)
     {
-      if (strstr (info->disassembler_options, "32") != NULL)
-       dialect &= ~PPC_OPCODE_64;
-      else if (strstr (info->disassembler_options, "64") != NULL)
+      if (info->mach == bfd_mach_ppc64)
        dialect |= PPC_OPCODE_64;
+      else
+       dialect |= PPC_OPCODE_32;
+      /* Choose a reasonable default.  */
+      dialect |= (PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_CLASSIC
+                 | PPC_OPCODE_601 | PPC_OPCODE_ALTIVEC);
     }
 
-  info->private_data = (char *) 0 + dialect;
-  return dialect;
+  info->private_data = priv;
+  POWERPC_DIALECT(info) = dialect;
+
+  return TRUE;
 }
 
 /* Print a big endian PowerPC instruction.  */
@@ -107,8 +256,9 @@ powerpc_dialect (struct disassemble_info *info)
 int
 print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info)
 {
-  int dialect = (char *) info->private_data - (char *) 0;
-  return print_insn_powerpc (memaddr, info, 1, dialect);
+  if (info->private_data == NULL && !powerpc_init_dialect (info))
+    return -1;
+  return print_insn_powerpc (memaddr, info, 1, POWERPC_DIALECT(info));
 }
 
 /* Print a little endian PowerPC instruction.  */
@@ -116,8 +266,9 @@ print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info)
 int
 print_insn_little_powerpc (bfd_vma memaddr, struct disassemble_info *info)
 {
-  int dialect = (char *) info->private_data - (char *) 0;
-  return print_insn_powerpc (memaddr, info, 0, dialect);
+  if (info->private_data == NULL && !powerpc_init_dialect (info))
+    return -1;
+  return print_insn_powerpc (memaddr, info, 0, POWERPC_DIALECT(info));
 }
 
 /* Print a POWER (RS/6000) instruction.  */
@@ -132,7 +283,7 @@ print_insn_rs6000 (bfd_vma memaddr, struct disassemble_info *info)
 
 static long
 operand_value_powerpc (const struct powerpc_operand *operand,
-                      unsigned long insn, int dialect)
+                      unsigned long insn, ppc_cpu_t dialect)
 {
   long value;
   int invalid;
@@ -162,7 +313,7 @@ operand_value_powerpc (const struct powerpc_operand *operand,
 
 static int
 skip_optional_operands (const unsigned char *opindex,
-                       unsigned long insn, int dialect)
+                       unsigned long insn, ppc_cpu_t dialect)
 {
   const struct powerpc_operand *operand;
 
@@ -184,7 +335,7 @@ static int
 print_insn_powerpc (bfd_vma memaddr,
                    struct disassemble_info *info,
                    int bigendian,
-                   int dialect)
+                   ppc_cpu_t dialect)
 {
   bfd_byte buffer[4];
   int status;
@@ -192,9 +343,7 @@ print_insn_powerpc (bfd_vma memaddr,
   const struct powerpc_opcode *opcode;
   const struct powerpc_opcode *opcode_end;
   unsigned long op;
-
-  if (dialect == 0)
-    dialect = powerpc_dialect (info);
+  ppc_cpu_t dialect_orig = dialect;
 
   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
   if (status != 0)
@@ -232,7 +381,8 @@ print_insn_powerpc (bfd_vma memaddr,
        continue;
 
       if ((insn & opcode->mask) != opcode->opcode
-         || (opcode->flags & dialect) == 0)
+         || (opcode->flags & dialect) == 0
+         || (opcode->deprecated & dialect_orig) != 0)
        continue;
 
       /* Make two passes over the operands.  First see if any of them
@@ -297,14 +447,20 @@ print_insn_powerpc (bfd_vma memaddr,
            (*info->fprintf_func) (info->stream, "f%ld", value);
          else if ((operand->flags & PPC_OPERAND_VR) != 0)
            (*info->fprintf_func) (info->stream, "v%ld", value);
+         else if ((operand->flags & PPC_OPERAND_VSR) != 0)
+           (*info->fprintf_func) (info->stream, "vs%ld", value);
          else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
            (*info->print_address_func) (memaddr + value, info);
          else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
            (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
-         else if ((operand->flags & PPC_OPERAND_CR) == 0
-                  || (dialect & PPC_OPCODE_PPC) == 0)
+         else if ((operand->flags & PPC_OPERAND_FSL) != 0) 
+           (*info->fprintf_func) (info->stream, "fsl%ld", value);
+         else if ((operand->flags & PPC_OPERAND_FCR) != 0)
+           (*info->fprintf_func) (info->stream, "fcr%ld", value);
+         else if ((operand->flags & PPC_OPERAND_UDI) != 0)
            (*info->fprintf_func) (info->stream, "%ld", value);
-         else
+         else if ((operand->flags & PPC_OPERAND_CR) != 0
+                  && (dialect & PPC_OPCODE_PPC) != 0)
            {
              if (operand->bitm == 7)
                (*info->fprintf_func) (info->stream, "cr%ld", value);
@@ -321,6 +477,8 @@ print_insn_powerpc (bfd_vma memaddr,
                  (*info->fprintf_func) (info->stream, "%s", cbnames[cc]);
                }
            }
+         else
+           (*info->fprintf_func) (info->stream, "%ld", value);
 
          if (need_paren)
            {
@@ -356,18 +514,20 @@ print_insn_powerpc (bfd_vma memaddr,
 void
 print_ppc_disassembler_options (FILE *stream)
 {
-  fprintf (stream, "\n\
+  unsigned int i, col;
+
+  fprintf (stream, _("\n\
 The following PPC specific disassembler options are supported for use with\n\
-the -M switch:\n");
-
-  fprintf (stream, "  booke|booke32|booke64    Disassemble the BookE instructions\n");
-  fprintf (stream, "  e300                     Disassemble the e300 instructions\n");
-  fprintf (stream, "  e500|e500x2              Disassemble the e500 instructions\n");
-  fprintf (stream, "  440                      Disassemble the 440 instructions\n");
-  fprintf (stream, "  efs                      Disassemble the EFS instructions\n");
-  fprintf (stream, "  power4                   Disassemble the Power4 instructions\n");
-  fprintf (stream, "  power5                   Disassemble the Power5 instructions\n");
-  fprintf (stream, "  power6                   Disassemble the Power6 instructions\n");
-  fprintf (stream, "  32                       Do not disassemble 64-bit instructions\n");
-  fprintf (stream, "  64                       Allow disassembly of 64-bit instructions\n");
+the -M switch:\n"));
+
+  for (col = 0, i = 0; i < sizeof (ppc_opts) / sizeof (ppc_opts[0]); i++)
+    {
+      col += fprintf (stream, " %s,", ppc_opts[i].opt);
+      if (col > 66)
+       {
+         fprintf (stream, "\n");
+         col = 0;
+       }
+    }
+  fprintf (stream, " 32, 64\n");
 }