| Michael Ellerman | ae06e37 | 2006-11-23 00:46:43 +0100 | [diff] [blame] | 1 | /* Disassemble SPU instructions | 
|  | 2 |  | 
|  | 3 | Copyright 2006 Free Software Foundation, Inc. | 
|  | 4 |  | 
|  | 5 | This file is part of GDB, GAS, and the GNU binutils. | 
|  | 6 |  | 
|  | 7 | This program is free software; you can redistribute it and/or modify | 
|  | 8 | it under the terms of the GNU General Public License as published by | 
|  | 9 | the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | (at your option) any later version. | 
|  | 11 |  | 
|  | 12 | This program is distributed in the hope that it will be useful, | 
|  | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | GNU General Public License for more details. | 
|  | 16 |  | 
|  | 17 | You should have received a copy of the GNU General Public License along | 
|  | 18 | with this program; if not, write to the Free Software Foundation, Inc., | 
|  | 19 | 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */ | 
|  | 20 |  | 
|  | 21 | #include <linux/string.h> | 
|  | 22 | #include "nonstdio.h" | 
|  | 23 | #include "ansidecl.h" | 
|  | 24 | #include "spu.h" | 
| Michael Ellerman | e042604 | 2006-11-23 00:46:45 +0100 | [diff] [blame] | 25 | #include "dis-asm.h" | 
| Michael Ellerman | ae06e37 | 2006-11-23 00:46:43 +0100 | [diff] [blame] | 26 |  | 
|  | 27 | /* This file provides a disassembler function which uses | 
|  | 28 | the disassembler interface defined in dis-asm.h.   */ | 
|  | 29 |  | 
|  | 30 | extern const struct spu_opcode spu_opcodes[]; | 
|  | 31 | extern const int spu_num_opcodes; | 
|  | 32 |  | 
|  | 33 | #define SPU_DISASM_TBL_SIZE (1 << 11) | 
|  | 34 | static const struct spu_opcode *spu_disassemble_table[SPU_DISASM_TBL_SIZE]; | 
|  | 35 |  | 
|  | 36 | static void | 
|  | 37 | init_spu_disassemble (void) | 
|  | 38 | { | 
|  | 39 | int i; | 
|  | 40 |  | 
|  | 41 | /* If two instructions have the same opcode then we prefer the first | 
|  | 42 | * one.  In most cases it is just an alternate mnemonic. */ | 
|  | 43 | for (i = 0; i < spu_num_opcodes; i++) | 
|  | 44 | { | 
|  | 45 | int o = spu_opcodes[i].opcode; | 
|  | 46 | if (o >= SPU_DISASM_TBL_SIZE) | 
|  | 47 | continue; /* abort (); */ | 
|  | 48 | if (spu_disassemble_table[o] == 0) | 
|  | 49 | spu_disassemble_table[o] = &spu_opcodes[i]; | 
|  | 50 | } | 
|  | 51 | } | 
|  | 52 |  | 
|  | 53 | /* Determine the instruction from the 10 least significant bits. */ | 
|  | 54 | static const struct spu_opcode * | 
|  | 55 | get_index_for_opcode (unsigned int insn) | 
|  | 56 | { | 
|  | 57 | const struct spu_opcode *index; | 
|  | 58 | unsigned int opcode = insn >> (32-11); | 
|  | 59 |  | 
|  | 60 | /* Init the table.  This assumes that element 0/opcode 0 (currently | 
|  | 61 | * NOP) is always used */ | 
|  | 62 | if (spu_disassemble_table[0] == 0) | 
|  | 63 | init_spu_disassemble (); | 
|  | 64 |  | 
|  | 65 | if ((index = spu_disassemble_table[opcode & 0x780]) != 0 | 
|  | 66 | && index->insn_type == RRR) | 
|  | 67 | return index; | 
|  | 68 |  | 
|  | 69 | if ((index = spu_disassemble_table[opcode & 0x7f0]) != 0 | 
|  | 70 | && (index->insn_type == RI18 || index->insn_type == LBT)) | 
|  | 71 | return index; | 
|  | 72 |  | 
|  | 73 | if ((index = spu_disassemble_table[opcode & 0x7f8]) != 0 | 
|  | 74 | && index->insn_type == RI10) | 
|  | 75 | return index; | 
|  | 76 |  | 
|  | 77 | if ((index = spu_disassemble_table[opcode & 0x7fc]) != 0 | 
|  | 78 | && (index->insn_type == RI16)) | 
|  | 79 | return index; | 
|  | 80 |  | 
|  | 81 | if ((index = spu_disassemble_table[opcode & 0x7fe]) != 0 | 
|  | 82 | && (index->insn_type == RI8)) | 
|  | 83 | return index; | 
|  | 84 |  | 
|  | 85 | if ((index = spu_disassemble_table[opcode & 0x7ff]) != 0) | 
|  | 86 | return index; | 
|  | 87 |  | 
| Al Viro | 9340b0d | 2007-02-09 16:38:15 +0000 | [diff] [blame] | 88 | return NULL; | 
| Michael Ellerman | ae06e37 | 2006-11-23 00:46:43 +0100 | [diff] [blame] | 89 | } | 
|  | 90 |  | 
|  | 91 | /* Print a Spu instruction.  */ | 
|  | 92 |  | 
|  | 93 | int | 
|  | 94 | print_insn_spu (unsigned long insn, unsigned long memaddr) | 
|  | 95 | { | 
|  | 96 | int value; | 
|  | 97 | int hex_value; | 
|  | 98 | const struct spu_opcode *index; | 
|  | 99 | enum spu_insns tag; | 
|  | 100 |  | 
|  | 101 | index = get_index_for_opcode (insn); | 
|  | 102 |  | 
|  | 103 | if (index == 0) | 
|  | 104 | { | 
|  | 105 | printf(".long 0x%x", insn); | 
|  | 106 | } | 
|  | 107 | else | 
|  | 108 | { | 
|  | 109 | int i; | 
|  | 110 | int paren = 0; | 
|  | 111 | tag = (enum spu_insns)(index - spu_opcodes); | 
|  | 112 | printf("%s", index->mnemonic); | 
|  | 113 | if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED | 
|  | 114 | || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ | 
|  | 115 | || tag == M_SYNC || tag == M_HBR) | 
|  | 116 | { | 
|  | 117 | int fb = (insn >> (32-18)) & 0x7f; | 
|  | 118 | if (fb & 0x40) | 
|  | 119 | printf(tag == M_SYNC ? "c" : "p"); | 
|  | 120 | if (fb & 0x20) | 
|  | 121 | printf("d"); | 
|  | 122 | if (fb & 0x10) | 
|  | 123 | printf("e"); | 
|  | 124 | } | 
|  | 125 | if (index->arg[0] != 0) | 
|  | 126 | printf("\t"); | 
|  | 127 | hex_value = 0; | 
|  | 128 | for (i = 1;  i <= index->arg[0]; i++) | 
|  | 129 | { | 
|  | 130 | int arg = index->arg[i]; | 
|  | 131 | if (arg != A_P && !paren && i > 1) | 
|  | 132 | printf(","); | 
|  | 133 |  | 
|  | 134 | switch (arg) | 
|  | 135 | { | 
|  | 136 | case A_T: | 
|  | 137 | printf("$%d", | 
|  | 138 | DECODE_INSN_RT (insn)); | 
|  | 139 | break; | 
|  | 140 | case A_A: | 
|  | 141 | printf("$%d", | 
|  | 142 | DECODE_INSN_RA (insn)); | 
|  | 143 | break; | 
|  | 144 | case A_B: | 
|  | 145 | printf("$%d", | 
|  | 146 | DECODE_INSN_RB (insn)); | 
|  | 147 | break; | 
|  | 148 | case A_C: | 
|  | 149 | printf("$%d", | 
|  | 150 | DECODE_INSN_RC (insn)); | 
|  | 151 | break; | 
|  | 152 | case A_S: | 
|  | 153 | printf("$sp%d", | 
|  | 154 | DECODE_INSN_RA (insn)); | 
|  | 155 | break; | 
|  | 156 | case A_H: | 
|  | 157 | printf("$ch%d", | 
|  | 158 | DECODE_INSN_RA (insn)); | 
|  | 159 | break; | 
|  | 160 | case A_P: | 
|  | 161 | paren++; | 
|  | 162 | printf("("); | 
|  | 163 | break; | 
|  | 164 | case A_U7A: | 
|  | 165 | printf("%d", | 
|  | 166 | 173 - DECODE_INSN_U8 (insn)); | 
|  | 167 | break; | 
|  | 168 | case A_U7B: | 
|  | 169 | printf("%d", | 
|  | 170 | 155 - DECODE_INSN_U8 (insn)); | 
|  | 171 | break; | 
|  | 172 | case A_S3: | 
|  | 173 | case A_S6: | 
|  | 174 | case A_S7: | 
|  | 175 | case A_S7N: | 
|  | 176 | case A_U3: | 
|  | 177 | case A_U5: | 
|  | 178 | case A_U6: | 
|  | 179 | case A_U7: | 
|  | 180 | hex_value = DECODE_INSN_I7 (insn); | 
|  | 181 | printf("%d", hex_value); | 
|  | 182 | break; | 
|  | 183 | case A_S11: | 
|  | 184 | print_address(memaddr + DECODE_INSN_I9a (insn) * 4); | 
|  | 185 | break; | 
|  | 186 | case A_S11I: | 
|  | 187 | print_address(memaddr + DECODE_INSN_I9b (insn) * 4); | 
|  | 188 | break; | 
|  | 189 | case A_S10: | 
|  | 190 | case A_S10B: | 
|  | 191 | hex_value = DECODE_INSN_I10 (insn); | 
|  | 192 | printf("%d", hex_value); | 
|  | 193 | break; | 
|  | 194 | case A_S14: | 
|  | 195 | hex_value = DECODE_INSN_I10 (insn) * 16; | 
|  | 196 | printf("%d", hex_value); | 
|  | 197 | break; | 
|  | 198 | case A_S16: | 
|  | 199 | hex_value = DECODE_INSN_I16 (insn); | 
|  | 200 | printf("%d", hex_value); | 
|  | 201 | break; | 
|  | 202 | case A_X16: | 
|  | 203 | hex_value = DECODE_INSN_U16 (insn); | 
|  | 204 | printf("%u", hex_value); | 
|  | 205 | break; | 
|  | 206 | case A_R18: | 
|  | 207 | value = DECODE_INSN_I16 (insn) * 4; | 
|  | 208 | if (value == 0) | 
|  | 209 | printf("%d", value); | 
|  | 210 | else | 
|  | 211 | { | 
|  | 212 | hex_value = memaddr + value; | 
|  | 213 | print_address(hex_value & 0x3ffff); | 
|  | 214 | } | 
|  | 215 | break; | 
|  | 216 | case A_S18: | 
|  | 217 | value = DECODE_INSN_U16 (insn) * 4; | 
|  | 218 | if (value == 0) | 
|  | 219 | printf("%d", value); | 
|  | 220 | else | 
|  | 221 | print_address(value); | 
|  | 222 | break; | 
|  | 223 | case A_U18: | 
|  | 224 | value = DECODE_INSN_U18 (insn); | 
|  | 225 | if (value == 0 || 1) | 
|  | 226 | { | 
|  | 227 | hex_value = value; | 
|  | 228 | printf("%u", value); | 
|  | 229 | } | 
|  | 230 | else | 
|  | 231 | print_address(value); | 
|  | 232 | break; | 
|  | 233 | case A_U14: | 
|  | 234 | hex_value = DECODE_INSN_U14 (insn); | 
|  | 235 | printf("%u", hex_value); | 
|  | 236 | break; | 
|  | 237 | } | 
|  | 238 | if (arg != A_P && paren) | 
|  | 239 | { | 
|  | 240 | printf(")"); | 
|  | 241 | paren--; | 
|  | 242 | } | 
|  | 243 | } | 
|  | 244 | if (hex_value > 16) | 
|  | 245 | printf("\t# %x", hex_value); | 
|  | 246 | } | 
|  | 247 | return 4; | 
|  | 248 | } |