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