/src/binutils-gdb/opcodes/spu-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Disassemble SPU instructions |
2 | | |
3 | | Copyright (C) 2006-2025 Free Software Foundation, Inc. |
4 | | |
5 | | This file is part of the GNU opcodes library. |
6 | | |
7 | | This library 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 3, or (at your option) |
10 | | any later version. |
11 | | |
12 | | It is distributed in the hope that it will be useful, but WITHOUT |
13 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
14 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
15 | | License for more details. |
16 | | |
17 | | You should have received a copy of the GNU General Public License |
18 | | along with this file; see the file COPYING. If not, write to the |
19 | | Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, |
20 | | MA 02110-1301, USA. */ |
21 | | |
22 | | #include "sysdep.h" |
23 | | #include <stdio.h> |
24 | | #include "disassemble.h" |
25 | | #include "opcode/spu.h" |
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 | | static const struct spu_opcode *spu_disassemble_table[(1<<11)]; |
34 | | |
35 | | static void |
36 | | init_spu_disassemble (void) |
37 | 2 | { |
38 | 2 | int i; |
39 | | |
40 | | /* If two instructions have the same opcode then we prefer the first |
41 | | * one. In most cases it is just an alternate mnemonic. */ |
42 | 488 | for (i = 0; i < spu_num_opcodes; i++) |
43 | 486 | { |
44 | 486 | int o = spu_opcodes[i].opcode; |
45 | 486 | if (o >= (1 << 11)) |
46 | 0 | abort (); |
47 | 486 | if (spu_disassemble_table[o] == 0) |
48 | 398 | spu_disassemble_table[o] = &spu_opcodes[i]; |
49 | 486 | } |
50 | 2 | } |
51 | | |
52 | | /* Determine the instruction from the 10 least significant bits. */ |
53 | | static const struct spu_opcode * |
54 | | get_index_for_opcode (unsigned int insn) |
55 | 438k | { |
56 | 438k | const struct spu_opcode *op_index; |
57 | 438k | unsigned int opcode = insn >> (32-11); |
58 | | |
59 | | /* Init the table. This assumes that element 0/opcode 0 (currently |
60 | | * NOP) is always used */ |
61 | 438k | if (spu_disassemble_table[0] == 0) |
62 | 2 | init_spu_disassemble (); |
63 | | |
64 | 438k | if ((op_index = spu_disassemble_table[opcode & 0x780]) != 0 |
65 | 438k | && op_index->insn_type == RRR) |
66 | 101k | return op_index; |
67 | | |
68 | 336k | if ((op_index = spu_disassemble_table[opcode & 0x7f0]) != 0 |
69 | 336k | && (op_index->insn_type == RI18 || op_index->insn_type == LBT)) |
70 | 15.2k | return op_index; |
71 | | |
72 | 321k | if ((op_index = spu_disassemble_table[opcode & 0x7f8]) != 0 |
73 | 321k | && op_index->insn_type == RI10) |
74 | 38.9k | return op_index; |
75 | | |
76 | 282k | if ((op_index = spu_disassemble_table[opcode & 0x7fc]) != 0 |
77 | 282k | && (op_index->insn_type == RI16)) |
78 | 15.6k | return op_index; |
79 | | |
80 | 267k | if ((op_index = spu_disassemble_table[opcode & 0x7fe]) != 0 |
81 | 267k | && (op_index->insn_type == RI8)) |
82 | 1.56k | return op_index; |
83 | | |
84 | 265k | if ((op_index = spu_disassemble_table[opcode & 0x7ff]) != 0) |
85 | 140k | return op_index; |
86 | | |
87 | 124k | return 0; |
88 | 265k | } |
89 | | |
90 | | /* Print a Spu instruction. */ |
91 | | |
92 | | int |
93 | | print_insn_spu (bfd_vma memaddr, struct disassemble_info *info) |
94 | 438k | { |
95 | 438k | bfd_byte buffer[4]; |
96 | 438k | int value; |
97 | 438k | int hex_value; |
98 | 438k | int status; |
99 | 438k | unsigned int insn; |
100 | 438k | const struct spu_opcode *op_index; |
101 | 438k | enum spu_insns tag; |
102 | | |
103 | 438k | status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
104 | 438k | if (status != 0) |
105 | 259 | { |
106 | 259 | (*info->memory_error_func) (status, memaddr, info); |
107 | 259 | return -1; |
108 | 259 | } |
109 | | |
110 | 438k | insn = bfd_getb32 (buffer); |
111 | | |
112 | 438k | op_index = get_index_for_opcode (insn); |
113 | | |
114 | 438k | if (op_index == 0) |
115 | 124k | { |
116 | 124k | (*info->fprintf_func) (info->stream, ".long 0x%x", insn); |
117 | 124k | } |
118 | 313k | else |
119 | 313k | { |
120 | 313k | int i; |
121 | 313k | int paren = 0; |
122 | 313k | tag = (enum spu_insns)(op_index - spu_opcodes); |
123 | 313k | (*info->fprintf_func) (info->stream, "%s", op_index->mnemonic); |
124 | 313k | if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED |
125 | 313k | || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ |
126 | 313k | || tag == M_SYNC || tag == M_HBR) |
127 | 10.9k | { |
128 | 10.9k | int fb = (insn >> (32-18)) & 0x7f; |
129 | 10.9k | if (fb & 0x40) |
130 | 5.85k | (*info->fprintf_func) (info->stream, tag == M_SYNC ? "c" : "p"); |
131 | 10.9k | if (fb & 0x20) |
132 | 3.10k | (*info->fprintf_func) (info->stream, "d"); |
133 | 10.9k | if (fb & 0x10) |
134 | 7.05k | (*info->fprintf_func) (info->stream, "e"); |
135 | 10.9k | } |
136 | 313k | if (op_index->arg[0] != 0) |
137 | 201k | (*info->fprintf_func) (info->stream, "\t"); |
138 | 313k | hex_value = 0; |
139 | 977k | for (i = 1; i <= op_index->arg[0]; i++) |
140 | 664k | { |
141 | 664k | int arg = op_index->arg[i]; |
142 | 664k | if (arg != A_P && !paren && i > 1) |
143 | 457k | (*info->fprintf_func) (info->stream, ","); |
144 | | |
145 | 664k | switch (arg) |
146 | 664k | { |
147 | 187k | case A_T: |
148 | 187k | (*info->fprintf_func) (info->stream, "$%d", |
149 | 187k | DECODE_INSN_RT (insn)); |
150 | 187k | break; |
151 | 168k | case A_A: |
152 | 168k | (*info->fprintf_func) (info->stream, "$%d", |
153 | 168k | DECODE_INSN_RA (insn)); |
154 | 168k | break; |
155 | 114k | case A_B: |
156 | 114k | (*info->fprintf_func) (info->stream, "$%d", |
157 | 114k | DECODE_INSN_RB (insn)); |
158 | 114k | break; |
159 | 101k | case A_C: |
160 | 101k | (*info->fprintf_func) (info->stream, "$%d", |
161 | 101k | DECODE_INSN_RC (insn)); |
162 | 101k | break; |
163 | 635 | case A_S: |
164 | 635 | (*info->fprintf_func) (info->stream, "$sp%d", |
165 | 635 | DECODE_INSN_RA (insn)); |
166 | 635 | break; |
167 | 1.32k | case A_H: |
168 | 1.32k | (*info->fprintf_func) (info->stream, "$ch%d", |
169 | 1.32k | DECODE_INSN_RA (insn)); |
170 | 1.32k | break; |
171 | 2.87k | case A_P: |
172 | 2.87k | paren++; |
173 | 2.87k | (*info->fprintf_func) (info->stream, "("); |
174 | 2.87k | break; |
175 | 1.09k | case A_U7A: |
176 | 1.09k | (*info->fprintf_func) (info->stream, "%d", |
177 | 1.09k | 173 - DECODE_INSN_U8 (insn)); |
178 | 1.09k | break; |
179 | 467 | case A_U7B: |
180 | 467 | (*info->fprintf_func) (info->stream, "%d", |
181 | 467 | 155 - DECODE_INSN_U8 (insn)); |
182 | 467 | break; |
183 | 343 | case A_S3: |
184 | 658 | case A_S6: |
185 | 1.02k | case A_S7: |
186 | 2.67k | case A_S7N: |
187 | 3.32k | case A_U3: |
188 | 3.66k | case A_U5: |
189 | 3.79k | case A_U6: |
190 | 4.51k | case A_U7: |
191 | 4.51k | hex_value = DECODE_INSN_I7 (insn); |
192 | 4.51k | (*info->fprintf_func) (info->stream, "%d", hex_value); |
193 | 4.51k | break; |
194 | 11.5k | case A_S11: |
195 | 11.5k | (*info->print_address_func) (memaddr + DECODE_INSN_I9a (insn) * 4, |
196 | 11.5k | info); |
197 | 11.5k | break; |
198 | 279 | case A_S11I: |
199 | 279 | (*info->print_address_func) (memaddr + DECODE_INSN_I9b (insn) * 4, |
200 | 279 | info); |
201 | 279 | break; |
202 | 26.2k | case A_S10: |
203 | 36.4k | case A_S10B: |
204 | 36.4k | hex_value = DECODE_INSN_I10 (insn); |
205 | 36.4k | (*info->fprintf_func) (info->stream, "%d", hex_value); |
206 | 36.4k | break; |
207 | 2.44k | case A_S14: |
208 | 2.44k | hex_value = DECODE_INSN_I10 (insn) * 16; |
209 | 2.44k | (*info->fprintf_func) (info->stream, "%d", hex_value); |
210 | 2.44k | break; |
211 | 773 | case A_S16: |
212 | 773 | hex_value = DECODE_INSN_I16 (insn); |
213 | 773 | (*info->fprintf_func) (info->stream, "%d", hex_value); |
214 | 773 | break; |
215 | 1.95k | case A_X16: |
216 | 1.95k | hex_value = DECODE_INSN_U16 (insn); |
217 | 1.95k | (*info->fprintf_func) (info->stream, "%u", hex_value); |
218 | 1.95k | break; |
219 | 11.5k | case A_R18: |
220 | 11.5k | value = DECODE_INSN_I16 (insn) * 4; |
221 | 11.5k | if (value == 0) |
222 | 1.03k | (*info->fprintf_func) (info->stream, "%d", value); |
223 | 10.5k | else |
224 | 10.5k | { |
225 | 10.5k | hex_value = memaddr + value; |
226 | 10.5k | (*info->print_address_func) (hex_value & 0x3ffff, info); |
227 | 10.5k | } |
228 | 11.5k | break; |
229 | 12.8k | case A_S18: |
230 | 12.8k | value = DECODE_INSN_U16 (insn) * 4; |
231 | 12.8k | if (value == 0) |
232 | 1.23k | (*info->fprintf_func) (info->stream, "%d", value); |
233 | 11.6k | else |
234 | 11.6k | (*info->print_address_func) (value, info); |
235 | 12.8k | break; |
236 | 3.68k | case A_U18: |
237 | 3.68k | value = DECODE_INSN_U18 (insn); |
238 | 3.68k | if (value == 0 || !(*info->symbol_at_address_func)(0, info)) |
239 | 3.68k | { |
240 | 3.68k | hex_value = value; |
241 | 3.68k | (*info->fprintf_func) (info->stream, "%u", value); |
242 | 3.68k | } |
243 | 0 | else |
244 | 0 | (*info->print_address_func) (value, info); |
245 | 3.68k | break; |
246 | 0 | case A_U14: |
247 | 0 | hex_value = DECODE_INSN_U14 (insn); |
248 | 0 | (*info->fprintf_func) (info->stream, "%u", hex_value); |
249 | 0 | break; |
250 | 664k | } |
251 | 664k | if (arg != A_P && paren) |
252 | 2.87k | { |
253 | 2.87k | (*info->fprintf_func) (info->stream, ")"); |
254 | 2.87k | paren--; |
255 | 2.87k | } |
256 | 664k | } |
257 | 313k | if (hex_value > 16) |
258 | 38.6k | (*info->fprintf_func) (info->stream, "\t# %x", hex_value); |
259 | 313k | } |
260 | 438k | return 4; |
261 | 438k | } |