2005-04-17 02:20:36 +04:00
/* ppc-dis.c -- Disassemble PowerPC instructions
2006-11-23 02:46:47 +03:00
Copyright 1994 , 1995 , 2000 , 2001 , 2002 , 2003 , 2004 , 2005 , 2006
Free Software Foundation , Inc .
2005-04-17 02:20:36 +04:00
Written by Ian Lance Taylor , Cygnus Support
This file is part of GDB , GAS , and the GNU binutils .
GDB , GAS , and the GNU binutils are free software ; you can redistribute
them and / or modify them under the terms of the GNU General Public
License as published by the Free Software Foundation ; either version
2 , or ( at your option ) any later version .
GDB , GAS , and the GNU binutils are distributed in the hope that they
will be useful , but WITHOUT ANY WARRANTY ; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this file ; see the file COPYING . If not , write to the Free
2006-11-23 02:46:47 +03:00
Software Foundation , 51 Franklin Street - Fifth Floor , Boston , MA 02110 - 1301 , USA . */
2005-04-17 02:20:36 +04:00
2006-11-23 02:46:47 +03:00
# include <asm/cputable.h>
2005-04-17 02:20:36 +04:00
# include "nonstdio.h"
# include "ansidecl.h"
# include "ppc.h"
2006-11-23 02:46:45 +03:00
# include "dis-asm.h"
2005-04-17 02:20:36 +04:00
/* Print a PowerPC or POWER instruction. */
int
2006-11-23 02:46:42 +03:00
print_insn_powerpc ( unsigned long insn , unsigned long memaddr )
2005-04-17 02:20:36 +04:00
{
const struct powerpc_opcode * opcode ;
const struct powerpc_opcode * opcode_end ;
unsigned long op ;
2006-11-23 02:46:42 +03:00
int dialect ;
2005-04-17 02:20:36 +04:00
2006-11-23 02:46:42 +03:00
dialect = PPC_OPCODE_PPC | PPC_OPCODE_CLASSIC | PPC_OPCODE_COMMON
2005-04-17 02:20:36 +04:00
| PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_ALTIVEC ;
2006-11-23 02:46:47 +03:00
if ( cpu_has_feature ( CPU_FTRS_POWER5 ) )
dialect | = PPC_OPCODE_POWER5 ;
if ( cpu_has_feature ( CPU_FTRS_CELL ) )
dialect | = PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC ;
if ( cpu_has_feature ( CPU_FTRS_POWER6 ) )
dialect | = PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC ;
2005-04-17 02:20:36 +04:00
/* Get the major opcode of the instruction. */
op = PPC_OP ( insn ) ;
/* Find the first match in the opcode table. We could speed this up
a bit by doing a binary search on the major opcode . */
opcode_end = powerpc_opcodes + powerpc_num_opcodes ;
again :
for ( opcode = powerpc_opcodes ; opcode < opcode_end ; opcode + + )
{
unsigned long table_op ;
const unsigned char * opindex ;
const struct powerpc_operand * operand ;
int invalid ;
int need_comma ;
int need_paren ;
table_op = PPC_OP ( opcode - > opcode ) ;
if ( op < table_op )
break ;
if ( op > table_op )
continue ;
if ( ( insn & opcode - > mask ) ! = opcode - > opcode
| | ( opcode - > flags & dialect ) = = 0 )
continue ;
/* Make two passes over the operands. First see if any of them
have extraction functions , and , if they do , make sure the
instruction is valid . */
invalid = 0 ;
for ( opindex = opcode - > operands ; * opindex ! = 0 ; opindex + + )
{
operand = powerpc_operands + * opindex ;
if ( operand - > extract )
( * operand - > extract ) ( insn , dialect , & invalid ) ;
}
if ( invalid )
continue ;
/* The instruction is valid. */
printf ( " %s " , opcode - > name ) ;
if ( opcode - > operands [ 0 ] ! = 0 )
printf ( " \t " ) ;
/* Now extract and print the operands. */
need_comma = 0 ;
need_paren = 0 ;
for ( opindex = opcode - > operands ; * opindex ! = 0 ; opindex + + )
{
long value ;
operand = powerpc_operands + * opindex ;
/* Operands that are marked FAKE are simply ignored. We
already made sure that the extract function considered
the instruction to be valid . */
if ( ( operand - > flags & PPC_OPERAND_FAKE ) ! = 0 )
continue ;
/* Extract the value from the instruction. */
if ( operand - > extract )
value = ( * operand - > extract ) ( insn , dialect , & invalid ) ;
else
{
value = ( insn > > operand - > shift ) & ( ( 1 < < operand - > bits ) - 1 ) ;
if ( ( operand - > flags & PPC_OPERAND_SIGNED ) ! = 0
& & ( value & ( 1 < < ( operand - > bits - 1 ) ) ) ! = 0 )
value - = 1 < < operand - > bits ;
}
/* If the operand is optional, and the value is zero, don't
print anything . */
if ( ( operand - > flags & PPC_OPERAND_OPTIONAL ) ! = 0
& & ( operand - > flags & PPC_OPERAND_NEXT ) = = 0
& & value = = 0 )
continue ;
if ( need_comma )
{
printf ( " , " ) ;
need_comma = 0 ;
}
/* Print the operand as directed by the flags. */
2006-11-23 02:46:47 +03:00
if ( ( operand - > flags & PPC_OPERAND_GPR ) ! = 0
| | ( ( operand - > flags & PPC_OPERAND_GPR_0 ) ! = 0 & & value ! = 0 ) )
2005-04-17 02:20:36 +04:00
printf ( " r%ld " , value ) ;
else if ( ( operand - > flags & PPC_OPERAND_FPR ) ! = 0 )
printf ( " f%ld " , value ) ;
else if ( ( operand - > flags & PPC_OPERAND_VR ) ! = 0 )
printf ( " v%ld " , value ) ;
else if ( ( operand - > flags & PPC_OPERAND_RELATIVE ) ! = 0 )
print_address ( memaddr + value ) ;
else if ( ( operand - > flags & PPC_OPERAND_ABSOLUTE ) ! = 0 )
print_address ( value & 0xffffffff ) ;
else if ( ( operand - > flags & PPC_OPERAND_CR ) = = 0
| | ( dialect & PPC_OPCODE_PPC ) = = 0 )
printf ( " %ld " , value ) ;
else
{
if ( operand - > bits = = 3 )
2006-11-23 02:46:47 +03:00
printf ( " cr%ld " , value ) ;
2005-04-17 02:20:36 +04:00
else
{
static const char * cbnames [ 4 ] = { " lt " , " gt " , " eq " , " so " } ;
int cr ;
int cc ;
cr = value > > 2 ;
if ( cr ! = 0 )
printf ( " 4*cr%d+ " , cr ) ;
cc = value & 3 ;
printf ( " %s " , cbnames [ cc ] ) ;
}
}
if ( need_paren )
{
printf ( " ) " ) ;
need_paren = 0 ;
}
if ( ( operand - > flags & PPC_OPERAND_PARENS ) = = 0 )
need_comma = 1 ;
else
{
printf ( " ( " ) ;
need_paren = 1 ;
}
}
/* We have found and printed an instruction; return. */
return 4 ;
}
if ( ( dialect & PPC_OPCODE_ANY ) ! = 0 )
{
dialect = ~ PPC_OPCODE_ANY ;
goto again ;
}
/* We could not find a match. */
printf ( " .long 0x%lx " , insn ) ;
return 4 ;
}