123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- /* Disassemble AVR instructions.
- Copyright (C) 1999-2022 Free Software Foundation, Inc.
- Contributed by Denis Chertykov <denisc@overta.ru>
- This file is part of libopcodes.
- This library is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- It is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #include "sysdep.h"
- #include <assert.h>
- #include "disassemble.h"
- #include "opintl.h"
- #include "libiberty.h"
- #include <stdint.h>
- struct avr_opcodes_s
- {
- char *name;
- char *constraints;
- char *opcode;
- int insn_size; /* In words. */
- int isa;
- unsigned int bin_opcode;
- };
- #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
- {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
- const struct avr_opcodes_s avr_opcodes[] =
- {
- #include "opcode/avr.h"
- {NULL, NULL, NULL, 0, 0, 0}
- };
- static const char * comment_start = "0x";
- static int
- avr_operand (unsigned int insn,
- unsigned int insn2,
- unsigned int pc,
- int constraint,
- char * opcode_str,
- char * buf,
- char * comment,
- int regs,
- int * sym,
- bfd_vma * sym_addr,
- disassemble_info * info)
- {
- int ok = 1;
- *sym = 0;
- switch (constraint)
- {
- /* Any register operand. */
- case 'r':
- if (regs)
- insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register. */
- else
- insn = (insn & 0x01f0) >> 4; /* Destination register. */
- sprintf (buf, "r%d", insn);
- break;
- case 'd':
- if (regs)
- sprintf (buf, "r%d", 16 + (insn & 0xf));
- else
- sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4));
- break;
- case 'w':
- sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
- break;
- case 'a':
- if (regs)
- sprintf (buf, "r%d", 16 + (insn & 7));
- else
- sprintf (buf, "r%d", 16 + ((insn >> 4) & 7));
- break;
- case 'v':
- if (regs)
- sprintf (buf, "r%d", (insn & 0xf) * 2);
- else
- sprintf (buf, "r%d", ((insn & 0xf0) >> 3));
- break;
- case 'e':
- {
- char *xyz;
- switch (insn & 0x100f)
- {
- case 0x0000: xyz = "Z"; break;
- case 0x1001: xyz = "Z+"; break;
- case 0x1002: xyz = "-Z"; break;
- case 0x0008: xyz = "Y"; break;
- case 0x1009: xyz = "Y+"; break;
- case 0x100a: xyz = "-Y"; break;
- case 0x100c: xyz = "X"; break;
- case 0x100d: xyz = "X+"; break;
- case 0x100e: xyz = "-X"; break;
- default: xyz = "??"; ok = 0;
- }
- strcpy (buf, xyz);
- if (AVR_UNDEF_P (insn))
- sprintf (comment, _("undefined"));
- }
- break;
- case 'z':
- *buf++ = 'Z';
- /* Check for post-increment. */
- char *s;
- for (s = opcode_str; *s; ++s)
- {
- if (*s == '+')
- {
- if (insn & (1 << (15 - (s - opcode_str))))
- *buf++ = '+';
- break;
- }
- }
- *buf = '\0';
- if (AVR_UNDEF_P (insn))
- sprintf (comment, _("undefined"));
- break;
- case 'b':
- {
- unsigned int x;
- x = (insn & 7);
- x |= (insn >> 7) & (3 << 3);
- x |= (insn >> 8) & (1 << 5);
- if (insn & 0x8)
- *buf++ = 'Y';
- else
- *buf++ = 'Z';
- sprintf (buf, "+%d", x);
- sprintf (comment, "0x%02x", x);
- }
- break;
- case 'h':
- *sym = 1;
- *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2;
- /* See PR binutils/2454. Ideally we would like to display the hex
- value of the address only once, but this would mean recoding
- objdump_print_address() which would affect many targets. */
- sprintf (buf, "%#lx", (unsigned long) *sym_addr);
- strcpy (comment, comment_start);
- info->insn_info_valid = 1;
- info->insn_type = dis_jsr;
- info->target = *sym_addr;
- break;
- case 'L':
- {
- int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2;
- sprintf (buf, ".%+-8d", rel_addr);
- *sym = 1;
- *sym_addr = pc + 2 + rel_addr;
- strcpy (comment, comment_start);
- info->insn_info_valid = 1;
- info->insn_type = dis_branch;
- info->target = *sym_addr;
- }
- break;
- case 'l':
- {
- int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
- sprintf (buf, ".%+-8d", rel_addr);
- *sym = 1;
- *sym_addr = pc + 2 + rel_addr;
- strcpy (comment, comment_start);
- info->insn_info_valid = 1;
- info->insn_type = dis_condbranch;
- info->target = *sym_addr;
- }
- break;
- case 'i':
- {
- unsigned int val = insn2 | 0x800000;
- *sym = 1;
- *sym_addr = val;
- sprintf (buf, "0x%04X", insn2);
- strcpy (comment, comment_start);
- }
- break;
- case 'j':
- {
- unsigned int val = ((insn & 0xf) | ((insn & 0x600) >> 5)
- | ((insn & 0x100) >> 2));
- if ((insn & 0x100) == 0)
- val |= 0x80;
- *sym = 1;
- *sym_addr = val | 0x800000;
- sprintf (buf, "0x%02x", val);
- strcpy (comment, comment_start);
- }
- break;
- case 'M':
- sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf));
- sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf));
- break;
- case 'n':
- sprintf (buf, "??");
- /* xgettext:c-format */
- opcodes_error_handler (_("internal disassembler error"));
- ok = 0;
- break;
- case 'K':
- {
- unsigned int x;
- x = (insn & 0xf) | ((insn >> 2) & 0x30);
- sprintf (buf, "0x%02x", x);
- sprintf (comment, "%d", x);
- }
- break;
- case 's':
- sprintf (buf, "%d", insn & 7);
- break;
- case 'S':
- sprintf (buf, "%d", (insn >> 4) & 7);
- break;
- case 'P':
- {
- unsigned int x;
- x = (insn & 0xf);
- x |= (insn >> 5) & 0x30;
- sprintf (buf, "0x%02x", x);
- sprintf (comment, "%d", x);
- }
- break;
- case 'p':
- {
- unsigned int x;
- x = (insn >> 3) & 0x1f;
- sprintf (buf, "0x%02x", x);
- sprintf (comment, "%d", x);
- }
- break;
- case 'E':
- sprintf (buf, "%d", (insn >> 4) & 15);
- break;
- case '?':
- *buf = '\0';
- break;
- default:
- sprintf (buf, "??");
- /* xgettext:c-format */
- opcodes_error_handler (_("unknown constraint `%c'"), constraint);
- ok = 0;
- }
- return ok;
- }
- /* Read the opcode from ADDR. Return 0 in success and save opcode
- in *INSN, otherwise, return -1. */
- static int
- avrdis_opcode (bfd_vma addr, disassemble_info *info, uint16_t *insn)
- {
- bfd_byte buffer[2];
- int status;
- status = info->read_memory_func (addr, buffer, 2, info);
- if (status == 0)
- {
- *insn = bfd_getl16 (buffer);
- return 0;
- }
- info->memory_error_func (status, addr, info);
- return -1;
- }
- int
- print_insn_avr (bfd_vma addr, disassemble_info *info)
- {
- uint16_t insn, insn2;
- const struct avr_opcodes_s *opcode;
- static unsigned int *maskptr;
- void *stream = info->stream;
- fprintf_ftype prin = info->fprintf_func;
- static unsigned int *avr_bin_masks;
- static int initialized;
- int cmd_len = 2;
- int ok = 0;
- char op1[20], op2[20], comment1[40], comment2[40];
- int sym_op1 = 0, sym_op2 = 0;
- bfd_vma sym_addr1, sym_addr2;
- /* Clear instruction information field. */
- info->insn_info_valid = 0;
- info->branch_delay_insns = 0;
- info->data_size = 0;
- info->insn_type = dis_noninsn;
- info->target = 0;
- info->target2 = 0;
- if (!initialized)
- {
- unsigned int nopcodes;
- /* PR 4045: Try to avoid duplicating the 0x prefix that
- objdump_print_addr() will put on addresses when there
- is no symbol table available. */
- if (info->symtab_size == 0)
- comment_start = " ";
- nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
- avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int));
- for (opcode = avr_opcodes, maskptr = avr_bin_masks;
- opcode->name;
- opcode++, maskptr++)
- {
- char * s;
- unsigned int bin = 0;
- unsigned int mask = 0;
- for (s = opcode->opcode; *s; ++s)
- {
- bin <<= 1;
- mask <<= 1;
- bin |= (*s == '1');
- mask |= (*s == '1' || *s == '0');
- }
- assert (s - opcode->opcode == 16);
- assert (opcode->bin_opcode == bin);
- *maskptr = mask;
- }
- initialized = 1;
- }
- if (avrdis_opcode (addr, info, &insn) != 0)
- return -1;
- for (opcode = avr_opcodes, maskptr = avr_bin_masks;
- opcode->name;
- opcode++, maskptr++)
- {
- if ((opcode->isa == AVR_ISA_TINY) && (info->mach != bfd_mach_avrtiny))
- continue;
- if ((insn & *maskptr) == opcode->bin_opcode)
- break;
- }
- /* Special case: disassemble `ldd r,b+0' as `ld r,b', and
- `std b+0,r' as `st b,r' (next entry in the table). */
- if (AVR_DISP0_P (insn))
- opcode++;
- op1[0] = 0;
- op2[0] = 0;
- comment1[0] = 0;
- comment2[0] = 0;
- if (opcode->name)
- {
- char *constraints = opcode->constraints;
- char *opcode_str = opcode->opcode;
- insn2 = 0;
- ok = 1;
- if (opcode->insn_size > 1)
- {
- if (avrdis_opcode (addr + 2, info, &insn2) != 0)
- return -1;
- cmd_len = 4;
- }
- if (*constraints && *constraints != '?')
- {
- int regs = REGISTER_P (*constraints);
- ok = avr_operand (insn, insn2, addr, *constraints, opcode_str, op1,
- comment1, 0, &sym_op1, &sym_addr1, info);
- if (ok && *(++constraints) == ',')
- ok = avr_operand (insn, insn2, addr, *(++constraints), opcode_str,
- op2, *comment1 ? comment2 : comment1, regs,
- &sym_op2, &sym_addr2, info);
- }
- }
- if (!ok)
- {
- /* Unknown opcode, or invalid combination of operands. */
- sprintf (op1, "0x%04x", insn);
- op2[0] = 0;
- sprintf (comment1, "????");
- comment2[0] = 0;
- }
- (*prin) (stream, "%s", ok ? opcode->name : ".word");
- if (*op1)
- (*prin) (stream, "\t%s", op1);
- if (*op2)
- (*prin) (stream, ", %s", op2);
- if (*comment1)
- (*prin) (stream, "\t; %s", comment1);
- if (sym_op1)
- info->print_address_func (sym_addr1, info);
- if (*comment2)
- (*prin) (stream, " %s", comment2);
- if (sym_op2)
- info->print_address_func (sym_addr2, info);
- return cmd_len;
- }
|