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