1 /* Print TI TMS320C80 (MVP) instructions
2    Copyright (C) 1996-2016 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 
26 static 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 
34 static void
print_operand_integer(struct disassemble_info * info,long value)35 print_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 
47 static void
print_operand_float(struct disassemble_info * info,long value)48 print_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 
56 static void
print_operand_control_register(struct disassemble_info * info,long value)57 print_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 
68 static void
print_operand_condition_code(struct disassemble_info * info,long value)69 print_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 
80 static void
print_operand_bitnum(struct disassemble_info * info,long value)81 print_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 
100 static void
print_operand(struct disassemble_info * info,long value,unsigned long insn,const struct tic80_operand * operand,bfd_vma memaddr)101 print_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 
151 static int
fill_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long * insnp)152 fill_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 
186 static int
print_one_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long insn,const struct tic80_opcode * opcode)187 print_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 
257 static int
print_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long insn,const struct tic80_opcode * vec_opcode)258 print_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 
302 int
print_insn_tic80(bfd_vma memaddr,struct disassemble_info * info)303 print_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