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