1 /* Disassemble MSP430 instructions.
2    Copyright (C) 2002-2014 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 "sysdep.h"
24 #include <stdio.h>
25 #include <ctype.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
msp430dis_opcode(bfd_vma addr,disassemble_info * info)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
msp430_nooperands(struct msp430_opcode_s * opcode,bfd_vma addr ATTRIBUTE_UNUSED,unsigned short insn ATTRIBUTE_UNUSED,char * comm,int * cycles)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
print_as2_reg_name(int regno,char * op1,char * comm1,int c2,int c3,int cd)85 print_as2_reg_name (int regno, char * op1, char * comm1,
86 		    int c2, int c3, int cd)
87 {
88   switch (regno)
89     {
90     case 2:
91       sprintf (op1, "#4");
92       sprintf (comm1, "r2 As==10");
93       return c2;
94 
95     case 3:
96       sprintf (op1, "#2");
97       sprintf (comm1, "r3 As==10");
98       return c3;
99 
100     default:
101       /* Indexed register mode @Rn.  */
102       sprintf (op1, "@r%d", regno);
103       return cd;
104     }
105 }
106 
107 static int
print_as3_reg_name(int regno,char * op1,char * comm1,int c2,int c3,int cd)108 print_as3_reg_name (int regno, char * op1, char * comm1,
109 		    int c2, int c3, int cd)
110 {
111   switch (regno)
112     {
113     case 2:
114       sprintf (op1, "#8");
115       sprintf (comm1, "r2 As==11");
116       return c2;
117 
118     case 3:
119       sprintf (op1, "#-1");
120       sprintf (comm1, "r3 As==11");
121       return c3;
122 
123     default:
124       /* Post incremented @Rn+.  */
125       sprintf (op1, "@r%d+", regno);
126       return cd;
127     }
128 }
129 
130 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)131 msp430_singleoperand (disassemble_info *info,
132 		      struct msp430_opcode_s *opcode,
133 		      bfd_vma addr,
134 		      unsigned short insn,
135 		      char *op,
136 		      char *comm,
137 		      unsigned short extension_word,
138 		      int *cycles)
139 {
140   int regs = 0, regd = 0;
141   int ad = 0, as = 0;
142   int where = 0;
143   int cmd_len = 2;
144   int dst = 0;
145   int fmt;
146   int extended_dst = extension_word & 0xf;
147 
148   regd = insn & 0x0f;
149   regs = (insn & 0x0f00) >> 8;
150   as = (insn & 0x0030) >> 4;
151   ad = (insn & 0x0080) >> 7;
152 
153   if (opcode->fmt < 0)
154     fmt = (- opcode->fmt) - 1;
155   else
156     fmt = opcode->fmt;
157 
158   switch (fmt)
159     {
160     case 0:			/* Emulated work with dst register.  */
161       if (regs != 2 && regs != 3 && regs != 1)
162 	return 0;
163 
164       /* Check if not clr insn.  */
165       if (opcode->bin_opcode == 0x4300 && (ad || as))
166 	return 0;
167 
168       /* Check if really inc, incd insns.  */
169       if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
170 	return 0;
171 
172       if (ad == 0)
173 	{
174 	  *cycles = 1;
175 
176 	  /* Register.  */
177 	  if (regd == 0)
178 	    {
179 	      *cycles += 1;
180 	      sprintf (op, "r0");
181 	    }
182 	  else if (regd == 1)
183 	    sprintf (op, "r1");
184 
185 	  else if (regd == 2)
186 	    sprintf (op, "r2");
187 
188 	  else
189 	    sprintf (op, "r%d", regd);
190 	}
191       else	/* ad == 1 msp430dis_opcode.  */
192 	{
193 	  if (regd == 0)
194 	    {
195 	      /* PC relative.  */
196 	      dst = msp430dis_opcode (addr + 2, info);
197 	      cmd_len += 2;
198 	      *cycles = 4;
199 	      sprintf (op, "0x%04x", dst);
200 	      sprintf (comm, "PC rel. abs addr 0x%04x",
201 		       PS ((short) (addr + 2) + dst));
202 	      if (extended_dst)
203 		{
204 		  dst |= extended_dst << 16;
205 		  sprintf (op, "0x%05x", dst);
206 		  sprintf (comm, "PC rel. abs addr 0x%05lx",
207 			   (long)((addr + 2 + dst) & 0xfffff));
208 		}
209 	    }
210 	  else if (regd == 2)
211 	    {
212 	      /* Absolute.  */
213 	      dst = msp430dis_opcode (addr + 2, info);
214 	      cmd_len += 2;
215 	      *cycles = 4;
216 	      sprintf (op, "&0x%04x", PS (dst));
217 	      if (extended_dst)
218 		{
219 		  dst |= extended_dst << 16;
220 		  sprintf (op, "&0x%05x", dst & 0xfffff);
221 		}
222 	    }
223 	  else
224 	    {
225 	      dst = msp430dis_opcode (addr + 2, info);
226 	      cmd_len += 2;
227 	      *cycles = 4;
228 	      if (extended_dst)
229 		{
230 		  dst |= extended_dst << 16;
231 		  if (dst & 0x80000)
232 		    dst |= -1 << 20;
233 		}
234 	      else if (dst & 0x8000)
235 		dst |= -1 << 16;
236 	      sprintf (op, "%d(r%d)", dst, regd);
237 	    }
238 	}
239       break;
240 
241     case 2:	/* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
242       if (as == 0)
243 	{
244 	  if (regd == 3)
245 	    {
246 	      /* Constsnts.  */
247 	      sprintf (op, "#0");
248 	      sprintf (comm, "r3 As==00");
249 	    }
250 	  else
251 	    {
252 	      /* Register.  */
253 	      sprintf (op, "r%d", regd);
254 	    }
255 	  *cycles = 1;
256 	}
257       else if (as == 2)
258 	{
259 	  * cycles = print_as2_reg_name (regd, op, comm, 1, 1, 3);
260 	}
261       else if (as == 3)
262 	{
263 	  if (regd == 0)
264 	    {
265 	      *cycles = 3;
266 	      /* absolute. @pc+ */
267 	      dst = msp430dis_opcode (addr + 2, info);
268 	      cmd_len += 2;
269 	      sprintf (op, "#%d", dst);
270 	      if (dst > 9 || dst < 0)
271 		sprintf (comm, "#0x%04x", PS (dst));
272 	      if (extended_dst)
273 		{
274 		  dst |= extended_dst << 16;
275 		  if (dst & 0x80000)
276 		    dst |= -1 << 20;
277 		  sprintf (op, "#%d", dst);
278 		  if (dst > 9 || dst < 0)
279 		    sprintf (comm, "#0x%05x", dst);
280 		}
281 	    }
282 	  else
283 	    * cycles = print_as3_reg_name (regd, op, comm, 1, 1, 3);
284 	}
285       else if (as == 1)
286 	{
287 	  *cycles = 4;
288 	  if (regd == 0)
289 	    {
290 	      /* PC relative.  */
291 	      dst = msp430dis_opcode (addr + 2, info);
292 	      cmd_len += 2;
293 	      sprintf (op, "0x%04x", PS (dst));
294 	      sprintf (comm, "PC rel. 0x%04x",
295 		       PS ((short) addr + 2 + dst));
296 	      if (extended_dst)
297 		{
298 		  dst |= extended_dst << 16;
299 		  sprintf (op, "0x%05x", dst & 0xffff);
300 		  sprintf (comm, "PC rel. 0x%05lx",
301 			   (long)((addr + 2 + dst) & 0xfffff));
302 		}
303 	    }
304 	  else if (regd == 2)
305 	    {
306 	      /* Absolute.  */
307 	      dst = msp430dis_opcode (addr + 2, info);
308 	      cmd_len += 2;
309 	      sprintf (op, "&0x%04x", PS (dst));
310 	      if (extended_dst)
311 		{
312 		  dst |= extended_dst << 16;
313 		  sprintf (op, "&0x%05x", dst & 0xfffff);
314 		}
315 	    }
316 	  else if (regd == 3)
317 	    {
318 	      *cycles = 1;
319 	      sprintf (op, "#1");
320 	      sprintf (comm, "r3 As==01");
321 	    }
322 	  else
323 	    {
324 	      /* Indexed.  */
325 	      dst = msp430dis_opcode (addr + 2, info);
326 	      cmd_len += 2;
327 	      if (extended_dst)
328 		{
329 		  dst |= extended_dst << 16;
330 		  if (dst & 0x80000)
331 		    dst |= -1 << 20;
332 		}
333 	      else if (dst & 0x8000)
334 		dst |= -1 << 16;
335 	      sprintf (op, "%d(r%d)", dst, regd);
336 	      if (dst > 9 || dst < 0)
337 		sprintf (comm, "%05x", dst);
338 	    }
339 	}
340       break;
341 
342     case 3:			/* Jumps.  */
343       where = insn & 0x03ff;
344       if (where & 0x200)
345 	where |= ~0x03ff;
346       if (where > 512 || where < -511)
347 	return 0;
348 
349       where *= 2;
350       sprintf (op, "$%+-8d", where + 2);
351       sprintf (comm, "abs 0x%lx", (long) (addr + 2 + where));
352       *cycles = 2;
353       return 2;
354       break;
355     default:
356       cmd_len = 0;
357     }
358 
359   return cmd_len;
360 }
361 
362 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)363 msp430_doubleoperand (disassemble_info *info,
364 		      struct msp430_opcode_s *opcode,
365 		      bfd_vma addr,
366 		      unsigned short insn,
367 		      char *op1,
368 		      char *op2,
369 		      char *comm1,
370 		      char *comm2,
371 		      unsigned short extension_word,
372 		      int *cycles)
373 {
374   int regs = 0, regd = 0;
375   int ad = 0, as = 0;
376   int cmd_len = 2;
377   int dst = 0;
378   int fmt;
379   int extended_dst = extension_word & 0xf;
380   int extended_src = (extension_word >> 7) & 0xf;
381 
382   regd = insn & 0x0f;
383   regs = (insn & 0x0f00) >> 8;
384   as = (insn & 0x0030) >> 4;
385   ad = (insn & 0x0080) >> 7;
386 
387   if (opcode->fmt < 0)
388     fmt = (- opcode->fmt) - 1;
389   else
390     fmt = opcode->fmt;
391 
392   if (fmt == 0)
393     {
394       /* Special case: rla and rlc are the only 2 emulated instructions that
395 	 fall into two operand instructions.  */
396       /* With dst, there are only:
397 	 Rm       	Register,
398          x(Rm)     	Indexed,
399          0xXXXX    	Relative,
400          &0xXXXX    	Absolute
401          emulated_ins   dst
402          basic_ins      dst, dst.  */
403 
404       if (regd != regs || as != ad)
405 	return 0;		/* May be 'data' section.  */
406 
407       if (ad == 0)
408 	{
409 	  /* Register mode.  */
410 	  if (regd == 3)
411 	    {
412 	      strcpy (comm1, _("Illegal as emulation instr"));
413 	      return -1;
414 	    }
415 
416 	  sprintf (op1, "r%d", regd);
417 	  *cycles = 1;
418 	}
419       else			/* ad == 1 */
420 	{
421 	  if (regd == 0)
422 	    {
423 	      /* PC relative, Symbolic.  */
424 	      dst = msp430dis_opcode (addr + 2, info);
425 	      cmd_len += 4;
426 	      *cycles = 6;
427 	      sprintf (op1, "0x%04x", PS (dst));
428 	      sprintf (comm1, "PC rel. 0x%04x",
429 		       PS ((short) addr + 2 + dst));
430 	      if (extension_word)
431 		{
432 		  dst |= extended_dst << 16;
433 		  if (dst & 0x80000)
434 		    dst |= -1 << 20;
435 		  sprintf (op1, "0x%05x", dst & 0xfffff);
436 		  sprintf (comm1, "PC rel. 0x%05lx",
437 			   (long)((addr + 2 + dst) & 0xfffff));
438 		}
439 	    }
440 	  else if (regd == 2)
441 	    {
442 	      /* Absolute.  */
443 	      dst = msp430dis_opcode (addr + 2, info);
444 	      /* If the 'src' field is not the same as the dst
445 		 then this is not an rla instruction.  */
446 	      if (dst != msp430dis_opcode (addr + 4, info))
447 		return 0;
448 	      cmd_len += 4;
449 	      *cycles = 6;
450 	      sprintf (op1, "&0x%04x", PS (dst));
451 	      if (extension_word)
452 		{
453 		  dst |= extended_dst << 16;
454 		  sprintf (op1, "&0x%05x", dst & 0xfffff);
455 		}
456 	    }
457 	  else
458 	    {
459 	      /* Indexed.  */
460 	      dst = msp430dis_opcode (addr + 2, info);
461 	      if (extension_word)
462 		{
463 		  dst |= extended_dst << 16;
464 		  if (dst & 0x80000)
465 		    dst |= -1 << 20;
466 		}
467 	      else if (dst & 0x8000)
468 		dst |= -1 << 16;
469 	      cmd_len += 4;
470 	      *cycles = 6;
471 	      sprintf (op1, "%d(r%d)", dst, regd);
472 	      if (dst > 9 || dst < -9)
473 		sprintf (comm1, "#0x%05x", dst);
474 	    }
475 	}
476 
477       *op2 = 0;
478       *comm2 = 0;
479 
480       return cmd_len;
481     }
482 
483   /* Two operands exactly.  */
484   if (ad == 0 && regd == 3)
485     {
486       /* R2/R3 are illegal as dest: may be data section.  */
487       strcpy (comm1, _("Illegal as 2-op instr"));
488       return -1;
489     }
490 
491   /* Source.  */
492   if (as == 0)
493     {
494       *cycles = 1;
495       if (regs == 3)
496 	{
497 	  /* Constants.  */
498 	  sprintf (op1, "#0");
499 	  sprintf (comm1, "r3 As==00");
500 	}
501       else
502 	{
503 	  /* Register.  */
504 	  sprintf (op1, "r%d", regs);
505 	}
506     }
507   else if (as == 2)
508     {
509       * cycles = print_as2_reg_name (regs, op1, comm1, 1, 1, regs == 0 ? 3 : 2);
510     }
511   else if (as == 3)
512     {
513       if (regs == 0)
514 	{
515 	  *cycles = 3;
516 	  /* Absolute. @pc+.  */
517 	  dst = msp430dis_opcode (addr + 2, info);
518 	  cmd_len += 2;
519 	  sprintf (op1, "#%d", dst);
520 	  if (dst > 9 || dst < 0)
521 	    sprintf (comm1, "#0x%04x", PS (dst));
522 	  if (extension_word)
523 	    {
524 	      dst |= extended_src << 16;
525 	      if (dst & 0x80000)
526 		dst |= -1 << 20;
527 	      sprintf (op1, "#%d", dst);
528 	      if (dst > 9 || dst < 0)
529 		sprintf (comm1, "0x%05x", dst & 0xfffff);
530 	    }
531 	}
532       else
533 	* cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2);
534     }
535   else if (as == 1)
536     {
537       if (regs == 0)
538 	{
539 	  *cycles = 4;
540 	  /* PC relative.  */
541 	  dst = msp430dis_opcode (addr + 2, info);
542 	  cmd_len += 2;
543 	  sprintf (op1, "0x%04x", PS (dst));
544 	  sprintf (comm1, "PC rel. 0x%04x",
545 		   PS ((short) addr + 2 + dst));
546 	  if (extension_word)
547 	    {
548 	      dst |= extended_src << 16;
549 	      if (dst & 0x80000)
550 		dst |= -1 << 20;
551 	      sprintf (op1, "0x%05x", dst & 0xfffff);
552 	      sprintf (comm1, "PC rel. 0x%05lx",
553 		       (long) ((addr + 2 + dst) & 0xfffff));
554 	    }
555 	}
556       else if (regs == 2)
557 	{
558 	  *cycles = 2;
559 	  /* Absolute.  */
560 	  dst = msp430dis_opcode (addr + 2, info);
561 	  cmd_len += 2;
562 	  sprintf (op1, "&0x%04x", PS (dst));
563 	  sprintf (comm1, "0x%04x", PS (dst));
564 	  if (extension_word)
565 	    {
566 	      dst |= extended_src << 16;
567 	      sprintf (op1, "&0x%05x", dst & 0xfffff);
568 	      * comm1 = 0;
569 	    }
570 	}
571       else if (regs == 3)
572 	{
573 	  *cycles = 1;
574 	  sprintf (op1, "#1");
575 	  sprintf (comm1, "r3 As==01");
576 	}
577       else
578 	{
579 	  *cycles = 3;
580 	  /* Indexed.  */
581 	  dst = msp430dis_opcode (addr + 2, info);
582 	  cmd_len += 2;
583 	  if (extension_word)
584 	    {
585 	      dst |= extended_src << 16;
586 	      if (dst & 0x80000)
587 		dst |= -1 << 20;
588 	    }
589 	  else if (dst & 0x8000)
590 	    dst |= -1 << 16;
591 	  sprintf (op1, "%d(r%d)", dst, regs);
592 	  if (dst > 9 || dst < -9)
593 	    sprintf (comm1, "0x%05x", dst);
594 	}
595     }
596 
597   /* Destination. Special care needed on addr + XXXX.  */
598 
599   if (ad == 0)
600     {
601       /* Register.  */
602       if (regd == 0)
603 	{
604 	  *cycles += 1;
605 	  sprintf (op2, "r0");
606 	}
607       else if (regd == 1)
608 	sprintf (op2, "r1");
609 
610       else if (regd == 2)
611 	sprintf (op2, "r2");
612 
613       else
614 	sprintf (op2, "r%d", regd);
615     }
616   else	/* ad == 1.  */
617     {
618       * cycles += 3;
619 
620       if (regd == 0)
621 	{
622 	  /* PC relative.  */
623 	  *cycles += 1;
624 	  dst = msp430dis_opcode (addr + cmd_len, info);
625 	  sprintf (op2, "0x%04x", PS (dst));
626 	  sprintf (comm2, "PC rel. 0x%04x",
627 		   PS ((short) addr + cmd_len + dst));
628 	  if (extension_word)
629 	    {
630 	      dst |= extended_dst << 16;
631 	      if (dst & 0x80000)
632 		dst |= -1 << 20;
633 	      sprintf (op2, "0x%05x", dst & 0xfffff);
634 	      sprintf (comm2, "PC rel. 0x%05lx",
635 		       (long)((addr + cmd_len + dst) & 0xfffff));
636 	    }
637 	  cmd_len += 2;
638 	}
639       else if (regd == 2)
640 	{
641 	  /* Absolute.  */
642 	  dst = msp430dis_opcode (addr + cmd_len, info);
643 	  cmd_len += 2;
644 	  sprintf (op2, "&0x%04x", PS (dst));
645 	  if (extension_word)
646 	    {
647 	      dst |= extended_dst << 16;
648 	      sprintf (op2, "&0x%05x", dst & 0xfffff);
649 	    }
650 	}
651       else
652 	{
653 	  dst = msp430dis_opcode (addr + cmd_len, info);
654 	  cmd_len += 2;
655 	  if (dst & 0x8000)
656 	    dst |= -1 << 16;
657 	  if (dst > 9 || dst < 0)
658 	    sprintf (comm2, "0x%04x", PS (dst));
659 	  if (extension_word)
660 	    {
661 	      dst |= extended_dst << 16;
662 	      if (dst & 0x80000)
663 		dst |= -1 << 20;
664 	      if (dst > 9 || dst < 0)
665 		sprintf (comm2, "0x%05x", dst & 0xfffff);
666 	    }
667 	  sprintf (op2, "%d(r%d)", dst, regd);
668 	}
669     }
670 
671   return cmd_len;
672 }
673 
674 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)675 msp430_branchinstr (disassemble_info *info,
676 		    struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
677 		    bfd_vma addr ATTRIBUTE_UNUSED,
678 		    unsigned short insn,
679 		    char *op1,
680 		    char *comm1,
681 		    int *cycles)
682 {
683   int regs = 0, regd = 0;
684   int as = 0;
685   int cmd_len = 2;
686   short dst = 0;
687 
688   regd = insn & 0x0f;
689   regs = (insn & 0x0f00) >> 8;
690   as = (insn & 0x0030) >> 4;
691 
692   if (regd != 0)	/* Destination register is not a PC.  */
693     return 0;
694 
695   /* dst is a source register.  */
696   if (as == 0)
697     {
698       /* Constants.  */
699       if (regs == 3)
700 	{
701 	  *cycles = 1;
702 	  sprintf (op1, "#0");
703 	  sprintf (comm1, "r3 As==00");
704 	}
705       else
706 	{
707 	  /* Register.  */
708 	  *cycles = 1;
709 	  sprintf (op1, "r%d", regs);
710 	}
711     }
712   else if (as == 2)
713     {
714       * cycles = print_as2_reg_name (regs, op1, comm1, 2, 1, 2);
715     }
716   else if (as == 3)
717     {
718       if (regs == 0)
719 	{
720 	  /* Absolute. @pc+  */
721 	  *cycles = 3;
722 	  dst = msp430dis_opcode (addr + 2, info);
723 	  cmd_len += 2;
724 	  sprintf (op1, "#0x%04x", PS (dst));
725 	}
726       else
727 	* cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2);
728     }
729   else if (as == 1)
730     {
731       * cycles = 3;
732 
733       if (regs == 0)
734 	{
735 	  /* PC relative.  */
736 	  dst = msp430dis_opcode (addr + 2, info);
737 	  cmd_len += 2;
738 	  (*cycles)++;
739 	  sprintf (op1, "0x%04x", PS (dst));
740 	  sprintf (comm1, "PC rel. 0x%04x",
741 		   PS ((short) addr + 2 + dst));
742 	}
743       else if (regs == 2)
744 	{
745 	  /* Absolute.  */
746 	  dst = msp430dis_opcode (addr + 2, info);
747 	  cmd_len += 2;
748 	  sprintf (op1, "&0x%04x", PS (dst));
749 	}
750       else if (regs == 3)
751 	{
752 	  (*cycles)--;
753 	  sprintf (op1, "#1");
754 	  sprintf (comm1, "r3 As==01");
755 	}
756       else
757 	{
758 	  /* Indexed.  */
759 	  dst = msp430dis_opcode (addr + 2, info);
760 	  cmd_len += 2;
761 	  if (dst & 0x8000)
762 	    dst |= -1 << 16;
763 	  sprintf (op1, "%d(r%d)", dst, regs);
764 	}
765     }
766 
767   return cmd_len;
768 }
769 
770 static int
msp430x_calla_instr(disassemble_info * info,bfd_vma addr,unsigned short insn,char * op1,char * comm1,int * cycles)771 msp430x_calla_instr (disassemble_info * info,
772 		     bfd_vma            addr,
773 		     unsigned short     insn,
774 		     char *             op1,
775 		     char *             comm1,
776 		     int *              cycles)
777 {
778   unsigned int   ureg = insn & 0xf;
779   int            reg = insn & 0xf;
780   int            am = (insn & 0xf0) >> 4;
781   int            cmd_len = 2;
782   unsigned short udst = 0;
783   short          dst = 0;
784 
785   switch (am)
786     {
787     case 4: /* CALLA Rdst */
788       *cycles = 1;
789       sprintf (op1, "r%d", reg);
790       break;
791 
792     case 5: /* CALLA x(Rdst) */
793       *cycles = 3;
794       dst = msp430dis_opcode (addr + 2, info);
795       cmd_len += 2;
796       sprintf (op1, "%d(r%d)", dst, reg);
797       if (reg == 0)
798 	sprintf (comm1, "PC rel. 0x%05lx", (long) (addr + 2 + dst));
799       else
800 	sprintf (comm1, "0x%05x", dst);
801       break;
802 
803     case 6: /* CALLA @Rdst */
804       *cycles = 2;
805       sprintf (op1, "@r%d", reg);
806       break;
807 
808     case 7: /* CALLA @Rdst+ */
809       *cycles = 2;
810       sprintf (op1, "@r%d+", reg);
811       break;
812 
813     case 8: /* CALLA &abs20 */
814       udst = msp430dis_opcode (addr + 2, info);
815       cmd_len += 2;
816       *cycles = 4;
817       sprintf (op1, "&%d", (ureg << 16) + udst);
818       sprintf (comm1, "0x%05x", (ureg << 16) + udst);
819       break;
820 
821     case 9: /* CALLA pcrel-sym */
822       dst = msp430dis_opcode (addr + 2, info);
823       cmd_len += 2;
824       *cycles = 4;
825       sprintf (op1, "%d(PC)", (reg << 16) + dst);
826       sprintf (comm1, "PC rel. 0x%05lx",
827 	       (long) (addr + 2 + dst + (reg << 16)));
828       break;
829 
830     case 11: /* CALLA #imm20 */
831       udst = msp430dis_opcode (addr + 2, info);
832       cmd_len += 2;
833       *cycles = 4;
834       sprintf (op1, "#%d", (ureg << 16) + udst);
835       sprintf (comm1, "0x%05x", (ureg << 16) + udst);
836       break;
837 
838     default:
839       strcpy (comm1, _("unrecognised CALLA addressing mode"));
840       return -1;
841     }
842 
843   return cmd_len;
844 }
845 
846 int
print_insn_msp430(bfd_vma addr,disassemble_info * info)847 print_insn_msp430 (bfd_vma addr, disassemble_info *info)
848 {
849   void *stream = info->stream;
850   fprintf_ftype prin = info->fprintf_func;
851   struct msp430_opcode_s *opcode;
852   char op1[32], op2[32], comm1[64], comm2[64];
853   int cmd_len = 0;
854   unsigned short insn;
855   int cycles = 0;
856   char *bc = "";
857   unsigned short extension_word = 0;
858 
859   insn = msp430dis_opcode (addr, info);
860   if (insn == (unsigned short) -1)
861     {
862       prin (stream, ".word	0xffff;	????");
863       return 2;
864     }
865 
866   if (((int) addr & 0xffff) > 0xffdf)
867     {
868       (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
869       return 2;
870     }
871 
872   *comm1 = 0;
873   *comm2 = 0;
874 
875   /* Check for an extension word.  */
876   if ((insn & 0xf800) == 0x1800)
877     {
878       extension_word = insn;
879       addr += 2;
880       insn = msp430dis_opcode (addr, info);
881       if (insn == (unsigned short) -1)
882 	{
883 	  prin (stream, ".word	0x%04x, 0xffff;	????",
884 		extension_word);
885 	  return 4;
886 	}
887    }
888 
889   for (opcode = msp430_opcodes; opcode->name; opcode++)
890     {
891       if ((insn & opcode->bin_mask) == opcode->bin_opcode
892 	  && opcode->bin_opcode != 0x9300)
893 	{
894 	  *op1 = 0;
895 	  *op2 = 0;
896 	  *comm1 = 0;
897 	  *comm2 = 0;
898 
899 	  /* r0 as destination. Ad should be zero.  */
900 	  if (opcode->insn_opnumb == 3
901 	      && (insn & 0x000f) == 0
902 	      && (insn & 0x0080) == 0)
903 	    {
904 	      cmd_len +=
905 		msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
906 				    &cycles);
907 	      if (cmd_len)
908 		break;
909 	    }
910 
911 	  switch (opcode->insn_opnumb)
912 	    {
913 	      int n;
914 	      int reg;
915 
916 	    case 4:
917 	      cmd_len += msp430x_calla_instr (info, addr, insn,
918 					      op1, comm1, & cycles);
919 	      break;
920 
921 	    case 5: /* PUSHM/POPM */
922 	      n = (insn & 0xf0) >> 4;
923 	      reg = (insn & 0xf);
924 
925 	      sprintf (op1, "#%d", n + 1);
926 	      if (opcode->bin_opcode == 0x1400)
927 		/* PUSHM */
928 		sprintf (op2, "r%d", reg);
929 	      else
930 		/* POPM */
931 		sprintf (op2, "r%d", reg + n);
932 	      if (insn & 0x100)
933 		sprintf (comm1, "16-bit words");
934 	      else
935 		{
936 		  sprintf (comm1, "20-bit words");
937 		  bc =".a";
938 		}
939 
940 	      cycles = 2; /*FIXME*/
941 	      cmd_len = 2;
942 	      break;
943 
944 	    case 6: /* RRAM, RRCM, RRUM, RLAM.  */
945 	      n = ((insn >> 10) & 0x3) + 1;
946 	      reg = (insn & 0xf);
947 	      if ((insn & 0x10) == 0)
948 		bc =".a";
949 	      sprintf (op1, "#%d", n);
950 	      sprintf (op2, "r%d", reg);
951 	      cycles = 2; /*FIXME*/
952 	      cmd_len = 2;
953 	      break;
954 
955 	    case 8: /* ADDA, CMPA, SUBA.  */
956 	      reg = (insn & 0xf);
957 	      n = (insn >> 8) & 0xf;
958 	      if (insn & 0x40)
959 		{
960 		  sprintf (op1, "r%d", n);
961 		  cmd_len = 2;
962 		}
963 	      else
964 		{
965 		  n <<= 16;
966 		  n |= msp430dis_opcode (addr + 2, info);
967 		  sprintf (op1, "#%d", n);
968 		  if (n > 9 || n < 0)
969 		    sprintf (comm1, "0x%05x", n);
970 		  cmd_len = 4;
971 		}
972 	      sprintf (op2, "r%d", reg);
973 	      cycles = 2; /*FIXME*/
974 	      break;
975 
976 	    case 9: /* MOVA */
977 	      reg = (insn & 0xf);
978 	      n = (insn >> 8) & 0xf;
979 	      switch ((insn >> 4) & 0xf)
980 		{
981 		case 0: /* MOVA @Rsrc, Rdst */
982 		  cmd_len = 2;
983 		  sprintf (op1, "@r%d", n);
984 		  if (strcmp (opcode->name, "bra") != 0)
985 		    sprintf (op2, "r%d", reg);
986 		  break;
987 
988 		case 1: /* MOVA @Rsrc+, Rdst */
989 		  cmd_len = 2;
990 		  if (strcmp (opcode->name, "reta") != 0)
991 		    {
992 		      sprintf (op1, "@r%d+", n);
993 		      if (strcmp (opcode->name, "bra") != 0)
994 			sprintf (op2, "r%d", reg);
995 		    }
996 		  break;
997 
998 		case 2: /* MOVA &abs20, Rdst */
999 		  cmd_len = 4;
1000 		  n <<= 16;
1001 		  n |= msp430dis_opcode (addr + 2, info);
1002 		  sprintf (op1, "&%d", n);
1003 		  if (n > 9 || n < 0)
1004 		    sprintf (comm1, "0x%05x", n);
1005 		  if (strcmp (opcode->name, "bra") != 0)
1006 		    sprintf (op2, "r%d", reg);
1007 		  break;
1008 
1009 		case 3: /* MOVA x(Rsrc), Rdst */
1010 		  cmd_len = 4;
1011 		  if (strcmp (opcode->name, "bra") != 0)
1012 		    sprintf (op2, "r%d", reg);
1013 		  reg = n;
1014 		  n = msp430dis_opcode (addr + 2, info);
1015 		  if (n & 0x8000)
1016 		    n |= -1 << 16;
1017 		  sprintf (op1, "%d(r%d)", n, reg);
1018 		  if (n > 9 || n < 0)
1019 		    {
1020 		      if (reg == 0)
1021 			sprintf (comm1, "PC rel. 0x%05lx",
1022 				 (long) (addr + 2 + n));
1023 		      else
1024 			sprintf (comm1, "0x%05x", n);
1025 		    }
1026 		  break;
1027 
1028 		case 6: /* MOVA Rsrc, &abs20 */
1029 		  cmd_len = 4;
1030 		  reg <<= 16;
1031 		  reg |= msp430dis_opcode (addr + 2, info);
1032 		  sprintf (op1, "r%d", n);
1033 		  sprintf (op2, "&%d", reg);
1034 		  if (reg > 9 || reg < 0)
1035 		    sprintf (comm2, "0x%05x", reg);
1036 		  break;
1037 
1038 		case 7: /* MOVA Rsrc, x(Rdst) */
1039 		  cmd_len = 4;
1040 		  sprintf (op1, "r%d", n);
1041 		  n = msp430dis_opcode (addr + 2, info);
1042 		  if (n & 0x8000)
1043 		    n |= -1 << 16;
1044 		  sprintf (op2, "%d(r%d)", n, reg);
1045 		  if (n > 9 || n < 0)
1046 		    {
1047 		      if (reg == 0)
1048 			sprintf (comm2, "PC rel. 0x%05lx",
1049 				 (long) (addr + 2 + n));
1050 		      else
1051 			sprintf (comm2, "0x%05x", n);
1052 		    }
1053 		  break;
1054 
1055 		case 8: /* MOVA #imm20, Rdst */
1056 		  cmd_len = 4;
1057 		  n <<= 16;
1058 		  n |= msp430dis_opcode (addr + 2, info);
1059 		  if (n & 0x80000)
1060 		    n |= -1 << 20;
1061 		  sprintf (op1, "#%d", n);
1062 		  if (n > 9 || n < 0)
1063 		    sprintf (comm1, "0x%05x", n);
1064 		  if (strcmp (opcode->name, "bra") != 0)
1065 		    sprintf (op2, "r%d", reg);
1066 		  break;
1067 
1068 		case 12: /* MOVA Rsrc, Rdst */
1069 		  cmd_len = 2;
1070 		  sprintf (op1, "r%d", n);
1071 		  if (strcmp (opcode->name, "bra") != 0)
1072 		    sprintf (op2, "r%d", reg);
1073 		  break;
1074 
1075 		default:
1076 		  break;
1077 		}
1078 	      cycles = 2; /* FIXME */
1079 	      break;
1080 	    }
1081 
1082 	  if (cmd_len)
1083 	    break;
1084 
1085 	  switch (opcode->insn_opnumb)
1086 	    {
1087 	    case 0:
1088 	      cmd_len += msp430_nooperands (opcode, addr, insn, comm1, &cycles);
1089 	      break;
1090 	    case 2:
1091 	      cmd_len +=
1092 		msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
1093 				      comm1, comm2,
1094 				      extension_word,
1095 				      &cycles);
1096 	      if (insn & BYTE_OPERATION)
1097 		{
1098 		  if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0))
1099 		    bc = ".a";
1100 		  else
1101 		    bc = ".b";
1102 		}
1103 	      else if (extension_word)
1104 		{
1105 		  if (extension_word & (1 << 6))
1106 		    bc = ".w";
1107 		  else
1108 		    {
1109 		      bc = ".?";
1110 		      sprintf (comm2, _("Reserved use of A/L and B/W bits detected"));
1111 		    }
1112 		}
1113 
1114 	      break;
1115 	    case 1:
1116 	      cmd_len +=
1117 		msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
1118 				      extension_word,
1119 				      &cycles);
1120 	      if (extension_word
1121 		  && (strcmp (opcode->name, "swpb") == 0
1122 		      || strcmp (opcode->name, "sxt") == 0))
1123 		{
1124 		  if (insn & BYTE_OPERATION)
1125 		    {
1126 		      bc = ".?";
1127 		      sprintf (comm2, _("Reserved use of A/L and B/W bits detected"));
1128 		    }
1129 		  else if (extension_word & BYTE_OPERATION)
1130 		    bc = ".w";
1131 		  else
1132 		    bc = ".a";
1133 		}
1134 	      else if (insn & BYTE_OPERATION && opcode->fmt != 3)
1135 		{
1136 		  if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0))
1137 		    bc = ".a";
1138 		  else
1139 		    bc = ".b";
1140 		}
1141 	      else if (extension_word)
1142 		{
1143 		  if (extension_word & (1 << 6))
1144 		    bc = ".w";
1145 		  else
1146 		    {
1147 		      bc = ".?";
1148 		      sprintf (comm2, _("Reserved use of A/L and B/W bits detected"));
1149 		    }
1150 		}
1151 	      break;
1152 	    default:
1153 	      break;
1154 	    }
1155 	}
1156 
1157       if (cmd_len)
1158 	break;
1159     }
1160 
1161   if (cmd_len < 1)
1162     {
1163       /* Unknown opcode, or invalid combination of operands.  */
1164       if (extension_word)
1165 	{
1166 	  prin (stream, ".word	0x%04x, 0x%04x;	????", extension_word, PS (insn));
1167 	  if (*comm1)
1168 	    prin (stream, "\t %s", comm1);
1169 	  return 4;
1170 	}
1171       (*prin) (stream, ".word	0x%04x;	????", PS (insn));
1172       return 2;
1173     }
1174 
1175   /* Display the repeat count (if set) for extended register mode.  */
1176   if (cmd_len == 2 && ((extension_word & 0xf) != 0))
1177     {
1178       if (extension_word & (1 << 7))
1179 	prin (stream, "rpt r%d { ", extension_word & 0xf);
1180       else
1181 	prin (stream, "rpt #%d { ", (extension_word & 0xf) + 1);
1182     }
1183 
1184   if (extension_word && opcode->name[strlen (opcode->name) - 1] != 'x')
1185     (*prin) (stream, "%sx%s", opcode->name, bc);
1186   else
1187     (*prin) (stream, "%s%s", opcode->name, bc);
1188 
1189   if (*op1)
1190     (*prin) (stream, "\t%s", op1);
1191   if (*op2)
1192     (*prin) (stream, ",");
1193 
1194   if (strlen (op1) < 7)
1195     (*prin) (stream, "\t");
1196   if (!strlen (op1))
1197     (*prin) (stream, "\t");
1198 
1199   if (*op2)
1200     (*prin) (stream, "%s", op2);
1201   if (strlen (op2) < 8)
1202     (*prin) (stream, "\t");
1203 
1204   if (*comm1 || *comm2)
1205     (*prin) (stream, ";");
1206   else if (cycles)
1207     {
1208       if (*op2)
1209 	(*prin) (stream, ";");
1210       else
1211 	{
1212 	  if (strlen (op1) < 7)
1213 	    (*prin) (stream, ";");
1214 	  else
1215 	    (*prin) (stream, "\t;");
1216 	}
1217     }
1218   if (*comm1)
1219     (*prin) (stream, "%s", comm1);
1220   if (*comm1 && *comm2)
1221     (*prin) (stream, ",");
1222   if (*comm2)
1223     (*prin) (stream, " %s", comm2);
1224 
1225   if (extension_word)
1226     cmd_len += 2;
1227 
1228   return cmd_len;
1229 }
1230