Back to index

cell-binutils  2.17cvs20070401
msp430-dis.c
Go to the documentation of this file.
00001 /* Disassemble MSP430 instructions.
00002    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
00003    
00004    Contributed by Dmitry Diky <diwil@mail.ru>
00005         
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015    
00016    You should have received a copy of the GNU General Public License
00017    along with this program; if not, write to the Free Software
00018    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
00019    MA 02110-1301, USA.  */
00020 
00021 #include <stdio.h>
00022 #include <ctype.h>
00023 #include <string.h>
00024 #include <sys/types.h>
00025 
00026 #include "dis-asm.h"
00027 #include "opintl.h"
00028 #include "libiberty.h"
00029 
00030 #define DASM_SECTION
00031 #include "opcode/msp430.h"
00032 #undef DASM_SECTION
00033 
00034 
00035 #define PS(x)   (0xffff & (x))
00036 
00037 static unsigned short
00038 msp430dis_opcode (bfd_vma addr, disassemble_info *info)
00039 {
00040   bfd_byte buffer[2];
00041   int status;
00042 
00043   status = info->read_memory_func (addr, buffer, 2, info);
00044   if (status != 0)
00045     {
00046       info->memory_error_func (status, addr, info);
00047       return -1;
00048     }
00049   return bfd_getl16 (buffer);
00050 }
00051 
00052 static int
00053 msp430_nooperands (struct msp430_opcode_s *opcode,
00054                  bfd_vma addr ATTRIBUTE_UNUSED,
00055                  unsigned short insn ATTRIBUTE_UNUSED,
00056                  char *comm,
00057                  int *cycles)
00058 {
00059   /* Pop with constant.  */
00060   if (insn == 0x43b2)
00061     return 0;
00062   if (insn == opcode->bin_opcode)
00063     return 2;
00064 
00065   if (opcode->fmt == 0)
00066     {
00067       if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
00068        return 0;
00069 
00070       strcpy (comm, "emulated...");
00071       *cycles = 1;
00072     }
00073   else
00074     {
00075       strcpy (comm, "return from interupt");
00076       *cycles = 5;
00077     }
00078 
00079   return 2;
00080 }
00081 
00082 static int
00083 msp430_singleoperand (disassemble_info *info,
00084                     struct msp430_opcode_s *opcode,
00085                     bfd_vma addr,
00086                     unsigned short insn,
00087                     char *op,
00088                     char *comm,
00089                     int *cycles)
00090 {
00091   int regs = 0, regd = 0;
00092   int ad = 0, as = 0;
00093   int where = 0;
00094   int cmd_len = 2;
00095   short dst = 0;
00096 
00097   regd = insn & 0x0f;
00098   regs = (insn & 0x0f00) >> 8;
00099   as = (insn & 0x0030) >> 4;
00100   ad = (insn & 0x0080) >> 7;
00101 
00102   switch (opcode->fmt)
00103     {
00104     case 0:                 /* Emulated work with dst register.  */
00105       if (regs != 2 && regs != 3 && regs != 1)
00106        return 0;
00107 
00108       /* Check if not clr insn.  */
00109       if (opcode->bin_opcode == 0x4300 && (ad || as))
00110        return 0;
00111 
00112       /* Check if really inc, incd insns.  */
00113       if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
00114        return 0;
00115 
00116       if (ad == 0)
00117        {
00118          *cycles = 1;
00119 
00120          /* Register.  */
00121          if (regd == 0)
00122            {
00123              *cycles += 1;
00124              sprintf (op, "r0");
00125            }
00126          else if (regd == 1)
00127            sprintf (op, "r1");
00128 
00129          else if (regd == 2)
00130            sprintf (op, "r2");
00131 
00132          else
00133            sprintf (op, "r%d", regd);
00134        }
00135       else    /* ad == 1 msp430dis_opcode.  */
00136        {
00137          if (regd == 0)
00138            {
00139              /* PC relative.  */
00140              dst = msp430dis_opcode (addr + 2, info);
00141              cmd_len += 2;
00142              *cycles = 4;
00143              sprintf (op, "0x%04x", dst);
00144              sprintf (comm, "PC rel. abs addr 0x%04x",
00145                      PS ((short) (addr + 2) + dst));
00146            }
00147          else if (regd == 2)
00148            {
00149              /* Absolute.  */
00150              dst = msp430dis_opcode (addr + 2, info);
00151              cmd_len += 2;
00152              *cycles = 4;
00153              sprintf (op, "&0x%04x", PS (dst));
00154            }
00155          else
00156            {
00157              dst = msp430dis_opcode (addr + 2, info);
00158              cmd_len += 2;
00159              *cycles = 4;
00160              sprintf (op, "%d(r%d)", dst, regd);
00161            }
00162        }
00163       break;
00164 
00165     case 2:   /* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
00166       if (as == 0)
00167        {
00168          if (regd == 3)
00169            {
00170              /* Constsnts.  */
00171              sprintf (op, "#0");
00172              sprintf (comm, "r3 As==00");
00173            }
00174          else
00175            {
00176              /* Register.  */
00177              sprintf (op, "r%d", regd);
00178            }
00179          *cycles = 1;
00180        }
00181       else if (as == 2)
00182        {
00183          *cycles = 1;
00184          if (regd == 2)
00185            {
00186              sprintf (op, "#4");
00187              sprintf (comm, "r2 As==10");
00188            }
00189          else if (regd == 3)
00190            {
00191              sprintf (op, "#2");
00192              sprintf (comm, "r3 As==10");
00193            }
00194          else
00195            {
00196              *cycles = 3;
00197              /* Indexed register mode @Rn.  */
00198              sprintf (op, "@r%d", regd);
00199            }
00200        }
00201       else if (as == 3)
00202        {
00203          *cycles = 1;
00204          if (regd == 2)
00205            {
00206              sprintf (op, "#8");
00207              sprintf (comm, "r2 As==11");
00208            }
00209          else if (regd == 3)
00210            {
00211              sprintf (op, "#-1");
00212              sprintf (comm, "r3 As==11");
00213            }
00214          else if (regd == 0)
00215            {
00216              *cycles = 3;
00217              /* absolute. @pc+ */
00218              dst = msp430dis_opcode (addr + 2, info);
00219              cmd_len += 2;
00220              sprintf (op, "#%d", dst);
00221              sprintf (comm, "#0x%04x", PS (dst));
00222            }
00223          else
00224            {
00225              *cycles = 3;
00226              sprintf (op, "@r%d+", regd);
00227            }
00228        }
00229       else if (as == 1)
00230        {
00231          *cycles = 4;
00232          if (regd == 0)
00233            {
00234              /* PC relative.  */
00235              dst = msp430dis_opcode (addr + 2, info);
00236              cmd_len += 2;
00237              sprintf (op, "0x%04x", PS (dst));
00238              sprintf (comm, "PC rel. 0x%04x",
00239                      PS ((short) addr + 2 + dst));
00240            }
00241          else if (regd == 2)
00242            {
00243              /* Absolute.  */
00244              dst = msp430dis_opcode (addr + 2, info);
00245              cmd_len += 2;
00246              sprintf (op, "&0x%04x", PS (dst));
00247            }
00248          else if (regd == 3)
00249            {
00250              *cycles = 1;
00251              sprintf (op, "#1");
00252              sprintf (comm, "r3 As==01");
00253            }
00254          else
00255            {
00256              /* Indexd.  */
00257              dst = msp430dis_opcode (addr + 2, info);
00258              cmd_len += 2;
00259              sprintf (op, "%d(r%d)", dst, regd);
00260            }
00261        }
00262       break;
00263 
00264     case 3:                 /* Jumps.  */
00265       where = insn & 0x03ff;
00266       if (where & 0x200)
00267        where |= ~0x03ff;
00268       if (where > 512 || where < -511)
00269        return 0;
00270 
00271       where *= 2;
00272       sprintf (op, "$%+-8d", where + 2);
00273       sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
00274       *cycles = 2;
00275       return 2;
00276       break;
00277     default:
00278       cmd_len = 0;
00279     }
00280 
00281   return cmd_len;
00282 }
00283 
00284 static int
00285 msp430_doubleoperand (disassemble_info *info,
00286                     struct msp430_opcode_s *opcode,
00287                     bfd_vma addr,
00288                     unsigned short insn,
00289                     char *op1,
00290                     char *op2,
00291                     char *comm1,
00292                     char *comm2,
00293                     int *cycles)
00294 {
00295   int regs = 0, regd = 0;
00296   int ad = 0, as = 0;
00297   int cmd_len = 2;
00298   short dst = 0;
00299 
00300   regd = insn & 0x0f;
00301   regs = (insn & 0x0f00) >> 8;
00302   as = (insn & 0x0030) >> 4;
00303   ad = (insn & 0x0080) >> 7;
00304 
00305   if (opcode->fmt == 0)
00306     {
00307       /* Special case: rla and rlc are the only 2 emulated instructions that
00308         fall into two operand instructions.  */
00309       /* With dst, there are only:
00310         Rm           Register,
00311          x(Rm)       Indexed,
00312          0xXXXX      Relative,
00313          &0xXXXX     Absolute 
00314          emulated_ins   dst
00315          basic_ins      dst, dst.  */
00316 
00317       if (regd != regs || as != ad)
00318        return 0;            /* May be 'data' section.  */
00319 
00320       if (ad == 0)
00321        {
00322          /* Register mode.  */
00323          if (regd == 3)
00324            {
00325              strcpy (comm1, _("Illegal as emulation instr"));
00326              return -1;
00327            }
00328 
00329          sprintf (op1, "r%d", regd);
00330          *cycles = 1;
00331        }
00332       else                  /* ad == 1 */
00333        {
00334          if (regd == 0)
00335            {
00336              /* PC relative, Symbolic.  */
00337              dst = msp430dis_opcode (addr + 2, info);
00338              cmd_len += 4;
00339              *cycles = 6;
00340              sprintf (op1, "0x%04x", PS (dst));
00341              sprintf (comm1, "PC rel. 0x%04x",
00342                      PS ((short) addr + 2 + dst));
00343 
00344            }
00345          else if (regd == 2)
00346            {
00347              /* Absolute.  */
00348              dst = msp430dis_opcode (addr + 2, info);
00349              /* If the 'src' field is not the same as the dst
00350                then this is not an rla instruction.  */
00351              if (dst != msp430dis_opcode (addr + 4, info))
00352               return 0;
00353              cmd_len += 4;
00354              *cycles = 6;
00355              sprintf (op1, "&0x%04x", PS (dst));
00356            }
00357          else
00358            {
00359              /* Indexed.  */
00360              dst = msp430dis_opcode (addr + 2, info);
00361              cmd_len += 4;
00362              *cycles = 6;
00363              sprintf (op1, "%d(r%d)", dst, regd);
00364            }
00365        }
00366 
00367       *op2 = 0;
00368       *comm2 = 0;
00369       return cmd_len;
00370     }
00371 
00372   /* Two operands exactly.  */
00373   if (ad == 0 && regd == 3)
00374     {
00375       /* R2/R3 are illegal as dest: may be data section.  */
00376       strcpy (comm1, _("Illegal as 2-op instr"));
00377       return -1;
00378     }
00379 
00380   /* Source.  */
00381   if (as == 0)
00382     {
00383       *cycles = 1;
00384       if (regs == 3)
00385        {
00386          /* Constsnts.  */
00387          sprintf (op1, "#0");
00388          sprintf (comm1, "r3 As==00");
00389        }
00390       else
00391        {
00392          /* Register.  */
00393          sprintf (op1, "r%d", regs);
00394        }
00395     }
00396   else if (as == 2)
00397     {
00398       *cycles = 1;
00399 
00400       if (regs == 2)
00401        {
00402          sprintf (op1, "#4");
00403          sprintf (comm1, "r2 As==10");
00404        }
00405       else if (regs == 3)
00406        {
00407          sprintf (op1, "#2");
00408          sprintf (comm1, "r3 As==10");
00409        }
00410       else
00411        {
00412          *cycles = 2;
00413 
00414          /* Indexed register mode @Rn.  */
00415          sprintf (op1, "@r%d", regs);
00416        }
00417       if (!regs)
00418        *cycles = 3;
00419     }
00420   else if (as == 3)
00421     {
00422       if (regs == 2)
00423        {
00424          sprintf (op1, "#8");
00425          sprintf (comm1, "r2 As==11");
00426          *cycles = 1;
00427        }
00428       else if (regs == 3)
00429        {
00430          sprintf (op1, "#-1");
00431          sprintf (comm1, "r3 As==11");
00432          *cycles = 1;
00433        }
00434       else if (regs == 0)
00435        {
00436          *cycles = 3;
00437          /* Absolute. @pc+.  */
00438          dst = msp430dis_opcode (addr + 2, info);
00439          cmd_len += 2;
00440          sprintf (op1, "#%d", dst);
00441          sprintf (comm1, "#0x%04x", PS (dst));
00442        }
00443       else
00444        {
00445          *cycles = 2;
00446          sprintf (op1, "@r%d+", regs);
00447        }
00448     }
00449   else if (as == 1)
00450     {
00451       if (regs == 0)
00452        {
00453          *cycles = 4;
00454          /* PC relative.  */
00455          dst = msp430dis_opcode (addr + 2, info);
00456          cmd_len += 2;
00457          sprintf (op1, "0x%04x", PS (dst));
00458          sprintf (comm1, "PC rel. 0x%04x",
00459                  PS ((short) addr + 2 + dst));
00460        }
00461       else if (regs == 2)
00462        {
00463          *cycles = 2;
00464          /* Absolute.  */
00465          dst = msp430dis_opcode (addr + 2, info);
00466          cmd_len += 2;
00467          sprintf (op1, "&0x%04x", PS (dst));
00468          sprintf (comm1, "0x%04x", PS (dst));
00469        }
00470       else if (regs == 3)
00471        {
00472          *cycles = 1;
00473          sprintf (op1, "#1");
00474          sprintf (comm1, "r3 As==01");
00475        }
00476       else
00477        {
00478          *cycles = 3;
00479          /* Indexed.  */
00480          dst = msp430dis_opcode (addr + 2, info);
00481          cmd_len += 2;
00482          sprintf (op1, "%d(r%d)", dst, regs);
00483        }
00484     }
00485 
00486   /* Destination. Special care needed on addr + XXXX.  */
00487 
00488   if (ad == 0)
00489     {
00490       /* Register.  */
00491       if (regd == 0)
00492        {
00493          *cycles += 1;
00494          sprintf (op2, "r0");
00495        }
00496       else if (regd == 1)
00497        sprintf (op2, "r1");
00498 
00499       else if (regd == 2)
00500        sprintf (op2, "r2");
00501 
00502       else
00503        sprintf (op2, "r%d", regd);
00504     }
00505   else /* ad == 1.  */
00506     {
00507       * cycles += 3;
00508 
00509       if (regd == 0)
00510        {
00511          /* PC relative.  */
00512          *cycles += 1;
00513          dst = msp430dis_opcode (addr + cmd_len, info);
00514          sprintf (op2, "0x%04x", PS (dst));
00515          sprintf (comm2, "PC rel. 0x%04x",
00516                  PS ((short) addr + cmd_len + dst));
00517          cmd_len += 2;
00518        }
00519       else if (regd == 2)
00520        {
00521          /* Absolute.  */
00522          dst = msp430dis_opcode (addr + cmd_len, info);
00523          cmd_len += 2;
00524          sprintf (op2, "&0x%04x", PS (dst));
00525        }
00526       else
00527        {
00528          dst = msp430dis_opcode (addr + cmd_len, info);
00529          cmd_len += 2;
00530          sprintf (op2, "%d(r%d)", dst, regd);
00531        }
00532     }
00533 
00534   return cmd_len;
00535 }
00536 
00537 static int
00538 msp430_branchinstr (disassemble_info *info,
00539                   struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
00540                   bfd_vma addr ATTRIBUTE_UNUSED,
00541                   unsigned short insn,
00542                   char *op1,
00543                   char *comm1,
00544                   int *cycles)
00545 {
00546   int regs = 0, regd = 0;
00547   int ad = 0, as = 0;
00548   int cmd_len = 2;
00549   short dst = 0;
00550 
00551   regd = insn & 0x0f;
00552   regs = (insn & 0x0f00) >> 8;
00553   as = (insn & 0x0030) >> 4;
00554   ad = (insn & 0x0080) >> 7;
00555 
00556   if (regd != 0)     /* Destination register is not a PC.  */
00557     return 0;
00558 
00559   /* dst is a source register.  */
00560   if (as == 0)
00561     {
00562       /* Constants.  */
00563       if (regs == 3)
00564        {
00565          *cycles = 1;
00566          sprintf (op1, "#0");
00567          sprintf (comm1, "r3 As==00");
00568        }
00569       else
00570        {
00571          /* Register.  */
00572          *cycles = 1;
00573          sprintf (op1, "r%d", regs);
00574        }
00575     }
00576   else if (as == 2)
00577     {
00578       if (regs == 2)
00579        {
00580          *cycles = 2;
00581          sprintf (op1, "#4");
00582          sprintf (comm1, "r2 As==10");
00583        }
00584       else if (regs == 3)
00585        {
00586          *cycles = 1;
00587          sprintf (op1, "#2");
00588          sprintf (comm1, "r3 As==10");
00589        }
00590       else
00591        {
00592          /* Indexed register mode @Rn.  */
00593          *cycles = 2;
00594          sprintf (op1, "@r%d", regs);
00595        }
00596     }
00597   else if (as == 3)
00598     {
00599       if (regs == 2)
00600        {
00601          *cycles = 1;
00602          sprintf (op1, "#8");
00603          sprintf (comm1, "r2 As==11");
00604        }
00605       else if (regs == 3)
00606        {
00607          *cycles = 1;
00608          sprintf (op1, "#-1");
00609          sprintf (comm1, "r3 As==11");
00610        }
00611       else if (regs == 0)
00612        {
00613          /* Absolute. @pc+  */
00614          *cycles = 3;
00615          dst = msp430dis_opcode (addr + 2, info);
00616          cmd_len += 2;
00617          sprintf (op1, "#0x%04x", PS (dst));
00618        }
00619       else
00620        {
00621          *cycles = 2;
00622          sprintf (op1, "@r%d+", regs);
00623        }
00624     }
00625   else if (as == 1)
00626     {
00627       * cycles = 3;
00628 
00629       if (regs == 0)
00630        {
00631          /* PC relative.  */
00632          dst = msp430dis_opcode (addr + 2, info);
00633          cmd_len += 2;
00634          (*cycles)++;
00635          sprintf (op1, "0x%04x", PS (dst));
00636          sprintf (comm1, "PC rel. 0x%04x",
00637                  PS ((short) addr + 2 + dst));
00638        }
00639       else if (regs == 2)
00640        {
00641          /* Absolute.  */
00642          dst = msp430dis_opcode (addr + 2, info);
00643          cmd_len += 2;
00644          sprintf (op1, "&0x%04x", PS (dst));
00645        }
00646       else if (regs == 3)
00647        {
00648          (*cycles)--;
00649          sprintf (op1, "#1");
00650          sprintf (comm1, "r3 As==01");
00651        }
00652       else
00653        {
00654          /* Indexd.  */
00655          dst = msp430dis_opcode (addr + 2, info);
00656          cmd_len += 2;
00657          sprintf (op1, "%d(r%d)", dst, regs);
00658        }
00659     }
00660 
00661   return cmd_len;
00662 }
00663 
00664 int
00665 print_insn_msp430 (bfd_vma addr, disassemble_info *info)
00666 {
00667   void *stream = info->stream;
00668   fprintf_ftype prin = info->fprintf_func;
00669   struct msp430_opcode_s *opcode;
00670   char op1[32], op2[32], comm1[64], comm2[64];
00671   int cmd_len = 0;
00672   unsigned short insn;
00673   int cycles = 0;
00674   char *bc = "";
00675   char dinfo[32];           /* Debug purposes.  */
00676 
00677   insn = msp430dis_opcode (addr, info);
00678   sprintf (dinfo, "0x%04x", insn);
00679 
00680   if (((int) addr & 0xffff) > 0xffdf)
00681     {
00682       (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
00683       return 2;
00684     }
00685 
00686   *comm1 = 0;
00687   *comm2 = 0;
00688 
00689   for (opcode = msp430_opcodes; opcode->name; opcode++)
00690     {
00691       if ((insn & opcode->bin_mask) == opcode->bin_opcode
00692          && opcode->bin_opcode != 0x9300)
00693        {
00694          *op1 = 0;
00695          *op2 = 0;
00696          *comm1 = 0;
00697          *comm2 = 0;
00698 
00699          /* r0 as destination. Ad should be zero.  */
00700          if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0
00701              && (0x0080 & insn) == 0)
00702            {
00703              cmd_len =
00704               msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
00705                                 &cycles);
00706              if (cmd_len)
00707               break;
00708            }
00709 
00710          switch (opcode->insn_opnumb)
00711            {
00712            case 0:
00713              cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles);
00714              break;
00715            case 2:
00716              cmd_len =
00717               msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
00718                                   comm1, comm2, &cycles);
00719              if (insn & BYTE_OPERATION)
00720               bc = ".b";
00721              break;
00722            case 1:
00723              cmd_len =
00724               msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
00725                                   &cycles);
00726              if (insn & BYTE_OPERATION && opcode->fmt != 3)
00727               bc = ".b";
00728              break;
00729            default:
00730              break;
00731            }
00732        }
00733 
00734       if (cmd_len)
00735        break;
00736     }
00737 
00738   dinfo[5] = 0;
00739 
00740   if (cmd_len < 1)
00741     {
00742       /* Unknown opcode, or invalid combination of operands.  */
00743       (*prin) (stream, ".word      0x%04x;       ????", PS (insn));
00744       return 2;
00745     }
00746 
00747   (*prin) (stream, "%s%s", opcode->name, bc);
00748 
00749   if (*op1)
00750     (*prin) (stream, "\t%s", op1);
00751   if (*op2)
00752     (*prin) (stream, ",");
00753 
00754   if (strlen (op1) < 7)
00755     (*prin) (stream, "\t");
00756   if (!strlen (op1))
00757     (*prin) (stream, "\t");
00758 
00759   if (*op2)
00760     (*prin) (stream, "%s", op2);
00761   if (strlen (op2) < 8)
00762     (*prin) (stream, "\t");
00763 
00764   if (*comm1 || *comm2)
00765     (*prin) (stream, ";");
00766   else if (cycles)
00767     {
00768       if (*op2)
00769        (*prin) (stream, ";");
00770       else
00771        {
00772          if (strlen (op1) < 7)
00773            (*prin) (stream, ";");
00774          else
00775            (*prin) (stream, "\t;");
00776        }
00777     }
00778   if (*comm1)
00779     (*prin) (stream, "%s", comm1);
00780   if (*comm1 && *comm2)
00781     (*prin) (stream, ",");
00782   if (*comm2)
00783     (*prin) (stream, " %s", comm2);
00784   return cmd_len;
00785 }