1 /* Disassemble AVR instructions.
2    Copyright 1999, 2000, 2002, 2004, 2005, 2006
3    Free Software Foundation, Inc.
4 
5    Contributed by Denis Chertykov <denisc@overta.ru>
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
20 
21 #include <assert.h>
22 #include "sysdep.h"
23 #include "dis-asm.h"
24 #include "opintl.h"
25 #include "libiberty.h"
26 
27 struct avr_opcodes_s
28 {
29   char *name;
30   char *constraints;
31   char *opcode;
32   int insn_size;		/* In words.  */
33   int isa;
34   unsigned int bin_opcode;
35 };
36 
37 #define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
38 {#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
39 
40 const struct avr_opcodes_s avr_opcodes[] =
41 {
42   #include "opcode/avr.h"
43   {NULL, NULL, NULL, 0, 0, 0}
44 };
45 
46 static int
avr_operand(unsigned int insn,unsigned int insn2,unsigned int pc,int constraint,char * buf,char * comment,int regs,int * sym,bfd_vma * sym_addr)47 avr_operand (unsigned int insn, unsigned int insn2, unsigned int pc, int constraint,
48              char *buf, char *comment, int regs, int *sym, bfd_vma *sym_addr)
49 {
50   int ok = 1;
51   *sym = 0;
52 
53   switch (constraint)
54     {
55       /* Any register operand.  */
56     case 'r':
57       if (regs)
58 	insn = (insn & 0xf) | ((insn & 0x0200) >> 5); /* Source register.  */
59       else
60 	insn = (insn & 0x01f0) >> 4; /* Destination register.  */
61 
62       sprintf (buf, "r%d", insn);
63       break;
64 
65     case 'd':
66       if (regs)
67 	sprintf (buf, "r%d", 16 + (insn & 0xf));
68       else
69 	sprintf (buf, "r%d", 16 + ((insn & 0xf0) >> 4));
70       break;
71 
72     case 'w':
73       sprintf (buf, "r%d", 24 + ((insn & 0x30) >> 3));
74       break;
75 
76     case 'a':
77       if (regs)
78 	sprintf (buf, "r%d", 16 + (insn & 7));
79       else
80 	sprintf (buf, "r%d", 16 + ((insn >> 4) & 7));
81       break;
82 
83     case 'v':
84       if (regs)
85 	sprintf (buf, "r%d", (insn & 0xf) * 2);
86       else
87 	sprintf (buf, "r%d", ((insn & 0xf0) >> 3));
88       break;
89 
90     case 'e':
91       {
92 	char *xyz;
93 
94 	switch (insn & 0x100f)
95 	  {
96 	    case 0x0000: xyz = "Z";  break;
97 	    case 0x1001: xyz = "Z+"; break;
98 	    case 0x1002: xyz = "-Z"; break;
99 	    case 0x0008: xyz = "Y";  break;
100 	    case 0x1009: xyz = "Y+"; break;
101 	    case 0x100a: xyz = "-Y"; break;
102 	    case 0x100c: xyz = "X";  break;
103 	    case 0x100d: xyz = "X+"; break;
104 	    case 0x100e: xyz = "-X"; break;
105 	    default: xyz = "??"; ok = 0;
106 	  }
107 	sprintf (buf, xyz);
108 
109 	if (AVR_UNDEF_P (insn))
110 	  sprintf (comment, _("undefined"));
111       }
112       break;
113 
114     case 'z':
115       *buf++ = 'Z';
116       if (insn & 0x1)
117 	*buf++ = '+';
118       *buf = '\0';
119       if (AVR_UNDEF_P (insn))
120 	sprintf (comment, _("undefined"));
121       break;
122 
123     case 'b':
124       {
125 	unsigned int x;
126 
127 	x = (insn & 7);
128 	x |= (insn >> 7) & (3 << 3);
129 	x |= (insn >> 8) & (1 << 5);
130 
131 	if (insn & 0x8)
132 	  *buf++ = 'Y';
133 	else
134 	  *buf++ = 'Z';
135 	sprintf (buf, "+%d", x);
136 	sprintf (comment, "0x%02x", x);
137       }
138       break;
139 
140     case 'h':
141       *sym = 1;
142       *sym_addr = ((((insn & 1) | ((insn & 0x1f0) >> 3)) << 16) | insn2) * 2;
143       /* See PR binutils/2545.  Ideally we would like to display the hex
144 	 value of the address only once, but this would mean recoding
145 	 objdump_print_address() which would affect many targets.  */
146       sprintf (buf, "%#lx", (unsigned long) *sym_addr);
147       sprintf (comment, "0x");
148 
149       break;
150 
151     case 'L':
152       {
153 	int rel_addr = (((insn & 0xfff) ^ 0x800) - 0x800) * 2;
154 	sprintf (buf, ".%+-8d", rel_addr);
155         *sym = 1;
156         *sym_addr = pc + 2 + rel_addr;
157 	sprintf (comment, "0x");
158       }
159       break;
160 
161     case 'l':
162       {
163 	int rel_addr = ((((insn >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
164 	sprintf (buf, ".%+-8d", rel_addr);
165         *sym = 1;
166         *sym_addr = pc + 2 + rel_addr;
167 	sprintf (comment, "0x");
168       }
169       break;
170 
171     case 'i':
172       sprintf (buf, "0x%04X", insn2);
173       break;
174 
175     case 'M':
176       sprintf (buf, "0x%02X", ((insn & 0xf00) >> 4) | (insn & 0xf));
177       sprintf (comment, "%d", ((insn & 0xf00) >> 4) | (insn & 0xf));
178       break;
179 
180     case 'n':
181       sprintf (buf, "??");
182       fprintf (stderr, _("Internal disassembler error"));
183       ok = 0;
184       break;
185 
186     case 'K':
187       {
188 	unsigned int x;
189 
190 	x = (insn & 0xf) | ((insn >> 2) & 0x30);
191 	sprintf (buf, "0x%02x", x);
192 	sprintf (comment, "%d", x);
193       }
194       break;
195 
196     case 's':
197       sprintf (buf, "%d", insn & 7);
198       break;
199 
200     case 'S':
201       sprintf (buf, "%d", (insn >> 4) & 7);
202       break;
203 
204     case 'P':
205       {
206 	unsigned int x;
207 
208 	x = (insn & 0xf);
209 	x |= (insn >> 5) & 0x30;
210 	sprintf (buf, "0x%02x", x);
211 	sprintf (comment, "%d", x);
212       }
213       break;
214 
215     case 'p':
216       {
217 	unsigned int x;
218 
219 	x = (insn >> 3) & 0x1f;
220 	sprintf (buf, "0x%02x", x);
221 	sprintf (comment, "%d", x);
222       }
223       break;
224 
225     case '?':
226       *buf = '\0';
227       break;
228 
229     default:
230       sprintf (buf, "??");
231       fprintf (stderr, _("unknown constraint `%c'"), constraint);
232       ok = 0;
233     }
234 
235     return ok;
236 }
237 
238 static unsigned short
avrdis_opcode(bfd_vma addr,disassemble_info * info)239 avrdis_opcode (bfd_vma addr, disassemble_info *info)
240 {
241   bfd_byte buffer[2];
242   int status;
243 
244   status = info->read_memory_func (addr, buffer, 2, info);
245 
246   if (status == 0)
247     return bfd_getl16 (buffer);
248 
249   info->memory_error_func (status, addr, info);
250   return -1;
251 }
252 
253 
254 int
print_insn_avr(bfd_vma addr,disassemble_info * info)255 print_insn_avr (bfd_vma addr, disassemble_info *info)
256 {
257   unsigned int insn, insn2;
258   const struct avr_opcodes_s *opcode;
259   static unsigned int *maskptr;
260   void *stream = info->stream;
261   fprintf_ftype prin = info->fprintf_func;
262   static unsigned int *avr_bin_masks;
263   static int initialized;
264   int cmd_len = 2;
265   int ok = 0;
266   char op1[20], op2[20], comment1[40], comment2[40];
267   int sym_op1 = 0, sym_op2 = 0;
268   bfd_vma sym_addr1, sym_addr2;
269 
270   if (!initialized)
271     {
272       unsigned int nopcodes;
273 
274       nopcodes = sizeof (avr_opcodes) / sizeof (struct avr_opcodes_s);
275 
276       avr_bin_masks = xmalloc (nopcodes * sizeof (unsigned int));
277 
278       for (opcode = avr_opcodes, maskptr = avr_bin_masks;
279 	   opcode->name;
280 	   opcode++, maskptr++)
281 	{
282 	  char * s;
283 	  unsigned int bin = 0;
284 	  unsigned int mask = 0;
285 
286 	  for (s = opcode->opcode; *s; ++s)
287 	    {
288 	      bin <<= 1;
289 	      mask <<= 1;
290 	      bin |= (*s == '1');
291 	      mask |= (*s == '1' || *s == '0');
292 	    }
293 	  assert (s - opcode->opcode == 16);
294 	  assert (opcode->bin_opcode == bin);
295 	  *maskptr = mask;
296 	}
297 
298       initialized = 1;
299     }
300 
301   insn = avrdis_opcode (addr, info);
302 
303   for (opcode = avr_opcodes, maskptr = avr_bin_masks;
304        opcode->name;
305        opcode++, maskptr++)
306     if ((insn & *maskptr) == opcode->bin_opcode)
307       break;
308 
309   /* Special case: disassemble `ldd r,b+0' as `ld r,b', and
310      `std b+0,r' as `st b,r' (next entry in the table).  */
311 
312   if (AVR_DISP0_P (insn))
313     opcode++;
314 
315   op1[0] = 0;
316   op2[0] = 0;
317   comment1[0] = 0;
318   comment2[0] = 0;
319 
320   if (opcode->name)
321     {
322       char *op = opcode->constraints;
323 
324       insn2 = 0;
325       ok = 1;
326 
327       if (opcode->insn_size > 1)
328 	{
329 	  insn2 = avrdis_opcode (addr + 2, info);
330 	  cmd_len = 4;
331 	}
332 
333       if (*op && *op != '?')
334 	{
335 	  int regs = REGISTER_P (*op);
336 
337 	  ok = avr_operand (insn, insn2, addr, *op, op1, comment1, 0, &sym_op1, &sym_addr1);
338 
339 	  if (ok && *(++op) == ',')
340 	    ok = avr_operand (insn, insn2, addr, *(++op), op2,
341 			      *comment1 ? comment2 : comment1, regs, &sym_op2, &sym_addr2);
342 	}
343     }
344 
345   if (!ok)
346     {
347       /* Unknown opcode, or invalid combination of operands.  */
348       sprintf (op1, "0x%04x", insn);
349       op2[0] = 0;
350       sprintf (comment1, "????");
351       comment2[0] = 0;
352     }
353 
354   (*prin) (stream, "%s", ok ? opcode->name : ".word");
355 
356   if (*op1)
357       (*prin) (stream, "\t%s", op1);
358 
359   if (*op2)
360     (*prin) (stream, ", %s", op2);
361 
362   if (*comment1)
363     (*prin) (stream, "\t; %s", comment1);
364 
365   if (sym_op1)
366     info->print_address_func (sym_addr1, info);
367 
368   if (*comment2)
369     (*prin) (stream, " %s", comment2);
370 
371   if (sym_op2)
372     info->print_address_func (sym_addr2, info);
373 
374   return cmd_len;
375 }
376