Update the address and phone number of the FSF
[deliverable/binutils-gdb.git] / opcodes / msp430-dis.c
CommitLineData
2469cfa2 1/* Disassemble MSP430 instructions.
3ab48931 2 Copyright (C) 2002, 2004 Free Software Foundation, Inc.
2469cfa2
NC
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
f4321104 18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
2469cfa2
NC
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
34static unsigned short msp430dis_opcode
35 PARAMS ((bfd_vma, disassemble_info *));
36int print_insn_msp430
37 PARAMS ((bfd_vma, disassemble_info *));
38int msp430_nooperands
39 PARAMS ((struct msp430_opcode_s *, bfd_vma, unsigned short, char *, int *));
40int msp430_singleoperand
41 PARAMS ((disassemble_info *, struct msp430_opcode_s *, bfd_vma, unsigned short,
42 char *, char *, int *));
43int msp430_doubleoperand
44 PARAMS ((disassemble_info *, struct msp430_opcode_s *, bfd_vma, unsigned short,
45 char *, char *, char *, char *, int *));
46int 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
52static unsigned short
53msp430dis_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
69int
70print_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
194int
195msp430_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
226int
227msp430_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
430int
431msp430_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);
3ab48931
NC
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;
2469cfa2
NC
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
683int
684msp430_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}
This page took 0.158818 seconds and 4 git commands to generate.