Update the address and phone number of the FSF
[external/binutils.git] / opcodes / msp430-dis.c
1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002, 2004 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, MA 02110-1301, 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               /* If the 'src' field is not the same as the dst
495                  then this is not an rla instruction.  */
496               if (dst != msp430dis_opcode (addr + 4, info))
497                 return 0;
498               cmd_len += 4;
499               *cycles = 6;
500               sprintf (op1, "&0x%04x", PS (dst));
501             }
502           else
503             {
504               /* Indexed.  */
505               dst = msp430dis_opcode (addr + 2, info);
506               cmd_len += 4;
507               *cycles = 6;
508               sprintf (op1, "%d(r%d)", dst, regd);
509             }
510         }
511
512       *op2 = 0;
513       *comm2 = 0;
514       return cmd_len;
515     }
516
517   /* Two operands exactly.  */
518   if (ad == 0 && regd == 3)
519     {
520       /* R2/R3 are illegal as dest: may be data section.  */
521       strcpy (comm1, "Illegal as 2-op instr");
522       return -1;
523     }
524
525   /* Source.  */
526   if (as == 0)
527     {
528       *cycles = 1;
529       if (regs == 3)
530         {
531           /* Constsnts.  */
532           sprintf (op1, "#0");
533           sprintf (comm1, "r3 As==00");
534         }
535       else
536         {
537           /* Register.  */
538           sprintf (op1, "r%d", regs);
539         }
540     }
541   else if (as == 2)
542     {
543       *cycles = 1;
544
545       if (regs == 2)
546         {
547           sprintf (op1, "#4");
548           sprintf (comm1, "r2 As==10");
549         }
550       else if (regs == 3)
551         {
552           sprintf (op1, "#2");
553           sprintf (comm1, "r3 As==10");
554         }
555       else
556         {
557           *cycles = 2;
558
559           /* Indexed register mode @Rn.  */
560           sprintf (op1, "@r%d", regs);
561         }
562       if (!regs)
563         *cycles = 3;
564     }
565   else if (as == 3)
566     {
567       if (regs == 2)
568         {
569           sprintf (op1, "#8");
570           sprintf (comm1, "r2 As==11");
571           *cycles = 1;
572         }
573       else if (regs == 3)
574         {
575           sprintf (op1, "#-1");
576           sprintf (comm1, "r3 As==11");
577           *cycles = 1;
578         }
579       else if (regs == 0)
580         {
581           *cycles = 3;
582           /* Absolute. @pc+  */
583           dst = msp430dis_opcode (addr + 2, info);
584           cmd_len += 2;
585           sprintf (op1, "#%d", dst);
586           sprintf (comm1, "#0x%04x", PS (dst));
587         }
588       else
589         {
590           *cycles = 2;
591           sprintf (op1, "@r%d+", regs);
592         }
593     }
594   else if (as == 1)
595     {
596       if (regs == 0)
597         {
598           *cycles = 4;
599           /* PC relative.  */
600           dst = msp430dis_opcode (addr + 2, info);
601           cmd_len += 2;
602           sprintf (op1, "0x%04x", PS (dst));
603           sprintf (comm1, "PC rel. 0x%04x",
604                    PS ((short) addr + 2 + dst));
605         }
606       else if (regs == 2)
607         {
608           *cycles = 2;
609           /* Absolute.  */
610           dst = msp430dis_opcode (addr + 2, info);
611           cmd_len += 2;
612           sprintf (op1, "&0x%04x", PS (dst));
613           sprintf (comm1, "0x%04x", PS (dst));
614         }
615       else if (regs == 3)
616         {
617           *cycles = 1;
618           sprintf (op1, "#1");
619           sprintf (comm1, "r3 As==01");
620         }
621       else
622         {
623           *cycles = 3;
624           /* Indexed.  */
625           dst = msp430dis_opcode (addr + 2, info);
626           cmd_len += 2;
627           sprintf (op1, "%d(r%d)", dst, regs);
628         }
629     }
630
631   /* Destination. Special care needed on addr + XXXX.  */
632
633   if (ad == 0)
634     {
635       /* Register.  */
636       if (regd == 0)
637         {
638           *cycles += 1;
639           sprintf (op2, "r0");
640         }
641       else if (regd == 1)
642         sprintf (op2, "r1");
643
644       else if (regd == 2)
645         sprintf (op2, "r2");
646
647       else
648         sprintf (op2, "r%d", regd);
649     }
650   else                          /* ad == 1.  */
651     {
652       * cycles += 3;
653
654       if (regd == 0)
655         {
656           /* PC relative.  */
657           *cycles += 1;
658           dst = msp430dis_opcode (addr + cmd_len, info);
659           sprintf (op2, "0x%04x", PS (dst));
660           sprintf (comm2, "PC rel. 0x%04x",
661                    PS ((short) addr + cmd_len + dst));
662           cmd_len += 2;
663         }
664       else if (regd == 2)
665         {
666           /* Absolute.  */
667           dst = msp430dis_opcode (addr + cmd_len, info);
668           cmd_len += 2;
669           sprintf (op2, "&0x%04x", PS (dst));
670         }
671       else
672         {
673           dst = msp430dis_opcode (addr + cmd_len, info);
674           cmd_len += 2;
675           sprintf (op2, "%d(r%d)", dst, regd);
676         }
677     }
678
679   return cmd_len;
680 }
681
682
683 int
684 msp430_branchinstr (info, opcode, addr, insn, op1, comm1, cycles)
685      disassemble_info *info;
686      struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED;
687      bfd_vma addr ATTRIBUTE_UNUSED;
688      unsigned short insn;
689      char *op1;
690      char *comm1;
691      int *cycles;
692 {
693   int regs = 0, regd = 0;
694   int ad = 0, as = 0;
695   int cmd_len = 2;
696   short dst = 0;
697
698   regd = insn & 0x0f;
699   regs = (insn & 0x0f00) >> 8;
700   as = (insn & 0x0030) >> 4;
701   ad = (insn & 0x0080) >> 7;
702
703   if (regd != 0)        /* Destination register is not a PC.  */
704     return 0;
705
706   /* dst is a source register.  */
707   if (as == 0)
708     {
709       /* Constants.  */
710       if (regs == 3)
711         {
712           *cycles = 1;
713           sprintf (op1, "#0");
714           sprintf (comm1, "r3 As==00");
715         }
716       else
717         {
718           /* Register.  */
719           *cycles = 1;
720           sprintf (op1, "r%d", regs);
721         }
722     }
723   else if (as == 2)
724     {
725       if (regs == 2)
726         {
727           *cycles = 2;
728           sprintf (op1, "#4");
729           sprintf (comm1, "r2 As==10");
730         }
731       else if (regs == 3)
732         {
733           *cycles = 1;
734           sprintf (op1, "#2");
735           sprintf (comm1, "r3 As==10");
736         }
737       else
738         {
739           /* Indexed register mode @Rn.  */
740           *cycles = 2;
741           sprintf (op1, "@r%d", regs);
742         }
743     }
744   else if (as == 3)
745     {
746       if (regs == 2)
747         {
748           *cycles = 1;
749           sprintf (op1, "#8");
750           sprintf (comm1, "r2 As==11");
751         }
752       else if (regs == 3)
753         {
754           *cycles = 1;
755           sprintf (op1, "#-1");
756           sprintf (comm1, "r3 As==11");
757         }
758       else if (regs == 0)
759         {
760           /* Absolute. @pc+  */
761           *cycles = 3;
762           dst = msp430dis_opcode (addr + 2, info);
763           cmd_len += 2;
764           sprintf (op1, "#0x%04x", PS (dst));
765         }
766       else
767         {
768           *cycles = 2;
769           sprintf (op1, "@r%d+", regs);
770         }
771     }
772   else if (as == 1)
773     {
774       * cycles = 3;
775
776       if (regs == 0)
777         {
778           /* PC relative.  */
779           dst = msp430dis_opcode (addr + 2, info);
780           cmd_len += 2;
781           (*cycles)++;
782           sprintf (op1, "0x%04x", PS (dst));
783           sprintf (comm1, "PC rel. 0x%04x",
784                    PS ((short) addr + 2 + dst));
785         }
786       else if (regs == 2)
787         {
788           /* Absolute.  */
789           dst = msp430dis_opcode (addr + 2, info);
790           cmd_len += 2;
791           sprintf (op1, "&0x%04x", PS (dst));
792         }
793       else if (regs == 3)
794         {
795           (*cycles)--;
796           sprintf (op1, "#1");
797           sprintf (comm1, "r3 As==01");
798         }
799       else
800         {
801           /* Indexd.  */
802           dst = msp430dis_opcode (addr + 2, info);
803           cmd_len += 2;
804           sprintf (op1, "%d(r%d)", dst, regs);
805         }
806     }
807
808   return cmd_len;
809 }