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