1214571Sdim/* Disassemble SPU instructions 2214571Sdim 3214571Sdim Copyright 2006 Free Software Foundation, Inc. 4214571Sdim 5214571Sdim This file is part of GDB, GAS, and the GNU binutils. 6214571Sdim 7214571Sdim This program is free software; you can redistribute it and/or modify 8214571Sdim it under the terms of the GNU General Public License as published by 9214571Sdim the Free Software Foundation; either version 2 of the License, or 10214571Sdim (at your option) any later version. 11214571Sdim 12214571Sdim This program is distributed in the hope that it will be useful, 13214571Sdim but WITHOUT ANY WARRANTY; without even the implied warranty of 14214571Sdim MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15214571Sdim GNU General Public License for more details. 16214571Sdim 17214571Sdim You should have received a copy of the GNU General Public License along 18214571Sdim with this program; if not, write to the Free Software Foundation, Inc., 19214571Sdim 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 20214571Sdim 21214571Sdim#include <stdio.h> 22214571Sdim#include "sysdep.h" 23214571Sdim#include "dis-asm.h" 24214571Sdim#include "opcode/spu.h" 25214571Sdim 26214571Sdim/* This file provides a disassembler function which uses 27214571Sdim the disassembler interface defined in dis-asm.h. */ 28214571Sdim 29214571Sdimextern const struct spu_opcode spu_opcodes[]; 30214571Sdimextern const int spu_num_opcodes; 31214571Sdim 32214571Sdimstatic const struct spu_opcode *spu_disassemble_table[(1<<11)]; 33214571Sdim 34214571Sdimstatic void 35214571Sdiminit_spu_disassemble (void) 36214571Sdim{ 37214571Sdim int i; 38214571Sdim 39214571Sdim /* If two instructions have the same opcode then we prefer the first 40214571Sdim * one. In most cases it is just an alternate mnemonic. */ 41214571Sdim for (i = 0; i < spu_num_opcodes; i++) 42214571Sdim { 43214571Sdim int o = spu_opcodes[i].opcode; 44214571Sdim if (o >= (1 << 11)) 45214571Sdim abort (); 46214571Sdim if (spu_disassemble_table[o] == 0) 47214571Sdim spu_disassemble_table[o] = &spu_opcodes[i]; 48214571Sdim } 49214571Sdim} 50214571Sdim 51214571Sdim/* Determine the instruction from the 10 least significant bits. */ 52214571Sdimstatic const struct spu_opcode * 53214571Sdimget_index_for_opcode (unsigned int insn) 54214571Sdim{ 55214571Sdim const struct spu_opcode *index; 56214571Sdim unsigned int opcode = insn >> (32-11); 57214571Sdim 58214571Sdim /* Init the table. This assumes that element 0/opcode 0 (currently 59214571Sdim * NOP) is always used */ 60214571Sdim if (spu_disassemble_table[0] == 0) 61214571Sdim init_spu_disassemble (); 62214571Sdim 63214571Sdim if ((index = spu_disassemble_table[opcode & 0x780]) != 0 64214571Sdim && index->insn_type == RRR) 65214571Sdim return index; 66214571Sdim 67214571Sdim if ((index = spu_disassemble_table[opcode & 0x7f0]) != 0 68214571Sdim && (index->insn_type == RI18 || index->insn_type == LBT)) 69214571Sdim return index; 70214571Sdim 71214571Sdim if ((index = spu_disassemble_table[opcode & 0x7f8]) != 0 72214571Sdim && index->insn_type == RI10) 73214571Sdim return index; 74214571Sdim 75214571Sdim if ((index = spu_disassemble_table[opcode & 0x7fc]) != 0 76214571Sdim && (index->insn_type == RI16)) 77214571Sdim return index; 78214571Sdim 79214571Sdim if ((index = spu_disassemble_table[opcode & 0x7fe]) != 0 80214571Sdim && (index->insn_type == RI8)) 81214571Sdim return index; 82214571Sdim 83214571Sdim if ((index = spu_disassemble_table[opcode & 0x7ff]) != 0) 84214571Sdim return index; 85214571Sdim 86214571Sdim return 0; 87214571Sdim} 88214571Sdim 89214571Sdim/* Print a Spu instruction. */ 90214571Sdim 91214571Sdimint 92214571Sdimprint_insn_spu (bfd_vma memaddr, struct disassemble_info *info) 93214571Sdim{ 94214571Sdim bfd_byte buffer[4]; 95214571Sdim int value; 96214571Sdim int hex_value; 97214571Sdim int status; 98214571Sdim unsigned int insn; 99214571Sdim const struct spu_opcode *index; 100214571Sdim enum spu_insns tag; 101214571Sdim 102214571Sdim status = (*info->read_memory_func) (memaddr, buffer, 4, info); 103214571Sdim if (status != 0) 104214571Sdim { 105214571Sdim (*info->memory_error_func) (status, memaddr, info); 106214571Sdim return -1; 107214571Sdim } 108214571Sdim 109214571Sdim insn = bfd_getb32 (buffer); 110214571Sdim 111214571Sdim index = get_index_for_opcode (insn); 112214571Sdim 113214571Sdim if (index == 0) 114214571Sdim { 115214571Sdim (*info->fprintf_func) (info->stream, ".long 0x%x", insn); 116214571Sdim } 117214571Sdim else 118214571Sdim { 119214571Sdim int i; 120214571Sdim int paren = 0; 121214571Sdim tag = (enum spu_insns)(index - spu_opcodes); 122214571Sdim (*info->fprintf_func) (info->stream, "%s", index->mnemonic); 123214571Sdim if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED 124214571Sdim || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ 125214571Sdim || tag == M_SYNC || tag == M_HBR) 126214571Sdim { 127214571Sdim int fb = (insn >> (32-18)) & 0x7f; 128214571Sdim if (fb & 0x40) 129214571Sdim (*info->fprintf_func) (info->stream, tag == M_SYNC ? "c" : "p"); 130214571Sdim if (fb & 0x20) 131214571Sdim (*info->fprintf_func) (info->stream, "d"); 132214571Sdim if (fb & 0x10) 133214571Sdim (*info->fprintf_func) (info->stream, "e"); 134214571Sdim } 135214571Sdim if (index->arg[0] != 0) 136214571Sdim (*info->fprintf_func) (info->stream, "\t"); 137214571Sdim hex_value = 0; 138214571Sdim for (i = 1; i <= index->arg[0]; i++) 139214571Sdim { 140214571Sdim int arg = index->arg[i]; 141214571Sdim if (arg != A_P && !paren && i > 1) 142214571Sdim (*info->fprintf_func) (info->stream, ","); 143214571Sdim 144214571Sdim switch (arg) 145214571Sdim { 146214571Sdim case A_T: 147214571Sdim (*info->fprintf_func) (info->stream, "$%d", 148214571Sdim DECODE_INSN_RT (insn)); 149214571Sdim break; 150214571Sdim case A_A: 151214571Sdim (*info->fprintf_func) (info->stream, "$%d", 152214571Sdim DECODE_INSN_RA (insn)); 153214571Sdim break; 154214571Sdim case A_B: 155214571Sdim (*info->fprintf_func) (info->stream, "$%d", 156214571Sdim DECODE_INSN_RB (insn)); 157214571Sdim break; 158214571Sdim case A_C: 159214571Sdim (*info->fprintf_func) (info->stream, "$%d", 160214571Sdim DECODE_INSN_RC (insn)); 161214571Sdim break; 162214571Sdim case A_S: 163214571Sdim (*info->fprintf_func) (info->stream, "$sp%d", 164214571Sdim DECODE_INSN_RA (insn)); 165214571Sdim break; 166214571Sdim case A_H: 167214571Sdim (*info->fprintf_func) (info->stream, "$ch%d", 168214571Sdim DECODE_INSN_RA (insn)); 169214571Sdim break; 170214571Sdim case A_P: 171214571Sdim paren++; 172214571Sdim (*info->fprintf_func) (info->stream, "("); 173214571Sdim break; 174214571Sdim case A_U7A: 175214571Sdim (*info->fprintf_func) (info->stream, "%d", 176214571Sdim 173 - DECODE_INSN_U8 (insn)); 177214571Sdim break; 178214571Sdim case A_U7B: 179214571Sdim (*info->fprintf_func) (info->stream, "%d", 180214571Sdim 155 - DECODE_INSN_U8 (insn)); 181214571Sdim break; 182214571Sdim case A_S3: 183214571Sdim case A_S6: 184214571Sdim case A_S7: 185214571Sdim case A_S7N: 186214571Sdim case A_U3: 187214571Sdim case A_U5: 188214571Sdim case A_U6: 189214571Sdim case A_U7: 190214571Sdim hex_value = DECODE_INSN_I7 (insn); 191214571Sdim (*info->fprintf_func) (info->stream, "%d", hex_value); 192214571Sdim break; 193214571Sdim case A_S11: 194214571Sdim (*info->print_address_func) (memaddr + DECODE_INSN_I9a (insn) * 4, 195214571Sdim info); 196214571Sdim break; 197214571Sdim case A_S11I: 198214571Sdim (*info->print_address_func) (memaddr + DECODE_INSN_I9b (insn) * 4, 199214571Sdim info); 200214571Sdim break; 201214571Sdim case A_S10: 202214571Sdim case A_S10B: 203214571Sdim hex_value = DECODE_INSN_I10 (insn); 204214571Sdim (*info->fprintf_func) (info->stream, "%d", hex_value); 205214571Sdim break; 206214571Sdim case A_S14: 207214571Sdim hex_value = DECODE_INSN_I10 (insn) * 16; 208214571Sdim (*info->fprintf_func) (info->stream, "%d", hex_value); 209214571Sdim break; 210214571Sdim case A_S16: 211214571Sdim hex_value = DECODE_INSN_I16 (insn); 212214571Sdim (*info->fprintf_func) (info->stream, "%d", hex_value); 213214571Sdim break; 214214571Sdim case A_X16: 215214571Sdim hex_value = DECODE_INSN_U16 (insn); 216214571Sdim (*info->fprintf_func) (info->stream, "%u", hex_value); 217214571Sdim break; 218214571Sdim case A_R18: 219214571Sdim value = DECODE_INSN_I16 (insn) * 4; 220214571Sdim if (value == 0) 221214571Sdim (*info->fprintf_func) (info->stream, "%d", value); 222214571Sdim else 223214571Sdim { 224214571Sdim hex_value = memaddr + value; 225214571Sdim (*info->print_address_func) (hex_value & 0x3ffff, info); 226214571Sdim } 227214571Sdim break; 228214571Sdim case A_S18: 229214571Sdim value = DECODE_INSN_U16 (insn) * 4; 230214571Sdim if (value == 0) 231214571Sdim (*info->fprintf_func) (info->stream, "%d", value); 232214571Sdim else 233214571Sdim (*info->print_address_func) (value, info); 234214571Sdim break; 235214571Sdim case A_U18: 236214571Sdim value = DECODE_INSN_U18 (insn); 237214571Sdim if (value == 0 || !(*info->symbol_at_address_func)(0, info)) 238214571Sdim { 239214571Sdim hex_value = value; 240214571Sdim (*info->fprintf_func) (info->stream, "%u", value); 241214571Sdim } 242214571Sdim else 243214571Sdim (*info->print_address_func) (value, info); 244214571Sdim break; 245214571Sdim case A_U14: 246214571Sdim hex_value = DECODE_INSN_U14 (insn); 247214571Sdim (*info->fprintf_func) (info->stream, "%u", hex_value); 248214571Sdim break; 249214571Sdim } 250214571Sdim if (arg != A_P && paren) 251214571Sdim { 252214571Sdim (*info->fprintf_func) (info->stream, ")"); 253214571Sdim paren--; 254214571Sdim } 255214571Sdim } 256214571Sdim if (hex_value > 16) 257214571Sdim (*info->fprintf_func) (info->stream, "\t# %x", hex_value); 258214571Sdim } 259214571Sdim return 4; 260214571Sdim} 261