Commit | Line | Data |
---|---|---|
026df7c5 NC |
1 | /* Print instructions for the Texas TMS320C[34]X, for GDB and GNU Binutils. |
2 | ||
9b201bb5 | 3 | Copyright 2002, 2003, 2005, 2007 Free Software Foundation, Inc. |
026df7c5 NC |
4 | |
5 | Contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz) | |
47b0e7ad | 6 | |
9b201bb5 NC |
7 | This file is part of the GNU opcodes library. |
8 | ||
9 | This library is free software; you can redistribute it and/or modify | |
026df7c5 | 10 | it under the terms of the GNU General Public License as published by |
9b201bb5 NC |
11 | the Free Software Foundation; either version 3, or (at your option) |
12 | any later version. | |
026df7c5 | 13 | |
9b201bb5 NC |
14 | It is distributed in the hope that it will be useful, but WITHOUT |
15 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
16 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
17 | License for more details. | |
026df7c5 NC |
18 | |
19 | You should have received a copy of the GNU General Public License | |
20 | along with this program; if not, write to the Free Software | |
47b0e7ad NC |
21 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
22 | MA 02110-1301, USA. */ | |
026df7c5 NC |
23 | |
24 | #include <math.h> | |
25 | #include "libiberty.h" | |
26 | #include "dis-asm.h" | |
27 | #include "opcode/tic4x.h" | |
28 | ||
be33c5dd | 29 | #define TIC4X_DEBUG 0 |
026df7c5 | 30 | |
be33c5dd | 31 | #define TIC4X_HASH_SIZE 11 /* 11 (bits) and above should give unique entries. */ |
47b0e7ad | 32 | #define TIC4X_SPESOP_SIZE 8 /* Max 8. ops for special instructions. */ |
026df7c5 NC |
33 | |
34 | typedef enum | |
47b0e7ad NC |
35 | { |
36 | IMMED_SINT, | |
37 | IMMED_SUINT, | |
38 | IMMED_SFLOAT, | |
39 | IMMED_INT, | |
40 | IMMED_UINT, | |
41 | IMMED_FLOAT | |
42 | } | |
026df7c5 NC |
43 | immed_t; |
44 | ||
45 | typedef enum | |
47b0e7ad NC |
46 | { |
47 | INDIRECT_SHORT, | |
48 | INDIRECT_LONG, | |
49 | INDIRECT_TIC4X | |
50 | } | |
026df7c5 NC |
51 | indirect_t; |
52 | ||
be33c5dd SS |
53 | static int tic4x_version = 0; |
54 | static int tic4x_dp = 0; | |
026df7c5 NC |
55 | |
56 | static int | |
47b0e7ad | 57 | tic4x_pc_offset (unsigned int op) |
026df7c5 NC |
58 | { |
59 | /* Determine the PC offset for a C[34]x instruction. | |
60 | This could be simplified using some boolean algebra | |
61 | but at the expense of readability. */ | |
62 | switch (op >> 24) | |
63 | { | |
64 | case 0x60: /* br */ | |
65 | case 0x62: /* call (C4x) */ | |
66 | case 0x64: /* rptb (C4x) */ | |
67 | return 1; | |
68 | case 0x61: /* brd */ | |
69 | case 0x63: /* laj */ | |
70 | case 0x65: /* rptbd (C4x) */ | |
71 | return 3; | |
72 | case 0x66: /* swi */ | |
73 | case 0x67: | |
74 | return 0; | |
75 | default: | |
76 | break; | |
77 | } | |
47b0e7ad | 78 | |
026df7c5 NC |
79 | switch ((op & 0xffe00000) >> 20) |
80 | { | |
81 | case 0x6a0: /* bB */ | |
82 | case 0x720: /* callB */ | |
83 | case 0x740: /* trapB */ | |
84 | return 1; | |
47b0e7ad | 85 | |
026df7c5 NC |
86 | case 0x6a2: /* bBd */ |
87 | case 0x6a6: /* bBat */ | |
88 | case 0x6aa: /* bBaf */ | |
89 | case 0x722: /* lajB */ | |
90 | case 0x748: /* latB */ | |
91 | case 0x798: /* rptbd */ | |
92 | return 3; | |
47b0e7ad | 93 | |
026df7c5 NC |
94 | default: |
95 | break; | |
96 | } | |
47b0e7ad | 97 | |
026df7c5 NC |
98 | switch ((op & 0xfe200000) >> 20) |
99 | { | |
100 | case 0x6e0: /* dbB */ | |
101 | return 1; | |
47b0e7ad | 102 | |
026df7c5 NC |
103 | case 0x6e2: /* dbBd */ |
104 | return 3; | |
47b0e7ad | 105 | |
026df7c5 NC |
106 | default: |
107 | break; | |
108 | } | |
47b0e7ad | 109 | |
026df7c5 NC |
110 | return 0; |
111 | } | |
112 | ||
113 | static int | |
47b0e7ad | 114 | tic4x_print_char (struct disassemble_info * info, char ch) |
026df7c5 NC |
115 | { |
116 | if (info != NULL) | |
117 | (*info->fprintf_func) (info->stream, "%c", ch); | |
118 | return 1; | |
119 | } | |
120 | ||
121 | static int | |
47b0e7ad | 122 | tic4x_print_str (struct disassemble_info *info, char *str) |
026df7c5 NC |
123 | { |
124 | if (info != NULL) | |
125 | (*info->fprintf_func) (info->stream, "%s", str); | |
126 | return 1; | |
127 | } | |
128 | ||
129 | static int | |
47b0e7ad | 130 | tic4x_print_register (struct disassemble_info *info, unsigned long regno) |
026df7c5 | 131 | { |
47b0e7ad | 132 | static tic4x_register_t ** registertable = NULL; |
026df7c5 | 133 | unsigned int i; |
47b0e7ad | 134 | |
026df7c5 NC |
135 | if (registertable == NULL) |
136 | { | |
47b0e7ad | 137 | registertable = xmalloc (sizeof (tic4x_register_t *) * REG_TABLE_SIZE); |
be33c5dd | 138 | for (i = 0; i < tic3x_num_registers; i++) |
47b0e7ad | 139 | registertable[tic3x_registers[i].regno] = (tic4x_register_t *) (tic3x_registers + i); |
be33c5dd | 140 | if (IS_CPU_TIC4X (tic4x_version)) |
026df7c5 NC |
141 | { |
142 | /* Add C4x additional registers, overwriting | |
143 | any C3x registers if necessary. */ | |
be33c5dd | 144 | for (i = 0; i < tic4x_num_registers; i++) |
47b0e7ad NC |
145 | registertable[tic4x_registers[i].regno] = |
146 | (tic4x_register_t *)(tic4x_registers + i); | |
026df7c5 NC |
147 | } |
148 | } | |
be33c5dd | 149 | if ((int) regno > (IS_CPU_TIC4X (tic4x_version) ? TIC4X_REG_MAX : TIC3X_REG_MAX)) |
026df7c5 NC |
150 | return 0; |
151 | if (info != NULL) | |
152 | (*info->fprintf_func) (info->stream, "%s", registertable[regno]->name); | |
153 | return 1; | |
154 | } | |
155 | ||
156 | static int | |
47b0e7ad | 157 | tic4x_print_addr (struct disassemble_info *info, unsigned long addr) |
026df7c5 NC |
158 | { |
159 | if (info != NULL) | |
160 | (*info->print_address_func)(addr, info); | |
161 | return 1; | |
162 | } | |
163 | ||
164 | static int | |
47b0e7ad NC |
165 | tic4x_print_relative (struct disassemble_info *info, |
166 | unsigned long pc, | |
167 | long offset, | |
168 | unsigned long opcode) | |
026df7c5 | 169 | { |
be33c5dd | 170 | return tic4x_print_addr (info, pc + offset + tic4x_pc_offset (opcode)); |
026df7c5 NC |
171 | } |
172 | ||
173 | static int | |
47b0e7ad | 174 | tic4x_print_direct (struct disassemble_info *info, unsigned long arg) |
026df7c5 NC |
175 | { |
176 | if (info != NULL) | |
177 | { | |
178 | (*info->fprintf_func) (info->stream, "@"); | |
be33c5dd | 179 | tic4x_print_addr (info, arg + (tic4x_dp << 16)); |
026df7c5 NC |
180 | } |
181 | return 1; | |
182 | } | |
47b0e7ad | 183 | #if 0 |
026df7c5 NC |
184 | /* FIXME: make the floating point stuff not rely on host |
185 | floating point arithmetic. */ | |
47b0e7ad NC |
186 | |
187 | static void | |
188 | tic4x_print_ftoa (unsigned int val, FILE *stream, fprintf_ftype pfunc) | |
026df7c5 NC |
189 | { |
190 | int e; | |
191 | int s; | |
192 | int f; | |
193 | double num = 0.0; | |
47b0e7ad NC |
194 | |
195 | e = EXTRS (val, 31, 24); /* Exponent. */ | |
026df7c5 NC |
196 | if (e != -128) |
197 | { | |
47b0e7ad NC |
198 | s = EXTRU (val, 23, 23); /* Sign bit. */ |
199 | f = EXTRU (val, 22, 0); /* Mantissa. */ | |
026df7c5 NC |
200 | if (s) |
201 | f += -2 * (1 << 23); | |
202 | else | |
203 | f += (1 << 23); | |
204 | num = f / (double)(1 << 23); | |
205 | num = ldexp (num, e); | |
47b0e7ad | 206 | } |
026df7c5 NC |
207 | (*pfunc)(stream, "%.9g", num); |
208 | } | |
47b0e7ad | 209 | #endif |
026df7c5 NC |
210 | |
211 | static int | |
47b0e7ad NC |
212 | tic4x_print_immed (struct disassemble_info *info, |
213 | immed_t type, | |
214 | unsigned long arg) | |
026df7c5 NC |
215 | { |
216 | int s; | |
217 | int f; | |
218 | int e; | |
219 | double num = 0.0; | |
47b0e7ad | 220 | |
026df7c5 NC |
221 | if (info == NULL) |
222 | return 1; | |
223 | switch (type) | |
224 | { | |
225 | case IMMED_SINT: | |
226 | case IMMED_INT: | |
0fd3a477 | 227 | (*info->fprintf_func) (info->stream, "%ld", (long) arg); |
026df7c5 | 228 | break; |
47b0e7ad | 229 | |
026df7c5 NC |
230 | case IMMED_SUINT: |
231 | case IMMED_UINT: | |
0fd3a477 | 232 | (*info->fprintf_func) (info->stream, "%lu", arg); |
026df7c5 | 233 | break; |
47b0e7ad | 234 | |
026df7c5 NC |
235 | case IMMED_SFLOAT: |
236 | e = EXTRS (arg, 15, 12); | |
237 | if (e != -8) | |
238 | { | |
239 | s = EXTRU (arg, 11, 11); | |
240 | f = EXTRU (arg, 10, 0); | |
241 | if (s) | |
242 | f += -2 * (1 << 11); | |
243 | else | |
244 | f += (1 << 11); | |
245 | num = f / (double)(1 << 11); | |
246 | num = ldexp (num, e); | |
247 | } | |
248 | (*info->fprintf_func) (info->stream, "%f", num); | |
249 | break; | |
250 | case IMMED_FLOAT: | |
251 | e = EXTRS (arg, 31, 24); | |
252 | if (e != -128) | |
253 | { | |
254 | s = EXTRU (arg, 23, 23); | |
255 | f = EXTRU (arg, 22, 0); | |
256 | if (s) | |
257 | f += -2 * (1 << 23); | |
258 | else | |
259 | f += (1 << 23); | |
260 | num = f / (double)(1 << 23); | |
261 | num = ldexp (num, e); | |
262 | } | |
263 | (*info->fprintf_func) (info->stream, "%f", num); | |
264 | break; | |
265 | } | |
266 | return 1; | |
267 | } | |
268 | ||
269 | static int | |
47b0e7ad | 270 | tic4x_print_cond (struct disassemble_info *info, unsigned int cond) |
026df7c5 | 271 | { |
be33c5dd | 272 | static tic4x_cond_t **condtable = NULL; |
026df7c5 | 273 | unsigned int i; |
47b0e7ad | 274 | |
026df7c5 NC |
275 | if (condtable == NULL) |
276 | { | |
47b0e7ad | 277 | condtable = xmalloc (sizeof (tic4x_cond_t *) * 32); |
be33c5dd | 278 | for (i = 0; i < tic4x_num_conds; i++) |
47b0e7ad | 279 | condtable[tic4x_conds[i].cond] = (tic4x_cond_t *)(tic4x_conds + i); |
026df7c5 NC |
280 | } |
281 | if (cond > 31 || condtable[cond] == NULL) | |
282 | return 0; | |
283 | if (info != NULL) | |
284 | (*info->fprintf_func) (info->stream, "%s", condtable[cond]->name); | |
285 | return 1; | |
286 | } | |
287 | ||
288 | static int | |
47b0e7ad NC |
289 | tic4x_print_indirect (struct disassemble_info *info, |
290 | indirect_t type, | |
291 | unsigned long arg) | |
026df7c5 NC |
292 | { |
293 | unsigned int aregno; | |
294 | unsigned int modn; | |
295 | unsigned int disp; | |
296 | char *a; | |
297 | ||
298 | aregno = 0; | |
299 | modn = 0; | |
300 | disp = 1; | |
301 | switch(type) | |
302 | { | |
be33c5dd | 303 | case INDIRECT_TIC4X: /* *+ARn(disp) */ |
026df7c5 NC |
304 | disp = EXTRU (arg, 7, 3); |
305 | aregno = EXTRU (arg, 2, 0) + REG_AR0; | |
306 | modn = 0; | |
307 | break; | |
308 | case INDIRECT_SHORT: | |
309 | disp = 1; | |
310 | aregno = EXTRU (arg, 2, 0) + REG_AR0; | |
311 | modn = EXTRU (arg, 7, 3); | |
312 | break; | |
313 | case INDIRECT_LONG: | |
314 | disp = EXTRU (arg, 7, 0); | |
315 | aregno = EXTRU (arg, 10, 8) + REG_AR0; | |
316 | modn = EXTRU (arg, 15, 11); | |
317 | if (modn > 7 && disp != 0) | |
318 | return 0; | |
319 | break; | |
320 | default: | |
be33c5dd SS |
321 | (*info->fprintf_func)(info->stream, "# internal error: Unknown indirect type %d", type); |
322 | return 0; | |
026df7c5 | 323 | } |
be33c5dd | 324 | if (modn > TIC3X_MODN_MAX) |
026df7c5 | 325 | return 0; |
be33c5dd | 326 | a = tic4x_indirects[modn].name; |
026df7c5 NC |
327 | while (*a) |
328 | { | |
329 | switch (*a) | |
330 | { | |
331 | case 'a': | |
be33c5dd | 332 | tic4x_print_register (info, aregno); |
026df7c5 NC |
333 | break; |
334 | case 'd': | |
be33c5dd | 335 | tic4x_print_immed (info, IMMED_UINT, disp); |
026df7c5 NC |
336 | break; |
337 | case 'y': | |
be33c5dd | 338 | tic4x_print_str (info, "ir0"); |
026df7c5 NC |
339 | break; |
340 | case 'z': | |
be33c5dd | 341 | tic4x_print_str (info, "ir1"); |
026df7c5 NC |
342 | break; |
343 | default: | |
be33c5dd | 344 | tic4x_print_char (info, *a); |
026df7c5 NC |
345 | break; |
346 | } | |
347 | a++; | |
348 | } | |
349 | return 1; | |
350 | } | |
351 | ||
352 | static int | |
47b0e7ad NC |
353 | tic4x_print_op (struct disassemble_info *info, |
354 | unsigned long instruction, | |
355 | tic4x_inst_t *p, | |
356 | unsigned long pc) | |
026df7c5 NC |
357 | { |
358 | int val; | |
359 | char *s; | |
360 | char *parallel = NULL; | |
361 | ||
362 | /* Print instruction name. */ | |
363 | s = p->name; | |
364 | while (*s && parallel == NULL) | |
365 | { | |
366 | switch (*s) | |
367 | { | |
368 | case 'B': | |
be33c5dd | 369 | if (! tic4x_print_cond (info, EXTRU (instruction, 20, 16))) |
026df7c5 NC |
370 | return 0; |
371 | break; | |
372 | case 'C': | |
be33c5dd | 373 | if (! tic4x_print_cond (info, EXTRU (instruction, 27, 23))) |
026df7c5 NC |
374 | return 0; |
375 | break; | |
376 | case '_': | |
47b0e7ad | 377 | parallel = s + 1; /* Skip past `_' in name. */ |
026df7c5 NC |
378 | break; |
379 | default: | |
be33c5dd | 380 | tic4x_print_char (info, *s); |
026df7c5 NC |
381 | break; |
382 | } | |
383 | s++; | |
384 | } | |
47b0e7ad | 385 | |
026df7c5 NC |
386 | /* Print arguments. */ |
387 | s = p->args; | |
388 | if (*s) | |
be33c5dd | 389 | tic4x_print_char (info, ' '); |
026df7c5 NC |
390 | |
391 | while (*s) | |
392 | { | |
393 | switch (*s) | |
394 | { | |
47b0e7ad | 395 | case '*': /* Indirect 0--15. */ |
be33c5dd | 396 | if (! tic4x_print_indirect (info, INDIRECT_LONG, |
47b0e7ad | 397 | EXTRU (instruction, 15, 0))) |
026df7c5 NC |
398 | return 0; |
399 | break; | |
400 | ||
47b0e7ad | 401 | case '#': /* Only used for ldp, ldpk. */ |
be33c5dd | 402 | tic4x_print_immed (info, IMMED_UINT, EXTRU (instruction, 15, 0)); |
026df7c5 NC |
403 | break; |
404 | ||
47b0e7ad | 405 | case '@': /* Direct 0--15. */ |
be33c5dd | 406 | tic4x_print_direct (info, EXTRU (instruction, 15, 0)); |
026df7c5 NC |
407 | break; |
408 | ||
47b0e7ad | 409 | case 'A': /* Address register 24--22. */ |
be33c5dd | 410 | if (! tic4x_print_register (info, EXTRU (instruction, 24, 22) + |
47b0e7ad | 411 | REG_AR0)) |
026df7c5 NC |
412 | return 0; |
413 | break; | |
414 | ||
415 | case 'B': /* 24-bit unsigned int immediate br(d)/call/rptb | |
416 | address 0--23. */ | |
be33c5dd SS |
417 | if (IS_CPU_TIC4X (tic4x_version)) |
418 | tic4x_print_relative (info, pc, EXTRS (instruction, 23, 0), | |
47b0e7ad | 419 | p->opcode); |
026df7c5 | 420 | else |
be33c5dd | 421 | tic4x_print_addr (info, EXTRU (instruction, 23, 0)); |
026df7c5 NC |
422 | break; |
423 | ||
47b0e7ad | 424 | case 'C': /* Indirect (short C4x) 0--7. */ |
be33c5dd | 425 | if (! IS_CPU_TIC4X (tic4x_version)) |
026df7c5 | 426 | return 0; |
be33c5dd | 427 | if (! tic4x_print_indirect (info, INDIRECT_TIC4X, |
47b0e7ad | 428 | EXTRU (instruction, 7, 0))) |
026df7c5 NC |
429 | return 0; |
430 | break; | |
431 | ||
432 | case 'D': | |
433 | /* Cockup if get here... */ | |
434 | break; | |
435 | ||
47b0e7ad | 436 | case 'E': /* Register 0--7. */ |
44287f60 | 437 | case 'e': |
be33c5dd | 438 | if (! tic4x_print_register (info, EXTRU (instruction, 7, 0))) |
026df7c5 NC |
439 | return 0; |
440 | break; | |
441 | ||
47b0e7ad | 442 | case 'F': /* 16-bit float immediate 0--15. */ |
be33c5dd | 443 | tic4x_print_immed (info, IMMED_SFLOAT, |
47b0e7ad | 444 | EXTRU (instruction, 15, 0)); |
026df7c5 NC |
445 | break; |
446 | ||
47b0e7ad NC |
447 | case 'i': /* Extended indirect 0--7. */ |
448 | if (EXTRU (instruction, 7, 5) == 7) | |
9c87d6c7 | 449 | { |
47b0e7ad | 450 | if (!tic4x_print_register (info, EXTRU (instruction, 4, 0))) |
9c87d6c7 SS |
451 | return 0; |
452 | break; | |
453 | } | |
454 | /* Fallthrough */ | |
455 | ||
47b0e7ad | 456 | case 'I': /* Indirect (short) 0--7. */ |
be33c5dd | 457 | if (! tic4x_print_indirect (info, INDIRECT_SHORT, |
47b0e7ad | 458 | EXTRU (instruction, 7, 0))) |
026df7c5 NC |
459 | return 0; |
460 | break; | |
461 | ||
9c87d6c7 | 462 | case 'j': /* Extended indirect 8--15 */ |
47b0e7ad | 463 | if (EXTRU (instruction, 15, 13) == 7) |
9c87d6c7 | 464 | { |
47b0e7ad | 465 | if (! tic4x_print_register (info, EXTRU (instruction, 12, 8))) |
9c87d6c7 SS |
466 | return 0; |
467 | break; | |
468 | } | |
469 | ||
47b0e7ad | 470 | case 'J': /* Indirect (short) 8--15. */ |
be33c5dd | 471 | if (! tic4x_print_indirect (info, INDIRECT_SHORT, |
47b0e7ad | 472 | EXTRU (instruction, 15, 8))) |
026df7c5 NC |
473 | return 0; |
474 | break; | |
475 | ||
47b0e7ad | 476 | case 'G': /* Register 8--15. */ |
44287f60 | 477 | case 'g': |
be33c5dd | 478 | if (! tic4x_print_register (info, EXTRU (instruction, 15, 8))) |
026df7c5 NC |
479 | return 0; |
480 | break; | |
481 | ||
47b0e7ad | 482 | case 'H': /* Register 16--18. */ |
be33c5dd | 483 | if (! tic4x_print_register (info, EXTRU (instruction, 18, 16))) |
026df7c5 NC |
484 | return 0; |
485 | break; | |
486 | ||
47b0e7ad | 487 | case 'K': /* Register 19--21. */ |
be33c5dd | 488 | if (! tic4x_print_register (info, EXTRU (instruction, 21, 19))) |
026df7c5 NC |
489 | return 0; |
490 | break; | |
491 | ||
47b0e7ad | 492 | case 'L': /* Register 22--24. */ |
be33c5dd | 493 | if (! tic4x_print_register (info, EXTRU (instruction, 24, 22))) |
026df7c5 NC |
494 | return 0; |
495 | break; | |
496 | ||
47b0e7ad | 497 | case 'M': /* Register 22--22. */ |
be33c5dd | 498 | tic4x_print_register (info, EXTRU (instruction, 22, 22) + REG_R2); |
026df7c5 NC |
499 | break; |
500 | ||
47b0e7ad | 501 | case 'N': /* Register 23--23. */ |
be33c5dd | 502 | tic4x_print_register (info, EXTRU (instruction, 23, 23) + REG_R0); |
026df7c5 NC |
503 | break; |
504 | ||
47b0e7ad | 505 | case 'O': /* Indirect (short C4x) 8--15. */ |
be33c5dd | 506 | if (! IS_CPU_TIC4X (tic4x_version)) |
026df7c5 | 507 | return 0; |
be33c5dd | 508 | if (! tic4x_print_indirect (info, INDIRECT_TIC4X, |
47b0e7ad | 509 | EXTRU (instruction, 15, 8))) |
026df7c5 NC |
510 | return 0; |
511 | break; | |
512 | ||
47b0e7ad | 513 | case 'P': /* Displacement 0--15 (used by Bcond and BcondD). */ |
be33c5dd | 514 | tic4x_print_relative (info, pc, EXTRS (instruction, 15, 0), |
47b0e7ad | 515 | p->opcode); |
026df7c5 NC |
516 | break; |
517 | ||
47b0e7ad | 518 | case 'Q': /* Register 0--15. */ |
44287f60 | 519 | case 'q': |
be33c5dd | 520 | if (! tic4x_print_register (info, EXTRU (instruction, 15, 0))) |
026df7c5 NC |
521 | return 0; |
522 | break; | |
523 | ||
47b0e7ad | 524 | case 'R': /* Register 16--20. */ |
44287f60 | 525 | case 'r': |
be33c5dd | 526 | if (! tic4x_print_register (info, EXTRU (instruction, 20, 16))) |
026df7c5 NC |
527 | return 0; |
528 | break; | |
529 | ||
47b0e7ad | 530 | case 'S': /* 16-bit signed immediate 0--15. */ |
be33c5dd | 531 | tic4x_print_immed (info, IMMED_SINT, |
47b0e7ad | 532 | EXTRS (instruction, 15, 0)); |
026df7c5 NC |
533 | break; |
534 | ||
47b0e7ad | 535 | case 'T': /* 5-bit signed immediate 16--20 (C4x stik). */ |
be33c5dd | 536 | if (! IS_CPU_TIC4X (tic4x_version)) |
026df7c5 | 537 | return 0; |
be33c5dd | 538 | if (! tic4x_print_immed (info, IMMED_SUINT, |
47b0e7ad | 539 | EXTRU (instruction, 20, 16))) |
026df7c5 NC |
540 | return 0; |
541 | break; | |
542 | ||
47b0e7ad | 543 | case 'U': /* 16-bit unsigned int immediate 0--15. */ |
be33c5dd | 544 | tic4x_print_immed (info, IMMED_SUINT, EXTRU (instruction, 15, 0)); |
026df7c5 NC |
545 | break; |
546 | ||
47b0e7ad | 547 | case 'V': /* 5/9-bit unsigned vector 0--4/8. */ |
be33c5dd | 548 | tic4x_print_immed (info, IMMED_SUINT, |
47b0e7ad NC |
549 | IS_CPU_TIC4X (tic4x_version) ? |
550 | EXTRU (instruction, 8, 0) : | |
551 | EXTRU (instruction, 4, 0) & ~0x20); | |
026df7c5 NC |
552 | break; |
553 | ||
47b0e7ad | 554 | case 'W': /* 8-bit signed immediate 0--7. */ |
be33c5dd | 555 | if (! IS_CPU_TIC4X (tic4x_version)) |
026df7c5 | 556 | return 0; |
be33c5dd | 557 | tic4x_print_immed (info, IMMED_SINT, EXTRS (instruction, 7, 0)); |
026df7c5 NC |
558 | break; |
559 | ||
47b0e7ad | 560 | case 'X': /* Expansion register 4--0. */ |
026df7c5 NC |
561 | val = EXTRU (instruction, 4, 0) + REG_IVTP; |
562 | if (val < REG_IVTP || val > REG_TVTP) | |
563 | return 0; | |
be33c5dd | 564 | if (! tic4x_print_register (info, val)) |
026df7c5 NC |
565 | return 0; |
566 | break; | |
567 | ||
47b0e7ad | 568 | case 'Y': /* Address register 16--20. */ |
026df7c5 NC |
569 | val = EXTRU (instruction, 20, 16); |
570 | if (val < REG_AR0 || val > REG_SP) | |
571 | return 0; | |
be33c5dd | 572 | if (! tic4x_print_register (info, val)) |
026df7c5 NC |
573 | return 0; |
574 | break; | |
575 | ||
47b0e7ad | 576 | case 'Z': /* Expansion register 16--20. */ |
026df7c5 NC |
577 | val = EXTRU (instruction, 20, 16) + REG_IVTP; |
578 | if (val < REG_IVTP || val > REG_TVTP) | |
579 | return 0; | |
be33c5dd | 580 | if (! tic4x_print_register (info, val)) |
026df7c5 NC |
581 | return 0; |
582 | break; | |
583 | ||
47b0e7ad | 584 | case '|': /* Parallel instruction. */ |
be33c5dd SS |
585 | tic4x_print_str (info, " || "); |
586 | tic4x_print_str (info, parallel); | |
587 | tic4x_print_char (info, ' '); | |
026df7c5 NC |
588 | break; |
589 | ||
590 | case ';': | |
be33c5dd | 591 | tic4x_print_char (info, ','); |
026df7c5 NC |
592 | break; |
593 | ||
594 | default: | |
be33c5dd | 595 | tic4x_print_char (info, *s); |
026df7c5 NC |
596 | break; |
597 | } | |
598 | s++; | |
599 | } | |
600 | return 1; | |
601 | } | |
602 | ||
603 | static void | |
47b0e7ad NC |
604 | tic4x_hash_opcode_special (tic4x_inst_t **optable_special, |
605 | const tic4x_inst_t *inst) | |
9c87d6c7 SS |
606 | { |
607 | int i; | |
608 | ||
47b0e7ad NC |
609 | for (i = 0;i < TIC4X_SPESOP_SIZE; i++) |
610 | if (optable_special[i] != NULL | |
611 | && optable_special[i]->opcode == inst->opcode) | |
9c87d6c7 | 612 | { |
47b0e7ad NC |
613 | /* Collision (we have it already) - overwrite. */ |
614 | optable_special[i] = (tic4x_inst_t *) inst; | |
9c87d6c7 SS |
615 | return; |
616 | } | |
617 | ||
47b0e7ad NC |
618 | for (i = 0; i < TIC4X_SPESOP_SIZE; i++) |
619 | if (optable_special[i] == NULL) | |
9c87d6c7 | 620 | { |
47b0e7ad NC |
621 | /* Add the new opcode. */ |
622 | optable_special[i] = (tic4x_inst_t *) inst; | |
9c87d6c7 SS |
623 | return; |
624 | } | |
625 | ||
626 | /* This should never occur. This happens if the number of special | |
be33c5dd | 627 | instructions exceeds TIC4X_SPESOP_SIZE. Please increase the variable |
9c87d6c7 | 628 | of this variable */ |
be33c5dd | 629 | #if TIC4X_DEBUG |
47b0e7ad | 630 | printf ("optable_special[] is full, please increase TIC4X_SPESOP_SIZE!\n"); |
9c87d6c7 SS |
631 | #endif |
632 | } | |
633 | ||
634 | static void | |
47b0e7ad NC |
635 | tic4x_hash_opcode (tic4x_inst_t **optable, |
636 | tic4x_inst_t **optable_special, | |
637 | const tic4x_inst_t *inst, | |
638 | const unsigned long tic4x_oplevel) | |
026df7c5 NC |
639 | { |
640 | int j; | |
be33c5dd SS |
641 | int opcode = inst->opcode >> (32 - TIC4X_HASH_SIZE); |
642 | int opmask = inst->opmask >> (32 - TIC4X_HASH_SIZE); | |
47b0e7ad | 643 | |
be33c5dd | 644 | /* Use a TIC4X_HASH_SIZE bit index as a hash index. We should |
026df7c5 | 645 | have unique entries so there's no point having a linked list |
47b0e7ad | 646 | for each entry? */ |
026df7c5 | 647 | for (j = opcode; j < opmask; j++) |
47b0e7ad NC |
648 | if ((j & opmask) == opcode |
649 | && inst->oplevel & tic4x_oplevel) | |
026df7c5 | 650 | { |
be33c5dd | 651 | #if TIC4X_DEBUG |
026df7c5 NC |
652 | /* We should only have collisions for synonyms like |
653 | ldp for ldi. */ | |
654 | if (optable[j] != NULL) | |
47b0e7ad NC |
655 | printf ("Collision at index %d, %s and %s\n", |
656 | j, optable[j]->name, inst->name); | |
026df7c5 | 657 | #endif |
9c87d6c7 SS |
658 | /* Catch those ops that collide with others already inside the |
659 | hash, and have a opmask greater than the one we use in the | |
660 | hash. Store them in a special-list, that will handle full | |
661 | 32-bit INSN, not only the first 11-bit (or so). */ | |
47b0e7ad NC |
662 | if (optable[j] != NULL |
663 | && inst->opmask & ~(opmask << (32 - TIC4X_HASH_SIZE))) | |
9c87d6c7 | 664 | { |
47b0e7ad NC |
665 | /* Add the instruction already on the list. */ |
666 | tic4x_hash_opcode_special (optable_special, optable[j]); | |
9c87d6c7 | 667 | |
47b0e7ad NC |
668 | /* Add the new instruction. */ |
669 | tic4x_hash_opcode_special (optable_special, inst); | |
9c87d6c7 SS |
670 | } |
671 | ||
47b0e7ad | 672 | optable[j] = (tic4x_inst_t *) inst; |
026df7c5 NC |
673 | } |
674 | } | |
675 | ||
676 | /* Disassemble the instruction in 'instruction'. | |
677 | 'pc' should be the address of this instruction, it will | |
678 | be used to print the target address if this is a relative jump or call | |
679 | the disassembled instruction is written to 'info'. | |
680 | The function returns the length of this instruction in words. */ | |
681 | ||
682 | static int | |
47b0e7ad NC |
683 | tic4x_disassemble (unsigned long pc, |
684 | unsigned long instruction, | |
685 | struct disassemble_info *info) | |
026df7c5 | 686 | { |
be33c5dd SS |
687 | static tic4x_inst_t **optable = NULL; |
688 | static tic4x_inst_t **optable_special = NULL; | |
689 | tic4x_inst_t *p; | |
026df7c5 | 690 | int i; |
be33c5dd | 691 | unsigned long tic4x_oplevel; |
47b0e7ad | 692 | |
be33c5dd | 693 | tic4x_version = info->mach; |
9c87d6c7 | 694 | |
be33c5dd | 695 | tic4x_oplevel = (IS_CPU_TIC4X (tic4x_version)) ? OP_C4X : 0; |
47b0e7ad NC |
696 | tic4x_oplevel |= OP_C3X | OP_LPWR | OP_IDLE2 | OP_ENH; |
697 | ||
026df7c5 NC |
698 | if (optable == NULL) |
699 | { | |
47b0e7ad | 700 | optable = xcalloc (sizeof (tic4x_inst_t *), (1 << TIC4X_HASH_SIZE)); |
9c87d6c7 | 701 | |
47b0e7ad | 702 | optable_special = xcalloc (sizeof (tic4x_inst_t *), TIC4X_SPESOP_SIZE); |
9c87d6c7 | 703 | |
026df7c5 NC |
704 | /* Install opcodes in reverse order so that preferred |
705 | forms overwrite synonyms. */ | |
be33c5dd | 706 | for (i = tic4x_num_insts - 1; i >= 0; i--) |
47b0e7ad NC |
707 | tic4x_hash_opcode (optable, optable_special, &tic4x_insts[i], |
708 | tic4x_oplevel); | |
9c87d6c7 SS |
709 | |
710 | /* We now need to remove the insn that are special from the | |
711 | "normal" optable, to make the disasm search this extra list | |
47b0e7ad NC |
712 | for them. */ |
713 | for (i = 0; i < TIC4X_SPESOP_SIZE; i++) | |
714 | if (optable_special[i] != NULL) | |
be33c5dd | 715 | optable[optable_special[i]->opcode >> (32 - TIC4X_HASH_SIZE)] = NULL; |
026df7c5 | 716 | } |
47b0e7ad | 717 | |
026df7c5 NC |
718 | /* See if we can pick up any loading of the DP register... */ |
719 | if ((instruction >> 16) == 0x5070 || (instruction >> 16) == 0x1f70) | |
be33c5dd | 720 | tic4x_dp = EXTRU (instruction, 15, 0); |
9c87d6c7 | 721 | |
be33c5dd | 722 | p = optable[instruction >> (32 - TIC4X_HASH_SIZE)]; |
47b0e7ad | 723 | if (p != NULL) |
9c87d6c7 | 724 | { |
47b0e7ad NC |
725 | if (((instruction & p->opmask) == p->opcode) |
726 | && tic4x_print_op (NULL, instruction, p, pc)) | |
be33c5dd | 727 | tic4x_print_op (info, instruction, p, pc); |
9c87d6c7 | 728 | else |
0fd3a477 | 729 | (*info->fprintf_func) (info->stream, "%08lx", instruction); |
9c87d6c7 | 730 | } |
026df7c5 | 731 | else |
9c87d6c7 | 732 | { |
be33c5dd | 733 | for (i = 0; i<TIC4X_SPESOP_SIZE; i++) |
9c87d6c7 | 734 | if (optable_special[i] != NULL |
47b0e7ad | 735 | && optable_special[i]->opcode == instruction) |
9c87d6c7 SS |
736 | { |
737 | (*info->fprintf_func)(info->stream, "%s", optable_special[i]->name); | |
738 | break; | |
739 | } | |
47b0e7ad | 740 | if (i == TIC4X_SPESOP_SIZE) |
0fd3a477 | 741 | (*info->fprintf_func) (info->stream, "%08lx", instruction); |
9c87d6c7 | 742 | } |
026df7c5 NC |
743 | |
744 | /* Return size of insn in words. */ | |
47b0e7ad | 745 | return 1; |
026df7c5 NC |
746 | } |
747 | ||
748 | /* The entry point from objdump and gdb. */ | |
749 | int | |
47b0e7ad | 750 | print_insn_tic4x (bfd_vma memaddr, struct disassemble_info *info) |
026df7c5 NC |
751 | { |
752 | int status; | |
753 | unsigned long pc; | |
754 | unsigned long op; | |
755 | bfd_byte buffer[4]; | |
47b0e7ad | 756 | |
026df7c5 NC |
757 | status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
758 | if (status != 0) | |
759 | { | |
760 | (*info->memory_error_func) (status, memaddr, info); | |
761 | return -1; | |
762 | } | |
47b0e7ad | 763 | |
026df7c5 NC |
764 | pc = memaddr; |
765 | op = bfd_getl32 (buffer); | |
766 | info->bytes_per_line = 4; | |
767 | info->bytes_per_chunk = 4; | |
768 | info->octets_per_byte = 4; | |
769 | info->display_endian = BFD_ENDIAN_LITTLE; | |
be33c5dd | 770 | return tic4x_disassemble (pc, op, info) * 4; |
026df7c5 | 771 | } |