ppc-dis.c revision 104834
160484Sobrien/* ppc-dis.c -- Disassemble PowerPC instructions
292828Sobrien   Copyright 1994, 1995, 2000, 2001, 2002 Free Software Foundation, Inc.
360484Sobrien   Written by Ian Lance Taylor, Cygnus Support
460484Sobrien
560484SobrienThis file is part of GDB, GAS, and the GNU binutils.
660484Sobrien
760484SobrienGDB, GAS, and the GNU binutils are free software; you can redistribute
860484Sobrienthem and/or modify them under the terms of the GNU General Public
960484SobrienLicense as published by the Free Software Foundation; either version
1060484Sobrien2, or (at your option) any later version.
1160484Sobrien
1260484SobrienGDB, GAS, and the GNU binutils are distributed in the hope that they
1360484Sobrienwill be useful, but WITHOUT ANY WARRANTY; without even the implied
1460484Sobrienwarranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
1560484Sobrienthe GNU General Public License for more details.
1660484Sobrien
1760484SobrienYou should have received a copy of the GNU General Public License
1860484Sobrienalong with this file; see the file COPYING.  If not, write to the Free
1960484SobrienSoftware Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2060484Sobrien
2160484Sobrien#include <stdio.h>
2260484Sobrien#include "sysdep.h"
2360484Sobrien#include "dis-asm.h"
2460484Sobrien#include "opcode/ppc.h"
2560484Sobrien
2660484Sobrien/* This file provides several disassembler functions, all of which use
2760484Sobrien   the disassembler interface defined in dis-asm.h.  Several functions
2860484Sobrien   are provided because this file handles disassembly for the PowerPC
2960484Sobrien   in both big and little endian mode and also for the POWER (RS/6000)
3060484Sobrien   chip.  */
3160484Sobrien
3260484Sobrienstatic int print_insn_powerpc PARAMS ((bfd_vma, struct disassemble_info *,
3360484Sobrien				       int bigendian, int dialect));
3460484Sobrien
3589857Sobrienstatic int powerpc_dialect PARAMS ((struct disassemble_info *));
3660484Sobrien
3789857Sobrien/* Determine which set of machines to disassemble for.  PPC403/601 or
3892828Sobrien   BookE.  For convenience, also disassemble instructions supported
3992828Sobrien   by the AltiVec vector unit.  */
4089857Sobrien
4160484Sobrienint
4289857Sobrienpowerpc_dialect(info)
4389857Sobrien     struct disassemble_info *info;
4489857Sobrien{
4589857Sobrien  int dialect = PPC_OPCODE_PPC | PPC_OPCODE_ALTIVEC;
4689857Sobrien
4789857Sobrien  if (BFD_DEFAULT_TARGET_SIZE == 64)
4889857Sobrien    dialect |= PPC_OPCODE_64;
4989857Sobrien
5089857Sobrien  if (info->disassembler_options
5189857Sobrien      && (strcmp (info->disassembler_options, "booke") == 0
5289857Sobrien	  || strcmp (info->disassembler_options, "booke32") == 0
5389857Sobrien	  || strcmp (info->disassembler_options, "booke64") == 0))
5489857Sobrien    dialect |= PPC_OPCODE_BOOKE | PPC_OPCODE_BOOKE64;
5589857Sobrien  else
5689857Sobrien    dialect |= PPC_OPCODE_403 | PPC_OPCODE_601;
5789857Sobrien
5892828Sobrien  if (info->disassembler_options
5992828Sobrien      && strcmp (info->disassembler_options, "power4") == 0)
6092828Sobrien    dialect |= PPC_OPCODE_POWER4;
6192828Sobrien
6289857Sobrien  if (info->disassembler_options)
6389857Sobrien    {
6489857Sobrien      if (strstr (info->disassembler_options, "32") != NULL)
6589857Sobrien	dialect &= ~PPC_OPCODE_64;
6689857Sobrien      else if (strstr (info->disassembler_options, "64") != NULL)
6789857Sobrien	dialect |= PPC_OPCODE_64;
6889857Sobrien    }
6989857Sobrien
7089857Sobrien  return dialect;
7189857Sobrien}
7289857Sobrien
7389857Sobrien/* Print a big endian PowerPC instruction.  */
7489857Sobrien
7589857Sobrienint
7660484Sobrienprint_insn_big_powerpc (memaddr, info)
7760484Sobrien     bfd_vma memaddr;
7860484Sobrien     struct disassemble_info *info;
7960484Sobrien{
8089857Sobrien  return print_insn_powerpc (memaddr, info, 1, powerpc_dialect(info));
8160484Sobrien}
8260484Sobrien
8389857Sobrien/* Print a little endian PowerPC instruction.  */
8460484Sobrien
8560484Sobrienint
8660484Sobrienprint_insn_little_powerpc (memaddr, info)
8760484Sobrien     bfd_vma memaddr;
8860484Sobrien     struct disassemble_info *info;
8960484Sobrien{
9089857Sobrien  return print_insn_powerpc (memaddr, info, 0, powerpc_dialect(info));
9160484Sobrien}
9260484Sobrien
9360484Sobrien/* Print a POWER (RS/6000) instruction.  */
9460484Sobrien
9560484Sobrienint
9660484Sobrienprint_insn_rs6000 (memaddr, info)
9760484Sobrien     bfd_vma memaddr;
9860484Sobrien     struct disassemble_info *info;
9960484Sobrien{
10060484Sobrien  return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER);
10160484Sobrien}
10260484Sobrien
10360484Sobrien/* Print a PowerPC or POWER instruction.  */
10460484Sobrien
10560484Sobrienstatic int
10660484Sobrienprint_insn_powerpc (memaddr, info, bigendian, dialect)
10760484Sobrien     bfd_vma memaddr;
10860484Sobrien     struct disassemble_info *info;
10960484Sobrien     int bigendian;
11060484Sobrien     int dialect;
11160484Sobrien{
11260484Sobrien  bfd_byte buffer[4];
11360484Sobrien  int status;
11460484Sobrien  unsigned long insn;
11560484Sobrien  const struct powerpc_opcode *opcode;
11660484Sobrien  const struct powerpc_opcode *opcode_end;
11760484Sobrien  unsigned long op;
11860484Sobrien
11960484Sobrien  status = (*info->read_memory_func) (memaddr, buffer, 4, info);
12060484Sobrien  if (status != 0)
12160484Sobrien    {
12260484Sobrien      (*info->memory_error_func) (status, memaddr, info);
12360484Sobrien      return -1;
12460484Sobrien    }
12560484Sobrien
12660484Sobrien  if (bigendian)
12760484Sobrien    insn = bfd_getb32 (buffer);
12860484Sobrien  else
12960484Sobrien    insn = bfd_getl32 (buffer);
13060484Sobrien
13160484Sobrien  /* Get the major opcode of the instruction.  */
13260484Sobrien  op = PPC_OP (insn);
13360484Sobrien
13460484Sobrien  /* Find the first match in the opcode table.  We could speed this up
13560484Sobrien     a bit by doing a binary search on the major opcode.  */
13660484Sobrien  opcode_end = powerpc_opcodes + powerpc_num_opcodes;
13760484Sobrien  for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
13860484Sobrien    {
13960484Sobrien      unsigned long table_op;
14060484Sobrien      const unsigned char *opindex;
14160484Sobrien      const struct powerpc_operand *operand;
14260484Sobrien      int invalid;
14360484Sobrien      int need_comma;
14460484Sobrien      int need_paren;
14560484Sobrien
14660484Sobrien      table_op = PPC_OP (opcode->opcode);
14760484Sobrien      if (op < table_op)
14860484Sobrien	break;
14960484Sobrien      if (op > table_op)
15060484Sobrien	continue;
15160484Sobrien
15260484Sobrien      if ((insn & opcode->mask) != opcode->opcode
15360484Sobrien	  || (opcode->flags & dialect) == 0)
15460484Sobrien	continue;
15560484Sobrien
15660484Sobrien      /* Make two passes over the operands.  First see if any of them
15760484Sobrien	 have extraction functions, and, if they do, make sure the
15860484Sobrien	 instruction is valid.  */
15960484Sobrien      invalid = 0;
16060484Sobrien      for (opindex = opcode->operands; *opindex != 0; opindex++)
16160484Sobrien	{
16260484Sobrien	  operand = powerpc_operands + *opindex;
16360484Sobrien	  if (operand->extract)
16489857Sobrien	    (*operand->extract) (insn, dialect, &invalid);
16560484Sobrien	}
16660484Sobrien      if (invalid)
16760484Sobrien	continue;
16860484Sobrien
16960484Sobrien      /* The instruction is valid.  */
17060484Sobrien      (*info->fprintf_func) (info->stream, "%s", opcode->name);
17160484Sobrien      if (opcode->operands[0] != 0)
17260484Sobrien	(*info->fprintf_func) (info->stream, "\t");
17360484Sobrien
17460484Sobrien      /* Now extract and print the operands.  */
17560484Sobrien      need_comma = 0;
17660484Sobrien      need_paren = 0;
17760484Sobrien      for (opindex = opcode->operands; *opindex != 0; opindex++)
17860484Sobrien	{
17960484Sobrien	  long value;
18060484Sobrien
18160484Sobrien	  operand = powerpc_operands + *opindex;
18260484Sobrien
18360484Sobrien	  /* Operands that are marked FAKE are simply ignored.  We
18460484Sobrien	     already made sure that the extract function considered
18560484Sobrien	     the instruction to be valid.  */
18660484Sobrien	  if ((operand->flags & PPC_OPERAND_FAKE) != 0)
18760484Sobrien	    continue;
18860484Sobrien
18960484Sobrien	  /* Extract the value from the instruction.  */
19060484Sobrien	  if (operand->extract)
19189857Sobrien	    value = (*operand->extract) (insn, dialect, (int *) NULL);
19260484Sobrien	  else
19360484Sobrien	    {
19460484Sobrien	      value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
19560484Sobrien	      if ((operand->flags & PPC_OPERAND_SIGNED) != 0
19660484Sobrien		  && (value & (1 << (operand->bits - 1))) != 0)
19760484Sobrien		value -= 1 << operand->bits;
19860484Sobrien	    }
19960484Sobrien
20060484Sobrien	  /* If the operand is optional, and the value is zero, don't
20160484Sobrien	     print anything.  */
20260484Sobrien	  if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
20360484Sobrien	      && (operand->flags & PPC_OPERAND_NEXT) == 0
20460484Sobrien	      && value == 0)
20560484Sobrien	    continue;
20660484Sobrien
20760484Sobrien	  if (need_comma)
20860484Sobrien	    {
20960484Sobrien	      (*info->fprintf_func) (info->stream, ",");
21060484Sobrien	      need_comma = 0;
21160484Sobrien	    }
21260484Sobrien
21360484Sobrien	  /* Print the operand as directed by the flags.  */
21460484Sobrien	  if ((operand->flags & PPC_OPERAND_GPR) != 0)
21560484Sobrien	    (*info->fprintf_func) (info->stream, "r%ld", value);
21660484Sobrien	  else if ((operand->flags & PPC_OPERAND_FPR) != 0)
21760484Sobrien	    (*info->fprintf_func) (info->stream, "f%ld", value);
21877298Sobrien	  else if ((operand->flags & PPC_OPERAND_VR) != 0)
21977298Sobrien	    (*info->fprintf_func) (info->stream, "v%ld", value);
22060484Sobrien	  else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
22160484Sobrien	    (*info->print_address_func) (memaddr + value, info);
22260484Sobrien	  else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
22360484Sobrien	    (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info);
22460484Sobrien	  else if ((operand->flags & PPC_OPERAND_CR) == 0
22560484Sobrien		   || (dialect & PPC_OPCODE_PPC) == 0)
22660484Sobrien	    (*info->fprintf_func) (info->stream, "%ld", value);
22760484Sobrien	  else
22860484Sobrien	    {
22960484Sobrien	      if (operand->bits == 3)
23060484Sobrien		(*info->fprintf_func) (info->stream, "cr%d", value);
23160484Sobrien	      else
23260484Sobrien		{
23360484Sobrien		  static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
23460484Sobrien		  int cr;
23560484Sobrien		  int cc;
23660484Sobrien
23760484Sobrien		  cr = value >> 2;
23860484Sobrien		  if (cr != 0)
23960484Sobrien		    (*info->fprintf_func) (info->stream, "4*cr%d", cr);
24060484Sobrien		  cc = value & 3;
24160484Sobrien		  if (cc != 0)
24260484Sobrien		    {
24360484Sobrien		      if (cr != 0)
24460484Sobrien			(*info->fprintf_func) (info->stream, "+");
24560484Sobrien		      (*info->fprintf_func) (info->stream, "%s", cbnames[cc]);
24660484Sobrien		    }
24760484Sobrien		}
24860484Sobrien	    }
24960484Sobrien
25060484Sobrien	  if (need_paren)
25160484Sobrien	    {
25260484Sobrien	      (*info->fprintf_func) (info->stream, ")");
25360484Sobrien	      need_paren = 0;
25460484Sobrien	    }
25560484Sobrien
25660484Sobrien	  if ((operand->flags & PPC_OPERAND_PARENS) == 0)
25760484Sobrien	    need_comma = 1;
25860484Sobrien	  else
25960484Sobrien	    {
26060484Sobrien	      (*info->fprintf_func) (info->stream, "(");
26160484Sobrien	      need_paren = 1;
26260484Sobrien	    }
26360484Sobrien	}
26460484Sobrien
26560484Sobrien      /* We have found and printed an instruction; return.  */
26660484Sobrien      return 4;
26760484Sobrien    }
26860484Sobrien
26960484Sobrien  /* We could not find a match.  */
27060484Sobrien  (*info->fprintf_func) (info->stream, ".long 0x%lx", insn);
27160484Sobrien
27260484Sobrien  return 4;
27360484Sobrien}
274104834Sobrien
275104834Sobrienvoid
276104834Sobrienprint_ppc_disassembler_options (FILE * stream)
277104834Sobrien{
278104834Sobrien  fprintf (stream, "\n\
279104834SobrienThe following PPC specific disassembler options are supported for use with\n\
280104834Sobrienthe -M switch:\n");
281104834Sobrien
282104834Sobrien  fprintf (stream, "  booke|booke32|booke64    Disassemble the BookE instructions\n");
283104834Sobrien  fprintf (stream, "  power4                   Disassemble the Power4 instructions\n");
284104834Sobrien  fprintf (stream, "  32                       Do not disassemble 64-bit instructions\n");
285104834Sobrien  fprintf (stream, "  64                       Allow disassembly of 64-bit instructions\n");
286104834Sobrien}
287