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
39typedef unsigned long dwordU;
40typedef unsigned short wordU;
41
42typedef struct
43{
44  dwordU val;
45  int nbits;
46} parameter;
47
48/* Structure to hold valid 'cinv' instruction options.  */
49
50typedef struct
51  {
52    /* Cinv printed string.  */
53    char *str;
54    /* Value corresponding to the string.  */
55    unsigned int value;
56  }
57cinv_entry;
58
59/* CRX 'cinv' options.  */
60const 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.  */
69typedef 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  }
80REG_ARG_TYPE;
81
82/* Number of valid 'cinv' instruction options.  */
83int NUMCINVS = ((sizeof crx_cinvs)/(sizeof crx_cinvs[0]));
84/* Current opcode table entry we're disassembling.  */
85const inst *instruction;
86/* Current instruction we're disassembling.  */
87ins currInsn;
88/* The current instruction is read into 3 consecutive words.  */
89wordU words[3];
90/* Contains all words in appropriate order.  */
91ULONGLONG allWords;
92/* Holds the current processed argument number.  */
93int processing_argument_number;
94/* Nonzero means a CST4 instruction.  */
95int cst4flag;
96/* Nonzero means the instruction's original size is
97   incremented (escape sequence is used).  */
98int size_changed;
99
100static int get_number_of_operands (void);
101static argtype getargtype     (operand_type);
102static int getbits	      (operand_type);
103static char *getregname	      (reg);
104static char *getcopregname    (copreg, reg_type);
105static char * getprocregname  (int);
106static char *gettrapstring    (unsigned);
107static char *getcinvstring    (unsigned);
108static void getregliststring  (int, char *, enum REG_ARG_TYPE);
109static wordU get_word_at_PC   (bfd_vma, struct disassemble_info *);
110static void get_words_at_PC   (bfd_vma, struct disassemble_info *);
111static unsigned long build_mask (void);
112static int powerof2	      (int);
113static int match_opcode	      (void);
114static void make_instruction  (void);
115static void print_arguments   (ins *, bfd_vma, struct disassemble_info *);
116static void print_arg	      (argument *, bfd_vma, struct disassemble_info *);
117
118/* Retrieve the number of operands for the current assembled instruction.  */
119
120static int
121get_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
133static int
134getbits (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
144static argtype
145getargtype (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
156static char *
157gettrapstring (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
171static char *
172getcinvstring (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
185char *
186getregname (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
198char *
199getcopregname (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
216static char *
217getprocregname (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
230static int
231powerof2 (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
243void
244getregliststring (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
304static parameter
305makelongparameter (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
317static unsigned long
318build_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
351static int
352match_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
376static void
377make_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
498static void
499print_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
635static void
636print_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
653static void
654make_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
683static wordU
684get_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
700static void
701get_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
715int
716print_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}
745