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