avr-dis.c revision 1.1.1.8
1/* Disassemble AVR instructions. 2 Copyright (C) 1999-2020 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 "bfd_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, unsigned int insn2, unsigned int pc, int constraint, 53 char *opcode_str, char *buf, char *comment, int regs, int *sym, bfd_vma *sym_addr) 54{ 55 int ok = 1; 56 *sym = 0; 57 58 switch (constraint) 59 { 60 /* Any register operand. */ 61 case 'r': 62 if (regs) 63 insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register. */ 64 else 65 insn = (insn & 0x01f0) >> 4; /* Destination register. */ 66 67 sprintf (buf, "r%d", insn); 68 break; 69 70 case 'd': 71 if (regs) 72 sprintf (buf, "r%d", 16 + (insn & 0xf)); 73 else 74 sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4)); 75 break; 76 77 case 'w': 78 sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3)); 79 break; 80 81 case 'a': 82 if (regs) 83 sprintf (buf, "r%d", 16 + (insn & 7)); 84 else 85 sprintf (buf, "r%d", 16 + ((insn >> 4) & 7)); 86 break; 87 88 case 'v': 89 if (regs) 90 sprintf (buf, "r%d", (insn & 0xf) * 2); 91 else 92 sprintf (buf, "r%d", ((insn & 0xf0) >> 3)); 93 break; 94 95 case 'e': 96 { 97 char *xyz; 98 99 switch (insn & 0x100f) 100 { 101 case 0x0000: xyz = "Z"; break; 102 case 0x1001: xyz = "Z+"; break; 103 case 0x1002: xyz = "-Z"; break; 104 case 0x0008: xyz = "Y"; break; 105 case 0x1009: xyz = "Y+"; break; 106 case 0x100a: xyz = "-Y"; break; 107 case 0x100c: xyz = "X"; break; 108 case 0x100d: xyz = "X+"; break; 109 case 0x100e: xyz = "-X"; break; 110 default: xyz = "??"; ok = 0; 111 } 112 strcpy (buf, xyz); 113 114 if (AVR_UNDEF_P (insn)) 115 sprintf (comment, _("undefined")); 116 } 117 break; 118 119 case 'z': 120 *buf++ = 'Z'; 121 122 /* Check for post-increment. */ 123 char *s; 124 for (s = opcode_str; *s; ++s) 125 { 126 if (*s == '+') 127 { 128 if (insn & (1 << (15 - (s - opcode_str)))) 129 *buf++ = '+'; 130 break; 131 } 132 } 133 134 *buf = '\0'; 135 if (AVR_UNDEF_P (insn)) 136 sprintf (comment, _("undefined")); 137 break; 138 139 case 'b': 140 { 141 unsigned int x; 142 143 x = (insn & 7); 144 x |= (insn >> 7) & (3 << 3); 145 x |= (insn >> 8) & (1 << 5); 146 147 if (insn & 0x8) 148 *buf++ = 'Y'; 149 else 150 *buf++ = 'Z'; 151 sprintf (buf, "+%d", x); 152 sprintf (comment, "0x%02x", x); 153 } 154 break; 155 156 case 'h': 157 *sym = 1; 158 *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2; 159 /* See PR binutils/2454. Ideally we would like to display the hex 160 value of the address only once, but this would mean recoding 161 objdump_print_address() which would affect many targets. */ 162 sprintf (buf, "%#lx", (unsigned long) *sym_addr); 163 strcpy (comment, comment_start); 164 break; 165 166 case 'L': 167 { 168 int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2; 169 sprintf (buf, ".%+-8d", rel_addr); 170 *sym = 1; 171 *sym_addr = pc + 2 + rel_addr; 172 strcpy (comment, comment_start); 173 } 174 break; 175 176 case 'l': 177 { 178 int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2; 179 180 sprintf (buf, ".%+-8d", rel_addr); 181 *sym = 1; 182 *sym_addr = pc + 2 + rel_addr; 183 strcpy (comment, comment_start); 184 } 185 break; 186 187 case 'i': 188 { 189 unsigned int val = insn2 | 0x800000; 190 *sym = 1; 191 *sym_addr = val; 192 sprintf (buf, "0x%04X", insn2); 193 strcpy (comment, comment_start); 194 } 195 break; 196 197 case 'j': 198 { 199 unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5) 200 | ((insn & 0x100) >> 2)); 201 if ((insn & 0x100) == 0) 202 val |= 0x80; 203 *sym = 1; 204 *sym_addr = val | 0x800000; 205 sprintf (buf, "0x%02x", val); 206 strcpy (comment, comment_start); 207 } 208 break; 209 210 case 'M': 211 sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf)); 212 sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf)); 213 break; 214 215 case 'n': 216 sprintf (buf, "??"); 217 /* xgettext:c-format */ 218 opcodes_error_handler (_("internal disassembler error")); 219 ok = 0; 220 break; 221 222 case 'K': 223 { 224 unsigned int x; 225 226 x = (insn & 0xf) | ((insn >> 2) & 0x30); 227 sprintf (buf, "0x%02x", x); 228 sprintf (comment, "%d", x); 229 } 230 break; 231 232 case 's': 233 sprintf (buf, "%d", insn & 7); 234 break; 235 236 case 'S': 237 sprintf (buf, "%d", (insn >> 4) & 7); 238 break; 239 240 case 'P': 241 { 242 unsigned int x; 243 244 x = (insn & 0xf); 245 x |= (insn >> 5) & 0x30; 246 sprintf (buf, "0x%02x", x); 247 sprintf (comment, "%d", x); 248 } 249 break; 250 251 case 'p': 252 { 253 unsigned int x; 254 255 x = (insn >> 3) & 0x1f; 256 sprintf (buf, "0x%02x", x); 257 sprintf (comment, "%d", x); 258 } 259 break; 260 261 case 'E': 262 sprintf (buf, "%d", (insn >> 4) & 15); 263 break; 264 265 case '?': 266 *buf = '\0'; 267 break; 268 269 default: 270 sprintf (buf, "??"); 271 /* xgettext:c-format */ 272 opcodes_error_handler (_("unknown constraint `%c'"), constraint); 273 ok = 0; 274 } 275 276 return ok; 277} 278 279/* Read the opcode from ADDR. Return 0 in success and save opcode 280 in *INSN, otherwise, return -1. */ 281 282static int 283avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn) 284{ 285 bfd_byte buffer[2]; 286 int status; 287 288 status = info->read_memory_func (addr, buffer, 2, info); 289 290 if (status == 0) 291 { 292 *insn = bfd_getl16 (buffer); 293 return 0; 294 } 295 296 info->memory_error_func (status, addr, info); 297 return -1; 298} 299 300 301int 302print_insn_avr (bfd_vma addr, disassemble_info *info) 303{ 304 uint16_t insn, insn2; 305 const struct avr_opcodes_s *opcode; 306 static unsigned int *maskptr; 307 void *stream = info->stream; 308 fprintf_ftype prin = info->fprintf_func; 309 static unsigned int *avr_bin_masks; 310 static int initialized; 311 int cmd_len = 2; 312 int ok = 0; 313 char op1[20], op2[20], comment1[40], comment2[40]; 314 int sym_op1 = 0, sym_op2 = 0; 315 bfd_vma sym_addr1, sym_addr2; 316 317 318 if (!initialized) 319 { 320 unsigned int nopcodes; 321 322 /* PR 4045: Try to avoid duplicating the 0x prefix that 323 objdump_print_addr() will put on addresses when there 324 is no symbol table available. */ 325 if (info->symtab_size == 0) 326 comment_start = " "; 327 328 nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s); 329 330 avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int)); 331 332 for (opcode = avr_opcodes, maskptr = avr_bin_masks; 333 opcode->name; 334 opcode++, maskptr++) 335 { 336 char * s; 337 unsigned int bin = 0; 338 unsigned int mask = 0; 339 340 for (s = opcode->opcode; *s; ++s) 341 { 342 bin <<= 1; 343 mask <<= 1; 344 bin |= (*s == '1'); 345 mask |= (*s == '1' || *s == '0'); 346 } 347 assert (s - opcode->opcode == 16); 348 assert (opcode->bin_opcode == bin); 349 *maskptr = mask; 350 } 351 352 initialized = 1; 353 } 354 355 if (avrdis_opcode (addr, info, &insn) != 0) 356 return -1; 357 358 for (opcode = avr_opcodes, maskptr = avr_bin_masks; 359 opcode->name; 360 opcode++, maskptr++) 361 { 362 if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny)) 363 continue; 364 if ((insn & *maskptr) == opcode->bin_opcode) 365 break; 366 } 367 368 /* Special case: disassemble `ldd r,b+0' as `ld r,b', and 369 `std b+0,r' as `st b,r' (next entry in the table). */ 370 371 if (AVR_DISP0_P (insn)) 372 opcode++; 373 374 op1[0] = 0; 375 op2[0] = 0; 376 comment1[0] = 0; 377 comment2[0] = 0; 378 379 if (opcode->name) 380 { 381 char *constraints = opcode->constraints; 382 char *opcode_str = opcode->opcode; 383 384 insn2 = 0; 385 ok = 1; 386 387 if (opcode->insn_size > 1) 388 { 389 if (avrdis_opcode (addr + 2, info, &insn2) != 0) 390 return -1; 391 cmd_len = 4; 392 } 393 394 if (*constraints && *constraints != '?') 395 { 396 int regs = REGISTER_P (*constraints); 397 398 ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1, comment1, 0, &sym_op1, &sym_addr1); 399 400 if (ok && *(++constraints) == ',') 401 ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str, op2, 402 *comment1 ? comment2 : comment1, regs, &sym_op2, &sym_addr2); 403 } 404 } 405 406 if (!ok) 407 { 408 /* Unknown opcode, or invalid combination of operands. */ 409 sprintf (op1, "0x%04x", insn); 410 op2[0] = 0; 411 sprintf (comment1, "????"); 412 comment2[0] = 0; 413 } 414 415 (*prin) (stream, "%s", ok ? opcode->name : ".word"); 416 417 if (*op1) 418 (*prin) (stream, "\t%s", op1); 419 420 if (*op2) 421 (*prin) (stream, ", %s", op2); 422 423 if (*comment1) 424 (*prin) (stream, "\t; %s", comment1); 425 426 if (sym_op1) 427 info->print_address_func (sym_addr1, info); 428 429 if (*comment2) 430 (*prin) (stream, " %s", comment2); 431 432 if (sym_op2) 433 info->print_address_func (sym_addr2, info); 434 435 return cmd_len; 436} 437