1/* Print TI TMS320C80 (MVP) instructions
2   Copyright (C) 1996-2017 Free Software Foundation, Inc.
3
4   This file is part of the GNU opcodes library.
5
6   This library is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3, or (at your option)
9   any later version.
10
11   It is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
14   License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21#include "sysdep.h"
22#include <stdio.h>
23#include "opcode/tic80.h"
24#include "dis-asm.h"
25
26static int length;
27
28/* Print an integer operand.  Try to be somewhat smart about the
29   format by assuming that small positive or negative integers are
30   probably loop increment values, structure offsets, or similar
31   values that are more meaningful printed as signed decimal values.
32   Larger numbers are probably better printed as hex values.  */
33
34static void
35print_operand_integer (struct disassemble_info *info, long value)
36{
37  if ((value > 9999 || value < -9999))
38    (*info->fprintf_func) (info->stream, "%#lx", value);
39  else
40    (*info->fprintf_func) (info->stream, "%ld", value);
41}
42
43/* FIXME: depends upon sizeof (long) == sizeof (float) and
44   also upon host floating point format matching target
45   floating point format.  */
46
47static void
48print_operand_float (struct disassemble_info *info, long value)
49{
50  union { float f; long l; } fval;
51
52  fval.l = value;
53  (*info->fprintf_func) (info->stream, "%g", fval.f);
54}
55
56static void
57print_operand_control_register (struct disassemble_info *info, long value)
58{
59  const char *tmp;
60
61  tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CR);
62  if (tmp != NULL)
63    (*info->fprintf_func) (info->stream, "%s", tmp);
64  else
65    (*info->fprintf_func) (info->stream, "%#lx", value);
66}
67
68static void
69print_operand_condition_code (struct disassemble_info *info, long value)
70{
71  const char *tmp;
72
73  tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CC);
74  if (tmp != NULL)
75    (*info->fprintf_func) (info->stream, "%s", tmp);
76  else
77    (*info->fprintf_func) (info->stream, "%ld", value);
78}
79
80static void
81print_operand_bitnum (struct disassemble_info *info, long value)
82{
83  int bitnum;
84  const char *tmp;
85
86  bitnum = ~value & 0x1F;
87  tmp = tic80_value_to_symbol (bitnum, TIC80_OPERAND_BITNUM);
88  if (tmp != NULL)
89    (*info->fprintf_func) (info->stream, "%s", tmp);
90  else
91    (*info->fprintf_func) (info->stream, "%d", bitnum);
92}
93
94/* Print the operand as directed by the flags.  */
95
96#define M_SI(insn,op) ((((op)->flags & TIC80_OPERAND_M_SI) != 0) && ((insn) & (1 << 17)))
97#define M_LI(insn,op) ((((op)->flags & TIC80_OPERAND_M_LI) != 0) && ((insn) & (1 << 15)))
98#define R_SCALED(insn,op) ((((op)->flags & TIC80_OPERAND_SCALED) != 0) && ((insn) & (1 << 11)))
99
100static void
101print_operand (struct disassemble_info *info,
102	       long value,
103	       unsigned long insn,
104	       const struct tic80_operand *operand,
105	       bfd_vma memaddr)
106{
107  if ((operand->flags & TIC80_OPERAND_GPR) != 0)
108    {
109      (*info->fprintf_func) (info->stream, "r%ld", value);
110      if (M_SI (insn, operand) || M_LI (insn, operand))
111	{
112	  (*info->fprintf_func) (info->stream, ":m");
113	}
114    }
115  else if ((operand->flags & TIC80_OPERAND_FPA) != 0)
116    (*info->fprintf_func) (info->stream, "a%ld", value);
117
118  else if ((operand->flags & TIC80_OPERAND_PCREL) != 0)
119    (*info->print_address_func) (memaddr + 4 * value, info);
120
121  else if ((operand->flags & TIC80_OPERAND_BASEREL) != 0)
122    (*info->print_address_func) (value, info);
123
124  else if ((operand->flags & TIC80_OPERAND_BITNUM) != 0)
125    print_operand_bitnum (info, value);
126
127  else if ((operand->flags & TIC80_OPERAND_CC) != 0)
128    print_operand_condition_code (info, value);
129
130  else if ((operand->flags & TIC80_OPERAND_CR) != 0)
131    print_operand_control_register (info, value);
132
133  else if ((operand->flags & TIC80_OPERAND_FLOAT) != 0)
134    print_operand_float (info, value);
135
136  else if ((operand->flags & TIC80_OPERAND_BITFIELD))
137    (*info->fprintf_func) (info->stream, "%#lx", value);
138
139  else
140    print_operand_integer (info, value);
141
142  /* If this is a scaled operand, then print the modifier.  */
143  if (R_SCALED (insn, operand))
144    (*info->fprintf_func) (info->stream, ":s");
145}
146
147/* Get the next 32 bit word from the instruction stream and convert it
148   into internal format in the unsigned long INSN, for which we are
149   passed the address.  Return 0 on success, -1 on error.  */
150
151static int
152fill_instruction (struct disassemble_info *info,
153		  bfd_vma memaddr,
154		  unsigned long *insnp)
155{
156  bfd_byte buffer[4];
157  int status;
158
159  /* Get the bits for the next 32 bit word and put in buffer.  */
160  status = (*info->read_memory_func) (memaddr + length, buffer, 4, info);
161  if (status != 0)
162    {
163      (*info->memory_error_func) (status, memaddr, info);
164      return -1;
165    }
166
167  /* Read was successful, so increment count of bytes read and convert
168     the bits into internal format.  */
169
170  length += 4;
171  if (info->endian == BFD_ENDIAN_LITTLE)
172    *insnp = bfd_getl32 (buffer);
173
174  else if (info->endian == BFD_ENDIAN_BIG)
175    *insnp = bfd_getb32 (buffer);
176
177  else
178    /* FIXME: Should probably just default to one or the other.  */
179    abort ();
180
181  return 0;
182}
183
184/* We have chosen an opcode table entry.  */
185
186static int
187print_one_instruction (struct disassemble_info *info,
188		       bfd_vma memaddr,
189		       unsigned long insn,
190		       const struct tic80_opcode *opcode)
191{
192  const struct tic80_operand *operand;
193  long value;
194  int status;
195  const unsigned char *opindex;
196  int close_paren;
197
198  (*info->fprintf_func) (info->stream, "%-10s", opcode->name);
199
200  for (opindex = opcode->operands; *opindex != 0; opindex++)
201    {
202      operand = tic80_operands + *opindex;
203
204      /* Extract the value from the instruction.  */
205      if (operand->extract)
206	value = (*operand->extract) (insn, NULL);
207
208      else if (operand->bits == 32)
209	{
210	  status = fill_instruction (info, memaddr, (unsigned long *) &value);
211	  if (status == -1)
212	    return status;
213	}
214      else
215	{
216	  value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
217
218	  if ((operand->flags & TIC80_OPERAND_SIGNED) != 0
219	      && (value & (1 << (operand->bits - 1))) != 0)
220	    value -= 1 << operand->bits;
221	}
222
223      /* If this operand is enclosed in parenthesis, then print
224	 the open paren, otherwise just print the regular comma
225	 separator, except for the first operand.  */
226      if ((operand->flags & TIC80_OPERAND_PARENS) == 0)
227	{
228	  close_paren = 0;
229	  if (opindex != opcode->operands)
230	    (*info->fprintf_func) (info->stream, ",");
231	}
232      else
233	{
234	  close_paren = 1;
235	  (*info->fprintf_func) (info->stream, "(");
236	}
237
238      print_operand (info, value, insn, operand, memaddr);
239
240      /* If we printed an open paren before printing this operand, close
241	 it now. The flag gets reset on each loop.  */
242      if (close_paren)
243	(*info->fprintf_func) (info->stream, ")");
244    }
245
246  return length;
247}
248
249/* There are no specific bits that tell us for certain whether a vector
250   instruction opcode contains one or two instructions.  However since
251   a destination register of r0 is illegal, we can check for nonzero
252   values in both destination register fields.  Only opcodes that have
253   two valid instructions will have non-zero in both.  */
254
255#define TWO_INSN(insn) ((((insn) & (0x1F << 27)) != 0) && (((insn) & (0x1F << 22)) != 0))
256
257static int
258print_instruction (struct disassemble_info *info,
259		   bfd_vma memaddr,
260		   unsigned long insn,
261		   const struct tic80_opcode *vec_opcode)
262{
263  const struct tic80_opcode *opcode;
264  const struct tic80_opcode *opcode_end;
265
266  /* Find the first opcode match in the opcodes table.  For vector
267     opcodes (vec_opcode != NULL) find the first match that is not the
268     previously found match.  FIXME: there should be faster ways to
269     search (hash table or binary search), but don't worry too much
270     about it until other TIc80 support is finished.  */
271
272  opcode_end = tic80_opcodes + tic80_num_opcodes;
273  for (opcode = tic80_opcodes; opcode < opcode_end; opcode++)
274    {
275      if ((insn & opcode->mask) == opcode->opcode &&
276	  opcode != vec_opcode)
277	break;
278    }
279
280  if (opcode == opcode_end)
281    {
282      /* No match found, just print the bits as a .word directive.  */
283      (*info->fprintf_func) (info->stream, ".word %#08lx", insn);
284    }
285  else
286    {
287      /* Match found, decode the instruction.  */
288      length = print_one_instruction (info, memaddr, insn, opcode);
289      if (opcode->flags & TIC80_VECTOR && vec_opcode == NULL && TWO_INSN (insn))
290	{
291	  /* There is another instruction to print from the same opcode.
292	     Print the separator and then find and print the other
293	     instruction.  */
294	  (*info->fprintf_func) (info->stream, "   ||   ");
295	  length = print_instruction (info, memaddr, insn, opcode);
296	}
297    }
298
299  return length;
300}
301
302int
303print_insn_tic80 (bfd_vma memaddr, struct disassemble_info *info)
304{
305  unsigned long insn;
306  int status;
307
308  length = 0;
309  info->bytes_per_line = 8;
310  status = fill_instruction (info, memaddr, &insn);
311  if (status != -1)
312    status = print_instruction (info, memaddr, insn, NULL);
313
314  return status;
315}
316