OSDN Git Service

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