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