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