1/* Disassemble D10V instructions.
2   Copyright 1996, 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18#include <stdio.h>
19
20#include "sysdep.h"
21#include "opcode/d10v.h"
22#include "dis-asm.h"
23
24/* The PC wraps at 18 bits, except for the segment number,
25   so use this mask to keep the parts we want.  */
26#define PC_MASK	0x0303FFFF
27
28static void dis_2_short PARAMS ((unsigned long insn, bfd_vma memaddr,
29				 struct disassemble_info *info, int order));
30static void dis_long PARAMS ((unsigned long insn, bfd_vma memaddr,
31			      struct disassemble_info *info));
32static void print_operand
33  PARAMS ((struct d10v_operand *, long unsigned int, struct d10v_opcode *,
34	   bfd_vma, struct disassemble_info *));
35
36int
37print_insn_d10v (memaddr, info)
38     bfd_vma memaddr;
39     struct disassemble_info *info;
40{
41  int status;
42  bfd_byte buffer[4];
43  unsigned long insn;
44
45  status = (*info->read_memory_func) (memaddr, buffer, 4, info);
46  if (status != 0)
47    {
48      (*info->memory_error_func) (status, memaddr, info);
49      return -1;
50    }
51  insn = bfd_getb32 (buffer);
52
53  status = insn & FM11;
54  switch (status)
55    {
56    case 0:
57      dis_2_short (insn, memaddr, info, 2);
58      break;
59    case FM01:
60      dis_2_short (insn, memaddr, info, 0);
61      break;
62    case FM10:
63      dis_2_short (insn, memaddr, info, 1);
64      break;
65    case FM11:
66      dis_long (insn, memaddr, info);
67      break;
68    }
69  return 4;
70}
71
72static void
73print_operand (oper, insn, op, memaddr, info)
74     struct d10v_operand *oper;
75     unsigned long insn;
76     struct d10v_opcode *op;
77     bfd_vma memaddr;
78     struct disassemble_info *info;
79{
80  int num, shift;
81
82  if (oper->flags == OPERAND_ATMINUS)
83    {
84      (*info->fprintf_func) (info->stream, "@-");
85      return;
86    }
87  if (oper->flags == OPERAND_MINUS)
88    {
89      (*info->fprintf_func) (info->stream, "-");
90      return;
91    }
92  if (oper->flags == OPERAND_PLUS)
93    {
94      (*info->fprintf_func) (info->stream, "+");
95      return;
96    }
97  if (oper->flags == OPERAND_ATSIGN)
98    {
99      (*info->fprintf_func) (info->stream, "@");
100      return;
101    }
102  if (oper->flags == OPERAND_ATPAR)
103    {
104      (*info->fprintf_func) (info->stream, "@(");
105      return;
106    }
107
108  shift = oper->shift;
109
110  /* The LONG_L format shifts registers over by 15.  */
111  if (op->format == LONG_L && (oper->flags & OPERAND_REG))
112    shift += 15;
113
114  num = (insn >> shift) & (0x7FFFFFFF >> (31 - oper->bits));
115
116  if (oper->flags & OPERAND_REG)
117    {
118      int i;
119      int match = 0;
120      num += (oper->flags
121	      & (OPERAND_GPR | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL));
122      if (oper->flags & (OPERAND_ACC0 | OPERAND_ACC1))
123	num += num ? OPERAND_ACC1 : OPERAND_ACC0;
124      for (i = 0; i < d10v_reg_name_cnt (); i++)
125	{
126	  if (num == (d10v_predefined_registers[i].value & ~ OPERAND_SP))
127	    {
128	      if (d10v_predefined_registers[i].pname)
129		(*info->fprintf_func) (info->stream, "%s",
130				       d10v_predefined_registers[i].pname);
131	      else
132		(*info->fprintf_func) (info->stream, "%s",
133				       d10v_predefined_registers[i].name);
134	      match = 1;
135	      break;
136	    }
137	}
138      if (match == 0)
139	{
140	  /* This would only get executed if a register was not in the
141	     register table.  */
142	  if (oper->flags & (OPERAND_ACC0 | OPERAND_ACC1))
143	    (*info->fprintf_func) (info->stream, "a");
144	  else if (oper->flags & OPERAND_CONTROL)
145	    (*info->fprintf_func) (info->stream, "cr");
146	  else if (oper->flags & OPERAND_REG)
147	    (*info->fprintf_func) (info->stream, "r");
148	  (*info->fprintf_func) (info->stream, "%d", num & REGISTER_MASK);
149	}
150    }
151  else
152    {
153      /* Addresses are right-shifted by 2.  */
154      if (oper->flags & OPERAND_ADDR)
155	{
156	  long max;
157	  int neg = 0;
158	  max = (1 << (oper->bits - 1));
159	  if (num & max)
160	    {
161	      num = -num & ((1 << oper->bits) - 1);
162	      neg = 1;
163	    }
164	  num = num << 2;
165	  if (info->flags & INSN_HAS_RELOC)
166	    (*info->print_address_func) (num & PC_MASK, info);
167	  else
168	    {
169	      if (neg)
170		(*info->print_address_func) ((memaddr - num) & PC_MASK, info);
171	      else
172		(*info->print_address_func) ((memaddr + num) & PC_MASK, info);
173	    }
174	}
175      else
176	{
177	  if (oper->flags & OPERAND_SIGNED)
178	    {
179	      int max = (1 << (oper->bits - 1));
180	      if (num & max)
181		{
182		  num = -num & ((1 << oper->bits) - 1);
183		  (*info->fprintf_func) (info->stream, "-");
184		}
185	    }
186	  (*info->fprintf_func) (info->stream, "0x%x", num);
187	}
188    }
189}
190
191static void
192dis_long (insn, memaddr, info)
193     unsigned long insn;
194     bfd_vma memaddr;
195     struct disassemble_info *info;
196{
197  int i;
198  struct d10v_opcode *op = (struct d10v_opcode *) d10v_opcodes;
199  struct d10v_operand *oper;
200  int need_paren = 0;
201  int match = 0;
202
203  while (op->name)
204    {
205      if ((op->format & LONG_OPCODE) && ((op->mask & insn) == (unsigned long) op->opcode))
206	{
207	  match = 1;
208	  (*info->fprintf_func) (info->stream, "%s\t", op->name);
209	  for (i = 0; op->operands[i]; i++)
210	    {
211	      oper = (struct d10v_operand *) &d10v_operands[op->operands[i]];
212	      if (oper->flags == OPERAND_ATPAR)
213		need_paren = 1;
214	      print_operand (oper, insn, op, memaddr, info);
215	      if (op->operands[i + 1] && oper->bits
216		  && d10v_operands[op->operands[i + 1]].flags != OPERAND_PLUS
217		  && d10v_operands[op->operands[i + 1]].flags != OPERAND_MINUS)
218		(*info->fprintf_func) (info->stream, ", ");
219	    }
220	  break;
221	}
222      op++;
223    }
224
225  if (!match)
226    (*info->fprintf_func) (info->stream, ".long\t0x%08x", insn);
227
228  if (need_paren)
229    (*info->fprintf_func) (info->stream, ")");
230}
231
232static void
233dis_2_short (insn, memaddr, info, order)
234     unsigned long insn;
235     bfd_vma memaddr;
236     struct disassemble_info *info;
237     int order;
238{
239  int i, j;
240  unsigned int ins[2];
241  struct d10v_opcode *op;
242  int match, num_match = 0;
243  struct d10v_operand *oper;
244  int need_paren = 0;
245
246  ins[0] = (insn & 0x3FFFFFFF) >> 15;
247  ins[1] = insn & 0x00007FFF;
248
249  for (j = 0; j < 2; j++)
250    {
251      op = (struct d10v_opcode *) d10v_opcodes;
252      match = 0;
253      while (op->name)
254	{
255	  if ((op->format & SHORT_OPCODE)
256	      && ((op->mask & ins[j]) == (unsigned long) op->opcode))
257	    {
258	      (*info->fprintf_func) (info->stream, "%s\t", op->name);
259	      for (i = 0; op->operands[i]; i++)
260		{
261		  oper = (struct d10v_operand *) &d10v_operands[op->operands[i]];
262		  if (oper->flags == OPERAND_ATPAR)
263		    need_paren = 1;
264		  print_operand (oper, ins[j], op, memaddr, info);
265		  if (op->operands[i + 1] && oper->bits
266		      && d10v_operands[op->operands[i + 1]].flags != OPERAND_PLUS
267		      && d10v_operands[op->operands[i + 1]].flags != OPERAND_MINUS)
268		    (*info->fprintf_func) (info->stream, ", ");
269		}
270	      match = 1;
271	      num_match++;
272	      break;
273	    }
274	  op++;
275	}
276      if (!match)
277	(*info->fprintf_func) (info->stream, "unknown");
278
279      switch (order)
280	{
281	case 0:
282	  (*info->fprintf_func) (info->stream, "\t->\t");
283	  order = -1;
284	  break;
285	case 1:
286	  (*info->fprintf_func) (info->stream, "\t<-\t");
287	  order = -1;
288	  break;
289	case 2:
290	  (*info->fprintf_func) (info->stream, "\t||\t");
291	  order = -1;
292	  break;
293	default:
294	  break;
295	}
296    }
297
298  if (num_match == 0)
299    (*info->fprintf_func) (info->stream, ".long\t0x%08x", insn);
300
301  if (need_paren)
302    (*info->fprintf_func) (info->stream, ")");
303}
304