* tic80-dis.c (print_insn_tic80): Print floating point operands
[external/binutils.git] / opcodes / tic80-dis.c
1 /* Print TI TMS320C80 (MVP) instructions
2    Copyright 1996 Free Software Foundation, Inc.
3
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.
8
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.
13
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #include <stdio.h>
19
20 #include "ansidecl.h"
21 #include "opcode/tic80.h"
22 #include "dis-asm.h"
23
24 #define M_SI(insn,op) ((((op) -> flags & TIC80_OPERAND_M_SI) != 0) && ((insn) & (1 << 17)))
25 #define M_LI(insn,op) ((((op) -> flags & TIC80_OPERAND_M_LI) != 0) && ((insn) & (1 << 15)))
26 #define R_SCALED(insn,op) ((((op) -> flags & TIC80_OPERAND_SCALED) != 0) && ((insn) & (1 << 11)))
27
28 int 
29 print_insn_tic80 (memaddr, info)
30      bfd_vma memaddr;
31      struct disassemble_info *info;
32 {
33   bfd_byte buffer[4];
34   int status;
35   unsigned long insn[2];
36   const struct tic80_opcode *opcode;
37   const struct tic80_opcode *opcode_end;
38   const unsigned char *opindex;
39   const struct tic80_operand *operand;
40   int close_paren;
41   int length = 4;
42
43   status = (*info->read_memory_func) (memaddr, buffer, 4, info);
44   if (status != 0)
45     {
46       (*info->memory_error_func) (status, memaddr, info);
47       return -1;
48     }
49
50   if (info -> endian == BFD_ENDIAN_LITTLE)
51     {
52       insn[0] = bfd_getl32 (buffer);
53     }
54   else if (info -> endian == BFD_ENDIAN_BIG)
55     {
56       insn[0] = bfd_getb32 (buffer);
57     }
58   else
59     {
60       /* FIXME: Should probably just default to one or the other */
61       abort ();
62     }
63
64   /* Find the first opcode match in the opcodes table.  FIXME: there should
65      be faster ways to find one (hash table or binary search), but don't
66      worry too much about it until other TIc80 support is finished. */
67
68   opcode_end = tic80_opcodes + tic80_num_opcodes;
69   for (opcode = tic80_opcodes; opcode < opcode_end; opcode++)
70     {
71       if ((insn[0] & opcode -> mask) == opcode -> opcode)
72         {
73           break;
74         }
75     }
76
77   if (opcode == opcode_end)
78     {
79       /* No match found, just print the bits as a .word directive */
80       (*info -> fprintf_func) (info -> stream, ".word %#08lx", insn[0]);
81     }
82   else
83     {
84       /* Match found, decode the instruction.  */
85       (*info -> fprintf_func) (info -> stream, "%s", opcode -> name);
86
87       /* Now extract and print the operands. */
88       if (opcode -> operands[0] != 0)
89         {
90           (*info -> fprintf_func) (info -> stream, "\t");
91         }
92       for (opindex = opcode -> operands; *opindex != 0; opindex++)
93         {
94           long value;
95
96           operand = tic80_operands + *opindex;
97
98           /* Extract the value from the instruction.  */
99           if (operand -> extract)
100             {
101               value = (*operand -> extract) (insn[0], (int *) NULL);
102             }
103           else if (operand -> bits == 32)
104             {
105               status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info);
106               if (status != 0)
107                 {
108                   (*info->memory_error_func) (status, memaddr, info);
109                   return -1;
110                 }
111
112               if (info -> endian == BFD_ENDIAN_LITTLE)
113                 {
114                   insn[1] = bfd_getl32 (buffer);
115                 }
116               else if (info -> endian == BFD_ENDIAN_BIG)
117                 {
118                   insn[1] = bfd_getb32 (buffer);
119                 }
120               value = (long) insn[1];
121               length += 4;
122             }
123           else
124             {
125               value = (insn[0] >> operand -> shift) & ((1 << operand -> bits) - 1);
126               if ((operand -> flags & TIC80_OPERAND_SIGNED) != 0
127                   && (value & (1 << (operand -> bits - 1))) != 0)
128                 value -= 1 << operand -> bits;
129             }
130
131           /* If this operand is enclosed in parenthesis, then print
132              the open paren, otherwise just print the regular comma
133              separator, except for the first operand. */
134
135           if ((operand -> flags & TIC80_OPERAND_PARENS) == 0)
136             {
137               close_paren = 0;
138               if (opindex != opcode -> operands)
139                 {
140                   (*info -> fprintf_func) (info -> stream, ",");
141                 }
142             }
143           else
144             {
145               close_paren = 1;
146               (*info -> fprintf_func) (info -> stream, "(");
147             }
148
149           /* Print the operand as directed by the flags.  */
150
151           if ((operand -> flags & TIC80_OPERAND_GPR) != 0)
152             {
153               (*info -> fprintf_func) (info -> stream, "r%ld", value);
154               if (M_SI (insn[0], operand) || M_LI (insn[0], operand))
155                 {
156                   (*info -> fprintf_func) (info -> stream, ":m");
157                 }
158             }
159           else if ((operand -> flags & TIC80_OPERAND_FPA) != 0)
160             {
161               (*info -> fprintf_func) (info -> stream, "a%ld", value);
162             }
163           else if ((operand -> flags & TIC80_OPERAND_RELATIVE) != 0)
164             {
165               (*info -> print_address_func) (memaddr + 4 * value, info);
166             }
167           else if ((operand -> flags & TIC80_OPERAND_BITNUM) != 0)
168             {
169               char *syms[30] = {
170                 "eq.b", "ne.b", "gt.b", "le.b", "lt.b", "ge.b",
171                 "hi.b", "ls.b", "lo.b", "hs.b", "eq.h", "ne.h",
172                 "gt.h", "le.h", "lt.h", "ge.h", "hi.h", "ls.h",
173                 "lo.h", "hs.h", "eq.w", "ne.w", "gt.w", "le.w",
174                 "lt.w", "ge.w", "hi.w", "ls.w", "lo.w", "hs.w"
175               };
176               int bitnum = ~value & 0x1F;
177
178               if (bitnum < 30)
179                 {
180                   /* Found a value within range */
181                   (*info -> fprintf_func) (info -> stream, "%s", syms[bitnum]);
182                 }
183               else
184                 {
185                   /* Not in range, just print as bit number */
186                   (*info -> fprintf_func) (info -> stream, "%ld", bitnum);
187                 }
188             }
189           else if ((operand -> flags & TIC80_OPERAND_CC) != 0)
190             {
191               char *syms[24] = {
192                 "nev.b", "gt0.b", "eq0.b", "ge0.b", "lt0.b", "ne0.b", "le0.b", "alw.b",
193                 "nev.h", "gt0.h", "eq0.h", "ge0.h", "lt0.h", "ne0.h", "le0.h", "alw.h",
194                 "nev.w", "gt0.w", "eq0.w", "ge0.w", "lt0.w", "ne0.w", "le0.w", "alw.w"
195               };
196               if (value < 24)
197                 {
198                   /* Found a value within range */
199                   (*info -> fprintf_func) (info -> stream, "%s", syms[value]);
200                 }
201               else
202                 {
203                   /* Not in range, just print as decimal digit. */
204                   (*info -> fprintf_func) (info -> stream, "%ld", value);
205                 }
206             }
207           else if ((operand -> flags & TIC80_OPERAND_CR) != 0)
208             {
209               char *tmp;
210               switch (value)
211                 {
212                 case 0:         tmp = "EPC";            break;
213                 case 1:         tmp = "EIP";            break;
214                 case 2:         tmp = "CONFIG";         break;
215                 case 4:         tmp = "INTPEN";         break;
216                 case 6:         tmp = "IE";             break;
217                 case 8:         tmp = "FPST";           break;
218                 case 0xA:       tmp = "PPERROR";        break;
219                 case 0xD:       tmp = "PKTREQ";         break;
220                 case 0xE:       tmp = "TCOUNT";         break;
221                 case 0xF:       tmp = "TSCALE";         break;
222                 case 0x10:      tmp = "FLTOP";          break;
223                 case 0x11:      tmp = "FLTADR";         break;
224                 case 0x12:      tmp = "FLTTAG";         break;
225                 case 0x13:      tmp = "FLTDTL";         break;
226                 case 0x14:      tmp = "FLTDTH";         break;
227                 case 0x20:      tmp = "SYSSTK";         break;
228                 case 0x21:      tmp = "SYSTMP";         break;
229                 case 0x30:      tmp = "MPC";            break;
230                 case 0x31:      tmp = "MIP";            break;
231                 case 0x33:      tmp = "ECOMCNTL";       break;
232                 case 0x34:      tmp = "ANASTAT";        break;
233                 case 0x39:      tmp = "BRK1";           break;
234                 case 0x3A:      tmp = "BRK2";           break;
235                 case 0x200:     tmp = "ITAG0";          break;
236                 case 0x201:     tmp = "ITAG1";          break;
237                 case 0x202:     tmp = "ITAG2";          break;
238                 case 0x203:     tmp = "ITAG3";          break;
239                 case 0x204:     tmp = "ITAG4";          break;
240                 case 0x205:     tmp = "ITAG5";          break;
241                 case 0x206:     tmp = "ITAG6";          break;
242                 case 0x207:     tmp = "ITAG7";          break;
243                 case 0x208:     tmp = "ITAG8";          break;
244                 case 0x209:     tmp = "ITAG9";          break;
245                 case 0x20A:     tmp = "ITAG10";         break;
246                 case 0x20B:     tmp = "ITAG11";         break;
247                 case 0x20C:     tmp = "ITAG12";         break;
248                 case 0x20D:     tmp = "ITAG13";         break;
249                 case 0x20E:     tmp = "ITAG14";         break;
250                 case 0x20F:     tmp = "ITAG15";         break;
251                 case 0x300:     tmp = "ILRU";           break;
252                 case 0x400:     tmp = "DTAG0";          break;
253                 case 0x401:     tmp = "DTAG1";          break;
254                 case 0x402:     tmp = "DTAG2";          break;
255                 case 0x403:     tmp = "DTAG3";          break;
256                 case 0x404:     tmp = "DTAG4";          break;
257                 case 0x405:     tmp = "DTAG5";          break;
258                 case 0x406:     tmp = "DTAG6";          break;
259                 case 0x407:     tmp = "DTAG7";          break;
260                 case 0x408:     tmp = "DTAG8";          break;
261                 case 0x409:     tmp = "DTAG9";          break;
262                 case 0x40A:     tmp = "DTAG10";         break;
263                 case 0x40B:     tmp = "DTAG11";         break;
264                 case 0x40C:     tmp = "DTAG12";         break;
265                 case 0x40D:     tmp = "DTAG13";         break;
266                 case 0x40E:     tmp = "DTAG14";         break;
267                 case 0x40F:     tmp = "DTAG15";         break;
268                 case 0x500:     tmp = "DLRU";           break;
269                 case 0x4000:    tmp = "IN0P";           break;
270                 case 0x4001:    tmp = "IN1P";           break;
271                 case 0x4002:    tmp = "OUTP";           break;
272                 default:        tmp = NULL;             break;
273                 }
274               if (tmp != NULL)
275                 {
276                   (*info -> fprintf_func) (info -> stream, "%s", tmp);
277                 }
278               else
279                 {
280                   (*info -> fprintf_func) (info -> stream, "%#lx", value);
281                 }
282             }
283           else if ((operand -> flags & TIC80_OPERAND_FLOAT) != 0)
284             {
285               /* FIXME: depends upon sizeof (long) == sizeof (float) */
286               union { float f; long l; } fval;
287
288               fval.l = value;
289               (*info -> fprintf_func) (info -> stream, "%g", fval.f);
290             }
291           else
292             {
293               if ((value > 999 || value < -999)
294                   || operand -> flags & TIC80_OPERAND_BITFIELD)
295                 {
296                   (*info -> fprintf_func) (info -> stream, "%#lx", value);
297                 }
298               else
299                 {
300                   (*info -> fprintf_func) (info -> stream, "%ld", value);
301                 }
302             }
303
304           /* If this is a scaled operand, then print the modifier */
305
306           if (R_SCALED (insn[0], operand))
307             {
308               (*info -> fprintf_func) (info -> stream, ":s");
309             }
310
311           /* If we printed an open paren before printing this operand, close
312              it now. The flag gets reset on each loop. */
313
314           if (close_paren)
315             {
316               (*info -> fprintf_func) (info -> stream, ")");
317             }
318         }
319     }
320
321   return (length);
322 }