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