]>
Commit | Line | Data |
---|---|---|
6357e7f6 FF |
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. */ | |
a79d0193 FF |
17 | |
18 | #include <stdio.h> | |
19 | ||
20 | #include "ansidecl.h" | |
21 | #include "opcode/tic80.h" | |
22 | #include "dis-asm.h" | |
23 | ||
937fe722 FF |
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 | ||
a79d0193 FF |
27 | int |
28 | print_insn_tic80 (memaddr, info) | |
29 | bfd_vma memaddr; | |
30 | struct disassemble_info *info; | |
31 | { | |
872dc6f0 FF |
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; | |
937fe722 | 39 | int close_paren; |
872dc6f0 FF |
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. */ | |
872dc6f0 FF |
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 | ||
937fe722 FF |
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) | |
872dc6f0 | 135 | { |
937fe722 FF |
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, "("); | |
872dc6f0 FF |
146 | } |
147 | ||
148 | /* Print the operand as directed by the flags. */ | |
937fe722 | 149 | |
872dc6f0 FF |
150 | if ((operand -> flags & TIC80_OPERAND_GPR) != 0) |
151 | { | |
152 | (*info -> fprintf_func) (info -> stream, "r%ld", value); | |
937fe722 FF |
153 | if (M_SI (insn[0], operand) || M_LI (insn[0], operand)) |
154 | { | |
155 | (*info -> fprintf_func) (info -> stream, ":m"); | |
156 | } | |
872dc6f0 FF |
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 | { | |
1f8c8c60 | 164 | (*info -> print_address_func) (memaddr + 4 * value, info); |
872dc6f0 | 165 | } |
1f8c8c60 | 166 | else if ((operand -> flags & TIC80_OPERAND_BITNUM) != 0) |
872dc6f0 | 167 | { |
1f8c8c60 FF |
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 | } | |
872dc6f0 FF |
182 | else |
183 | { | |
1f8c8c60 FF |
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); | |
872dc6f0 | 280 | } |
872dc6f0 FF |
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 | ||
937fe722 FF |
295 | /* If we printed an open paren before printing this operand, close |
296 | it now. The flag gets reset on each loop. */ | |
872dc6f0 | 297 | |
937fe722 | 298 | if (close_paren) |
872dc6f0 | 299 | { |
937fe722 | 300 | (*info -> fprintf_func) (info -> stream, ")"); |
872dc6f0 FF |
301 | } |
302 | } | |
303 | } | |
304 | ||
305 | return (length); | |
a79d0193 | 306 | } |