1 /* Disassemble MSP430 instructions. 2 Copyright (C) 2002, 2004, 2005 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., 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 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 35 #define PS(x) (0xffff & (x)) 36 37 static unsigned short 38 msp430dis_opcode (bfd_vma addr, disassemble_info *info) 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 52 static int 53 msp430_nooperands (struct msp430_opcode_s *opcode, 54 bfd_vma addr ATTRIBUTE_UNUSED, 55 unsigned short insn ATTRIBUTE_UNUSED, 56 char *comm, 57 int *cycles) 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 82 static int 83 msp430_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) 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 } 135 else /* ad == 1 msp430dis_opcode. */ 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... */ 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 284 static int 285 msp430_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) 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 { 325 strcpy (comm1, _("Illegal as emulation instr")); 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); 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; 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. */ 376 strcpy (comm1, _("Illegal as 2-op instr")); 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; 437 /* Absolute. @pc+. */ 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 } 505 else /* ad == 1. */ 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 537 static int 538 msp430_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) 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 } 663 664 int 665 print_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 } 786