This commit was manufactured by cvs2svn to create branch 'binutils-
[external/binutils.git] / opcodes / d30v-dis.c
1 /* Disassemble D30V instructions.
2    Copyright (C) 1997, 1998, 2000 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       /* repeati has a relocation, but its first argument is a plain
309          immediate.  OTOH instructions like djsri have a pc-relative
310          delay target, but a absolute jump target.  Therefore, a test
311          of insn->op->reloc_flag is not specific enough; we must test
312          if the actual operand we are handling now is pc-relative.  */
313       else if (oper->flags & OPERAND_PCREL)
314         {
315           int neg = 0;
316           
317           /* IMM6S3 is unsigned.  */
318           if (oper->flags & OPERAND_SIGNED || bits == 32)
319             {
320               long max;
321               max = (1 << (bits - 1));
322               if (val & max)
323                 {
324                   if (bits == 32)
325                     val = -val;
326                   else
327                     val = -val & ((1 << bits)-1);
328                   neg = 1;
329                 }
330             }
331           if (neg)
332             {
333               (*info->fprintf_func) (info->stream, "-%x\t(",val);
334               (*info->print_address_func) ((memaddr - val) & PC_MASK, info);
335               (*info->fprintf_func) (info->stream, ")");
336             }
337           else
338             {
339               (*info->fprintf_func) (info->stream, "%x\t(",val);
340               (*info->print_address_func) ((memaddr + val) & PC_MASK, info);
341               (*info->fprintf_func) (info->stream, ")");
342             }
343         }
344       else if (insn->op->reloc_flag == RELOC_ABS)
345         {
346           (*info->print_address_func) (val, info);
347         }
348       else
349         {
350           if (oper->flags & OPERAND_SIGNED)
351             {
352               int max = (1 << (bits - 1));
353               if (val & max)
354                 {
355                   val = -val;
356                   if (bits < 32)
357                     val &= ((1 << bits) - 1);
358                   (*info->fprintf_func) (info->stream, "-");
359                 }
360             }
361           (*info->fprintf_func) (info->stream, "0x%x",val);
362         }
363       /* if there is another operand, then write a comma and space */
364       if (insn->form->operands[opind] && !(found_control && opind == 2))
365         need_comma = 1;
366     }
367   if (need_paren)
368     (*info->fprintf_func) (info->stream, ")");
369 }
370
371
372
373 static int
374 extract_value (num, oper, is_long)
375      long long num;
376      struct d30v_operand *oper;
377      int is_long;
378 {
379   int val;
380   int shift = 12 - oper->position;
381   int mask = (0xFFFFFFFF >> (32 - oper->bits));
382
383   if (is_long)
384     {
385       if (oper->bits == 32) 
386         {
387           /* piece together 32-bit constant */
388           val = ((num & 0x3FFFF)
389                  | ((num & 0xFF00000) >> 2)
390                  | ((num & 0x3F00000000LL) >> 6));
391         }
392       else      
393         val = (num >> (32 + shift)) & mask;     
394     }
395   else
396     val = (num >> shift) & mask;
397
398   if (oper->flags & OPERAND_SHIFT)
399     val <<= 3;
400
401   return val;
402 }