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