1214571Sdim/* Disassemble SPU instructions
2214571Sdim
3214571Sdim   Copyright 2006 Free Software Foundation, Inc.
4214571Sdim
5214571Sdim   This file is part of GDB, GAS, and the GNU binutils.
6214571Sdim
7214571Sdim   This program is free software; you can redistribute it and/or modify
8214571Sdim   it under the terms of the GNU General Public License as published by
9214571Sdim   the Free Software Foundation; either version 2 of the License, or
10214571Sdim   (at your option) any later version.
11214571Sdim
12214571Sdim   This program is distributed in the hope that it will be useful,
13214571Sdim   but WITHOUT ANY WARRANTY; without even the implied warranty of
14214571Sdim   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15214571Sdim   GNU General Public License for more details.
16214571Sdim
17214571Sdim   You should have received a copy of the GNU General Public License along
18214571Sdim   with this program; if not, write to the Free Software Foundation, Inc.,
19214571Sdim   51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
20214571Sdim
21214571Sdim#include <stdio.h>
22214571Sdim#include "sysdep.h"
23214571Sdim#include "dis-asm.h"
24214571Sdim#include "opcode/spu.h"
25214571Sdim
26214571Sdim/* This file provides a disassembler function which uses
27214571Sdim   the disassembler interface defined in dis-asm.h.   */
28214571Sdim
29214571Sdimextern const struct spu_opcode spu_opcodes[];
30214571Sdimextern const int spu_num_opcodes;
31214571Sdim
32214571Sdimstatic const struct spu_opcode *spu_disassemble_table[(1<<11)];
33214571Sdim
34214571Sdimstatic void
35214571Sdiminit_spu_disassemble (void)
36214571Sdim{
37214571Sdim  int i;
38214571Sdim
39214571Sdim  /* If two instructions have the same opcode then we prefer the first
40214571Sdim   * one.  In most cases it is just an alternate mnemonic. */
41214571Sdim  for (i = 0; i < spu_num_opcodes; i++)
42214571Sdim    {
43214571Sdim      int o = spu_opcodes[i].opcode;
44214571Sdim      if (o >= (1 << 11))
45214571Sdim	abort ();
46214571Sdim      if (spu_disassemble_table[o] == 0)
47214571Sdim	spu_disassemble_table[o] = &spu_opcodes[i];
48214571Sdim    }
49214571Sdim}
50214571Sdim
51214571Sdim/* Determine the instruction from the 10 least significant bits. */
52214571Sdimstatic const struct spu_opcode *
53214571Sdimget_index_for_opcode (unsigned int insn)
54214571Sdim{
55214571Sdim  const struct spu_opcode *index;
56214571Sdim  unsigned int opcode = insn >> (32-11);
57214571Sdim
58214571Sdim  /* Init the table.  This assumes that element 0/opcode 0 (currently
59214571Sdim   * NOP) is always used */
60214571Sdim  if (spu_disassemble_table[0] == 0)
61214571Sdim    init_spu_disassemble ();
62214571Sdim
63214571Sdim  if ((index = spu_disassemble_table[opcode & 0x780]) != 0
64214571Sdim      && index->insn_type == RRR)
65214571Sdim    return index;
66214571Sdim
67214571Sdim  if ((index = spu_disassemble_table[opcode & 0x7f0]) != 0
68214571Sdim      && (index->insn_type == RI18 || index->insn_type == LBT))
69214571Sdim    return index;
70214571Sdim
71214571Sdim  if ((index = spu_disassemble_table[opcode & 0x7f8]) != 0
72214571Sdim      && index->insn_type == RI10)
73214571Sdim    return index;
74214571Sdim
75214571Sdim  if ((index = spu_disassemble_table[opcode & 0x7fc]) != 0
76214571Sdim      && (index->insn_type == RI16))
77214571Sdim    return index;
78214571Sdim
79214571Sdim  if ((index = spu_disassemble_table[opcode & 0x7fe]) != 0
80214571Sdim      && (index->insn_type == RI8))
81214571Sdim    return index;
82214571Sdim
83214571Sdim  if ((index = spu_disassemble_table[opcode & 0x7ff]) != 0)
84214571Sdim    return index;
85214571Sdim
86214571Sdim  return 0;
87214571Sdim}
88214571Sdim
89214571Sdim/* Print a Spu instruction.  */
90214571Sdim
91214571Sdimint
92214571Sdimprint_insn_spu (bfd_vma memaddr, struct disassemble_info *info)
93214571Sdim{
94214571Sdim  bfd_byte buffer[4];
95214571Sdim  int value;
96214571Sdim  int hex_value;
97214571Sdim  int status;
98214571Sdim  unsigned int insn;
99214571Sdim  const struct spu_opcode *index;
100214571Sdim  enum spu_insns tag;
101214571Sdim
102214571Sdim  status = (*info->read_memory_func) (memaddr, buffer, 4, info);
103214571Sdim  if (status != 0)
104214571Sdim    {
105214571Sdim      (*info->memory_error_func) (status, memaddr, info);
106214571Sdim      return -1;
107214571Sdim    }
108214571Sdim
109214571Sdim  insn = bfd_getb32 (buffer);
110214571Sdim
111214571Sdim  index = get_index_for_opcode (insn);
112214571Sdim
113214571Sdim  if (index == 0)
114214571Sdim    {
115214571Sdim      (*info->fprintf_func) (info->stream, ".long 0x%x", insn);
116214571Sdim    }
117214571Sdim  else
118214571Sdim    {
119214571Sdim      int i;
120214571Sdim      int paren = 0;
121214571Sdim      tag = (enum spu_insns)(index - spu_opcodes);
122214571Sdim      (*info->fprintf_func) (info->stream, "%s", index->mnemonic);
123214571Sdim      if (tag == M_BI || tag == M_BISL || tag == M_IRET || tag == M_BISLED
124214571Sdim	  || tag == M_BIHNZ || tag == M_BIHZ || tag == M_BINZ || tag == M_BIZ
125214571Sdim          || tag == M_SYNC || tag == M_HBR)
126214571Sdim	{
127214571Sdim	  int fb = (insn >> (32-18)) & 0x7f;
128214571Sdim	  if (fb & 0x40)
129214571Sdim	    (*info->fprintf_func) (info->stream, tag == M_SYNC ? "c" : "p");
130214571Sdim	  if (fb & 0x20)
131214571Sdim	    (*info->fprintf_func) (info->stream, "d");
132214571Sdim	  if (fb & 0x10)
133214571Sdim	    (*info->fprintf_func) (info->stream, "e");
134214571Sdim	}
135214571Sdim      if (index->arg[0] != 0)
136214571Sdim	(*info->fprintf_func) (info->stream, "\t");
137214571Sdim      hex_value = 0;
138214571Sdim      for (i = 1;  i <= index->arg[0]; i++)
139214571Sdim	{
140214571Sdim	  int arg = index->arg[i];
141214571Sdim	  if (arg != A_P && !paren && i > 1)
142214571Sdim	    (*info->fprintf_func) (info->stream, ",");
143214571Sdim
144214571Sdim	  switch (arg)
145214571Sdim	    {
146214571Sdim	    case A_T:
147214571Sdim	      (*info->fprintf_func) (info->stream, "$%d",
148214571Sdim				     DECODE_INSN_RT (insn));
149214571Sdim	      break;
150214571Sdim	    case A_A:
151214571Sdim	      (*info->fprintf_func) (info->stream, "$%d",
152214571Sdim				     DECODE_INSN_RA (insn));
153214571Sdim	      break;
154214571Sdim	    case A_B:
155214571Sdim	      (*info->fprintf_func) (info->stream, "$%d",
156214571Sdim				     DECODE_INSN_RB (insn));
157214571Sdim	      break;
158214571Sdim	    case A_C:
159214571Sdim	      (*info->fprintf_func) (info->stream, "$%d",
160214571Sdim				     DECODE_INSN_RC (insn));
161214571Sdim	      break;
162214571Sdim	    case A_S:
163214571Sdim	      (*info->fprintf_func) (info->stream, "$sp%d",
164214571Sdim				     DECODE_INSN_RA (insn));
165214571Sdim	      break;
166214571Sdim	    case A_H:
167214571Sdim	      (*info->fprintf_func) (info->stream, "$ch%d",
168214571Sdim				     DECODE_INSN_RA (insn));
169214571Sdim	      break;
170214571Sdim	    case A_P:
171214571Sdim	      paren++;
172214571Sdim	      (*info->fprintf_func) (info->stream, "(");
173214571Sdim	      break;
174214571Sdim	    case A_U7A:
175214571Sdim	      (*info->fprintf_func) (info->stream, "%d",
176214571Sdim				     173 - DECODE_INSN_U8 (insn));
177214571Sdim	      break;
178214571Sdim	    case A_U7B:
179214571Sdim	      (*info->fprintf_func) (info->stream, "%d",
180214571Sdim				     155 - DECODE_INSN_U8 (insn));
181214571Sdim	      break;
182214571Sdim	    case A_S3:
183214571Sdim	    case A_S6:
184214571Sdim	    case A_S7:
185214571Sdim	    case A_S7N:
186214571Sdim	    case A_U3:
187214571Sdim	    case A_U5:
188214571Sdim	    case A_U6:
189214571Sdim	    case A_U7:
190214571Sdim	      hex_value = DECODE_INSN_I7 (insn);
191214571Sdim	      (*info->fprintf_func) (info->stream, "%d", hex_value);
192214571Sdim	      break;
193214571Sdim	    case A_S11:
194214571Sdim	      (*info->print_address_func) (memaddr + DECODE_INSN_I9a (insn) * 4,
195214571Sdim					   info);
196214571Sdim	      break;
197214571Sdim	    case A_S11I:
198214571Sdim	      (*info->print_address_func) (memaddr + DECODE_INSN_I9b (insn) * 4,
199214571Sdim					   info);
200214571Sdim	      break;
201214571Sdim	    case A_S10:
202214571Sdim	    case A_S10B:
203214571Sdim	      hex_value = DECODE_INSN_I10 (insn);
204214571Sdim	      (*info->fprintf_func) (info->stream, "%d", hex_value);
205214571Sdim	      break;
206214571Sdim	    case A_S14:
207214571Sdim	      hex_value = DECODE_INSN_I10 (insn) * 16;
208214571Sdim	      (*info->fprintf_func) (info->stream, "%d", hex_value);
209214571Sdim	      break;
210214571Sdim	    case A_S16:
211214571Sdim	      hex_value = DECODE_INSN_I16 (insn);
212214571Sdim	      (*info->fprintf_func) (info->stream, "%d", hex_value);
213214571Sdim	      break;
214214571Sdim	    case A_X16:
215214571Sdim	      hex_value = DECODE_INSN_U16 (insn);
216214571Sdim	      (*info->fprintf_func) (info->stream, "%u", hex_value);
217214571Sdim	      break;
218214571Sdim	    case A_R18:
219214571Sdim	      value = DECODE_INSN_I16 (insn) * 4;
220214571Sdim	      if (value == 0)
221214571Sdim		(*info->fprintf_func) (info->stream, "%d", value);
222214571Sdim	      else
223214571Sdim		{
224214571Sdim		  hex_value = memaddr + value;
225214571Sdim		  (*info->print_address_func) (hex_value & 0x3ffff, info);
226214571Sdim		}
227214571Sdim	      break;
228214571Sdim	    case A_S18:
229214571Sdim	      value = DECODE_INSN_U16 (insn) * 4;
230214571Sdim	      if (value == 0)
231214571Sdim		(*info->fprintf_func) (info->stream, "%d", value);
232214571Sdim	      else
233214571Sdim		(*info->print_address_func) (value, info);
234214571Sdim	      break;
235214571Sdim	    case A_U18:
236214571Sdim	      value = DECODE_INSN_U18 (insn);
237214571Sdim	      if (value == 0 || !(*info->symbol_at_address_func)(0, info))
238214571Sdim		{
239214571Sdim		  hex_value = value;
240214571Sdim		  (*info->fprintf_func) (info->stream, "%u", value);
241214571Sdim		}
242214571Sdim	      else
243214571Sdim		(*info->print_address_func) (value, info);
244214571Sdim	      break;
245214571Sdim	    case A_U14:
246214571Sdim	      hex_value = DECODE_INSN_U14 (insn);
247214571Sdim	      (*info->fprintf_func) (info->stream, "%u", hex_value);
248214571Sdim	      break;
249214571Sdim	    }
250214571Sdim	  if (arg != A_P && paren)
251214571Sdim	    {
252214571Sdim	      (*info->fprintf_func) (info->stream, ")");
253214571Sdim	      paren--;
254214571Sdim	    }
255214571Sdim	}
256214571Sdim      if (hex_value > 16)
257214571Sdim	(*info->fprintf_func) (info->stream, "\t# %x", hex_value);
258214571Sdim    }
259214571Sdim  return 4;
260214571Sdim}
261