/src/binutils-gdb/opcodes/d30v-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Disassemble D30V instructions. |
2 | | Copyright (C) 1997-2023 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of the GNU opcodes library. |
5 | | |
6 | | This library is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | It is distributed in the hope that it will be useful, but WITHOUT |
12 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
13 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
14 | | License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program; if not, write to the Free Software |
18 | | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
19 | | MA 02110-1301, USA. */ |
20 | | |
21 | | #include "sysdep.h" |
22 | | #include <stdio.h> |
23 | | #include "opcode/d30v.h" |
24 | | #include "disassemble.h" |
25 | | #include "opintl.h" |
26 | | #include "libiberty.h" |
27 | | |
28 | 2.68k | #define PC_MASK 0xFFFFFFFF |
29 | | |
30 | | /* Return 0 if lookup fails, |
31 | | 1 if found and only one form, |
32 | | 2 if found and there are short and long forms. */ |
33 | | |
34 | | static int |
35 | | lookup_opcode (struct d30v_insn *insn, long num, int is_long) |
36 | 130k | { |
37 | 130k | int i = 0, op_index; |
38 | 130k | struct d30v_format *f; |
39 | 130k | struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table; |
40 | 130k | int op1 = (num >> 25) & 0x7; |
41 | 130k | int op2 = (num >> 20) & 0x1f; |
42 | 130k | int mod = (num >> 18) & 0x3; |
43 | | |
44 | | /* Find the opcode. */ |
45 | 130k | do |
46 | 8.62M | { |
47 | 8.62M | if ((op->op1 == op1) && (op->op2 == op2)) |
48 | 94.5k | break; |
49 | 8.53M | op++; |
50 | 8.53M | } |
51 | 8.53M | while (op->name); |
52 | | |
53 | 130k | if (!op || !op->name) |
54 | 36.0k | return 0; |
55 | | |
56 | 116k | while (op->op1 == op1 && op->op2 == op2) |
57 | 97.9k | { |
58 | | /* Scan through all the formats for the opcode. */ |
59 | 97.9k | op_index = op->format[i++]; |
60 | 97.9k | do |
61 | 115k | { |
62 | 115k | f = (struct d30v_format *) &d30v_format_table[op_index]; |
63 | 182k | while (f->form == op_index) |
64 | 143k | { |
65 | 143k | if ((!is_long || f->form >= LONG) && (f->modifier == mod)) |
66 | 76.3k | { |
67 | 76.3k | insn->form = f; |
68 | 76.3k | break; |
69 | 76.3k | } |
70 | 67.4k | f++; |
71 | 67.4k | } |
72 | 115k | if (insn->form) |
73 | 76.3k | break; |
74 | 115k | } |
75 | 97.9k | while ((op_index = op->format[i++]) != 0); |
76 | 97.9k | if (insn->form) |
77 | 76.3k | break; |
78 | 21.6k | op++; |
79 | 21.6k | i = 0; |
80 | 21.6k | } |
81 | 94.5k | if (insn->form == NULL) |
82 | 18.2k | return 0; |
83 | | |
84 | 76.3k | insn->op = op; |
85 | 76.3k | insn->ecc = (num >> 28) & 0x7; |
86 | 76.3k | if (op->format[1]) |
87 | 65.1k | return 2; |
88 | 11.1k | else |
89 | 11.1k | return 1; |
90 | 76.3k | } |
91 | | |
92 | | static int |
93 | | extract_value (uint64_t num, const struct d30v_operand *oper, int is_long) |
94 | 133k | { |
95 | 133k | unsigned int val; |
96 | 133k | int shift = 12 - oper->position; |
97 | 133k | unsigned int mask = (0xFFFFFFFF >> (32 - oper->bits)); |
98 | | |
99 | 133k | if (is_long) |
100 | 1.73k | { |
101 | 1.73k | if (oper->bits == 32) |
102 | | /* Piece together 32-bit constant. */ |
103 | 595 | val = ((num & 0x3FFFF) |
104 | 595 | | ((num & 0xFF00000) >> 2) |
105 | 595 | | ((num & 0x3F00000000LL) >> 6)); |
106 | 1.13k | else |
107 | 1.13k | val = (num >> (32 + shift)) & mask; |
108 | 1.73k | } |
109 | 131k | else |
110 | 131k | val = (num >> shift) & mask; |
111 | | |
112 | 133k | if (oper->flags & OPERAND_SHIFT) |
113 | 3.87k | val <<= 3; |
114 | | |
115 | 133k | return val; |
116 | 133k | } |
117 | | |
118 | | static void |
119 | | print_insn (struct disassemble_info *info, |
120 | | bfd_vma memaddr, |
121 | | uint64_t num, |
122 | | struct d30v_insn *insn, |
123 | | int is_long, |
124 | | int show_ext) |
125 | 76.3k | { |
126 | 76.3k | unsigned int val, opnum; |
127 | 76.3k | const struct d30v_operand *oper; |
128 | 76.3k | int i, match, need_comma = 0, need_paren = 0, found_control = 0; |
129 | 76.3k | unsigned int opind = 0; |
130 | | |
131 | 76.3k | (*info->fprintf_func) (info->stream, "%s", insn->op->name); |
132 | | |
133 | | /* Check for CMP or CMPU. */ |
134 | 76.3k | if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) |
135 | 225 | { |
136 | 225 | opind++; |
137 | 225 | val = |
138 | 225 | extract_value (num, |
139 | 225 | &d30v_operand_table[insn->form->operands[0]], |
140 | 225 | is_long); |
141 | 225 | (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]); |
142 | 225 | } |
143 | | |
144 | | /* Add in ".s" or ".l". */ |
145 | 76.3k | if (show_ext == 2) |
146 | 65.1k | { |
147 | 65.1k | if (is_long) |
148 | 595 | (*info->fprintf_func) (info->stream, ".l"); |
149 | 64.5k | else |
150 | 64.5k | (*info->fprintf_func) (info->stream, ".s"); |
151 | 65.1k | } |
152 | | |
153 | 76.3k | if (insn->ecc) |
154 | 24.2k | (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]); |
155 | | |
156 | 76.3k | (*info->fprintf_func) (info->stream, "\t"); |
157 | | |
158 | 219k | while (opind < ARRAY_SIZE (insn->form->operands) |
159 | 219k | && (opnum = insn->form->operands[opind++]) != 0) |
160 | 143k | { |
161 | 143k | int bits; |
162 | | |
163 | 143k | oper = &d30v_operand_table[opnum]; |
164 | 143k | bits = oper->bits; |
165 | 143k | if (oper->flags & OPERAND_SHIFT) |
166 | 3.87k | bits += 3; |
167 | | |
168 | 143k | if (need_comma |
169 | 143k | && oper->flags != OPERAND_PLUS |
170 | 143k | && oper->flags != OPERAND_MINUS) |
171 | 56.9k | { |
172 | 56.9k | need_comma = 0; |
173 | 56.9k | (*info->fprintf_func) (info->stream, ", "); |
174 | 56.9k | } |
175 | | |
176 | 143k | if (oper->flags == OPERAND_ATMINUS) |
177 | 0 | { |
178 | 0 | (*info->fprintf_func) (info->stream, "@-"); |
179 | 0 | continue; |
180 | 0 | } |
181 | 143k | if (oper->flags == OPERAND_MINUS) |
182 | 1.30k | { |
183 | 1.30k | (*info->fprintf_func) (info->stream, "-"); |
184 | 1.30k | continue; |
185 | 1.30k | } |
186 | 142k | if (oper->flags == OPERAND_PLUS) |
187 | 1.76k | { |
188 | 1.76k | (*info->fprintf_func) (info->stream, "+"); |
189 | 1.76k | continue; |
190 | 1.76k | } |
191 | 140k | if (oper->flags == OPERAND_ATSIGN) |
192 | 0 | { |
193 | 0 | (*info->fprintf_func) (info->stream, "@"); |
194 | 0 | continue; |
195 | 0 | } |
196 | 140k | if (oper->flags == OPERAND_ATPAR) |
197 | 7.70k | { |
198 | 7.70k | (*info->fprintf_func) (info->stream, "@("); |
199 | 7.70k | need_paren = 1; |
200 | 7.70k | continue; |
201 | 7.70k | } |
202 | | |
203 | 132k | if (oper->flags == OPERAND_SPECIAL) |
204 | 410 | continue; |
205 | | |
206 | 132k | val = extract_value (num, oper, is_long); |
207 | | |
208 | 132k | if (oper->flags & OPERAND_REG) |
209 | 120k | { |
210 | 120k | match = 0; |
211 | 120k | if (oper->flags & OPERAND_CONTROL) |
212 | 410 | { |
213 | 410 | const struct d30v_operand *oper3 |
214 | 410 | = &d30v_operand_table[insn->form->operands[2]]; |
215 | 410 | int id = extract_value (num, oper3, is_long); |
216 | | |
217 | 410 | found_control = 1; |
218 | 410 | switch (id) |
219 | 410 | { |
220 | 220 | case 0: |
221 | 220 | val |= OPERAND_CONTROL; |
222 | 220 | break; |
223 | 40 | case 1: |
224 | 156 | case 2: |
225 | 156 | val = OPERAND_CONTROL + MAX_CONTROL_REG + id; |
226 | 156 | break; |
227 | 34 | case 3: |
228 | 34 | val |= OPERAND_FLAG; |
229 | 34 | break; |
230 | 0 | default: |
231 | | /* xgettext: c-format */ |
232 | 0 | opcodes_error_handler (_("illegal id (%d)"), id); |
233 | 0 | abort (); |
234 | 410 | } |
235 | 410 | } |
236 | 120k | else if (oper->flags & OPERAND_ACC) |
237 | 393 | val |= OPERAND_ACC; |
238 | 120k | else if (oper->flags & OPERAND_FLAG) |
239 | 2.73k | val |= OPERAND_FLAG; |
240 | 12.8M | for (i = 0; i < reg_name_cnt (); i++) |
241 | 12.8M | { |
242 | 12.8M | if (val == pre_defined_registers[i].value) |
243 | 120k | { |
244 | 120k | if (pre_defined_registers[i].pname) |
245 | 3.96k | (*info->fprintf_func) |
246 | 3.96k | (info->stream, "%s", pre_defined_registers[i].pname); |
247 | 116k | else |
248 | 116k | (*info->fprintf_func) |
249 | 116k | (info->stream, "%s", pre_defined_registers[i].name); |
250 | 120k | match = 1; |
251 | 120k | break; |
252 | 120k | } |
253 | 12.8M | } |
254 | 120k | if (match == 0) |
255 | 34 | { |
256 | | /* This would only get executed if a register was not in |
257 | | the register table. */ |
258 | 34 | (*info->fprintf_func) |
259 | 34 | (info->stream, _("<unknown register %d>"), val & 0x3F); |
260 | 34 | } |
261 | 120k | } |
262 | | /* repeati has a relocation, but its first argument is a plain |
263 | | immediate. OTOH instructions like djsri have a pc-relative |
264 | | delay target, but an absolute jump target. Therefore, a test |
265 | | of insn->op->reloc_flag is not specific enough; we must test |
266 | | if the actual operand we are handling now is pc-relative. */ |
267 | 11.4k | else if (oper->flags & OPERAND_PCREL) |
268 | 2.68k | { |
269 | 2.68k | int neg = 0; |
270 | | |
271 | | /* IMM6S3 is unsigned. */ |
272 | 2.68k | if (oper->flags & OPERAND_SIGNED || bits == 32) |
273 | 2.13k | { |
274 | 2.13k | unsigned int sign = 1u << (bits - 1); |
275 | 2.13k | if (val & sign) |
276 | 1.07k | { |
277 | 1.07k | val = -val & (sign + sign - 1); |
278 | 1.07k | neg = 1; |
279 | 1.07k | } |
280 | 2.13k | } |
281 | 2.68k | if (neg) |
282 | 1.07k | { |
283 | 1.07k | (*info->fprintf_func) (info->stream, "-%x\t(", val); |
284 | 1.07k | (*info->print_address_func) ((memaddr - val) & PC_MASK, info); |
285 | 1.07k | (*info->fprintf_func) (info->stream, ")"); |
286 | 1.07k | } |
287 | 1.61k | else |
288 | 1.61k | { |
289 | 1.61k | (*info->fprintf_func) (info->stream, "%x\t(", val); |
290 | 1.61k | (*info->print_address_func) ((memaddr + val) & PC_MASK, info); |
291 | 1.61k | (*info->fprintf_func) (info->stream, ")"); |
292 | 1.61k | } |
293 | 2.68k | } |
294 | 8.73k | else if (insn->op->reloc_flag == RELOC_ABS) |
295 | 1.23k | { |
296 | 1.23k | (*info->print_address_func) (val, info); |
297 | 1.23k | } |
298 | 7.49k | else |
299 | 7.49k | { |
300 | 7.49k | if (oper->flags & OPERAND_SIGNED) |
301 | 5.96k | { |
302 | 5.96k | unsigned int sign = 1u << (bits - 1); |
303 | | |
304 | 5.96k | if (val & sign) |
305 | 3.33k | { |
306 | 3.33k | val = -val & (sign + sign - 1); |
307 | 3.33k | (*info->fprintf_func) (info->stream, "-"); |
308 | 3.33k | } |
309 | 5.96k | } |
310 | 7.49k | (*info->fprintf_func) (info->stream, "0x%x", val); |
311 | 7.49k | } |
312 | | /* If there is another operand, then write a comma and space. */ |
313 | 132k | if (opind < ARRAY_SIZE (insn->form->operands) |
314 | 132k | && insn->form->operands[opind] |
315 | 132k | && !(found_control && opind == 2)) |
316 | 56.9k | need_comma = 1; |
317 | 132k | } |
318 | 76.3k | if (need_paren) |
319 | 7.70k | (*info->fprintf_func) (info->stream, ")"); |
320 | 76.3k | } |
321 | | |
322 | | int |
323 | | print_insn_d30v (bfd_vma memaddr, struct disassemble_info *info) |
324 | 70.1k | { |
325 | 70.1k | int status, result; |
326 | 70.1k | bfd_byte buffer[12]; |
327 | 70.1k | uint32_t in1, in2; |
328 | 70.1k | struct d30v_insn insn; |
329 | 70.1k | uint64_t num; |
330 | | |
331 | 70.1k | insn.form = NULL; |
332 | | |
333 | 70.1k | info->bytes_per_line = 8; |
334 | 70.1k | info->bytes_per_chunk = 4; |
335 | 70.1k | info->display_endian = BFD_ENDIAN_BIG; |
336 | | |
337 | 70.1k | status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
338 | 70.1k | if (status != 0) |
339 | 54 | { |
340 | 54 | (*info->memory_error_func) (status, memaddr, info); |
341 | 54 | return -1; |
342 | 54 | } |
343 | 70.1k | in1 = bfd_getb32 (buffer); |
344 | | |
345 | 70.1k | status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info); |
346 | 70.1k | if (status != 0) |
347 | 137 | { |
348 | 137 | info->bytes_per_line = 8; |
349 | 137 | if (!(result = lookup_opcode (&insn, in1, 0))) |
350 | 67 | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); |
351 | 70 | else |
352 | 70 | print_insn (info, memaddr, (uint64_t) in1, &insn, 0, result); |
353 | 137 | return 4; |
354 | 137 | } |
355 | 69.9k | in2 = bfd_getb32 (buffer); |
356 | | |
357 | 69.9k | if (in1 & in2 & FM01) |
358 | 9.46k | { |
359 | | /* LONG instruction. */ |
360 | 9.46k | if (!(result = lookup_opcode (&insn, in1, 1))) |
361 | 8.87k | { |
362 | 8.87k | (*info->fprintf_func) (info->stream, ".long\t0x%x,0x%x", in1, in2); |
363 | 8.87k | return 8; |
364 | 8.87k | } |
365 | 595 | num = (uint64_t) in1 << 32 | in2; |
366 | 595 | print_insn (info, memaddr, num, &insn, 1, result); |
367 | 595 | } |
368 | 60.5k | else |
369 | 60.5k | { |
370 | 60.5k | num = in1; |
371 | 60.5k | if (!(result = lookup_opcode (&insn, in1, 0))) |
372 | 22.7k | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); |
373 | 37.7k | else |
374 | 37.7k | print_insn (info, memaddr, num, &insn, 0, result); |
375 | | |
376 | 60.5k | switch (((in1 >> 31) << 1) | (in2 >> 31)) |
377 | 60.5k | { |
378 | 40.1k | case 0: |
379 | 40.1k | (*info->fprintf_func) (info->stream, "\t||\t"); |
380 | 40.1k | break; |
381 | 10.3k | case 1: |
382 | 10.3k | (*info->fprintf_func) (info->stream, "\t->\t"); |
383 | 10.3k | break; |
384 | 10.0k | case 2: |
385 | 10.0k | (*info->fprintf_func) (info->stream, "\t<-\t"); |
386 | 10.0k | default: |
387 | 10.0k | break; |
388 | 60.5k | } |
389 | | |
390 | 60.5k | insn.form = NULL; |
391 | 60.5k | num = in2; |
392 | 60.5k | if (!(result = lookup_opcode (&insn, in2, 0))) |
393 | 22.6k | (*info->fprintf_func) (info->stream, ".long\t0x%x", in2); |
394 | 37.9k | else |
395 | 37.9k | print_insn (info, memaddr, num, &insn, 0, result); |
396 | 60.5k | } |
397 | 61.0k | return 8; |
398 | 69.9k | } |