1 /* Disassemble MSP430 instructions. 2 Copyright (C) 2002, 2004, 2005, 2007 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 } 788