1*3d8817e4Smiod /* Print TI TMS320C80 (MVP) instructions
2*3d8817e4Smiod    Copyright 1996, 1997, 1998, 2000, 2005 Free Software Foundation, Inc.
3*3d8817e4Smiod 
4*3d8817e4Smiod    This file is free software; you can redistribute it and/or modify
5*3d8817e4Smiod    it under the terms of the GNU General Public License as published by
6*3d8817e4Smiod    the Free Software Foundation; either version 2 of the License, or
7*3d8817e4Smiod    (at your option) any later version.
8*3d8817e4Smiod 
9*3d8817e4Smiod    This program is distributed in the hope that it will be useful,
10*3d8817e4Smiod    but WITHOUT ANY WARRANTY; without even the implied warranty of
11*3d8817e4Smiod    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*3d8817e4Smiod    GNU General Public License for more details.
13*3d8817e4Smiod 
14*3d8817e4Smiod    You should have received a copy of the GNU General Public License
15*3d8817e4Smiod    along with this program; if not, write to the Free Software
16*3d8817e4Smiod    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
17*3d8817e4Smiod    MA 02110-1301, USA.  */
18*3d8817e4Smiod 
19*3d8817e4Smiod #include <stdio.h>
20*3d8817e4Smiod 
21*3d8817e4Smiod #include "sysdep.h"
22*3d8817e4Smiod #include "opcode/tic80.h"
23*3d8817e4Smiod #include "dis-asm.h"
24*3d8817e4Smiod 
25*3d8817e4Smiod static int length;
26*3d8817e4Smiod 
27*3d8817e4Smiod /* Print an integer operand.  Try to be somewhat smart about the
28*3d8817e4Smiod    format by assuming that small positive or negative integers are
29*3d8817e4Smiod    probably loop increment values, structure offsets, or similar
30*3d8817e4Smiod    values that are more meaningful printed as signed decimal values.
31*3d8817e4Smiod    Larger numbers are probably better printed as hex values.  */
32*3d8817e4Smiod 
33*3d8817e4Smiod static void
print_operand_integer(struct disassemble_info * info,long value)34*3d8817e4Smiod print_operand_integer (struct disassemble_info *info, long value)
35*3d8817e4Smiod {
36*3d8817e4Smiod   if ((value > 9999 || value < -9999))
37*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%#lx", value);
38*3d8817e4Smiod   else
39*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%ld", value);
40*3d8817e4Smiod }
41*3d8817e4Smiod 
42*3d8817e4Smiod /* FIXME: depends upon sizeof (long) == sizeof (float) and
43*3d8817e4Smiod    also upon host floating point format matching target
44*3d8817e4Smiod    floating point format.  */
45*3d8817e4Smiod 
46*3d8817e4Smiod static void
print_operand_float(struct disassemble_info * info,long value)47*3d8817e4Smiod print_operand_float (struct disassemble_info *info, long value)
48*3d8817e4Smiod {
49*3d8817e4Smiod   union { float f; long l; } fval;
50*3d8817e4Smiod 
51*3d8817e4Smiod   fval.l = value;
52*3d8817e4Smiod   (*info->fprintf_func) (info->stream, "%g", fval.f);
53*3d8817e4Smiod }
54*3d8817e4Smiod 
55*3d8817e4Smiod static void
print_operand_control_register(struct disassemble_info * info,long value)56*3d8817e4Smiod print_operand_control_register (struct disassemble_info *info, long value)
57*3d8817e4Smiod {
58*3d8817e4Smiod   const char *tmp;
59*3d8817e4Smiod 
60*3d8817e4Smiod   tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CR);
61*3d8817e4Smiod   if (tmp != NULL)
62*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%s", tmp);
63*3d8817e4Smiod   else
64*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%#lx", value);
65*3d8817e4Smiod }
66*3d8817e4Smiod 
67*3d8817e4Smiod static void
print_operand_condition_code(struct disassemble_info * info,long value)68*3d8817e4Smiod print_operand_condition_code (struct disassemble_info *info, long value)
69*3d8817e4Smiod {
70*3d8817e4Smiod   const char *tmp;
71*3d8817e4Smiod 
72*3d8817e4Smiod   tmp = tic80_value_to_symbol (value, TIC80_OPERAND_CC);
73*3d8817e4Smiod   if (tmp != NULL)
74*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%s", tmp);
75*3d8817e4Smiod   else
76*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%ld", value);
77*3d8817e4Smiod }
78*3d8817e4Smiod 
79*3d8817e4Smiod static void
print_operand_bitnum(struct disassemble_info * info,long value)80*3d8817e4Smiod print_operand_bitnum (struct disassemble_info *info, long value)
81*3d8817e4Smiod {
82*3d8817e4Smiod   int bitnum;
83*3d8817e4Smiod   const char *tmp;
84*3d8817e4Smiod 
85*3d8817e4Smiod   bitnum = ~value & 0x1F;
86*3d8817e4Smiod   tmp = tic80_value_to_symbol (bitnum, TIC80_OPERAND_BITNUM);
87*3d8817e4Smiod   if (tmp != NULL)
88*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%s", tmp);
89*3d8817e4Smiod   else
90*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%d", bitnum);
91*3d8817e4Smiod }
92*3d8817e4Smiod 
93*3d8817e4Smiod /* Print the operand as directed by the flags.  */
94*3d8817e4Smiod 
95*3d8817e4Smiod #define M_SI(insn,op) ((((op)->flags & TIC80_OPERAND_M_SI) != 0) && ((insn) & (1 << 17)))
96*3d8817e4Smiod #define M_LI(insn,op) ((((op)->flags & TIC80_OPERAND_M_LI) != 0) && ((insn) & (1 << 15)))
97*3d8817e4Smiod #define R_SCALED(insn,op) ((((op)->flags & TIC80_OPERAND_SCALED) != 0) && ((insn) & (1 << 11)))
98*3d8817e4Smiod 
99*3d8817e4Smiod static void
print_operand(struct disassemble_info * info,long value,unsigned long insn,const struct tic80_operand * operand,bfd_vma memaddr)100*3d8817e4Smiod print_operand (struct disassemble_info *info,
101*3d8817e4Smiod 	       long value,
102*3d8817e4Smiod 	       unsigned long insn,
103*3d8817e4Smiod 	       const struct tic80_operand *operand,
104*3d8817e4Smiod 	       bfd_vma memaddr)
105*3d8817e4Smiod {
106*3d8817e4Smiod   if ((operand->flags & TIC80_OPERAND_GPR) != 0)
107*3d8817e4Smiod     {
108*3d8817e4Smiod       (*info->fprintf_func) (info->stream, "r%ld", value);
109*3d8817e4Smiod       if (M_SI (insn, operand) || M_LI (insn, operand))
110*3d8817e4Smiod 	{
111*3d8817e4Smiod 	  (*info->fprintf_func) (info->stream, ":m");
112*3d8817e4Smiod 	}
113*3d8817e4Smiod     }
114*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_FPA) != 0)
115*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "a%ld", value);
116*3d8817e4Smiod 
117*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_PCREL) != 0)
118*3d8817e4Smiod     (*info->print_address_func) (memaddr + 4 * value, info);
119*3d8817e4Smiod 
120*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_BASEREL) != 0)
121*3d8817e4Smiod     (*info->print_address_func) (value, info);
122*3d8817e4Smiod 
123*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_BITNUM) != 0)
124*3d8817e4Smiod     print_operand_bitnum (info, value);
125*3d8817e4Smiod 
126*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_CC) != 0)
127*3d8817e4Smiod     print_operand_condition_code (info, value);
128*3d8817e4Smiod 
129*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_CR) != 0)
130*3d8817e4Smiod     print_operand_control_register (info, value);
131*3d8817e4Smiod 
132*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_FLOAT) != 0)
133*3d8817e4Smiod     print_operand_float (info, value);
134*3d8817e4Smiod 
135*3d8817e4Smiod   else if ((operand->flags & TIC80_OPERAND_BITFIELD))
136*3d8817e4Smiod     (*info->fprintf_func) (info->stream, "%#lx", value);
137*3d8817e4Smiod 
138*3d8817e4Smiod   else
139*3d8817e4Smiod     print_operand_integer (info, value);
140*3d8817e4Smiod 
141*3d8817e4Smiod   /* If this is a scaled operand, then print the modifier.  */
142*3d8817e4Smiod   if (R_SCALED (insn, operand))
143*3d8817e4Smiod     (*info->fprintf_func) (info->stream, ":s");
144*3d8817e4Smiod }
145*3d8817e4Smiod 
146*3d8817e4Smiod /* Get the next 32 bit word from the instruction stream and convert it
147*3d8817e4Smiod    into internal format in the unsigned long INSN, for which we are
148*3d8817e4Smiod    passed the address.  Return 0 on success, -1 on error.  */
149*3d8817e4Smiod 
150*3d8817e4Smiod static int
fill_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long * insnp)151*3d8817e4Smiod fill_instruction (struct disassemble_info *info,
152*3d8817e4Smiod 		  bfd_vma memaddr,
153*3d8817e4Smiod 		  unsigned long *insnp)
154*3d8817e4Smiod {
155*3d8817e4Smiod   bfd_byte buffer[4];
156*3d8817e4Smiod   int status;
157*3d8817e4Smiod 
158*3d8817e4Smiod   /* Get the bits for the next 32 bit word and put in buffer.  */
159*3d8817e4Smiod   status = (*info->read_memory_func) (memaddr + length, buffer, 4, info);
160*3d8817e4Smiod   if (status != 0)
161*3d8817e4Smiod     {
162*3d8817e4Smiod       (*info->memory_error_func) (status, memaddr, info);
163*3d8817e4Smiod       return -1;
164*3d8817e4Smiod     }
165*3d8817e4Smiod 
166*3d8817e4Smiod   /* Read was successful, so increment count of bytes read and convert
167*3d8817e4Smiod      the bits into internal format.  */
168*3d8817e4Smiod 
169*3d8817e4Smiod   length += 4;
170*3d8817e4Smiod   if (info->endian == BFD_ENDIAN_LITTLE)
171*3d8817e4Smiod     *insnp = bfd_getl32 (buffer);
172*3d8817e4Smiod 
173*3d8817e4Smiod   else if (info->endian == BFD_ENDIAN_BIG)
174*3d8817e4Smiod     *insnp = bfd_getb32 (buffer);
175*3d8817e4Smiod 
176*3d8817e4Smiod   else
177*3d8817e4Smiod     /* FIXME: Should probably just default to one or the other.  */
178*3d8817e4Smiod     abort ();
179*3d8817e4Smiod 
180*3d8817e4Smiod   return 0;
181*3d8817e4Smiod }
182*3d8817e4Smiod 
183*3d8817e4Smiod /* We have chosen an opcode table entry.  */
184*3d8817e4Smiod 
185*3d8817e4Smiod static int
print_one_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long insn,const struct tic80_opcode * opcode)186*3d8817e4Smiod print_one_instruction (struct disassemble_info *info,
187*3d8817e4Smiod 		       bfd_vma memaddr,
188*3d8817e4Smiod 		       unsigned long insn,
189*3d8817e4Smiod 		       const struct tic80_opcode *opcode)
190*3d8817e4Smiod {
191*3d8817e4Smiod   const struct tic80_operand *operand;
192*3d8817e4Smiod   long value;
193*3d8817e4Smiod   int status;
194*3d8817e4Smiod   const unsigned char *opindex;
195*3d8817e4Smiod   int close_paren;
196*3d8817e4Smiod 
197*3d8817e4Smiod   (*info->fprintf_func) (info->stream, "%-10s", opcode->name);
198*3d8817e4Smiod 
199*3d8817e4Smiod   for (opindex = opcode->operands; *opindex != 0; opindex++)
200*3d8817e4Smiod     {
201*3d8817e4Smiod       operand = tic80_operands + *opindex;
202*3d8817e4Smiod 
203*3d8817e4Smiod       /* Extract the value from the instruction.  */
204*3d8817e4Smiod       if (operand->extract)
205*3d8817e4Smiod 	value = (*operand->extract) (insn, NULL);
206*3d8817e4Smiod 
207*3d8817e4Smiod       else if (operand->bits == 32)
208*3d8817e4Smiod 	{
209*3d8817e4Smiod 	  status = fill_instruction (info, memaddr, (unsigned long *) &value);
210*3d8817e4Smiod 	  if (status == -1)
211*3d8817e4Smiod 	    return status;
212*3d8817e4Smiod 	}
213*3d8817e4Smiod       else
214*3d8817e4Smiod 	{
215*3d8817e4Smiod 	  value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
216*3d8817e4Smiod 
217*3d8817e4Smiod 	  if ((operand->flags & TIC80_OPERAND_SIGNED) != 0
218*3d8817e4Smiod 	      && (value & (1 << (operand->bits - 1))) != 0)
219*3d8817e4Smiod 	    value -= 1 << operand->bits;
220*3d8817e4Smiod 	}
221*3d8817e4Smiod 
222*3d8817e4Smiod       /* If this operand is enclosed in parenthesis, then print
223*3d8817e4Smiod 	 the open paren, otherwise just print the regular comma
224*3d8817e4Smiod 	 separator, except for the first operand.  */
225*3d8817e4Smiod       if ((operand->flags & TIC80_OPERAND_PARENS) == 0)
226*3d8817e4Smiod 	{
227*3d8817e4Smiod 	  close_paren = 0;
228*3d8817e4Smiod 	  if (opindex != opcode->operands)
229*3d8817e4Smiod 	    (*info->fprintf_func) (info->stream, ",");
230*3d8817e4Smiod 	}
231*3d8817e4Smiod       else
232*3d8817e4Smiod 	{
233*3d8817e4Smiod 	  close_paren = 1;
234*3d8817e4Smiod 	  (*info->fprintf_func) (info->stream, "(");
235*3d8817e4Smiod 	}
236*3d8817e4Smiod 
237*3d8817e4Smiod       print_operand (info, value, insn, operand, memaddr);
238*3d8817e4Smiod 
239*3d8817e4Smiod       /* If we printed an open paren before printing this operand, close
240*3d8817e4Smiod 	 it now. The flag gets reset on each loop.  */
241*3d8817e4Smiod       if (close_paren)
242*3d8817e4Smiod 	(*info->fprintf_func) (info->stream, ")");
243*3d8817e4Smiod     }
244*3d8817e4Smiod 
245*3d8817e4Smiod   return length;
246*3d8817e4Smiod }
247*3d8817e4Smiod 
248*3d8817e4Smiod /* There are no specific bits that tell us for certain whether a vector
249*3d8817e4Smiod    instruction opcode contains one or two instructions.  However since
250*3d8817e4Smiod    a destination register of r0 is illegal, we can check for nonzero
251*3d8817e4Smiod    values in both destination register fields.  Only opcodes that have
252*3d8817e4Smiod    two valid instructions will have non-zero in both.  */
253*3d8817e4Smiod 
254*3d8817e4Smiod #define TWO_INSN(insn) ((((insn) & (0x1F << 27)) != 0) && (((insn) & (0x1F << 22)) != 0))
255*3d8817e4Smiod 
256*3d8817e4Smiod static int
print_instruction(struct disassemble_info * info,bfd_vma memaddr,unsigned long insn,const struct tic80_opcode * vec_opcode)257*3d8817e4Smiod print_instruction (struct disassemble_info *info,
258*3d8817e4Smiod 		   bfd_vma memaddr,
259*3d8817e4Smiod 		   unsigned long insn,
260*3d8817e4Smiod 		   const struct tic80_opcode *vec_opcode)
261*3d8817e4Smiod {
262*3d8817e4Smiod   const struct tic80_opcode *opcode;
263*3d8817e4Smiod   const struct tic80_opcode *opcode_end;
264*3d8817e4Smiod 
265*3d8817e4Smiod   /* Find the first opcode match in the opcodes table.  For vector
266*3d8817e4Smiod      opcodes (vec_opcode != NULL) find the first match that is not the
267*3d8817e4Smiod      previously found match.  FIXME: there should be faster ways to
268*3d8817e4Smiod      search (hash table or binary search), but don't worry too much
269*3d8817e4Smiod      about it until other TIc80 support is finished.  */
270*3d8817e4Smiod 
271*3d8817e4Smiod   opcode_end = tic80_opcodes + tic80_num_opcodes;
272*3d8817e4Smiod   for (opcode = tic80_opcodes; opcode < opcode_end; opcode++)
273*3d8817e4Smiod     {
274*3d8817e4Smiod       if ((insn & opcode->mask) == opcode->opcode &&
275*3d8817e4Smiod 	  opcode != vec_opcode)
276*3d8817e4Smiod 	break;
277*3d8817e4Smiod     }
278*3d8817e4Smiod 
279*3d8817e4Smiod   if (opcode == opcode_end)
280*3d8817e4Smiod     {
281*3d8817e4Smiod       /* No match found, just print the bits as a .word directive.  */
282*3d8817e4Smiod       (*info->fprintf_func) (info->stream, ".word %#08lx", insn);
283*3d8817e4Smiod     }
284*3d8817e4Smiod   else
285*3d8817e4Smiod     {
286*3d8817e4Smiod       /* Match found, decode the instruction.  */
287*3d8817e4Smiod       length = print_one_instruction (info, memaddr, insn, opcode);
288*3d8817e4Smiod       if (opcode->flags & TIC80_VECTOR && vec_opcode == NULL && TWO_INSN (insn))
289*3d8817e4Smiod 	{
290*3d8817e4Smiod 	  /* There is another instruction to print from the same opcode.
291*3d8817e4Smiod 	     Print the separator and then find and print the other
292*3d8817e4Smiod 	     instruction.  */
293*3d8817e4Smiod 	  (*info->fprintf_func) (info->stream, "   ||   ");
294*3d8817e4Smiod 	  length = print_instruction (info, memaddr, insn, opcode);
295*3d8817e4Smiod 	}
296*3d8817e4Smiod     }
297*3d8817e4Smiod 
298*3d8817e4Smiod   return length;
299*3d8817e4Smiod }
300*3d8817e4Smiod 
301*3d8817e4Smiod int
print_insn_tic80(bfd_vma memaddr,struct disassemble_info * info)302*3d8817e4Smiod print_insn_tic80 (bfd_vma memaddr, struct disassemble_info *info)
303*3d8817e4Smiod {
304*3d8817e4Smiod   unsigned long insn;
305*3d8817e4Smiod   int status;
306*3d8817e4Smiod 
307*3d8817e4Smiod   length = 0;
308*3d8817e4Smiod   info->bytes_per_line = 8;
309*3d8817e4Smiod   status = fill_instruction (info, memaddr, &insn);
310*3d8817e4Smiod   if (status != -1)
311*3d8817e4Smiod     status = print_instruction (info, memaddr, insn, NULL);
312*3d8817e4Smiod 
313*3d8817e4Smiod   return status;
314*3d8817e4Smiod }
315