include/
[external/binutils.git] / opcodes / msp430-dis.c
1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002, 2004, 2005, 2007, 2009 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 }