1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
3 
4    Contributed by Dmitry Diky <diwil@mail.ru>
5 
6    This file is part of the GNU opcodes library.
7 
8    This library is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3, or (at your option)
11    any later version.
12 
13    It is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
16    License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21    MA 02110-1301, USA.  */
22 
23 #include <stdio.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/types.h>
27 
28 #include "dis-asm.h"
29 #include "opintl.h"
30 #include "libiberty.h"
31 
32 #define DASM_SECTION
33 #include "opcode/msp430.h"
34 #undef DASM_SECTION
35 
36 
37 #define PS(x)   (0xffff & (x))
38 
39 static unsigned short
40 msp430dis_opcode (bfd_vma addr, disassemble_info *info)
41 {
42   bfd_byte buffer[2];
43   int status;
44 
45   status = info->read_memory_func (addr, buffer, 2, info);
46   if (status != 0)
47     {
48       info->memory_error_func (status, addr, info);
49       return -1;
50     }
51   return bfd_getl16 (buffer);
52 }
53 
54 static int
55 msp430_nooperands (struct msp430_opcode_s *opcode,
56 		   bfd_vma addr ATTRIBUTE_UNUSED,
57 		   unsigned short insn ATTRIBUTE_UNUSED,
58 		   char *comm,
59 		   int *cycles)
60 {
61   /* Pop with constant.  */
62   if (insn == 0x43b2)
63     return 0;
64   if (insn == opcode->bin_opcode)
65     return 2;
66 
67   if (opcode->fmt == 0)
68     {
69       if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
70 	return 0;
71 
72       strcpy (comm, "emulated...");
73       *cycles = 1;
74     }
75   else
76     {
77       strcpy (comm, "return from interupt");
78       *cycles = 5;
79     }
80 
81   return 2;
82 }
83 
84 static int
85 msp430_singleoperand (disassemble_info *info,
86 		      struct msp430_opcode_s *opcode,
87 		      bfd_vma addr,
88 		      unsigned short insn,
89 		      char *op,
90 		      char *comm,
91 		      int *cycles)
92 {
93   int regs = 0, regd = 0;
94   int ad = 0, as = 0;
95   int where = 0;
96   int cmd_len = 2;
97   short dst = 0;
98 
99   regd = insn & 0x0f;
100   regs = (insn & 0x0f00) >> 8;
101   as = (insn & 0x0030) >> 4;
102   ad = (insn & 0x0080) >> 7;
103 
104   switch (opcode->fmt)
105     {
106     case 0:			/* Emulated work with dst register.  */
107       if (regs != 2 && regs != 3 && regs != 1)
108 	return 0;
109 
110       /* Check if not clr insn.  */
111       if (opcode->bin_opcode == 0x4300 && (ad || as))
112 	return 0;
113 
114       /* Check if really inc, incd insns.  */
115       if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
116 	return 0;
117 
118       if (ad == 0)
119 	{
120 	  *cycles = 1;
121 
122 	  /* Register.  */
123 	  if (regd == 0)
124 	    {
125 	      *cycles += 1;
126 	      sprintf (op, "r0");
127 	    }
128 	  else if (regd == 1)
129 	    sprintf (op, "r1");
130 
131 	  else if (regd == 2)
132 	    sprintf (op, "r2");
133 
134 	  else
135 	    sprintf (op, "r%d", regd);
136 	}
137       else	/* ad == 1 msp430dis_opcode.  */
138 	{
139 	  if (regd == 0)
140 	    {
141 	      /* PC relative.  */
142 	      dst = msp430dis_opcode (addr + 2, info);
143 	      cmd_len += 2;
144 	      *cycles = 4;
145 	      sprintf (op, "0x%04x", dst);
146 	      sprintf (comm, "PC rel. abs addr 0x%04x",
147 		       PS ((short) (addr + 2) + dst));
148 	    }
149 	  else if (regd == 2)
150 	    {
151 	      /* Absolute.  */
152 	      dst = msp430dis_opcode (addr + 2, info);
153 	      cmd_len += 2;
154 	      *cycles = 4;
155 	      sprintf (op, "&0x%04x", PS (dst));
156 	    }
157 	  else
158 	    {
159 	      dst = msp430dis_opcode (addr + 2, info);
160 	      cmd_len += 2;
161 	      *cycles = 4;
162 	      sprintf (op, "%d(r%d)", dst, regd);
163 	    }
164 	}
165       break;
166 
167     case 2:	/* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
168       if (as == 0)
169 	{
170 	  if (regd == 3)
171 	    {
172 	      /* Constsnts.  */
173 	      sprintf (op, "#0");
174 	      sprintf (comm, "r3 As==00");
175 	    }
176 	  else
177 	    {
178 	      /* Register.  */
179 	      sprintf (op, "r%d", regd);
180 	    }
181 	  *cycles = 1;
182 	}
183       else if (as == 2)
184 	{
185 	  *cycles = 1;
186 	  if (regd == 2)
187 	    {
188 	      sprintf (op, "#4");
189 	      sprintf (comm, "r2 As==10");
190 	    }
191 	  else if (regd == 3)
192 	    {
193 	      sprintf (op, "#2");
194 	      sprintf (comm, "r3 As==10");
195 	    }
196 	  else
197 	    {
198 	      *cycles = 3;
199 	      /* Indexed register mode @Rn.  */
200 	      sprintf (op, "@r%d", regd);
201 	    }
202 	}
203       else if (as == 3)
204 	{
205 	  *cycles = 1;
206 	  if (regd == 2)
207 	    {
208 	      sprintf (op, "#8");
209 	      sprintf (comm, "r2 As==11");
210 	    }
211 	  else if (regd == 3)
212 	    {
213 	      sprintf (op, "#-1");
214 	      sprintf (comm, "r3 As==11");
215 	    }
216 	  else if (regd == 0)
217 	    {
218 	      *cycles = 3;
219 	      /* absolute. @pc+ */
220 	      dst = msp430dis_opcode (addr + 2, info);
221 	      cmd_len += 2;
222 	      sprintf (op, "#%d", dst);
223 	      sprintf (comm, "#0x%04x", PS (dst));
224 	    }
225 	  else
226 	    {
227 	      *cycles = 3;
228 	      sprintf (op, "@r%d+", regd);
229 	    }
230 	}
231       else if (as == 1)
232 	{
233 	  *cycles = 4;
234 	  if (regd == 0)
235 	    {
236 	      /* PC relative.  */
237 	      dst = msp430dis_opcode (addr + 2, info);
238 	      cmd_len += 2;
239 	      sprintf (op, "0x%04x", PS (dst));
240 	      sprintf (comm, "PC rel. 0x%04x",
241 		       PS ((short) addr + 2 + dst));
242 	    }
243 	  else if (regd == 2)
244 	    {
245 	      /* Absolute.  */
246 	      dst = msp430dis_opcode (addr + 2, info);
247 	      cmd_len += 2;
248 	      sprintf (op, "&0x%04x", PS (dst));
249 	    }
250 	  else if (regd == 3)
251 	    {
252 	      *cycles = 1;
253 	      sprintf (op, "#1");
254 	      sprintf (comm, "r3 As==01");
255 	    }
256 	  else
257 	    {
258 	      /* Indexd.  */
259 	      dst = msp430dis_opcode (addr + 2, info);
260 	      cmd_len += 2;
261 	      sprintf (op, "%d(r%d)", dst, regd);
262 	    }
263 	}
264       break;
265 
266     case 3:			/* Jumps.  */
267       where = insn & 0x03ff;
268       if (where & 0x200)
269 	where |= ~0x03ff;
270       if (where > 512 || where < -511)
271 	return 0;
272 
273       where *= 2;
274       sprintf (op, "$%+-8d", where + 2);
275       sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
276       *cycles = 2;
277       return 2;
278       break;
279     default:
280       cmd_len = 0;
281     }
282 
283   return cmd_len;
284 }
285 
286 static int
287 msp430_doubleoperand (disassemble_info *info,
288 		      struct msp430_opcode_s *opcode,
289 		      bfd_vma addr,
290 		      unsigned short insn,
291 		      char *op1,
292 		      char *op2,
293 		      char *comm1,
294 		      char *comm2,
295 		      int *cycles)
296 {
297   int regs = 0, regd = 0;
298   int ad = 0, as = 0;
299   int cmd_len = 2;
300   short dst = 0;
301 
302   regd = insn & 0x0f;
303   regs = (insn & 0x0f00) >> 8;
304   as = (insn & 0x0030) >> 4;
305   ad = (insn & 0x0080) >> 7;
306 
307   if (opcode->fmt == 0)
308     {
309       /* Special case: rla and rlc are the only 2 emulated instructions that
310 	 fall into two operand instructions.  */
311       /* With dst, there are only:
312 	 Rm       	Register,
313          x(Rm)     	Indexed,
314          0xXXXX    	Relative,
315          &0xXXXX    	Absolute
316          emulated_ins   dst
317          basic_ins      dst, dst.  */
318 
319       if (regd != regs || as != ad)
320 	return 0;		/* May be 'data' section.  */
321 
322       if (ad == 0)
323 	{
324 	  /* Register mode.  */
325 	  if (regd == 3)
326 	    {
327 	      strcpy (comm1, _("Illegal as emulation instr"));
328 	      return -1;
329 	    }
330 
331 	  sprintf (op1, "r%d", regd);
332 	  *cycles = 1;
333 	}
334       else			/* ad == 1 */
335 	{
336 	  if (regd == 0)
337 	    {
338 	      /* PC relative, Symbolic.  */
339 	      dst = msp430dis_opcode (addr + 2, info);
340 	      cmd_len += 4;
341 	      *cycles = 6;
342 	      sprintf (op1, "0x%04x", PS (dst));
343 	      sprintf (comm1, "PC rel. 0x%04x",
344 		       PS ((short) addr + 2 + dst));
345 
346 	    }
347 	  else if (regd == 2)
348 	    {
349 	      /* Absolute.  */
350 	      dst = msp430dis_opcode (addr + 2, info);
351 	      /* If the 'src' field is not the same as the dst
352 		 then this is not an rla instruction.  */
353 	      if (dst != msp430dis_opcode (addr + 4, info))
354 		return 0;
355 	      cmd_len += 4;
356 	      *cycles = 6;
357 	      sprintf (op1, "&0x%04x", PS (dst));
358 	    }
359 	  else
360 	    {
361 	      /* Indexed.  */
362 	      dst = msp430dis_opcode (addr + 2, info);
363 	      cmd_len += 4;
364 	      *cycles = 6;
365 	      sprintf (op1, "%d(r%d)", dst, regd);
366 	    }
367 	}
368 
369       *op2 = 0;
370       *comm2 = 0;
371       return cmd_len;
372     }
373 
374   /* Two operands exactly.  */
375   if (ad == 0 && regd == 3)
376     {
377       /* R2/R3 are illegal as dest: may be data section.  */
378       strcpy (comm1, _("Illegal as 2-op instr"));
379       return -1;
380     }
381 
382   /* Source.  */
383   if (as == 0)
384     {
385       *cycles = 1;
386       if (regs == 3)
387 	{
388 	  /* Constsnts.  */
389 	  sprintf (op1, "#0");
390 	  sprintf (comm1, "r3 As==00");
391 	}
392       else
393 	{
394 	  /* Register.  */
395 	  sprintf (op1, "r%d", regs);
396 	}
397     }
398   else if (as == 2)
399     {
400       *cycles = 1;
401 
402       if (regs == 2)
403 	{
404 	  sprintf (op1, "#4");
405 	  sprintf (comm1, "r2 As==10");
406 	}
407       else if (regs == 3)
408 	{
409 	  sprintf (op1, "#2");
410 	  sprintf (comm1, "r3 As==10");
411 	}
412       else
413 	{
414 	  *cycles = 2;
415 
416 	  /* Indexed register mode @Rn.  */
417 	  sprintf (op1, "@r%d", regs);
418 	}
419       if (!regs)
420 	*cycles = 3;
421     }
422   else if (as == 3)
423     {
424       if (regs == 2)
425 	{
426 	  sprintf (op1, "#8");
427 	  sprintf (comm1, "r2 As==11");
428 	  *cycles = 1;
429 	}
430       else if (regs == 3)
431 	{
432 	  sprintf (op1, "#-1");
433 	  sprintf (comm1, "r3 As==11");
434 	  *cycles = 1;
435 	}
436       else if (regs == 0)
437 	{
438 	  *cycles = 3;
439 	  /* Absolute. @pc+.  */
440 	  dst = msp430dis_opcode (addr + 2, info);
441 	  cmd_len += 2;
442 	  sprintf (op1, "#%d", dst);
443 	  sprintf (comm1, "#0x%04x", PS (dst));
444 	}
445       else
446 	{
447 	  *cycles = 2;
448 	  sprintf (op1, "@r%d+", regs);
449 	}
450     }
451   else if (as == 1)
452     {
453       if (regs == 0)
454 	{
455 	  *cycles = 4;
456 	  /* PC relative.  */
457 	  dst = msp430dis_opcode (addr + 2, info);
458 	  cmd_len += 2;
459 	  sprintf (op1, "0x%04x", PS (dst));
460 	  sprintf (comm1, "PC rel. 0x%04x",
461 		   PS ((short) addr + 2 + dst));
462 	}
463       else if (regs == 2)
464 	{
465 	  *cycles = 2;
466 	  /* Absolute.  */
467 	  dst = msp430dis_opcode (addr + 2, info);
468 	  cmd_len += 2;
469 	  sprintf (op1, "&0x%04x", PS (dst));
470 	  sprintf (comm1, "0x%04x", PS (dst));
471 	}
472       else if (regs == 3)
473 	{
474 	  *cycles = 1;
475 	  sprintf (op1, "#1");
476 	  sprintf (comm1, "r3 As==01");
477 	}
478       else
479 	{
480 	  *cycles = 3;
481 	  /* Indexed.  */
482 	  dst = msp430dis_opcode (addr + 2, info);
483 	  cmd_len += 2;
484 	  sprintf (op1, "%d(r%d)", dst, regs);
485 	}
486     }
487 
488   /* Destination. Special care needed on addr + XXXX.  */
489 
490   if (ad == 0)
491     {
492       /* Register.  */
493       if (regd == 0)
494 	{
495 	  *cycles += 1;
496 	  sprintf (op2, "r0");
497 	}
498       else if (regd == 1)
499 	sprintf (op2, "r1");
500 
501       else if (regd == 2)
502 	sprintf (op2, "r2");
503 
504       else
505 	sprintf (op2, "r%d", regd);
506     }
507   else	/* ad == 1.  */
508     {
509       * cycles += 3;
510 
511       if (regd == 0)
512 	{
513 	  /* PC relative.  */
514 	  *cycles += 1;
515 	  dst = msp430dis_opcode (addr + cmd_len, info);
516 	  sprintf (op2, "0x%04x", PS (dst));
517 	  sprintf (comm2, "PC rel. 0x%04x",
518 		   PS ((short) addr + cmd_len + dst));
519 	  cmd_len += 2;
520 	}
521       else if (regd == 2)
522 	{
523 	  /* Absolute.  */
524 	  dst = msp430dis_opcode (addr + cmd_len, info);
525 	  cmd_len += 2;
526 	  sprintf (op2, "&0x%04x", PS (dst));
527 	}
528       else
529 	{
530 	  dst = msp430dis_opcode (addr + cmd_len, info);
531 	  cmd_len += 2;
532 	  sprintf (op2, "%d(r%d)", dst, regd);
533 	}
534     }
535 
536   return cmd_len;
537 }
538 
539 static int
540 msp430_branchinstr (disassemble_info *info,
541 		    struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
542 		    bfd_vma addr ATTRIBUTE_UNUSED,
543 		    unsigned short insn,
544 		    char *op1,
545 		    char *comm1,
546 		    int *cycles)
547 {
548   int regs = 0, regd = 0;
549   int ad = 0, as = 0;
550   int cmd_len = 2;
551   short dst = 0;
552 
553   regd = insn & 0x0f;
554   regs = (insn & 0x0f00) >> 8;
555   as = (insn & 0x0030) >> 4;
556   ad = (insn & 0x0080) >> 7;
557 
558   if (regd != 0)	/* Destination register is not a PC.  */
559     return 0;
560 
561   /* dst is a source register.  */
562   if (as == 0)
563     {
564       /* Constants.  */
565       if (regs == 3)
566 	{
567 	  *cycles = 1;
568 	  sprintf (op1, "#0");
569 	  sprintf (comm1, "r3 As==00");
570 	}
571       else
572 	{
573 	  /* Register.  */
574 	  *cycles = 1;
575 	  sprintf (op1, "r%d", regs);
576 	}
577     }
578   else if (as == 2)
579     {
580       if (regs == 2)
581 	{
582 	  *cycles = 2;
583 	  sprintf (op1, "#4");
584 	  sprintf (comm1, "r2 As==10");
585 	}
586       else if (regs == 3)
587 	{
588 	  *cycles = 1;
589 	  sprintf (op1, "#2");
590 	  sprintf (comm1, "r3 As==10");
591 	}
592       else
593 	{
594 	  /* Indexed register mode @Rn.  */
595 	  *cycles = 2;
596 	  sprintf (op1, "@r%d", regs);
597 	}
598     }
599   else if (as == 3)
600     {
601       if (regs == 2)
602 	{
603 	  *cycles = 1;
604 	  sprintf (op1, "#8");
605 	  sprintf (comm1, "r2 As==11");
606 	}
607       else if (regs == 3)
608 	{
609 	  *cycles = 1;
610 	  sprintf (op1, "#-1");
611 	  sprintf (comm1, "r3 As==11");
612 	}
613       else if (regs == 0)
614 	{
615 	  /* Absolute. @pc+  */
616 	  *cycles = 3;
617 	  dst = msp430dis_opcode (addr + 2, info);
618 	  cmd_len += 2;
619 	  sprintf (op1, "#0x%04x", PS (dst));
620 	}
621       else
622 	{
623 	  *cycles = 2;
624 	  sprintf (op1, "@r%d+", regs);
625 	}
626     }
627   else if (as == 1)
628     {
629       * cycles = 3;
630 
631       if (regs == 0)
632 	{
633 	  /* PC relative.  */
634 	  dst = msp430dis_opcode (addr + 2, info);
635 	  cmd_len += 2;
636 	  (*cycles)++;
637 	  sprintf (op1, "0x%04x", PS (dst));
638 	  sprintf (comm1, "PC rel. 0x%04x",
639 		   PS ((short) addr + 2 + dst));
640 	}
641       else if (regs == 2)
642 	{
643 	  /* Absolute.  */
644 	  dst = msp430dis_opcode (addr + 2, info);
645 	  cmd_len += 2;
646 	  sprintf (op1, "&0x%04x", PS (dst));
647 	}
648       else if (regs == 3)
649 	{
650 	  (*cycles)--;
651 	  sprintf (op1, "#1");
652 	  sprintf (comm1, "r3 As==01");
653 	}
654       else
655 	{
656 	  /* Indexd.  */
657 	  dst = msp430dis_opcode (addr + 2, info);
658 	  cmd_len += 2;
659 	  sprintf (op1, "%d(r%d)", dst, regs);
660 	}
661     }
662 
663   return cmd_len;
664 }
665 
666 int
667 print_insn_msp430 (bfd_vma addr, disassemble_info *info)
668 {
669   void *stream = info->stream;
670   fprintf_ftype prin = info->fprintf_func;
671   struct msp430_opcode_s *opcode;
672   char op1[32], op2[32], comm1[64], comm2[64];
673   int cmd_len = 0;
674   unsigned short insn;
675   int cycles = 0;
676   char *bc = "";
677   char dinfo[32];		/* Debug purposes.  */
678 
679   insn = msp430dis_opcode (addr, info);
680   sprintf (dinfo, "0x%04x", insn);
681 
682   if (((int) addr & 0xffff) > 0xffdf)
683     {
684       (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
685       return 2;
686     }
687 
688   *comm1 = 0;
689   *comm2 = 0;
690 
691   for (opcode = msp430_opcodes; opcode->name; opcode++)
692     {
693       if ((insn & opcode->bin_mask) == opcode->bin_opcode
694 	  && opcode->bin_opcode != 0x9300)
695 	{
696 	  *op1 = 0;
697 	  *op2 = 0;
698 	  *comm1 = 0;
699 	  *comm2 = 0;
700 
701 	  /* r0 as destination. Ad should be zero.  */
702 	  if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0
703 	      && (0x0080 & insn) == 0)
704 	    {
705 	      cmd_len =
706 		msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
707 				    &cycles);
708 	      if (cmd_len)
709 		break;
710 	    }
711 
712 	  switch (opcode->insn_opnumb)
713 	    {
714 	    case 0:
715 	      cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles);
716 	      break;
717 	    case 2:
718 	      cmd_len =
719 		msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
720 				      comm1, comm2, &cycles);
721 	      if (insn & BYTE_OPERATION)
722 		bc = ".b";
723 	      break;
724 	    case 1:
725 	      cmd_len =
726 		msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
727 				      &cycles);
728 	      if (insn & BYTE_OPERATION && opcode->fmt != 3)
729 		bc = ".b";
730 	      break;
731 	    default:
732 	      break;
733 	    }
734 	}
735 
736       if (cmd_len)
737 	break;
738     }
739 
740   dinfo[5] = 0;
741 
742   if (cmd_len < 1)
743     {
744       /* Unknown opcode, or invalid combination of operands.  */
745       (*prin) (stream, ".word	0x%04x;	????", PS (insn));
746       return 2;
747     }
748 
749   (*prin) (stream, "%s%s", opcode->name, bc);
750 
751   if (*op1)
752     (*prin) (stream, "\t%s", op1);
753   if (*op2)
754     (*prin) (stream, ",");
755 
756   if (strlen (op1) < 7)
757     (*prin) (stream, "\t");
758   if (!strlen (op1))
759     (*prin) (stream, "\t");
760 
761   if (*op2)
762     (*prin) (stream, "%s", op2);
763   if (strlen (op2) < 8)
764     (*prin) (stream, "\t");
765 
766   if (*comm1 || *comm2)
767     (*prin) (stream, ";");
768   else if (cycles)
769     {
770       if (*op2)
771 	(*prin) (stream, ";");
772       else
773 	{
774 	  if (strlen (op1) < 7)
775 	    (*prin) (stream, ";");
776 	  else
777 	    (*prin) (stream, "\t;");
778 	}
779     }
780   if (*comm1)
781     (*prin) (stream, "%s", comm1);
782   if (*comm1 && *comm2)
783     (*prin) (stream, ",");
784   if (*comm2)
785     (*prin) (stream, " %s", comm2);
786   return cmd_len;
787 }
788