1/* Disassembly routines for TMS320C54X architecture
2   Copyright 1999, 2000, 2001 Free Software Foundation, Inc.
3   Contributed by Timothy Wall (twall@cygnus.com)
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 <stdlib.h>
23#include "sysdep.h"
24#include "dis-asm.h"
25#include "opcode/tic54x.h"
26#include "coff/tic54x.h"
27
28static int has_lkaddr (unsigned short, const template *);
29static int get_insn_size (unsigned short, const template *);
30static int print_instruction (disassemble_info *, bfd_vma,
31                              unsigned short, const char *,
32                              const enum optype [], int, int);
33static int print_parallel_instruction (disassemble_info *, bfd_vma,
34                                       unsigned short,
35                                       const template *, int);
36static int sprint_dual_address (disassemble_info *,char [],
37                                unsigned short);
38static int sprint_indirect_address (disassemble_info *,char [],
39                                    unsigned short);
40static int sprint_direct_address (disassemble_info *,char [],
41                                  unsigned short);
42static int sprint_mmr (disassemble_info *,char [],int);
43static int sprint_condition (disassemble_info *,char *,unsigned short);
44static int sprint_cc2 (disassemble_info *,char *,unsigned short);
45
46int
47print_insn_tic54x (bfd_vma memaddr, disassemble_info *info)
48{
49  bfd_byte opbuf[2];
50  unsigned short opcode;
51  int status, size;
52  const template* tm;
53
54  status = (*info->read_memory_func) (memaddr, opbuf, 2, info);
55  if (status != 0)
56  {
57    (*info->memory_error_func) (status, memaddr, info);
58    return -1;
59  }
60
61  opcode = bfd_getl16 (opbuf);
62  tm = tic54x_get_insn (info, memaddr, opcode, &size);
63
64  info->bytes_per_line = 2;
65  info->bytes_per_chunk = 2;
66  info->octets_per_byte = 2;
67  info->display_endian = BFD_ENDIAN_LITTLE;
68
69  if (tm->flags & FL_PAR)
70  {
71    if (!print_parallel_instruction (info, memaddr, opcode, tm, size))
72      return -1;
73  }
74  else
75  {
76    if (!print_instruction (info, memaddr, opcode,
77                            (char *) tm->name,
78                            tm->operand_types,
79                            size, (tm->flags & FL_EXT)))
80      return -1;
81  }
82
83  return size * 2;
84}
85
86static int
87has_lkaddr (unsigned short memdata, const template *tm)
88{
89  return (IS_LKADDR (memdata)
90	  && (OPTYPE (tm->operand_types[0]) == OP_Smem
91	      || OPTYPE (tm->operand_types[1]) == OP_Smem
92	      || OPTYPE (tm->operand_types[2]) == OP_Smem
93	      || OPTYPE (tm->operand_types[1]) == OP_Sind
94              || OPTYPE (tm->operand_types[0]) == OP_Lmem
95              || OPTYPE (tm->operand_types[1]) == OP_Lmem));
96}
97
98/* always returns 1 (whether an insn template was found) since we provide an
99   "unknown instruction" template */
100const template*
101tic54x_get_insn (disassemble_info *info, bfd_vma addr,
102                 unsigned short memdata, int *size)
103{
104  const template *tm = NULL;
105
106  for (tm = tic54x_optab; tm->name; tm++)
107  {
108    if (tm->opcode == (memdata & tm->mask))
109    {
110      /* a few opcodes span two words */
111      if (tm->flags & FL_EXT)
112        {
113          /* if lk addressing is used, the second half of the opcode gets
114             pushed one word later */
115          bfd_byte opbuf[2];
116          bfd_vma addr2 = addr + 1 + has_lkaddr (memdata, tm);
117          int status = (*info->read_memory_func) (addr2, opbuf, 2, info);
118          // FIXME handle errors
119          if (status == 0)
120            {
121              unsigned short data2 = bfd_getl16 (opbuf);
122              if (tm->opcode2 == (data2 & tm->mask2))
123                {
124                  if (size) *size = get_insn_size (memdata, tm);
125                  return tm;
126                }
127            }
128        }
129      else
130        {
131          if (size) *size = get_insn_size (memdata, tm);
132          return tm;
133        }
134    }
135  }
136  for (tm = (template *) tic54x_paroptab; tm->name; tm++)
137  {
138    if (tm->opcode == (memdata & tm->mask))
139    {
140      if (size) *size = get_insn_size (memdata, tm);
141      return tm;
142    }
143  }
144
145  if (size) *size = 1;
146  return &tic54x_unknown_opcode;
147}
148
149static int
150get_insn_size (unsigned short memdata, const template *insn)
151{
152  int size;
153
154  if (insn->flags & FL_PAR)
155    {
156      /* only non-parallel instructions support lk addressing */
157      size = insn->words;
158    }
159  else
160    {
161      size = insn->words + has_lkaddr (memdata, insn);
162    }
163
164  return size;
165}
166
167int
168print_instruction (info, memaddr, opcode, tm_name, tm_operands, size, ext)
169  disassemble_info *info;
170  bfd_vma memaddr;
171  unsigned short opcode;
172  const char *tm_name;
173  const enum optype tm_operands[];
174  int size;
175  int ext;
176{
177  static int n;
178  /* string storage for multiple operands */
179  char operand[4][64] = { {0},{0},{0},{0}, };
180  bfd_byte buf[2];
181  unsigned long opcode2 = 0;
182  unsigned long lkaddr = 0;
183  enum optype src = OP_None;
184  enum optype dst = OP_None;
185  int i, shift;
186  char *comma = "";
187
188  info->fprintf_func (info->stream, "%-7s", tm_name);
189
190  if (size > 1)
191    {
192      int status = (*info->read_memory_func) (memaddr + 1, buf, 2, info);
193      if (status != 0)
194        return 0;
195      lkaddr = opcode2 = bfd_getl16 (buf);
196      if (size > 2)
197        {
198          status = (*info->read_memory_func) (memaddr + 2, buf, 2, info);
199          if (status != 0)
200            return 0;
201          opcode2 = bfd_getl16 (buf);
202        }
203    }
204
205  for (i = 0; i < MAX_OPERANDS && OPTYPE (tm_operands[i]) != OP_None; i++)
206    {
207      char *next_comma = ",";
208      int optional = (tm_operands[i] & OPT) != 0;
209
210      switch (OPTYPE (tm_operands[i]))
211        {
212        case OP_Xmem:
213          sprint_dual_address (info, operand[i], XMEM (opcode));
214          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
215          break;
216        case OP_Ymem:
217          sprint_dual_address (info, operand[i], YMEM (opcode));
218          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
219          break;
220        case OP_Smem:
221        case OP_Sind:
222        case OP_Lmem:
223          info->fprintf_func (info->stream, "%s", comma);
224          if (INDIRECT (opcode))
225            {
226              if (MOD (opcode) >= 12)
227                {
228                  bfd_vma addr = lkaddr;
229                  int arf = ARF (opcode);
230                  int mod = MOD (opcode);
231                  if (mod == 15)
232                      info->fprintf_func (info->stream, "*(");
233                  else
234                      info->fprintf_func (info->stream, "*%sar%d(",
235                                          (mod == 13 || mod == 14 ? "+" : ""),
236                                          arf);
237                  (*(info->print_address_func)) ((bfd_vma) addr, info);
238                  info->fprintf_func (info->stream, ")%s",
239                                      mod == 14 ? "%" : "");
240                }
241              else
242                {
243                  sprint_indirect_address (info, operand[i], opcode);
244                  info->fprintf_func (info->stream, "%s", operand[i]);
245                }
246            }
247          else
248          {
249            /* FIXME -- use labels (print_address_func) */
250            /* in order to do this, we need to guess what DP is */
251            sprint_direct_address (info, operand[i], opcode);
252            info->fprintf_func (info->stream, "%s", operand[i]);
253          }
254          break;
255        case OP_dmad:
256          info->fprintf_func (info->stream, "%s", comma);
257          (*(info->print_address_func)) ((bfd_vma) opcode2, info);
258          break;
259        case OP_xpmad:
260          /* upper 7 bits of address are in the opcode */
261          opcode2 += ((unsigned long) opcode & 0x7F) << 16;
262          /* fall through */
263        case OP_pmad:
264          info->fprintf_func (info->stream, "%s", comma);
265          (*(info->print_address_func)) ((bfd_vma) opcode2, info);
266          break;
267        case OP_MMRX:
268          sprint_mmr (info, operand[i], MMRX (opcode));
269          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
270          break;
271        case OP_MMRY:
272          sprint_mmr (info, operand[i], MMRY (opcode));
273          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
274          break;
275        case OP_MMR:
276          sprint_mmr (info, operand[i], MMR (opcode));
277          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
278          break;
279        case OP_PA:
280          sprintf (operand[i], "pa%d", (unsigned) opcode2);
281          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
282          break;
283        case OP_SRC:
284          src = SRC (ext ? opcode2 : opcode) ? OP_B : OP_A;
285          sprintf (operand[i], (src == OP_B) ? "b" : "a");
286          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
287          break;
288        case OP_SRC1:
289          src = SRC1 (ext ? opcode2 : opcode) ? OP_B : OP_A;
290          sprintf (operand[i], (src == OP_B) ? "b" : "a");
291          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
292          break;
293        case OP_RND:
294          dst = DST (opcode) ? OP_B : OP_A;
295          sprintf (operand[i], (dst == OP_B) ? "a" : "b");
296          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
297          break;
298        case OP_DST:
299          dst = DST (ext ? opcode2 : opcode) ? OP_B : OP_A;
300          if (!optional || dst != src)
301            {
302              sprintf (operand[i], (dst == OP_B) ? "b" : "a");
303              info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
304            }
305          else
306            next_comma = comma;
307          break;
308        case OP_B:
309          sprintf (operand[i], "b");
310          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
311          break;
312        case OP_A:
313          sprintf (operand[i], "a");
314          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
315          break;
316        case OP_ARX:
317          sprintf (operand[i], "ar%d", (int) ARX (opcode));
318          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
319          break;
320        case OP_SHIFT:
321          shift = SHIFT (ext ? opcode2 : opcode);
322          if (!optional || shift != 0)
323            {
324              sprintf (operand[i], "%d", shift);
325              info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
326            }
327          else
328            next_comma = comma;
329          break;
330        case OP_SHFT:
331          shift = SHFT (opcode);
332          if (!optional || shift != 0)
333            {
334              sprintf (operand[i], "%d", (unsigned) shift);
335              info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
336            }
337          else
338            next_comma = comma;
339          break;
340        case OP_lk:
341          sprintf (operand[i], "#%d", (int) (short) opcode2);
342          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
343          break;
344        case OP_T:
345          sprintf (operand[i], "t");
346          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
347          break;
348        case OP_TS:
349          sprintf (operand[i], "ts");
350          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
351          break;
352        case OP_k8:
353          sprintf (operand[i], "%d", (int) ((signed char) (opcode & 0xFF)));
354          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
355          break;
356        case OP_16:
357          sprintf (operand[i], "16");
358          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
359          break;
360        case OP_ASM:
361          sprintf (operand[i], "asm");
362          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
363          break;
364        case OP_BITC:
365          sprintf (operand[i], "%d", (int) (opcode & 0xF));
366          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
367          break;
368        case OP_CC:
369          /* put all CC operands in the same operand */
370          sprint_condition (info, operand[i], opcode);
371          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
372          i = MAX_OPERANDS;
373          break;
374        case OP_CC2:
375          sprint_cc2 (info, operand[i], opcode);
376          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
377          break;
378        case OP_CC3:
379        {
380          const char *code[] = { "eq", "lt", "gt", "neq" };
381          sprintf (operand[i], code[CC3 (opcode)]);
382          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
383          break;
384        }
385        case OP_123:
386          {
387            int code = (opcode >> 8) & 0x3;
388            sprintf (operand[i], "%d", (code == 0) ? 1 : (code == 2) ? 2 : 3);
389            info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
390            break;
391          }
392        case OP_k5:
393          sprintf (operand[i], "#%d",
394                   (int) (((signed char) opcode & 0x1F) << 3) >> 3);
395          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
396          break;
397        case OP_k8u:
398          sprintf (operand[i], "#%d", (unsigned) (opcode & 0xFF));
399          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
400          break;
401        case OP_k3:
402          sprintf (operand[i], "#%d", (int) (opcode & 0x7));
403          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
404          break;
405        case OP_lku:
406          sprintf (operand[i], "#%d", (unsigned) opcode2);
407          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
408          break;
409        case OP_N:
410          n = (opcode >> 9) & 0x1;
411          sprintf (operand[i], "st%d", n);
412          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
413          break;
414        case OP_SBIT:
415        {
416          const char *status0[] = {
417            "0", "1", "2", "3", "4", "5", "6", "7", "8",
418            "ovb", "ova", "c", "tc", "13", "14", "15"
419          };
420          const char *status1[] = {
421            "0", "1", "2", "3", "4",
422            "cmpt", "frct", "c16", "sxm", "ovm", "10",
423            "intm", "hm", "xf", "cpl", "braf"
424          };
425          sprintf (operand[i], "%s",
426                   n ? status1[SBIT (opcode)] : status0[SBIT (opcode)]);
427          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
428          break;
429        }
430        case OP_12:
431          sprintf (operand[i], "%d", (int) ((opcode >> 9) & 1) + 1);
432          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
433          break;
434        case OP_TRN:
435          sprintf (operand[i], "trn");
436          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
437          break;
438        case OP_DP:
439          sprintf (operand[i], "dp");
440          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
441          break;
442        case OP_k9:
443          /* FIXME-- this is DP, print the original address? */
444          sprintf (operand[i], "#%d", (int) (opcode & 0x1FF));
445          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
446          break;
447        case OP_ARP:
448          sprintf (operand[i], "arp");
449          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
450          break;
451        case OP_031:
452          sprintf (operand[i], "%d", (int) (opcode & 0x1F));
453          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
454          break;
455        default:
456          sprintf (operand[i], "??? (0x%x)", tm_operands[i]);
457          info->fprintf_func (info->stream, "%s%s", comma, operand[i]);
458          break;
459        }
460      comma = next_comma;
461    }
462  return 1;
463}
464
465static int
466print_parallel_instruction (info, memaddr, opcode, ptm, size)
467  disassemble_info *info;
468  bfd_vma memaddr;
469  unsigned short opcode;
470  const template *ptm;
471  int size;
472{
473  print_instruction (info, memaddr, opcode,
474                     ptm->name, ptm->operand_types, size, 0);
475  info->fprintf_func (info->stream, " || ");
476  return print_instruction (info, memaddr, opcode,
477                            ptm->parname, ptm->paroperand_types, size, 0);
478}
479
480static int
481sprint_dual_address (info, buf, code)
482  disassemble_info *info ATTRIBUTE_UNUSED;
483  char buf[];
484  unsigned short code;
485{
486  const char *formats[] = {
487    "*ar%d",
488    "*ar%d-",
489    "*ar%d+",
490    "*ar%d+0%%",
491  };
492  return sprintf (buf, formats[XMOD (code)], XARX (code));
493}
494
495static int
496sprint_indirect_address (info, buf, opcode)
497  disassemble_info *info ATTRIBUTE_UNUSED;
498  char buf[];
499  unsigned short opcode;
500{
501  const char *formats[] = {
502    "*ar%d",
503    "*ar%d-",
504    "*ar%d+",
505    "*+ar%d",
506    "*ar%d-0B",
507    "*ar%d-0",
508    "*ar%d+0",
509    "*ar%d+0B",
510    "*ar%d-%%",
511    "*ar%d-0%%",
512    "*ar%d+%%",
513    "*ar%d+0%%",
514  };
515  return sprintf (buf, formats[MOD (opcode)], ARF (opcode));
516}
517
518static int
519sprint_direct_address (info, buf, opcode)
520  disassemble_info *info ATTRIBUTE_UNUSED;
521  char buf[];
522  unsigned short opcode;
523{
524  /* FIXME -- look up relocation if available */
525  return sprintf (buf, "DP+0x%02x", (int) (opcode & 0x7F));
526}
527
528static int
529sprint_mmr (info, buf, mmr)
530  disassemble_info *info ATTRIBUTE_UNUSED;
531  char buf[];
532  int mmr;
533{
534  symbol *reg = (symbol *) mmregs;
535  while (reg->name != NULL)
536    {
537      if (mmr == reg->value)
538        {
539          sprintf (buf, "%s", (reg + 1)->name);
540          return 1;
541        }
542      ++reg;
543    }
544  sprintf (buf, "MMR(%d)", mmr); /* FIXME -- different targets.  */
545  return 0;
546}
547
548static int
549sprint_cc2 (info, buf, opcode)
550  disassemble_info *info ATTRIBUTE_UNUSED;
551  char *buf;
552  unsigned short opcode;
553{
554  const char *cc2[] = {
555    "??", "??", "ageq", "alt", "aneq", "aeq", "agt", "aleq",
556    "??", "??", "bgeq", "blt", "bneq", "beq", "bgt", "bleq",
557  };
558  return sprintf (buf, "%s", cc2[opcode & 0xF]);
559}
560
561static int
562sprint_condition (info, buf, opcode)
563  disassemble_info *info ATTRIBUTE_UNUSED;
564  char *buf;
565  unsigned short opcode;
566{
567  char *start = buf;
568  const char *cmp[] = {
569      "??", "??", "geq", "lt", "neq", "eq", "gt", "leq"
570  };
571  if (opcode & 0x40)
572    {
573      char acc = (opcode & 0x8) ? 'b' : 'a';
574      if (opcode & 0x7)
575          buf += sprintf (buf, "%c%s%s", acc, cmp[(opcode & 0x7)],
576                          (opcode & 0x20) ? ", " : "");
577      if (opcode & 0x20)
578          buf += sprintf (buf, "%c%s", acc, (opcode & 0x10) ? "ov" : "nov");
579    }
580  else if (opcode & 0x3F)
581    {
582      if (opcode & 0x30)
583        buf += sprintf (buf, "%s%s",
584                        ((opcode & 0x30) == 0x30) ? "tc" : "ntc",
585                        (opcode & 0x0F) ? ", " : "");
586      if (opcode & 0x0C)
587        buf += sprintf (buf, "%s%s",
588                        ((opcode & 0x0C) == 0x0C) ? "c" : "nc",
589                        (opcode & 0x03) ? ", " : "");
590      if (opcode & 0x03)
591        buf += sprintf (buf, "%s",
592                        ((opcode & 0x03) == 0x03) ? "bio" : "nbio");
593    }
594  else
595    buf += sprintf (buf, "unc");
596
597  return buf - start;
598}
599