OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [gnu/] [binutils/] [cpu/] [ip2k.opc] - Rev 318

Go to most recent revision | Compare with Previous | Blame | View Log

/* IP2K opcode support.  -*- C -*-
   Copyright 2002, 2005, 2011 Free Software Foundation, Inc.

   Contributed by Red Hat Inc;

   This file is part of the GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

/*
   Each section is delimited with start and end markers.

   <arch>-opc.h additions use: "-- opc.h"
   <arch>-opc.c additions use: "-- opc.c"
   <arch>-asm.c additions use: "-- asm.c"
   <arch>-dis.c additions use: "-- dis.c"
   <arch>-ibd.h additions use: "-- ibd.h".  */

/* -- opc.h */

/* Check applicability of instructions against machines.  */
#define CGEN_VALIDATE_INSN_SUPPORTED

/* Allows reason codes to be output when assembler errors occur.  */
#define CGEN_VERBOSE_ASSEMBLER_ERRORS

/* Override disassembly hashing - there are variable bits in the top
   byte of these instructions.  */
#define CGEN_DIS_HASH_SIZE 8
#define CGEN_DIS_HASH(buf, value) \
  (((* (unsigned char*) (buf)) >> 5) % CGEN_DIS_HASH_SIZE)

#define CGEN_ASM_HASH_SIZE 127
#define CGEN_ASM_HASH(insn) ip2k_asm_hash (insn)

extern unsigned int ip2k_asm_hash (const char *);
extern int ip2k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);

/* -- opc.c */

#include "safe-ctype.h"

/* A better hash function for instruction mnemonics.  */
unsigned int
ip2k_asm_hash (const char* insn)
{
  unsigned int hash;
  const char* m = insn;

  for (hash = 0; *m && ! ISSPACE (*m); m++)
    hash = (hash * 23) ^ (0x1F & TOLOWER (*m));

  /* printf ("%s %d\n", insn, (hash % CGEN_ASM_HASH_SIZE)); */

  return hash % CGEN_ASM_HASH_SIZE;
}


/* Special check to ensure that instruction exists for given machine.  */

int
ip2k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
{
  int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);

  /* No mach attribute?  Assume it's supported for all machs.  */
  if (machs == 0)
    return 1;
  
  return (machs & cd->machs) != 0;
}


/* -- asm.c */

