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