xref: /openbsd-src/gnu/usr.bin/binutils/opcodes/avr-dis.c (revision d2201f2f89f0be1a0be6f7568000ed297414a06d)
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