1/* Disassemble AVR instructions. 2 Copyright (C) 1999-2022 Free Software Foundation, Inc. 3 4 Contributed by Denis Chertykov <denisc@overta.ru> 5 6 This file is part of libopcodes. 7 8 This library is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3, or (at your option) 11 any later version. 12 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. 17 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 20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 21 MA 02110-1301, USA. */ 22 23#include "sysdep.h" 24#include <assert.h> 25#include "disassemble.h" 26#include "opintl.h" 27#include "libiberty.h" 28#include <stdint.h> 29 30struct avr_opcodes_s 31{ 32 char *name; 33 char *constraints; 34 char *opcode; 35 int insn_size; /* In words. */ 36 int isa; 37 unsigned int bin_opcode; 38}; 39 40#define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \ 41{#NAME, CONSTR, OPCODE, SIZE, ISA, BIN}, 42 43const struct avr_opcodes_s avr_opcodes[] = 44{ 45 #include "opcode/avr.h" 46 {NULL, NULL, NULL, 0, 0, 0} 47}; 48 49static const char * comment_start = "0x"; 50 51static int 52avr_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, 59 enum disassembler_style * style, 60 int regs, 61 int * sym, 62 bfd_vma * sym_addr, 63 disassemble_info * info) 64{ 65 int ok = 1; 66 *sym = 0; 67 68 switch (constraint) 69 { 70 /* Any register operand. */ 71 case 'r': 72 if (regs) 73 insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register. */ 74 else 75 insn = (insn & 0x01f0) >> 4; /* Destination register. */ 76 77 sprintf (buf, "r%d", insn); 78 *style = dis_style_register; 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)); 86 *style = dis_style_register; 87 break; 88 89 case 'w': 90 sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3)); 91 *style = dis_style_register; 92 break; 93 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)); 99 *style = dis_style_register; 100 break; 101 102 case 'v': 103 if (regs) 104 sprintf (buf, "r%d", (insn & 0xf) * 2); 105 else 106 sprintf (buf, "r%d", ((insn & 0xf0) >> 3)); 107 *style = dis_style_register; 108 break; 109 110 case 'e': 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 } 127 strcpy (buf, xyz); 128 129 if (AVR_UNDEF_P (insn)) 130 sprintf (comment, _("undefined")); 131 } 132 *style = dis_style_register; 133 break; 134 135 case 'z': 136 *buf++ = 'Z'; 137 138 /* Check for post-increment. */ 139 char *s; 140 for (s = opcode_str; *s; ++s) 141 { 142 if (*s == '+') 143 { 144 if (insn & (1 << (15 - (s - opcode_str)))) 145 *buf++ = '+'; 146 break; 147 } 148 } 149 150 *buf = '\0'; 151 if (AVR_UNDEF_P (insn)) 152 sprintf (comment, _("undefined")); 153 *style = dis_style_register; 154 break; 155 156 case 'b': 157 { 158 unsigned int x; 159 160 x = (insn & 7); 161 x |= (insn >> 7) & (3 << 3); 162 x |= (insn >> 8) & (1 << 5); 163 164 if (insn & 0x8) 165 *buf++ = 'Y'; 166 else 167 *buf++ = 'Z'; 168 sprintf (buf, "+%d", x); 169 sprintf (comment, "0x%02x", x); 170 *style = dis_style_register; 171 } 172 break; 173 174 case 'h': 175 *sym = 1; 176 *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2; 177 /* See PR binutils/2454. Ideally we would like to display the hex 178 value of the address only once, but this would mean recoding 179 objdump_print_address() which would affect many targets. */ 180 sprintf (buf, "%#lx", (unsigned long) *sym_addr); 181 strcpy (comment, comment_start); 182 info->insn_info_valid = 1; 183 info->insn_type = dis_jsr; 184 info->target = *sym_addr; 185 *style = dis_style_address; 186 break; 187 188 case 'L': 189 { 190 int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2; 191 sprintf (buf, ".%+-8d", rel_addr); 192 *sym = 1; 193 *sym_addr = pc + 2 + rel_addr; 194 strcpy (comment, comment_start); 195 info->insn_info_valid = 1; 196 info->insn_type = dis_branch; 197 info->target = *sym_addr; 198 *style = dis_style_address_offset; 199 } 200 break; 201 202 case 'l': 203 { 204 int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2; 205 206 sprintf (buf, ".%+-8d", rel_addr); 207 *sym = 1; 208 *sym_addr = pc + 2 + rel_addr; 209 strcpy (comment, comment_start); 210 info->insn_info_valid = 1; 211 info->insn_type = dis_condbranch; 212 info->target = *sym_addr; 213 *style = dis_style_address_offset; 214 } 215 break; 216 217 case 'i': 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); 224 *style = dis_style_immediate; 225 } 226 break; 227 228 case 'j': 229 { 230 unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5) 231 | ((insn & 0x100) >> 2)); 232 if ((insn & 0x100) == 0) 233 val |= 0x80; 234 *sym = 1; 235 *sym_addr = val | 0x800000; 236 sprintf (buf, "0x%02x", val); 237 strcpy (comment, comment_start); 238 *style = dis_style_immediate; 239 } 240 break; 241 242 case 'M': 243 sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf)); 244 sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf)); 245 *style = dis_style_immediate; 246 break; 247 248 case 'n': 249 sprintf (buf, "??"); 250 /* xgettext:c-format */ 251 opcodes_error_handler (_("internal disassembler error")); 252 ok = 0; 253 *style = dis_style_immediate; 254 break; 255 256 case 'K': 257 { 258 unsigned int x; 259 260 x = (insn & 0xf) | ((insn >> 2) & 0x30); 261 sprintf (buf, "0x%02x", x); 262 sprintf (comment, "%d", x); 263 *style = dis_style_immediate; 264 } 265 break; 266 267 case 's': 268 sprintf (buf, "%d", insn & 7); 269 *style = dis_style_immediate; 270 break; 271 272 case 'S': 273 sprintf (buf, "%d", (insn >> 4) & 7); 274 *style = dis_style_immediate; 275 break; 276 277 case 'P': 278 { 279 unsigned int x; 280 281 x = (insn & 0xf); 282 x |= (insn >> 5) & 0x30; 283 sprintf (buf, "0x%02x", x); 284 sprintf (comment, "%d", x); 285 *style = dis_style_address; 286 } 287 break; 288 289 case 'p': 290 { 291 unsigned int x; 292 293 x = (insn >> 3) & 0x1f; 294 sprintf (buf, "0x%02x", x); 295 sprintf (comment, "%d", x); 296 *style = dis_style_address; 297 } 298 break; 299 300 case 'E': 301 sprintf (buf, "%d", (insn >> 4) & 15); 302 *style = dis_style_immediate; 303 break; 304 305 case '?': 306 *buf = '\0'; 307 break; 308 309 default: 310 sprintf (buf, "??"); 311 /* xgettext:c-format */ 312 opcodes_error_handler (_("unknown constraint `%c'"), constraint); 313 ok = 0; 314 } 315 316 return ok; 317} 318 319/* Read the opcode from ADDR. Return 0 in success and save opcode 320 in *INSN, otherwise, return -1. */ 321 322static int 323avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn) 324{ 325 bfd_byte buffer[2]; 326 int status; 327 328 status = info->read_memory_func (addr, buffer, 2, info); 329 330 if (status == 0) 331 { 332 *insn = bfd_getl16 (buffer); 333 return 0; 334 } 335 336 info->memory_error_func (status, addr, info); 337 return -1; 338} 339 340 341int 342print_insn_avr (bfd_vma addr, disassemble_info *info) 343{ 344 uint16_t insn, insn2; 345 const struct avr_opcodes_s *opcode; 346 static unsigned int *maskptr; 347 void *stream = info->stream; 348 fprintf_styled_ftype prin = info->fprintf_styled_func; 349 static unsigned int *avr_bin_masks; 350 static int initialized; 351 int cmd_len = 2; 352 int ok = 0; 353 char op1[20], op2[20], comment1[40], comment2[40]; 354 enum disassembler_style style_op1, style_op2; 355 int sym_op1 = 0, sym_op2 = 0; 356 bfd_vma sym_addr1, sym_addr2; 357 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; 365 366 if (!initialized) 367 { 368 unsigned int nopcodes; 369 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 376 nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s); 377 378 avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int)); 379 380 for (opcode = avr_opcodes, maskptr = avr_bin_masks; 381 opcode->name; 382 opcode++, maskptr++) 383 { 384 char * s; 385 unsigned int bin = 0; 386 unsigned int mask = 0; 387 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); 397 *maskptr = mask; 398 } 399 400 initialized = 1; 401 } 402 403 if (avrdis_opcode (addr, info, &insn) != 0) 404 return -1; 405 406 for (opcode = avr_opcodes, maskptr = avr_bin_masks; 407 opcode->name; 408 opcode++, maskptr++) 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 } 415 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; 426 style_op1 = dis_style_text; 427 style_op2 = dis_style_text; 428 429 if (opcode->name) 430 { 431 char *constraints = opcode->constraints; 432 char *opcode_str = opcode->opcode; 433 434 insn2 = 0; 435 ok = 1; 436 437 if (opcode->insn_size > 1) 438 { 439 if (avrdis_opcode (addr + 2, info, &insn2) != 0) 440 return -1; 441 cmd_len = 4; 442 } 443 444 if (*constraints && *constraints != '?') 445 { 446 int regs = REGISTER_P (*constraints); 447 448 ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1, 449 comment1, &style_op1, 0, &sym_op1, &sym_addr1, 450 info); 451 452 if (ok && *(++constraints) == ',') 453 ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str, 454 op2, *comment1 ? comment2 : comment1, 455 &style_op2, regs, &sym_op2, &sym_addr2, 456 info); 457 } 458 } 459 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 } 468 469 (*prin) (stream, ok ? dis_style_mnemonic : dis_style_assembler_directive, 470 "%s", ok ? opcode->name : ".word"); 471 472 if (*op1) 473 (*prin) (stream, style_op1, "\t%s", op1); 474 475 if (*op2) 476 { 477 (*prin) (stream, dis_style_text, ", "); 478 (*prin) (stream, style_op2, "%s", op2); 479 } 480 481 if (*comment1) 482 (*prin) (stream, dis_style_comment_start, "\t; %s", comment1); 483 484 if (sym_op1) 485 info->print_address_func (sym_addr1, info); 486 487 if (*comment2) 488 (*prin) (stream, dis_style_comment_start, " %s", comment2); 489 490 if (sym_op2) 491 info->print_address_func (sym_addr2, info); 492 493 return cmd_len; 494} 495