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