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 
25 static 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 
33 static void
34 print_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 
46 static void
47 print_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 
55 static void
56 print_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 
67 static void
68 print_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 
79 static void
80 print_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 
99 static void
100 print_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 
150 static int
151 fill_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 
185 static int
186 print_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 
256 static int
257 print_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 
301 int
302 print_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