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