1 /* Disassemble SPU instructions
2 
3    Copyright (C) 2006-2024 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
init_spu_disassemble(void)36 init_spu_disassemble (void)
37 {
38   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   for (i = 0; i < spu_num_opcodes; i++)
43     {
44       int o = spu_opcodes[i].opcode;
45       if (o >= (1 << 11))
46           abort ();
47       if (spu_disassemble_table[o] == 0)
48           spu_disassemble_table[o] = &spu_opcodes[i];
49     }
50 }
51 
52 /* Determine the instruction from the 10 least significant bits. */
53 static const struct spu_opcode *
get_index_for_opcode(unsigned int insn)54 get_index_for_opcode (unsigned int insn)
55 {
56   const struct spu_opcode *op_index;
57   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   if (spu_disassemble_table[0] == 0)
62     init_spu_disassemble ();
63 
64   if ((op_index = spu_disassemble_table[opcode & 0x780]) != 0
65       && op_index->insn_type == RRR)
66     return op_index;
67 
68   if ((op_index = spu_disassemble_table[opcode & 0x7f0]) != 0
69       && (op_index->insn_type == RI18 || op_index->insn_type == LBT))
70     return op_index;
71 
72   if ((op_index = spu_disassemble_table[opcode & 0x7f8]) != 0
73       && op_index->insn_type == RI10)
74     return op_index;
75 
76   if ((op_index = spu_disassemble_table[opcode & 0x7fc]) != 0
77       && (op_index->insn_type == RI16))
78     return op_index;
79 
80   if ((op_index = spu_disassemble_table[opcode & 0x7fe]) != 0
81       && (op_index->insn_type == RI8))
82     return op_index;
83 
84   if ((op_index = spu_disassemble_table[opcode & 0x7ff]) != 0)
85     return op_index;
86 
87   return 0;
88 }
89 
90 /* Print a Spu instruction.  */
91 
92 int
print_insn_spu(bfd_vma memaddr,struct disassemble_info * info)93 print_insn_spu (bfd_vma memaddr, struct disassemble_info *info)
94 {
95   bfd_byte buffer[4];
96   int value;
97   int hex_value;
98   int status;
99   unsigned int insn;
100   const struct spu_opcode *op_index;
101   enum spu_insns tag;
102 
103   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
104   if (status != 0)
105     {
106       (*info->memory_error_func) (status, memaddr, info);
107       return -1;
108     }
109 
110   insn = bfd_getb32 (buffer);
111 
112   op_index = get_index_for_opcode (insn);
113 
114   if (op_index == 0)
115     {
116       (*info->fprintf_func) (info->stream, ".long 0x%x", insn);
117     }
118   else
119     {
120       int i;
121       int paren = 0;
122       tag = (enum spu_insns)(op_index - spu_opcodes);
123       (*info->fprintf_func) (info->stream, "%s", op_index->mnemonic);
124       if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED
125             || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ
126           || tag == M_SYNC || tag == M_HBR)
127           {
128             int fb = (insn >> (32-18)) & 0x7f;
129             if (fb & 0x40)
130               (*info->fprintf_func) (info->stream, tag == M_SYNC ? "c" : "p");
131             if (fb & 0x20)
132               (*info->fprintf_func) (info->stream, "d");
133             if (fb & 0x10)
134               (*info->fprintf_func) (info->stream, "e");
135           }
136       if (op_index->arg[0] != 0)
137           (*info->fprintf_func) (info->stream, "\t");
138       hex_value = 0;
139       for (i = 1;  i <= op_index->arg[0]; i++)
140           {
141             int arg = op_index->arg[i];
142             if (arg != A_P && !paren && i > 1)
143               (*info->fprintf_func) (info->stream, ",");
144 
145             switch (arg)
146               {
147               case A_T:
148                 (*info->fprintf_func) (info->stream, "$%d",
149                                              DECODE_INSN_RT (insn));
150                 break;
151               case A_A:
152                 (*info->fprintf_func) (info->stream, "$%d",
153                                              DECODE_INSN_RA (insn));
154                 break;
155               case A_B:
156                 (*info->fprintf_func) (info->stream, "$%d",
157                                              DECODE_INSN_RB (insn));
158                 break;
159               case A_C:
160                 (*info->fprintf_func) (info->stream, "$%d",
161                                              DECODE_INSN_RC (insn));
162                 break;
163               case A_S:
164                 (*info->fprintf_func) (info->stream, "$sp%d",
165                                              DECODE_INSN_RA (insn));
166                 break;
167               case A_H:
168                 (*info->fprintf_func) (info->stream, "$ch%d",
169                                              DECODE_INSN_RA (insn));
170                 break;
171               case A_P:
172                 paren++;
173                 (*info->fprintf_func) (info->stream, "(");
174                 break;
175               case A_U7A:
176                 (*info->fprintf_func) (info->stream, "%d",
177                                              173 - DECODE_INSN_U8 (insn));
178                 break;
179               case A_U7B:
180                 (*info->fprintf_func) (info->stream, "%d",
181                                              155 - DECODE_INSN_U8 (insn));
182                 break;
183               case A_S3:
184               case A_S6:
185               case A_S7:
186               case A_S7N:
187               case A_U3:
188               case A_U5:
189               case A_U6:
190               case A_U7:
191                 hex_value = DECODE_INSN_I7 (insn);
192                 (*info->fprintf_func) (info->stream, "%d", hex_value);
193                 break;
194               case A_S11:
195                 (*info->print_address_func) (memaddr + DECODE_INSN_I9a (insn) * 4,
196                                                      info);
197                 break;
198               case A_S11I:
199                 (*info->print_address_func) (memaddr + DECODE_INSN_I9b (insn) * 4,
200                                                      info);
201                 break;
202               case A_S10:
203               case A_S10B:
204                 hex_value = DECODE_INSN_I10 (insn);
205                 (*info->fprintf_func) (info->stream, "%d", hex_value);
206                 break;
207               case A_S14:
208                 hex_value = DECODE_INSN_I10 (insn) * 16;
209                 (*info->fprintf_func) (info->stream, "%d", hex_value);
210                 break;
211               case A_S16:
212                 hex_value = DECODE_INSN_I16 (insn);
213                 (*info->fprintf_func) (info->stream, "%d", hex_value);
214                 break;
215               case A_X16:
216                 hex_value = DECODE_INSN_U16 (insn);
217                 (*info->fprintf_func) (info->stream, "%u", hex_value);
218                 break;
219               case A_R18:
220                 value = DECODE_INSN_I16 (insn) * 4;
221                 if (value == 0)
222                     (*info->fprintf_func) (info->stream, "%d", value);
223                 else
224                     {
225                       hex_value = memaddr + value;
226                       (*info->print_address_func) (hex_value & 0x3ffff, info);
227                     }
228                 break;
229               case A_S18:
230                 value = DECODE_INSN_U16 (insn) * 4;
231                 if (value == 0)
232                     (*info->fprintf_func) (info->stream, "%d", value);
233                 else
234                     (*info->print_address_func) (value, info);
235                 break;
236               case A_U18:
237                 value = DECODE_INSN_U18 (insn);
238                 if (value == 0 || !(*info->symbol_at_address_func)(0, info))
239                     {
240                       hex_value = value;
241                       (*info->fprintf_func) (info->stream, "%u", value);
242                     }
243                 else
244                     (*info->print_address_func) (value, info);
245                 break;
246               case A_U14:
247                 hex_value = DECODE_INSN_U14 (insn);
248                 (*info->fprintf_func) (info->stream, "%u", hex_value);
249                 break;
250               }
251             if (arg != A_P && paren)
252               {
253                 (*info->fprintf_func) (info->stream, ")");
254                 paren--;
255               }
256           }
257       if (hex_value > 16)
258           (*info->fprintf_func) (info->stream, "\t# %x", hex_value);
259     }
260   return 4;
261 }
262