]>
Commit | Line | Data |
---|---|---|
252b5132 | 1 | /* Disassemble D30V instructions. |
2dcee538 | 2 | Copyright 1997, 1998, 2000, 2001 Free Software Foundation, Inc. |
252b5132 RH |
3 | |
4 | This program 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> | |
0d8dfecf | 19 | #include "sysdep.h" |
2dcee538 | 20 | #include "opcode/d30v.h" |
252b5132 RH |
21 | #include "dis-asm.h" |
22 | #include "opintl.h" | |
23 | ||
24 | #define PC_MASK 0xFFFFFFFF | |
25 | ||
2dcee538 KH |
26 | static int lookup_opcode PARAMS ((struct d30v_insn *insn, long num, int is_long)); |
27 | static void print_insn PARAMS ((struct disassemble_info *info, bfd_vma memaddr, long long num, | |
28 | struct d30v_insn *insn, int is_long, int show_ext)); | |
29 | static int extract_value PARAMS ((long long num, struct d30v_operand *oper, int is_long)); | |
252b5132 | 30 | |
2dcee538 | 31 | int |
252b5132 RH |
32 | print_insn_d30v (memaddr, info) |
33 | bfd_vma memaddr; | |
34 | struct disassemble_info *info; | |
35 | { | |
36 | int status, result; | |
37 | bfd_byte buffer[12]; | |
2dcee538 | 38 | unsigned long in1, in2; |
252b5132 RH |
39 | struct d30v_insn insn; |
40 | long long num; | |
41 | ||
2dcee538 | 42 | insn.form = (struct d30v_format *) NULL; |
252b5132 RH |
43 | |
44 | info->bytes_per_line = 8; | |
45 | info->bytes_per_chunk = 4; | |
46 | info->display_endian = BFD_ENDIAN_BIG; | |
47 | ||
48 | status = (*info->read_memory_func) (memaddr, buffer, 4, info); | |
49 | if (status != 0) | |
50 | { | |
51 | (*info->memory_error_func) (status, memaddr, info); | |
52 | return -1; | |
53 | } | |
54 | in1 = bfd_getb32 (buffer); | |
55 | ||
2dcee538 | 56 | status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info); |
252b5132 RH |
57 | if (status != 0) |
58 | { | |
59 | info->bytes_per_line = 8; | |
2dcee538 KH |
60 | if (!(result = lookup_opcode (&insn, in1, 0))) |
61 | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); | |
252b5132 | 62 | else |
2dcee538 KH |
63 | print_insn (info, memaddr, (long long) in1, &insn, 0, result); |
64 | return 4; | |
252b5132 RH |
65 | } |
66 | in2 = bfd_getb32 (buffer); | |
2dcee538 | 67 | |
252b5132 RH |
68 | if (in1 & in2 & FM01) |
69 | { | |
2dcee538 KH |
70 | /* LONG instruction. */ |
71 | if (!(result = lookup_opcode (&insn, in1, 1))) | |
252b5132 | 72 | { |
2dcee538 | 73 | (*info->fprintf_func) (info->stream, ".long\t0x%x,0x%x", in1, in2); |
252b5132 RH |
74 | return 8; |
75 | } | |
2dcee538 KH |
76 | num = (long long) in1 << 32 | in2; |
77 | print_insn (info, memaddr, num, &insn, 1, result); | |
252b5132 RH |
78 | } |
79 | else | |
80 | { | |
81 | num = in1; | |
2dcee538 KH |
82 | if (!(result = lookup_opcode (&insn, in1, 0))) |
83 | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); | |
252b5132 | 84 | else |
2dcee538 KH |
85 | print_insn (info, memaddr, num, &insn, 0, result); |
86 | ||
87 | switch (((in1 >> 31) << 1) | (in2 >> 31)) | |
252b5132 RH |
88 | { |
89 | case 0: | |
2dcee538 | 90 | (*info->fprintf_func) (info->stream, "\t||\t"); |
252b5132 RH |
91 | break; |
92 | case 1: | |
2dcee538 | 93 | (*info->fprintf_func) (info->stream, "\t->\t"); |
252b5132 RH |
94 | break; |
95 | case 2: | |
2dcee538 | 96 | (*info->fprintf_func) (info->stream, "\t<-\t"); |
252b5132 RH |
97 | default: |
98 | break; | |
99 | } | |
2dcee538 KH |
100 | |
101 | insn.form = (struct d30v_format *) NULL; | |
252b5132 | 102 | num = in2; |
2dcee538 KH |
103 | if (!(result = lookup_opcode (&insn, in2, 0))) |
104 | (*info->fprintf_func) (info->stream, ".long\t0x%x", in2); | |
252b5132 | 105 | else |
2dcee538 | 106 | print_insn (info, memaddr, num, &insn, 0, result); |
252b5132 RH |
107 | } |
108 | return 8; | |
109 | } | |
110 | ||
2dcee538 KH |
111 | /* Return 0 if lookup fails, |
112 | 1 if found and only one form, | |
113 | 2 if found and there are short and long forms. */ | |
252b5132 | 114 | |
252b5132 RH |
115 | static int |
116 | lookup_opcode (insn, num, is_long) | |
117 | struct d30v_insn *insn; | |
118 | long num; | |
119 | int is_long; | |
120 | { | |
2dcee538 | 121 | int i = 0, index; |
252b5132 | 122 | struct d30v_format *f; |
2dcee538 | 123 | struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table; |
252b5132 RH |
124 | int op1 = (num >> 25) & 0x7; |
125 | int op2 = (num >> 20) & 0x1f; | |
126 | int mod = (num >> 18) & 0x3; | |
127 | ||
2dcee538 KH |
128 | /* Find the opcode. */ |
129 | do | |
130 | { | |
131 | if ((op->op1 == op1) && (op->op2 == op2)) | |
132 | break; | |
133 | op++; | |
134 | } | |
135 | while (op->name); | |
252b5132 RH |
136 | |
137 | if (!op || !op->name) | |
138 | return 0; | |
139 | ||
140 | while (op->op1 == op1 && op->op2 == op2) | |
141 | { | |
2dcee538 | 142 | /* Scan through all the formats for the opcode. */ |
252b5132 | 143 | index = op->format[i++]; |
2dcee538 | 144 | do |
252b5132 | 145 | { |
2dcee538 | 146 | f = (struct d30v_format *) &d30v_format_table[index]; |
252b5132 RH |
147 | while (f->form == index) |
148 | { | |
149 | if ((!is_long || f->form >= LONG) && (f->modifier == mod)) | |
150 | { | |
151 | insn->form = f; | |
152 | break; | |
153 | } | |
154 | f++; | |
155 | } | |
156 | if (insn->form) | |
157 | break; | |
2dcee538 KH |
158 | } |
159 | while ((index = op->format[i++]) != 0); | |
252b5132 RH |
160 | if (insn->form) |
161 | break; | |
162 | op++; | |
2dcee538 | 163 | i = 0; |
252b5132 RH |
164 | } |
165 | if (insn->form == NULL) | |
166 | return 0; | |
167 | ||
168 | insn->op = op; | |
169 | insn->ecc = (num >> 28) & 0x7; | |
170 | if (op->format[1]) | |
171 | return 2; | |
172 | else | |
173 | return 1; | |
174 | } | |
175 | ||
2dcee538 KH |
176 | static void |
177 | print_insn (info, memaddr, num, insn, is_long, show_ext) | |
252b5132 RH |
178 | struct disassemble_info *info; |
179 | bfd_vma memaddr; | |
180 | long long num; | |
181 | struct d30v_insn *insn; | |
182 | int is_long; | |
183 | int show_ext; | |
184 | { | |
2dcee538 | 185 | int val, opnum, need_comma = 0; |
252b5132 | 186 | struct d30v_operand *oper; |
2dcee538 | 187 | int i, match, opind = 0, need_paren = 0, found_control = 0; |
252b5132 | 188 | |
2dcee538 | 189 | (*info->fprintf_func) (info->stream, "%s", insn->op->name); |
252b5132 | 190 | |
2dcee538 | 191 | /* Check for CMP or CMPU. */ |
252b5132 RH |
192 | if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) |
193 | { | |
194 | opind++; | |
2dcee538 KH |
195 | val = |
196 | extract_value (num, | |
197 | (struct d30v_operand *) &d30v_operand_table[insn->form->operands[0]], | |
198 | is_long); | |
199 | (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]); | |
252b5132 RH |
200 | } |
201 | ||
2dcee538 | 202 | /* Add in ".s" or ".l". */ |
252b5132 RH |
203 | if (show_ext == 2) |
204 | { | |
205 | if (is_long) | |
206 | (*info->fprintf_func) (info->stream, ".l"); | |
207 | else | |
208 | (*info->fprintf_func) (info->stream, ".s"); | |
209 | } | |
210 | ||
211 | if (insn->ecc) | |
2dcee538 | 212 | (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]); |
252b5132 RH |
213 | |
214 | (*info->fprintf_func) (info->stream, "\t"); | |
215 | ||
216 | while ((opnum = insn->form->operands[opind++]) != 0) | |
217 | { | |
218 | int bits; | |
2dcee538 | 219 | oper = (struct d30v_operand *) &d30v_operand_table[opnum]; |
252b5132 RH |
220 | bits = oper->bits; |
221 | if (oper->flags & OPERAND_SHIFT) | |
222 | bits += 3; | |
223 | ||
2dcee538 KH |
224 | if (need_comma |
225 | && oper->flags != OPERAND_PLUS | |
226 | && oper->flags != OPERAND_MINUS) | |
252b5132 | 227 | { |
2dcee538 | 228 | need_comma = 0; |
252b5132 RH |
229 | (*info->fprintf_func) (info->stream, ", "); |
230 | } | |
231 | ||
232 | if (oper->flags == OPERAND_ATMINUS) | |
233 | { | |
234 | (*info->fprintf_func) (info->stream, "@-"); | |
235 | continue; | |
236 | } | |
237 | if (oper->flags == OPERAND_MINUS) | |
238 | { | |
2dcee538 | 239 | (*info->fprintf_func) (info->stream, "-"); |
252b5132 RH |
240 | continue; |
241 | } | |
242 | if (oper->flags == OPERAND_PLUS) | |
243 | { | |
2dcee538 | 244 | (*info->fprintf_func) (info->stream, "+"); |
252b5132 RH |
245 | continue; |
246 | } | |
247 | if (oper->flags == OPERAND_ATSIGN) | |
248 | { | |
2dcee538 | 249 | (*info->fprintf_func) (info->stream, "@"); |
252b5132 RH |
250 | continue; |
251 | } | |
252 | if (oper->flags == OPERAND_ATPAR) | |
253 | { | |
2dcee538 | 254 | (*info->fprintf_func) (info->stream, "@("); |
252b5132 RH |
255 | need_paren = 1; |
256 | continue; | |
257 | } | |
258 | ||
259 | if (oper->flags == OPERAND_SPECIAL) | |
260 | continue; | |
261 | ||
2dcee538 KH |
262 | val = extract_value (num, oper, is_long); |
263 | ||
252b5132 RH |
264 | if (oper->flags & OPERAND_REG) |
265 | { | |
266 | match = 0; | |
267 | if (oper->flags & OPERAND_CONTROL) | |
268 | { | |
2dcee538 KH |
269 | struct d30v_operand *oper3 = |
270 | (struct d30v_operand *) &d30v_operand_table[insn->form->operands[2]]; | |
271 | int id = extract_value (num, oper3, is_long); | |
252b5132 | 272 | found_control = 1; |
2dcee538 | 273 | switch (id) |
252b5132 RH |
274 | { |
275 | case 0: | |
276 | val |= OPERAND_CONTROL; | |
277 | break; | |
278 | case 1: | |
279 | case 2: | |
280 | val = OPERAND_CONTROL + MAX_CONTROL_REG + id; | |
281 | break; | |
282 | case 3: | |
283 | val |= OPERAND_FLAG; | |
284 | break; | |
285 | default: | |
2dcee538 | 286 | fprintf (stderr, "illegal id (%d)\n", id); |
252b5132 RH |
287 | } |
288 | } | |
289 | else if (oper->flags & OPERAND_ACC) | |
290 | val |= OPERAND_ACC; | |
291 | else if (oper->flags & OPERAND_FLAG) | |
292 | val |= OPERAND_FLAG; | |
2dcee538 | 293 | for (i = 0; i < reg_name_cnt (); i++) |
252b5132 RH |
294 | { |
295 | if (val == pre_defined_registers[i].value) | |
296 | { | |
297 | if (pre_defined_registers[i].pname) | |
298 | (*info->fprintf_func) | |
2dcee538 | 299 | (info->stream, "%s", pre_defined_registers[i].pname); |
252b5132 RH |
300 | else |
301 | (*info->fprintf_func) | |
2dcee538 KH |
302 | (info->stream, "%s", pre_defined_registers[i].name); |
303 | match = 1; | |
252b5132 RH |
304 | break; |
305 | } | |
306 | } | |
2dcee538 | 307 | if (match == 0) |
252b5132 | 308 | { |
2dcee538 KH |
309 | /* This would only get executed if a register was not in |
310 | the register table. */ | |
252b5132 | 311 | (*info->fprintf_func) |
2dcee538 | 312 | (info->stream, _("<unknown register %d>"), val & 0x3F); |
252b5132 RH |
313 | } |
314 | } | |
866afedc NC |
315 | /* repeati has a relocation, but its first argument is a plain |
316 | immediate. OTOH instructions like djsri have a pc-relative | |
d9a35582 | 317 | delay target, but an absolute jump target. Therefore, a test |
866afedc NC |
318 | of insn->op->reloc_flag is not specific enough; we must test |
319 | if the actual operand we are handling now is pc-relative. */ | |
320 | else if (oper->flags & OPERAND_PCREL) | |
252b5132 | 321 | { |
866afedc | 322 | int neg = 0; |
2dcee538 | 323 | |
866afedc NC |
324 | /* IMM6S3 is unsigned. */ |
325 | if (oper->flags & OPERAND_SIGNED || bits == 32) | |
252b5132 | 326 | { |
866afedc NC |
327 | long max; |
328 | max = (1 << (bits - 1)); | |
329 | if (val & max) | |
330 | { | |
331 | if (bits == 32) | |
332 | val = -val; | |
333 | else | |
2dcee538 | 334 | val = -val & ((1 << bits) - 1); |
866afedc NC |
335 | neg = 1; |
336 | } | |
337 | } | |
338 | if (neg) | |
339 | { | |
2dcee538 | 340 | (*info->fprintf_func) (info->stream, "-%x\t(", val); |
866afedc NC |
341 | (*info->print_address_func) ((memaddr - val) & PC_MASK, info); |
342 | (*info->fprintf_func) (info->stream, ")"); | |
252b5132 | 343 | } |
866afedc | 344 | else |
252b5132 | 345 | { |
2dcee538 | 346 | (*info->fprintf_func) (info->stream, "%x\t(", val); |
866afedc NC |
347 | (*info->print_address_func) ((memaddr + val) & PC_MASK, info); |
348 | (*info->fprintf_func) (info->stream, ")"); | |
252b5132 | 349 | } |
252b5132 RH |
350 | } |
351 | else if (insn->op->reloc_flag == RELOC_ABS) | |
352 | { | |
353 | (*info->print_address_func) (val, info); | |
354 | } | |
355 | else | |
356 | { | |
357 | if (oper->flags & OPERAND_SIGNED) | |
358 | { | |
359 | int max = (1 << (bits - 1)); | |
360 | if (val & max) | |
361 | { | |
362 | val = -val; | |
363 | if (bits < 32) | |
364 | val &= ((1 << bits) - 1); | |
365 | (*info->fprintf_func) (info->stream, "-"); | |
366 | } | |
367 | } | |
2dcee538 | 368 | (*info->fprintf_func) (info->stream, "0x%x", val); |
252b5132 | 369 | } |
2dcee538 | 370 | /* If there is another operand, then write a comma and space. */ |
252b5132 RH |
371 | if (insn->form->operands[opind] && !(found_control && opind == 2)) |
372 | need_comma = 1; | |
373 | } | |
374 | if (need_paren) | |
375 | (*info->fprintf_func) (info->stream, ")"); | |
376 | } | |
377 | ||
252b5132 RH |
378 | static int |
379 | extract_value (num, oper, is_long) | |
380 | long long num; | |
381 | struct d30v_operand *oper; | |
382 | int is_long; | |
383 | { | |
384 | int val; | |
385 | int shift = 12 - oper->position; | |
386 | int mask = (0xFFFFFFFF >> (32 - oper->bits)); | |
387 | ||
388 | if (is_long) | |
389 | { | |
2dcee538 | 390 | if (oper->bits == 32) |
252b5132 | 391 | { |
2dcee538 | 392 | /* Piece together 32-bit constant. */ |
252b5132 RH |
393 | val = ((num & 0x3FFFF) |
394 | | ((num & 0xFF00000) >> 2) | |
395 | | ((num & 0x3F00000000LL) >> 6)); | |
396 | } | |
2dcee538 KH |
397 | else |
398 | val = (num >> (32 + shift)) & mask; | |
252b5132 RH |
399 | } |
400 | else | |
401 | val = (num >> shift) & mask; | |
402 | ||
403 | if (oper->flags & OPERAND_SHIFT) | |
404 | val <<= 3; | |
405 | ||
406 | return val; | |
407 | } |