/* Disassemble MSP430 instructions. Copyright (C) 2002-2015 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of the GNU opcodes library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include #include #include #include "dis-asm.h" #include "opintl.h" #include "libiberty.h" #define DASM_SECTION #include "opcode/msp430.h" #undef DASM_SECTION #define PS(x) (0xffff & (x)) static unsigned short msp430dis_opcode (bfd_vma addr, disassemble_info *info) { bfd_byte buffer[2]; int status; status = info->read_memory_func (addr, buffer, 2, info); if (status != 0) { info->memory_error_func (status, addr, info); return -1; } return bfd_getl16 (buffer); } static int msp430_nooperands (struct msp430_opcode_s *opcode, bfd_vma addr ATTRIBUTE_UNUSED, unsigned short insn ATTRIBUTE_UNUSED, char *comm, int *cycles) { /* Pop with constant. */ if (insn == 0x43b2) return 0; if (insn == opcode->bin_opcode) return 2; if (opcode->fmt == 0) { if ((insn & 0x0f00) != 0x0300 || (insn & 0x0f00) != 0x0200) return 0; strcpy (comm, "emulated..."); *cycles = 1; } else { strcpy (comm, "return from interupt"); *cycles = 5; } return 2; } static int print_as2_reg_name (int regno, char * op1, char * comm1, int c2, int c3, int cd) { switch (regno) { case 2: sprintf (op1, "#4"); sprintf (comm1, "r2 As==10"); return c2; case 3: sprintf (op1, "#2"); sprintf (comm1, "r3 As==10"); return c3; default: /* Indexed register mode @Rn. */ sprintf (op1, "@r%d", regno); return cd; } } static int print_as3_reg_name (int regno, char * op1, char * comm1, int c2, int c3, int cd) { switch (regno) { case 2: sprintf (op1, "#8"); sprintf (comm1, "r2 As==11"); return c2; case 3: sprintf (op1, "#-1"); sprintf (comm1, "r3 As==11"); return c3; default: /* Post incremented @Rn+. */ sprintf (op1, "@r%d+", regno); return cd; } } static int msp430_singleoperand (disassemble_info *info, struct msp430_opcode_s *opcode, bfd_vma addr, unsigned short insn, char *op, char *comm, unsigned short extension_word, int *cycles) { int regs = 0, regd = 0; int ad = 0, as = 0; int where = 0; int cmd_len = 2; int dst = 0; int fmt; int extended_dst = extension_word & 0xf; regd = insn & 0x0f; regs = (insn & 0x0f00) >> 8; as = (insn & 0x0030) >> 4; ad = (insn & 0x0080) >> 7; if (opcode->fmt < 0) fmt = (- opcode->fmt) - 1; else fmt = opcode->fmt; switch (fmt) { case 0: /* Emulated work with dst register. */ if (regs != 2 && regs != 3 && regs != 1) return 0; /* Check if not clr insn. */ if (opcode->bin_opcode == 0x4300 && (ad || as)) return 0; /* Check if really inc, incd insns. */ if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3) return 0; if (ad == 0) { *cycles = 1; /* Register. */ if (regd == 0) { *cycles += 1; sprintf (op, "r0"); } else if (regd == 1) sprintf (op, "r1"); else if (regd == 2) sprintf (op, "r2"); else sprintf (op, "r%d", regd); } else /* ad == 1 msp430dis_opcode. */ { if (regd == 0) { /* PC relative. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; sprintf (op, "0x%04x", dst); sprintf (comm, "PC rel. abs addr 0x%04x", PS ((short) (addr + 2) + dst)); if (extended_dst) { dst |= extended_dst << 16; sprintf (op, "0x%05x", dst); sprintf (comm, "PC rel. abs addr 0x%05lx", (long)((addr + 2 + dst) & 0xfffff)); } } else if (regd == 2) { /* Absolute. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; sprintf (op, "&0x%04x", PS (dst)); if (extended_dst) { dst |= extended_dst << 16; sprintf (op, "&0x%05x", dst & 0xfffff); } } else { dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; if (extended_dst) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; } else if (dst & 0x8000) dst |= -1U << 16; sprintf (op, "%d(r%d)", dst, regd); } } break; case 2: /* rrc, push, call, swpb, rra, sxt, push, call, reti etc... */ if (as == 0) { if (regd == 3) { /* Constsnts. */ sprintf (op, "#0"); sprintf (comm, "r3 As==00"); } else { /* Register. */ sprintf (op, "r%d", regd); } *cycles = 1; } else if (as == 2) { * cycles = print_as2_reg_name (regd, op, comm, 1, 1, 3); } else if (as == 3) { if (regd == 0) { *cycles = 3; /* absolute. @pc+ */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op, "#%d", dst); if (dst > 9 || dst < 0) sprintf (comm, "#0x%04x", PS (dst)); if (extended_dst) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; sprintf (op, "#%d", dst); if (dst > 9 || dst < 0) sprintf (comm, "#0x%05x", dst); } } else * cycles = print_as3_reg_name (regd, op, comm, 1, 1, 3); } else if (as == 1) { *cycles = 4; if (regd == 0) { /* PC relative. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op, "0x%04x", PS (dst)); sprintf (comm, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); if (extended_dst) { dst |= extended_dst << 16; sprintf (op, "0x%05x", dst & 0xffff); sprintf (comm, "PC rel. 0x%05lx", (long)((addr + 2 + dst) & 0xfffff)); } } else if (regd == 2) { /* Absolute. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op, "&0x%04x", PS (dst)); if (extended_dst) { dst |= extended_dst << 16; sprintf (op, "&0x%05x", dst & 0xfffff); } } else if (regd == 3) { *cycles = 1; sprintf (op, "#1"); sprintf (comm, "r3 As==01"); } else { /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; if (extended_dst) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; } else if (dst & 0x8000) dst |= -1U << 16; sprintf (op, "%d(r%d)", dst, regd); if (dst > 9 || dst < 0) sprintf (comm, "%05x", dst); } } break; case 3: /* Jumps. */ where = insn & 0x03ff; if (where & 0x200) where |= ~0x03ff; if (where > 512 || where < -511) return 0; where *= 2; sprintf (op, "$%+-8d", where + 2); sprintf (comm, "abs 0x%lx", (long) (addr + 2 + where)); *cycles = 2; return 2; break; default: cmd_len = 0; } return cmd_len; } static int msp430_doubleoperand (disassemble_info *info, struct msp430_opcode_s *opcode, bfd_vma addr, unsigned short insn, char *op1, char *op2, char *comm1, char *comm2, unsigned short extension_word, int *cycles) { int regs = 0, regd = 0; int ad = 0, as = 0; int cmd_len = 2; int dst = 0; int fmt; int extended_dst = extension_word & 0xf; int extended_src = (extension_word >> 7) & 0xf; regd = insn & 0x0f; regs = (insn & 0x0f00) >> 8; as = (insn & 0x0030) >> 4; ad = (insn & 0x0080) >> 7; if (opcode->fmt < 0) fmt = (- opcode->fmt) - 1; else fmt = opcode->fmt; if (fmt == 0) { /* Special case: rla and rlc are the only 2 emulated instructions that fall into two operand instructions. */ /* With dst, there are only: Rm Register, x(Rm) Indexed, 0xXXXX Relative, &0xXXXX Absolute emulated_ins dst basic_ins dst, dst. */ if (regd != regs || as != ad) return 0; /* May be 'data' section. */ if (ad == 0) { /* Register mode. */ if (regd == 3) { strcpy (comm1, _("Illegal as emulation instr")); return -1; } sprintf (op1, "r%d", regd); *cycles = 1; } else /* ad == 1 */ { if (regd == 0) { /* PC relative, Symbolic. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 4; *cycles = 6; sprintf (op1, "0x%04x", PS (dst)); sprintf (comm1, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); if (extension_word) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; sprintf (op1, "0x%05x", dst & 0xfffff); sprintf (comm1, "PC rel. 0x%05lx", (long)((addr + 2 + dst) & 0xfffff)); } } else if (regd == 2) { /* Absolute. */ dst = msp430dis_opcode (addr + 2, info); /* If the 'src' field is not the same as the dst then this is not an rla instruction. */ if (dst != msp430dis_opcode (addr + 4, info)) return 0; cmd_len += 4; *cycles = 6; sprintf (op1, "&0x%04x", PS (dst)); if (extension_word) { dst |= extended_dst << 16; sprintf (op1, "&0x%05x", dst & 0xfffff); } } else { /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); if (extension_word) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; } else if (dst & 0x8000) dst |= -1U << 16; cmd_len += 4; *cycles = 6; sprintf (op1, "%d(r%d)", dst, regd); if (dst > 9 || dst < -9) sprintf (comm1, "#0x%05x", dst); } } *op2 = 0; *comm2 = 0; return cmd_len; } /* Two operands exactly. */ if (ad == 0 && regd == 3) { /* R2/R3 are illegal as dest: may be data section. */ strcpy (comm1, _("Illegal as 2-op instr")); return -1; } /* Source. */ if (as == 0) { *cycles = 1; if (regs == 3) { /* Constants. */ sprintf (op1, "#0"); sprintf (comm1, "r3 As==00"); } else { /* Register. */ sprintf (op1, "r%d", regs); } } else if (as == 2) { * cycles = print_as2_reg_name (regs, op1, comm1, 1, 1, regs == 0 ? 3 : 2); } else if (as == 3) { if (regs == 0) { *cycles = 3; /* Absolute. @pc+. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "#%d", dst); if (dst > 9 || dst < 0) sprintf (comm1, "#0x%04x", PS (dst)); if (extension_word) { dst |= extended_src << 16; if (dst & 0x80000) dst |= -1U << 20; sprintf (op1, "#%d", dst); if (dst > 9 || dst < 0) sprintf (comm1, "0x%05x", dst & 0xfffff); } } else * cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2); } else if (as == 1) { if (regs == 0) { *cycles = 4; /* PC relative. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "0x%04x", PS (dst)); sprintf (comm1, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); if (extension_word) { dst |= extended_src << 16; if (dst & 0x80000) dst |= -1U << 20; sprintf (op1, "0x%05x", dst & 0xfffff); sprintf (comm1, "PC rel. 0x%05lx", (long) ((addr + 2 + dst) & 0xfffff)); } } else if (regs == 2) { *cycles = 2; /* Absolute. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "&0x%04x", PS (dst)); sprintf (comm1, "0x%04x", PS (dst)); if (extension_word) { dst |= extended_src << 16; sprintf (op1, "&0x%05x", dst & 0xfffff); * comm1 = 0; } } else if (regs == 3) { *cycles = 1; sprintf (op1, "#1"); sprintf (comm1, "r3 As==01"); } else { *cycles = 3; /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; if (extension_word) { dst |= extended_src << 16; if (dst & 0x80000) dst |= -1U << 20; } else if (dst & 0x8000) dst |= -1U << 16; sprintf (op1, "%d(r%d)", dst, regs); if (dst > 9 || dst < -9) sprintf (comm1, "0x%05x", dst); } } /* Destination. Special care needed on addr + XXXX. */ if (ad == 0) { /* Register. */ if (regd == 0) { *cycles += 1; sprintf (op2, "r0"); } else if (regd == 1) sprintf (op2, "r1"); else if (regd == 2) sprintf (op2, "r2"); else sprintf (op2, "r%d", regd); } else /* ad == 1. */ { * cycles += 3; if (regd == 0) { /* PC relative. */ *cycles += 1; dst = msp430dis_opcode (addr + cmd_len, info); sprintf (op2, "0x%04x", PS (dst)); sprintf (comm2, "PC rel. 0x%04x", PS ((short) addr + cmd_len + dst)); if (extension_word) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; sprintf (op2, "0x%05x", dst & 0xfffff); sprintf (comm2, "PC rel. 0x%05lx", (long)((addr + cmd_len + dst) & 0xfffff)); } cmd_len += 2; } else if (regd == 2) { /* Absolute. */ dst = msp430dis_opcode (addr + cmd_len, info); cmd_len += 2; sprintf (op2, "&0x%04x", PS (dst)); if (extension_word) { dst |= extended_dst << 16; sprintf (op2, "&0x%05x", dst & 0xfffff); } } else { dst = msp430dis_opcode (addr + cmd_len, info); cmd_len += 2; if (dst & 0x8000) dst |= -1U << 16; if (dst > 9 || dst < 0) sprintf (comm2, "0x%04x", PS (dst)); if (extension_word) { dst |= extended_dst << 16; if (dst & 0x80000) dst |= -1U << 20; if (dst > 9 || dst < 0) sprintf (comm2, "0x%05x", dst & 0xfffff); } sprintf (op2, "%d(r%d)", dst, regd); } } return cmd_len; } static int msp430_branchinstr (disassemble_info *info, struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED, bfd_vma addr ATTRIBUTE_UNUSED, unsigned short insn, char *op1, char *comm1, int *cycles) { int regs = 0, regd = 0; int as = 0; int cmd_len = 2; short dst = 0; regd = insn & 0x0f; regs = (insn & 0x0f00) >> 8; as = (insn & 0x0030) >> 4; if (regd != 0) /* Destination register is not a PC. */ return 0; /* dst is a source register. */ if (as == 0) { /* Constants. */ if (regs == 3) { *cycles = 1; sprintf (op1, "#0"); sprintf (comm1, "r3 As==00"); } else { /* Register. */ *cycles = 1; sprintf (op1, "r%d", regs); } } else if (as == 2) { * cycles = print_as2_reg_name (regs, op1, comm1, 2, 1, 2); } else if (as == 3) { if (regs == 0) { /* Absolute. @pc+ */ *cycles = 3; dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "#0x%04x", PS (dst)); } else * cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2); } else if (as == 1) { * cycles = 3; if (regs == 0) { /* PC relative. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; (*cycles)++; sprintf (op1, "0x%04x", PS (dst)); sprintf (comm1, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); } else if (regs == 2) { /* Absolute. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "&0x%04x", PS (dst)); } else if (regs == 3) { (*cycles)--; sprintf (op1, "#1"); sprintf (comm1, "r3 As==01"); } else { /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; if (dst & 0x8000) dst |= -1U << 16; sprintf (op1, "%d(r%d)", dst, regs); } } return cmd_len; } static int msp430x_calla_instr (disassemble_info * info, bfd_vma addr, unsigned short insn, char * op1, char * comm1, int * cycles) { unsigned int ureg = insn & 0xf; int reg = insn & 0xf; int am = (insn & 0xf0) >> 4; int cmd_len = 2; unsigned short udst = 0; short dst = 0; switch (am) { case 4: /* CALLA Rdst */ *cycles = 1; sprintf (op1, "r%d", reg); break; case 5: /* CALLA x(Rdst) */ *cycles = 3; dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "%d(r%d)", dst, reg); if (reg == 0) sprintf (comm1, "PC rel. 0x%05lx", (long) (addr + 2 + dst)); else sprintf (comm1, "0x%05x", dst); break; case 6: /* CALLA @Rdst */ *cycles = 2; sprintf (op1, "@r%d", reg); break; case 7: /* CALLA @Rdst+ */ *cycles = 2; sprintf (op1, "@r%d+", reg); break; case 8: /* CALLA &abs20 */ udst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; sprintf (op1, "&%d", (ureg << 16) + udst); sprintf (comm1, "0x%05x", (ureg << 16) + udst); break; case 9: /* CALLA pcrel-sym */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; sprintf (op1, "%d(PC)", (reg << 16) + dst); sprintf (comm1, "PC rel. 0x%05lx", (long) (addr + 2 + dst + (reg << 16))); break; case 11: /* CALLA #imm20 */ udst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; sprintf (op1, "#%d", (ureg << 16) + udst); sprintf (comm1, "0x%05x", (ureg << 16) + udst); break; default: strcpy (comm1, _("unrecognised CALLA addressing mode")); return -1; } return cmd_len; } int print_insn_msp430 (bfd_vma addr, disassemble_info *info) { void *stream = info->stream; fprintf_ftype prin = info->fprintf_func; struct msp430_opcode_s *opcode; char op1[32], op2[32], comm1[64], comm2[64]; int cmd_len = 0; unsigned short insn; int cycles = 0; char *bc = ""; unsigned short extension_word = 0; insn = msp430dis_opcode (addr, info); if (insn == (unsigned short) -1) { prin (stream, ".word 0xffff; ????"); return 2; } if (((int) addr & 0xffff) > 0xffdf) { (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn); return 2; } *comm1 = 0; *comm2 = 0; /* Check for an extension word. */ if ((insn & 0xf800) == 0x1800) { extension_word = insn; addr += 2; insn = msp430dis_opcode (addr, info); if (insn == (unsigned short) -1) { prin (stream, ".word 0x%04x, 0xffff; ????", extension_word); return 4; } } for (opcode = msp430_opcodes; opcode->name; opcode++) { if ((insn & opcode->bin_mask) == opcode->bin_opcode && opcode->bin_opcode != 0x9300) { *op1 = 0; *op2 = 0; *comm1 = 0; *comm2 = 0; /* r0 as destination. Ad should be zero. */ if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0 && (insn & 0x0080) == 0) { cmd_len += msp430_branchinstr (info, opcode, addr, insn, op1, comm1, &cycles); if (cmd_len) break; } switch (opcode->insn_opnumb) { int n; int reg; case 4: cmd_len += msp430x_calla_instr (info, addr, insn, op1, comm1, & cycles); break; case 5: /* PUSHM/POPM */ n = (insn & 0xf0) >> 4; reg = (insn & 0xf); sprintf (op1, "#%d", n + 1); if (opcode->bin_opcode == 0x1400) /* PUSHM */ sprintf (op2, "r%d", reg); else /* POPM */ sprintf (op2, "r%d", reg + n); if (insn & 0x100) sprintf (comm1, "16-bit words"); else { sprintf (comm1, "20-bit words"); bc =".a"; } cycles = 2; /*FIXME*/ cmd_len = 2; break; case 6: /* RRAM, RRCM, RRUM, RLAM. */ n = ((insn >> 10) & 0x3) + 1; reg = (insn & 0xf); if ((insn & 0x10) == 0) bc =".a"; sprintf (op1, "#%d", n); sprintf (op2, "r%d", reg); cycles = 2; /*FIXME*/ cmd_len = 2; break; case 8: /* ADDA, CMPA, SUBA. */ reg = (insn & 0xf); n = (insn >> 8) & 0xf; if (insn & 0x40) { sprintf (op1, "r%d", n); cmd_len = 2; } else { n <<= 16; n |= msp430dis_opcode (addr + 2, info); sprintf (op1, "#%d", n); if (n > 9 || n < 0) sprintf (comm1, "0x%05x", n); cmd_len = 4; } sprintf (op2, "r%d", reg); cycles = 2; /*FIXME*/ break; case 9: /* MOVA */ reg = (insn & 0xf); n = (insn >> 8) & 0xf; switch ((insn >> 4) & 0xf) { case 0: /* MOVA @Rsrc, Rdst */ cmd_len = 2; sprintf (op1, "@r%d", n); if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); break; case 1: /* MOVA @Rsrc+, Rdst */ cmd_len = 2; if (strcmp (opcode->name, "reta") != 0) { sprintf (op1, "@r%d+", n); if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); } break; case 2: /* MOVA &abs20, Rdst */ cmd_len = 4; n <<= 16; n |= msp430dis_opcode (addr + 2, info); sprintf (op1, "&%d", n); if (n > 9 || n < 0) sprintf (comm1, "0x%05x", n); if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); break; case 3: /* MOVA x(Rsrc), Rdst */ cmd_len = 4; if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); reg = n; n = msp430dis_opcode (addr + 2, info); if (n & 0x8000) n |= -1U << 16; sprintf (op1, "%d(r%d)", n, reg); if (n > 9 || n < 0) { if (reg == 0) sprintf (comm1, "PC rel. 0x%05lx", (long) (addr + 2 + n)); else sprintf (comm1, "0x%05x", n); } break; case 6: /* MOVA Rsrc, &abs20 */ cmd_len = 4; reg <<= 16; reg |= msp430dis_opcode (addr + 2, info); sprintf (op1, "r%d", n); sprintf (op2, "&%d", reg); if (reg > 9 || reg < 0) sprintf (comm2, "0x%05x", reg); break; case 7: /* MOVA Rsrc, x(Rdst) */ cmd_len = 4; sprintf (op1, "r%d", n); n = msp430dis_opcode (addr + 2, info); if (n & 0x8000) n |= -1U << 16; sprintf (op2, "%d(r%d)", n, reg); if (n > 9 || n < 0) { if (reg == 0) sprintf (comm2, "PC rel. 0x%05lx", (long) (addr + 2 + n)); else sprintf (comm2, "0x%05x", n); } break; case 8: /* MOVA #imm20, Rdst */ cmd_len = 4; n <<= 16; n |= msp430dis_opcode (addr + 2, info); if (n & 0x80000) n |= -1U << 20; sprintf (op1, "#%d", n); if (n > 9 || n < 0) sprintf (comm1, "0x%05x", n); if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); break; case 12: /* MOVA Rsrc, Rdst */ cmd_len = 2; sprintf (op1, "r%d", n); if (strcmp (opcode->name, "bra") != 0) sprintf (op2, "r%d", reg); break; default: break; } cycles = 2; /* FIXME */ break; } if (cmd_len) break; switch (opcode->insn_opnumb) { case 0: cmd_len += msp430_nooperands (opcode, addr, insn, comm1, &cycles); break; case 2: cmd_len += msp430_doubleoperand (info, opcode, addr, insn, op1, op2, comm1, comm2, extension_word, &cycles); if (insn & BYTE_OPERATION) { if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0)) bc = ".a"; else bc = ".b"; } else if (extension_word) { if (extension_word & (1 << 6)) bc = ".w"; else { bc = ".?"; sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); } } break; case 1: cmd_len += msp430_singleoperand (info, opcode, addr, insn, op1, comm1, extension_word, &cycles); if (extension_word && (strcmp (opcode->name, "swpb") == 0 || strcmp (opcode->name, "sxt") == 0)) { if (insn & BYTE_OPERATION) { bc = ".?"; sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); } else if (extension_word & BYTE_OPERATION) bc = ".w"; else bc = ".a"; } else if (insn & BYTE_OPERATION && opcode->fmt != 3) { if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0)) bc = ".a"; else bc = ".b"; } else if (extension_word) { if (extension_word & (1 << 6)) bc = ".w"; else { bc = ".?"; sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); } } break; default: break; } } if (cmd_len) break; } if (cmd_len < 1) { /* Unknown opcode, or invalid combination of operands. */ if (extension_word) { prin (stream, ".word 0x%04x, 0x%04x; ????", extension_word, PS (insn)); if (*comm1) prin (stream, "\t %s", comm1); return 4; } (*prin) (stream, ".word 0x%04x; ????", PS (insn)); return 2; } /* Display the repeat count (if set) for extended register mode. */ if (cmd_len == 2 && ((extension_word & 0xf) != 0)) { if (extension_word & (1 << 7)) prin (stream, "rpt r%d { ", extension_word & 0xf); else prin (stream, "rpt #%d { ", (extension_word & 0xf) + 1); } if (extension_word && opcode->name[strlen (opcode->name) - 1] != 'x') (*prin) (stream, "%sx%s", opcode->name, bc); else (*prin) (stream, "%s%s", opcode->name, bc); if (*op1) (*prin) (stream, "\t%s", op1); if (*op2) (*prin) (stream, ","); if (strlen (op1) < 7) (*prin) (stream, "\t"); if (!strlen (op1)) (*prin) (stream, "\t"); if (*op2) (*prin) (stream, "%s", op2); if (strlen (op2) < 8) (*prin) (stream, "\t"); if (*comm1 || *comm2) (*prin) (stream, ";"); else if (cycles) { if (*op2) (*prin) (stream, ";"); else { if (strlen (op1) < 7) (*prin) (stream, ";"); else (*prin) (stream, "\t;"); } } if (*comm1) (*prin) (stream, "%s", comm1); if (*comm1 && *comm2) (*prin) (stream, ","); if (*comm2) (*prin) (stream, " %s", comm2); if (extension_word) cmd_len += 2; return cmd_len; }