1/* Disassemble SPU instructions
2
3   Copyright (C) 2006-2017 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
30extern const struct spu_opcode spu_opcodes[];
31extern const int spu_num_opcodes;
32
33static const struct spu_opcode *spu_disassemble_table[(1<<11)];
34
35static void
36init_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. */
53static const struct spu_opcode *
54get_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
92int
93print_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