1 /* Disassemble MSP430 instructions. 2 Copyright (C) 2002, 2004 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 /* 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; 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 683 int 684 msp430_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 } 810