* arc-opc.c: Include bfd.h.
[deliverable/binutils-gdb.git] / opcodes / tic4x-dis.c
CommitLineData
026df7c5
NC
1/* Print instructions for the Texas TMS320C[34]X, for GDB and GNU Binutils.
2
3 Copyright 2002 Free Software Foundation, Inc.
4
5 Contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz)
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21#include <math.h>
22#include "libiberty.h"
23#include "dis-asm.h"
24#include "opcode/tic4x.h"
25
26#define C4X_DEBUG 0
27
28#define C4X_HASH_SIZE 11 /* 11 and above should give unique entries. */
29
30typedef enum
31 {
32 IMMED_SINT,
33 IMMED_SUINT,
34 IMMED_SFLOAT,
35 IMMED_INT,
36 IMMED_UINT,
37 IMMED_FLOAT
38 }
39immed_t;
40
41typedef enum
42 {
43 INDIRECT_SHORT,
44 INDIRECT_LONG,
45 INDIRECT_C4X
46 }
47indirect_t;
48
49static int c4x_version = 0;
50static int c4x_dp = 0;
51
52static int
53c4x_pc_offset (unsigned int op)
54{
55 /* Determine the PC offset for a C[34]x instruction.
56 This could be simplified using some boolean algebra
57 but at the expense of readability. */
58 switch (op >> 24)
59 {
60 case 0x60: /* br */
61 case 0x62: /* call (C4x) */
62 case 0x64: /* rptb (C4x) */
63 return 1;
64 case 0x61: /* brd */
65 case 0x63: /* laj */
66 case 0x65: /* rptbd (C4x) */
67 return 3;
68 case 0x66: /* swi */
69 case 0x67:
70 return 0;
71 default:
72 break;
73 }
74
75 switch ((op & 0xffe00000) >> 20)
76 {
77 case 0x6a0: /* bB */
78 case 0x720: /* callB */
79 case 0x740: /* trapB */
80 return 1;
81
82 case 0x6a2: /* bBd */
83 case 0x6a6: /* bBat */
84 case 0x6aa: /* bBaf */
85 case 0x722: /* lajB */
86 case 0x748: /* latB */
87 case 0x798: /* rptbd */
88 return 3;
89
90 default:
91 break;
92 }
93
94 switch ((op & 0xfe200000) >> 20)
95 {
96 case 0x6e0: /* dbB */
97 return 1;
98
99 case 0x6e2: /* dbBd */
100 return 3;
101
102 default:
103 break;
104 }
105
106 return 0;
107}
108
109static int
110c4x_print_char (struct disassemble_info * info, char ch)
111{
112 if (info != NULL)
113 (*info->fprintf_func) (info->stream, "%c", ch);
114 return 1;
115}
116
117static int
118c4x_print_str (struct disassemble_info *info, char *str)
119{
120 if (info != NULL)
121 (*info->fprintf_func) (info->stream, "%s", str);
122 return 1;
123}
124
125static int
126c4x_print_register (struct disassemble_info *info,
127 unsigned long regno)
128{
129 static c4x_register_t **registertable = NULL;
130 unsigned int i;
131
132 if (registertable == NULL)
133 {
134 registertable = (c4x_register_t **)
135 xmalloc (sizeof (c4x_register_t *) * REG_TABLE_SIZE);
136 for (i = 0; i < c3x_num_registers; i++)
137 registertable[c3x_registers[i].regno] = (void *)&c3x_registers[i];
138 if (IS_CPU_C4X (c4x_version))
139 {
140 /* Add C4x additional registers, overwriting
141 any C3x registers if necessary. */
142 for (i = 0; i < c4x_num_registers; i++)
143 registertable[c4x_registers[i].regno] = (void *)&c4x_registers[i];
144 }
145 }
146 if ((int) regno > (IS_CPU_C4X (c4x_version) ? C4X_REG_MAX : C3X_REG_MAX))
147 return 0;
148 if (info != NULL)
149 (*info->fprintf_func) (info->stream, "%s", registertable[regno]->name);
150 return 1;
151}
152
153static int
154c4x_print_addr (struct disassemble_info *info,
155 unsigned long addr)
156{
157 if (info != NULL)
158 (*info->print_address_func)(addr, info);
159 return 1;
160}
161
162static int
163c4x_print_relative (struct disassemble_info *info,
164 unsigned long pc,
165 long offset,
166 unsigned long opcode)
167{
168 return c4x_print_addr (info, pc + offset + c4x_pc_offset (opcode));
169}
170
171static int
172c4x_print_direct (struct disassemble_info *info,
173 unsigned long arg)
174{
175 if (info != NULL)
176 {
177 (*info->fprintf_func) (info->stream, "@");
178 c4x_print_addr (info, arg + (c4x_dp << 16));
179 }
180 return 1;
181}
182
183/* FIXME: make the floating point stuff not rely on host
184 floating point arithmetic. */
185void
186c4x_print_ftoa (unsigned int val,
187 FILE *stream,
188 int (*pfunc)())
189{
190 int e;
191 int s;
192 int f;
193 double num = 0.0;
194
195 e = EXTRS (val, 31, 24); /* exponent */
196 if (e != -128)
197 {
198 s = EXTRU (val, 23, 23); /* sign bit */
199 f = EXTRU (val, 22, 0); /* mantissa */
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);
206 }
207 (*pfunc)(stream, "%.9g", num);
208}
209
210static int
211c4x_print_immed (struct disassemble_info *info,
212 immed_t type,
213 unsigned long arg)
214{
215 int s;
216 int f;
217 int e;
218 double num = 0.0;
219
220 if (info == NULL)
221 return 1;
222 switch (type)
223 {
224 case IMMED_SINT:
225 case IMMED_INT:
226 (*info->fprintf_func) (info->stream, "%d", (long)arg);
227 break;
228
229 case IMMED_SUINT:
230 case IMMED_UINT:
231 (*info->fprintf_func) (info->stream, "%u", arg);
232 break;
233
234 case IMMED_SFLOAT:
235 e = EXTRS (arg, 15, 12);
236 if (e != -8)
237 {
238 s = EXTRU (arg, 11, 11);
239 f = EXTRU (arg, 10, 0);
240 if (s)
241 f += -2 * (1 << 11);
242 else
243 f += (1 << 11);
244 num = f / (double)(1 << 11);
245 num = ldexp (num, e);
246 }
247 (*info->fprintf_func) (info->stream, "%f", num);
248 break;
249 case IMMED_FLOAT:
250 e = EXTRS (arg, 31, 24);
251 if (e != -128)
252 {
253 s = EXTRU (arg, 23, 23);
254 f = EXTRU (arg, 22, 0);
255 if (s)
256 f += -2 * (1 << 23);
257 else
258 f += (1 << 23);
259 num = f / (double)(1 << 23);
260 num = ldexp (num, e);
261 }
262 (*info->fprintf_func) (info->stream, "%f", num);
263 break;
264 }
265 return 1;
266}
267
268static int
269c4x_print_cond (struct disassemble_info *info,
270 unsigned int cond)
271{
272 static c4x_cond_t **condtable = NULL;
273 unsigned int i;
274
275 if (condtable == NULL)
276 {
277 condtable = (c4x_cond_t **)xmalloc (sizeof (c4x_cond_t *) * 32);
278 for (i = 0; i < num_conds; i++)
279 condtable[c4x_conds[i].cond] = (void *)&c4x_conds[i];
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
288static int
289c4x_print_indirect (struct disassemble_info *info,
290 indirect_t type,
291 unsigned long arg)
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 {
303 case INDIRECT_C4X: /* *+ARn(disp) */
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:
321 abort ();
322 }
323 if (modn > C3X_MODN_MAX)
324 return 0;
325 a = c4x_indirects[modn].name;
326 while (*a)
327 {
328 switch (*a)
329 {
330 case 'a':
331 c4x_print_register (info, aregno);
332 break;
333 case 'd':
334 c4x_print_immed (info, IMMED_UINT, disp);
335 break;
336 case 'y':
337 c4x_print_str (info, "ir0");
338 break;
339 case 'z':
340 c4x_print_str (info, "ir1");
341 break;
342 default:
343 c4x_print_char (info, *a);
344 break;
345 }
346 a++;
347 }
348 return 1;
349}
350
351static int
352c4x_print_op (struct disassemble_info *info,
353 unsigned long instruction,
354 c4x_inst_t *p, unsigned long pc)
355{
356 int val;
357 char *s;
358 char *parallel = NULL;
359
360 /* Print instruction name. */
361 s = p->name;
362 while (*s && parallel == NULL)
363 {
364 switch (*s)
365 {
366 case 'B':
367 if (! c4x_print_cond (info, EXTRU (instruction, 20, 16)))
368 return 0;
369 break;
370 case 'C':
371 if (! c4x_print_cond (info, EXTRU (instruction, 27, 23)))
372 return 0;
373 break;
374 case '_':
375 parallel = s + 1; /* Skip past `_' in name */
376 break;
377 default:
378 c4x_print_char (info, *s);
379 break;
380 }
381 s++;
382 }
383
384 /* Print arguments. */
385 s = p->args;
386 if (*s)
387 c4x_print_char (info, ' ');
388
389 while (*s)
390 {
391 switch (*s)
392 {
393 case '*': /* indirect 0--15 */
394 if (! c4x_print_indirect (info, INDIRECT_LONG,
395 EXTRU (instruction, 15, 0)))
396 return 0;
397 break;
398
399 case '#': /* only used for ldp, ldpk */
400 c4x_print_immed (info, IMMED_UINT, EXTRU (instruction, 15, 0));
401 break;
402
403 case '@': /* direct 0--15 */
404 c4x_print_direct (info, EXTRU (instruction, 15, 0));
405 break;
406
407 case 'A': /* address register 24--22 */
408 if (! c4x_print_register (info, EXTRU (instruction, 24, 22) +
409 REG_AR0))
410 return 0;
411 break;
412
413 case 'B': /* 24-bit unsigned int immediate br(d)/call/rptb
414 address 0--23. */
415 if (IS_CPU_C4X (c4x_version))
416 c4x_print_relative (info, pc, EXTRS (instruction, 23, 0),
417 p->opcode);
418 else
419 c4x_print_addr (info, EXTRU (instruction, 23, 0));
420 break;
421
422 case 'C': /* indirect (short C4x) 0--7 */
423 if (! IS_CPU_C4X (c4x_version))
424 return 0;
425 if (! c4x_print_indirect (info, INDIRECT_C4X,
426 EXTRU (instruction, 7, 0)))
427 return 0;
428 break;
429
430 case 'D':
431 /* Cockup if get here... */
432 break;
433
434 case 'E': /* register 0--7 */
435 if (! c4x_print_register (info, EXTRU (instruction, 7, 0)))
436 return 0;
437 break;
438
439 case 'F': /* 16-bit float immediate 0--15 */
440 c4x_print_immed (info, IMMED_SFLOAT,
441 EXTRU (instruction, 15, 0));
442 break;
443
444 case 'I': /* indirect (short) 0--7 */
445 if (! c4x_print_indirect (info, INDIRECT_SHORT,
446 EXTRU (instruction, 7, 0)))
447 return 0;
448 break;
449
450 case 'J': /* indirect (short) 8--15 */
451 if (! c4x_print_indirect (info, INDIRECT_SHORT,
452 EXTRU (instruction, 15, 8)))
453 return 0;
454 break;
455
456 case 'G': /* register 8--15 */
457 if (! c4x_print_register (info, EXTRU (instruction, 15, 8)))
458 return 0;
459 break;
460
461 case 'H': /* register 16--18 */
462 if (! c4x_print_register (info, EXTRU (instruction, 18, 16)))
463 return 0;
464 break;
465
466 case 'K': /* register 19--21 */
467 if (! c4x_print_register (info, EXTRU (instruction, 21, 19)))
468 return 0;
469 break;
470
471 case 'L': /* register 22--24 */
472 if (! c4x_print_register (info, EXTRU (instruction, 24, 22)))
473 return 0;
474 break;
475
476 case 'M': /* register 22--22 */
477 c4x_print_register (info, EXTRU (instruction, 22, 22) + REG_R2);
478 break;
479
480 case 'N': /* register 23--23 */
481 c4x_print_register (info, EXTRU (instruction, 22, 22) + REG_R0);
482 break;
483
484 case 'O': /* indirect (short C4x) 8--15 */
485 if (! IS_CPU_C4X (c4x_version))
486 return 0;
487 if (! c4x_print_indirect (info, INDIRECT_C4X,
488 EXTRU (instruction, 15, 8)))
489 return 0;
490 break;
491
492 case 'P': /* displacement 0--15 (used by Bcond and BcondD) */
493 c4x_print_relative (info, pc, EXTRS (instruction, 15, 0),
494 p->opcode);
495 break;
496
497 case 'Q': /* register 0--15 */
498 if (! c4x_print_register (info, EXTRU (instruction, 15, 0)))
499 return 0;
500 break;
501
502 case 'R': /* register 16--20 */
503 if (! c4x_print_register (info, EXTRU (instruction, 20, 16)))
504 return 0;
505 break;
506
507 case 'S': /* 16-bit signed immediate 0--15 */
508 c4x_print_immed (info, IMMED_SINT,
509 EXTRS (instruction, 15, 0));
510 break;
511
512 case 'T': /* 5-bit signed immediate 16--20 (C4x stik) */
513 if (! IS_CPU_C4X (c4x_version))
514 return 0;
515 if (! c4x_print_immed (info, IMMED_SUINT,
516 EXTRU (instruction, 20, 16)))
517 return 0;
518 break;
519
520 case 'U': /* 16-bit unsigned int immediate 0--15 */
521 c4x_print_immed (info, IMMED_SUINT, EXTRU (instruction, 15, 0));
522 break;
523
524 case 'V': /* 5/9-bit unsigned vector 0--4/8 */
525 c4x_print_immed (info, IMMED_SUINT,
526 IS_CPU_C4X (c4x_version) ?
527 EXTRU (instruction, 8, 0) :
528 EXTRU (instruction, 4, 0) & ~0x20);
529 break;
530
531 case 'W': /* 8-bit signed immediate 0--7 */
532 if (! IS_CPU_C4X (c4x_version))
533 return 0;
534 c4x_print_immed (info, IMMED_SINT, EXTRS (instruction, 7, 0));
535 break;
536
537 case 'X': /* expansion register 4--0 */
538 val = EXTRU (instruction, 4, 0) + REG_IVTP;
539 if (val < REG_IVTP || val > REG_TVTP)
540 return 0;
541 if (! c4x_print_register (info, val))
542 return 0;
543 break;
544
545 case 'Y': /* address register 16--20 */
546 val = EXTRU (instruction, 20, 16);
547 if (val < REG_AR0 || val > REG_SP)
548 return 0;
549 if (! c4x_print_register (info, val))
550 return 0;
551 break;
552
553 case 'Z': /* expansion register 16--20 */
554 val = EXTRU (instruction, 20, 16) + REG_IVTP;
555 if (val < REG_IVTP || val > REG_TVTP)
556 return 0;
557 if (! c4x_print_register (info, val))
558 return 0;
559 break;
560
561 case '|': /* Parallel instruction */
562 c4x_print_str (info, " || ");
563 c4x_print_str (info, parallel);
564 c4x_print_char (info, ' ');
565 break;
566
567 case ';':
568 c4x_print_char (info, ',');
569 break;
570
571 default:
572 c4x_print_char (info, *s);
573 break;
574 }
575 s++;
576 }
577 return 1;
578}
579
580static void
581c4x_hash_opcode (c4x_inst_t **optable,
582 const c4x_inst_t *inst)
583{
584 int j;
585 int opcode = inst->opcode >> (32 - C4X_HASH_SIZE);
586 int opmask = inst->opmask >> (32 - C4X_HASH_SIZE);
587
588 /* Use a C4X_HASH_SIZE bit index as a hash index. We should
589 have unique entries so there's no point having a linked list
590 for each entry? */
591 for (j = opcode; j < opmask; j++)
592 if ((j & opmask) == opcode)
593 {
594#if C4X_DEBUG
595 /* We should only have collisions for synonyms like
596 ldp for ldi. */
597 if (optable[j] != NULL)
598 printf("Collision at index %d, %s and %s\n",
599 j, optable[j]->name, inst->name);
600#endif
601 optable[j] = (void *)inst;
602 }
603}
604
605/* Disassemble the instruction in 'instruction'.
606 'pc' should be the address of this instruction, it will
607 be used to print the target address if this is a relative jump or call
608 the disassembled instruction is written to 'info'.
609 The function returns the length of this instruction in words. */
610
611static int
612c4x_disassemble (unsigned long pc,
613 unsigned long instruction,
614 struct disassemble_info *info)
615{
616 static c4x_inst_t **optable = NULL;
617 c4x_inst_t *p;
618 int i;
619
620 c4x_version = info->mach;
621
622 if (optable == NULL)
623 {
624 optable = (c4x_inst_t **)
625 xcalloc (sizeof (c4x_inst_t *), (1 << C4X_HASH_SIZE));
626 /* Install opcodes in reverse order so that preferred
627 forms overwrite synonyms. */
628 for (i = c3x_num_insts - 1; i >= 0; i--)
629 c4x_hash_opcode (optable, &c3x_insts[i]);
630 if (IS_CPU_C4X (c4x_version))
631 {
632 for (i = c4x_num_insts - 1; i >= 0; i--)
633 c4x_hash_opcode (optable, &c4x_insts[i]);
634 }
635 }
636
637 /* See if we can pick up any loading of the DP register... */
638 if ((instruction >> 16) == 0x5070 || (instruction >> 16) == 0x1f70)
639 c4x_dp = EXTRU (instruction, 15, 0);
640
641 p = optable[instruction >> (32 - C4X_HASH_SIZE)];
642 if (p != NULL && ((instruction & p->opmask) == p->opcode)
643 && c4x_print_op (NULL, instruction, p, pc))
644 c4x_print_op (info, instruction, p, pc);
645 else
646 (*info->fprintf_func) (info->stream, "%08x", instruction);
647
648 /* Return size of insn in words. */
649 return 1;
650}
651
652/* The entry point from objdump and gdb. */
653int
654print_insn_tic4x (memaddr, info)
655 bfd_vma memaddr;
656 struct disassemble_info *info;
657{
658 int status;
659 unsigned long pc;
660 unsigned long op;
661 bfd_byte buffer[4];
662
663 status = (*info->read_memory_func) (memaddr, buffer, 4, info);
664 if (status != 0)
665 {
666 (*info->memory_error_func) (status, memaddr, info);
667 return -1;
668 }
669
670 pc = memaddr;
671 op = bfd_getl32 (buffer);
672 info->bytes_per_line = 4;
673 info->bytes_per_chunk = 4;
674 info->octets_per_byte = 4;
675 info->display_endian = BFD_ENDIAN_LITTLE;
676 return c4x_disassemble (pc, op, info) * 4;
677}
This page took 0.048681 seconds and 4 git commands to generate.