19990502 sourceware import
[external/binutils.git] / opcodes / d30v-dis.c
1 /* Disassemble D30V instructions.
2    Copyright (C) 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 #include <stdio.h>
19 #include "opcode/d30v.h" 
20 #include "dis-asm.h"
21 #include "opintl.h"
22
23 #define PC_MASK 0xFFFFFFFF
24
25 static int lookup_opcode PARAMS (( struct d30v_insn *insn, long num, int is_long ));
26 static void print_insn PARAMS (( struct disassemble_info *info, bfd_vma memaddr, long long num, 
27                                  struct d30v_insn *insn, int is_long, int show_ext ));
28 static int extract_value PARAMS (( long long num, struct d30v_operand *oper, int is_long ));
29
30 int 
31 print_insn_d30v (memaddr, info)
32      bfd_vma memaddr;
33      struct disassemble_info *info;
34 {
35   int status, result;
36   bfd_byte buffer[12];
37   unsigned long in1,in2;
38   struct d30v_insn insn;
39   long long num;
40
41   insn.form = (struct d30v_format *)NULL;
42
43   info->bytes_per_line = 8;
44   info->bytes_per_chunk = 4;
45   info->display_endian = BFD_ENDIAN_BIG;
46
47   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
48   if (status != 0)
49     {
50       (*info->memory_error_func) (status, memaddr, info);
51       return -1;
52     }
53   in1 = bfd_getb32 (buffer);
54
55   status = (*info->read_memory_func) (memaddr+4, buffer, 4, info);
56   if (status != 0)
57     {
58       info->bytes_per_line = 8;
59       if (!(result = lookup_opcode(&insn, in1, 0)))
60         (*info->fprintf_func) (info->stream, ".long\t0x%x",in1);
61       else
62         print_insn(info, memaddr, (long long) in1, &insn, 0, result);
63       return 4;      
64     }
65   in2 = bfd_getb32 (buffer);
66   
67   if (in1 & in2 & FM01)
68     {
69       /* LONG instruction */
70       if (!(result = lookup_opcode(&insn, in1, 1)))
71         {
72           (*info->fprintf_func) (info->stream, ".long\t0x%x,0x%x",in1,in2);
73           return 8;
74         }
75       num = (long long)in1 << 32 | in2;
76       print_insn(info, memaddr, num, &insn, 1, result);
77     }
78   else
79     {
80       num = in1;
81       if (!(result = lookup_opcode(&insn, in1, 0)))
82         (*info->fprintf_func) (info->stream, ".long\t0x%x",in1);
83       else
84         print_insn(info, memaddr, num, &insn, 0, result);
85       
86       switch ( ((in1>>31)<<1) | (in2>>31) )
87         {
88         case 0:
89           (*info->fprintf_func) (info->stream, "\t||\t");   
90           break;
91         case 1:
92           (*info->fprintf_func) (info->stream, "\t->\t");   
93           break;
94         case 2:
95           (*info->fprintf_func) (info->stream, "\t<-\t");   
96         default:
97           break;
98         }
99       
100       insn.form = (struct d30v_format *)NULL;
101       num = in2;
102       if (!(result = lookup_opcode(&insn, in2, 0)))
103         (*info->fprintf_func) (info->stream, ".long\t0x%x",in2);
104       else
105         print_insn(info, memaddr, num, &insn, 0, result);
106
107     }
108   return 8;
109 }
110
111
112 /* returns 0 if lookup fails */
113 /* 1 if found and only one form */
114 /* 2 if found and there are short and long forms */
115 static int
116 lookup_opcode (insn, num, is_long)
117      struct d30v_insn *insn;
118      long num;
119      int is_long;
120 {
121   int i=0, index;
122   struct d30v_format *f;
123   struct d30v_opcode *op = (struct d30v_opcode *)d30v_opcode_table;
124   int op1 = (num >> 25) & 0x7;
125   int op2 = (num >> 20) & 0x1f;
126   int mod = (num >> 18) & 0x3;
127
128   /* find the opcode */
129   do {
130     if ((op->op1 == op1) && (op->op2 == op2))
131       break;
132     op++;
133   } while (op->name);
134
135   if (!op || !op->name)
136     return 0;
137
138   while (op->op1 == op1 && op->op2 == op2)
139     {
140       /* scan through all the formats for the opcode  */
141       index = op->format[i++];
142       do 
143         {
144           f = (struct d30v_format *)&d30v_format_table[index];
145           while (f->form == index)
146             {
147               if ((!is_long || f->form >= LONG) && (f->modifier == mod))
148                 {
149                   insn->form = f;
150                   break;
151                 }
152               f++;
153             }
154           if (insn->form)
155             break;
156         } while ((index = op->format[i++]) != 0);
157       if (insn->form)
158         break;
159       op++;
160       i=0;
161     }
162   if (insn->form == NULL)
163     return 0;
164
165   insn->op = op;
166   insn->ecc = (num >> 28) & 0x7;
167   if (op->format[1])
168     return 2;
169   else
170     return 1;
171 }
172
173
174 static void 
175 print_insn ( info, memaddr, num, insn, is_long, show_ext )
176      struct disassemble_info *info;
177      bfd_vma memaddr;
178      long long num;
179      struct d30v_insn *insn;
180      int is_long;
181      int show_ext;
182 {
183   int val, opnum, need_comma=0;
184   struct d30v_operand *oper;
185   int i, match, opind=0, need_paren=0, found_control=0;
186
187   (*info->fprintf_func) (info->stream, "%s",insn->op->name);
188
189   /* check for CMP or CMPU */
190   if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME)
191     {
192       opind++;
193       val = extract_value(num,(struct d30v_operand *)&d30v_operand_table[insn->form->operands[0]],is_long);
194       (*info->fprintf_func) (info->stream, "%s",d30v_cc_names[val]);
195     }
196
197   /* add in ".s" or ".l" */
198   if (show_ext == 2)
199     {
200       if (is_long)
201         (*info->fprintf_func) (info->stream, ".l");
202       else
203         (*info->fprintf_func) (info->stream, ".s");
204     }
205
206   if (insn->ecc)
207     (*info->fprintf_func) (info->stream, "/%s",d30v_ecc_names[insn->ecc]);
208
209   (*info->fprintf_func) (info->stream, "\t");
210
211   while ((opnum = insn->form->operands[opind++]) != 0)
212     {
213       int bits;
214       oper = (struct d30v_operand *)&d30v_operand_table[opnum];
215       bits = oper->bits;
216       if (oper->flags & OPERAND_SHIFT)
217         bits += 3;
218
219       if (need_comma && oper->flags != OPERAND_PLUS && oper->flags != OPERAND_MINUS)
220         {
221           need_comma=0;
222           (*info->fprintf_func) (info->stream, ", ");
223         }
224
225       if (oper->flags == OPERAND_ATMINUS)
226         {
227           (*info->fprintf_func) (info->stream, "@-");
228           continue;
229         }
230       if (oper->flags == OPERAND_MINUS)
231         {
232           (*info->fprintf_func) (info->stream, "-");   
233           continue;
234         }
235       if (oper->flags == OPERAND_PLUS)
236         {
237           (*info->fprintf_func) (info->stream, "+");   
238           continue;
239         }
240       if (oper->flags == OPERAND_ATSIGN)
241         {
242           (*info->fprintf_func) (info->stream, "@");   
243           continue;
244         }
245       if (oper->flags == OPERAND_ATPAR)
246         {
247           (*info->fprintf_func) (info->stream, "@(");   
248           need_paren = 1;
249           continue;
250         }
251
252       if (oper->flags == OPERAND_SPECIAL)
253         continue;
254
255       val = extract_value(num, oper, is_long);
256       
257       if (oper->flags & OPERAND_REG)
258         {
259           match = 0;
260           if (oper->flags & OPERAND_CONTROL)
261             {
262               struct d30v_operand *oper3 = 
263                 (struct d30v_operand *)&d30v_operand_table[insn->form->operands[2]];          
264               int id = extract_value (num, oper3, is_long );
265               found_control = 1;
266               switch ( id )
267                 {
268                 case 0:
269                   val |= OPERAND_CONTROL;
270                   break;
271                 case 1:
272                 case 2:
273                   val = OPERAND_CONTROL + MAX_CONTROL_REG + id;
274                   break;
275                 case 3:
276                   val |= OPERAND_FLAG;
277                   break;
278                 default:
279                   fprintf(stderr,"illegal id (%d)\n",id);
280                 }
281             }
282           else if (oper->flags & OPERAND_ACC)
283             val |= OPERAND_ACC;
284           else if (oper->flags & OPERAND_FLAG)
285             val |= OPERAND_FLAG;
286           for (i=0;i<reg_name_cnt();i++)
287             {
288               if (val == pre_defined_registers[i].value)
289                 {
290                   if (pre_defined_registers[i].pname)
291                     (*info->fprintf_func)
292                       (info->stream, "%s",pre_defined_registers[i].pname);
293                   else
294                     (*info->fprintf_func)
295                       (info->stream, "%s",pre_defined_registers[i].name);
296                   match=1;
297                   break;
298                 }
299             }
300           if (match==0)
301             {
302               /* this would only get executed if a register was not in the 
303                  register table */
304               (*info->fprintf_func)
305                 (info->stream, _("<unknown register %d>"), val & 0x3F);       
306             }
307         }
308       else if (insn->op->reloc_flag == RELOC_PCREL || 
309                (opind == 1 && (insn->form->form == SHORT_D2 || insn->form->form == LONG_D)))
310         {
311           long max;
312           int neg=0;
313           max = (1 << (bits - 1));
314           if (val & max)
315             {
316               if (bits == 32)
317                 val = -val;
318               else
319                 val = -val & ((1 << bits)-1);
320               neg = 1;
321             }
322           if (opind == 1 && (insn->form->form == SHORT_D2 || insn->form->form == LONG_D))
323             {
324               (*info->fprintf_func) (info->stream, "%x",val);
325             }
326           else {
327             if (neg)
328               {
329                 (*info->fprintf_func) (info->stream, "-%x\t(",val);
330                 (*info->print_address_func) ((memaddr - val) & PC_MASK, info);
331                 (*info->fprintf_func) (info->stream, ")");
332               }
333             else
334               {
335                 (*info->fprintf_func) (info->stream, "%x\t(",val);
336                 (*info->print_address_func) ((memaddr + val) & PC_MASK, info);
337                 (*info->fprintf_func) (info->stream, ")");
338               }
339           }
340         }
341       else if (insn->op->reloc_flag == RELOC_ABS)
342         {
343           (*info->print_address_func) (val, info);
344         }
345       else
346         {
347           if (oper->flags & OPERAND_SIGNED)
348             {
349               int max = (1 << (bits - 1));
350               if (val & max)
351                 {
352                   val = -val;
353                   if (bits < 32)
354                     val &= ((1 << bits) - 1);
355                   (*info->fprintf_func) (info->stream, "-");
356                 }
357             }
358           (*info->fprintf_func) (info->stream, "0x%x",val);
359         }
360       /* if there is another operand, then write a comma and space */
361       if (insn->form->operands[opind] && !(found_control && opind == 2))
362         need_comma = 1;
363     }
364   if (need_paren)
365     (*info->fprintf_func) (info->stream, ")");
366 }
367
368
369
370 static int
371 extract_value (num, oper, is_long)
372      long long num;
373      struct d30v_operand *oper;
374      int is_long;
375 {
376   int val;
377   int shift = 12 - oper->position;
378   int mask = (0xFFFFFFFF >> (32 - oper->bits));
379
380   if (is_long)
381     {
382       if (oper->bits == 32) 
383         {
384           /* piece together 32-bit constant */
385           val = ((num & 0x3FFFF)
386                  | ((num & 0xFF00000) >> 2)
387                  | ((num & 0x3F00000000LL) >> 6));
388         }
389       else      
390         val = (num >> (32 + shift)) & mask;     
391     }
392   else
393     val = (num >> shift) & mask;
394
395   if (oper->flags & OPERAND_SHIFT)
396     val <<= 3;
397
398   return val;
399 }