Add new port: crx-elf
[external/binutils.git] / opcodes / crx-dis.c
1 /* Disassembler code for CRX.
2    Copyright 2004 Free Software Foundation, Inc.
3    Contributed by Tomer Levi, NSC, Israel.
4    Written by Tomer Levi.
5
6    This file is part of the GNU binutils and GDB, the GNU debugger.
7
8    This program is free software; you can redistribute it and/or modify it under
9    the terms of the GNU General Public License as published by the Free
10    Software Foundation; either version 2, or (at your option)
11    any later version.
12
13    This program is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16    more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
21
22 #include "dis-asm.h"
23 #include "sysdep.h"
24 #include "opcode/crx.h"
25
26 /* String to print when opcode was not matched.  */
27 #define ILLEGAL "illegal"
28   /* Escape to 16-bit immediate.  */
29 #define ESCAPE_16_BIT  0xE
30
31 /* Extract 'n_bits' from 'a' starting from offset 'offs'.  */
32 #define EXTRACT(a, offs, n_bits)            \
33   (n_bits == 32 ? (((a) >> (offs)) & ~0L)   \
34   : (((a) >> (offs)) & ((1 << (n_bits)) -1)))
35
36 /* Set Bit Mask - a mask to set all bits starting from offset 'offs'.  */
37 #define SBM(offs)  ((((1 << (32 - offs)) -1) << (offs)))
38
39 typedef unsigned long dwordU;
40 typedef unsigned short wordU;
41
42 typedef struct
43 {
44   dwordU val;
45   int nbits;
46 } parameter;
47
48 /* Structure to hold valid 'cinv' instruction options.  */
49
50 typedef struct
51   {
52     /* Cinv printed string.  */
53     char *str;
54     /* Value corresponding to the string.  */
55     unsigned int value;
56   }
57 cinv_entry;
58
59 /* CRX 'cinv' options.  */
60 const cinv_entry crx_cinvs[] =
61 {
62   {"[i]", 2}, {"[i,u]", 3}, {"[d]", 4},
63   {"[d,u]", 5}, {"[d,i]", 6}, {"[d,i,u]", 7}
64 };
65
66 /* Number of valid 'cinv' instruction options.  */
67 int NUMCINVS = ((sizeof crx_cinvs)/(sizeof crx_cinvs[0]));
68 /* Current opcode table entry we're disassembling.  */
69 const inst *instruction;
70 /* Current instruction we're disassembling.  */
71 ins currInsn;
72 /* The current instruction is read into 3 consecutive words.  */
73 wordU words[3];
74 /* Contains all words in appropriate order.  */
75 ULONGLONG allWords;
76 /* Holds the current processed argument number.  */
77 int processing_argument_number;
78 /* Nonzero means a CST4 instruction.  */
79 int cst4flag;
80 /* Nonzero means the instruction's original size is
81    incremented (escape sequence is used).  */
82 int size_changed;
83
84 static int get_number_of_operands (void);
85 static argtype getargtype     (operand_type);
86 static int getbits            (operand_type);
87 static char *getregname       (reg);
88 static char *getcopregname    (copreg, reg_type);
89 static char * getprocregname  (int);
90 static char *gettrapstring    (unsigned);
91 static char *getcinvstring    (unsigned);
92 static void getregliststring  (int, char *, int);
93 static wordU get_word_at_PC   (bfd_vma, struct disassemble_info *);
94 static void get_words_at_PC   (bfd_vma, struct disassemble_info *);
95 static unsigned long build_mask (void);
96 static int powerof2           (int);
97 static int match_opcode       (void);
98 static void make_instruction  (void);
99 static void print_arguments   (ins *, struct disassemble_info *);
100 static void print_arg         (argument *, struct disassemble_info *);
101
102 /* Retrieve the number of operands for the current assembled instruction.  */
103
104 static int
105 get_number_of_operands (void)
106 {
107   int i;
108
109   for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
110     ;
111
112   return i;
113 }
114
115 /* Return the bit size for a given operand.  */
116
117 static int
118 getbits (operand_type op)
119 {
120   if (op < MAX_OPRD)
121     return crx_optab[op].bit_size;
122   else
123     return 0;
124 }
125
126 /* Return the argument type of a given operand.  */
127
128 static argtype
129 getargtype (operand_type op)
130 {
131   if (op < MAX_OPRD)
132     return crx_optab[op].arg_type;
133   else
134     return nullargs;
135 }
136
137 /* Given the trap index in dispatch table, return its name.
138    This routine is used when disassembling the 'excp' instruction.  */
139
140 static char *
141 gettrapstring (unsigned int index)
142 {
143   const trap_entry *trap;
144
145   for (trap = crx_traps; trap < crx_traps + NUMTRAPS; trap++)
146     if (trap->entry == index)
147       return trap->name;
148
149   return ILLEGAL;
150 }
151
152 /* Given a 'cinv' instruction constant operand, return its corresponding string.
153    This routine is used when disassembling the 'cinv' instruction.  */
154
155 static char *
156 getcinvstring (unsigned int num)
157 {
158   const cinv_entry *cinv;
159
160   for (cinv = crx_cinvs; cinv < (crx_cinvs + NUMCINVS); cinv++)
161     if (cinv->value == num)
162       return cinv->str;
163
164   return ILLEGAL;
165 }
166
167 /* Given a register enum value, retrieve its name.  */
168
169 char *
170 getregname (reg r)
171 {
172   const reg_entry *reg = &crx_regtab[r];
173
174   if (reg->type != CRX_R_REGTYPE)
175     return ILLEGAL;
176   else
177     return reg->name;
178 }
179
180 /* Given a coprocessor register enum value, retrieve its name.  */
181
182 char *
183 getcopregname (copreg r, reg_type type)
184 {
185   const reg_entry *reg;
186
187   if (type == CRX_C_REGTYPE)
188     reg = &crx_copregtab[r];
189   else if (type == CRX_CS_REGTYPE)
190     reg = &crx_copregtab[r+(cs0-c0)];
191   else
192     return ILLEGAL;
193
194   return reg->name;
195 }
196
197
198 /* Getting a processor register name.  */
199
200 static char *
201 getprocregname (int index)
202 {
203   const reg_entry *r;
204
205   for (r = crx_regtab; r < crx_regtab + NUMREGS; r++)
206     if (r->image == index)
207       return r->name;
208
209   return "ILLEGAL REGISTER";
210 }
211
212 /* Get the power of two for a given integer.  */
213
214 static int
215 powerof2 (int x)
216 {
217   int product, i;
218
219   for (i = 0, product = 1; i < x; i++)
220     product *= 2;
221
222   return product;
223 }
224
225 /* Transform a register bit mask to a register list.  */
226
227 void
228 getregliststring (int trap, char *string, int core_cop)
229 {
230   char temp_string[5];
231   int i;
232
233   string[0] = '{';
234   string[1] = '\0';
235
236   for (i = 0; i < 16; i++)
237     {
238       if (trap & 0x1)
239         {
240           if (core_cop)
241             sprintf (temp_string, "r%d", i);
242           else
243             sprintf (temp_string, "c%d", i);
244           strcat (string, temp_string);
245           if (trap & 0xfffe)
246             strcat (string, ",");
247         }
248       trap = trap >> 1;
249     }
250
251   strcat (string, "}");
252 }
253
254 /* START and END are relating 'allWords' struct, which is 48 bits size.
255
256                           START|--------|END
257             +---------+---------+---------+---------+
258             |         |    V    |     A   |   L     |
259             +---------+---------+---------+---------+
260                       0         16        32        48
261     words                 [0]       [1]       [2]       */
262
263 static parameter
264 makelongparameter (ULONGLONG val, int start, int end)
265 {
266   parameter p;
267
268   p.val = (dwordU) EXTRACT(val, 48 - end, end - start);
269   p.nbits = end - start;
270   return p;
271 }
272
273 /* Build a mask of the instruction's 'constant' opcode,
274    based on the instruction's printing flags.  */
275
276 static unsigned long
277 build_mask (void)
278 {
279   unsigned int print_flags;
280   unsigned long mask;
281
282   print_flags = instruction->flags & FMT_CRX;
283   switch (print_flags)
284     {
285       case FMT_1:
286         mask = 0xF0F00000;
287         break;
288       case FMT_2:
289         mask = 0xFFF0FF00;
290         break;
291       case FMT_3:
292         mask = 0xFFF00F00;
293         break;
294       case FMT_4:
295         mask = 0xFFF0F000;
296         break;
297       case FMT_5:
298         mask = 0xFFF0FFF0;
299         break;
300       default:
301         mask = SBM(instruction->match_bits);
302         break;
303     }
304
305   return mask;
306 }
307
308 /* Search for a matching opcode. Return 1 for success, 0 for failure.  */
309
310 static int
311 match_opcode (void)
312 {
313   unsigned long mask;
314
315   /* The instruction 'constant' opcode doewsn't exceed 32 bits.  */
316   unsigned long doubleWord = words[1] + (words[0] << 16);
317
318   /* Start searching from end of instruction table.  */
319   instruction = &crx_instruction[NUMOPCODES - 2];
320
321   /* Loop over instruction table until a full match is found.  */
322   while (instruction >= crx_instruction)
323     {
324       mask = build_mask ();
325       if ((doubleWord & mask) == BIN(instruction->match, instruction->match_bits))
326         return 1;
327       else
328         instruction--;
329     }
330   return 0;
331 }
332
333 /* Set the proper parameter value for different type of arguments.  */
334
335 static void
336 make_argument (argument * a, int start_bits)
337 {
338   int inst_bit_size, total_size;
339   parameter p;
340
341   if ((instruction->size == 3) && a->size >= 16)
342     inst_bit_size = 48;
343   else
344     inst_bit_size = 32;
345
346   switch (a->type)
347     {
348     case arg_copr:
349     case arg_copsr:
350       p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
351                              inst_bit_size - start_bits);
352       a->cr = p.val;
353       break;
354
355     case arg_r:
356       p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
357                              inst_bit_size - start_bits);
358       a->r = p.val;
359       break;
360
361     case arg_ic:
362       p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
363                              inst_bit_size - start_bits);
364
365       if ((p.nbits == 4) && cst4flag)
366         {
367           if (IS_INSN_TYPE (CMPBR_INS) && (p.val == ESCAPE_16_BIT))
368             {
369               /* A special case, where the value is actually stored
370                  in the last 4 bits.  */
371               p = makelongparameter (allWords, 44, 48);
372               /* The size of the instruction should be incremented.  */
373               size_changed = 1;
374             }
375
376           if (p.val == 6)
377             p.val = -1;
378           else if (p.val == 13)
379             p.val = 48;
380           else if (p.val == 5)
381             p.val = -4;
382           else if (p.val == 10)
383             p.val = 32;
384           else if (p.val == 11)
385             p.val = 20;
386           else if (p.val == 9)
387             p.val = 16;
388         }
389
390       a->constant = p.val;
391       break;
392
393     case arg_icr:
394       a->scale = 0;
395       total_size = a->size + 10;  /* sizeof(rbase + ridx + scl2) = 10.  */
396       p = makelongparameter (allWords, inst_bit_size - total_size,
397                              inst_bit_size - (total_size - 4));
398       a->r = p.val;
399       p = makelongparameter (allWords, inst_bit_size - (total_size - 4),
400                              inst_bit_size - (total_size - 8));
401       a->i_r = p.val;
402       p = makelongparameter (allWords, inst_bit_size - (total_size - 8),
403                              inst_bit_size - (total_size - 10));
404       a->scale = p.val;
405       p = makelongparameter (allWords, inst_bit_size - (total_size - 10),
406                              inst_bit_size);
407       a->constant = p.val;
408       break;
409
410     case arg_rbase:
411       p = makelongparameter (allWords, inst_bit_size - (start_bits + 4),
412                              inst_bit_size - start_bits);
413       a->r = p.val;
414       break;
415
416     case arg_cr:
417       if (a->size <= 8)
418         {
419           p = makelongparameter (allWords, inst_bit_size - (start_bits + 4),
420                                  inst_bit_size - start_bits);
421           a->r = p.val;
422           /* Case for opc4 r dispu rbase.  */
423           p = makelongparameter (allWords, inst_bit_size - (start_bits + 8),
424                                  inst_bit_size - (start_bits + 4));
425         }
426       else
427         {
428           /* The 'rbase' start_bits is always relative to a 32-bit data type.  */
429           p = makelongparameter (allWords, 32 - (start_bits + 4),
430                                  32 - start_bits);
431           a->r = p.val;
432           p = makelongparameter (allWords, 32 - start_bits,
433                                  inst_bit_size);
434         }
435       if ((p.nbits == 4) && cst4flag)
436         {
437           if (instruction->flags & DISPUW4)
438             p.val *= 2;
439           else if (instruction->flags & DISPUD4)
440             p.val *= 4;
441         }
442       a->constant = p.val;
443       break;
444
445     case arg_c:
446       p = makelongparameter (allWords, inst_bit_size - (start_bits + a->size),
447                              inst_bit_size - start_bits);
448       a->constant = p.val;
449       break;
450     default:
451       break;
452     }
453 }
454
455 /*  Print a single argument.  */
456
457 static void
458 print_arg (argument *a, struct disassemble_info *info)
459 {
460   LONGLONG longdisp, mask;
461   char sign_flag;
462   int op_index = 0;
463   char string[200];
464   PTR stream = info->stream;
465   fprintf_ftype func = info->fprintf_func;
466
467   switch (a->type)
468     {
469     case arg_copr:
470       func (stream, "%s", getcopregname (a->cr, CRX_C_REGTYPE));
471       break;
472
473     case arg_copsr:
474       func (stream, "%s", getcopregname (a->cr, CRX_CS_REGTYPE));
475       break;
476
477     case arg_r:
478       if (IS_INSN_MNEMONIC ("mtpr") || IS_INSN_MNEMONIC ("mfpr"))
479         func (stream, "%s", getprocregname (a->r));
480       else
481         func (stream, "%s", getregname (a->r));
482       break;
483
484     case arg_ic:
485       if (IS_INSN_MNEMONIC ("excp"))
486         func (stream, "%s", gettrapstring (a->constant));
487
488       else if (IS_INSN_MNEMONIC ("cinv"))
489         func (stream, "%s", getcinvstring (a->constant));
490
491       else if (INST_HAS_REG_LIST)
492         {
493           if (!IS_INSN_TYPE (COP_REG_INS))
494             {
495               getregliststring (a->constant, string, 1);
496               func (stream, "%s", string);
497             }
498           else
499             {
500               /*  Check for proper argument number.  */
501               if (processing_argument_number == 2)
502                 {
503                   getregliststring (a->constant, string, 0);
504                   func (stream, "%s", string);
505                 }
506               else
507                 func (stream, "$0x%x", a->constant);
508             }
509         }
510       else
511         func (stream, "$0x%x", a->constant);
512       break;
513
514     case arg_icr:
515       func (stream, "0x%x(%s,%s,%d)", a->constant, getregname (a->r),
516             getregname (a->i_r), powerof2 (a->scale));
517       break;
518
519     case arg_rbase:
520       func (stream, "(%s)", getregname (a->r));
521       break;
522
523     case arg_cr:
524       func (stream, "0x%x(%s)", a->constant, getregname (a->r));
525
526       if (IS_INSN_TYPE (LD_STOR_INS_INC))
527         func (stream, "+");
528       break;
529
530     case arg_c:
531       /* Removed the *2 part as because implicit zeros are no more required.
532          Have to fix this as this needs a bit of extension in terms of branchins.
533          Have to add support for cmp and branch instructions.  */
534       if (IS_INSN_TYPE (BRANCH_INS) || IS_INSN_MNEMONIC ("bal")
535           || IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (DCR_BRANCH_INS)
536           || IS_INSN_TYPE (COP_BRANCH_INS))
537         {
538           func (stream, "%c", '*');
539           longdisp = a->constant;
540           longdisp <<= 1;
541           sign_flag = '+';
542
543           switch (a->size)
544             {
545             case 8:
546             case 16:
547             case 24:
548             case 32:
549               mask = ((LONGLONG)1 << a->size) - 1;
550               if (longdisp & ((LONGLONG)1 << a->size))
551                 {
552                   sign_flag = '-';
553                   longdisp = ~(longdisp) + 1;
554                 }
555               a->constant = (unsigned long int) (longdisp & mask);
556               break;
557             default:
558               func (stream,
559                     "Wrong offset used in branch/bal instruction");
560               break;
561             }
562
563           func (stream, "%c", sign_flag);
564         }
565       /* For branch Neq instruction it is 2*offset + 2.  */
566       if (IS_INSN_TYPE (BRANCH_NEQ_INS))
567         a->constant = 2 * a->constant + 2;
568       if (IS_INSN_TYPE (LD_STOR_INS_INC)
569           || IS_INSN_TYPE (LD_STOR_INS)
570           || IS_INSN_TYPE (STOR_IMM_INS)
571           || IS_INSN_TYPE (CSTBIT_INS))
572         {
573           op_index = instruction->flags & REVERSE_MATCH ? 0 : 1;
574           if (instruction->operands[op_index].op_type == abs16)
575             a->constant |= 0xFFFF0000;
576         }
577       func (stream, "0x%x", a->constant);
578       break;
579     default:
580       break;
581     }
582 }
583
584 /* Print all the arguments of CURRINSN instruction.  */
585
586 static void
587 print_arguments (ins *currInsn, struct disassemble_info *info)
588 {
589   int i;
590
591   for (i = 0; i < currInsn->nargs; i++)
592     {
593       processing_argument_number = i;
594
595       print_arg (&currInsn->arg[i], info);
596
597       if (i != currInsn->nargs - 1)
598         info->fprintf_func (info->stream, ", ");
599     }
600 }
601
602 /* Build the instruction's arguments.  */
603
604 static void
605 make_instruction (void)
606 {
607   int i;
608   unsigned int temp_value, shift;
609   argument a;
610
611   for (i = 0; i < currInsn.nargs; i++)
612     {
613       a.type = getargtype (instruction->operands[i].op_type);
614       if (instruction->operands[i].op_type == cst4
615           || instruction->operands[i].op_type == rbase_cst4)
616         cst4flag = 1;
617       a.size = getbits (instruction->operands[i].op_type);
618       shift = instruction->operands[i].shift;
619
620       make_argument (&a, shift);
621       currInsn.arg[i] = a;
622     }
623
624   /* Calculate instruction size (in bytes).  */
625   currInsn.size = instruction->size + (size_changed ? 1 : 0);
626   currInsn.size *= 2;
627
628   /* Swapping first and second arguments.  */
629   if (IS_INSN_TYPE (COP_BRANCH_INS))
630     {
631       temp_value = currInsn.arg[0].constant;
632       currInsn.arg[0].constant = currInsn.arg[1].constant;
633       currInsn.arg[1].constant = temp_value;
634     }
635 }
636
637 /* Retrieve a single word from a given memory address.  */
638
639 static wordU
640 get_word_at_PC (bfd_vma memaddr, struct disassemble_info *info)
641 {
642   bfd_byte buffer[4];
643   int status;
644   wordU insn = 0;
645
646   status = info->read_memory_func (memaddr, buffer, 2, info);
647
648   if (status == 0)
649     insn = (wordU) bfd_getl16 (buffer);
650
651   return insn;
652 }
653
654 /* Retrieve multiple words (3) from a given memory address.  */
655
656 static void
657 get_words_at_PC (bfd_vma memaddr, struct disassemble_info *info)
658 {
659   int i;
660   bfd_vma mem;
661
662   for (i = 0, mem = memaddr; i < 3; i++, mem += 2)
663     words[i] = get_word_at_PC (mem, info);
664
665   allWords =
666     ((ULONGLONG) words[0] << 32) + ((unsigned long) words[1] << 16) + words[2];
667 }
668
669 /* Prints the instruction by calling print_arguments after proper matching.  */
670
671 int
672 print_insn_crx (memaddr, info)
673      bfd_vma memaddr;
674      struct disassemble_info *info;
675 {
676   int is_decoded;     /* Nonzero means instruction has a match.  */
677
678   /* Initialize global variables.  */
679   cst4flag = 0;
680   size_changed = 0;
681
682   /* Retrieve the encoding from current memory location.  */
683   get_words_at_PC (memaddr, info);
684   /* Find a matching opcode in table.  */
685   is_decoded = match_opcode ();
686   /* If found, print the instruction's mnemonic and arguments.  */
687   if (is_decoded > 0 && (words[0] << 16 || words[1]) != 0)
688     {
689       info->fprintf_func (info->stream, "%s", instruction->mnemonic);
690       if ((currInsn.nargs = get_number_of_operands ()) != 0)
691         info->fprintf_func (info->stream, "\t");
692       make_instruction ();
693       print_arguments (&currInsn, info);
694       return currInsn.size;
695     }
696
697   /* No match found.  */
698   info->fprintf_func (info->stream,"%s ",ILLEGAL);
699   return 2;
700 }