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