Home | History | Annotate | Download | only in cpu
      1 /* IP2K opcode support.  -*- C -*-
      2    Copyright 2002, 2005, 2011 Free Software Foundation, Inc.
      3 
      4    Contributed by Red Hat Inc;
      5 
      6    This file is part of the GNU Binutils.
      7 
      8    This program is free software; you can redistribute it and/or modify
      9    it under the terms of the GNU General Public License as published by
     10    the Free Software Foundation; either version 3 of the License, or
     11    (at your option) any later version.
     12 
     13    This program is distributed in the hope that it will be useful,
     14    but WITHOUT ANY WARRANTY; without even the implied warranty of
     15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16    GNU General Public License for more details.
     17 
     18    You should have received a copy of the GNU General Public License
     19    along with this program; if not, write to the Free Software
     20    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     21    MA 02110-1301, USA.  */
     22 
     23 /*
     24    Each section is delimited with start and end markers.
     25 
     26    <arch>-opc.h additions use: "-- opc.h"
     27    <arch>-opc.c additions use: "-- opc.c"
     28    <arch>-asm.c additions use: "-- asm.c"
     29    <arch>-dis.c additions use: "-- dis.c"
     30    <arch>-ibd.h additions use: "-- ibd.h".  */
     31 
     32 /* -- opc.h */
     34 
     35 /* Check applicability of instructions against machines.  */
     36 #define CGEN_VALIDATE_INSN_SUPPORTED
     37 
     38 /* Allows reason codes to be output when assembler errors occur.  */
     39 #define CGEN_VERBOSE_ASSEMBLER_ERRORS
     40 
     41 /* Override disassembly hashing - there are variable bits in the top
     42    byte of these instructions.  */
     43 #define CGEN_DIS_HASH_SIZE 8
     44 #define CGEN_DIS_HASH(buf, value) \
     45   (((* (unsigned char*) (buf)) >> 5) % CGEN_DIS_HASH_SIZE)
     46 
     47 #define CGEN_ASM_HASH_SIZE 127
     48 #define CGEN_ASM_HASH(insn) ip2k_asm_hash (insn)
     49 
     50 extern unsigned int ip2k_asm_hash (const char *);
     51 extern int ip2k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
     52 
     53 /* -- opc.c */
     55 
     56 #include "safe-ctype.h"
     57 
     58 /* A better hash function for instruction mnemonics.  */
     59 unsigned int
     60 ip2k_asm_hash (const char* insn)
     61 {
     62   unsigned int hash;
     63   const char* m = insn;
     64 
     65   for (hash = 0; *m && ! ISSPACE (*m); m++)
     66     hash = (hash * 23) ^ (0x1F & TOLOWER (*m));
     67 
     68   /* printf ("%s %d\n", insn, (hash % CGEN_ASM_HASH_SIZE)); */
     69 
     70   return hash % CGEN_ASM_HASH_SIZE;
     71 }
     72 
     73 
     74 /* Special check to ensure that instruction exists for given machine.  */
     75 
     76 int
     77 ip2k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
     78 {
     79   int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);
     80 
     81   /* No mach attribute?  Assume it's supported for all machs.  */
     82   if (machs == 0)
     83     return 1;
     84 
     85   return (machs & cd->machs) != 0;
     86 }
     87 
     88 
     89 /* -- asm.c */
     91 
     92 static const char *
     93 parse_fr (CGEN_CPU_DESC cd,
     94 	  const char **strp,
     95 	  int opindex,
     96 	  unsigned long *valuep)
     97 {
     98   const char *errmsg;
     99   const char *old_strp;
    100   char *afteroffset;
    101   enum cgen_parse_operand_result result_type;
    102   bfd_vma value;
    103   extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
    104   bfd_vma tempvalue;
    105 
    106   old_strp = *strp;
    107   afteroffset = NULL;
    108 
    109   /* Check here to see if you're about to try parsing a w as the first arg
    110      and return an error if you are.  */
    111   if ((strncmp (*strp, "w", 1) == 0) || (strncmp (*strp, "W", 1) == 0))
    112     {
    113       (*strp)++;
    114 
    115       if ((strncmp (*strp, ",", 1) == 0) || ISSPACE (**strp))
    116 	{
    117 	  /* We've been passed a w.  Return with an error message so that
    118 	     cgen will try the next parsing option.  */
    119 	  errmsg = _("W keyword invalid in FR operand slot.");
    120 	  return errmsg;
    121 	}
    122       *strp = old_strp;
    123     }
    124 
    125   /* Attempt parse as register keyword. */
    126   errmsg = cgen_parse_keyword (cd, strp, & ip2k_cgen_opval_register_names,
    127 			       (long *) valuep);
    128   if (*strp != NULL
    129       && errmsg == NULL)
    130     return errmsg;
    131 
    132   /* Attempt to parse for "(IP)".  */
    133   afteroffset = strstr (*strp, "(IP)");
    134 
    135   if (afteroffset == NULL)
    136     /* Make sure it's not in lower case.  */
    137     afteroffset = strstr (*strp, "(ip)");
    138 
    139   if (afteroffset != NULL)
    140     {
    141       if (afteroffset != *strp)
    142 	{
    143 	  /* Invalid offset present.  */
    144 	  errmsg = _("offset(IP) is not a valid form");
    145 	  return errmsg;
    146 	}
    147       else
    148 	{
    149 	  *strp += 4;
    150 	  *valuep = 0;
    151 	  errmsg = NULL;
    152 	  return errmsg;
    153 	}
    154     }
    155 
    156   /* Attempt to parse for DP. ex: mov w, offset(DP)
    157                                   mov offset(DP),w   */
    158 
    159   /* Try parsing it as an address and see what comes back.  */
    160   afteroffset = strstr (*strp, "(DP)");
    161 
    162   if (afteroffset == NULL)
    163     /* Maybe it's in lower case.  */
    164     afteroffset = strstr (*strp, "(dp)");
    165 
    166   if (afteroffset != NULL)
    167     {
    168       if (afteroffset == *strp)
    169 	{
    170 	  /* No offset present. Use 0 by default.  */
    171 	  tempvalue = 0;
    172 	  errmsg = NULL;
    173 	}
    174       else
    175 	errmsg = cgen_parse_address (cd, strp, opindex,
    176 				     BFD_RELOC_IP2K_FR_OFFSET,
    177 				     & result_type, & tempvalue);
    178 
    179       if (errmsg == NULL)
    180 	{
    181 	  if (tempvalue <= 127)
    182 	    {
    183 	      /* Value is ok.  Fix up the first 2 bits and return.  */
    184 	      *valuep = 0x0100 | tempvalue;
    185 	      *strp += 4; /* Skip over the (DP) in *strp.  */
    186 	      return errmsg;
    187 	    }
    188 	  else
    189 	    {
    190 	      /* Found something there in front of (DP) but it's out
    191 		 of range.  */
    192 	      errmsg = _("(DP) offset out of range.");
    193 	      return errmsg;
    194 	    }
    195 	}
    196     }
    197 
    198 
    199   /* Attempt to parse for SP. ex: mov w, offset(SP)
    200                                   mov offset(SP), w.  */
    201   afteroffset = strstr (*strp, "(SP)");
    202 
    203   if (afteroffset == NULL)
    204     /* Maybe it's in lower case.  */
    205     afteroffset = strstr (*strp, "(sp)");
    206 
    207   if (afteroffset != NULL)
    208     {
    209       if (afteroffset == *strp)
    210 	{
    211 	  /* No offset present. Use 0 by default.  */
    212 	  tempvalue = 0;
    213 	  errmsg = NULL;
    214 	}
    215       else
    216 	errmsg = cgen_parse_address (cd, strp, opindex,
    217 				     BFD_RELOC_IP2K_FR_OFFSET,
    218 				     & result_type, & tempvalue);
    219 
    220       if (errmsg == NULL)
    221 	{
    222 	  if (tempvalue <= 127)
    223 	    {
    224 	      /* Value is ok.  Fix up the first 2 bits and return.  */
    225 	      *valuep = 0x0180 | tempvalue;
    226 	      *strp += 4; /* Skip over the (SP) in *strp.  */
    227 	      return errmsg;
    228 	    }
    229 	  else
    230 	    {
    231 	      /* Found something there in front of (SP) but it's out
    232 		 of range.  */
    233 	      errmsg = _("(SP) offset out of range.");
    234 	      return errmsg;
    235 	    }
    236 	}
    237     }
    238 
    239   /* Attempt to parse as an address.  */
    240   *strp = old_strp;
    241   errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_IP2K_FR9,
    242 			       & result_type, & value);
    243   if (errmsg == NULL)
    244     {
    245       *valuep = value;
    246 
    247       /* If a parenthesis is found, warn about invalid form.  */
    248       if (**strp == '(')
    249 	errmsg = _("illegal use of parentheses");
    250 
    251       /* If a numeric value is specified, ensure that it is between
    252 	 1 and 255.  */
    253       else if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
    254 	{
    255 	  if (value < 0x1 || value > 0xff)
    256 	    errmsg = _("operand out of range (not between 1 and 255)");
    257 	}
    258     }
    259   return errmsg;
    260 }
    261 
    262 static const char *
    263 parse_addr16 (CGEN_CPU_DESC cd,
    264 	      const char **strp,
    265 	      int opindex,
    266 	      unsigned long *valuep)
    267 {
    268   const char *errmsg;
    269   enum cgen_parse_operand_result result_type;
    270   bfd_reloc_code_real_type code = BFD_RELOC_NONE;
    271   bfd_vma value;
    272 
    273   if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16H)
    274     code = BFD_RELOC_IP2K_HI8DATA;
    275   else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16L)
    276     code = BFD_RELOC_IP2K_LO8DATA;
    277   else
    278     {
    279       /* Something is very wrong. opindex has to be one of the above.  */
    280       errmsg = _("parse_addr16: invalid opindex.");
    281       return errmsg;
    282     }
    283 
    284   errmsg = cgen_parse_address (cd, strp, opindex, code,
    285 			       & result_type, & value);
    286   if (errmsg == NULL)
    287     {
    288       /* We either have a relocation or a number now.  */
    289       if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
    290 	{
    291 	  /* We got a number back.  */
    292 	  if (code == BFD_RELOC_IP2K_HI8DATA)
    293             value >>= 8;
    294 	  else
    295 	    /* code = BFD_RELOC_IP2K_LOW8DATA.  */
    296 	    value &= 0x00FF;
    297 	}
    298       *valuep = value;
    299     }
    300 
    301   return errmsg;
    302 }
    303 
    304 static const char *
    305 parse_addr16_cjp (CGEN_CPU_DESC cd,
    306 		  const char **strp,
    307 		  int opindex,
    308 		  unsigned long *valuep)
    309 {
    310   const char *errmsg;
    311   enum cgen_parse_operand_result result_type;
    312   bfd_reloc_code_real_type code = BFD_RELOC_NONE;
    313   bfd_vma value;
    314 
    315   if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
    316     code = BFD_RELOC_IP2K_ADDR16CJP;
    317   else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
    318     code = BFD_RELOC_IP2K_PAGE3;
    319 
    320   errmsg = cgen_parse_address (cd, strp, opindex, code,
    321 			       & result_type, & value);
    322   if (errmsg == NULL)
    323     {
    324       if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
    325 	{
    326 	  if ((value & 0x1) == 0)  /* If the address is even .... */
    327 	    {
    328 	      if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
    329                 *valuep = (value >> 1) & 0x1FFF;  /* Should mask be 1FFF?  */
    330 	      else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
    331                 *valuep = (value >> 14) & 0x7;
    332 	    }
    333           else
    334  	    errmsg = _("Byte address required. - must be even.");
    335 	}
    336       else if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED)
    337 	{
    338 	  /* This will happen for things like (s2-s1) where s2 and s1
    339 	     are labels.  */
    340 	  *valuep = value;
    341 	}
    342       else
    343         errmsg = _("cgen_parse_address returned a symbol. Literal required.");
    344     }
    345   return errmsg;
    346 }
    347 
    348 static const char *
    349 parse_lit8 (CGEN_CPU_DESC cd,
    350 	    const char **strp,
    351 	    int opindex,
    352 	    long *valuep)
    353 {
    354   const char *errmsg;
    355   enum cgen_parse_operand_result result_type;
    356   bfd_reloc_code_real_type code = BFD_RELOC_NONE;
    357   bfd_vma value;
    358 
    359   /* Parse %OP relocating operators.  */
    360   if (strncmp (*strp, "%bank", 5) == 0)
    361     {
    362       *strp += 5;
    363       code = BFD_RELOC_IP2K_BANK;
    364     }
    365   else if (strncmp (*strp, "%lo8data", 8) == 0)
    366     {
    367       *strp += 8;
    368       code = BFD_RELOC_IP2K_LO8DATA;
    369     }
    370   else if (strncmp (*strp, "%hi8data", 8) == 0)
    371     {
    372       *strp += 8;
    373       code = BFD_RELOC_IP2K_HI8DATA;
    374     }
    375   else if (strncmp (*strp, "%ex8data", 8) == 0)
    376     {
    377       *strp += 8;
    378       code = BFD_RELOC_IP2K_EX8DATA;
    379     }
    380   else if (strncmp (*strp, "%lo8insn", 8) == 0)
    381     {
    382       *strp += 8;
    383       code = BFD_RELOC_IP2K_LO8INSN;
    384     }
    385   else if (strncmp (*strp, "%hi8insn", 8) == 0)
    386     {
    387       *strp += 8;
    388       code = BFD_RELOC_IP2K_HI8INSN;
    389     }
    390 
    391   /* Parse %op operand.  */
    392   if (code != BFD_RELOC_NONE)
    393     {
    394       errmsg = cgen_parse_address (cd, strp, opindex, code,
    395 				   & result_type, & value);
    396       if ((errmsg == NULL) &&
    397 	  (result_type != CGEN_PARSE_OPERAND_RESULT_QUEUED))
    398 	errmsg = _("percent-operator operand is not a symbol");
    399 
    400       *valuep = value;
    401     }
    402   /* Parse as a number.  */
    403   else
    404     {
    405       errmsg = cgen_parse_signed_integer (cd, strp, opindex, valuep);
    406 
    407       /* Truncate to eight bits to accept both signed and unsigned input.  */
    408       if (errmsg == NULL)
    409 	*valuep &= 0xFF;
    410     }
    411 
    412   return errmsg;
    413 }
    414 
    415 static const char *
    416 parse_bit3 (CGEN_CPU_DESC cd,
    417 	    const char **strp,
    418 	    int opindex,
    419 	    unsigned long *valuep)
    420 {
    421   const char *errmsg;
    422   char mode = 0;
    423   long count = 0;
    424   unsigned long value;
    425 
    426   if (strncmp (*strp, "%bit", 4) == 0)
    427     {
    428       *strp += 4;
    429       mode = 1;
    430     }
    431   else if (strncmp (*strp, "%msbbit", 7) == 0)
    432     {
    433       *strp += 7;
    434       mode = 1;
    435     }
    436   else if (strncmp (*strp, "%lsbbit", 7) == 0)
    437     {
    438       *strp += 7;
    439       mode = 2;
    440     }
    441 
    442   errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
    443   if (errmsg)
    444     return errmsg;
    445 
    446   if (mode)
    447     {
    448       value = * valuep;
    449       if (value == 0)
    450 	{
    451 	  errmsg = _("Attempt to find bit index of 0");
    452 	  return errmsg;
    453 	}
    454 
    455       if (mode == 1)
    456 	{
    457 	  count = 31;
    458 	  while ((value & 0x80000000) == 0)
    459 	    {
    460 	      count--;
    461 	      value <<= 1;
    462 	    }
    463 	}
    464       else if (mode == 2)
    465 	{
    466 	  count = 0;
    467 	  while ((value & 0x00000001) == 0)
    468 	    {
    469 	      count++;
    470 	      value >>= 1;
    471 	    }
    472 	}
    473 
    474       *valuep = count;
    475     }
    476 
    477   return errmsg;
    478 }
    479 
    480 /* -- dis.c */
    481 
    482 static void
    483 print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    484 	  void * dis_info,
    485 	  long value,
    486 	  unsigned int attrs ATTRIBUTE_UNUSED,
    487 	  bfd_vma pc ATTRIBUTE_UNUSED,
    488 	  int length ATTRIBUTE_UNUSED)
    489 {
    490   disassemble_info *info = (disassemble_info *) dis_info;
    491   const CGEN_KEYWORD_ENTRY *ke;
    492   extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
    493   long offsettest;
    494   long offsetvalue;
    495 
    496   if (value == 0) /* This is (IP).  */
    497     {
    498       (*info->fprintf_func) (info->stream, "%s", "(IP)");
    499       return;
    500     }
    501 
    502   offsettest = value >> 7;
    503   offsetvalue = value & 0x7F;
    504 
    505   /* Check to see if first two bits are 10 -> (DP).  */
    506   if (offsettest == 2)
    507     {
    508       if (offsetvalue == 0)
    509 	(*info->fprintf_func) (info->stream, "%s","(DP)");
    510       else
    511 	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
    512       return;
    513     }
    514 
    515   /* Check to see if first two bits are 11 -> (SP).  */
    516   if (offsettest == 3)
    517     {
    518       if (offsetvalue == 0)
    519 	(*info->fprintf_func) (info->stream, "%s", "(SP)");
    520       else
    521 	(*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
    522       return;
    523     }
    524 
    525   /* Attempt to print as a register keyword.  */
    526   ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);
    527 
    528   if (ke != NULL)
    529     (*info->fprintf_func) (info->stream, "%s", ke->name);
    530   else
    531     /* Print as an address literal.  */
    532     (*info->fprintf_func) (info->stream, "$%02lx", value);
    533 }
    534 
    535 static void
    536 print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    537 		 void * dis_info,
    538 		 long value,
    539 		 unsigned int attrs ATTRIBUTE_UNUSED,
    540 		 bfd_vma pc ATTRIBUTE_UNUSED,
    541 		 int length ATTRIBUTE_UNUSED)
    542 {
    543   disassemble_info *info = (disassemble_info *) dis_info;
    544 
    545   (*info->fprintf_func) (info->stream, "$%lx", value);
    546 }
    547 
    548 static void
    549 print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    550 		  void * dis_info,
    551 		  long value,
    552 		  unsigned int attrs ATTRIBUTE_UNUSED,
    553 		  bfd_vma pc ATTRIBUTE_UNUSED,
    554 		  int length ATTRIBUTE_UNUSED)
    555 {
    556   disassemble_info *info = (disassemble_info *) dis_info;
    557 
    558   (*info->fprintf_func) (info->stream, "$%02lx", value);
    559 }
    560 
    561 static void
    562 print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    563 			 void * dis_info,
    564 			 long value,
    565 			 unsigned int attrs ATTRIBUTE_UNUSED,
    566 			 bfd_vma pc ATTRIBUTE_UNUSED,
    567 			 int length ATTRIBUTE_UNUSED)
    568 {
    569   disassemble_info *info = (disassemble_info *) dis_info;
    570 
    571   /* This is a loadh instruction. Shift the value to the left
    572      by 8 bits so that disassembled code will reassemble properly.  */
    573   value = ((value << 8) & 0xFF00);
    574 
    575   (*info->fprintf_func) (info->stream, "$%04lx", value);
    576 }
    577 
    578 static void
    579 print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    580 			 void * dis_info,
    581 			 long value,
    582 			 unsigned int attrs ATTRIBUTE_UNUSED,
    583 			 bfd_vma pc ATTRIBUTE_UNUSED,
    584 			 int length ATTRIBUTE_UNUSED)
    585 {
    586   disassemble_info *info = (disassemble_info *) dis_info;
    587 
    588   (*info->fprintf_func) (info->stream, "$%04lx", value);
    589 }
    590 
    591 static void
    592 print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    593 		   void * dis_info,
    594 		   long value,
    595 		   unsigned int attrs ATTRIBUTE_UNUSED,
    596 		   bfd_vma pc ATTRIBUTE_UNUSED,
    597 		   int length ATTRIBUTE_UNUSED)
    598 {
    599   disassemble_info *info = (disassemble_info *) dis_info;
    600 
    601   value = ((value << 14) & 0x1C000);
    602   ;value = (value  & 0x1FFFF);
    603   (*info->fprintf_func) (info->stream, "$%05lx", value);
    604 }
    605 
    606 static void
    607 print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    608 		    void * dis_info,
    609 		    long value,
    610 		    unsigned int attrs ATTRIBUTE_UNUSED,
    611 		    bfd_vma pc ATTRIBUTE_UNUSED,
    612 		    int length ATTRIBUTE_UNUSED)
    613 {
    614   disassemble_info *info = (disassemble_info *) dis_info;
    615 
    616   value = ((value << 1) & 0x1FFFF);
    617   (*info->fprintf_func) (info->stream, "$%05lx", value);
    618 }
    619 
    620 static void
    621 print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    622 	       void * dis_info,
    623 	       long value,
    624 	       unsigned int attrs ATTRIBUTE_UNUSED,
    625 	       bfd_vma pc ATTRIBUTE_UNUSED,
    626 	       int length ATTRIBUTE_UNUSED)
    627 {
    628   disassemble_info *info = (disassemble_info *) dis_info;
    629 
    630   (*info->fprintf_func) (info->stream, "%ld", value);
    631 }
    632 
    633 
    634 
    635 /* -- */
    636 
    637