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