]>
Commit | Line | Data |
---|---|---|
adde6300 | 1 | /* Disassemble AVR instructions. |
fd67aa11 | 2 | Copyright (C) 1999-2024 Free Software Foundation, Inc. |
adde6300 AM |
3 | |
4 | Contributed by Denis Chertykov <denisc@overta.ru> | |
5 | ||
9b201bb5 NC |
6 | This file is part of libopcodes. |
7 | ||
8 | This library is free software; you can redistribute it and/or modify | |
47b0e7ad | 9 | it under the terms of the GNU General Public License as published by |
9b201bb5 NC |
10 | the Free Software Foundation; either version 3, or (at your option) |
11 | any later version. | |
adde6300 | 12 | |
9b201bb5 NC |
13 | It is distributed in the hope that it will be useful, but WITHOUT |
14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
15 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
16 | License for more details. | |
adde6300 | 17 | |
47b0e7ad NC |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program; if not, write to the Free Software | |
9b201bb5 NC |
20 | Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
21 | MA 02110-1301, USA. */ | |
adde6300 | 22 | |
0d8dfecf | 23 | #include "sysdep.h" |
df7b86aa | 24 | #include <assert.h> |
88c1242d | 25 | #include "disassemble.h" |
adde6300 | 26 | #include "opintl.h" |
11041102 | 27 | #include "libiberty.h" |
3dfb1b6d | 28 | #include <stdint.h> |
3c504221 | 29 | |
bab84c47 | 30 | struct avr_opcodes_s |
adde6300 | 31 | { |
bab84c47 DC |
32 | char *name; |
33 | char *constraints; | |
34 | char *opcode; | |
47b0e7ad | 35 | int insn_size; /* In words. */ |
bab84c47 DC |
36 | int isa; |
37 | unsigned int bin_opcode; | |
bab84c47 | 38 | }; |
adde6300 | 39 | |
bab84c47 | 40 | #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \ |
11041102 | 41 | {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN}, |
adde6300 | 42 | |
11041102 | 43 | const struct avr_opcodes_s avr_opcodes[] = |
adde6300 | 44 | { |
bab84c47 | 45 | #include "opcode/avr.h" |
11041102 | 46 | {NULL, NULL, NULL, 0, 0, 0} |
bab84c47 | 47 | }; |
adde6300 | 48 | |
af692060 NC |
49 | static const char * comment_start = "0x"; |
50 | ||
463f102c | 51 | static int |
96c7115a MN |
52 | avr_operand (unsigned int insn, |
53 | unsigned int insn2, | |
54 | unsigned int pc, | |
55 | int constraint, | |
56 | char * opcode_str, | |
57 | char * buf, | |
58 | char * comment, | |
a0f3a4c6 | 59 | enum disassembler_style * style, |
96c7115a MN |
60 | int regs, |
61 | int * sym, | |
62 | bfd_vma * sym_addr, | |
63 | disassemble_info * info) | |
adde6300 | 64 | { |
463f102c | 65 | int ok = 1; |
246f4c05 | 66 | *sym = 0; |
463f102c | 67 | |
bab84c47 DC |
68 | switch (constraint) |
69 | { | |
70 | /* Any register operand. */ | |
71 | case 'r': | |
72 | if (regs) | |
47b0e7ad | 73 | insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register. */ |
bab84c47 | 74 | else |
47b0e7ad | 75 | insn = (insn & 0x01f0) >> 4; /* Destination register. */ |
43e65147 | 76 | |
bab84c47 | 77 | sprintf (buf, "r%d", insn); |
a0f3a4c6 | 78 | *style = dis_style_register; |
bab84c47 DC |
79 | break; |
80 | ||
81 | case 'd': | |
82 | if (regs) | |
83 | sprintf (buf, "r%d", 16 + (insn & 0xf)); | |
84 | else | |
85 | sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4)); | |
a0f3a4c6 | 86 | *style = dis_style_register; |
bab84c47 | 87 | break; |
43e65147 | 88 | |
bab84c47 DC |
89 | case 'w': |
90 | sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3)); | |
a0f3a4c6 | 91 | *style = dis_style_register; |
bab84c47 | 92 | break; |
43e65147 | 93 | |
bab84c47 DC |
94 | case 'a': |
95 | if (regs) | |
96 | sprintf (buf, "r%d", 16 + (insn & 7)); | |
97 | else | |
98 | sprintf (buf, "r%d", 16 + ((insn >> 4) & 7)); | |
a0f3a4c6 | 99 | *style = dis_style_register; |
bab84c47 | 100 | break; |
adde6300 | 101 | |
bab84c47 DC |
102 | case 'v': |
103 | if (regs) | |
104 | sprintf (buf, "r%d", (insn & 0xf) * 2); | |
105 | else | |
106 | sprintf (buf, "r%d", ((insn & 0xf0) >> 3)); | |
a0f3a4c6 | 107 | *style = dis_style_register; |
bab84c47 DC |
108 | break; |
109 | ||
110 | case 'e': | |
463f102c DC |
111 | { |
112 | char *xyz; | |
113 | ||
114 | switch (insn & 0x100f) | |
115 | { | |
116 | case 0x0000: xyz = "Z"; break; | |
117 | case 0x1001: xyz = "Z+"; break; | |
118 | case 0x1002: xyz = "-Z"; break; | |
119 | case 0x0008: xyz = "Y"; break; | |
120 | case 0x1009: xyz = "Y+"; break; | |
121 | case 0x100a: xyz = "-Y"; break; | |
122 | case 0x100c: xyz = "X"; break; | |
123 | case 0x100d: xyz = "X+"; break; | |
124 | case 0x100e: xyz = "-X"; break; | |
125 | default: xyz = "??"; ok = 0; | |
126 | } | |
c8941035 | 127 | strcpy (buf, xyz); |
463f102c DC |
128 | |
129 | if (AVR_UNDEF_P (insn)) | |
130 | sprintf (comment, _("undefined")); | |
131 | } | |
a0f3a4c6 | 132 | *style = dis_style_register; |
bab84c47 DC |
133 | break; |
134 | ||
135 | case 'z': | |
136 | *buf++ = 'Z'; | |
8cc66334 EW |
137 | |
138 | /* Check for post-increment. */ | |
139 | char *s; | |
140 | for (s = opcode_str; *s; ++s) | |
141 | { | |
142 | if (*s == '+') | |
143 | { | |
5d73b1f1 NC |
144 | if (insn & (1 << (15 - (s - opcode_str)))) |
145 | *buf++ = '+'; | |
8cc66334 EW |
146 | break; |
147 | } | |
148 | } | |
149 | ||
bab84c47 | 150 | *buf = '\0'; |
463f102c DC |
151 | if (AVR_UNDEF_P (insn)) |
152 | sprintf (comment, _("undefined")); | |
a0f3a4c6 | 153 | *style = dis_style_register; |
bab84c47 DC |
154 | break; |
155 | ||
156 | case 'b': | |
157 | { | |
463f102c | 158 | unsigned int x; |
43e65147 | 159 | |
bab84c47 DC |
160 | x = (insn & 7); |
161 | x |= (insn >> 7) & (3 << 3); | |
162 | x |= (insn >> 8) & (1 << 5); | |
43e65147 | 163 | |
bab84c47 DC |
164 | if (insn & 0x8) |
165 | *buf++ = 'Y'; | |
166 | else | |
167 | *buf++ = 'Z'; | |
168 | sprintf (buf, "+%d", x); | |
169 | sprintf (comment, "0x%02x", x); | |
a0f3a4c6 | 170 | *style = dis_style_register; |
bab84c47 DC |
171 | } |
172 | break; | |
43e65147 | 173 | |
bab84c47 | 174 | case 'h': |
246f4c05 SS |
175 | *sym = 1; |
176 | *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2; | |
c4f5c3d7 | 177 | /* See PR binutils/2454. Ideally we would like to display the hex |
52f16a0e NC |
178 | value of the address only once, but this would mean recoding |
179 | objdump_print_address() which would affect many targets. */ | |
43e65147 | 180 | sprintf (buf, "%#lx", (unsigned long) *sym_addr); |
c8941035 | 181 | strcpy (comment, comment_start); |
96c7115a MN |
182 | info->insn_info_valid = 1; |
183 | info->insn_type = dis_jsr; | |
184 | info->target = *sym_addr; | |
a0f3a4c6 | 185 | *style = dis_style_address; |
bab84c47 | 186 | break; |
43e65147 | 187 | |
bab84c47 DC |
188 | case 'L': |
189 | { | |
190 | int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2; | |
191 | sprintf (buf, ".%+-8d", rel_addr); | |
246f4c05 SS |
192 | *sym = 1; |
193 | *sym_addr = pc + 2 + rel_addr; | |
c8941035 | 194 | strcpy (comment, comment_start); |
96c7115a MN |
195 | info->insn_info_valid = 1; |
196 | info->insn_type = dis_branch; | |
197 | info->target = *sym_addr; | |
a0f3a4c6 | 198 | *style = dis_style_address_offset; |
bab84c47 DC |
199 | } |
200 | break; | |
201 | ||
202 | case 'l': | |
203 | { | |
204 | int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2; | |
af692060 | 205 | |
bab84c47 | 206 | sprintf (buf, ".%+-8d", rel_addr); |
246f4c05 SS |
207 | *sym = 1; |
208 | *sym_addr = pc + 2 + rel_addr; | |
c8941035 | 209 | strcpy (comment, comment_start); |
96c7115a MN |
210 | info->insn_info_valid = 1; |
211 | info->insn_type = dis_condbranch; | |
212 | info->target = *sym_addr; | |
a0f3a4c6 | 213 | *style = dis_style_address_offset; |
bab84c47 DC |
214 | } |
215 | break; | |
216 | ||
217 | case 'i': | |
1857fe72 DC |
218 | { |
219 | unsigned int val = insn2 | 0x800000; | |
220 | *sym = 1; | |
221 | *sym_addr = val; | |
222 | sprintf (buf, "0x%04X", insn2); | |
223 | strcpy (comment, comment_start); | |
a0f3a4c6 | 224 | *style = dis_style_immediate; |
1857fe72 | 225 | } |
bab84c47 | 226 | break; |
f36e8886 BS |
227 | |
228 | case 'j': | |
229 | { | |
230 | unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5) | |
231 | | ((insn & 0x100) >> 2)); | |
1d378749 NC |
232 | if ((insn & 0x100) == 0) |
233 | val |= 0x80; | |
1857fe72 DC |
234 | *sym = 1; |
235 | *sym_addr = val | 0x800000; | |
f36e8886 | 236 | sprintf (buf, "0x%02x", val); |
1857fe72 | 237 | strcpy (comment, comment_start); |
a0f3a4c6 | 238 | *style = dis_style_immediate; |
f36e8886 BS |
239 | } |
240 | break; | |
43e65147 | 241 | |
bab84c47 DC |
242 | case 'M': |
243 | sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf)); | |
244 | sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf)); | |
a0f3a4c6 | 245 | *style = dis_style_immediate; |
bab84c47 DC |
246 | break; |
247 | ||
248 | case 'n': | |
463f102c | 249 | sprintf (buf, "??"); |
a6743a54 AM |
250 | /* xgettext:c-format */ |
251 | opcodes_error_handler (_("internal disassembler error")); | |
463f102c | 252 | ok = 0; |
a0f3a4c6 | 253 | *style = dis_style_immediate; |
bab84c47 | 254 | break; |
43e65147 | 255 | |
bab84c47 | 256 | case 'K': |
463f102c DC |
257 | { |
258 | unsigned int x; | |
259 | ||
260 | x = (insn & 0xf) | ((insn >> 2) & 0x30); | |
261 | sprintf (buf, "0x%02x", x); | |
262 | sprintf (comment, "%d", x); | |
a0f3a4c6 | 263 | *style = dis_style_immediate; |
463f102c | 264 | } |
bab84c47 | 265 | break; |
43e65147 | 266 | |
bab84c47 DC |
267 | case 's': |
268 | sprintf (buf, "%d", insn & 7); | |
a0f3a4c6 | 269 | *style = dis_style_immediate; |
bab84c47 | 270 | break; |
43e65147 | 271 | |
bab84c47 DC |
272 | case 'S': |
273 | sprintf (buf, "%d", (insn >> 4) & 7); | |
a0f3a4c6 | 274 | *style = dis_style_immediate; |
bab84c47 | 275 | break; |
43e65147 | 276 | |
bab84c47 DC |
277 | case 'P': |
278 | { | |
279 | unsigned int x; | |
47b0e7ad | 280 | |
bab84c47 DC |
281 | x = (insn & 0xf); |
282 | x |= (insn >> 5) & 0x30; | |
283 | sprintf (buf, "0x%02x", x); | |
284 | sprintf (comment, "%d", x); | |
a0f3a4c6 | 285 | *style = dis_style_address; |
bab84c47 DC |
286 | } |
287 | break; | |
288 | ||
289 | case 'p': | |
290 | { | |
291 | unsigned int x; | |
43e65147 | 292 | |
bab84c47 DC |
293 | x = (insn >> 3) & 0x1f; |
294 | sprintf (buf, "0x%02x", x); | |
295 | sprintf (comment, "%d", x); | |
a0f3a4c6 | 296 | *style = dis_style_address; |
bab84c47 DC |
297 | } |
298 | break; | |
43e65147 | 299 | |
8cc66334 EW |
300 | case 'E': |
301 | sprintf (buf, "%d", (insn >> 4) & 15); | |
a0f3a4c6 | 302 | *style = dis_style_immediate; |
8cc66334 | 303 | break; |
43e65147 | 304 | |
bab84c47 DC |
305 | case '?': |
306 | *buf = '\0'; | |
307 | break; | |
43e65147 | 308 | |
bab84c47 | 309 | default: |
463f102c | 310 | sprintf (buf, "??"); |
a6743a54 AM |
311 | /* xgettext:c-format */ |
312 | opcodes_error_handler (_("unknown constraint `%c'"), constraint); | |
463f102c | 313 | ok = 0; |
bab84c47 | 314 | } |
463f102c DC |
315 | |
316 | return ok; | |
adde6300 AM |
317 | } |
318 | ||
0a7e1018 YQ |
319 | /* Read the opcode from ADDR. Return 0 in success and save opcode |
320 | in *INSN, otherwise, return -1. */ | |
321 | ||
322 | static int | |
323 | avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn) | |
adde6300 AM |
324 | { |
325 | bfd_byte buffer[2]; | |
326 | int status; | |
47b0e7ad NC |
327 | |
328 | status = info->read_memory_func (addr, buffer, 2, info); | |
329 | ||
330 | if (status == 0) | |
0a7e1018 YQ |
331 | { |
332 | *insn = bfd_getl16 (buffer); | |
333 | return 0; | |
334 | } | |
47b0e7ad NC |
335 | |
336 | info->memory_error_func (status, addr, info); | |
337 | return -1; | |
adde6300 AM |
338 | } |
339 | ||
340 | ||
341 | int | |
47b0e7ad | 342 | print_insn_avr (bfd_vma addr, disassemble_info *info) |
adde6300 | 343 | { |
0a7e1018 | 344 | uint16_t insn, insn2; |
11041102 KD |
345 | const struct avr_opcodes_s *opcode; |
346 | static unsigned int *maskptr; | |
adde6300 | 347 | void *stream = info->stream; |
a0f3a4c6 | 348 | fprintf_styled_ftype prin = info->fprintf_styled_func; |
11041102 | 349 | static unsigned int *avr_bin_masks; |
bab84c47 | 350 | static int initialized; |
adde6300 | 351 | int cmd_len = 2; |
463f102c DC |
352 | int ok = 0; |
353 | char op1[20], op2[20], comment1[40], comment2[40]; | |
a0f3a4c6 | 354 | enum disassembler_style style_op1, style_op2; |
246f4c05 SS |
355 | int sym_op1 = 0, sym_op2 = 0; |
356 | bfd_vma sym_addr1, sym_addr2; | |
adde6300 | 357 | |
96c7115a MN |
358 | /* Clear instruction information field. */ |
359 | info->insn_info_valid = 0; | |
360 | info->branch_delay_insns = 0; | |
361 | info->data_size = 0; | |
362 | info->insn_type = dis_noninsn; | |
363 | info->target = 0; | |
364 | info->target2 = 0; | |
af692060 | 365 | |
bab84c47 DC |
366 | if (!initialized) |
367 | { | |
11041102 KD |
368 | unsigned int nopcodes; |
369 | ||
af692060 NC |
370 | /* PR 4045: Try to avoid duplicating the 0x prefix that |
371 | objdump_print_addr() will put on addresses when there | |
372 | is no symbol table available. */ | |
373 | if (info->symtab_size == 0) | |
374 | comment_start = " "; | |
375 | ||
11041102 | 376 | nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s); |
43e65147 | 377 | |
47b0e7ad | 378 | avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int)); |
11041102 KD |
379 | |
380 | for (opcode = avr_opcodes, maskptr = avr_bin_masks; | |
381 | opcode->name; | |
382 | opcode++, maskptr++) | |
bab84c47 DC |
383 | { |
384 | char * s; | |
385 | unsigned int bin = 0; | |
386 | unsigned int mask = 0; | |
43e65147 | 387 | |
bab84c47 DC |
388 | for (s = opcode->opcode; *s; ++s) |
389 | { | |
390 | bin <<= 1; | |
391 | mask <<= 1; | |
392 | bin |= (*s == '1'); | |
393 | mask |= (*s == '1' || *s == '0'); | |
394 | } | |
395 | assert (s - opcode->opcode == 16); | |
396 | assert (opcode->bin_opcode == bin); | |
11041102 | 397 | *maskptr = mask; |
bab84c47 | 398 | } |
11041102 KD |
399 | |
400 | initialized = 1; | |
bab84c47 | 401 | } |
adde6300 | 402 | |
0a7e1018 YQ |
403 | if (avrdis_opcode (addr, info, &insn) != 0) |
404 | return -1; | |
43e65147 | 405 | |
11041102 KD |
406 | for (opcode = avr_opcodes, maskptr = avr_bin_masks; |
407 | opcode->name; | |
408 | opcode++, maskptr++) | |
f36e8886 BS |
409 | { |
410 | if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny)) | |
411 | continue; | |
412 | if ((insn & *maskptr) == opcode->bin_opcode) | |
413 | break; | |
414 | } | |
43e65147 | 415 | |
463f102c DC |
416 | /* Special case: disassemble `ldd r,b+0' as `ld r,b', and |
417 | `std b+0,r' as `st b,r' (next entry in the table). */ | |
418 | ||
419 | if (AVR_DISP0_P (insn)) | |
420 | opcode++; | |
421 | ||
422 | op1[0] = 0; | |
423 | op2[0] = 0; | |
424 | comment1[0] = 0; | |
425 | comment2[0] = 0; | |
a0f3a4c6 MN |
426 | style_op1 = dis_style_text; |
427 | style_op2 = dis_style_text; | |
463f102c | 428 | |
bab84c47 | 429 | if (opcode->name) |
adde6300 | 430 | { |
8cc66334 EW |
431 | char *constraints = opcode->constraints; |
432 | char *opcode_str = opcode->opcode; | |
bab84c47 | 433 | |
00d2865b | 434 | insn2 = 0; |
463f102c | 435 | ok = 1; |
bab84c47 DC |
436 | |
437 | if (opcode->insn_size > 1) | |
438 | { | |
0a7e1018 YQ |
439 | if (avrdis_opcode (addr + 2, info, &insn2) != 0) |
440 | return -1; | |
bab84c47 DC |
441 | cmd_len = 4; |
442 | } | |
443 | ||
8cc66334 | 444 | if (*constraints && *constraints != '?') |
bab84c47 | 445 | { |
8cc66334 | 446 | int regs = REGISTER_P (*constraints); |
bab84c47 | 447 | |
96c7115a | 448 | ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1, |
a0f3a4c6 MN |
449 | comment1, &style_op1, 0, &sym_op1, &sym_addr1, |
450 | info); | |
bab84c47 | 451 | |
8cc66334 | 452 | if (ok && *(++constraints) == ',') |
96c7115a | 453 | ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str, |
a0f3a4c6 MN |
454 | op2, *comment1 ? comment2 : comment1, |
455 | &style_op2, regs, &sym_op2, &sym_addr2, | |
456 | info); | |
bab84c47 | 457 | } |
463f102c | 458 | } |
bab84c47 | 459 | |
463f102c DC |
460 | if (!ok) |
461 | { | |
462 | /* Unknown opcode, or invalid combination of operands. */ | |
463 | sprintf (op1, "0x%04x", insn); | |
464 | op2[0] = 0; | |
465 | sprintf (comment1, "????"); | |
466 | comment2[0] = 0; | |
467 | } | |
bab84c47 | 468 | |
a0f3a4c6 MN |
469 | (*prin) (stream, ok ? dis_style_mnemonic : dis_style_assembler_directive, |
470 | "%s", ok ? opcode->name : ".word"); | |
471 | ||
463f102c | 472 | if (*op1) |
a0f3a4c6 | 473 | (*prin) (stream, style_op1, "\t%s", op1); |
bab84c47 | 474 | |
463f102c | 475 | if (*op2) |
a0f3a4c6 MN |
476 | { |
477 | (*prin) (stream, dis_style_text, ", "); | |
478 | (*prin) (stream, style_op2, "%s", op2); | |
479 | } | |
463f102c DC |
480 | |
481 | if (*comment1) | |
a0f3a4c6 | 482 | (*prin) (stream, dis_style_comment_start, "\t; %s", comment1); |
463f102c | 483 | |
246f4c05 | 484 | if (sym_op1) |
73f643e9 | 485 | info->print_address_func (sym_addr1, info); |
246f4c05 | 486 | |
463f102c | 487 | if (*comment2) |
a0f3a4c6 | 488 | (*prin) (stream, dis_style_comment_start, " %s", comment2); |
bab84c47 | 489 | |
246f4c05 | 490 | if (sym_op2) |
73f643e9 | 491 | info->print_address_func (sym_addr2, info); |
246f4c05 | 492 | |
adde6300 AM |
493 | return cmd_len; |
494 | } |