static const char *
parse_fr (CGEN_CPU_DESC cd,
          const char **strp,
          int opindex,
          unsigned long *valuep)
{
  const char *errmsg;
  const char *old_strp;
  char *afteroffset; 
  enum cgen_parse_operand_result result_type;
  bfd_vma value;
  extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
  bfd_vma tempvalue;

  old_strp = *strp;
  afteroffset = NULL;

  /* Check here to see if you're about to try parsing a w as the first arg
     and return an error if you are.  */
  if ((strncmp (*strp, "w", 1) == 0) || (strncmp (*strp, "W", 1) == 0))
    {
      (*strp)++;

      if ((strncmp (*strp, ",", 1) == 0) || ISSPACE (**strp))
        {
          /* We've been passed a w.  Return with an error message so that
             cgen will try the next parsing option.  */
          errmsg = _("W keyword invalid in FR operand slot.");
          return errmsg;
        }
      *strp = old_strp;
    }

  /* Attempt parse as register keyword. */
  errmsg = cgen_parse_keyword (cd, strp, & ip2k_cgen_opval_register_names,
                               (long *) valuep);
  if (*strp != NULL
      && errmsg == NULL)
    return errmsg;

  /* Attempt to parse for "(IP)".  */
  afteroffset = strstr (*strp, "(IP)");

  if (afteroffset == NULL)
    /* Make sure it's not in lower case.  */
    afteroffset = strstr (*strp, "(ip)");

  if (afteroffset != NULL)
    {
      if (afteroffset != *strp)
        {
          /* Invalid offset present.  */
          errmsg = _("offset(IP) is not a valid form");
          return errmsg;
        }
      else
        {
          *strp += 4; 
          *valuep = 0;
          errmsg = NULL;
          return errmsg;
        }
    }

  /* Attempt to parse for DP. ex: mov w, offset(DP)
                                  mov offset(DP),w   */

  /* Try parsing it as an address and see what comes back.  */
  afteroffset = strstr (*strp, "(DP)");

  if (afteroffset == NULL)
    /* Maybe it's in lower case.  */
    afteroffset = strstr (*strp, "(dp)");

  if (afteroffset != NULL)
    {
      if (afteroffset == *strp)
        {
          /* No offset present. Use 0 by default.  */
          tempvalue = 0;
          errmsg = NULL;
        }
      else
        errmsg = cgen_parse_address (cd, strp, opindex,
                                     BFD_RELOC_IP2K_FR_OFFSET,
                                     & result_type, & tempvalue);

      if (errmsg == NULL)
        {
          if (tempvalue <= 127)
            {
              /* Value is ok.  Fix up the first 2 bits and return.  */
              *valuep = 0x0100 | tempvalue;
              *strp += 4; /* Skip over the (DP) in *strp.  */
              return errmsg;
            }
          else
            {
              /* Found something there in front of (DP) but it's out
                 of range.  */
              errmsg = _("(DP) offset out of range.");
              return errmsg;
            }
        }
    }


  /* Attempt to parse for SP. ex: mov w, offset(SP)
                                  mov offset(SP), w.  */
  afteroffset = strstr (*strp, "(SP)");

  if (afteroffset == NULL)
    /* Maybe it's in lower case.  */
    afteroffset = strstr (*strp, "(sp)");

  if (afteroffset != NULL)
    {
      if (afteroffset == *strp)
        {
          /* No offset present. Use 0 by default.  */
          tempvalue = 0;
          errmsg = NULL;
        }
      else
        errmsg = cgen_parse_address (cd, strp, opindex,
                                     BFD_RELOC_IP2K_FR_OFFSET,
                                     & result_type, & tempvalue);

      if (errmsg == NULL)
        {
          if (tempvalue <= 127)
            {
              /* Value is ok.  Fix up the first 2 bits and return.  */
              *valuep = 0x0180 | tempvalue;
              *strp += 4; /* Skip over the (SP) in *strp.  */
              return errmsg;
            }
          else
            {
              /* Found something there in front of (SP) but it's out
                 of range.  */
              errmsg = _("(SP) offset out of range.");
              return errmsg;
            }
        }
    }

  /* Attempt to parse as an address.  */
  *strp = old_strp;
  errmsg = cgen_parse_address (cd, strp, opindex, BFD_RELOC_IP2K_FR9,
                               & result_type, & value);
  if (errmsg == NULL)
    {
      *valuep = value;

      /* If a parenthesis is found, warn about invalid form.  */
      if (**strp == '(')
        errmsg = _("illegal use of parentheses");

      /* If a numeric value is specified, ensure that it is between
         1 and 255.  */
      else if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
        {
          if (value < 0x1 || value > 0xff)
            errmsg = _("operand out of range (not between 1 and 255)");
        }
    }
  return errmsg;
}

static const char *
parse_addr16 (CGEN_CPU_DESC cd,
              const char **strp,
              int opindex,
              unsigned long *valuep)
{
  const char *errmsg;
  enum cgen_parse_operand_result result_type;
  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
  bfd_vma value;

  if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16H)
    code = BFD_RELOC_IP2K_HI8DATA;
  else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16L)
    code = BFD_RELOC_IP2K_LO8DATA;
  else
    {
      /* Something is very wrong. opindex has to be one of the above.  */
      errmsg = _("parse_addr16: invalid opindex.");
      return errmsg;
    }
  
  errmsg = cgen_parse_address (cd, strp, opindex, code,
                               & result_type, & value);
  if (errmsg == NULL)
    {
      /* We either have a relocation or a number now.  */
      if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
        {
          /* We got a number back.  */
          if (code == BFD_RELOC_IP2K_HI8DATA)
            value >>= 8;
          else
            /* code = BFD_RELOC_IP2K_LOW8DATA.  */
            value &= 0x00FF;
        }   
      *valuep = value;
    }

  return errmsg;
}

