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