1 /* Disassemble D10V instructions.
2    Copyright 1996, 1997, 1998, 2000, 2001 Free Software Foundation, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, 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 
28 static void dis_2_short PARAMS ((unsigned long insn, bfd_vma memaddr,
29 				 struct disassemble_info *info, int order));
30 static void dis_long PARAMS ((unsigned long insn, bfd_vma memaddr,
31 			      struct disassemble_info *info));
32 static void print_operand
33   PARAMS ((struct d10v_operand *, long unsigned int, struct d10v_opcode *,
34 	   bfd_vma, struct disassemble_info *));
35 
36 int
print_insn_d10v(memaddr,info)37 print_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 
72 static void
print_operand(oper,insn,op,memaddr,info)73 print_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 
191 static void
dis_long(insn,memaddr,info)192 dis_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 
232 static void
dis_2_short(insn,memaddr,info,order)233 dis_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