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