Replace "the the" with "the"
[external/binutils.git] / opcodes / tic80-dis.c
1 /* Print TI TMS320C80 (MVP) instructions
2    Copyright (C) 1996-2019 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 "disassemble.h"
25
26 static int length;
27 \f
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
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 \f
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
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
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
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
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 \f
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
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 \f
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
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
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 \f
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
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 \f
302 int
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 }