Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* Print TI TMS320C80 (MVP) instructions |
82704155 | 2 | Copyright (C) 1996-2019 Free Software Foundation, Inc. |
252b5132 | 3 | |
9b201bb5 NC |
4 | This file is part of the GNU opcodes library. |
5 | ||
6 | This library is free software; you can redistribute it and/or modify | |
47b0e7ad | 7 | it under the terms of the GNU General Public License as published by |
9b201bb5 NC |
8 | the Free Software Foundation; either version 3, or (at your option) |
9 | any later version. | |
252b5132 | 10 | |
9b201bb5 NC |
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. | |
252b5132 | 15 | |
47b0e7ad NC |
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. */ | |
252b5132 | 20 | |
0d8dfecf | 21 | #include "sysdep.h" |
df7b86aa | 22 | #include <stdio.h> |
252b5132 | 23 | #include "opcode/tic80.h" |
88c1242d | 24 | #include "disassemble.h" |
252b5132 RH |
25 | |
26 | static int length; | |
252b5132 RH |
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. | |
c6d805e0 | 32 | Larger numbers are probably better printed as hex values. */ |
252b5132 RH |
33 | |
34 | static void | |
47b0e7ad | 35 | print_operand_integer (struct disassemble_info *info, long value) |
252b5132 RH |
36 | { |
37 | if ((value > 9999 || value < -9999)) | |
47b0e7ad | 38 | (*info->fprintf_func) (info->stream, "%#lx", value); |
252b5132 | 39 | else |
47b0e7ad | 40 | (*info->fprintf_func) (info->stream, "%ld", value); |
252b5132 | 41 | } |
252b5132 RH |
42 | \f |
43 | /* FIXME: depends upon sizeof (long) == sizeof (float) and | |
44 | also upon host floating point format matching target | |
c6d805e0 | 45 | floating point format. */ |
252b5132 RH |
46 | |
47 | static void | |
47b0e7ad | 48 | print_operand_float (struct disassemble_info *info, long value) |
252b5132 RH |
49 | { |
50 | union { float f; long l; } fval; | |
51 | ||
52 | fval.l = value; | |
c6d805e0 | 53 | (*info->fprintf_func) (info->stream, "%g", fval.f); |
252b5132 | 54 | } |
47b0e7ad | 55 | |
252b5132 | 56 | static void |
47b0e7ad | 57 | print_operand_control_register (struct disassemble_info *info, long value) |
252b5132 RH |
58 | { |
59 | const char *tmp; | |
60 | ||
61 | tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CR); | |
62 | if (tmp != NULL) | |
47b0e7ad | 63 | (*info->fprintf_func) (info->stream, "%s", tmp); |
252b5132 | 64 | else |
47b0e7ad | 65 | (*info->fprintf_func) (info->stream, "%#lx", value); |
252b5132 | 66 | } |
47b0e7ad | 67 | |
252b5132 | 68 | static void |
47b0e7ad | 69 | print_operand_condition_code (struct disassemble_info *info, long value) |
252b5132 RH |
70 | { |
71 | const char *tmp; | |
72 | ||
73 | tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CC); | |
74 | if (tmp != NULL) | |
47b0e7ad | 75 | (*info->fprintf_func) (info->stream, "%s", tmp); |
252b5132 | 76 | else |
47b0e7ad | 77 | (*info->fprintf_func) (info->stream, "%ld", value); |
252b5132 | 78 | } |
47b0e7ad | 79 | |
252b5132 | 80 | static void |
47b0e7ad | 81 | print_operand_bitnum (struct disassemble_info *info, long value) |
252b5132 RH |
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) | |
47b0e7ad | 89 | (*info->fprintf_func) (info->stream, "%s", tmp); |
252b5132 | 90 | else |
0fd3a477 | 91 | (*info->fprintf_func) (info->stream, "%d", bitnum); |
252b5132 | 92 | } |
252b5132 RH |
93 | \f |
94 | /* Print the operand as directed by the flags. */ | |
95 | ||
c6d805e0 KH |
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))) | |
252b5132 RH |
99 | |
100 | static void | |
47b0e7ad NC |
101 | print_operand (struct disassemble_info *info, |
102 | long value, | |
103 | unsigned long insn, | |
104 | const struct tic80_operand *operand, | |
105 | bfd_vma memaddr) | |
252b5132 | 106 | { |
c6d805e0 | 107 | if ((operand->flags & TIC80_OPERAND_GPR) != 0) |
252b5132 | 108 | { |
c6d805e0 | 109 | (*info->fprintf_func) (info->stream, "r%ld", value); |
252b5132 RH |
110 | if (M_SI (insn, operand) || M_LI (insn, operand)) |
111 | { | |
c6d805e0 | 112 | (*info->fprintf_func) (info->stream, ":m"); |
252b5132 RH |
113 | } |
114 | } | |
c6d805e0 | 115 | else if ((operand->flags & TIC80_OPERAND_FPA) != 0) |
47b0e7ad NC |
116 | (*info->fprintf_func) (info->stream, "a%ld", value); |
117 | ||
c6d805e0 | 118 | else if ((operand->flags & TIC80_OPERAND_PCREL) != 0) |
47b0e7ad NC |
119 | (*info->print_address_func) (memaddr + 4 * value, info); |
120 | ||
c6d805e0 | 121 | else if ((operand->flags & TIC80_OPERAND_BASEREL) != 0) |
47b0e7ad NC |
122 | (*info->print_address_func) (value, info); |
123 | ||
c6d805e0 | 124 | else if ((operand->flags & TIC80_OPERAND_BITNUM) != 0) |
47b0e7ad NC |
125 | print_operand_bitnum (info, value); |
126 | ||
c6d805e0 | 127 | else if ((operand->flags & TIC80_OPERAND_CC) != 0) |
47b0e7ad NC |
128 | print_operand_condition_code (info, value); |
129 | ||
c6d805e0 | 130 | else if ((operand->flags & TIC80_OPERAND_CR) != 0) |
47b0e7ad NC |
131 | print_operand_control_register (info, value); |
132 | ||
c6d805e0 | 133 | else if ((operand->flags & TIC80_OPERAND_FLOAT) != 0) |
47b0e7ad NC |
134 | print_operand_float (info, value); |
135 | ||
c6d805e0 | 136 | else if ((operand->flags & TIC80_OPERAND_BITFIELD)) |
47b0e7ad NC |
137 | (*info->fprintf_func) (info->stream, "%#lx", value); |
138 | ||
252b5132 | 139 | else |
47b0e7ad | 140 | print_operand_integer (info, value); |
252b5132 | 141 | |
c6d805e0 | 142 | /* If this is a scaled operand, then print the modifier. */ |
252b5132 | 143 | if (R_SCALED (insn, operand)) |
47b0e7ad NC |
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) | |
252b5132 | 162 | { |
47b0e7ad NC |
163 | (*info->memory_error_func) (status, memaddr, info); |
164 | return -1; | |
252b5132 | 165 | } |
47b0e7ad NC |
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; | |
252b5132 | 182 | } |
47b0e7ad | 183 | |
c6d805e0 | 184 | /* We have chosen an opcode table entry. */ |
252b5132 RH |
185 | |
186 | static int | |
47b0e7ad NC |
187 | print_one_instruction (struct disassemble_info *info, |
188 | bfd_vma memaddr, | |
189 | unsigned long insn, | |
190 | const struct tic80_opcode *opcode) | |
252b5132 RH |
191 | { |
192 | const struct tic80_operand *operand; | |
193 | long value; | |
194 | int status; | |
195 | const unsigned char *opindex; | |
196 | int close_paren; | |
197 | ||
c6d805e0 | 198 | (*info->fprintf_func) (info->stream, "%-10s", opcode->name); |
252b5132 | 199 | |
c6d805e0 | 200 | for (opindex = opcode->operands; *opindex != 0; opindex++) |
252b5132 RH |
201 | { |
202 | operand = tic80_operands + *opindex; | |
203 | ||
204 | /* Extract the value from the instruction. */ | |
c6d805e0 | 205 | if (operand->extract) |
47b0e7ad NC |
206 | value = (*operand->extract) (insn, NULL); |
207 | ||
c6d805e0 | 208 | else if (operand->bits == 32) |
252b5132 RH |
209 | { |
210 | status = fill_instruction (info, memaddr, (unsigned long *) &value); | |
211 | if (status == -1) | |
47b0e7ad | 212 | return status; |
252b5132 RH |
213 | } |
214 | else | |
215 | { | |
c6d805e0 | 216 | value = (insn >> operand->shift) & ((1 << operand->bits) - 1); |
47b0e7ad | 217 | |
c6d805e0 KH |
218 | if ((operand->flags & TIC80_OPERAND_SIGNED) != 0 |
219 | && (value & (1 << (operand->bits - 1))) != 0) | |
47b0e7ad | 220 | value -= 1 << operand->bits; |
252b5132 RH |
221 | } |
222 | ||
223 | /* If this operand is enclosed in parenthesis, then print | |
224 | the open paren, otherwise just print the regular comma | |
c6d805e0 | 225 | separator, except for the first operand. */ |
c6d805e0 | 226 | if ((operand->flags & TIC80_OPERAND_PARENS) == 0) |
252b5132 RH |
227 | { |
228 | close_paren = 0; | |
c6d805e0 | 229 | if (opindex != opcode->operands) |
47b0e7ad | 230 | (*info->fprintf_func) (info->stream, ","); |
252b5132 RH |
231 | } |
232 | else | |
233 | { | |
234 | close_paren = 1; | |
c6d805e0 | 235 | (*info->fprintf_func) (info->stream, "("); |
252b5132 RH |
236 | } |
237 | ||
238 | print_operand (info, value, insn, operand, memaddr); | |
239 | ||
240 | /* If we printed an open paren before printing this operand, close | |
c6d805e0 | 241 | it now. The flag gets reset on each loop. */ |
252b5132 | 242 | if (close_paren) |
47b0e7ad | 243 | (*info->fprintf_func) (info->stream, ")"); |
252b5132 | 244 | } |
47b0e7ad NC |
245 | |
246 | return length; | |
252b5132 | 247 | } |
252b5132 | 248 | \f |
252b5132 RH |
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 | |
c6d805e0 | 253 | two valid instructions will have non-zero in both. */ |
252b5132 RH |
254 | |
255 | #define TWO_INSN(insn) ((((insn) & (0x1F << 27)) != 0) && (((insn) & (0x1F << 22)) != 0)) | |
256 | ||
257 | static int | |
47b0e7ad NC |
258 | print_instruction (struct disassemble_info *info, |
259 | bfd_vma memaddr, | |
260 | unsigned long insn, | |
261 | const struct tic80_opcode *vec_opcode) | |
252b5132 RH |
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 | |
c6d805e0 | 270 | about it until other TIc80 support is finished. */ |
252b5132 RH |
271 | |
272 | opcode_end = tic80_opcodes + tic80_num_opcodes; | |
273 | for (opcode = tic80_opcodes; opcode < opcode_end; opcode++) | |
274 | { | |
c6d805e0 | 275 | if ((insn & opcode->mask) == opcode->opcode && |
252b5132 | 276 | opcode != vec_opcode) |
47b0e7ad | 277 | break; |
252b5132 RH |
278 | } |
279 | ||
280 | if (opcode == opcode_end) | |
281 | { | |
c6d805e0 KH |
282 | /* No match found, just print the bits as a .word directive. */ |
283 | (*info->fprintf_func) (info->stream, ".word %#08lx", insn); | |
252b5132 RH |
284 | } |
285 | else | |
286 | { | |
287 | /* Match found, decode the instruction. */ | |
288 | length = print_one_instruction (info, memaddr, insn, opcode); | |
c6d805e0 | 289 | if (opcode->flags & TIC80_VECTOR && vec_opcode == NULL && TWO_INSN (insn)) |
252b5132 RH |
290 | { |
291 | /* There is another instruction to print from the same opcode. | |
292 | Print the separator and then find and print the other | |
c6d805e0 KH |
293 | instruction. */ |
294 | (*info->fprintf_func) (info->stream, " || "); | |
252b5132 RH |
295 | length = print_instruction (info, memaddr, insn, opcode); |
296 | } | |
297 | } | |
252b5132 | 298 | |
47b0e7ad | 299 | return length; |
252b5132 | 300 | } |
252b5132 | 301 | \f |
c6d805e0 | 302 | int |
47b0e7ad | 303 | print_insn_tic80 (bfd_vma memaddr, struct disassemble_info *info) |
252b5132 RH |
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) | |
47b0e7ad NC |
312 | status = print_instruction (info, memaddr, insn, NULL); |
313 | ||
314 | return status; | |
252b5132 | 315 | } |