ppc-dis.c (8204B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* ppc-dis.c -- Disassemble PowerPC instructions 3 Copyright (C) 1994-2016 Free Software Foundation, Inc. 4 Written by Ian Lance Taylor, Cygnus Support 5 6This file is part of GDB, GAS, and the GNU binutils. 7 8 */ 9 10#include <asm/cputable.h> 11#include <asm/cpu_has_feature.h> 12#include "nonstdio.h" 13#include "ansidecl.h" 14#include "ppc.h" 15#include "dis-asm.h" 16 17/* This file provides several disassembler functions, all of which use 18 the disassembler interface defined in dis-asm.h. Several functions 19 are provided because this file handles disassembly for the PowerPC 20 in both big and little endian mode and also for the POWER (RS/6000) 21 chip. */ 22 23/* Extract the operand value from the PowerPC or POWER instruction. */ 24 25static long 26operand_value_powerpc (const struct powerpc_operand *operand, 27 unsigned long insn, ppc_cpu_t dialect) 28{ 29 long value; 30 int invalid; 31 /* Extract the value from the instruction. */ 32 if (operand->extract) 33 value = (*operand->extract) (insn, dialect, &invalid); 34 else 35 { 36 if (operand->shift >= 0) 37 value = (insn >> operand->shift) & operand->bitm; 38 else 39 value = (insn << -operand->shift) & operand->bitm; 40 if ((operand->flags & PPC_OPERAND_SIGNED) != 0) 41 { 42 /* BITM is always some number of zeros followed by some 43 number of ones, followed by some number of zeros. */ 44 unsigned long top = operand->bitm; 45 /* top & -top gives the rightmost 1 bit, so this 46 fills in any trailing zeros. */ 47 top |= (top & -top) - 1; 48 top &= ~(top >> 1); 49 value = (value ^ top) - top; 50 } 51 } 52 53 return value; 54} 55 56/* Determine whether the optional operand(s) should be printed. */ 57 58static int 59skip_optional_operands (const unsigned char *opindex, 60 unsigned long insn, ppc_cpu_t dialect) 61{ 62 const struct powerpc_operand *operand; 63 64 for (; *opindex != 0; opindex++) 65 { 66 operand = &powerpc_operands[*opindex]; 67 if ((operand->flags & PPC_OPERAND_NEXT) != 0 68 || ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 69 && operand_value_powerpc (operand, insn, dialect) != 70 ppc_optional_operand_value (operand))) 71 return 0; 72 } 73 74 return 1; 75} 76 77/* Find a match for INSN in the opcode table, given machine DIALECT. 78 A DIALECT of -1 is special, matching all machine opcode variations. */ 79 80static const struct powerpc_opcode * 81lookup_powerpc (unsigned long insn, ppc_cpu_t dialect) 82{ 83 const struct powerpc_opcode *opcode; 84 const struct powerpc_opcode *opcode_end; 85 86 opcode_end = powerpc_opcodes + powerpc_num_opcodes; 87 /* Find the first match in the opcode table for this major opcode. */ 88 for (opcode = powerpc_opcodes; opcode < opcode_end; ++opcode) 89 { 90 const unsigned char *opindex; 91 const struct powerpc_operand *operand; 92 int invalid; 93 94 if ((insn & opcode->mask) != opcode->opcode 95 || (dialect != (ppc_cpu_t) -1 96 && ((opcode->flags & dialect) == 0 97 || (opcode->deprecated & dialect) != 0))) 98 continue; 99 100 /* Check validity of operands. */ 101 invalid = 0; 102 for (opindex = opcode->operands; *opindex != 0; opindex++) 103 { 104 operand = powerpc_operands + *opindex; 105 if (operand->extract) 106 (*operand->extract) (insn, dialect, &invalid); 107 } 108 if (invalid) 109 continue; 110 111 return opcode; 112 } 113 114 return NULL; 115} 116 117/* Print a PowerPC or POWER instruction. */ 118 119int print_insn_powerpc (unsigned long insn, unsigned long memaddr) 120{ 121 const struct powerpc_opcode *opcode; 122 bool insn_is_short; 123 ppc_cpu_t dialect; 124 125 dialect = PPC_OPCODE_PPC | PPC_OPCODE_COMMON 126 | PPC_OPCODE_64 | PPC_OPCODE_POWER4 | PPC_OPCODE_ALTIVEC; 127 128 if (cpu_has_feature(CPU_FTRS_POWER5)) 129 dialect |= PPC_OPCODE_POWER5; 130 131 if (cpu_has_feature(CPU_FTRS_CELL)) 132 dialect |= (PPC_OPCODE_CELL | PPC_OPCODE_ALTIVEC); 133 134 if (cpu_has_feature(CPU_FTRS_POWER6)) 135 dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_ALTIVEC); 136 137 if (cpu_has_feature(CPU_FTRS_POWER7)) 138 dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 139 | PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX); 140 141 if (cpu_has_feature(CPU_FTRS_POWER8)) 142 dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 143 | PPC_OPCODE_POWER8 | PPC_OPCODE_HTM 144 | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 | PPC_OPCODE_VSX); 145 146 if (cpu_has_feature(CPU_FTRS_POWER9)) 147 dialect |= (PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7 148 | PPC_OPCODE_POWER8 | PPC_OPCODE_POWER9 | PPC_OPCODE_HTM 149 | PPC_OPCODE_ALTIVEC | PPC_OPCODE_ALTIVEC2 150 | PPC_OPCODE_VSX | PPC_OPCODE_VSX3); 151 152 /* Get the major opcode of the insn. */ 153 opcode = NULL; 154 insn_is_short = false; 155 156 if (opcode == NULL) 157 opcode = lookup_powerpc (insn, dialect); 158 if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0) 159 opcode = lookup_powerpc (insn, (ppc_cpu_t) -1); 160 161 if (opcode != NULL) 162 { 163 const unsigned char *opindex; 164 const struct powerpc_operand *operand; 165 int need_comma; 166 int need_paren; 167 int skip_optional; 168 169 if (opcode->operands[0] != 0) 170 printf("%-7s ", opcode->name); 171 else 172 printf("%s", opcode->name); 173 174 if (insn_is_short) 175 /* The operands will be fetched out of the 16-bit instruction. */ 176 insn >>= 16; 177 178 /* Now extract and print the operands. */ 179 need_comma = 0; 180 need_paren = 0; 181 skip_optional = -1; 182 for (opindex = opcode->operands; *opindex != 0; opindex++) 183 { 184 long value; 185 186 operand = powerpc_operands + *opindex; 187 188 /* Operands that are marked FAKE are simply ignored. We 189 already made sure that the extract function considered 190 the instruction to be valid. */ 191 if ((operand->flags & PPC_OPERAND_FAKE) != 0) 192 continue; 193 194 /* If all of the optional operands have the value zero, 195 then don't print any of them. */ 196 if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) 197 { 198 if (skip_optional < 0) 199 skip_optional = skip_optional_operands (opindex, insn, 200 dialect); 201 if (skip_optional) 202 continue; 203 } 204 205 value = operand_value_powerpc (operand, insn, dialect); 206 207 if (need_comma) 208 { 209 printf(","); 210 need_comma = 0; 211 } 212 213 /* Print the operand as directed by the flags. */ 214 if ((operand->flags & PPC_OPERAND_GPR) != 0 215 || ((operand->flags & PPC_OPERAND_GPR_0) != 0 && value != 0)) 216 printf("r%ld", value); 217 else if ((operand->flags & PPC_OPERAND_FPR) != 0) 218 printf("f%ld", value); 219 else if ((operand->flags & PPC_OPERAND_VR) != 0) 220 printf("v%ld", value); 221 else if ((operand->flags & PPC_OPERAND_VSR) != 0) 222 printf("vs%ld", value); 223 else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) 224 print_address(memaddr + value); 225 else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) 226 print_address(value & 0xffffffff); 227 else if ((operand->flags & PPC_OPERAND_FSL) != 0) 228 printf("fsl%ld", value); 229 else if ((operand->flags & PPC_OPERAND_FCR) != 0) 230 printf("fcr%ld", value); 231 else if ((operand->flags & PPC_OPERAND_UDI) != 0) 232 printf("%ld", value); 233 else if ((operand->flags & PPC_OPERAND_CR_REG) != 0 234 && (((dialect & PPC_OPCODE_PPC) != 0) 235 || ((dialect & PPC_OPCODE_VLE) != 0))) 236 printf("cr%ld", value); 237 else if (((operand->flags & PPC_OPERAND_CR_BIT) != 0) 238 && (((dialect & PPC_OPCODE_PPC) != 0) 239 || ((dialect & PPC_OPCODE_VLE) != 0))) 240 { 241 static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; 242 int cr; 243 int cc; 244 245 cr = value >> 2; 246 if (cr != 0) 247 printf("4*cr%d+", cr); 248 cc = value & 3; 249 printf("%s", cbnames[cc]); 250 } 251 else 252 printf("%d", (int) value); 253 254 if (need_paren) 255 { 256 printf(")"); 257 need_paren = 0; 258 } 259 260 if ((operand->flags & PPC_OPERAND_PARENS) == 0) 261 need_comma = 1; 262 else 263 { 264 printf("("); 265 need_paren = 1; 266 } 267 } 268 269 /* We have found and printed an instruction. 270 If it was a short VLE instruction we have more to do. */ 271 if (insn_is_short) 272 { 273 memaddr += 2; 274 return 2; 275 } 276 else 277 /* Otherwise, return. */ 278 return 4; 279 } 280 281 /* We could not find a match. */ 282 printf(".long 0x%lx", insn); 283 284 return 4; 285}