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