Add PR binutils/3000 to its entry.
[deliverable/binutils-gdb.git] / opcodes / msp430-dis.c
CommitLineData
2469cfa2 1/* Disassemble MSP430 instructions.
47b0e7ad 2 Copyright (C) 2002, 2004, 2005 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
47b0e7ad
NC
18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
2469cfa2
NC
20
21#include <stdio.h>
22#include <ctype.h>
23#include <string.h>
24#include <sys/types.h>
25
26#include "dis-asm.h"
27#include "opintl.h"
28#include "libiberty.h"
29
30#define DASM_SECTION
31#include "opcode/msp430.h"
32#undef DASM_SECTION
33
34
2469cfa2
NC
35#define PS(x) (0xffff & (x))
36
37static unsigned short
47b0e7ad 38msp430dis_opcode (bfd_vma addr, disassemble_info *info)
2469cfa2
NC
39{
40 bfd_byte buffer[2];
41 int status;
42
43 status = info->read_memory_func (addr, buffer, 2, info);
44 if (status != 0)
45 {
46 info->memory_error_func (status, addr, info);
47 return -1;
48 }
49 return bfd_getl16 (buffer);
50}
51
47b0e7ad
NC
52static int
53msp430_nooperands (struct msp430_opcode_s *opcode,
54 bfd_vma addr ATTRIBUTE_UNUSED,
55 unsigned short insn ATTRIBUTE_UNUSED,
56 char *comm,
57 int *cycles)
2469cfa2
NC
58{
59 /* Pop with constant. */
60 if (insn == 0x43b2)
61 return 0;
62 if (insn == opcode->bin_opcode)
63 return 2;
64
65 if (opcode->fmt == 0)
66 {
67 if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
68 return 0;
69
70 strcpy (comm, "emulated...");
71 *cycles = 1;
72 }
73 else
74 {
75 strcpy (comm, "return from interupt");
76 *cycles = 5;
77 }
78
79 return 2;
80}
81
47b0e7ad
NC
82static int
83msp430_singleoperand (disassemble_info *info,
84 struct msp430_opcode_s *opcode,
85 bfd_vma addr,
86 unsigned short insn,
87 char *op,
88 char *comm,
89 int *cycles)
2469cfa2
NC
90{
91 int regs = 0, regd = 0;
92 int ad = 0, as = 0;
93 int where = 0;
94 int cmd_len = 2;
95 short dst = 0;
96
97 regd = insn & 0x0f;
98 regs = (insn & 0x0f00) >> 8;
99 as = (insn & 0x0030) >> 4;
100 ad = (insn & 0x0080) >> 7;
101
102 switch (opcode->fmt)
103 {
104 case 0: /* Emulated work with dst register. */
105 if (regs != 2 && regs != 3 && regs != 1)
106 return 0;
107
108 /* Check if not clr insn. */
109 if (opcode->bin_opcode == 0x4300 && (ad || as))
110 return 0;
111
112 /* Check if really inc, incd insns. */
113 if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
114 return 0;
115
116 if (ad == 0)
117 {
118 *cycles = 1;
119
120 /* Register. */
121 if (regd == 0)
122 {
123 *cycles += 1;
124 sprintf (op, "r0");
125 }
126 else if (regd == 1)
127 sprintf (op, "r1");
128
129 else if (regd == 2)
130 sprintf (op, "r2");
131
132 else
133 sprintf (op, "r%d", regd);
134 }
47b0e7ad 135 else /* ad == 1 msp430dis_opcode. */
2469cfa2
NC
136 {
137 if (regd == 0)
138 {
139 /* PC relative. */
140 dst = msp430dis_opcode (addr + 2, info);
141 cmd_len += 2;
142 *cycles = 4;
143 sprintf (op, "0x%04x", dst);
144 sprintf (comm, "PC rel. abs addr 0x%04x",
145 PS ((short) (addr + 2) + dst));
146 }
147 else if (regd == 2)
148 {
149 /* Absolute. */
150 dst = msp430dis_opcode (addr + 2, info);
151 cmd_len += 2;
152 *cycles = 4;
153 sprintf (op, "&0x%04x", PS (dst));
154 }
155 else
156 {
157 dst = msp430dis_opcode (addr + 2, info);
158 cmd_len += 2;
159 *cycles = 4;
160 sprintf (op, "%d(r%d)", dst, regd);
161 }
162 }
163 break;
164
165 case 2: /* rrc, push, call, swpb, rra, sxt, push, call, reti etc... */
2469cfa2
NC
166 if (as == 0)
167 {
168 if (regd == 3)
169 {
170 /* Constsnts. */
171 sprintf (op, "#0");
172 sprintf (comm, "r3 As==00");
173 }
174 else
175 {
176 /* Register. */
177 sprintf (op, "r%d", regd);
178 }
179 *cycles = 1;
180 }
181 else if (as == 2)
182 {
183 *cycles = 1;
184 if (regd == 2)
185 {
186 sprintf (op, "#4");
187 sprintf (comm, "r2 As==10");
188 }
189 else if (regd == 3)
190 {
191 sprintf (op, "#2");
192 sprintf (comm, "r3 As==10");
193 }
194 else
195 {
196 *cycles = 3;
197 /* Indexed register mode @Rn. */
198 sprintf (op, "@r%d", regd);
199 }
200 }
201 else if (as == 3)
202 {
203 *cycles = 1;
204 if (regd == 2)
205 {
206 sprintf (op, "#8");
207 sprintf (comm, "r2 As==11");
208 }
209 else if (regd == 3)
210 {
211 sprintf (op, "#-1");
212 sprintf (comm, "r3 As==11");
213 }
214 else if (regd == 0)
215 {
216 *cycles = 3;
217 /* absolute. @pc+ */
218 dst = msp430dis_opcode (addr + 2, info);
219 cmd_len += 2;
220 sprintf (op, "#%d", dst);
221 sprintf (comm, "#0x%04x", PS (dst));
222 }
223 else
224 {
225 *cycles = 3;
226 sprintf (op, "@r%d+", regd);
227 }
228 }
229 else if (as == 1)
230 {
231 *cycles = 4;
232 if (regd == 0)
233 {
234 /* PC relative. */
235 dst = msp430dis_opcode (addr + 2, info);
236 cmd_len += 2;
237 sprintf (op, "0x%04x", PS (dst));
238 sprintf (comm, "PC rel. 0x%04x",
239 PS ((short) addr + 2 + dst));
240 }
241 else if (regd == 2)
242 {
243 /* Absolute. */
244 dst = msp430dis_opcode (addr + 2, info);
245 cmd_len += 2;
246 sprintf (op, "&0x%04x", PS (dst));
247 }
248 else if (regd == 3)
249 {
250 *cycles = 1;
251 sprintf (op, "#1");
252 sprintf (comm, "r3 As==01");
253 }
254 else
255 {
256 /* Indexd. */
257 dst = msp430dis_opcode (addr + 2, info);
258 cmd_len += 2;
259 sprintf (op, "%d(r%d)", dst, regd);
260 }
261 }
262 break;
263
264 case 3: /* Jumps. */
265 where = insn & 0x03ff;
266 if (where & 0x200)
267 where |= ~0x03ff;
268 if (where > 512 || where < -511)
269 return 0;
270
271 where *= 2;
272 sprintf (op, "$%+-8d", where + 2);
273 sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
274 *cycles = 2;
275 return 2;
276 break;
277 default:
278 cmd_len = 0;
279 }
280
281 return cmd_len;
282}
283
47b0e7ad
NC
284static int
285msp430_doubleoperand (disassemble_info *info,
286 struct msp430_opcode_s *opcode,
287 bfd_vma addr,
288 unsigned short insn,
289 char *op1,
290 char *op2,
291 char *comm1,
292 char *comm2,
293 int *cycles)
2469cfa2
NC
294{
295 int regs = 0, regd = 0;
296 int ad = 0, as = 0;
297 int cmd_len = 2;
298 short dst = 0;
299
300 regd = insn & 0x0f;
301 regs = (insn & 0x0f00) >> 8;
302 as = (insn & 0x0030) >> 4;
303 ad = (insn & 0x0080) >> 7;
304
305 if (opcode->fmt == 0)
306 {
307 /* Special case: rla and rlc are the only 2 emulated instructions that
308 fall into two operand instructions. */
309 /* With dst, there are only:
310 Rm Register,
311 x(Rm) Indexed,
312 0xXXXX Relative,
313 &0xXXXX Absolute
314 emulated_ins dst
315 basic_ins dst, dst. */
316
317 if (regd != regs || as != ad)
318 return 0; /* May be 'data' section. */
319
320 if (ad == 0)
321 {
322 /* Register mode. */
323 if (regd == 3)
324 {
47b0e7ad 325 strcpy (comm1, _("Illegal as emulation instr"));
2469cfa2
NC
326 return -1;
327 }
328
329 sprintf (op1, "r%d", regd);
330 *cycles = 1;
331 }
332 else /* ad == 1 */
333 {
334 if (regd == 0)
335 {
336 /* PC relative, Symbolic. */
337 dst = msp430dis_opcode (addr + 2, info);
338 cmd_len += 4;
339 *cycles = 6;
340 sprintf (op1, "0x%04x", PS (dst));
341 sprintf (comm1, "PC rel. 0x%04x",
342 PS ((short) addr + 2 + dst));
343
344 }
345 else if (regd == 2)
346 {
347 /* Absolute. */
348 dst = msp430dis_opcode (addr + 2, info);
3ab48931
NC
349 /* If the 'src' field is not the same as the dst
350 then this is not an rla instruction. */
351 if (dst != msp430dis_opcode (addr + 4, info))
352 return 0;
2469cfa2
NC
353 cmd_len += 4;
354 *cycles = 6;
355 sprintf (op1, "&0x%04x", PS (dst));
356 }
357 else
358 {
359 /* Indexed. */
360 dst = msp430dis_opcode (addr + 2, info);
361 cmd_len += 4;
362 *cycles = 6;
363 sprintf (op1, "%d(r%d)", dst, regd);
364 }
365 }
366
367 *op2 = 0;
368 *comm2 = 0;
369 return cmd_len;
370 }
371
372 /* Two operands exactly. */
373 if (ad == 0 && regd == 3)
374 {
375 /* R2/R3 are illegal as dest: may be data section. */
47b0e7ad 376 strcpy (comm1, _("Illegal as 2-op instr"));
2469cfa2
NC
377 return -1;
378 }
379
380 /* Source. */
381 if (as == 0)
382 {
383 *cycles = 1;
384 if (regs == 3)
385 {
386 /* Constsnts. */
387 sprintf (op1, "#0");
388 sprintf (comm1, "r3 As==00");
389 }
390 else
391 {
392 /* Register. */
393 sprintf (op1, "r%d", regs);
394 }
395 }
396 else if (as == 2)
397 {
398 *cycles = 1;
399
400 if (regs == 2)
401 {
402 sprintf (op1, "#4");
403 sprintf (comm1, "r2 As==10");
404 }
405 else if (regs == 3)
406 {
407 sprintf (op1, "#2");
408 sprintf (comm1, "r3 As==10");
409 }
410 else
411 {
412 *cycles = 2;
413
414 /* Indexed register mode @Rn. */
415 sprintf (op1, "@r%d", regs);
416 }
417 if (!regs)
418 *cycles = 3;
419 }
420 else if (as == 3)
421 {
422 if (regs == 2)
423 {
424 sprintf (op1, "#8");
425 sprintf (comm1, "r2 As==11");
426 *cycles = 1;
427 }
428 else if (regs == 3)
429 {
430 sprintf (op1, "#-1");
431 sprintf (comm1, "r3 As==11");
432 *cycles = 1;
433 }
434 else if (regs == 0)
435 {
436 *cycles = 3;
47b0e7ad 437 /* Absolute. @pc+. */
2469cfa2
NC
438 dst = msp430dis_opcode (addr + 2, info);
439 cmd_len += 2;
440 sprintf (op1, "#%d", dst);
441 sprintf (comm1, "#0x%04x", PS (dst));
442 }
443 else
444 {
445 *cycles = 2;
446 sprintf (op1, "@r%d+", regs);
447 }
448 }
449 else if (as == 1)
450 {
451 if (regs == 0)
452 {
453 *cycles = 4;
454 /* PC relative. */
455 dst = msp430dis_opcode (addr + 2, info);
456 cmd_len += 2;
457 sprintf (op1, "0x%04x", PS (dst));
458 sprintf (comm1, "PC rel. 0x%04x",
459 PS ((short) addr + 2 + dst));
460 }
461 else if (regs == 2)
462 {
463 *cycles = 2;
464 /* Absolute. */
465 dst = msp430dis_opcode (addr + 2, info);
466 cmd_len += 2;
467 sprintf (op1, "&0x%04x", PS (dst));
468 sprintf (comm1, "0x%04x", PS (dst));
469 }
470 else if (regs == 3)
471 {
472 *cycles = 1;
473 sprintf (op1, "#1");
474 sprintf (comm1, "r3 As==01");
475 }
476 else
477 {
478 *cycles = 3;
479 /* Indexed. */
480 dst = msp430dis_opcode (addr + 2, info);
481 cmd_len += 2;
482 sprintf (op1, "%d(r%d)", dst, regs);
483 }
484 }
485
486 /* Destination. Special care needed on addr + XXXX. */
487
488 if (ad == 0)
489 {
490 /* Register. */
491 if (regd == 0)
492 {
493 *cycles += 1;
494 sprintf (op2, "r0");
495 }
496 else if (regd == 1)
497 sprintf (op2, "r1");
498
499 else if (regd == 2)
500 sprintf (op2, "r2");
501
502 else
503 sprintf (op2, "r%d", regd);
504 }
47b0e7ad 505 else /* ad == 1. */
2469cfa2
NC
506 {
507 * cycles += 3;
508
509 if (regd == 0)
510 {
511 /* PC relative. */
512 *cycles += 1;
513 dst = msp430dis_opcode (addr + cmd_len, info);
514 sprintf (op2, "0x%04x", PS (dst));
515 sprintf (comm2, "PC rel. 0x%04x",
516 PS ((short) addr + cmd_len + dst));
517 cmd_len += 2;
518 }
519 else if (regd == 2)
520 {
521 /* Absolute. */
522 dst = msp430dis_opcode (addr + cmd_len, info);
523 cmd_len += 2;
524 sprintf (op2, "&0x%04x", PS (dst));
525 }
526 else
527 {
528 dst = msp430dis_opcode (addr + cmd_len, info);
529 cmd_len += 2;
530 sprintf (op2, "%d(r%d)", dst, regd);
531 }
532 }
533
534 return cmd_len;
535}
536
47b0e7ad
NC
537static int
538msp430_branchinstr (disassemble_info *info,
539 struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
540 bfd_vma addr ATTRIBUTE_UNUSED,
541 unsigned short insn,
542 char *op1,
543 char *comm1,
544 int *cycles)
2469cfa2
NC
545{
546 int regs = 0, regd = 0;
547 int ad = 0, as = 0;
548 int cmd_len = 2;
549 short dst = 0;
550
551 regd = insn & 0x0f;
552 regs = (insn & 0x0f00) >> 8;
553 as = (insn & 0x0030) >> 4;
554 ad = (insn & 0x0080) >> 7;
555
556 if (regd != 0) /* Destination register is not a PC. */
557 return 0;
558
559 /* dst is a source register. */
560 if (as == 0)
561 {
562 /* Constants. */
563 if (regs == 3)
564 {
565 *cycles = 1;
566 sprintf (op1, "#0");
567 sprintf (comm1, "r3 As==00");
568 }
569 else
570 {
571 /* Register. */
572 *cycles = 1;
573 sprintf (op1, "r%d", regs);
574 }
575 }
576 else if (as == 2)
577 {
578 if (regs == 2)
579 {
580 *cycles = 2;
581 sprintf (op1, "#4");
582 sprintf (comm1, "r2 As==10");
583 }
584 else if (regs == 3)
585 {
586 *cycles = 1;
587 sprintf (op1, "#2");
588 sprintf (comm1, "r3 As==10");
589 }
590 else
591 {
592 /* Indexed register mode @Rn. */
593 *cycles = 2;
594 sprintf (op1, "@r%d", regs);
595 }
596 }
597 else if (as == 3)
598 {
599 if (regs == 2)
600 {
601 *cycles = 1;
602 sprintf (op1, "#8");
603 sprintf (comm1, "r2 As==11");
604 }
605 else if (regs == 3)
606 {
607 *cycles = 1;
608 sprintf (op1, "#-1");
609 sprintf (comm1, "r3 As==11");
610 }
611 else if (regs == 0)
612 {
613 /* Absolute. @pc+ */
614 *cycles = 3;
615 dst = msp430dis_opcode (addr + 2, info);
616 cmd_len += 2;
617 sprintf (op1, "#0x%04x", PS (dst));
618 }
619 else
620 {
621 *cycles = 2;
622 sprintf (op1, "@r%d+", regs);
623 }
624 }
625 else if (as == 1)
626 {
627 * cycles = 3;
628
629 if (regs == 0)
630 {
631 /* PC relative. */
632 dst = msp430dis_opcode (addr + 2, info);
633 cmd_len += 2;
634 (*cycles)++;
635 sprintf (op1, "0x%04x", PS (dst));
636 sprintf (comm1, "PC rel. 0x%04x",
637 PS ((short) addr + 2 + dst));
638 }
639 else if (regs == 2)
640 {
641 /* Absolute. */
642 dst = msp430dis_opcode (addr + 2, info);
643 cmd_len += 2;
644 sprintf (op1, "&0x%04x", PS (dst));
645 }
646 else if (regs == 3)
647 {
648 (*cycles)--;
649 sprintf (op1, "#1");
650 sprintf (comm1, "r3 As==01");
651 }
652 else
653 {
654 /* Indexd. */
655 dst = msp430dis_opcode (addr + 2, info);
656 cmd_len += 2;
657 sprintf (op1, "%d(r%d)", dst, regs);
658 }
659 }
660
661 return cmd_len;
662}
47b0e7ad
NC
663
664int
665print_insn_msp430 (bfd_vma addr, disassemble_info *info)
666{
667 void *stream = info->stream;
668 fprintf_ftype prin = info->fprintf_func;
669 struct msp430_opcode_s *opcode;
670 char op1[32], op2[32], comm1[64], comm2[64];
671 int cmd_len = 0;
672 unsigned short insn;
673 int cycles = 0;
674 char *bc = "";
675 char dinfo[32]; /* Debug purposes. */
676
677 insn = msp430dis_opcode (addr, info);
678 sprintf (dinfo, "0x%04x", insn);
679
680 if (((int) addr & 0xffff) > 0xffdf)
681 {
682 (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
683 return 2;
684 }
685
686 *comm1 = 0;
687 *comm2 = 0;
688
689 for (opcode = msp430_opcodes; opcode->name; opcode++)
690 {
691 if ((insn & opcode->bin_mask) == opcode->bin_opcode
692 && opcode->bin_opcode != 0x9300)
693 {
694 *op1 = 0;
695 *op2 = 0;
696 *comm1 = 0;
697 *comm2 = 0;
698
699 /* r0 as destination. Ad should be zero. */
700 if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0
701 && (0x0080 & insn) == 0)
702 {
703 cmd_len =
704 msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
705 &cycles);
706 if (cmd_len)
707 break;
708 }
709
710 switch (opcode->insn_opnumb)
711 {
712 case 0:
713 cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles);
714 break;
715 case 2:
716 cmd_len =
717 msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
718 comm1, comm2, &cycles);
719 if (insn & BYTE_OPERATION)
720 bc = ".b";
721 break;
722 case 1:
723 cmd_len =
724 msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
725 &cycles);
726 if (insn & BYTE_OPERATION && opcode->fmt != 3)
727 bc = ".b";
728 break;
729 default:
730 break;
731 }
732 }
733
734 if (cmd_len)
735 break;
736 }
737
738 dinfo[5] = 0;
739
740 if (cmd_len < 1)
741 {
742 /* Unknown opcode, or invalid combination of operands. */
743 (*prin) (stream, ".word 0x%04x; ????", PS (insn));
744 return 2;
745 }
746
747 (*prin) (stream, "%s%s", opcode->name, bc);
748
749 if (*op1)
750 (*prin) (stream, "\t%s", op1);
751 if (*op2)
752 (*prin) (stream, ",");
753
754 if (strlen (op1) < 7)
755 (*prin) (stream, "\t");
756 if (!strlen (op1))
757 (*prin) (stream, "\t");
758
759 if (*op2)
760 (*prin) (stream, "%s", op2);
761 if (strlen (op2) < 8)
762 (*prin) (stream, "\t");
763
764 if (*comm1 || *comm2)
765 (*prin) (stream, ";");
766 else if (cycles)
767 {
768 if (*op2)
769 (*prin) (stream, ";");
770 else
771 {
772 if (strlen (op1) < 7)
773 (*prin) (stream, ";");
774 else
775 (*prin) (stream, "\t;");
776 }
777 }
778 if (*comm1)
779 (*prin) (stream, "%s", comm1);
780 if (*comm1 && *comm2)
781 (*prin) (stream, ",");
782 if (*comm2)
783 (*prin) (stream, " %s", comm2);
784 return cmd_len;
785}
This page took 0.211532 seconds and 4 git commands to generate.