1/* Disassemble MSP430 instructions.
2   Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
3
4   Contributed by Dmitry Diky <diwil@mail.ru>
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, USA.  */
20
21#include <stdio.h>
22#include <ctype.h>
23#include <string.h>
24#include <sys/types.h>
25
26#include "dis-asm.h"
27#include "opintl.h"
28#include "libiberty.h"
29
30#define DASM_SECTION
31#include "opcode/msp430.h"
32#undef DASM_SECTION
33
34
35#define PS(x)   (0xffff & (x))
36
37static unsigned short
38msp430dis_opcode (bfd_vma addr, disassemble_info *info)
39{
40  bfd_byte buffer[2];
41  int status;
42
43  status = info->read_memory_func (addr, buffer, 2, info);
44  if (status != 0)
45    {
46      info->memory_error_func (status, addr, info);
47      return -1;
48    }
49  return bfd_getl16 (buffer);
50}
51
52static int
53msp430_nooperands (struct msp430_opcode_s *opcode,
54		   bfd_vma addr ATTRIBUTE_UNUSED,
55		   unsigned short insn ATTRIBUTE_UNUSED,
56		   char *comm,
57		   int *cycles)
58{
59  /* Pop with constant.  */
60  if (insn == 0x43b2)
61    return 0;
62  if (insn == opcode->bin_opcode)
63    return 2;
64
65  if (opcode->fmt == 0)
66    {
67      if ((insn & 0x0f00) != 3 || (insn & 0x0f00) != 2)
68	return 0;
69
70      strcpy (comm, "emulated...");
71      *cycles = 1;
72    }
73  else
74    {
75      strcpy (comm, "return from interupt");
76      *cycles = 5;
77    }
78
79  return 2;
80}
81
82static int
83msp430_singleoperand (disassemble_info *info,
84		      struct msp430_opcode_s *opcode,
85		      bfd_vma addr,
86		      unsigned short insn,
87		      char *op,
88		      char *comm,
89		      int *cycles)
90{
91  int regs = 0, regd = 0;
92  int ad = 0, as = 0;
93  int where = 0;
94  int cmd_len = 2;
95  short dst = 0;
96
97  regd = insn & 0x0f;
98  regs = (insn & 0x0f00) >> 8;
99  as = (insn & 0x0030) >> 4;
100  ad = (insn & 0x0080) >> 7;
101
102  switch (opcode->fmt)
103    {
104    case 0:			/* Emulated work with dst register.  */
105      if (regs != 2 && regs != 3 && regs != 1)
106	return 0;
107
108      /* Check if not clr insn.  */
109      if (opcode->bin_opcode == 0x4300 && (ad || as))
110	return 0;
111
112      /* Check if really inc, incd insns.  */
113      if ((opcode->bin_opcode & 0xff00) == 0x5300 && as == 3)
114	return 0;
115
116      if (ad == 0)
117	{
118	  *cycles = 1;
119
120	  /* Register.  */
121	  if (regd == 0)
122	    {
123	      *cycles += 1;
124	      sprintf (op, "r0");
125	    }
126	  else if (regd == 1)
127	    sprintf (op, "r1");
128
129	  else if (regd == 2)
130	    sprintf (op, "r2");
131
132	  else
133	    sprintf (op, "r%d", regd);
134	}
135      else	/* ad == 1 msp430dis_opcode.  */
136	{
137	  if (regd == 0)
138	    {
139	      /* PC relative.  */
140	      dst = msp430dis_opcode (addr + 2, info);
141	      cmd_len += 2;
142	      *cycles = 4;
143	      sprintf (op, "0x%04x", dst);
144	      sprintf (comm, "PC rel. abs addr 0x%04x",
145		       PS ((short) (addr + 2) + dst));
146	    }
147	  else if (regd == 2)
148	    {
149	      /* Absolute.  */
150	      dst = msp430dis_opcode (addr + 2, info);
151	      cmd_len += 2;
152	      *cycles = 4;
153	      sprintf (op, "&0x%04x", PS (dst));
154	    }
155	  else
156	    {
157	      dst = msp430dis_opcode (addr + 2, info);
158	      cmd_len += 2;
159	      *cycles = 4;
160	      sprintf (op, "%d(r%d)", dst, regd);
161	    }
162	}
163      break;
164
165    case 2:	/* rrc, push, call, swpb, rra, sxt, push, call, reti etc...  */
166      if (as == 0)
167	{
168	  if (regd == 3)
169	    {
170	      /* Constsnts.  */
171	      sprintf (op, "#0");
172	      sprintf (comm, "r3 As==00");
173	    }
174	  else
175	    {
176	      /* Register.  */
177	      sprintf (op, "r%d", regd);
178	    }
179	  *cycles = 1;
180	}
181      else if (as == 2)
182	{
183	  *cycles = 1;
184	  if (regd == 2)
185	    {
186	      sprintf (op, "#4");
187	      sprintf (comm, "r2 As==10");
188	    }
189	  else if (regd == 3)
190	    {
191	      sprintf (op, "#2");
192	      sprintf (comm, "r3 As==10");
193	    }
194	  else
195	    {
196	      *cycles = 3;
197	      /* Indexed register mode @Rn.  */
198	      sprintf (op, "@r%d", regd);
199	    }
200	}
201      else if (as == 3)
202	{
203	  *cycles = 1;
204	  if (regd == 2)
205	    {
206	      sprintf (op, "#8");
207	      sprintf (comm, "r2 As==11");
208	    }
209	  else if (regd == 3)
210	    {
211	      sprintf (op, "#-1");
212	      sprintf (comm, "r3 As==11");
213	    }
214	  else if (regd == 0)
215	    {
216	      *cycles = 3;
217	      /* absolute. @pc+ */
218	      dst = msp430dis_opcode (addr + 2, info);
219	      cmd_len += 2;
220	      sprintf (op, "#%d", dst);
221	      sprintf (comm, "#0x%04x", PS (dst));
222	    }
223	  else
224	    {
225	      *cycles = 3;
226	      sprintf (op, "@r%d+", regd);
227	    }
228	}
229      else if (as == 1)
230	{
231	  *cycles = 4;
232	  if (regd == 0)
233	    {
234	      /* PC relative.  */
235	      dst = msp430dis_opcode (addr + 2, info);
236	      cmd_len += 2;
237	      sprintf (op, "0x%04x", PS (dst));
238	      sprintf (comm, "PC rel. 0x%04x",
239		       PS ((short) addr + 2 + dst));
240	    }
241	  else if (regd == 2)
242	    {
243	      /* Absolute.  */
244	      dst = msp430dis_opcode (addr + 2, info);
245	      cmd_len += 2;
246	      sprintf (op, "&0x%04x", PS (dst));
247	    }
248	  else if (regd == 3)
249	    {
250	      *cycles = 1;
251	      sprintf (op, "#1");
252	      sprintf (comm, "r3 As==01");
253	    }
254	  else
255	    {
256	      /* Indexd.  */
257	      dst = msp430dis_opcode (addr + 2, info);
258	      cmd_len += 2;
259	      sprintf (op, "%d(r%d)", dst, regd);
260	    }
261	}
262      break;
263
264    case 3:			/* Jumps.  */
265      where = insn & 0x03ff;
266      if (where & 0x200)
267	where |= ~0x03ff;
268      if (where > 512 || where < -511)
269	return 0;
270
271      where *= 2;
272      sprintf (op, "$%+-8d", where + 2);
273      sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where));
274      *cycles = 2;
275      return 2;
276      break;
277    default:
278      cmd_len = 0;
279    }
280
281  return cmd_len;
282}
283
284static int
285msp430_doubleoperand (disassemble_info *info,
286		      struct msp430_opcode_s *opcode,
287		      bfd_vma addr,
288		      unsigned short insn,
289		      char *op1,
290		      char *op2,
291		      char *comm1,
292		      char *comm2,
293		      int *cycles)
294{
295  int regs = 0, regd = 0;
296  int ad = 0, as = 0;
297  int cmd_len = 2;
298  short dst = 0;
299
300  regd = insn & 0x0f;
301  regs = (insn & 0x0f00) >> 8;
302  as = (insn & 0x0030) >> 4;
303  ad = (insn & 0x0080) >> 7;
304
305  if (opcode->fmt == 0)
306    {
307      /* Special case: rla and rlc are the only 2 emulated instructions that
308	 fall into two operand instructions.  */
309      /* With dst, there are only:
310	 Rm       	Register,
311         x(Rm)     	Indexed,
312         0xXXXX    	Relative,
313         &0xXXXX    	Absolute
314         emulated_ins   dst
315         basic_ins      dst, dst.  */
316
317      if (regd != regs || as != ad)
318	return 0;		/* May be 'data' section.  */
319
320      if (ad == 0)
321	{
322	  /* Register mode.  */
323	  if (regd == 3)
324	    {
325	      strcpy (comm1, _("Illegal as emulation instr"));
326	      return -1;
327	    }
328
329	  sprintf (op1, "r%d", regd);
330	  *cycles = 1;
331	}
332      else			/* ad == 1 */
333	{
334	  if (regd == 0)
335	    {
336	      /* PC relative, Symbolic.  */
337	      dst = msp430dis_opcode (addr + 2, info);
338	      cmd_len += 4;
339	      *cycles = 6;
340	      sprintf (op1, "0x%04x", PS (dst));
341	      sprintf (comm1, "PC rel. 0x%04x",
342		       PS ((short) addr + 2 + dst));
343
344	    }
345	  else if (regd == 2)
346	    {
347	      /* Absolute.  */
348	      dst = msp430dis_opcode (addr + 2, info);
349	      /* If the 'src' field is not the same as the dst
350		 then this is not an rla instruction.  */
351	      if (dst != msp430dis_opcode (addr + 4, info))
352		return 0;
353	      cmd_len += 4;
354	      *cycles = 6;
355	      sprintf (op1, "&0x%04x", PS (dst));
356	    }
357	  else
358	    {
359	      /* Indexed.  */
360	      dst = msp430dis_opcode (addr + 2, info);
361	      cmd_len += 4;
362	      *cycles = 6;
363	      sprintf (op1, "%d(r%d)", dst, regd);
364	    }
365	}
366
367      *op2 = 0;
368      *comm2 = 0;
369      return cmd_len;
370    }
371
372  /* Two operands exactly.  */
373  if (ad == 0 && regd == 3)
374    {
375      /* R2/R3 are illegal as dest: may be data section.  */
376      strcpy (comm1, _("Illegal as 2-op instr"));
377      return -1;
378    }
379
380  /* Source.  */
381  if (as == 0)
382    {
383      *cycles = 1;
384      if (regs == 3)
385	{
386	  /* Constsnts.  */
387	  sprintf (op1, "#0");
388	  sprintf (comm1, "r3 As==00");
389	}
390      else
391	{
392	  /* Register.  */
393	  sprintf (op1, "r%d", regs);
394	}
395    }
396  else if (as == 2)
397    {
398      *cycles = 1;
399
400      if (regs == 2)
401	{
402	  sprintf (op1, "#4");
403	  sprintf (comm1, "r2 As==10");
404	}
405      else if (regs == 3)
406	{
407	  sprintf (op1, "#2");
408	  sprintf (comm1, "r3 As==10");
409	}
410      else
411	{
412	  *cycles = 2;
413
414	  /* Indexed register mode @Rn.  */
415	  sprintf (op1, "@r%d", regs);
416	}
417      if (!regs)
418	*cycles = 3;
419    }
420  else if (as == 3)
421    {
422      if (regs == 2)
423	{
424	  sprintf (op1, "#8");
425	  sprintf (comm1, "r2 As==11");
426	  *cycles = 1;
427	}
428      else if (regs == 3)
429	{
430	  sprintf (op1, "#-1");
431	  sprintf (comm1, "r3 As==11");
432	  *cycles = 1;
433	}
434      else if (regs == 0)
435	{
436	  *cycles = 3;
437	  /* Absolute. @pc+.  */
438	  dst = msp430dis_opcode (addr + 2, info);
439	  cmd_len += 2;
440	  sprintf (op1, "#%d", dst);
441	  sprintf (comm1, "#0x%04x", PS (dst));
442	}
443      else
444	{
445	  *cycles = 2;
446	  sprintf (op1, "@r%d+", regs);
447	}
448    }
449  else if (as == 1)
450    {
451      if (regs == 0)
452	{
453	  *cycles = 4;
454	  /* PC relative.  */
455	  dst = msp430dis_opcode (addr + 2, info);
456	  cmd_len += 2;
457	  sprintf (op1, "0x%04x", PS (dst));
458	  sprintf (comm1, "PC rel. 0x%04x",
459		   PS ((short) addr + 2 + dst));
460	}
461      else if (regs == 2)
462	{
463	  *cycles = 2;
464	  /* Absolute.  */
465	  dst = msp430dis_opcode (addr + 2, info);
466	  cmd_len += 2;
467	  sprintf (op1, "&0x%04x", PS (dst));
468	  sprintf (comm1, "0x%04x", PS (dst));
469	}
470      else if (regs == 3)
471	{
472	  *cycles = 1;
473	  sprintf (op1, "#1");
474	  sprintf (comm1, "r3 As==01");
475	}
476      else
477	{
478	  *cycles = 3;
479	  /* Indexed.  */
480	  dst = msp430dis_opcode (addr + 2, info);
481	  cmd_len += 2;
482	  sprintf (op1, "%d(r%d)", dst, regs);
483	}
484    }
485
486  /* Destination. Special care needed on addr + XXXX.  */
487
488  if (ad == 0)
489    {
490      /* Register.  */
491      if (regd == 0)
492	{
493	  *cycles += 1;
494	  sprintf (op2, "r0");
495	}
496      else if (regd == 1)
497	sprintf (op2, "r1");
498
499      else if (regd == 2)
500	sprintf (op2, "r2");
501
502      else
503	sprintf (op2, "r%d", regd);
504    }
505  else	/* ad == 1.  */
506    {
507      * cycles += 3;
508
509      if (regd == 0)
510	{
511	  /* PC relative.  */
512	  *cycles += 1;
513	  dst = msp430dis_opcode (addr + cmd_len, info);
514	  sprintf (op2, "0x%04x", PS (dst));
515	  sprintf (comm2, "PC rel. 0x%04x",
516		   PS ((short) addr + cmd_len + dst));
517	  cmd_len += 2;
518	}
519      else if (regd == 2)
520	{
521	  /* Absolute.  */
522	  dst = msp430dis_opcode (addr + cmd_len, info);
523	  cmd_len += 2;
524	  sprintf (op2, "&0x%04x", PS (dst));
525	}
526      else
527	{
528	  dst = msp430dis_opcode (addr + cmd_len, info);
529	  cmd_len += 2;
530	  sprintf (op2, "%d(r%d)", dst, regd);
531	}
532    }
533
534  return cmd_len;
535}
536
537static int
538msp430_branchinstr (disassemble_info *info,
539		    struct msp430_opcode_s *opcode ATTRIBUTE_UNUSED,
540		    bfd_vma addr ATTRIBUTE_UNUSED,
541		    unsigned short insn,
542		    char *op1,
543		    char *comm1,
544		    int *cycles)
545{
546  int regs = 0, regd = 0;
547  int ad = 0, as = 0;
548  int cmd_len = 2;
549  short dst = 0;
550
551  regd = insn & 0x0f;
552  regs = (insn & 0x0f00) >> 8;
553  as = (insn & 0x0030) >> 4;
554  ad = (insn & 0x0080) >> 7;
555
556  if (regd != 0)	/* Destination register is not a PC.  */
557    return 0;
558
559  /* dst is a source register.  */
560  if (as == 0)
561    {
562      /* Constants.  */
563      if (regs == 3)
564	{
565	  *cycles = 1;
566	  sprintf (op1, "#0");
567	  sprintf (comm1, "r3 As==00");
568	}
569      else
570	{
571	  /* Register.  */
572	  *cycles = 1;
573	  sprintf (op1, "r%d", regs);
574	}
575    }
576  else if (as == 2)
577    {
578      if (regs == 2)
579	{
580	  *cycles = 2;
581	  sprintf (op1, "#4");
582	  sprintf (comm1, "r2 As==10");
583	}
584      else if (regs == 3)
585	{
586	  *cycles = 1;
587	  sprintf (op1, "#2");
588	  sprintf (comm1, "r3 As==10");
589	}
590      else
591	{
592	  /* Indexed register mode @Rn.  */
593	  *cycles = 2;
594	  sprintf (op1, "@r%d", regs);
595	}
596    }
597  else if (as == 3)
598    {
599      if (regs == 2)
600	{
601	  *cycles = 1;
602	  sprintf (op1, "#8");
603	  sprintf (comm1, "r2 As==11");
604	}
605      else if (regs == 3)
606	{
607	  *cycles = 1;
608	  sprintf (op1, "#-1");
609	  sprintf (comm1, "r3 As==11");
610	}
611      else if (regs == 0)
612	{
613	  /* Absolute. @pc+  */
614	  *cycles = 3;
615	  dst = msp430dis_opcode (addr + 2, info);
616	  cmd_len += 2;
617	  sprintf (op1, "#0x%04x", PS (dst));
618	}
619      else
620	{
621	  *cycles = 2;
622	  sprintf (op1, "@r%d+", regs);
623	}
624    }
625  else if (as == 1)
626    {
627      * cycles = 3;
628
629      if (regs == 0)
630	{
631	  /* PC relative.  */
632	  dst = msp430dis_opcode (addr + 2, info);
633	  cmd_len += 2;
634	  (*cycles)++;
635	  sprintf (op1, "0x%04x", PS (dst));
636	  sprintf (comm1, "PC rel. 0x%04x",
637		   PS ((short) addr + 2 + dst));
638	}
639      else if (regs == 2)
640	{
641	  /* Absolute.  */
642	  dst = msp430dis_opcode (addr + 2, info);
643	  cmd_len += 2;
644	  sprintf (op1, "&0x%04x", PS (dst));
645	}
646      else if (regs == 3)
647	{
648	  (*cycles)--;
649	  sprintf (op1, "#1");
650	  sprintf (comm1, "r3 As==01");
651	}
652      else
653	{
654	  /* Indexd.  */
655	  dst = msp430dis_opcode (addr + 2, info);
656	  cmd_len += 2;
657	  sprintf (op1, "%d(r%d)", dst, regs);
658	}
659    }
660
661  return cmd_len;
662}
663
664int
665print_insn_msp430 (bfd_vma addr, disassemble_info *info)
666{
667  void *stream = info->stream;
668  fprintf_ftype prin = info->fprintf_func;
669  struct msp430_opcode_s *opcode;
670  char op1[32], op2[32], comm1[64], comm2[64];
671  int cmd_len = 0;
672  unsigned short insn;
673  int cycles = 0;
674  char *bc = "";
675  char dinfo[32];		/* Debug purposes.  */
676
677  insn = msp430dis_opcode (addr, info);
678  sprintf (dinfo, "0x%04x", insn);
679
680  if (((int) addr & 0xffff) > 0xffdf)
681    {
682      (*prin) (stream, "interrupt service routine at 0x%04x", 0xffff & insn);
683      return 2;
684    }
685
686  *comm1 = 0;
687  *comm2 = 0;
688
689  for (opcode = msp430_opcodes; opcode->name; opcode++)
690    {
691      if ((insn & opcode->bin_mask) == opcode->bin_opcode
692	  && opcode->bin_opcode != 0x9300)
693	{
694	  *op1 = 0;
695	  *op2 = 0;
696	  *comm1 = 0;
697	  *comm2 = 0;
698
699	  /* r0 as destination. Ad should be zero.  */
700	  if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0
701	      && (0x0080 & insn) == 0)
702	    {
703	      cmd_len =
704		msp430_branchinstr (info, opcode, addr, insn, op1, comm1,
705				    &cycles);
706	      if (cmd_len)
707		break;
708	    }
709
710	  switch (opcode->insn_opnumb)
711	    {
712	    case 0:
713	      cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles);
714	      break;
715	    case 2:
716	      cmd_len =
717		msp430_doubleoperand (info, opcode, addr, insn, op1, op2,
718				      comm1, comm2, &cycles);
719	      if (insn & BYTE_OPERATION)
720		bc = ".b";
721	      break;
722	    case 1:
723	      cmd_len =
724		msp430_singleoperand (info, opcode, addr, insn, op1, comm1,
725				      &cycles);
726	      if (insn & BYTE_OPERATION && opcode->fmt != 3)
727		bc = ".b";
728	      break;
729	    default:
730	      break;
731	    }
732	}
733
734      if (cmd_len)
735	break;
736    }
737
738  dinfo[5] = 0;
739
740  if (cmd_len < 1)
741    {
742      /* Unknown opcode, or invalid combination of operands.  */
743      (*prin) (stream, ".word	0x%04x;	????", PS (insn));
744      return 2;
745    }
746
747  (*prin) (stream, "%s%s", opcode->name, bc);
748
749  if (*op1)
750    (*prin) (stream, "\t%s", op1);
751  if (*op2)
752    (*prin) (stream, ",");
753
754  if (strlen (op1) < 7)
755    (*prin) (stream, "\t");
756  if (!strlen (op1))
757    (*prin) (stream, "\t");
758
759  if (*op2)
760    (*prin) (stream, "%s", op2);
761  if (strlen (op2) < 8)
762    (*prin) (stream, "\t");
763
764  if (*comm1 || *comm2)
765    (*prin) (stream, ";");
766  else if (cycles)
767    {
768      if (*op2)
769	(*prin) (stream, ";");
770      else
771	{
772	  if (strlen (op1) < 7)
773	    (*prin) (stream, ";");
774	  else
775	    (*prin) (stream, "\t;");
776	}
777    }
778  if (*comm1)
779    (*prin) (stream, "%s", comm1);
780  if (*comm1 && *comm2)
781    (*prin) (stream, ",");
782  if (*comm2)
783    (*prin) (stream, " %s", comm2);
784  return cmd_len;
785}
786