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