1 /* Disassemble SPU instructions
2 
3    Copyright (C) 2006-2014 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 "dis-asm.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