1f7cc78ecSespie /* Disassemble AVR instructions.
25f210c2aSfgsch Copyright 1999, 2000 Free Software Foundation, Inc.
3f7cc78ecSespie
4f7cc78ecSespie Contributed by Denis Chertykov <denisc@overta.ru>
5f7cc78ecSespie
6f7cc78ecSespie This program is free software; you can redistribute it and/or modify
7f7cc78ecSespie it under the terms of the GNU General Public License as published by
8f7cc78ecSespie the Free Software Foundation; either version 2 of the License, or
9f7cc78ecSespie (at your option) any later version.
10f7cc78ecSespie
11f7cc78ecSespie This program is distributed in the hope that it will be useful,
12f7cc78ecSespie but WITHOUT ANY WARRANTY; without even the implied warranty of
13f7cc78ecSespie MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14f7cc78ecSespie GNU General Public License for more details.
15f7cc78ecSespie
16f7cc78ecSespie You should have received a copy of the GNU General Public License
17f7cc78ecSespie along with this program; if not, write to the Free Software
18f7cc78ecSespie Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19f7cc78ecSespie
205f210c2aSfgsch #include <assert.h>
21f7cc78ecSespie #include "sysdep.h"
22f7cc78ecSespie #include "dis-asm.h"
23f7cc78ecSespie #include "opintl.h"
24*d2201f2fSdrahn #include "libiberty.h"
25f7cc78ecSespie
265f210c2aSfgsch struct avr_opcodes_s
275f210c2aSfgsch {
285f210c2aSfgsch char *name;
295f210c2aSfgsch char *constraints;
305f210c2aSfgsch char *opcode;
315f210c2aSfgsch int insn_size; /* in words */
325f210c2aSfgsch int isa;
335f210c2aSfgsch unsigned int bin_opcode;
34f7cc78ecSespie };
35f7cc78ecSespie
365f210c2aSfgsch #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
37*d2201f2fSdrahn {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
38f7cc78ecSespie
39*d2201f2fSdrahn const struct avr_opcodes_s avr_opcodes[] =
40f7cc78ecSespie {
415f210c2aSfgsch #include "opcode/avr.h"
42*d2201f2fSdrahn {NULL, NULL, NULL, 0, 0, 0}
435f210c2aSfgsch };
445f210c2aSfgsch
455f210c2aSfgsch static int avr_operand PARAMS ((unsigned int, unsigned int,
465f210c2aSfgsch unsigned int, int, char *, char *, int));
475f210c2aSfgsch
485f210c2aSfgsch static int
avr_operand(insn,insn2,pc,constraint,buf,comment,regs)495f210c2aSfgsch avr_operand (insn, insn2, pc, constraint, buf, comment, regs)
505f210c2aSfgsch unsigned int insn;
515f210c2aSfgsch unsigned int insn2;
525f210c2aSfgsch unsigned int pc;
535f210c2aSfgsch int constraint;
545f210c2aSfgsch char *buf;
555f210c2aSfgsch char *comment;
565f210c2aSfgsch int regs;
575f210c2aSfgsch {
585f210c2aSfgsch int ok = 1;
595f210c2aSfgsch
605f210c2aSfgsch switch (constraint)
615f210c2aSfgsch {
625f210c2aSfgsch /* Any register operand. */
635f210c2aSfgsch case 'r':
645f210c2aSfgsch if (regs)
655f210c2aSfgsch insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* source register */
665f210c2aSfgsch else
675f210c2aSfgsch insn = (insn & 0x01f0) >> 4; /* destination register */
685f210c2aSfgsch
695f210c2aSfgsch sprintf (buf, "r%d", insn);
705f210c2aSfgsch break;
715f210c2aSfgsch
725f210c2aSfgsch case 'd':
735f210c2aSfgsch if (regs)
745f210c2aSfgsch sprintf (buf, "r%d", 16 + (insn & 0xf));
755f210c2aSfgsch else
765f210c2aSfgsch sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4));
775f210c2aSfgsch break;
785f210c2aSfgsch
795f210c2aSfgsch case 'w':
805f210c2aSfgsch sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
815f210c2aSfgsch break;
825f210c2aSfgsch
835f210c2aSfgsch case 'a':
845f210c2aSfgsch if (regs)
855f210c2aSfgsch sprintf (buf, "r%d", 16 + (insn & 7));
865f210c2aSfgsch else
875f210c2aSfgsch sprintf (buf, "r%d", 16 + ((insn >> 4) & 7));
885f210c2aSfgsch break;
895f210c2aSfgsch
905f210c2aSfgsch case 'v':
915f210c2aSfgsch if (regs)
925f210c2aSfgsch sprintf (buf, "r%d", (insn & 0xf) * 2);
935f210c2aSfgsch else
945f210c2aSfgsch sprintf (buf, "r%d", ((insn & 0xf0) >> 3));
955f210c2aSfgsch break;
965f210c2aSfgsch
975f210c2aSfgsch case 'e':
985f210c2aSfgsch {
995f210c2aSfgsch char *xyz;
1005f210c2aSfgsch
1015f210c2aSfgsch switch (insn & 0x100f)
1025f210c2aSfgsch {
1035f210c2aSfgsch case 0x0000: xyz = "Z"; break;
1045f210c2aSfgsch case 0x1001: xyz = "Z+"; break;
1055f210c2aSfgsch case 0x1002: xyz = "-Z"; break;
1065f210c2aSfgsch case 0x0008: xyz = "Y"; break;
1075f210c2aSfgsch case 0x1009: xyz = "Y+"; break;
1085f210c2aSfgsch case 0x100a: xyz = "-Y"; break;
1095f210c2aSfgsch case 0x100c: xyz = "X"; break;
1105f210c2aSfgsch case 0x100d: xyz = "X+"; break;
1115f210c2aSfgsch case 0x100e: xyz = "-X"; break;
1125f210c2aSfgsch default: xyz = "??"; ok = 0;
1135f210c2aSfgsch }
1145f210c2aSfgsch sprintf (buf, xyz);
1155f210c2aSfgsch
1165f210c2aSfgsch if (AVR_UNDEF_P (insn))
1175f210c2aSfgsch sprintf (comment, _("undefined"));
1185f210c2aSfgsch }
1195f210c2aSfgsch break;
1205f210c2aSfgsch
1215f210c2aSfgsch case 'z':
1225f210c2aSfgsch *buf++ = 'Z';
1235f210c2aSfgsch if (insn & 0x1)
1245f210c2aSfgsch *buf++ = '+';
1255f210c2aSfgsch *buf = '\0';
1265f210c2aSfgsch if (AVR_UNDEF_P (insn))
1275f210c2aSfgsch sprintf (comment, _("undefined"));
1285f210c2aSfgsch break;
1295f210c2aSfgsch
1305f210c2aSfgsch case 'b':
1315f210c2aSfgsch {
1325f210c2aSfgsch unsigned int x;
1335f210c2aSfgsch
1345f210c2aSfgsch x = (insn & 7);
1355f210c2aSfgsch x |= (insn >> 7) & (3 << 3);
1365f210c2aSfgsch x |= (insn >> 8) & (1 << 5);
1375f210c2aSfgsch
1385f210c2aSfgsch if (insn & 0x8)
1395f210c2aSfgsch *buf++ = 'Y';
1405f210c2aSfgsch else
1415f210c2aSfgsch *buf++ = 'Z';
1425f210c2aSfgsch sprintf (buf, "+%d", x);
1435f210c2aSfgsch sprintf (comment, "0x%02x", x);
1445f210c2aSfgsch }
1455f210c2aSfgsch break;
1465f210c2aSfgsch
1475f210c2aSfgsch case 'h':
1485f210c2aSfgsch sprintf (buf, "0x%x",
1495f210c2aSfgsch ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2);
1505f210c2aSfgsch break;
1515f210c2aSfgsch
1525f210c2aSfgsch case 'L':
1535f210c2aSfgsch {
1545f210c2aSfgsch int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2;
1555f210c2aSfgsch sprintf (buf, ".%+-8d", rel_addr);
1565f210c2aSfgsch sprintf (comment, "0x%x", pc + 2 + rel_addr);
1575f210c2aSfgsch }
1585f210c2aSfgsch break;
1595f210c2aSfgsch
1605f210c2aSfgsch case 'l':
1615f210c2aSfgsch {
1625f210c2aSfgsch int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
1635f210c2aSfgsch sprintf (buf, ".%+-8d", rel_addr);
1645f210c2aSfgsch sprintf (comment, "0x%x", pc + 2 + rel_addr);
1655f210c2aSfgsch }
1665f210c2aSfgsch break;
1675f210c2aSfgsch
1685f210c2aSfgsch case 'i':
1695f210c2aSfgsch sprintf (buf, "0x%04X", insn2);
1705f210c2aSfgsch break;
1715f210c2aSfgsch
1725f210c2aSfgsch case 'M':
1735f210c2aSfgsch sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf));
1745f210c2aSfgsch sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf));
1755f210c2aSfgsch break;
1765f210c2aSfgsch
1775f210c2aSfgsch case 'n':
1785f210c2aSfgsch sprintf (buf, "??");
1795f210c2aSfgsch fprintf (stderr, _("Internal disassembler error"));
1805f210c2aSfgsch ok = 0;
1815f210c2aSfgsch break;
1825f210c2aSfgsch
1835f210c2aSfgsch case 'K':
1845f210c2aSfgsch {
1855f210c2aSfgsch unsigned int x;
1865f210c2aSfgsch
1875f210c2aSfgsch x = (insn & 0xf) | ((insn >> 2) & 0x30);
1885f210c2aSfgsch sprintf (buf, "0x%02x", x);
1895f210c2aSfgsch sprintf (comment, "%d", x);
1905f210c2aSfgsch }
1915f210c2aSfgsch break;
1925f210c2aSfgsch
1935f210c2aSfgsch case 's':
1945f210c2aSfgsch sprintf (buf, "%d", insn & 7);
1955f210c2aSfgsch break;
1965f210c2aSfgsch
1975f210c2aSfgsch case 'S':
1985f210c2aSfgsch sprintf (buf, "%d", (insn >> 4) & 7);
1995f210c2aSfgsch break;
2005f210c2aSfgsch
2015f210c2aSfgsch case 'P':
2025f210c2aSfgsch {
2035f210c2aSfgsch unsigned int x;
2045f210c2aSfgsch x = (insn & 0xf);
2055f210c2aSfgsch x |= (insn >> 5) & 0x30;
2065f210c2aSfgsch sprintf (buf, "0x%02x", x);
2075f210c2aSfgsch sprintf (comment, "%d", x);
2085f210c2aSfgsch }
2095f210c2aSfgsch break;
2105f210c2aSfgsch
2115f210c2aSfgsch case 'p':
2125f210c2aSfgsch {
2135f210c2aSfgsch unsigned int x;
2145f210c2aSfgsch
2155f210c2aSfgsch x = (insn >> 3) & 0x1f;
2165f210c2aSfgsch sprintf (buf, "0x%02x", x);
2175f210c2aSfgsch sprintf (comment, "%d", x);
2185f210c2aSfgsch }
2195f210c2aSfgsch break;
2205f210c2aSfgsch
2215f210c2aSfgsch case '?':
2225f210c2aSfgsch *buf = '\0';
2235f210c2aSfgsch break;
2245f210c2aSfgsch
2255f210c2aSfgsch default:
2265f210c2aSfgsch sprintf (buf, "??");
2275f210c2aSfgsch fprintf (stderr, _("unknown constraint `%c'"), constraint);
2285f210c2aSfgsch ok = 0;
229f7cc78ecSespie }
230f7cc78ecSespie
2315f210c2aSfgsch return ok;
232f7cc78ecSespie }
233f7cc78ecSespie
2345f210c2aSfgsch static unsigned short avrdis_opcode PARAMS ((bfd_vma, disassemble_info *));
235f7cc78ecSespie
2365f210c2aSfgsch static unsigned short
avrdis_opcode(addr,info)237f7cc78ecSespie avrdis_opcode (addr, info)
238f7cc78ecSespie bfd_vma addr;
239f7cc78ecSespie disassemble_info *info;
240f7cc78ecSespie {
241f7cc78ecSespie bfd_byte buffer[2];
242f7cc78ecSespie int status;
243f7cc78ecSespie status = info->read_memory_func(addr, buffer, 2, info);
244f7cc78ecSespie if (status != 0)
245f7cc78ecSespie {
246f7cc78ecSespie info->memory_error_func(status, addr, info);
247f7cc78ecSespie return -1;
248f7cc78ecSespie }
249f7cc78ecSespie return bfd_getl16 (buffer);
250f7cc78ecSespie }
251f7cc78ecSespie
252f7cc78ecSespie
253f7cc78ecSespie int
print_insn_avr(addr,info)254f7cc78ecSespie print_insn_avr(addr, info)
255f7cc78ecSespie bfd_vma addr;
256f7cc78ecSespie disassemble_info *info;
257f7cc78ecSespie {
2585f210c2aSfgsch unsigned int insn, insn2;
259*d2201f2fSdrahn const struct avr_opcodes_s *opcode;
260*d2201f2fSdrahn static unsigned int *maskptr;
261f7cc78ecSespie void *stream = info->stream;
262f7cc78ecSespie fprintf_ftype prin = info->fprintf_func;
263*d2201f2fSdrahn static unsigned int *avr_bin_masks;
2645f210c2aSfgsch static int initialized;
265f7cc78ecSespie int cmd_len = 2;
2665f210c2aSfgsch int ok = 0;
2675f210c2aSfgsch char op1[20], op2[20], comment1[40], comment2[40];
268f7cc78ecSespie
2695f210c2aSfgsch if (!initialized)
2705f210c2aSfgsch {
271*d2201f2fSdrahn unsigned int nopcodes;
272f7cc78ecSespie
273*d2201f2fSdrahn nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
274*d2201f2fSdrahn
275*d2201f2fSdrahn avr_bin_masks = (unsigned int *)
276*d2201f2fSdrahn xmalloc (nopcodes * sizeof (unsigned int));
277*d2201f2fSdrahn
278*d2201f2fSdrahn for (opcode = avr_opcodes, maskptr = avr_bin_masks;
279*d2201f2fSdrahn opcode->name;
280*d2201f2fSdrahn opcode++, maskptr++)
281f7cc78ecSespie {
2825f210c2aSfgsch char * s;
2835f210c2aSfgsch unsigned int bin = 0;
2845f210c2aSfgsch unsigned int mask = 0;
2855f210c2aSfgsch
2865f210c2aSfgsch for (s = opcode->opcode; *s; ++s)
2875f210c2aSfgsch {
2885f210c2aSfgsch bin <<= 1;
2895f210c2aSfgsch mask <<= 1;
2905f210c2aSfgsch bin |= (*s == '1');
2915f210c2aSfgsch mask |= (*s == '1' || *s == '0');
292f7cc78ecSespie }
2935f210c2aSfgsch assert (s - opcode->opcode == 16);
2945f210c2aSfgsch assert (opcode->bin_opcode == bin);
295*d2201f2fSdrahn *maskptr = mask;
296f7cc78ecSespie }
297*d2201f2fSdrahn
298*d2201f2fSdrahn initialized = 1;
299f7cc78ecSespie }
3005f210c2aSfgsch
3015f210c2aSfgsch insn = avrdis_opcode (addr, info);
3025f210c2aSfgsch
303*d2201f2fSdrahn for (opcode = avr_opcodes, maskptr = avr_bin_masks;
304*d2201f2fSdrahn opcode->name;
305*d2201f2fSdrahn opcode++, maskptr++)
306f7cc78ecSespie {
307*d2201f2fSdrahn if ((insn & *maskptr) == opcode->bin_opcode)
308f7cc78ecSespie break;
309f7cc78ecSespie }
3105f210c2aSfgsch
3115f210c2aSfgsch /* Special case: disassemble `ldd r,b+0' as `ld r,b', and
3125f210c2aSfgsch `std b+0,r' as `st b,r' (next entry in the table). */
3135f210c2aSfgsch
3145f210c2aSfgsch if (AVR_DISP0_P (insn))
3155f210c2aSfgsch opcode++;
3165f210c2aSfgsch
3175f210c2aSfgsch op1[0] = 0;
3185f210c2aSfgsch op2[0] = 0;
3195f210c2aSfgsch comment1[0] = 0;
3205f210c2aSfgsch comment2[0] = 0;
3215f210c2aSfgsch
3225f210c2aSfgsch if (opcode->name)
323f7cc78ecSespie {
3245f210c2aSfgsch char *op = opcode->constraints;
3255f210c2aSfgsch
3265f210c2aSfgsch insn2 = 0;
3275f210c2aSfgsch ok = 1;
3285f210c2aSfgsch
3295f210c2aSfgsch if (opcode->insn_size > 1)
330f7cc78ecSespie {
3315f210c2aSfgsch insn2 = avrdis_opcode (addr + 2, info);
332f7cc78ecSespie cmd_len = 4;
333f7cc78ecSespie }
3345f210c2aSfgsch
3355f210c2aSfgsch if (*op && *op != '?')
336f7cc78ecSespie {
3375f210c2aSfgsch int regs = REGISTER_P (*op);
3385f210c2aSfgsch
3395f210c2aSfgsch ok = avr_operand (insn, insn2, addr, *op, op1, comment1, 0);
3405f210c2aSfgsch
3415f210c2aSfgsch if (ok && *(++op) == ',')
3425f210c2aSfgsch ok = avr_operand (insn, insn2, addr, *(++op), op2,
3435f210c2aSfgsch *comment1 ? comment2 : comment1, regs);
3445f210c2aSfgsch }
3455f210c2aSfgsch }
3465f210c2aSfgsch
3475f210c2aSfgsch if (!ok)
348f7cc78ecSespie {
3495f210c2aSfgsch /* Unknown opcode, or invalid combination of operands. */
3505f210c2aSfgsch sprintf (op1, "0x%04x", insn);
3515f210c2aSfgsch op2[0] = 0;
3525f210c2aSfgsch sprintf (comment1, "????");
3535f210c2aSfgsch comment2[0] = 0;
354f7cc78ecSespie }
3555f210c2aSfgsch
3565f210c2aSfgsch (*prin) (stream, "%s", ok ? opcode->name : ".word");
3575f210c2aSfgsch
3585f210c2aSfgsch if (*op1)
3595f210c2aSfgsch (*prin) (stream, "\t%s", op1);
3605f210c2aSfgsch
3615f210c2aSfgsch if (*op2)
3625f210c2aSfgsch (*prin) (stream, ", %s", op2);
3635f210c2aSfgsch
3645f210c2aSfgsch if (*comment1)
3655f210c2aSfgsch (*prin) (stream, "\t; %s", comment1);
3665f210c2aSfgsch
3675f210c2aSfgsch if (*comment2)
3685f210c2aSfgsch (*prin) (stream, " %s", comment2);
3695f210c2aSfgsch
370f7cc78ecSespie return cmd_len;
371f7cc78ecSespie }
372