xref: /openbsd-src/gnu/usr.bin/binutils/opcodes/avr-dis.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /* Disassemble AVR instructions.
2    Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3 
4    Contributed by Denis Chertykov <denisc@overta.ru>
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19 
20 
21 #include "sysdep.h"
22 #include "dis-asm.h"
23 #include "opintl.h"
24 
25 typedef unsigned char u8;
26 typedef unsigned short u16;
27 typedef unsigned long u32;
28 
29 #define IFMASK(a,b)     ((opcode & (a)) == (b))
30 
31 static char* SREG_flags = "CZNVSHTI";
32 static char* sect94[] = {"COM","NEG","SWAP","INC","NULL","ASR","LSR","ROR",
33 			 0,0,"DEC",0,0,0,0,0};
34 static char* sect98[] = {"CBI","SBIC","SBI","SBIS"};
35 static char* branchs[] = {
36   "BRCS","BREQ","BRMI","BRVS",
37   "BRLT","BRHS","BRTS","BRIE",
38   "BRCC","BRNE","BRPL","BRVC",
39   "BRGE","BRHC","BRTC","BRID"
40 };
41 
42 static char* last4[] = {"BLD","BST","SBRC","SBRS"};
43 
44 
45 static void dispLDD PARAMS ((u16, char *));
46 
47 static void
48 dispLDD (opcode, dest)
49      u16 opcode;
50      char *dest;
51 {
52   opcode = (((opcode & 0x2000) >> 8) | ((opcode & 0x0c00) >> 7)
53 	    | (opcode & 7));
54   sprintf(dest, "%d", opcode);
55 }
56 
57 
58 static void regPP PARAMS ((u16, char *));
59 
60 static void
61 regPP (opcode, dest)
62      u16 opcode;
63      char *dest;
64 {
65   opcode = ((opcode & 0x0600) >> 5) | (opcode & 0xf);
66   sprintf(dest, "0x%02X", opcode);
67 }
68 
69 
70 static void reg50 PARAMS ((u16, char *));
71 
72 static void
73 reg50 (opcode, dest)
74      u16 opcode;
75      char *dest;
76 {
77   opcode = (opcode & 0x01f0) >> 4;
78   sprintf(dest, "R%d", opcode);
79 }
80 
81 
82 static void reg104 PARAMS ((u16, char *));
83 
84 static void
85 reg104 (opcode, dest)
86      u16 opcode;
87      char *dest;
88 {
89   opcode = (opcode & 0xf) | ((opcode & 0x0200) >> 5);
90   sprintf(dest, "R%d", opcode);
91 }
92 
93 
94 static void reg40 PARAMS ((u16, char *));
95 
96 static void
97 reg40 (opcode, dest)
98      u16 opcode;
99      char *dest;
100 {
101   opcode = (opcode & 0xf0) >> 4;
102   sprintf(dest, "R%d", opcode + 16);
103 }
104 
105 
106 static void reg20w PARAMS ((u16, char *));
107 
108 static void
109 reg20w (opcode, dest)
110      u16 opcode;
111      char *dest;
112 {
113   opcode = (opcode & 0x30) >> 4;
114   sprintf(dest, "R%d", 24 + opcode * 2);
115 }
116 
117 
118 static void lit404 PARAMS ((u16, char *));
119 
120 static void
121 lit404 (opcode, dest)
122      u16 opcode;
123      char *dest;
124 {
125   opcode = ((opcode & 0xf00) >> 4) | (opcode & 0xf);
126   sprintf(dest, "0x%02X", opcode);
127 }
128 
129 
130 static void lit204 PARAMS ((u16, char *));
131 
132 static void
133 lit204 (opcode, dest)
134      u16 opcode;
135      char *dest;
136 {
137   opcode = ((opcode & 0xc0) >> 2) | (opcode & 0xf);
138   sprintf(dest, "0x%02X", opcode);
139 }
140 
141 
142 static void add0fff PARAMS ((u16, char *, int));
143 
144 static void
145 add0fff (op, dest, pc)
146      u16 op;
147      char *dest;
148      int pc;
149 {
150   int rel_addr = (((op & 0xfff) ^ 0x800) - 0x800) * 2;
151   sprintf(dest, ".%+-8d ; 0x%06X", rel_addr, pc + 2 + rel_addr);
152 }
153 
154 
155 static void add03f8 PARAMS ((u16, char *, int));
156 
157 static void
158 add03f8 (op, dest, pc)
159      u16 op;
160      char *dest;
161      int pc;
162 {
163   int rel_addr = ((((op >> 3) & 0x7f) ^ 0x40) - 0x40) * 2;
164   sprintf(dest, ".%+-8d ; 0x%06X", rel_addr, pc + 2 + rel_addr);
165 }
166 
167 
168 static u16 avrdis_opcode PARAMS ((bfd_vma, disassemble_info *));
169 
170 static u16
171 avrdis_opcode (addr, info)
172      bfd_vma addr;
173      disassemble_info *info;
174 {
175   bfd_byte buffer[2];
176   int status;
177   status = info->read_memory_func(addr, buffer, 2, info);
178   if (status != 0)
179     {
180       info->memory_error_func(status, addr, info);
181       return -1;
182     }
183   return bfd_getl16 (buffer);
184 }
185 
186 
187 int
188 print_insn_avr(addr, info)
189      bfd_vma addr;
190      disassemble_info *info;
191 {
192   char rr[200];
193   char rd[200];
194   u16 opcode;
195   void *stream = info->stream;
196   fprintf_ftype prin = info->fprintf_func;
197   int cmd_len = 2;
198 
199   opcode = avrdis_opcode (addr, info);
200 
201   if (IFMASK(0xd000, 0x8000))
202     {
203       char letter;
204       reg50(opcode, rd);
205       dispLDD(opcode, rr);
206       if (opcode & 8)
207 	letter = 'Y';
208       else
209 	letter = 'Z';
210       if (opcode & 0x0200)
211 	(*prin) (stream, "    STD     %c+%s,%s", letter, rr, rd);
212       else
213 	(*prin) (stream, "    LDD     %s,%c+%s", rd, letter, rr);
214     }
215   else
216     {
217       switch (opcode & 0xf000)
218         {
219         case 0x0000:
220 	  {
221 	    reg50(opcode, rd);
222 	    reg104(opcode, rr);
223 	    switch (opcode & 0x0c00)
224 	      {
225 	      case 0x0000:
226 		(*prin) (stream, "    NOP");
227 		break;
228 	      case 0x0400:
229 		(*prin) (stream, "    CPC     %s,%s", rd, rr);
230 		break;
231 	      case 0x0800:
232 		(*prin) (stream, "    SBC     %s,%s", rd, rr);
233 		break;
234 	      case 0x0c00:
235 		(*prin) (stream, "    ADD     %s,%s", rd, rr);
236 		break;
237 	      }
238 	  }
239 	  break;
240         case 0x1000:
241 	  {
242 	    reg50(opcode, rd);
243 	    reg104(opcode, rr);
244 	    switch (opcode & 0x0c00)
245 	      {
246 	      case 0x0000:
247 		(*prin) (stream, "    CPSE    %s,%s", rd, rr);
248 		break;
249 	      case 0x0400:
250 		(*prin) (stream, "    CP      %s,%s", rd, rr);
251 		break;
252 	      case 0x0800:
253 		(*prin) (stream, "    SUB     %s,%s", rd, rr);
254 		break;
255 	      case 0x0c00:
256 		(*prin) (stream, "    ADC     %s,%s", rd, rr);
257 		break;
258 	      }
259 	  }
260 	  break;
261         case 0x2000:
262 	  {
263 	    reg50(opcode, rd);
264 	    reg104(opcode, rr);
265 	    switch (opcode & 0x0c00)
266 	      {
267 	      case 0x0000:
268 		(*prin) (stream, "    AND     %s,%s", rd, rr);
269 		break;
270 	      case 0x0400:
271 		(*prin) (stream, "    EOR     %s,%s", rd, rr);
272 		break;
273 	      case 0x0800:
274 		(*prin) (stream, "    OR      %s,%s", rd, rr);
275 		break;
276 	      case 0x0c00:
277 		(*prin) (stream, "    MOV     %s,%s", rd, rr);
278 		break;
279 	      }
280 	  }
281 	  break;
282         case 0x3000:
283 	  {
284 	    reg40(opcode, rd);
285 	    lit404(opcode, rr);
286 	    (*prin) (stream, "    CPI     %s,%s", rd, rr);
287 	  }
288 	  break;
289         case 0x4000:
290 	  {
291 	    reg40(opcode, rd);
292 	    lit404(opcode, rr);
293 	    (*prin) (stream, "    SBCI    %s,%s", rd, rr);
294 	  }
295 	  break;
296         case 0x5000:
297 	  {
298 	    reg40(opcode, rd);
299 	    lit404(opcode, rr);
300 	    (*prin) (stream, "    SUBI    %s,%s", rd, rr);
301 	  }
302 	  break;
303         case 0x6000:
304 	  {
305 	    reg40(opcode, rd);
306 	    lit404(opcode, rr);
307 	    (*prin) (stream, "    ORI     %s,%s", rd, rr);
308 	  }
309 	  break;
310         case 0x7000:
311 	  {
312 	    reg40(opcode, rd);
313 	    lit404(opcode, rr);
314 	    (*prin) (stream, "    ANDI    %s,%s", rd, rr);
315 	  }
316 	  break;
317         case 0x9000:
318 	  {
319 	    switch (opcode & 0x0e00)
320 	      {
321 	      case 0x0000:
322 		{
323 		  reg50(opcode, rd);
324 		  switch (opcode & 0xf)
325 		    {
326 		    case 0x0:
327 		      {
328 			(*prin) (stream, "    LDS     %s,0x%04X", rd,
329 				 avrdis_opcode(addr + 2, info));
330 			cmd_len = 4;
331 		      }
332 		      break;
333 		    case 0x1:
334 		      (*prin) (stream, "    LD      %s,Z+", rd);
335 		      break;
336 		    case 0x2:
337 		      (*prin) (stream, "    LD      %s,-Z", rd);
338 		      break;
339 		    case 0x9:
340 		      (*prin) (stream, "    LD      %s,Y+", rd);
341 		      break;
342 		    case 0xa:
343 		      (*prin) (stream, "    LD      %s,-Y", rd);
344 		      break;
345 		    case 0xc:
346 		      (*prin) (stream, "    LD      %s,X", rd);
347 		      break;
348 		    case 0xd:
349 		      (*prin) (stream, "    LD      %s,X+", rd);
350 		      break;
351 		    case 0xe:
352 		      (*prin) (stream, "    LD      %s,-X", rd);
353 		      break;
354 		    case 0xf:
355 		      (*prin) (stream, "    POP     %s", rd);
356 		      break;
357 		    default:
358 		      (*prin) (stream, "    ????");
359 		      break;
360 		    }
361 		}
362 		break;
363 	      case 0x0200:
364 		{
365 		  reg50(opcode, rd);
366 		  switch (opcode & 0xf)
367 		    {
368 		    case 0x0:
369 		      {
370 			(*prin) (stream, "    STS     0x%04X,%s",
371 				 avrdis_opcode(addr + 2, info), rd);
372 			cmd_len = 4;
373 		      }
374 		      break;
375 		    case 0x1:
376 		      (*prin) (stream, "    ST      Z+,%s", rd);
377 		      break;
378 		    case 0x2:
379 		      (*prin) (stream, "    ST      -Z,%s", rd);
380 		      break;
381 		    case 0x9:
382 		      (*prin) (stream, "    ST      Y+,%s", rd);
383 		      break;
384 		    case 0xa:
385 		      (*prin) (stream, "    ST      -Y,%s", rd);
386 		      break;
387 		    case 0xc:
388 		      (*prin) (stream, "    ST      X,%s", rd);
389 		      break;
390 		    case 0xd:
391 		      (*prin) (stream, "    ST      X+,%s", rd);
392 		      break;
393 		    case 0xe:
394 		      (*prin) (stream, "    ST      -X,%s", rd);
395 		      break;
396 		    case 0xf:
397 		      (*prin) (stream, "    PUSH    %s", rd);
398 		      break;
399 		    default:
400 		      (*prin) (stream, "    ????");
401 		      break;
402 		    }
403 		}
404 		break;
405 	      case 0x0400:
406 		{
407 		  if (IFMASK(0x020c, 0x000c))
408 		    {
409 		      u32 k = ((opcode & 0x01f0) >> 3) | (opcode & 1);
410 		      k = (k << 16) | avrdis_opcode(addr + 2, info);
411 		      if (opcode & 0x0002)
412 			(*prin) (stream, "    CALL    0x%06X", k*2);
413 		      else
414 			(*prin) (stream, "    JMP     0x%06X", k*2);
415 		      cmd_len = 4;
416 		    }
417 		  else if (IFMASK(0x010f, 0x0008))
418 		    {
419 		      int sf = (opcode & 0x70) >> 4;
420 		      if (opcode & 0x0080)
421 			(*prin) (stream, "    CL%c", SREG_flags[sf]);
422 		      else
423 			(*prin) (stream, "    SE%c", SREG_flags[sf]);
424 		    }
425 		  else if (IFMASK(0x000f, 0x0009))
426 		    {
427 		      if (opcode & 0x0100)
428 			(*prin) (stream, "    ICALL");
429 		      else
430 			(*prin) (stream, "    IJMP");
431 		    }
432 		  else if (IFMASK(0x010f, 0x0108))
433 		    {
434 		      if (IFMASK(0x0090, 0x0000))
435 			(*prin) (stream, "    RET");
436 		      else if (IFMASK(0x0090, 0x0010))
437 			(*prin) (stream, "    RETI");
438 		      else if (IFMASK(0x00e0, 0x0080))
439 			(*prin) (stream, "    SLEEP");
440 		      else if (IFMASK(0x00e0, 0x00a0))
441 			(*prin) (stream, "    WDR");
442 		      else if (IFMASK(0x00f0, 0x00c0))
443 			(*prin) (stream, "    LPM");
444 		      else if (IFMASK(0x00f0, 0x00d0))
445 			(*prin) (stream, "    ELPM");
446 		      else
447 			(*prin) (stream, "    ????");
448 		    }
449 		  else
450 		    {
451 		      const char* p;
452 		      reg50(opcode, rd);
453 		      p = sect94[opcode & 0xf];
454 		      if (!p)
455 			p = "????";
456 		      (*prin) (stream, "    %-8s%s", p, rd);
457 		    }
458 		}
459 		break;
460 	      case 0x0600:
461 		{
462 		  if (opcode & 0x0200)
463 		    {
464 		      lit204(opcode, rd);
465 		      reg20w(opcode, rr);
466 		      if (opcode & 0x0100)
467 			(*prin) (stream, "    SBIW    %s,%s", rr, rd);
468 		      else
469 			(*prin) (stream, "    ADIW    %s,%s", rr, rd);
470 		    }
471 		}
472 		break;
473 	      case 0x0800:
474 	      case 0x0a00:
475 		{
476 		  (*prin) (stream, "    %-8s0x%02X,%d",
477 			   sect98[(opcode & 0x0300) >> 8],
478 			   (opcode & 0xf8) >> 3,
479 			   opcode & 7);
480 		}
481 		break;
482 	      default:
483 		{
484 		  reg50(opcode, rd);
485 		  reg104(opcode, rr);
486 		  (*prin) (stream, "    MUL     %s,%s", rd, rr);
487 		}
488 	      }
489 	  }
490 	  break;
491         case 0xb000:
492 	  {
493 	    reg50(opcode, rd);
494 	    regPP(opcode, rr);
495 	    if (opcode & 0x0800)
496 	      (*prin) (stream, "    OUT     %s,%s", rr, rd);
497 	    else
498 	      (*prin) (stream, "    IN      %s,%s", rd, rr);
499 	  }
500 	  break;
501         case 0xc000:
502 	  {
503 	    add0fff(opcode, rd, addr);
504 	    (*prin) (stream, "    RJMP    %s", rd);
505 	  }
506 	  break;
507         case 0xd000:
508 	  {
509 	    add0fff(opcode, rd, addr);
510 	    (*prin) (stream, "    RCALL   %s", rd);
511 	  }
512 	  break;
513         case 0xe000:
514 	  {
515 	    reg40(opcode, rd);
516 	    lit404(opcode, rr);
517 	    (*prin) (stream, "    LDI     %s,%s", rd, rr);
518 	  }
519 	  break;
520         case 0xf000:
521 	  {
522 	    if (opcode & 0x0800)
523 	      {
524 		reg50(opcode, rd);
525 		(*prin) (stream, "    %-8s%s,%d",
526 			 last4[(opcode & 0x0600) >> 9],
527 			 rd, opcode & 7);
528 	      }
529 	    else
530 	      {
531 		char* p;
532 		add03f8(opcode, rd, addr);
533 		p = branchs[((opcode & 0x0400) >> 7) | (opcode & 7)];
534 		(*prin) (stream, "    %-8s%s", p, rd);
535 	      }
536 	  }
537 	  break;
538         }
539     }
540   return cmd_len;
541 }
542