static const char *
parse_addr16_cjp (CGEN_CPU_DESC cd,
                  const char **strp,
                  int opindex,
                  unsigned long *valuep)
{
  const char *errmsg;
  enum cgen_parse_operand_result result_type;
  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
  bfd_vma value;
 
  if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
    code = BFD_RELOC_IP2K_ADDR16CJP;
  else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
    code = BFD_RELOC_IP2K_PAGE3;

  errmsg = cgen_parse_address (cd, strp, opindex, code,
                               & result_type, & value);
  if (errmsg == NULL)
    {
      if (result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
        {
          if ((value & 0x1) == 0)  /* If the address is even .... */
            {
              if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16CJP)
                *valuep = (value >> 1) & 0x1FFF;  /* Should mask be 1FFF?  */
              else if (opindex == (CGEN_OPERAND_TYPE) IP2K_OPERAND_ADDR16P)
                *valuep = (value >> 14) & 0x7;
            }
          else
            errmsg = _("Byte address required. - must be even.");
        }
      else if (result_type == CGEN_PARSE_OPERAND_RESULT_QUEUED)
        {
          /* This will happen for things like (s2-s1) where s2 and s1
             are labels.  */
          *valuep = value;
        }
      else 
        errmsg = _("cgen_parse_address returned a symbol. Literal required.");
    }
  return errmsg; 
}

static const char *
parse_lit8 (CGEN_CPU_DESC cd,
            const char **strp,
            int opindex,
            long *valuep)
{
  const char *errmsg;
  enum cgen_parse_operand_result result_type;
  bfd_reloc_code_real_type code = BFD_RELOC_NONE;
  bfd_vma value;

  /* Parse %OP relocating operators.  */
  if (strncmp (*strp, "%bank", 5) == 0)
    {
      *strp += 5;
      code = BFD_RELOC_IP2K_BANK;
    }
  else if (strncmp (*strp, "%lo8data", 8) == 0)
    {
      *strp += 8;
      code = BFD_RELOC_IP2K_LO8DATA;
    }
  else if (strncmp (*strp, "%hi8data", 8) == 0)
    {
      *strp += 8;
      code = BFD_RELOC_IP2K_HI8DATA;
    }
  else if (strncmp (*strp, "%ex8data", 8) == 0)
    {
      *strp += 8;
      code = BFD_RELOC_IP2K_EX8DATA;
    }
  else if (strncmp (*strp, "%lo8insn", 8) == 0)
    {
      *strp += 8;
      code = BFD_RELOC_IP2K_LO8INSN;
    }
  else if (strncmp (*strp, "%hi8insn", 8) == 0)
    {
      *strp += 8;
      code = BFD_RELOC_IP2K_HI8INSN;
    }

  /* Parse %op operand.  */
  if (code != BFD_RELOC_NONE)
    {
      errmsg = cgen_parse_address (cd, strp, opindex, code, 
                                   & result_type, & value);
      if ((errmsg == NULL) &&
          (result_type != CGEN_PARSE_OPERAND_RESULT_QUEUED))
        errmsg = _("percent-operator operand is not a symbol");

      *valuep = value;
    }
  /* Parse as a number.  */
  else
    {
      errmsg = cgen_parse_signed_integer (cd, strp, opindex, valuep);

      /* Truncate to eight bits to accept both signed and unsigned input.  */
      if (errmsg == NULL)
        *valuep &= 0xFF;
    }

  return errmsg;
}

static const char *
parse_bit3 (CGEN_CPU_DESC cd,
            const char **strp,
            int opindex,
            unsigned long *valuep)
{
  const char *errmsg;
  char mode = 0;
  long count = 0;
  unsigned long value;

  if (strncmp (*strp, "%bit", 4) == 0)
    {
      *strp += 4;
      mode = 1;
    }
  else if (strncmp (*strp, "%msbbit", 7) == 0)
    {
      *strp += 7;
      mode = 1;
    }
  else if (strncmp (*strp, "%lsbbit", 7) == 0)
    {
      *strp += 7;
      mode = 2;
    }

  errmsg = cgen_parse_unsigned_integer (cd, strp, opindex, valuep);
  if (errmsg)
    return errmsg;

  if (mode)
    {
      value = * valuep;
      if (value == 0)
        {
          errmsg = _("Attempt to find bit index of 0");
          return errmsg;
        }
    
      if (mode == 1)
        {
          count = 31;
          while ((value & 0x80000000) == 0)
            {
              count--;
              value <<= 1;
            }
        }
      else if (mode == 2)
        {
          count = 0;
          while ((value & 0x00000001) == 0)
            {
              count++;
              value >>= 1;
            }
        }
    
      *valuep = count;
    }

  return errmsg;
}

