OSDN Git Service

* ppc-opc.c: (powerpc_opcodes): Enable icswx for POWER7.
[pf3gnuchains/pf3gnuchains4x.git] / opcodes / msp430-dis.c
1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002, 2004, 2005, 2007, 2009, 2010
3    Free Software Foundation, Inc.
4    
5    Contributed by Dmitry Diky <diwil@mail.ru>
6         
7    This file is part of the GNU opcodes library.
8
9    This library is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3, or (at your option)
12    any later version.
13
14    It is distributed in the hope that it will be useful, but WITHOUT
15    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17    License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
22    MA 02110-1301, USA.  */
23
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <sys/types.h>
28
29 #include "dis-asm.h"
30 #include "opintl.h"
31 #include "libiberty.h"
32
33 #define DASM_SECTION
34 #include "opcode/msp430.h"
35 #undef DASM_SECTION
36
37
38 #define PS(x)   (0xffff & (x))
39
40 static unsigned short
41 msp430dis_opcode (bfd_vma addr, disassemble_info *info)
42 {
43   bfd_byte buffer[2];
44   int status;
45
46   status = info->read_memory_func (addr, buffer, 2, info);
47   if (status != 0)
48     {
49       info->memory_error_func (status, addr, info);
50       return -1;
51     }
52   return bfd_getl16 (buffer);
53 }
54
55 static int
56 msp430_nooperands (struct msp430_opcode_s *opcode,
57                    bfd_vma addr ATTRIBUTE_UNUSED,
58                    unsigned short insn ATTRIBUTE_UNUSED,
59                    char *comm,
60                    int *cycles)
61 {
62   /* Pop with constant.  */
63   if (insn == 0x43b2)
64     return 0;
65   if (insn == opcode->bin_opcode)
66     return 2;
67
68   if (opcode->fmt == 0)
69     {
70       if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
71         return 0;
72
73       strcpy (comm, "emulated...");
74       *cycles = 1;
75     }
76   else
77     {
78       strcpy (comm, "return from interupt");
79       *cycles = 5;
80     }
81
82   return 2;
83 }
84
85 static int
86 msp430_singleoperand (disassemble_info *info,
87                       struct msp430_opcode_s *opcode,
88                       bfd_vma addr,
89                       unsigned short insn,
90                       char *op,
91                       char *comm,
92                       int *cycles)
93 {
94   int regs = 0, regd = 0;
95   int ad = 0, as = 0;
96   int where = 0;
97   int cmd_len = 2;
98   short dst = 0;
99
100   regd = insn & 0x0f;
101   regs = (insn & 0x0f00) >> 8;
102   as = (insn & 0x0030) >> 4;
103   ad = (insn & 0x0080) >> 7;
104
105   switch (opcode->fmt)
106     {
107     case 0:                     /* Emulated work with dst register.  */
108       if (regs != 2 && regs != 3 && regs != 1)
109         return 0;
110
111       /* Check if not clr insn.  */
112       if (opcode->bin_opcode == 0x4300 && (ad || as))
113         return 0;
114
115       /* Check if really inc, incd insns.  */
116       if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
117         return 0;
118
119       if (ad == 0)
120         {
121           *cycles = 1;
122
123           /* Register.  */
124           if (regd == 0)
125             {
126               *cycles += 1;
127               sprintf (op, "r0");
128             }
129           else if (regd == 1)
130             sprintf (op, "r1");
131
132           else if (regd == 2)
133             sprintf (op, "r2");
134
135           else
136             sprintf (op, "r%d", regd);
137         }
138       else      /* ad == 1 msp430dis_opcode.  */
139         {
140           if (regd == 0)
141             {
142               /* PC relative.  */
143               dst = msp430dis_opcode (addr + 2, info);
144               cmd_len += 2;
145               *cycles = 4;
146               sprintf (op, "0x%04x", dst);
147               sprintf (comm, "PC rel. abs addr 0x%04x",
148                        PS ((short) (addr + 2) + dst));
149             }
150           else if (regd == 2)
151             {
152               /* Absolute.  */
153               dst = msp430dis_opcode (addr + 2, info);
154               cmd_len += 2;
155               *cycles = 4;
156               sprintf (op, "&0x%04x", PS (dst));
157             }
158           else
159             {
160               dst = msp430dis_opcode (addr + 2, info);
161               cmd_len += 2;
162               *cycles = 4;
163               sprintf (op, "%d(r%d)", dst, regd);
164             }
165         }
166       break;
167
168     case 2:     /* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
169       if (as == 0)
170         {
171           if (regd == 3)
172             {
173               /* Constsnts.  */
174               sprintf (op, "#0");
175               sprintf (comm, "r3 As==00");
176             }
177           else
178             {
179               /* Register.  */
180               sprintf (op, "r%d", regd);
181             }
182           *cycles = 1;
183         }
184       else if (as == 2)
185         {
186           *cycles = 1;
187           if (regd == 2)
188             {
189               sprintf (op, "#4");
190               sprintf (comm, "r2 As==10");
191             }
192           else if (regd == 3)
193             {
194               sprintf (op, "#2");
195               sprintf (comm, "r3 As==10");
196             }
197           else
198             {
199               *cycles = 3;
200               /* Indexed register mode @Rn.  */
201               sprintf (op, "@r%d", regd);
202             }
203         }
204       else if (as == 3)
205         {
206           *cycles = 1;
207           if (regd == 2)
208             {
209               sprintf (op, "#8");
210               sprintf (comm, "r2 As==11");
211             }
212           else if (regd == 3)
213             {
214               sprintf (op, "#-1");
215               sprintf (comm, "r3 As==11");
216             }
217           else if (regd == 0)
218             {
219               *cycles = 3;
220               /* absolute. @pc+ */
221               dst = msp430dis_opcode (addr + 2, info);
222               cmd_len += 2;
223               sprintf (op, "#%d", dst);
224               sprintf (comm, "#0x%04x", PS (dst));
225             }
226           else
227             {
228               *cycles = 3;
229               sprintf (op, "@r%d+", regd);
230             }
231         }
232       else if (as == 1)
233         {
234           *cycles = 4;
235           if (regd == 0)
236             {
237               /* PC relative.  */
238               dst = msp430dis_opcode (addr + 2, info);
239               cmd_len += 2;
240               sprintf (op, "0x%04x", PS (dst));
241               sprintf (comm, "PC rel. 0x%04x",
242                        PS ((short) addr + 2 + dst));
243             }
244           else if (regd == 2)
245             {
246               /* Absolute.  */
247               dst = msp430dis_opcode (addr + 2, info);
248               cmd_len += 2;
249               sprintf (op, "&0x%04x", PS (dst));
250             }
251           else if (regd == 3)
252             {
253               *cycles = 1;
254               sprintf (op, "#1");
255               sprintf (comm, "r3 As==01");
256             }
257           else
258             {
259               /* Indexd.  */
260               dst = msp430dis_opcode (addr + 2, info);
261               cmd_len += 2;
262               sprintf (op, "%d(r%d)", dst, regd);
263             }
264         }
265       break;
266
267     case 3:                     /* Jumps.  */
268       where = insn & 0x03ff;
269       if (where & 0x200)
270         where |= ~0x03ff;
271       if (where > 512 || where < -511)
272         return 0;
273
274       where *= 2;
275       sprintf (op, "$%+-8d", where + 2);
276       sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
277       *cycles = 2;
278       return 2;
279       break;
280     default:
281       cmd_len = 0;
282     }
283
284   return cmd_len;
285 }
286
287 static int
288 msp430_doubleoperand (disassemble_info *info,
289                       struct msp430_opcode_s *opcode,
290                       bfd_vma addr,
291                       unsigned short insn,
292                       char *op1,
293                       char *op2,
294                       char *comm1,
295                       char *comm2,
296                       int *cycles)
297 {
298   int regs = 0, regd = 0;
299   int ad = 0, as = 0;
300   int cmd_len = 2;
301   short dst = 0;
302
303   regd = insn & 0x0f;
304   regs = (insn & 0x0f00) >> 8;
305   as = (insn & 0x0030) >> 4;
306   ad = (insn & 0x0080) >> 7;
307
308   if (opcode->fmt == 0)
309     {
310       /* Special case: rla and rlc are the only 2 emulated instructions that
311          fall into two operand instructions.  */
312       /* With dst, there are only:
313          Rm             Register,
314          x(Rm)          Indexed,
315          0xXXXX         Relative,
316          &0xXXXX        Absolute 
317          emulated_ins   dst
318          basic_ins      dst, dst.  */
319
320       if (regd != regs || as != ad)
321         return 0;               /* May be 'data' section.  */
322
323       if (ad == 0)
324         {
325           /* Register mode.  */
326           if (regd == 3)
327             {
328               strcpy (comm1, _("Illegal as emulation instr"));
329               return -1;
330             }
331
332           sprintf (op1, "r%d", regd);
333           *cycles = 1;
334         }
335       else                      /* ad == 1 */
336         {
337           if (regd == 0)
338             {
339               /* PC relative, Symbolic.  */
340               dst = msp430dis_opcode (addr + 2, info);
341               cmd_len += 4;
342               *cycles = 6;
343               sprintf (op1, "0x%04x", PS (dst));
344               sprintf (comm1, "PC rel. 0x%04x",
345                        PS ((short) addr + 2 + dst));
346
347             }
348           else if (regd == 2)
349             {
350               /* Absolute.  */
351               dst = msp430dis_opcode (addr + 2, info);
352               /* If the 'src' field is not the same as the dst
353                  then this is not an rla instruction.  */
354               if (dst != msp430dis_opcode (addr + 4, info))
355                 return 0;
356               cmd_len += 4;
357               *cycles = 6;
358               sprintf (op1, "&0x%04x", PS (dst));
359             }
360           else
361             {
362               /* Indexed.  */
363               dst = msp430dis_opcode (addr + 2, info);
364               cmd_len += 4;
365               *cycles = 6;
366               sprintf (op1, "%d(r%d)", dst, regd);
367             }
368         }
369
370       *op2 = 0;
371       *comm2 = 0;
372       return cmd_len;
373     }
374
375   /* Two operands exactly.  */
376   if (ad == 0 && regd == 3)
377     {
378       /* R2/R3 are illegal as dest: may be data section.  */
379       strcpy (comm1, _("Illegal as 2-op instr"));
380       return -1;
381     }
382
383   /* Source.  */
384   if (as == 0)
385     {
386       *cycles = 1;
387       if (regs == 3)
388         {
389           /* Constsnts.  */
390           sprintf (op1, "#0");
391           sprintf (comm1, "r3 As==00");
392         }
393       else
394         {
395           /* Register.  */
396           sprintf (op1, "r%d", regs);
397         }
398     }
399   else if (as == 2)
400     {
401       *cycles = 1;
402
403       if (regs == 2)
404         {
405           sprintf (op1, "#4");
406           sprintf (comm1, "r2 As==10");
407         }
408       else if (regs == 3)
409         {
410           sprintf (op1, "#2");
411           sprintf (comm1, "r3 As==10");
412         }
413       else
414         {
415           *cycles = 2;
416
417           /* Indexed register mode @Rn.  */
418           sprintf (op1, "@r%d", regs);
419         }
420       if (!regs)
421         *cycles = 3;
422     }
423   else if (as == 3)
424     {
425       if (regs == 2)
426         {
427           sprintf (op1, "#8");
428           sprintf (comm1, "r2 As==11");
429           *cycles = 1;
430         }
431       else if (regs == 3)
432         {
433           sprintf (op1, "#-1");
434           sprintf (comm1, "r3 As==11");
435           *cycles = 1;
436         }
437       else if (regs == 0)
438         {
439           *cycles = 3;
440           /* Absolute. @pc+.  */
441           dst = msp430dis_opcode (addr + 2, info);
442           cmd_len += 2;
443           sprintf (op1, "#%d", dst);
444           sprintf (comm1, "#0x%04x", PS (dst));
445         }
446       else
447         {
448           *cycles = 2;
449           sprintf (op1, "@r%d+", regs);
450         }
451     }
452   else if (as == 1)
453     {
454       if (regs == 0)
455         {
456           *cycles = 4;
457           /* PC relative.  */
458           dst = msp430dis_opcode (addr + 2, info);
459           cmd_len += 2;
460           sprintf (op1, "0x%04x", PS (dst));
461           sprintf (comm1, "PC rel. 0x%04x",
462                    PS ((short) addr + 2 + dst));
463         }
464       else if (regs == 2)
465         {
466           *cycles = 2;
467           /* Absolute.  */
468           dst = msp430dis_opcode (addr + 2, info);
469           cmd_len += 2;
470           sprintf (op1, "&0x%04x", PS (dst));
471           sprintf (comm1, "0x%04x", PS (dst));
472         }
473       else if (regs == 3)
474         {
475           *cycles = 1;
476           sprintf (op1, "#1");
477           sprintf (comm1, "r3 As==01");
478         }
479       else
480         {
481           *cycles = 3;
482           /* Indexed.  */
483           dst = msp430dis_opcode (addr + 2, info);
484           cmd_len += 2;
485           sprintf (op1, "%d(r%d)", dst, regs);
486         }
487     }
488
489   /* Destination. Special care needed on addr + XXXX.  */
490
491   if (ad == 0)
492     {
493       /* Register.  */
494       if (regd == 0)
495         {
496           *cycles += 1;
497           sprintf (op2, "r0");
498         }
499       else if (regd == 1)
500         sprintf (op2, "r1");
501
502       else if (regd == 2)
503         sprintf (op2, "r2");
504
505       else
506         sprintf (op2, "r%d", regd);
507     }
508   else  /* ad == 1.  */
509     {
510       * cycles += 3;
511
512       if (regd == 0)
513         {
514           /* PC relative.  */
515           *cycles += 1;
516           dst = msp430dis_opcode (addr + cmd_len, info);
517           sprintf (op2, "0x%04x", PS (dst));
518           sprintf (comm2, "PC rel. 0x%04x",
519                    PS ((short) addr + cmd_len + dst));
520           cmd_len += 2;
521         }
522       else if (regd == 2)
523         {
524           /* Absolute.  */
525           dst = msp430dis_opcode (addr + cmd_len, info);
526           cmd_len += 2;
527           sprintf (op2, "&0x%04x", PS (dst));
528         }
529       else
530         {
531           dst = msp430dis_opcode (addr + cmd_len, info);
532           cmd_len += 2;
533           sprintf (op2, "%d(r%d)", dst, regd);
534         }
535     }
536
537   return cmd_len;
538 }
539
540 static int
541 msp430_branchinstr (disassemble_info *info,
542                     struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
543                     bfd_vma addr ATTRIBUTE_UNUSED,
544                     unsigned short insn,
545                     char *op1,
546                     char *comm1,
547                     int *cycles)
548 {
549   int regs = 0, regd = 0;
550   int as = 0;
551   int cmd_len = 2;
552   short dst = 0;
553
554   regd = insn & 0x0f;
555   regs = (insn & 0x0f00) >> 8;
556   as = (insn & 0x0030) >> 4;
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 }