/src/binutils-gdb/opcodes/d30v-dis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Disassemble D30V instructions. |
2 | | Copyright (C) 1997-2025 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 | 4.02k | #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 | 110k | { |
37 | 110k | int i = 0, op_index; |
38 | 110k | struct d30v_format *f; |
39 | 110k | struct d30v_opcode *op = (struct d30v_opcode *) d30v_opcode_table; |
40 | 110k | int op1 = (num >> 25) & 0x7; |
41 | 110k | int op2 = (num >> 20) & 0x1f; |
42 | 110k | int mod = (num >> 18) & 0x3; |
43 | | |
44 | | /* Find the opcode. */ |
45 | 110k | do |
46 | 8.27M | { |
47 | 8.27M | if ((op->op1 == op1) && (op->op2 == op2)) |
48 | 74.8k | break; |
49 | 8.19M | op++; |
50 | 8.19M | } |
51 | 8.19M | while (op->name); |
52 | | |
53 | 110k | if (!op || !op->name) |
54 | 35.5k | return 0; |
55 | | |
56 | 94.3k | while (op->op1 == op1 && op->op2 == op2) |
57 | 76.9k | { |
58 | | /* Scan through all the formats for the opcode. */ |
59 | 76.9k | op_index = op->format[i++]; |
60 | 76.9k | do |
61 | 94.6k | { |
62 | 94.6k | f = (struct d30v_format *) &d30v_format_table[op_index]; |
63 | 161k | while (f->form == op_index) |
64 | 124k | { |
65 | 124k | if ((!is_long || f->form >= LONG) && (f->modifier == mod)) |
66 | 57.4k | { |
67 | 57.4k | insn->form = f; |
68 | 57.4k | break; |
69 | 57.4k | } |
70 | 66.7k | f++; |
71 | 66.7k | } |
72 | 94.6k | if (insn->form) |
73 | 57.4k | break; |
74 | 94.6k | } |
75 | 76.9k | while ((op_index = op->format[i++]) != 0); |
76 | 76.9k | if (insn->form) |
77 | 57.4k | break; |
78 | 19.5k | op++; |
79 | 19.5k | i = 0; |
80 | 19.5k | } |
81 | 74.8k | if (insn->form == NULL) |
82 | 17.4k | return 0; |
83 | | |
84 | 57.4k | insn->op = op; |
85 | 57.4k | insn->ecc = (num >> 28) & 0x7; |
86 | 57.4k | if (op->format[1]) |
87 | 45.4k | return 2; |
88 | 11.9k | else |
89 | 11.9k | return 1; |
90 | 57.4k | } |
91 | | |
92 | | static int |
93 | | extract_value (uint64_t num, const struct d30v_operand *oper, int is_long) |
94 | 110k | { |
95 | 110k | unsigned int val; |
96 | 110k | int shift = 12 - oper->position; |
97 | 110k | unsigned int mask = (0xFFFFFFFF >> (32 - oper->bits)); |
98 | | |
99 | 110k | if (is_long) |
100 | 5.94k | { |
101 | 5.94k | if (oper->bits == 32) |
102 | | /* Piece together 32-bit constant. */ |
103 | 2.22k | val = ((num & 0x3FFFF) |
104 | 2.22k | | ((num & 0xFF00000) >> 2) |
105 | 2.22k | | ((num & 0x3F00000000LL) >> 6)); |
106 | 3.71k | else |
107 | 3.71k | val = (num >> (32 + shift)) & mask; |
108 | 5.94k | } |
109 | 105k | else |
110 | 105k | val = (num >> shift) & mask; |
111 | | |
112 | 110k | if (oper->flags & OPERAND_SHIFT) |
113 | 4.72k | val <<= 3; |
114 | | |
115 | 110k | return val; |
116 | 110k | } |
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 | 57.4k | { |
126 | 57.4k | unsigned int val, opnum; |
127 | 57.4k | const struct d30v_operand *oper; |
128 | 57.4k | int i, match, need_comma = 0, need_paren = 0, found_control = 0; |
129 | 57.4k | unsigned int opind = 0; |
130 | | |
131 | 57.4k | (*info->fprintf_func) (info->stream, "%s", insn->op->name); |
132 | | |
133 | | /* Check for CMP or CMPU. */ |
134 | 57.4k | if (d30v_operand_table[insn->form->operands[0]].flags & OPERAND_NAME) |
135 | 280 | { |
136 | 280 | opind++; |
137 | 280 | val = |
138 | 280 | extract_value (num, |
139 | 280 | &d30v_operand_table[insn->form->operands[0]], |
140 | 280 | is_long); |
141 | 280 | (*info->fprintf_func) (info->stream, "%s", d30v_cc_names[val]); |
142 | 280 | } |
143 | | |
144 | | /* Add in ".s" or ".l". */ |
145 | 57.4k | if (show_ext == 2) |
146 | 45.4k | { |
147 | 45.4k | if (is_long) |
148 | 2.22k | (*info->fprintf_func) (info->stream, ".l"); |
149 | 43.2k | else |
150 | 43.2k | (*info->fprintf_func) (info->stream, ".s"); |
151 | 45.4k | } |
152 | | |
153 | 57.4k | if (insn->ecc) |
154 | 25.3k | (*info->fprintf_func) (info->stream, "/%s", d30v_ecc_names[insn->ecc]); |
155 | | |
156 | 57.4k | (*info->fprintf_func) (info->stream, "\t"); |
157 | | |
158 | 179k | while (opind < ARRAY_SIZE (insn->form->operands) |
159 | 179k | && (opnum = insn->form->operands[opind++]) != 0) |
160 | 122k | { |
161 | 122k | int bits; |
162 | | |
163 | 122k | oper = &d30v_operand_table[opnum]; |
164 | 122k | bits = oper->bits; |
165 | 122k | if (oper->flags & OPERAND_SHIFT) |
166 | 4.72k | bits += 3; |
167 | | |
168 | 122k | if (need_comma |
169 | 122k | && oper->flags != OPERAND_PLUS |
170 | 122k | && oper->flags != OPERAND_MINUS) |
171 | 53.7k | { |
172 | 53.7k | need_comma = 0; |
173 | 53.7k | (*info->fprintf_func) (info->stream, ", "); |
174 | 53.7k | } |
175 | | |
176 | 122k | if (oper->flags == OPERAND_ATMINUS) |
177 | 0 | { |
178 | 0 | (*info->fprintf_func) (info->stream, "@-"); |
179 | 0 | continue; |
180 | 0 | } |
181 | 122k | if (oper->flags == OPERAND_MINUS) |
182 | 1.18k | { |
183 | 1.18k | (*info->fprintf_func) (info->stream, "-"); |
184 | 1.18k | continue; |
185 | 1.18k | } |
186 | 120k | if (oper->flags == OPERAND_PLUS) |
187 | 3.05k | { |
188 | 3.05k | (*info->fprintf_func) (info->stream, "+"); |
189 | 3.05k | continue; |
190 | 3.05k | } |
191 | 117k | if (oper->flags == OPERAND_ATSIGN) |
192 | 0 | { |
193 | 0 | (*info->fprintf_func) (info->stream, "@"); |
194 | 0 | continue; |
195 | 0 | } |
196 | 117k | if (oper->flags == OPERAND_ATPAR) |
197 | 7.24k | { |
198 | 7.24k | (*info->fprintf_func) (info->stream, "@("); |
199 | 7.24k | need_paren = 1; |
200 | 7.24k | continue; |
201 | 7.24k | } |
202 | | |
203 | 110k | if (oper->flags == OPERAND_SPECIAL) |
204 | 1.19k | continue; |
205 | | |
206 | 109k | val = extract_value (num, oper, is_long); |
207 | | |
208 | 109k | if (oper->flags & OPERAND_REG) |
209 | 95.9k | { |
210 | 95.9k | match = 0; |
211 | 95.9k | if (oper->flags & OPERAND_CONTROL) |
212 | 1.19k | { |
213 | 1.19k | const struct d30v_operand *oper3 |
214 | 1.19k | = &d30v_operand_table[insn->form->operands[2]]; |
215 | 1.19k | int id = extract_value (num, oper3, is_long); |
216 | | |
217 | 1.19k | found_control = 1; |
218 | 1.19k | switch (id) |
219 | 1.19k | { |
220 | 518 | case 0: |
221 | 518 | val |= OPERAND_CONTROL; |
222 | 518 | break; |
223 | 384 | case 1: |
224 | 565 | case 2: |
225 | 565 | val = OPERAND_CONTROL + MAX_CONTROL_REG + id; |
226 | 565 | break; |
227 | 115 | case 3: |
228 | 115 | val |= OPERAND_FLAG; |
229 | 115 | break; |
230 | 0 | default: |
231 | | /* xgettext: c-format */ |
232 | 0 | opcodes_error_handler (_("illegal id (%d)"), id); |
233 | 0 | abort (); |
234 | 1.19k | } |
235 | 1.19k | } |
236 | 94.7k | else if (oper->flags & OPERAND_ACC) |
237 | 291 | val |= OPERAND_ACC; |
238 | 94.4k | else if (oper->flags & OPERAND_FLAG) |
239 | 2.78k | val |= OPERAND_FLAG; |
240 | 10.5M | for (i = 0; i < reg_name_cnt (); i++) |
241 | 10.5M | { |
242 | 10.5M | if (val == pre_defined_registers[i].value) |
243 | 95.8k | { |
244 | 95.8k | if (pre_defined_registers[i].pname) |
245 | 3.21k | (*info->fprintf_func) |
246 | 3.21k | (info->stream, "%s", pre_defined_registers[i].pname); |
247 | 92.6k | else |
248 | 92.6k | (*info->fprintf_func) |
249 | 92.6k | (info->stream, "%s", pre_defined_registers[i].name); |
250 | 95.8k | match = 1; |
251 | 95.8k | break; |
252 | 95.8k | } |
253 | 10.5M | } |
254 | 95.9k | if (match == 0) |
255 | 113 | { |
256 | | /* This would only get executed if a register was not in |
257 | | the register table. */ |
258 | 113 | (*info->fprintf_func) |
259 | 113 | (info->stream, _("<unknown register %d>"), val & 0x3F); |
260 | 113 | } |
261 | 95.9k | } |
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 | 13.4k | else if (oper->flags & OPERAND_PCREL) |
268 | 4.02k | { |
269 | 4.02k | int neg = 0; |
270 | | |
271 | | /* IMM6S3 is unsigned. */ |
272 | 4.02k | if (oper->flags & OPERAND_SIGNED || bits == 32) |
273 | 2.37k | { |
274 | 2.37k | unsigned int sign = 1u << (bits - 1); |
275 | 2.37k | if (val & sign) |
276 | 1.19k | { |
277 | 1.19k | val = -val & (sign + sign - 1); |
278 | 1.19k | neg = 1; |
279 | 1.19k | } |
280 | 2.37k | } |
281 | 4.02k | if (neg) |
282 | 1.19k | { |
283 | 1.19k | (*info->fprintf_func) (info->stream, "-%x\t(", val); |
284 | 1.19k | (*info->print_address_func) ((memaddr - val) & PC_MASK, info); |
285 | 1.19k | (*info->fprintf_func) (info->stream, ")"); |
286 | 1.19k | } |
287 | 2.82k | else |
288 | 2.82k | { |
289 | 2.82k | (*info->fprintf_func) (info->stream, "%x\t(", val); |
290 | 2.82k | (*info->print_address_func) ((memaddr + val) & PC_MASK, info); |
291 | 2.82k | (*info->fprintf_func) (info->stream, ")"); |
292 | 2.82k | } |
293 | 4.02k | } |
294 | 9.47k | else if (insn->op->reloc_flag == RELOC_ABS) |
295 | 1.11k | { |
296 | 1.11k | (*info->print_address_func) (val, info); |
297 | 1.11k | } |
298 | 8.35k | else |
299 | 8.35k | { |
300 | 8.35k | if (oper->flags & OPERAND_SIGNED) |
301 | 5.75k | { |
302 | 5.75k | unsigned int sign = 1u << (bits - 1); |
303 | | |
304 | 5.75k | if (val & sign) |
305 | 2.55k | { |
306 | 2.55k | val = -val & (sign + sign - 1); |
307 | 2.55k | (*info->fprintf_func) (info->stream, "-"); |
308 | 2.55k | } |
309 | 5.75k | } |
310 | 8.35k | (*info->fprintf_func) (info->stream, "0x%x", val); |
311 | 8.35k | } |
312 | | /* If there is another operand, then write a comma and space. */ |
313 | 109k | if (opind < ARRAY_SIZE (insn->form->operands) |
314 | 109k | && insn->form->operands[opind] |
315 | 109k | && !(found_control && opind == 2)) |
316 | 53.7k | need_comma = 1; |
317 | 109k | } |
318 | 57.4k | if (need_paren) |
319 | 7.24k | (*info->fprintf_func) (info->stream, ")"); |
320 | 57.4k | } |
321 | | |
322 | | int |
323 | | print_insn_d30v (bfd_vma memaddr, struct disassemble_info *info) |
324 | 62.3k | { |
325 | 62.3k | int status, result; |
326 | 62.3k | bfd_byte buffer[12]; |
327 | 62.3k | uint32_t in1, in2; |
328 | 62.3k | struct d30v_insn insn; |
329 | 62.3k | uint64_t num; |
330 | | |
331 | 62.3k | insn.form = NULL; |
332 | | |
333 | 62.3k | info->bytes_per_line = 8; |
334 | 62.3k | info->bytes_per_chunk = 4; |
335 | 62.3k | info->display_endian = BFD_ENDIAN_BIG; |
336 | | |
337 | 62.3k | status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
338 | 62.3k | if (status != 0) |
339 | 213 | { |
340 | 213 | (*info->memory_error_func) (status, memaddr, info); |
341 | 213 | return -1; |
342 | 213 | } |
343 | 62.1k | in1 = bfd_getb32 (buffer); |
344 | | |
345 | 62.1k | status = (*info->read_memory_func) (memaddr + 4, buffer, 4, info); |
346 | 62.1k | if (status != 0) |
347 | 198 | { |
348 | 198 | info->bytes_per_line = 8; |
349 | 198 | if (!(result = lookup_opcode (&insn, in1, 0))) |
350 | 89 | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); |
351 | 109 | else |
352 | 109 | print_insn (info, memaddr, (uint64_t) in1, &insn, 0, result); |
353 | 198 | return 4; |
354 | 198 | } |
355 | 61.9k | in2 = bfd_getb32 (buffer); |
356 | | |
357 | 61.9k | if (in1 & in2 & FM01) |
358 | 13.7k | { |
359 | | /* LONG instruction. */ |
360 | 13.7k | if (!(result = lookup_opcode (&insn, in1, 1))) |
361 | 11.4k | { |
362 | 11.4k | (*info->fprintf_func) (info->stream, ".long\t0x%x,0x%x", in1, in2); |
363 | 11.4k | return 8; |
364 | 11.4k | } |
365 | 2.22k | num = (uint64_t) in1 << 32 | in2; |
366 | 2.22k | print_insn (info, memaddr, num, &insn, 1, result); |
367 | 2.22k | } |
368 | 48.2k | else |
369 | 48.2k | { |
370 | 48.2k | num = in1; |
371 | 48.2k | if (!(result = lookup_opcode (&insn, in1, 0))) |
372 | 21.2k | (*info->fprintf_func) (info->stream, ".long\t0x%x", in1); |
373 | 26.9k | else |
374 | 26.9k | print_insn (info, memaddr, num, &insn, 0, result); |
375 | | |
376 | 48.2k | switch (((in1 >> 31) << 1) | (in2 >> 31)) |
377 | 48.2k | { |
378 | 30.1k | case 0: |
379 | 30.1k | (*info->fprintf_func) (info->stream, "\t||\t"); |
380 | 30.1k | break; |
381 | 9.05k | case 1: |
382 | 9.05k | (*info->fprintf_func) (info->stream, "\t->\t"); |
383 | 9.05k | break; |
384 | 8.96k | case 2: |
385 | 8.96k | (*info->fprintf_func) (info->stream, "\t<-\t"); |
386 | 8.96k | default: |
387 | 8.96k | break; |
388 | 48.2k | } |
389 | | |
390 | 48.2k | insn.form = NULL; |
391 | 48.2k | num = in2; |
392 | 48.2k | if (!(result = lookup_opcode (&insn, in2, 0))) |
393 | 20.1k | (*info->fprintf_func) (info->stream, ".long\t0x%x", in2); |
394 | 28.1k | else |
395 | 28.1k | print_insn (info, memaddr, num, &insn, 0, result); |
396 | 48.2k | } |
397 | 50.4k | return 8; |
398 | 61.9k | } |