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