1/* Disassembly routines for TMS320C30 architecture
2   Copyright 1998, 1999, 2000, 2002, 2005 Free Software Foundation, Inc.
3   Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
18   02110-1301, USA.  */
19
20#include <errno.h>
21#include <math.h>
22#include "sysdep.h"
23#include "dis-asm.h"
24#include "opcode/tic30.h"
25
26#define NORMAL_INSN   1
27#define PARALLEL_INSN 2
28
29/* Gets the type of instruction based on the top 2 or 3 bits of the
30   instruction word.  */
31#define GET_TYPE(insn) (insn & 0x80000000 ? insn & 0xC0000000 : insn & 0xE0000000)
32
33/* Instruction types.  */
34#define TWO_OPERAND_1 0x00000000
35#define TWO_OPERAND_2 0x40000000
36#define THREE_OPERAND 0x20000000
37#define PAR_STORE     0xC0000000
38#define MUL_ADDS      0x80000000
39#define BRANCHES      0x60000000
40
41/* Specific instruction id bits.  */
42#define NORMAL_IDEN    0x1F800000
43#define PAR_STORE_IDEN 0x3E000000
44#define MUL_ADD_IDEN   0x2C000000
45#define BR_IMM_IDEN    0x1F000000
46#define BR_COND_IDEN   0x1C3F0000
47
48/* Addressing modes.  */
49#define AM_REGISTER 0x00000000
50#define AM_DIRECT   0x00200000
51#define AM_INDIRECT 0x00400000
52#define AM_IMM      0x00600000
53
54#define P_FIELD 0x03000000
55
56#define REG_AR0 0x08
57#define LDP_INSN 0x08700000
58
59/* TMS320C30 program counter for current instruction.  */
60static unsigned int _pc;
61
62struct instruction
63{
64  int type;
65  template *tm;
66  partemplate *ptm;
67};
68
69static int
70get_tic30_instruction (unsigned long insn_word, struct instruction *insn)
71{
72  switch (GET_TYPE (insn_word))
73    {
74    case TWO_OPERAND_1:
75    case TWO_OPERAND_2:
76    case THREE_OPERAND:
77      insn->type = NORMAL_INSN;
78      {
79	template *current_optab = (template *) tic30_optab;
80
81	for (; current_optab < tic30_optab_end; current_optab++)
82	  {
83	    if (GET_TYPE (current_optab->base_opcode) == GET_TYPE (insn_word))
84	      {
85		if (current_optab->operands == 0)
86		  {
87		    if (current_optab->base_opcode == insn_word)
88		      {
89			insn->tm = current_optab;
90			break;
91		      }
92		  }
93		else if ((current_optab->base_opcode & NORMAL_IDEN) == (insn_word & NORMAL_IDEN))
94		  {
95		    insn->tm = current_optab;
96		    break;
97		  }
98	      }
99	  }
100      }
101      break;
102
103    case PAR_STORE:
104      insn->type = PARALLEL_INSN;
105      {
106	partemplate *current_optab = (partemplate *) tic30_paroptab;
107
108	for (; current_optab < tic30_paroptab_end; current_optab++)
109	  {
110	    if (GET_TYPE (current_optab->base_opcode) == GET_TYPE (insn_word))
111	      {
112		if ((current_optab->base_opcode & PAR_STORE_IDEN)
113		    == (insn_word & PAR_STORE_IDEN))
114		  {
115		    insn->ptm = current_optab;
116		    break;
117		  }
118	      }
119	  }
120      }
121      break;
122
123    case MUL_ADDS:
124      insn->type = PARALLEL_INSN;
125      {
126	partemplate *current_optab = (partemplate *) tic30_paroptab;
127
128	for (; current_optab < tic30_paroptab_end; current_optab++)
129	  {
130	    if (GET_TYPE (current_optab->base_opcode) == GET_TYPE (insn_word))
131	      {
132		if ((current_optab->base_opcode & MUL_ADD_IDEN)
133		    == (insn_word & MUL_ADD_IDEN))
134		  {
135		    insn->ptm = current_optab;
136		    break;
137		  }
138	      }
139	  }
140      }
141      break;
142
143    case BRANCHES:
144      insn->type = NORMAL_INSN;
145      {
146	template *current_optab = (template *) tic30_optab;
147
148	for (; current_optab < tic30_optab_end; current_optab++)
149	  {
150	    if (GET_TYPE (current_optab->base_opcode) == GET_TYPE (insn_word))
151	      {
152		if (current_optab->operand_types[0] & Imm24)
153		  {
154		    if ((current_optab->base_opcode & BR_IMM_IDEN)
155			== (insn_word & BR_IMM_IDEN))
156		      {
157			insn->tm = current_optab;
158			break;
159		      }
160		  }
161		else if (current_optab->operands > 0)
162		  {
163		    if ((current_optab->base_opcode & BR_COND_IDEN)
164			== (insn_word & BR_COND_IDEN))
165		      {
166			insn->tm = current_optab;
167			break;
168		      }
169		  }
170		else
171		  {
172		    if ((current_optab->base_opcode & (BR_COND_IDEN | 0x00800000))
173			== (insn_word & (BR_COND_IDEN | 0x00800000)))
174		      {
175			insn->tm = current_optab;
176			break;
177		      }
178		  }
179	      }
180	  }
181      }
182      break;
183    default:
184      return 0;
185    }
186  return 1;
187}
188
189static int
190get_register_operand (unsigned char fragment, char *buffer)
191{
192  const reg *current_reg = tic30_regtab;
193
194  if (buffer == NULL)
195    return 0;
196  for (; current_reg < tic30_regtab_end; current_reg++)
197    {
198      if ((fragment & 0x1F) == current_reg->opcode)
199	{
200	  strcpy (buffer, current_reg->name);
201	  return 1;
202	}
203    }
204  return 0;
205}
206
207static int
208get_indirect_operand (unsigned short fragment,
209		      int size,
210		      char *buffer)
211{
212  unsigned char mod;
213  unsigned arnum;
214  unsigned char disp;
215
216  if (buffer == NULL)
217    return 0;
218  /* Determine which bits identify the sections of the indirect
219     operand based on the size in bytes.  */
220  switch (size)
221    {
222    case 1:
223      mod = (fragment & 0x00F8) >> 3;
224      arnum = (fragment & 0x0007);
225      disp = 0;
226      break;
227    case 2:
228      mod = (fragment & 0xF800) >> 11;
229      arnum = (fragment & 0x0700) >> 8;
230      disp = (fragment & 0x00FF);
231      break;
232    default:
233      return 0;
234    }
235  {
236    const ind_addr_type *current_ind = tic30_indaddr_tab;
237
238    for (; current_ind < tic30_indaddrtab_end; current_ind++)
239      {
240	if (current_ind->modfield == mod)
241	  {
242	    if (current_ind->displacement == IMPLIED_DISP && size == 2)
243	      continue;
244
245	    else
246	      {
247		size_t i, len;
248		int bufcnt;
249
250		len = strlen (current_ind->syntax);
251		for (i = 0, bufcnt = 0; i < len; i++, bufcnt++)
252		  {
253		    buffer[bufcnt] = current_ind->syntax[i];
254		    if (buffer[bufcnt - 1] == 'a' && buffer[bufcnt] == 'r')
255		      buffer[++bufcnt] = arnum + '0';
256		    if (buffer[bufcnt] == '('
257			&& current_ind->displacement == DISP_REQUIRED)
258		      {
259			sprintf (&buffer[bufcnt + 1], "%u", disp);
260			bufcnt += strlen (&buffer[bufcnt + 1]);
261		      }
262		  }
263		buffer[bufcnt + 1] = '\0';
264		break;
265	      }
266	  }
267      }
268  }
269  return 1;
270}
271
272static int
273cnvt_tmsfloat_ieee (unsigned long tmsfloat, int size, float *ieeefloat)
274{
275  unsigned long exp, sign, mant;
276  union
277  {
278    unsigned long l;
279    float f;
280  } val;
281
282  if (size == 2)
283    {
284      if ((tmsfloat & 0x0000F000) == 0x00008000)
285	tmsfloat = 0x80000000;
286      else
287	{
288	  tmsfloat <<= 16;
289	  tmsfloat = (long) tmsfloat >> 4;
290	}
291    }
292  exp = tmsfloat & 0xFF000000;
293  if (exp == 0x80000000)
294    {
295      *ieeefloat = 0.0;
296      return 1;
297    }
298  exp += 0x7F000000;
299  sign = (tmsfloat & 0x00800000) << 8;
300  mant = tmsfloat & 0x007FFFFF;
301  if (exp == 0xFF000000)
302    {
303      if (mant == 0)
304	*ieeefloat = ERANGE;
305#ifdef HUGE_VALF
306      if (sign == 0)
307	*ieeefloat = HUGE_VALF;
308      else
309	*ieeefloat = -HUGE_VALF;
310#else
311      if (sign == 0)
312	*ieeefloat = 1.0 / 0.0;
313      else
314	*ieeefloat = -1.0 / 0.0;
315#endif
316      return 1;
317    }
318  exp >>= 1;
319  if (sign)
320    {
321      mant = (~mant) & 0x007FFFFF;
322      mant += 1;
323      exp += mant & 0x00800000;
324      exp &= 0x7F800000;
325      mant &= 0x007FFFFF;
326    }
327  if (tmsfloat == 0x80000000)
328    sign = mant = exp = 0;
329  tmsfloat = sign | exp | mant;
330  val.l = tmsfloat;
331  *ieeefloat = val.f;
332  return 1;
333}
334
335static int
336print_two_operand (disassemble_info *info,
337		   unsigned long insn_word,
338		   struct instruction *insn)
339{
340  char name[12];
341  char operand[2][13] =
342  {
343    {0},
344    {0}
345  };
346  float f_number;
347
348  if (insn->tm == NULL)
349    return 0;
350  strcpy (name, insn->tm->name);
351  if (insn->tm->opcode_modifier == AddressMode)
352    {
353      int src_op, dest_op;
354      /* Determine whether instruction is a store or a normal instruction.  */
355      if ((insn->tm->operand_types[1] & (Direct | Indirect))
356	  == (Direct | Indirect))
357	{
358	  src_op = 1;
359	  dest_op = 0;
360	}
361      else
362	{
363	  src_op = 0;
364	  dest_op = 1;
365	}
366      /* Get the destination register.  */
367      if (insn->tm->operands == 2)
368	get_register_operand ((insn_word & 0x001F0000) >> 16, operand[dest_op]);
369      /* Get the source operand based on addressing mode.  */
370      switch (insn_word & AddressMode)
371	{
372	case AM_REGISTER:
373	  /* Check for the NOP instruction before getting the operand.  */
374	  if ((insn->tm->operand_types[0] & NotReq) == 0)
375	    get_register_operand ((insn_word & 0x0000001F), operand[src_op]);
376	  break;
377	case AM_DIRECT:
378	  sprintf (operand[src_op], "@0x%lX", (insn_word & 0x0000FFFF));
379	  break;
380	case AM_INDIRECT:
381	  get_indirect_operand ((insn_word & 0x0000FFFF), 2, operand[src_op]);
382	  break;
383	case AM_IMM:
384	  /* Get the value of the immediate operand based on variable type.  */
385	  switch (insn->tm->imm_arg_type)
386	    {
387	    case Imm_Float:
388	      cnvt_tmsfloat_ieee ((insn_word & 0x0000FFFF), 2, &f_number);
389	      sprintf (operand[src_op], "%2.2f", f_number);
390	      break;
391	    case Imm_SInt:
392	      sprintf (operand[src_op], "%d", (short) (insn_word & 0x0000FFFF));
393	      break;
394	    case Imm_UInt:
395	      sprintf (operand[src_op], "%lu", (insn_word & 0x0000FFFF));
396	      break;
397	    default:
398	      return 0;
399	    }
400	  /* Handle special case for LDP instruction.  */
401	  if ((insn_word & 0xFFFFFF00) == LDP_INSN)
402	    {
403	      strcpy (name, "ldp");
404	      sprintf (operand[0], "0x%06lX", (insn_word & 0x000000FF) << 16);
405	      operand[1][0] = '\0';
406	    }
407	}
408    }
409  /* Handle case for stack and rotate instructions.  */
410  else if (insn->tm->operands == 1)
411    {
412      if (insn->tm->opcode_modifier == StackOp)
413	get_register_operand ((insn_word & 0x001F0000) >> 16, operand[0]);
414    }
415  /* Output instruction to stream.  */
416  info->fprintf_func (info->stream, "   %s %s%c%s", name,
417		      operand[0][0] ? operand[0] : "",
418		      operand[1][0] ? ',' : ' ',
419		      operand[1][0] ? operand[1] : "");
420  return 1;
421}
422
423static int
424print_three_operand (disassemble_info *info,
425		     unsigned long insn_word,
426		     struct instruction *insn)
427{
428  char operand[3][13] =
429  {
430    {0},
431    {0},
432    {0}
433  };
434
435  if (insn->tm == NULL)
436    return 0;
437  switch (insn_word & AddressMode)
438    {
439    case AM_REGISTER:
440      get_register_operand ((insn_word & 0x000000FF), operand[0]);
441      get_register_operand ((insn_word & 0x0000FF00) >> 8, operand[1]);
442      break;
443    case AM_DIRECT:
444      get_register_operand ((insn_word & 0x000000FF), operand[0]);
445      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1]);
446      break;
447    case AM_INDIRECT:
448      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0]);
449      get_register_operand ((insn_word & 0x0000FF00) >> 8, operand[1]);
450      break;
451    case AM_IMM:
452      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0]);
453      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1]);
454      break;
455    default:
456      return 0;
457    }
458  if (insn->tm->operands == 3)
459    get_register_operand ((insn_word & 0x001F0000) >> 16, operand[2]);
460  info->fprintf_func (info->stream, "   %s %s,%s%c%s", insn->tm->name,
461		      operand[0], operand[1],
462		      operand[2][0] ? ',' : ' ',
463		      operand[2][0] ? operand[2] : "");
464  return 1;
465}
466
467static int
468print_par_insn (disassemble_info *info,
469		unsigned long insn_word,
470		struct instruction *insn)
471{
472  size_t i, len;
473  char *name1, *name2;
474  char operand[2][3][13] =
475  {
476    {
477      {0},
478      {0},
479      {0}
480    },
481    {
482      {0},
483      {0},
484      {0}
485    }
486  };
487
488  if (insn->ptm == NULL)
489    return 0;
490  /* Parse out the names of each of the parallel instructions from the
491     q_insn1_insn2 format.  */
492  name1 = (char *) strdup (insn->ptm->name + 2);
493  name2 = "";
494  len = strlen (name1);
495  for (i = 0; i < len; i++)
496    {
497      if (name1[i] == '_')
498	{
499	  name2 = &name1[i + 1];
500	  name1[i] = '\0';
501	  break;
502	}
503    }
504  /* Get the operands of the instruction based on the operand order.  */
505  switch (insn->ptm->oporder)
506    {
507    case OO_4op1:
508      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][0]);
509      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][1]);
510      get_register_operand ((insn_word >> 16) & 0x07, operand[1][0]);
511      get_register_operand ((insn_word >> 22) & 0x07, operand[0][1]);
512      break;
513    case OO_4op2:
514      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][0]);
515      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][0]);
516      get_register_operand ((insn_word >> 19) & 0x07, operand[1][1]);
517      get_register_operand ((insn_word >> 22) & 0x07, operand[0][1]);
518      break;
519    case OO_4op3:
520      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][1]);
521      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][1]);
522      get_register_operand ((insn_word >> 16) & 0x07, operand[1][0]);
523      get_register_operand ((insn_word >> 22) & 0x07, operand[0][0]);
524      break;
525    case OO_5op1:
526      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][0]);
527      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][1]);
528      get_register_operand ((insn_word >> 16) & 0x07, operand[1][0]);
529      get_register_operand ((insn_word >> 19) & 0x07, operand[0][1]);
530      get_register_operand ((insn_word >> 22) & 0x07, operand[0][2]);
531      break;
532    case OO_5op2:
533      get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][1]);
534      get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][1]);
535      get_register_operand ((insn_word >> 16) & 0x07, operand[1][0]);
536      get_register_operand ((insn_word >> 19) & 0x07, operand[0][0]);
537      get_register_operand ((insn_word >> 22) & 0x07, operand[0][2]);
538      break;
539    case OO_PField:
540      if (insn_word & 0x00800000)
541	get_register_operand (0x01, operand[0][2]);
542      else
543	get_register_operand (0x00, operand[0][2]);
544      if (insn_word & 0x00400000)
545	get_register_operand (0x03, operand[1][2]);
546      else
547	get_register_operand (0x02, operand[1][2]);
548      switch (insn_word & P_FIELD)
549	{
550	case 0x00000000:
551	  get_indirect_operand ((insn_word & 0x000000FF), 1, operand[0][1]);
552	  get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[0][0]);
553	  get_register_operand ((insn_word >> 16) & 0x07, operand[1][1]);
554	  get_register_operand ((insn_word >> 19) & 0x07, operand[1][0]);
555	  break;
556	case 0x01000000:
557	  get_indirect_operand ((insn_word & 0x000000FF), 1, operand[1][0]);
558	  get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[0][0]);
559	  get_register_operand ((insn_word >> 16) & 0x07, operand[1][1]);
560	  get_register_operand ((insn_word >> 19) & 0x07, operand[0][1]);
561	  break;
562	case 0x02000000:
563	  get_indirect_operand ((insn_word & 0x000000FF), 1, operand[1][1]);
564	  get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[1][0]);
565	  get_register_operand ((insn_word >> 16) & 0x07, operand[0][1]);
566	  get_register_operand ((insn_word >> 19) & 0x07, operand[0][0]);
567	  break;
568	case 0x03000000:
569	  get_indirect_operand ((insn_word & 0x000000FF), 1, operand[1][1]);
570	  get_indirect_operand ((insn_word & 0x0000FF00) >> 8, 1, operand[0][0]);
571	  get_register_operand ((insn_word >> 16) & 0x07, operand[1][0]);
572	  get_register_operand ((insn_word >> 19) & 0x07, operand[0][1]);
573	  break;
574	}
575      break;
576    default:
577      return 0;
578    }
579  info->fprintf_func (info->stream, "   %s %s,%s%c%s", name1,
580		      operand[0][0], operand[0][1],
581		      operand[0][2][0] ? ',' : ' ',
582		      operand[0][2][0] ? operand[0][2] : "");
583  info->fprintf_func (info->stream, "\n\t\t\t|| %s %s,%s%c%s", name2,
584		      operand[1][0], operand[1][1],
585		      operand[1][2][0] ? ',' : ' ',
586		      operand[1][2][0] ? operand[1][2] : "");
587  free (name1);
588  return 1;
589}
590
591static int
592print_branch (disassemble_info *info,
593	      unsigned long insn_word,
594	      struct instruction *insn)
595{
596  char operand[2][13] =
597  {
598    {0},
599    {0}
600  };
601  unsigned long address;
602  int print_label = 0;
603
604  if (insn->tm == NULL)
605    return 0;
606  /* Get the operands for 24-bit immediate jumps.  */
607  if (insn->tm->operand_types[0] & Imm24)
608    {
609      address = insn_word & 0x00FFFFFF;
610      sprintf (operand[0], "0x%lX", address);
611      print_label = 1;
612    }
613  /* Get the operand for the trap instruction.  */
614  else if (insn->tm->operand_types[0] & IVector)
615    {
616      address = insn_word & 0x0000001F;
617      sprintf (operand[0], "0x%lX", address);
618    }
619  else
620    {
621      address = insn_word & 0x0000FFFF;
622      /* Get the operands for the DB instructions.  */
623      if (insn->tm->operands == 2)
624	{
625	  get_register_operand (((insn_word & 0x01C00000) >> 22) + REG_AR0, operand[0]);
626	  if (insn_word & PCRel)
627	    {
628	      sprintf (operand[1], "%d", (short) address);
629	      print_label = 1;
630	    }
631	  else
632	    get_register_operand (insn_word & 0x0000001F, operand[1]);
633	}
634      /* Get the operands for the standard branches.  */
635      else if (insn->tm->operands == 1)
636	{
637	  if (insn_word & PCRel)
638	    {
639	      address = (short) address;
640	      sprintf (operand[0], "%ld", address);
641	      print_label = 1;
642	    }
643	  else
644	    get_register_operand (insn_word & 0x0000001F, operand[0]);
645	}
646    }
647  info->fprintf_func (info->stream, "   %s %s%c%s", insn->tm->name,
648		      operand[0][0] ? operand[0] : "",
649		      operand[1][0] ? ',' : ' ',
650		      operand[1][0] ? operand[1] : "");
651  /* Print destination of branch in relation to current symbol.  */
652  if (print_label && info->symbols)
653    {
654      asymbol *sym = *info->symbols;
655
656      if ((insn->tm->opcode_modifier == PCRel) && (insn_word & PCRel))
657	{
658	  address = (_pc + 1 + (short) address) - ((sym->section->vma + sym->value) / 4);
659	  /* Check for delayed instruction, if so adjust destination.  */
660	  if (insn_word & 0x00200000)
661	    address += 2;
662	}
663      else
664	{
665	  address -= ((sym->section->vma + sym->value) / 4);
666	}
667      if (address == 0)
668	info->fprintf_func (info->stream, " <%s>", sym->name);
669      else
670	info->fprintf_func (info->stream, " <%s %c %d>", sym->name,
671			    ((short) address < 0) ? '-' : '+',
672			    abs (address));
673    }
674  return 1;
675}
676
677int
678print_insn_tic30 (bfd_vma pc, disassemble_info *info)
679{
680  unsigned long insn_word;
681  struct instruction insn = { 0, NULL, NULL };
682  bfd_vma bufaddr = pc - info->buffer_vma;
683
684  /* Obtain the current instruction word from the buffer.  */
685  insn_word = (*(info->buffer + bufaddr) << 24) | (*(info->buffer + bufaddr + 1) << 16) |
686    (*(info->buffer + bufaddr + 2) << 8) | *(info->buffer + bufaddr + 3);
687  _pc = pc / 4;
688  /* Get the instruction refered to by the current instruction word
689     and print it out based on its type.  */
690  if (!get_tic30_instruction (insn_word, &insn))
691    return -1;
692  switch (GET_TYPE (insn_word))
693    {
694    case TWO_OPERAND_1:
695    case TWO_OPERAND_2:
696      if (!print_two_operand (info, insn_word, &insn))
697	return -1;
698      break;
699    case THREE_OPERAND:
700      if (!print_three_operand (info, insn_word, &insn))
701	return -1;
702      break;
703    case PAR_STORE:
704    case MUL_ADDS:
705      if (!print_par_insn (info, insn_word, &insn))
706	return -1;
707      break;
708    case BRANCHES:
709      if (!print_branch (info, insn_word, &insn))
710	return -1;
711      break;
712    }
713  return 4;
714}
715