include/elf:
[external/binutils.git] / opcodes / d10v-dis.c
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., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, 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
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
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
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
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               && ((((unsigned int) op->mask) & ins[j])
257                   == (unsigned int) op->opcode))
258             {
259               (*info->fprintf_func) (info->stream, "%s\t", op->name);
260               for (i = 0; op->operands[i]; i++)
261                 {
262                   oper = (struct d10v_operand *) &d10v_operands[op->operands[i]];
263                   if (oper->flags == OPERAND_ATPAR)
264                     need_paren = 1;
265                   print_operand (oper, ins[j], op, memaddr, info);
266                   if (op->operands[i + 1] && oper->bits
267                       && d10v_operands[op->operands[i + 1]].flags != OPERAND_PLUS
268                       && d10v_operands[op->operands[i + 1]].flags != OPERAND_MINUS)
269                     (*info->fprintf_func) (info->stream, ", ");
270                 }
271               match = 1;
272               num_match++;
273               break;
274             }
275           op++;
276         }
277       if (!match)
278         (*info->fprintf_func) (info->stream, "unknown");
279
280       switch (order)
281         {
282         case 0:
283           (*info->fprintf_func) (info->stream, "\t->\t");
284           order = -1;
285           break;
286         case 1:
287           (*info->fprintf_func) (info->stream, "\t<-\t");
288           order = -1;
289           break;
290         case 2:
291           (*info->fprintf_func) (info->stream, "\t||\t");
292           order = -1;
293           break;
294         default:
295           break;
296         }
297     }
298
299   if (num_match == 0)
300     (*info->fprintf_func) (info->stream, ".long\t0x%08x", insn);
301
302   if (need_paren)
303     (*info->fprintf_func) (info->stream, ")");
304 }