/* -- dis.c */

static void
print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
          void * dis_info,
          long value,
          unsigned int attrs ATTRIBUTE_UNUSED,
          bfd_vma pc ATTRIBUTE_UNUSED,
          int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;
  const CGEN_KEYWORD_ENTRY *ke;
  extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
  long offsettest;
  long offsetvalue;

  if (value == 0) /* This is (IP).  */
    {
      (*info->fprintf_func) (info->stream, "%s", "(IP)");
      return;
    }

  offsettest = value >> 7;
  offsetvalue = value & 0x7F;

  /* Check to see if first two bits are 10 -> (DP).  */
  if (offsettest == 2)
    {
      if (offsetvalue == 0)
        (*info->fprintf_func) (info->stream, "%s","(DP)");
      else
        (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
      return;
    }

  /* Check to see if first two bits are 11 -> (SP).  */
  if (offsettest == 3)
    {
      if (offsetvalue == 0)
        (*info->fprintf_func) (info->stream, "%s", "(SP)");
      else
        (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
      return;
    }

  /* Attempt to print as a register keyword.  */
  ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);

  if (ke != NULL)
    (*info->fprintf_func) (info->stream, "%s", ke->name);
  else
    /* Print as an address literal.  */
    (*info->fprintf_func) (info->stream, "$%02lx", value);
}

static void
print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                 void * dis_info,
                 long value,
                 unsigned int attrs ATTRIBUTE_UNUSED,
                 bfd_vma pc ATTRIBUTE_UNUSED,
                 int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  (*info->fprintf_func) (info->stream, "$%lx", value);
}

static void
print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                  void * dis_info,
                  long value,
                  unsigned int attrs ATTRIBUTE_UNUSED,
                  bfd_vma pc ATTRIBUTE_UNUSED,
                  int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  (*info->fprintf_func) (info->stream, "$%02lx", value);
}

static void
print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                         void * dis_info,
                         long value,
                         unsigned int attrs ATTRIBUTE_UNUSED,
                         bfd_vma pc ATTRIBUTE_UNUSED,
                         int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  /* This is a loadh instruction. Shift the value to the left
     by 8 bits so that disassembled code will reassemble properly.  */
  value = ((value << 8) & 0xFF00);

  (*info->fprintf_func) (info->stream, "$%04lx", value);
}

static void
print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                         void * dis_info,
                         long value,
                         unsigned int attrs ATTRIBUTE_UNUSED,
                         bfd_vma pc ATTRIBUTE_UNUSED,
                         int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  (*info->fprintf_func) (info->stream, "$%04lx", value);
}

static void
print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                   void * dis_info,
                   long value,
                   unsigned int attrs ATTRIBUTE_UNUSED,
                   bfd_vma pc ATTRIBUTE_UNUSED,
                   int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  value = ((value << 14) & 0x1C000);
  ;value = (value  & 0x1FFFF);
  (*info->fprintf_func) (info->stream, "$%05lx", value);
}

static void
print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
                    void * dis_info,
                    long value,
                    unsigned int attrs ATTRIBUTE_UNUSED,
                    bfd_vma pc ATTRIBUTE_UNUSED,
                    int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  value = ((value << 1) & 0x1FFFF);
  (*info->fprintf_func) (info->stream, "$%05lx", value);
}

static void
print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
               void * dis_info,
               long value,
               unsigned int attrs ATTRIBUTE_UNUSED,
               bfd_vma pc ATTRIBUTE_UNUSED,
               int length ATTRIBUTE_UNUSED)
{
  disassemble_info *info = (disassemble_info *) dis_info;

  (*info->fprintf_func) (info->stream, "%ld", value);
}



/* -- */

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.