Add support for msp430.
[deliverable/binutils-gdb.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 }
This page took 0.045146 seconds and 5 git commands to generate.