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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [binutils-2.18.50/] [gas/] [config/] [tc-d10v.c] - Diff between revs 156 and 816

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 156 Rev 816
/* tc-d10v.c -- Assembler code for the Mitsubishi D10V
/* tc-d10v.c -- Assembler code for the Mitsubishi D10V
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
 
 
   This file is part of GAS, the GNU Assembler.
   This file is part of GAS, the GNU Assembler.
 
 
   GAS is free software; you can redistribute it and/or modify
   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   the Free Software Foundation; either version 3, or (at your option)
   any later version.
   any later version.
 
 
   GAS is distributed in the hope that it will be useful,
   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   GNU General Public License for more details.
 
 
   You should have received a copy of the GNU General Public License
   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
   Boston, MA 02110-1301, USA.  */
   Boston, MA 02110-1301, USA.  */
 
 
#include "as.h"
#include "as.h"
#include "safe-ctype.h"
#include "safe-ctype.h"
#include "subsegs.h"
#include "subsegs.h"
#include "opcode/d10v.h"
#include "opcode/d10v.h"
#include "elf/ppc.h"
#include "elf/ppc.h"
 
 
const char comment_chars[]        = ";";
const char comment_chars[]        = ";";
const char line_comment_chars[]   = "#";
const char line_comment_chars[]   = "#";
const char line_separator_chars[] = "";
const char line_separator_chars[] = "";
const char *md_shortopts          = "O";
const char *md_shortopts          = "O";
const char EXP_CHARS[]            = "eE";
const char EXP_CHARS[]            = "eE";
const char FLT_CHARS[]            = "dD";
const char FLT_CHARS[]            = "dD";
 
 
int Optimizing = 0;
int Optimizing = 0;
 
 
#define AT_WORD_P(X) ((X)->X_op == O_right_shift \
#define AT_WORD_P(X) ((X)->X_op == O_right_shift \
                      && (X)->X_op_symbol != NULL \
                      && (X)->X_op_symbol != NULL \
                      && symbol_constant_p ((X)->X_op_symbol) \
                      && symbol_constant_p ((X)->X_op_symbol) \
                      && S_GET_VALUE ((X)->X_op_symbol) == AT_WORD_RIGHT_SHIFT)
                      && S_GET_VALUE ((X)->X_op_symbol) == AT_WORD_RIGHT_SHIFT)
#define AT_WORD_RIGHT_SHIFT 2
#define AT_WORD_RIGHT_SHIFT 2
 
 
/* Fixups.  */
/* Fixups.  */
#define MAX_INSN_FIXUPS  5
#define MAX_INSN_FIXUPS  5
 
 
struct d10v_fixup
struct d10v_fixup
{
{
  expressionS exp;
  expressionS exp;
  int operand;
  int operand;
  int pcrel;
  int pcrel;
  int size;
  int size;
  bfd_reloc_code_real_type reloc;
  bfd_reloc_code_real_type reloc;
};
};
 
 
typedef struct _fixups
typedef struct _fixups
{
{
  int fc;
  int fc;
  struct d10v_fixup fix[MAX_INSN_FIXUPS];
  struct d10v_fixup fix[MAX_INSN_FIXUPS];
  struct _fixups *next;
  struct _fixups *next;
} Fixups;
} Fixups;
 
 
static Fixups FixUps[2];
static Fixups FixUps[2];
static Fixups *fixups;
static Fixups *fixups;
 
 
static int do_not_ignore_hash = 0;
static int do_not_ignore_hash = 0;
 
 
typedef int packing_type;
typedef int packing_type;
#define PACK_UNSPEC     (0)     /* Packing order not specified.  */
#define PACK_UNSPEC     (0)     /* Packing order not specified.  */
#define PACK_PARALLEL   (1)     /* "||"  */
#define PACK_PARALLEL   (1)     /* "||"  */
#define PACK_LEFT_RIGHT (2)     /* "->"  */
#define PACK_LEFT_RIGHT (2)     /* "->"  */
#define PACK_RIGHT_LEFT (3)     /* "<-"  */
#define PACK_RIGHT_LEFT (3)     /* "<-"  */
static packing_type etype = PACK_UNSPEC; /* Used by d10v_cleanup.  */
static packing_type etype = PACK_UNSPEC; /* Used by d10v_cleanup.  */
 
 
/* TRUE if instruction swapping warnings should be inhibited.
/* TRUE if instruction swapping warnings should be inhibited.
   --nowarnswap.  */
   --nowarnswap.  */
static bfd_boolean flag_warn_suppress_instructionswap;
static bfd_boolean flag_warn_suppress_instructionswap;
 
 
/* TRUE if instruction packing should be performed when --gstabs is specified.
/* TRUE if instruction packing should be performed when --gstabs is specified.
   --gstabs-packing, --no-gstabs-packing.  */
   --gstabs-packing, --no-gstabs-packing.  */
static bfd_boolean flag_allow_gstabs_packing = 1;
static bfd_boolean flag_allow_gstabs_packing = 1;
 
 
/* Local functions.  */
/* Local functions.  */
 
 
enum options
enum options
{
{
  OPTION_NOWARNSWAP = OPTION_MD_BASE,
  OPTION_NOWARNSWAP = OPTION_MD_BASE,
  OPTION_GSTABSPACKING,
  OPTION_GSTABSPACKING,
  OPTION_NOGSTABSPACKING
  OPTION_NOGSTABSPACKING
};
};
 
 
struct option md_longopts[] =
struct option md_longopts[] =
{
{
  {"nowarnswap", no_argument, NULL, OPTION_NOWARNSWAP},
  {"nowarnswap", no_argument, NULL, OPTION_NOWARNSWAP},
  {"gstabspacking",  no_argument, NULL, OPTION_GSTABSPACKING},
  {"gstabspacking",  no_argument, NULL, OPTION_GSTABSPACKING},
  {"gstabs-packing", no_argument, NULL, OPTION_GSTABSPACKING},
  {"gstabs-packing", no_argument, NULL, OPTION_GSTABSPACKING},
  {"nogstabspacking",   no_argument, NULL, OPTION_NOGSTABSPACKING},
  {"nogstabspacking",   no_argument, NULL, OPTION_NOGSTABSPACKING},
  {"no-gstabs-packing", no_argument, NULL, OPTION_NOGSTABSPACKING},
  {"no-gstabs-packing", no_argument, NULL, OPTION_NOGSTABSPACKING},
  {NULL, no_argument, NULL, 0}
  {NULL, no_argument, NULL, 0}
};
};
 
 
size_t md_longopts_size = sizeof (md_longopts);
size_t md_longopts_size = sizeof (md_longopts);
 
 
/* Opcode hash table.  */
/* Opcode hash table.  */
static struct hash_control *d10v_hash;
static struct hash_control *d10v_hash;
 
 
/* Do a binary search of the d10v_predefined_registers array to see if
/* Do a binary search of the d10v_predefined_registers array to see if
   NAME is a valid regiter name.  Return the register number from the
   NAME is a valid regiter name.  Return the register number from the
   array on success, or -1 on failure.  */
   array on success, or -1 on failure.  */
 
 
static int
static int
reg_name_search (char *name)
reg_name_search (char *name)
{
{
  int middle, low, high;
  int middle, low, high;
  int cmp;
  int cmp;
 
 
  low = 0;
  low = 0;
  high = d10v_reg_name_cnt () - 1;
  high = d10v_reg_name_cnt () - 1;
 
 
  do
  do
    {
    {
      middle = (low + high) / 2;
      middle = (low + high) / 2;
      cmp = strcasecmp (name, d10v_predefined_registers[middle].name);
      cmp = strcasecmp (name, d10v_predefined_registers[middle].name);
      if (cmp < 0)
      if (cmp < 0)
        high = middle - 1;
        high = middle - 1;
      else if (cmp > 0)
      else if (cmp > 0)
        low = middle + 1;
        low = middle + 1;
      else
      else
        return d10v_predefined_registers[middle].value;
        return d10v_predefined_registers[middle].value;
    }
    }
  while (low <= high);
  while (low <= high);
  return -1;
  return -1;
}
}
 
 
/* Check the string at input_line_pointer
/* Check the string at input_line_pointer
   to see if it is a valid register name.  */
   to see if it is a valid register name.  */
 
 
static int
static int
register_name (expressionS *expressionP)
register_name (expressionS *expressionP)
{
{
  int reg_number;
  int reg_number;
  char c, *p = input_line_pointer;
  char c, *p = input_line_pointer;
 
 
  while (*p
  while (*p
         && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
         && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
    p++;
    p++;
 
 
  c = *p;
  c = *p;
  if (c)
  if (c)
    *p++ = 0;
    *p++ = 0;
 
 
  /* Look to see if it's in the register table.  */
  /* Look to see if it's in the register table.  */
  reg_number = reg_name_search (input_line_pointer);
  reg_number = reg_name_search (input_line_pointer);
  if (reg_number >= 0)
  if (reg_number >= 0)
    {
    {
      expressionP->X_op = O_register;
      expressionP->X_op = O_register;
      /* Temporarily store a pointer to the string here.  */
      /* Temporarily store a pointer to the string here.  */
      expressionP->X_op_symbol = (symbolS *) input_line_pointer;
      expressionP->X_op_symbol = (symbolS *) input_line_pointer;
      expressionP->X_add_number = reg_number;
      expressionP->X_add_number = reg_number;
      input_line_pointer = p;
      input_line_pointer = p;
      return 1;
      return 1;
    }
    }
  if (c)
  if (c)
    *(p - 1) = c;
    *(p - 1) = c;
  return 0;
  return 0;
}
}
 
 
static int
static int
check_range (unsigned long num, int bits, int flags)
check_range (unsigned long num, int bits, int flags)
{
{
  long min, max;
  long min, max;
  int retval = 0;
  int retval = 0;
 
 
  /* Don't bother checking 16-bit values.  */
  /* Don't bother checking 16-bit values.  */
  if (bits == 16)
  if (bits == 16)
    return 0;
    return 0;
 
 
  if (flags & OPERAND_SHIFT)
  if (flags & OPERAND_SHIFT)
    {
    {
      /* All special shift operands are unsigned and <= 16.
      /* All special shift operands are unsigned and <= 16.
         We allow 0 for now.  */
         We allow 0 for now.  */
      if (num > 16)
      if (num > 16)
        return 1;
        return 1;
      else
      else
        return 0;
        return 0;
    }
    }
 
 
  if (flags & OPERAND_SIGNED)
  if (flags & OPERAND_SIGNED)
    {
    {
      /* Signed 3-bit integers are restricted to the (-2, 3) range.  */
      /* Signed 3-bit integers are restricted to the (-2, 3) range.  */
      if (flags & RESTRICTED_NUM3)
      if (flags & RESTRICTED_NUM3)
        {
        {
          if ((long) num < -2 || (long) num > 3)
          if ((long) num < -2 || (long) num > 3)
            retval = 1;
            retval = 1;
        }
        }
      else
      else
        {
        {
          max = (1 << (bits - 1)) - 1;
          max = (1 << (bits - 1)) - 1;
          min = - (1 << (bits - 1));
          min = - (1 << (bits - 1));
          if (((long) num > max) || ((long) num < min))
          if (((long) num > max) || ((long) num < min))
            retval = 1;
            retval = 1;
        }
        }
    }
    }
  else
  else
    {
    {
      max = (1 << bits) - 1;
      max = (1 << bits) - 1;
      min = 0;
      min = 0;
      if (((long) num > max) || ((long) num < min))
      if (((long) num > max) || ((long) num < min))
        retval = 1;
        retval = 1;
    }
    }
  return retval;
  return retval;
}
}
 
 
void
void
md_show_usage (FILE *stream)
md_show_usage (FILE *stream)
{
{
  fprintf (stream, _("D10V options:\n\
  fprintf (stream, _("D10V options:\n\
-O                      Optimize.  Will do some operations in parallel.\n\
-O                      Optimize.  Will do some operations in parallel.\n\
--gstabs-packing        Pack adjacent short instructions together even\n\
--gstabs-packing        Pack adjacent short instructions together even\n\
                        when --gstabs is specified.  On by default.\n\
                        when --gstabs is specified.  On by default.\n\
--no-gstabs-packing     If --gstabs is specified, do not pack adjacent\n\
--no-gstabs-packing     If --gstabs is specified, do not pack adjacent\n\
                        instructions together.\n"));
                        instructions together.\n"));
}
}
 
 
int
int
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
{
{
  switch (c)
  switch (c)
    {
    {
    case 'O':
    case 'O':
      /* Optimize. Will attempt to parallelize operations.  */
      /* Optimize. Will attempt to parallelize operations.  */
      Optimizing = 1;
      Optimizing = 1;
      break;
      break;
    case OPTION_NOWARNSWAP:
    case OPTION_NOWARNSWAP:
      flag_warn_suppress_instructionswap = 1;
      flag_warn_suppress_instructionswap = 1;
      break;
      break;
    case OPTION_GSTABSPACKING:
    case OPTION_GSTABSPACKING:
      flag_allow_gstabs_packing = 1;
      flag_allow_gstabs_packing = 1;
      break;
      break;
    case OPTION_NOGSTABSPACKING:
    case OPTION_NOGSTABSPACKING:
      flag_allow_gstabs_packing = 0;
      flag_allow_gstabs_packing = 0;
      break;
      break;
    default:
    default:
      return 0;
      return 0;
    }
    }
  return 1;
  return 1;
}
}
 
 
symbolS *
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
{
  return 0;
  return 0;
}
}
 
 
char *
char *
md_atof (int type, char *litP, int *sizeP)
md_atof (int type, char *litP, int *sizeP)
{
{
  return ieee_md_atof (type, litP, sizeP, TRUE);
  return ieee_md_atof (type, litP, sizeP, TRUE);
}
}
 
 
void
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
                 asection *sec ATTRIBUTE_UNUSED,
                 fragS *fragP ATTRIBUTE_UNUSED)
                 fragS *fragP ATTRIBUTE_UNUSED)
{
{
  abort ();
  abort ();
}
}
 
 
valueT
valueT
md_section_align (asection *seg, valueT addr)
md_section_align (asection *seg, valueT addr)
{
{
  int align = bfd_get_section_alignment (stdoutput, seg);
  int align = bfd_get_section_alignment (stdoutput, seg);
  return ((addr + (1 << align) - 1) & (-1 << align));
  return ((addr + (1 << align) - 1) & (-1 << align));
}
}
 
 
void
void
md_begin (void)
md_begin (void)
{
{
  char *prev_name = "";
  char *prev_name = "";
  struct d10v_opcode *opcode;
  struct d10v_opcode *opcode;
  d10v_hash = hash_new ();
  d10v_hash = hash_new ();
 
 
  /* Insert unique names into hash table.  The D10v instruction set
  /* Insert unique names into hash table.  The D10v instruction set
     has many identical opcode names that have different opcodes based
     has many identical opcode names that have different opcodes based
     on the operands.  This hash table then provides a quick index to
     on the operands.  This hash table then provides a quick index to
     the first opcode with a particular name in the opcode table.  */
     the first opcode with a particular name in the opcode table.  */
 
 
  for (opcode = (struct d10v_opcode *) d10v_opcodes; opcode->name; opcode++)
  for (opcode = (struct d10v_opcode *) d10v_opcodes; opcode->name; opcode++)
    {
    {
      if (strcmp (prev_name, opcode->name))
      if (strcmp (prev_name, opcode->name))
        {
        {
          prev_name = (char *) opcode->name;
          prev_name = (char *) opcode->name;
          hash_insert (d10v_hash, opcode->name, (char *) opcode);
          hash_insert (d10v_hash, opcode->name, (char *) opcode);
        }
        }
    }
    }
 
 
  fixups = &FixUps[0];
  fixups = &FixUps[0];
  FixUps[0].next = &FixUps[1];
  FixUps[0].next = &FixUps[1];
  FixUps[1].next = &FixUps[0];
  FixUps[1].next = &FixUps[0];
}
}
 
 
/* Remove the postincrement or postdecrement operator ( '+' or '-' )
/* Remove the postincrement or postdecrement operator ( '+' or '-' )
   from an expression.  */
   from an expression.  */
 
 
static int
static int
postfix (char *p)
postfix (char *p)
{
{
  while (*p != '-' && *p != '+')
  while (*p != '-' && *p != '+')
    {
    {
      if (*p == 0 || *p == '\n' || *p == '\r')
      if (*p == 0 || *p == '\n' || *p == '\r')
        break;
        break;
      p++;
      p++;
    }
    }
 
 
  if (*p == '-')
  if (*p == '-')
    {
    {
      *p = ' ';
      *p = ' ';
      return -1;
      return -1;
    }
    }
  if (*p == '+')
  if (*p == '+')
    {
    {
      *p = ' ';
      *p = ' ';
      return 1;
      return 1;
    }
    }
 
 
  return 0;
  return 0;
}
}
 
 
static bfd_reloc_code_real_type
static bfd_reloc_code_real_type
get_reloc (struct d10v_operand *op)
get_reloc (struct d10v_operand *op)
{
{
  int bits = op->bits;
  int bits = op->bits;
 
 
  if (bits <= 4)
  if (bits <= 4)
    return 0;
    return 0;
 
 
  if (op->flags & OPERAND_ADDR)
  if (op->flags & OPERAND_ADDR)
    {
    {
      if (bits == 8)
      if (bits == 8)
        return BFD_RELOC_D10V_10_PCREL_R;
        return BFD_RELOC_D10V_10_PCREL_R;
      else
      else
        return BFD_RELOC_D10V_18_PCREL;
        return BFD_RELOC_D10V_18_PCREL;
    }
    }
 
 
  return BFD_RELOC_16;
  return BFD_RELOC_16;
}
}
 
 
/* Parse a string of operands.  Return an array of expressions.  */
/* Parse a string of operands.  Return an array of expressions.  */
 
 
static int
static int
get_operands (expressionS exp[])
get_operands (expressionS exp[])
{
{
  char *p = input_line_pointer;
  char *p = input_line_pointer;
  int numops = 0;
  int numops = 0;
  int post = 0;
  int post = 0;
  int uses_at = 0;
  int uses_at = 0;
 
 
  while (*p)
  while (*p)
    {
    {
      while (*p == ' ' || *p == '\t' || *p == ',')
      while (*p == ' ' || *p == '\t' || *p == ',')
        p++;
        p++;
      if (*p == 0 || *p == '\n' || *p == '\r')
      if (*p == 0 || *p == '\n' || *p == '\r')
        break;
        break;
 
 
      if (*p == '@')
      if (*p == '@')
        {
        {
          uses_at = 1;
          uses_at = 1;
 
 
          p++;
          p++;
          exp[numops].X_op = O_absent;
          exp[numops].X_op = O_absent;
          if (*p == '(')
          if (*p == '(')
            {
            {
              p++;
              p++;
              exp[numops].X_add_number = OPERAND_ATPAR;
              exp[numops].X_add_number = OPERAND_ATPAR;
            }
            }
          else if (*p == '-')
          else if (*p == '-')
            {
            {
              p++;
              p++;
              exp[numops].X_add_number = OPERAND_ATMINUS;
              exp[numops].X_add_number = OPERAND_ATMINUS;
            }
            }
          else
          else
            {
            {
              exp[numops].X_add_number = OPERAND_ATSIGN;
              exp[numops].X_add_number = OPERAND_ATSIGN;
              if (*p == '+')
              if (*p == '+')
                {
                {
                  numops++;
                  numops++;
                  exp[numops].X_op = O_absent;
                  exp[numops].X_op = O_absent;
                  exp[numops].X_add_number = OPERAND_PLUS;
                  exp[numops].X_add_number = OPERAND_PLUS;
                  p++;
                  p++;
                }
                }
              post = postfix (p);
              post = postfix (p);
            }
            }
          numops++;
          numops++;
          continue;
          continue;
        }
        }
 
 
      if (*p == ')')
      if (*p == ')')
        {
        {
          /* Just skip the trailing paren.  */
          /* Just skip the trailing paren.  */
          p++;
          p++;
          continue;
          continue;
        }
        }
 
 
      input_line_pointer = p;
      input_line_pointer = p;
 
 
      /* Check to see if it might be a register name.  */
      /* Check to see if it might be a register name.  */
      if (!register_name (&exp[numops]))
      if (!register_name (&exp[numops]))
        {
        {
          /* Parse as an expression.  */
          /* Parse as an expression.  */
          if (uses_at)
          if (uses_at)
            {
            {
              /* Any expression that involves the indirect addressing
              /* Any expression that involves the indirect addressing
                 cannot also involve immediate addressing.  Therefore
                 cannot also involve immediate addressing.  Therefore
                 the use of the hash character is illegal.  */
                 the use of the hash character is illegal.  */
              int save = do_not_ignore_hash;
              int save = do_not_ignore_hash;
              do_not_ignore_hash = 1;
              do_not_ignore_hash = 1;
 
 
              expression (&exp[numops]);
              expression (&exp[numops]);
 
 
              do_not_ignore_hash = save;
              do_not_ignore_hash = save;
            }
            }
          else
          else
            expression (&exp[numops]);
            expression (&exp[numops]);
        }
        }
 
 
      if (strncasecmp (input_line_pointer, "@word", 5) == 0)
      if (strncasecmp (input_line_pointer, "@word", 5) == 0)
        {
        {
          input_line_pointer += 5;
          input_line_pointer += 5;
          if (exp[numops].X_op == O_register)
          if (exp[numops].X_op == O_register)
            {
            {
              /* If it looked like a register name but was followed by
              /* If it looked like a register name but was followed by
                 "@word" then it was really a symbol, so change it to
                 "@word" then it was really a symbol, so change it to
                 one.  */
                 one.  */
              exp[numops].X_op = O_symbol;
              exp[numops].X_op = O_symbol;
              exp[numops].X_add_symbol =
              exp[numops].X_add_symbol =
                symbol_find_or_make ((char *) exp[numops].X_op_symbol);
                symbol_find_or_make ((char *) exp[numops].X_op_symbol);
            }
            }
 
 
          /* Check for identifier@word+constant.  */
          /* Check for identifier@word+constant.  */
          if (*input_line_pointer == '-' || *input_line_pointer == '+')
          if (*input_line_pointer == '-' || *input_line_pointer == '+')
            {
            {
              expressionS new_exp;
              expressionS new_exp;
              expression (&new_exp);
              expression (&new_exp);
              exp[numops].X_add_number = new_exp.X_add_number;
              exp[numops].X_add_number = new_exp.X_add_number;
            }
            }
 
 
          /* Convert expr into a right shift by AT_WORD_RIGHT_SHIFT.  */
          /* Convert expr into a right shift by AT_WORD_RIGHT_SHIFT.  */
          {
          {
            expressionS new_exp;
            expressionS new_exp;
            memset (&new_exp, 0, sizeof new_exp);
            memset (&new_exp, 0, sizeof new_exp);
            new_exp.X_add_number = AT_WORD_RIGHT_SHIFT;
            new_exp.X_add_number = AT_WORD_RIGHT_SHIFT;
            new_exp.X_op = O_constant;
            new_exp.X_op = O_constant;
            new_exp.X_unsigned = 1;
            new_exp.X_unsigned = 1;
            exp[numops].X_op_symbol = make_expr_symbol (&new_exp);
            exp[numops].X_op_symbol = make_expr_symbol (&new_exp);
            exp[numops].X_op = O_right_shift;
            exp[numops].X_op = O_right_shift;
          }
          }
 
 
          know (AT_WORD_P (&exp[numops]));
          know (AT_WORD_P (&exp[numops]));
        }
        }
 
 
      if (exp[numops].X_op == O_illegal)
      if (exp[numops].X_op == O_illegal)
        as_bad (_("illegal operand"));
        as_bad (_("illegal operand"));
      else if (exp[numops].X_op == O_absent)
      else if (exp[numops].X_op == O_absent)
        as_bad (_("missing operand"));
        as_bad (_("missing operand"));
 
 
      numops++;
      numops++;
      p = input_line_pointer;
      p = input_line_pointer;
    }
    }
 
 
  switch (post)
  switch (post)
    {
    {
    case -1:    /* Postdecrement mode.  */
    case -1:    /* Postdecrement mode.  */
      exp[numops].X_op = O_absent;
      exp[numops].X_op = O_absent;
      exp[numops++].X_add_number = OPERAND_MINUS;
      exp[numops++].X_add_number = OPERAND_MINUS;
      break;
      break;
    case 1:     /* Postincrement mode.  */
    case 1:     /* Postincrement mode.  */
      exp[numops].X_op = O_absent;
      exp[numops].X_op = O_absent;
      exp[numops++].X_add_number = OPERAND_PLUS;
      exp[numops++].X_add_number = OPERAND_PLUS;
      break;
      break;
    }
    }
 
 
  exp[numops].X_op = 0;
  exp[numops].X_op = 0;
  return numops;
  return numops;
}
}
 
 
static unsigned long
static unsigned long
d10v_insert_operand (unsigned long insn,
d10v_insert_operand (unsigned long insn,
                     int op_type,
                     int op_type,
                     offsetT value,
                     offsetT value,
                     int left,
                     int left,
                     fixS *fix)
                     fixS *fix)
{
{
  int shift, bits;
  int shift, bits;
 
 
  shift = d10v_operands[op_type].shift;
  shift = d10v_operands[op_type].shift;
  if (left)
  if (left)
    shift += 15;
    shift += 15;
 
 
  bits = d10v_operands[op_type].bits;
  bits = d10v_operands[op_type].bits;
 
 
  /* Truncate to the proper number of bits.  */
  /* Truncate to the proper number of bits.  */
  if (check_range (value, bits, d10v_operands[op_type].flags))
  if (check_range (value, bits, d10v_operands[op_type].flags))
    as_bad_where (fix->fx_file, fix->fx_line,
    as_bad_where (fix->fx_file, fix->fx_line,
                  _("operand out of range: %ld"), (long) value);
                  _("operand out of range: %ld"), (long) value);
 
 
  value &= 0x7FFFFFFF >> (31 - bits);
  value &= 0x7FFFFFFF >> (31 - bits);
  insn |= (value << shift);
  insn |= (value << shift);
 
 
  return insn;
  return insn;
}
}
 
 
/* Take a pointer to the opcode entry in the opcode table and the
/* Take a pointer to the opcode entry in the opcode table and the
   array of operand expressions.  Return the instruction.  */
   array of operand expressions.  Return the instruction.  */
 
 
static unsigned long
static unsigned long
build_insn (struct d10v_opcode *opcode,
build_insn (struct d10v_opcode *opcode,
            expressionS *opers,
            expressionS *opers,
            unsigned long insn)
            unsigned long insn)
{
{
  int i, bits, shift, flags, format;
  int i, bits, shift, flags, format;
  unsigned long number;
  unsigned long number;
 
 
  /* The insn argument is only used for the DIVS kludge.  */
  /* The insn argument is only used for the DIVS kludge.  */
  if (insn)
  if (insn)
    format = LONG_R;
    format = LONG_R;
  else
  else
    {
    {
      insn = opcode->opcode;
      insn = opcode->opcode;
      format = opcode->format;
      format = opcode->format;
    }
    }
 
 
  for (i = 0; opcode->operands[i]; i++)
  for (i = 0; opcode->operands[i]; i++)
    {
    {
      flags = d10v_operands[opcode->operands[i]].flags;
      flags = d10v_operands[opcode->operands[i]].flags;
      bits = d10v_operands[opcode->operands[i]].bits;
      bits = d10v_operands[opcode->operands[i]].bits;
      shift = d10v_operands[opcode->operands[i]].shift;
      shift = d10v_operands[opcode->operands[i]].shift;
      number = opers[i].X_add_number;
      number = opers[i].X_add_number;
 
 
      if (flags & OPERAND_REG)
      if (flags & OPERAND_REG)
        {
        {
          number &= REGISTER_MASK;
          number &= REGISTER_MASK;
          if (format == LONG_L)
          if (format == LONG_L)
            shift += 15;
            shift += 15;
        }
        }
 
 
      if (opers[i].X_op != O_register && opers[i].X_op != O_constant)
      if (opers[i].X_op != O_register && opers[i].X_op != O_constant)
        {
        {
          /* Now create a fixup.  */
          /* Now create a fixup.  */
 
 
          if (fixups->fc >= MAX_INSN_FIXUPS)
          if (fixups->fc >= MAX_INSN_FIXUPS)
            as_fatal (_("too many fixups"));
            as_fatal (_("too many fixups"));
 
 
          if (AT_WORD_P (&opers[i]))
          if (AT_WORD_P (&opers[i]))
            {
            {
              /* Recognize XXX>>1+N aka XXX@word+N as special (AT_WORD).  */
              /* Recognize XXX>>1+N aka XXX@word+N as special (AT_WORD).  */
              fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18;
              fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18;
              opers[i].X_op = O_symbol;
              opers[i].X_op = O_symbol;
              opers[i].X_op_symbol = NULL; /* Should free it.  */
              opers[i].X_op_symbol = NULL; /* Should free it.  */
              /* number is left shifted by AT_WORD_RIGHT_SHIFT so
              /* number is left shifted by AT_WORD_RIGHT_SHIFT so
                 that, it is aligned with the symbol's value.  Later,
                 that, it is aligned with the symbol's value.  Later,
                 BFD_RELOC_D10V_18 will right shift (symbol_value +
                 BFD_RELOC_D10V_18 will right shift (symbol_value +
                 X_add_number).  */
                 X_add_number).  */
              number <<= AT_WORD_RIGHT_SHIFT;
              number <<= AT_WORD_RIGHT_SHIFT;
              opers[i].X_add_number = number;
              opers[i].X_add_number = number;
            }
            }
          else
          else
            {
            {
              fixups->fix[fixups->fc].reloc =
              fixups->fix[fixups->fc].reloc =
                get_reloc ((struct d10v_operand *) &d10v_operands[opcode->operands[i]]);
                get_reloc ((struct d10v_operand *) &d10v_operands[opcode->operands[i]]);
 
 
              /* Check that an immediate was passed to ops that expect one.  */
              /* Check that an immediate was passed to ops that expect one.  */
              if ((flags & OPERAND_NUM)
              if ((flags & OPERAND_NUM)
                  && (fixups->fix[fixups->fc].reloc == 0))
                  && (fixups->fix[fixups->fc].reloc == 0))
                as_bad (_("operand is not an immediate"));
                as_bad (_("operand is not an immediate"));
            }
            }
 
 
          if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 ||
          if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 ||
              fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18)
              fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18)
            fixups->fix[fixups->fc].size = 2;
            fixups->fix[fixups->fc].size = 2;
          else
          else
            fixups->fix[fixups->fc].size = 4;
            fixups->fix[fixups->fc].size = 4;
 
 
          fixups->fix[fixups->fc].exp = opers[i];
          fixups->fix[fixups->fc].exp = opers[i];
          fixups->fix[fixups->fc].operand = opcode->operands[i];
          fixups->fix[fixups->fc].operand = opcode->operands[i];
          fixups->fix[fixups->fc].pcrel =
          fixups->fix[fixups->fc].pcrel =
            (flags & OPERAND_ADDR) ? TRUE : FALSE;
            (flags & OPERAND_ADDR) ? TRUE : FALSE;
          (fixups->fc)++;
          (fixups->fc)++;
        }
        }
 
 
      /* Truncate to the proper number of bits.  */
      /* Truncate to the proper number of bits.  */
      if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
      if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
        as_bad (_("operand out of range: %lu"), number);
        as_bad (_("operand out of range: %lu"), number);
      number &= 0x7FFFFFFF >> (31 - bits);
      number &= 0x7FFFFFFF >> (31 - bits);
      insn = insn | (number << shift);
      insn = insn | (number << shift);
    }
    }
 
 
  /* kludge: for DIVS, we need to put the operands in twice on the second
  /* kludge: for DIVS, we need to put the operands in twice on the second
     pass, format is changed to LONG_R to force the second set of operands
     pass, format is changed to LONG_R to force the second set of operands
     to not be shifted over 15.  */
     to not be shifted over 15.  */
  if ((opcode->opcode == OPCODE_DIVS) && (format == LONG_L))
  if ((opcode->opcode == OPCODE_DIVS) && (format == LONG_L))
    insn = build_insn (opcode, opers, insn);
    insn = build_insn (opcode, opers, insn);
 
 
  return insn;
  return insn;
}
}
 
 
/* Write out a long form instruction.  */
/* Write out a long form instruction.  */
 
 
static void
static void
write_long (unsigned long insn, Fixups *fx)
write_long (unsigned long insn, Fixups *fx)
{
{
  int i, where;
  int i, where;
  char *f = frag_more (4);
  char *f = frag_more (4);
 
 
  insn |= FM11;
  insn |= FM11;
  number_to_chars_bigendian (f, insn, 4);
  number_to_chars_bigendian (f, insn, 4);
 
 
  for (i = 0; i < fx->fc; i++)
  for (i = 0; i < fx->fc; i++)
    {
    {
      if (fx->fix[i].reloc)
      if (fx->fix[i].reloc)
        {
        {
          where = f - frag_now->fr_literal;
          where = f - frag_now->fr_literal;
          if (fx->fix[i].size == 2)
          if (fx->fix[i].size == 2)
            where += 2;
            where += 2;
 
 
          if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
          if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
            fx->fix[i].operand |= 4096;
            fx->fix[i].operand |= 4096;
 
 
          fix_new_exp (frag_now,
          fix_new_exp (frag_now,
                       where,
                       where,
                       fx->fix[i].size,
                       fx->fix[i].size,
                       &(fx->fix[i].exp),
                       &(fx->fix[i].exp),
                       fx->fix[i].pcrel,
                       fx->fix[i].pcrel,
                       fx->fix[i].operand|2048);
                       fx->fix[i].operand|2048);
        }
        }
    }
    }
  fx->fc = 0;
  fx->fc = 0;
}
}
 
 
/* Write out a short form instruction by itself.  */
/* Write out a short form instruction by itself.  */
 
 
static void
static void
write_1_short (struct d10v_opcode *opcode,
write_1_short (struct d10v_opcode *opcode,
               unsigned long insn,
               unsigned long insn,
               Fixups *fx)
               Fixups *fx)
{
{
  char *f = frag_more (4);
  char *f = frag_more (4);
  int i, where;
  int i, where;
 
 
  if (opcode->exec_type & PARONLY)
  if (opcode->exec_type & PARONLY)
    as_fatal (_("Instruction must be executed in parallel with another instruction."));
    as_fatal (_("Instruction must be executed in parallel with another instruction."));
 
 
  /* The other container needs to be NOP.
  /* The other container needs to be NOP.
     According to 4.3.1: for FM=00, sub-instructions performed only by IU
     According to 4.3.1: for FM=00, sub-instructions performed only by IU
     cannot be encoded in L-container.  */
     cannot be encoded in L-container.  */
  if (opcode->unit == IU)
  if (opcode->unit == IU)
    insn |= FM00 | (NOP << 15);         /* Right container.  */
    insn |= FM00 | (NOP << 15);         /* Right container.  */
  else
  else
    insn = FM00 | (insn << 15) | NOP;   /* Left container.  */
    insn = FM00 | (insn << 15) | NOP;   /* Left container.  */
 
 
  number_to_chars_bigendian (f, insn, 4);
  number_to_chars_bigendian (f, insn, 4);
  for (i = 0; i < fx->fc; i++)
  for (i = 0; i < fx->fc; i++)
    {
    {
      if (fx->fix[i].reloc)
      if (fx->fix[i].reloc)
        {
        {
          where = f - frag_now->fr_literal;
          where = f - frag_now->fr_literal;
          if (fx->fix[i].size == 2)
          if (fx->fix[i].size == 2)
            where += 2;
            where += 2;
 
 
          if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
          if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
            fx->fix[i].operand |= 4096;
            fx->fix[i].operand |= 4096;
 
 
          /* If it's an R reloc, we may have to switch it to L.  */
          /* If it's an R reloc, we may have to switch it to L.  */
          if ((fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R)
          if ((fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R)
              && (opcode->unit != IU))
              && (opcode->unit != IU))
            fx->fix[i].operand |= 1024;
            fx->fix[i].operand |= 1024;
 
 
          fix_new_exp (frag_now,
          fix_new_exp (frag_now,
                       where,
                       where,
                       fx->fix[i].size,
                       fx->fix[i].size,
                       &(fx->fix[i].exp),
                       &(fx->fix[i].exp),
                       fx->fix[i].pcrel,
                       fx->fix[i].pcrel,
                       fx->fix[i].operand|2048);
                       fx->fix[i].operand|2048);
        }
        }
    }
    }
  fx->fc = 0;
  fx->fc = 0;
}
}
 
 
/* Determine if there are any resource conflicts among two manually
/* Determine if there are any resource conflicts among two manually
   parallelized instructions.  Some of this was lifted from parallel_ok.  */
   parallelized instructions.  Some of this was lifted from parallel_ok.  */
 
 
static void
static void
check_resource_conflict (struct d10v_opcode *op1,
check_resource_conflict (struct d10v_opcode *op1,
                         unsigned long insn1,
                         unsigned long insn1,
                         struct d10v_opcode *op2,
                         struct d10v_opcode *op2,
                         unsigned long insn2)
                         unsigned long insn2)
{
{
  int i, j, flags, mask, shift, regno;
  int i, j, flags, mask, shift, regno;
  unsigned long ins, mod[2];
  unsigned long ins, mod[2];
  struct d10v_opcode *op;
  struct d10v_opcode *op;
 
 
  if ((op1->exec_type & SEQ)
  if ((op1->exec_type & SEQ)
      || ! ((op1->exec_type & PAR) || (op1->exec_type & PARONLY)))
      || ! ((op1->exec_type & PAR) || (op1->exec_type & PARONLY)))
    {
    {
      as_warn (_("packing conflict: %s must dispatch sequentially"),
      as_warn (_("packing conflict: %s must dispatch sequentially"),
              op1->name);
              op1->name);
      return;
      return;
    }
    }
 
 
  if ((op2->exec_type & SEQ)
  if ((op2->exec_type & SEQ)
      || ! ((op2->exec_type & PAR) || (op2->exec_type & PARONLY)))
      || ! ((op2->exec_type & PAR) || (op2->exec_type & PARONLY)))
    {
    {
      as_warn (_("packing conflict: %s must dispatch sequentially"),
      as_warn (_("packing conflict: %s must dispatch sequentially"),
              op2->name);
              op2->name);
      return;
      return;
    }
    }
 
 
   /* See if both instructions write to the same resource.
   /* See if both instructions write to the same resource.
 
 
      The idea here is to create two sets of bitmasks (mod and used) which
      The idea here is to create two sets of bitmasks (mod and used) which
      indicate which registers are modified or used by each instruction.
      indicate which registers are modified or used by each instruction.
      The operation can only be done in parallel if neither instruction
      The operation can only be done in parallel if neither instruction
      modifies the same register. Accesses to control registers and memory
      modifies the same register. Accesses to control registers and memory
      are treated as accesses to a single register. So if both instructions
      are treated as accesses to a single register. So if both instructions
      write memory or if the first instruction writes memory and the second
      write memory or if the first instruction writes memory and the second
      reads, then they cannot be done in parallel. We treat reads to the PSW
      reads, then they cannot be done in parallel. We treat reads to the PSW
      (which includes C, F0, and F1) in isolation. So simultaneously writing
      (which includes C, F0, and F1) in isolation. So simultaneously writing
      C and F0 in two different sub-instructions is permitted.  */
      C and F0 in two different sub-instructions is permitted.  */
 
 
  /* The bitmasks (mod and used) look like this (bit 31 = MSB).
  /* The bitmasks (mod and used) look like this (bit 31 = MSB).
     r0-r15       0-15
     r0-r15       0-15
     a0-a1        16-17
     a0-a1        16-17
     cr (not psw) 18
     cr (not psw) 18
     psw(other)   19
     psw(other)   19
     mem          20
     mem          20
     psw(C flag)  21
     psw(C flag)  21
     psw(F0 flag) 22  */
     psw(F0 flag) 22  */
 
 
  for (j = 0; j < 2; j++)
  for (j = 0; j < 2; j++)
    {
    {
      if (j == 0)
      if (j == 0)
        {
        {
          op = op1;
          op = op1;
          ins = insn1;
          ins = insn1;
        }
        }
      else
      else
        {
        {
          op = op2;
          op = op2;
          ins = insn2;
          ins = insn2;
        }
        }
      mod[j] = 0;
      mod[j] = 0;
      if (op->exec_type & BRANCH_LINK)
      if (op->exec_type & BRANCH_LINK)
        mod[j] |= 1 << 13;
        mod[j] |= 1 << 13;
 
 
      for (i = 0; op->operands[i]; i++)
      for (i = 0; op->operands[i]; i++)
        {
        {
          flags = d10v_operands[op->operands[i]].flags;
          flags = d10v_operands[op->operands[i]].flags;
          shift = d10v_operands[op->operands[i]].shift;
          shift = d10v_operands[op->operands[i]].shift;
          mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
          mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
          if (flags & OPERAND_REG)
          if (flags & OPERAND_REG)
            {
            {
              regno = (ins >> shift) & mask;
              regno = (ins >> shift) & mask;
              if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
              if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
                regno += 16;
                regno += 16;
              else if (flags & OPERAND_CONTROL) /* mvtc or mvfc */
              else if (flags & OPERAND_CONTROL) /* mvtc or mvfc */
                {
                {
                  if (regno == 0)
                  if (regno == 0)
                    regno = 19;
                    regno = 19;
                  else
                  else
                    regno = 18;
                    regno = 18;
                }
                }
              else if (flags & OPERAND_FFLAG)
              else if (flags & OPERAND_FFLAG)
                regno = 22;
                regno = 22;
              else if (flags & OPERAND_CFLAG)
              else if (flags & OPERAND_CFLAG)
                regno = 21;
                regno = 21;
 
 
              if (flags & OPERAND_DEST
              if (flags & OPERAND_DEST
                  /* Auto inc/dec also modifies the register.  */
                  /* Auto inc/dec also modifies the register.  */
                  || (op->operands[i + 1] != 0
                  || (op->operands[i + 1] != 0
                      && (d10v_operands[op->operands[i + 1]].flags
                      && (d10v_operands[op->operands[i + 1]].flags
                          & (OPERAND_PLUS | OPERAND_MINUS)) != 0))
                          & (OPERAND_PLUS | OPERAND_MINUS)) != 0))
                {
                {
                  mod[j] |= 1 << regno;
                  mod[j] |= 1 << regno;
                  if (flags & OPERAND_EVEN)
                  if (flags & OPERAND_EVEN)
                    mod[j] |= 1 << (regno + 1);
                    mod[j] |= 1 << (regno + 1);
                }
                }
            }
            }
          else if (flags & OPERAND_ATMINUS)
          else if (flags & OPERAND_ATMINUS)
            {
            {
              /* SP implicitly used/modified.  */
              /* SP implicitly used/modified.  */
              mod[j] |= 1 << 15;
              mod[j] |= 1 << 15;
            }
            }
        }
        }
 
 
      if (op->exec_type & WMEM)
      if (op->exec_type & WMEM)
        mod[j] |= 1 << 20;
        mod[j] |= 1 << 20;
      else if (op->exec_type & WF0)
      else if (op->exec_type & WF0)
        mod[j] |= 1 << 22;
        mod[j] |= 1 << 22;
      else if (op->exec_type & WCAR)
      else if (op->exec_type & WCAR)
        mod[j] |= 1 << 21;
        mod[j] |= 1 << 21;
    }
    }
 
 
  if ((mod[0] & mod[1]) == 0)
  if ((mod[0] & mod[1]) == 0)
    return;
    return;
  else
  else
    {
    {
      unsigned long x;
      unsigned long x;
      x = mod[0] & mod[1];
      x = mod[0] & mod[1];
 
 
      for (j = 0; j <= 15; j++)
      for (j = 0; j <= 15; j++)
        if (x & (1 << j))
        if (x & (1 << j))
          as_warn (_("resource conflict (R%d)"), j);
          as_warn (_("resource conflict (R%d)"), j);
      for (j = 16; j <= 17; j++)
      for (j = 16; j <= 17; j++)
        if (x & (1 << j))
        if (x & (1 << j))
          as_warn (_("resource conflict (A%d)"), j - 16);
          as_warn (_("resource conflict (A%d)"), j - 16);
      if (x & (1 << 19))
      if (x & (1 << 19))
        as_warn (_("resource conflict (PSW)"));
        as_warn (_("resource conflict (PSW)"));
      if (x & (1 << 21))
      if (x & (1 << 21))
        as_warn (_("resource conflict (C flag)"));
        as_warn (_("resource conflict (C flag)"));
      if (x & (1 << 22))
      if (x & (1 << 22))
        as_warn (_("resource conflict (F flag)"));
        as_warn (_("resource conflict (F flag)"));
    }
    }
}
}
 
 
/* Check 2 instructions and determine if they can be safely
/* Check 2 instructions and determine if they can be safely
   executed in parallel.  Return 1 if they can be.  */
   executed in parallel.  Return 1 if they can be.  */
 
 
static int
static int
parallel_ok (struct d10v_opcode *op1,
parallel_ok (struct d10v_opcode *op1,
             unsigned long insn1,
             unsigned long insn1,
             struct d10v_opcode *op2,
             struct d10v_opcode *op2,
             unsigned long insn2,
             unsigned long insn2,
             packing_type exec_type)
             packing_type exec_type)
{
{
  int i, j, flags, mask, shift, regno;
  int i, j, flags, mask, shift, regno;
  unsigned long ins, mod[2], used[2];
  unsigned long ins, mod[2], used[2];
  struct d10v_opcode *op;
  struct d10v_opcode *op;
 
 
  if ((op1->exec_type & SEQ) != 0 || (op2->exec_type & SEQ) != 0
  if ((op1->exec_type & SEQ) != 0 || (op2->exec_type & SEQ) != 0
      || (op1->exec_type & PAR) == 0 || (op2->exec_type & PAR) == 0
      || (op1->exec_type & PAR) == 0 || (op2->exec_type & PAR) == 0
      || (op1->unit == BOTH) || (op2->unit == BOTH)
      || (op1->unit == BOTH) || (op2->unit == BOTH)
      || (op1->unit == IU && op2->unit == IU)
      || (op1->unit == IU && op2->unit == IU)
      || (op1->unit == MU && op2->unit == MU))
      || (op1->unit == MU && op2->unit == MU))
    return 0;
    return 0;
 
 
  /* If this is auto parallelization, and the first instruction is a
  /* If this is auto parallelization, and the first instruction is a
     branch or should not be packed, then don't parallelize.  */
     branch or should not be packed, then don't parallelize.  */
  if (exec_type == PACK_UNSPEC
  if (exec_type == PACK_UNSPEC
      && (op1->exec_type & (ALONE | BRANCH)))
      && (op1->exec_type & (ALONE | BRANCH)))
    return 0;
    return 0;
 
 
  /* The idea here is to create two sets of bitmasks (mod and used)
  /* The idea here is to create two sets of bitmasks (mod and used)
     which indicate which registers are modified or used by each
     which indicate which registers are modified or used by each
     instruction.  The operation can only be done in parallel if
     instruction.  The operation can only be done in parallel if
     instruction 1 and instruction 2 modify different registers, and
     instruction 1 and instruction 2 modify different registers, and
     the first instruction does not modify registers that the second
     the first instruction does not modify registers that the second
     is using (The second instruction can modify registers that the
     is using (The second instruction can modify registers that the
     first is using as they are only written back after the first
     first is using as they are only written back after the first
     instruction has completed).  Accesses to control registers, PSW,
     instruction has completed).  Accesses to control registers, PSW,
     and memory are treated as accesses to a single register.  So if
     and memory are treated as accesses to a single register.  So if
     both instructions write memory or if the first instruction writes
     both instructions write memory or if the first instruction writes
     memory and the second reads, then they cannot be done in
     memory and the second reads, then they cannot be done in
     parallel.  Likewise, if the first instruction mucks with the psw
     parallel.  Likewise, if the first instruction mucks with the psw
     and the second reads the PSW (which includes C, F0, and F1), then
     and the second reads the PSW (which includes C, F0, and F1), then
     they cannot operate safely in parallel.  */
     they cannot operate safely in parallel.  */
 
 
  /* The bitmasks (mod and used) look like this (bit 31 = MSB).
  /* The bitmasks (mod and used) look like this (bit 31 = MSB).
     r0-r15       0-15
     r0-r15       0-15
     a0-a1        16-17
     a0-a1        16-17
     cr (not psw) 18
     cr (not psw) 18
     psw          19
     psw          19
     mem          20  */
     mem          20  */
 
 
  for (j = 0; j < 2; j++)
  for (j = 0; j < 2; j++)
    {
    {
      if (j == 0)
      if (j == 0)
        {
        {
          op = op1;
          op = op1;
          ins = insn1;
          ins = insn1;
        }
        }
      else
      else
        {
        {
          op = op2;
          op = op2;
          ins = insn2;
          ins = insn2;
        }
        }
      mod[j] = used[j] = 0;
      mod[j] = used[j] = 0;
      if (op->exec_type & BRANCH_LINK)
      if (op->exec_type & BRANCH_LINK)
        mod[j] |= 1 << 13;
        mod[j] |= 1 << 13;
 
 
      for (i = 0; op->operands[i]; i++)
      for (i = 0; op->operands[i]; i++)
        {
        {
          flags = d10v_operands[op->operands[i]].flags;
          flags = d10v_operands[op->operands[i]].flags;
          shift = d10v_operands[op->operands[i]].shift;
          shift = d10v_operands[op->operands[i]].shift;
          mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
          mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
          if (flags & OPERAND_REG)
          if (flags & OPERAND_REG)
            {
            {
              regno = (ins >> shift) & mask;
              regno = (ins >> shift) & mask;
              if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
              if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
                regno += 16;
                regno += 16;
              else if (flags & OPERAND_CONTROL) /* mvtc or mvfc.  */
              else if (flags & OPERAND_CONTROL) /* mvtc or mvfc.  */
                {
                {
                  if (regno == 0)
                  if (regno == 0)
                    regno = 19;
                    regno = 19;
                  else
                  else
                    regno = 18;
                    regno = 18;
                }
                }
              else if (flags & (OPERAND_FFLAG | OPERAND_CFLAG))
              else if (flags & (OPERAND_FFLAG | OPERAND_CFLAG))
                regno = 19;
                regno = 19;
 
 
              if (flags & OPERAND_DEST)
              if (flags & OPERAND_DEST)
                {
                {
                  mod[j] |= 1 << regno;
                  mod[j] |= 1 << regno;
                  if (flags & OPERAND_EVEN)
                  if (flags & OPERAND_EVEN)
                    mod[j] |= 1 << (regno + 1);
                    mod[j] |= 1 << (regno + 1);
                }
                }
              else
              else
                {
                {
                  used[j] |= 1 << regno;
                  used[j] |= 1 << regno;
                  if (flags & OPERAND_EVEN)
                  if (flags & OPERAND_EVEN)
                    used[j] |= 1 << (regno + 1);
                    used[j] |= 1 << (regno + 1);
 
 
                  /* Auto inc/dec also modifies the register.  */
                  /* Auto inc/dec also modifies the register.  */
                  if (op->operands[i + 1] != 0
                  if (op->operands[i + 1] != 0
                      && (d10v_operands[op->operands[i + 1]].flags
                      && (d10v_operands[op->operands[i + 1]].flags
                          & (OPERAND_PLUS | OPERAND_MINUS)) != 0)
                          & (OPERAND_PLUS | OPERAND_MINUS)) != 0)
                    mod[j] |= 1 << regno;
                    mod[j] |= 1 << regno;
                }
                }
            }
            }
          else if (flags & OPERAND_ATMINUS)
          else if (flags & OPERAND_ATMINUS)
            {
            {
              /* SP implicitly used/modified.  */
              /* SP implicitly used/modified.  */
              mod[j] |= 1 << 15;
              mod[j] |= 1 << 15;
              used[j] |= 1 << 15;
              used[j] |= 1 << 15;
            }
            }
        }
        }
      if (op->exec_type & RMEM)
      if (op->exec_type & RMEM)
        used[j] |= 1 << 20;
        used[j] |= 1 << 20;
      else if (op->exec_type & WMEM)
      else if (op->exec_type & WMEM)
        mod[j] |= 1 << 20;
        mod[j] |= 1 << 20;
      else if (op->exec_type & RF0)
      else if (op->exec_type & RF0)
        used[j] |= 1 << 19;
        used[j] |= 1 << 19;
      else if (op->exec_type & WF0)
      else if (op->exec_type & WF0)
        mod[j] |= 1 << 19;
        mod[j] |= 1 << 19;
      else if (op->exec_type & WCAR)
      else if (op->exec_type & WCAR)
        mod[j] |= 1 << 19;
        mod[j] |= 1 << 19;
    }
    }
  if ((mod[0] & mod[1]) == 0 && (mod[0] & used[1]) == 0)
  if ((mod[0] & mod[1]) == 0 && (mod[0] & used[1]) == 0)
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* Expects two short instructions.
/* Expects two short instructions.
   If possible, writes out both as a single packed instruction.
   If possible, writes out both as a single packed instruction.
   Otherwise, writes out the first one, packed with a NOP.
   Otherwise, writes out the first one, packed with a NOP.
   Returns number of instructions not written out.  */
   Returns number of instructions not written out.  */
 
 
static int
static int
write_2_short (struct d10v_opcode *opcode1,
write_2_short (struct d10v_opcode *opcode1,
               unsigned long insn1,
               unsigned long insn1,
               struct d10v_opcode *opcode2,
               struct d10v_opcode *opcode2,
               unsigned long insn2,
               unsigned long insn2,
               packing_type exec_type,
               packing_type exec_type,
               Fixups *fx)
               Fixups *fx)
{
{
  unsigned long insn;
  unsigned long insn;
  char *f;
  char *f;
  int i, j, where;
  int i, j, where;
 
 
  if ((exec_type != PACK_PARALLEL)
  if ((exec_type != PACK_PARALLEL)
      && ((opcode1->exec_type & PARONLY) || (opcode2->exec_type & PARONLY)))
      && ((opcode1->exec_type & PARONLY) || (opcode2->exec_type & PARONLY)))
    as_fatal (_("Instruction must be executed in parallel"));
    as_fatal (_("Instruction must be executed in parallel"));
 
 
  if ((opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE))
  if ((opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE))
    as_fatal (_("Long instructions may not be combined."));
    as_fatal (_("Long instructions may not be combined."));
 
 
  switch (exec_type)
  switch (exec_type)
    {
    {
    case PACK_UNSPEC:   /* Order not specified.  */
    case PACK_UNSPEC:   /* Order not specified.  */
      if (opcode1->exec_type & ALONE)
      if (opcode1->exec_type & ALONE)
        {
        {
          /* Case of a short branch on a separate GAS line.  Pack with NOP.  */
          /* Case of a short branch on a separate GAS line.  Pack with NOP.  */
          write_1_short (opcode1, insn1, fx->next);
          write_1_short (opcode1, insn1, fx->next);
          return 1;
          return 1;
        }
        }
      if (Optimizing
      if (Optimizing
          && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
          && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
        {
        {
          /* Parallel.  */
          /* Parallel.  */
          if (opcode1->unit == IU)
          if (opcode1->unit == IU)
            insn = FM00 | (insn2 << 15) | insn1;
            insn = FM00 | (insn2 << 15) | insn1;
          else if (opcode2->unit == MU)
          else if (opcode2->unit == MU)
            insn = FM00 | (insn2 << 15) | insn1;
            insn = FM00 | (insn2 << 15) | insn1;
          else
          else
            insn = FM00 | (insn1 << 15) | insn2;
            insn = FM00 | (insn1 << 15) | insn2;
        }
        }
      else if (opcode1->unit == IU)
      else if (opcode1->unit == IU)
        /* Reverse sequential with IU opcode1 on right and done first.  */
        /* Reverse sequential with IU opcode1 on right and done first.  */
        insn = FM10 | (insn2 << 15) | insn1;
        insn = FM10 | (insn2 << 15) | insn1;
      else
      else
        /* Sequential with non-IU opcode1 on left and done first.  */
        /* Sequential with non-IU opcode1 on left and done first.  */
        insn = FM01 | (insn1 << 15) | insn2;
        insn = FM01 | (insn1 << 15) | insn2;
      break;
      break;
 
 
    case PACK_PARALLEL:
    case PACK_PARALLEL:
      if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
      if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
        as_fatal
        as_fatal
          (_("One of these instructions may not be executed in parallel."));
          (_("One of these instructions may not be executed in parallel."));
      if (opcode1->unit == IU)
      if (opcode1->unit == IU)
        {
        {
          if (opcode2->unit == IU)
          if (opcode2->unit == IU)
            as_fatal (_("Two IU instructions may not be executed in parallel"));
            as_fatal (_("Two IU instructions may not be executed in parallel"));
          if (!flag_warn_suppress_instructionswap)
          if (!flag_warn_suppress_instructionswap)
            as_warn (_("Swapping instruction order"));
            as_warn (_("Swapping instruction order"));
          insn = FM00 | (insn2 << 15) | insn1;
          insn = FM00 | (insn2 << 15) | insn1;
        }
        }
      else if (opcode2->unit == MU)
      else if (opcode2->unit == MU)
        {
        {
          if (opcode1->unit == MU)
          if (opcode1->unit == MU)
            as_fatal (_("Two MU instructions may not be executed in parallel"));
            as_fatal (_("Two MU instructions may not be executed in parallel"));
          if (!flag_warn_suppress_instructionswap)
          if (!flag_warn_suppress_instructionswap)
            as_warn (_("Swapping instruction order"));
            as_warn (_("Swapping instruction order"));
          insn = FM00 | (insn2 << 15) | insn1;
          insn = FM00 | (insn2 << 15) | insn1;
        }
        }
      else
      else
        insn = FM00 | (insn1 << 15) | insn2;
        insn = FM00 | (insn1 << 15) | insn2;
      check_resource_conflict (opcode1, insn1, opcode2, insn2);
      check_resource_conflict (opcode1, insn1, opcode2, insn2);
      break;
      break;
 
 
    case PACK_LEFT_RIGHT:
    case PACK_LEFT_RIGHT:
      if (opcode1->unit != IU)
      if (opcode1->unit != IU)
        insn = FM01 | (insn1 << 15) | insn2;
        insn = FM01 | (insn1 << 15) | insn2;
      else if (opcode2->unit == MU || opcode2->unit == EITHER)
      else if (opcode2->unit == MU || opcode2->unit == EITHER)
        {
        {
          if (!flag_warn_suppress_instructionswap)
          if (!flag_warn_suppress_instructionswap)
            as_warn (_("Swapping instruction order"));
            as_warn (_("Swapping instruction order"));
          insn = FM10 | (insn2 << 15) | insn1;
          insn = FM10 | (insn2 << 15) | insn1;
        }
        }
      else
      else
        as_fatal (_("IU instruction may not be in the left container"));
        as_fatal (_("IU instruction may not be in the left container"));
      if (opcode1->exec_type & ALONE)
      if (opcode1->exec_type & ALONE)
        as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
        as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
      break;
      break;
 
 
    case PACK_RIGHT_LEFT:
    case PACK_RIGHT_LEFT:
      if (opcode2->unit != MU)
      if (opcode2->unit != MU)
        insn = FM10 | (insn1 << 15) | insn2;
        insn = FM10 | (insn1 << 15) | insn2;
      else if (opcode1->unit == IU || opcode1->unit == EITHER)
      else if (opcode1->unit == IU || opcode1->unit == EITHER)
        {
        {
          if (!flag_warn_suppress_instructionswap)
          if (!flag_warn_suppress_instructionswap)
            as_warn (_("Swapping instruction order"));
            as_warn (_("Swapping instruction order"));
          insn = FM01 | (insn2 << 15) | insn1;
          insn = FM01 | (insn2 << 15) | insn1;
        }
        }
      else
      else
        as_fatal (_("MU instruction may not be in the right container"));
        as_fatal (_("MU instruction may not be in the right container"));
      if (opcode2->exec_type & ALONE)
      if (opcode2->exec_type & ALONE)
        as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
        as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
      break;
      break;
 
 
    default:
    default:
      as_fatal (_("unknown execution type passed to write_2_short()"));
      as_fatal (_("unknown execution type passed to write_2_short()"));
    }
    }
 
 
  f = frag_more (4);
  f = frag_more (4);
  number_to_chars_bigendian (f, insn, 4);
  number_to_chars_bigendian (f, insn, 4);
 
 
  /* Process fixup chains.  fx refers to insn2 when j == 0, and to
  /* Process fixup chains.  fx refers to insn2 when j == 0, and to
     insn1 when j == 1.  Yes, it's reversed.  */
     insn1 when j == 1.  Yes, it's reversed.  */
 
 
  for (j = 0; j < 2; j++)
  for (j = 0; j < 2; j++)
    {
    {
      for (i = 0; i < fx->fc; i++)
      for (i = 0; i < fx->fc; i++)
        {
        {
          if (fx->fix[i].reloc)
          if (fx->fix[i].reloc)
            {
            {
              where = f - frag_now->fr_literal;
              where = f - frag_now->fr_literal;
              if (fx->fix[i].size == 2)
              if (fx->fix[i].size == 2)
                where += 2;
                where += 2;
 
 
              if (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R
              if (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R
                  /* A BFD_RELOC_D10V_10_PCREL_R relocation applied to
                  /* A BFD_RELOC_D10V_10_PCREL_R relocation applied to
                     the instruction in the L container has to be
                     the instruction in the L container has to be
                     adjusted to BDF_RELOC_D10V_10_PCREL_L.  When
                     adjusted to BDF_RELOC_D10V_10_PCREL_L.  When
                     j==0, we're processing insn2's operands, so we
                     j==0, we're processing insn2's operands, so we
                     want to mark the operand if insn2 is *not* in the
                     want to mark the operand if insn2 is *not* in the
                     R container.  When j==1, we're processing insn1's
                     R container.  When j==1, we're processing insn1's
                     operands, so we want to mark the operand if insn2
                     operands, so we want to mark the operand if insn2
                     *is* in the R container.  Note that, if two
                     *is* in the R container.  Note that, if two
                     instructions are identical, we're never going to
                     instructions are identical, we're never going to
                     swap them, so the test is safe.  */
                     swap them, so the test is safe.  */
                  && j == ((insn & 0x7fff) == insn2))
                  && j == ((insn & 0x7fff) == insn2))
                fx->fix[i].operand |= 1024;
                fx->fix[i].operand |= 1024;
 
 
              if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
              if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
                fx->fix[i].operand |= 4096;
                fx->fix[i].operand |= 4096;
 
 
              fix_new_exp (frag_now,
              fix_new_exp (frag_now,
                           where,
                           where,
                           fx->fix[i].size,
                           fx->fix[i].size,
                           &(fx->fix[i].exp),
                           &(fx->fix[i].exp),
                           fx->fix[i].pcrel,
                           fx->fix[i].pcrel,
                           fx->fix[i].operand|2048);
                           fx->fix[i].operand|2048);
            }
            }
        }
        }
      fx->fc = 0;
      fx->fc = 0;
      fx = fx->next;
      fx = fx->next;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* This is the main entry point for the machine-dependent assembler.
/* This is the main entry point for the machine-dependent assembler.
   str points to a machine-dependent instruction.  This function is
   str points to a machine-dependent instruction.  This function is
   supposed to emit the frags/bytes it assembles to.  For the D10V, it
   supposed to emit the frags/bytes it assembles to.  For the D10V, it
   mostly handles the special VLIW parsing and packing and leaves the
   mostly handles the special VLIW parsing and packing and leaves the
   difficult stuff to do_assemble().  */
   difficult stuff to do_assemble().  */
 
 
static unsigned long prev_insn;
static unsigned long prev_insn;
static struct d10v_opcode *prev_opcode = 0;
static struct d10v_opcode *prev_opcode = 0;
static subsegT prev_subseg;
static subsegT prev_subseg;
static segT prev_seg = 0;;
static segT prev_seg = 0;;
 
 
/* Find the symbol which has the same name as the register in exp.  */
/* Find the symbol which has the same name as the register in exp.  */
 
 
static symbolS *
static symbolS *
find_symbol_matching_register (expressionS *exp)
find_symbol_matching_register (expressionS *exp)
{
{
  int i;
  int i;
 
 
  if (exp->X_op != O_register)
  if (exp->X_op != O_register)
    return NULL;
    return NULL;
 
 
  /* Find the name of the register.  */
  /* Find the name of the register.  */
  for (i = d10v_reg_name_cnt (); i--;)
  for (i = d10v_reg_name_cnt (); i--;)
    if (d10v_predefined_registers[i].value == exp->X_add_number)
    if (d10v_predefined_registers[i].value == exp->X_add_number)
      break;
      break;
 
 
  if (i < 0)
  if (i < 0)
    abort ();
    abort ();
 
 
  /* Now see if a symbol has been defined with the same name.  */
  /* Now see if a symbol has been defined with the same name.  */
  return symbol_find (d10v_predefined_registers[i].name);
  return symbol_find (d10v_predefined_registers[i].name);
}
}
 
 
/* Get a pointer to an entry in the opcode table.
/* Get a pointer to an entry in the opcode table.
   The function must look at all opcodes with the same name and use
   The function must look at all opcodes with the same name and use
   the operands to choose the correct opcode.  */
   the operands to choose the correct opcode.  */
 
 
static struct d10v_opcode *
static struct d10v_opcode *
find_opcode (struct d10v_opcode *opcode, expressionS myops[])
find_opcode (struct d10v_opcode *opcode, expressionS myops[])
{
{
  int i, match;
  int i, match;
  struct d10v_opcode *next_opcode;
  struct d10v_opcode *next_opcode;
 
 
  /* Get all the operands and save them as expressions.  */
  /* Get all the operands and save them as expressions.  */
  get_operands (myops);
  get_operands (myops);
 
 
  /* Now see if the operand is a fake.  If so, find the correct size
  /* Now see if the operand is a fake.  If so, find the correct size
     instruction, if possible.  */
     instruction, if possible.  */
  if (opcode->format == OPCODE_FAKE)
  if (opcode->format == OPCODE_FAKE)
    {
    {
      int opnum = opcode->operands[0];
      int opnum = opcode->operands[0];
      int flags;
      int flags;
 
 
      if (myops[opnum].X_op == O_register)
      if (myops[opnum].X_op == O_register)
        {
        {
          myops[opnum].X_op = O_symbol;
          myops[opnum].X_op = O_symbol;
          myops[opnum].X_add_symbol =
          myops[opnum].X_add_symbol =
            symbol_find_or_make ((char *) myops[opnum].X_op_symbol);
            symbol_find_or_make ((char *) myops[opnum].X_op_symbol);
          myops[opnum].X_add_number = 0;
          myops[opnum].X_add_number = 0;
          myops[opnum].X_op_symbol = NULL;
          myops[opnum].X_op_symbol = NULL;
        }
        }
 
 
      next_opcode = opcode + 1;
      next_opcode = opcode + 1;
 
 
      /* If the first operand is supposed to be a register, make sure
      /* If the first operand is supposed to be a register, make sure
         we got a valid one.  */
         we got a valid one.  */
      flags = d10v_operands[next_opcode->operands[0]].flags;
      flags = d10v_operands[next_opcode->operands[0]].flags;
      if (flags & OPERAND_REG)
      if (flags & OPERAND_REG)
        {
        {
          int X_op = myops[0].X_op;
          int X_op = myops[0].X_op;
          int num = myops[0].X_add_number;
          int num = myops[0].X_add_number;
 
 
          if (X_op != O_register
          if (X_op != O_register
              || (num & ~flags
              || (num & ~flags
                  & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
                  & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
                     | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL))
                     | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL))
              || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
              || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
            {
            {
              as_bad (_("bad opcode or operands"));
              as_bad (_("bad opcode or operands"));
              return 0;
              return 0;
            }
            }
        }
        }
 
 
      if (myops[opnum].X_op == O_constant
      if (myops[opnum].X_op == O_constant
          || (myops[opnum].X_op == O_symbol
          || (myops[opnum].X_op == O_symbol
              && S_IS_DEFINED (myops[opnum].X_add_symbol)
              && S_IS_DEFINED (myops[opnum].X_add_symbol)
              && (S_GET_SEGMENT (myops[opnum].X_add_symbol) == now_seg)))
              && (S_GET_SEGMENT (myops[opnum].X_add_symbol) == now_seg)))
        {
        {
          for (i = 0; opcode->operands[i + 1]; i++)
          for (i = 0; opcode->operands[i + 1]; i++)
            {
            {
              int bits = d10v_operands[next_opcode->operands[opnum]].bits;
              int bits = d10v_operands[next_opcode->operands[opnum]].bits;
              int flags = d10v_operands[next_opcode->operands[opnum]].flags;
              int flags = d10v_operands[next_opcode->operands[opnum]].flags;
              if (flags & OPERAND_ADDR)
              if (flags & OPERAND_ADDR)
                bits += 2;
                bits += 2;
 
 
              if (myops[opnum].X_op == O_constant)
              if (myops[opnum].X_op == O_constant)
                {
                {
                  if (!check_range (myops[opnum].X_add_number, bits, flags))
                  if (!check_range (myops[opnum].X_add_number, bits, flags))
                    break;
                    break;
                }
                }
              else
              else
                {
                {
                  fragS *sym_frag;
                  fragS *sym_frag;
                  fragS *f;
                  fragS *f;
                  unsigned long current_position;
                  unsigned long current_position;
                  unsigned long symbol_position;
                  unsigned long symbol_position;
                  unsigned long value;
                  unsigned long value;
                  bfd_boolean found_symbol;
                  bfd_boolean found_symbol;
 
 
                  /* Calculate the address of the current instruction
                  /* Calculate the address of the current instruction
                     and the address of the symbol.  Do this by summing
                     and the address of the symbol.  Do this by summing
                     the offsets of previous frags until we reach the
                     the offsets of previous frags until we reach the
                     frag containing the symbol, and the current frag.  */
                     frag containing the symbol, and the current frag.  */
                  sym_frag = symbol_get_frag (myops[opnum].X_add_symbol);
                  sym_frag = symbol_get_frag (myops[opnum].X_add_symbol);
                  found_symbol = FALSE;
                  found_symbol = FALSE;
 
 
                  current_position =
                  current_position =
                    obstack_next_free (&frchain_now->frch_obstack)
                    obstack_next_free (&frchain_now->frch_obstack)
                    - frag_now->fr_literal;
                    - frag_now->fr_literal;
                  symbol_position = S_GET_VALUE (myops[opnum].X_add_symbol);
                  symbol_position = S_GET_VALUE (myops[opnum].X_add_symbol);
 
 
                  for (f = frchain_now->frch_root; f; f = f->fr_next)
                  for (f = frchain_now->frch_root; f; f = f->fr_next)
                    {
                    {
                      current_position += f->fr_fix + f->fr_offset;
                      current_position += f->fr_fix + f->fr_offset;
 
 
                      if (f == sym_frag)
                      if (f == sym_frag)
                        found_symbol = TRUE;
                        found_symbol = TRUE;
 
 
                      if (! found_symbol)
                      if (! found_symbol)
                        symbol_position += f->fr_fix + f->fr_offset;
                        symbol_position += f->fr_fix + f->fr_offset;
                    }
                    }
 
 
                  value = symbol_position;
                  value = symbol_position;
 
 
                  if (flags & OPERAND_ADDR)
                  if (flags & OPERAND_ADDR)
                    value -= current_position;
                    value -= current_position;
 
 
                  if (AT_WORD_P (&myops[opnum]))
                  if (AT_WORD_P (&myops[opnum]))
                    {
                    {
                      if (bits > 4)
                      if (bits > 4)
                        {
                        {
                          bits += 2;
                          bits += 2;
                          if (!check_range (value, bits, flags))
                          if (!check_range (value, bits, flags))
                            break;
                            break;
                        }
                        }
                    }
                    }
                  else if (!check_range (value, bits, flags))
                  else if (!check_range (value, bits, flags))
                    break;
                    break;
                }
                }
              next_opcode++;
              next_opcode++;
            }
            }
 
 
          if (opcode->operands [i + 1] == 0)
          if (opcode->operands [i + 1] == 0)
            as_fatal (_("value out of range"));
            as_fatal (_("value out of range"));
          else
          else
            opcode = next_opcode;
            opcode = next_opcode;
        }
        }
      else
      else
        /* Not a constant, so use a long instruction.  */
        /* Not a constant, so use a long instruction.  */
        opcode += 2;
        opcode += 2;
    }
    }
 
 
  match = 0;
  match = 0;
 
 
  /* Now search the opcode table table for one with operands
  /* Now search the opcode table table for one with operands
     that matches what we've got.  */
     that matches what we've got.  */
  while (!match)
  while (!match)
    {
    {
      match = 1;
      match = 1;
      for (i = 0; opcode->operands[i]; i++)
      for (i = 0; opcode->operands[i]; i++)
        {
        {
          int flags = d10v_operands[opcode->operands[i]].flags;
          int flags = d10v_operands[opcode->operands[i]].flags;
          int X_op = myops[i].X_op;
          int X_op = myops[i].X_op;
          int num = myops[i].X_add_number;
          int num = myops[i].X_add_number;
 
 
          if (X_op == 0)
          if (X_op == 0)
            {
            {
              match = 0;
              match = 0;
              break;
              break;
            }
            }
 
 
          if (flags & OPERAND_REG)
          if (flags & OPERAND_REG)
            {
            {
              if ((X_op != O_register)
              if ((X_op != O_register)
                  || (num & ~flags
                  || (num & ~flags
                      & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
                      & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
                         | OPERAND_FFLAG | OPERAND_CFLAG
                         | OPERAND_FFLAG | OPERAND_CFLAG
                         | OPERAND_CONTROL))
                         | OPERAND_CONTROL))
                  || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
                  || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
                {
                {
                  match = 0;
                  match = 0;
                  break;
                  break;
                }
                }
            }
            }
 
 
          if (((flags & OPERAND_MINUS)   && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
          if (((flags & OPERAND_MINUS)   && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
              ((flags & OPERAND_PLUS)    && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
              ((flags & OPERAND_PLUS)    && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
              ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
              ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
              ((flags & OPERAND_ATPAR)   && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
              ((flags & OPERAND_ATPAR)   && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
              ((flags & OPERAND_ATSIGN)  && ((X_op != O_absent) || ((num != OPERAND_ATSIGN) && (num != OPERAND_ATPAR)))))
              ((flags & OPERAND_ATSIGN)  && ((X_op != O_absent) || ((num != OPERAND_ATSIGN) && (num != OPERAND_ATPAR)))))
            {
            {
              match = 0;
              match = 0;
              break;
              break;
            }
            }
 
 
          /* Unfortunately, for the indirect operand in instructions such
          /* Unfortunately, for the indirect operand in instructions such
             as ``ldb r1, @(c,r14)'' this function can be passed
             as ``ldb r1, @(c,r14)'' this function can be passed
             X_op == O_register (because 'c' is a valid register name).
             X_op == O_register (because 'c' is a valid register name).
             However we cannot just ignore the case when X_op == O_register
             However we cannot just ignore the case when X_op == O_register
             but flags & OPERAND_REG is null, so we check to see if a symbol
             but flags & OPERAND_REG is null, so we check to see if a symbol
             of the same name as the register exists.  If the symbol does
             of the same name as the register exists.  If the symbol does
             exist, then the parser was unable to distinguish the two cases
             exist, then the parser was unable to distinguish the two cases
             and we fix things here. (Ref: PR14826)  */
             and we fix things here. (Ref: PR14826)  */
 
 
          if (!(flags & OPERAND_REG) && (X_op == O_register))
          if (!(flags & OPERAND_REG) && (X_op == O_register))
            {
            {
              symbolS * sym;
              symbolS * sym;
 
 
              sym = find_symbol_matching_register (& myops[i]);
              sym = find_symbol_matching_register (& myops[i]);
 
 
              if (sym != NULL)
              if (sym != NULL)
                {
                {
                  myops[i].X_op = X_op = O_symbol;
                  myops[i].X_op = X_op = O_symbol;
                  myops[i].X_add_symbol = sym;
                  myops[i].X_add_symbol = sym;
                }
                }
              else
              else
                as_bad
                as_bad
                  (_("illegal operand - register name found where none expected"));
                  (_("illegal operand - register name found where none expected"));
            }
            }
        }
        }
 
 
      /* We're only done if the operands matched so far AND there
      /* We're only done if the operands matched so far AND there
             are no more to check.  */
             are no more to check.  */
      if (match && myops[i].X_op == 0)
      if (match && myops[i].X_op == 0)
        break;
        break;
      else
      else
        match = 0;
        match = 0;
 
 
      next_opcode = opcode + 1;
      next_opcode = opcode + 1;
 
 
      if (next_opcode->opcode == 0)
      if (next_opcode->opcode == 0)
        break;
        break;
 
 
      if (strcmp (next_opcode->name, opcode->name))
      if (strcmp (next_opcode->name, opcode->name))
        break;
        break;
 
 
      opcode = next_opcode;
      opcode = next_opcode;
    }
    }
 
 
  if (!match)
  if (!match)
    {
    {
      as_bad (_("bad opcode or operands"));
      as_bad (_("bad opcode or operands"));
      return 0;
      return 0;
    }
    }
 
 
  /* Check that all registers that are required to be even are.
  /* Check that all registers that are required to be even are.
     Also, if any operands were marked as registers, but were really symbols,
     Also, if any operands were marked as registers, but were really symbols,
     fix that here.  */
     fix that here.  */
  for (i = 0; opcode->operands[i]; i++)
  for (i = 0; opcode->operands[i]; i++)
    {
    {
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
          (myops[i].X_add_number & 1))
          (myops[i].X_add_number & 1))
        as_fatal (_("Register number must be EVEN"));
        as_fatal (_("Register number must be EVEN"));
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_NOSP)
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_NOSP)
          && (myops[i].X_add_number & OPERAND_SP))
          && (myops[i].X_add_number & OPERAND_SP))
        as_bad (_("Unsupported use of sp"));
        as_bad (_("Unsupported use of sp"));
      if (myops[i].X_op == O_register)
      if (myops[i].X_op == O_register)
        {
        {
          if (!(d10v_operands[opcode->operands[i]].flags & OPERAND_REG))
          if (!(d10v_operands[opcode->operands[i]].flags & OPERAND_REG))
            {
            {
              myops[i].X_op = O_symbol;
              myops[i].X_op = O_symbol;
              myops[i].X_add_symbol =
              myops[i].X_add_symbol =
                symbol_find_or_make ((char *) myops[i].X_op_symbol);
                symbol_find_or_make ((char *) myops[i].X_op_symbol);
              myops[i].X_add_number = 0;
              myops[i].X_add_number = 0;
              myops[i].X_op_symbol = NULL;
              myops[i].X_op_symbol = NULL;
            }
            }
        }
        }
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_CONTROL)
      if ((d10v_operands[opcode->operands[i]].flags & OPERAND_CONTROL)
          && (myops[i].X_add_number == OPERAND_CONTROL + 4
          && (myops[i].X_add_number == OPERAND_CONTROL + 4
              || myops[i].X_add_number == OPERAND_CONTROL + 5
              || myops[i].X_add_number == OPERAND_CONTROL + 5
              || myops[i].X_add_number == OPERAND_CONTROL + 6
              || myops[i].X_add_number == OPERAND_CONTROL + 6
              || myops[i].X_add_number == OPERAND_CONTROL + 12
              || myops[i].X_add_number == OPERAND_CONTROL + 12
              || myops[i].X_add_number == OPERAND_CONTROL + 13
              || myops[i].X_add_number == OPERAND_CONTROL + 13
              || myops[i].X_add_number == OPERAND_CONTROL + 15))
              || myops[i].X_add_number == OPERAND_CONTROL + 15))
        as_warn (_("cr%ld is a reserved control register"),
        as_warn (_("cr%ld is a reserved control register"),
                 myops[i].X_add_number - OPERAND_CONTROL);
                 myops[i].X_add_number - OPERAND_CONTROL);
    }
    }
  return opcode;
  return opcode;
}
}
 
 
/* Assemble a single instruction.
/* Assemble a single instruction.
   Return an opcode, or -1 (an invalid opcode) on error.  */
   Return an opcode, or -1 (an invalid opcode) on error.  */
 
 
static unsigned long
static unsigned long
do_assemble (char *str, struct d10v_opcode **opcode)
do_assemble (char *str, struct d10v_opcode **opcode)
{
{
  unsigned char *op_start, *op_end;
  unsigned char *op_start, *op_end;
  char *save;
  char *save;
  char name[20];
  char name[20];
  int nlen = 0;
  int nlen = 0;
  expressionS myops[6];
  expressionS myops[6];
 
 
  /* Drop leading whitespace.  */
  /* Drop leading whitespace.  */
  while (*str == ' ')
  while (*str == ' ')
    str++;
    str++;
 
 
  /* Find the opcode end.  */
  /* Find the opcode end.  */
  for (op_start = op_end = (unsigned char *) str;
  for (op_start = op_end = (unsigned char *) str;
       *op_end && nlen < 20 && !is_end_of_line[*op_end] && *op_end != ' ';
       *op_end && nlen < 20 && !is_end_of_line[*op_end] && *op_end != ' ';
       op_end++)
       op_end++)
    {
    {
      name[nlen] = TOLOWER (op_start[nlen]);
      name[nlen] = TOLOWER (op_start[nlen]);
      nlen++;
      nlen++;
    }
    }
  name[nlen] = 0;
  name[nlen] = 0;
 
 
  if (nlen == 0)
  if (nlen == 0)
    return -1;
    return -1;
 
 
  /* Find the first opcode with the proper name.  */
  /* Find the first opcode with the proper name.  */
  *opcode = (struct d10v_opcode *) hash_find (d10v_hash, name);
  *opcode = (struct d10v_opcode *) hash_find (d10v_hash, name);
  if (*opcode == NULL)
  if (*opcode == NULL)
    return -1;
    return -1;
 
 
  save = input_line_pointer;
  save = input_line_pointer;
  input_line_pointer = (char *) op_end;
  input_line_pointer = (char *) op_end;
  *opcode = find_opcode (*opcode, myops);
  *opcode = find_opcode (*opcode, myops);
  if (*opcode == 0)
  if (*opcode == 0)
    return -1;
    return -1;
  input_line_pointer = save;
  input_line_pointer = save;
 
 
  return build_insn ((*opcode), myops, 0);
  return build_insn ((*opcode), myops, 0);
}
}
 
 
/* If while processing a fixup, a reloc really needs to be created.
/* If while processing a fixup, a reloc really needs to be created.
   Then it is done here.  */
   Then it is done here.  */
 
 
arelent *
arelent *
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
{
{
  arelent *reloc;
  arelent *reloc;
  reloc = xmalloc (sizeof (arelent));
  reloc = xmalloc (sizeof (arelent));
  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
  reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
  if (reloc->howto == (reloc_howto_type *) NULL)
  if (reloc->howto == (reloc_howto_type *) NULL)
    {
    {
      as_bad_where (fixp->fx_file, fixp->fx_line,
      as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("reloc %d not supported by object file format"),
                    _("reloc %d not supported by object file format"),
                    (int) fixp->fx_r_type);
                    (int) fixp->fx_r_type);
      return NULL;
      return NULL;
    }
    }
 
 
  if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
  if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
    reloc->address = fixp->fx_offset;
    reloc->address = fixp->fx_offset;
 
 
  reloc->addend = 0;
  reloc->addend = 0;
 
 
  return reloc;
  return reloc;
}
}
 
 
int
int
md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
                               asection *seg ATTRIBUTE_UNUSED)
                               asection *seg ATTRIBUTE_UNUSED)
{
{
  abort ();
  abort ();
  return 0;
  return 0;
}
}
 
 
long
long
md_pcrel_from_section (fixS *fixp, segT sec)
md_pcrel_from_section (fixS *fixp, segT sec)
{
{
  if (fixp->fx_addsy != (symbolS *) NULL
  if (fixp->fx_addsy != (symbolS *) NULL
      && (!S_IS_DEFINED (fixp->fx_addsy)
      && (!S_IS_DEFINED (fixp->fx_addsy)
          || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
          || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
    return 0;
    return 0;
  return fixp->fx_frag->fr_address + fixp->fx_where;
  return fixp->fx_frag->fr_address + fixp->fx_where;
}
}
 
 
void
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
{
  char *where;
  char *where;
  unsigned long insn;
  unsigned long insn;
  long value = *valP;
  long value = *valP;
  int op_type;
  int op_type;
  int left = 0;
  int left = 0;
 
 
  if (fixP->fx_addsy == (symbolS *) NULL)
  if (fixP->fx_addsy == (symbolS *) NULL)
    fixP->fx_done = 1;
    fixP->fx_done = 1;
 
 
  /* We don't actually support subtracting a symbol.  */
  /* We don't actually support subtracting a symbol.  */
  if (fixP->fx_subsy != (symbolS *) NULL)
  if (fixP->fx_subsy != (symbolS *) NULL)
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
    as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
 
 
  op_type = fixP->fx_r_type;
  op_type = fixP->fx_r_type;
  if (op_type & 2048)
  if (op_type & 2048)
    {
    {
      op_type -= 2048;
      op_type -= 2048;
      if (op_type & 1024)
      if (op_type & 1024)
        {
        {
          op_type -= 1024;
          op_type -= 1024;
          fixP->fx_r_type = BFD_RELOC_D10V_10_PCREL_L;
          fixP->fx_r_type = BFD_RELOC_D10V_10_PCREL_L;
          left = 1;
          left = 1;
        }
        }
      else if (op_type & 4096)
      else if (op_type & 4096)
        {
        {
          op_type -= 4096;
          op_type -= 4096;
          fixP->fx_r_type = BFD_RELOC_D10V_18;
          fixP->fx_r_type = BFD_RELOC_D10V_18;
        }
        }
      else
      else
        fixP->fx_r_type =
        fixP->fx_r_type =
          get_reloc ((struct d10v_operand *) &d10v_operands[op_type]);
          get_reloc ((struct d10v_operand *) &d10v_operands[op_type]);
    }
    }
 
 
  /* Fetch the instruction, insert the fully resolved operand
  /* Fetch the instruction, insert the fully resolved operand
     value, and stuff the instruction back again.  */
     value, and stuff the instruction back again.  */
  where = fixP->fx_frag->fr_literal + fixP->fx_where;
  where = fixP->fx_frag->fr_literal + fixP->fx_where;
  insn = bfd_getb32 ((unsigned char *) where);
  insn = bfd_getb32 ((unsigned char *) where);
 
 
  switch (fixP->fx_r_type)
  switch (fixP->fx_r_type)
    {
    {
    case BFD_RELOC_D10V_10_PCREL_L:
    case BFD_RELOC_D10V_10_PCREL_L:
    case BFD_RELOC_D10V_10_PCREL_R:
    case BFD_RELOC_D10V_10_PCREL_R:
    case BFD_RELOC_D10V_18_PCREL:
    case BFD_RELOC_D10V_18_PCREL:
      /* If the fix is relative to a global symbol, not a section
      /* If the fix is relative to a global symbol, not a section
         symbol, then ignore the offset.
         symbol, then ignore the offset.
         XXX - Do we have to worry about branches to a symbol + offset ?  */
         XXX - Do we have to worry about branches to a symbol + offset ?  */
      if (fixP->fx_addsy != NULL
      if (fixP->fx_addsy != NULL
          && S_IS_EXTERNAL (fixP->fx_addsy) )
          && S_IS_EXTERNAL (fixP->fx_addsy) )
        {
        {
          segT fseg = S_GET_SEGMENT (fixP->fx_addsy);
          segT fseg = S_GET_SEGMENT (fixP->fx_addsy);
          segment_info_type *segf = seg_info(fseg);
          segment_info_type *segf = seg_info(fseg);
 
 
          if ( segf && segf->sym != fixP->fx_addsy)
          if ( segf && segf->sym != fixP->fx_addsy)
            value = 0;
            value = 0;
        }
        }
      /* Drop through.  */
      /* Drop through.  */
    case BFD_RELOC_D10V_18:
    case BFD_RELOC_D10V_18:
      /* Instruction addresses are always right-shifted by 2.  */
      /* Instruction addresses are always right-shifted by 2.  */
      value >>= AT_WORD_RIGHT_SHIFT;
      value >>= AT_WORD_RIGHT_SHIFT;
      if (fixP->fx_size == 2)
      if (fixP->fx_size == 2)
        bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
        bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
      else
      else
        {
        {
          struct d10v_opcode *rep, *repi;
          struct d10v_opcode *rep, *repi;
 
 
          rep = (struct d10v_opcode *) hash_find (d10v_hash, "rep");
          rep = (struct d10v_opcode *) hash_find (d10v_hash, "rep");
          repi = (struct d10v_opcode *) hash_find (d10v_hash, "repi");
          repi = (struct d10v_opcode *) hash_find (d10v_hash, "repi");
          if ((insn & FM11) == FM11
          if ((insn & FM11) == FM11
              && ((repi != NULL
              && ((repi != NULL
                   && (insn & repi->mask) == (unsigned) repi->opcode)
                   && (insn & repi->mask) == (unsigned) repi->opcode)
                  || (rep != NULL
                  || (rep != NULL
                      && (insn & rep->mask) == (unsigned) rep->opcode))
                      && (insn & rep->mask) == (unsigned) rep->opcode))
              && value < 4)
              && value < 4)
            as_fatal
            as_fatal
              (_("line %d: rep or repi must include at least 4 instructions"),
              (_("line %d: rep or repi must include at least 4 instructions"),
               fixP->fx_line);
               fixP->fx_line);
          insn =
          insn =
            d10v_insert_operand (insn, op_type, (offsetT) value, left, fixP);
            d10v_insert_operand (insn, op_type, (offsetT) value, left, fixP);
          bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
          bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
        }
        }
      break;
      break;
    case BFD_RELOC_32:
    case BFD_RELOC_32:
      bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
      bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
      break;
      break;
    case BFD_RELOC_16:
    case BFD_RELOC_16:
      bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
      bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
      break;
      break;
 
 
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_INHERIT:
    case BFD_RELOC_VTABLE_ENTRY:
    case BFD_RELOC_VTABLE_ENTRY:
      fixP->fx_done = 0;
      fixP->fx_done = 0;
      return;
      return;
 
 
    default:
    default:
      as_fatal (_("line %d: unknown relocation type: 0x%x"),
      as_fatal (_("line %d: unknown relocation type: 0x%x"),
                fixP->fx_line, fixP->fx_r_type);
                fixP->fx_line, fixP->fx_r_type);
    }
    }
}
}
 
 
/* d10v_cleanup() is called after the assembler has finished parsing
/* d10v_cleanup() is called after the assembler has finished parsing
   the input file, when a label is read from the input file, or when a
   the input file, when a label is read from the input file, or when a
   stab directive is output.  Because the D10V assembler sometimes
   stab directive is output.  Because the D10V assembler sometimes
   saves short instructions to see if it can package them with the
   saves short instructions to see if it can package them with the
   next instruction, there may be a short instruction that still needs
   next instruction, there may be a short instruction that still needs
   to be written.
   to be written.
 
 
   NOTE: accesses a global, etype.
   NOTE: accesses a global, etype.
   NOTE: invoked by various macros such as md_cleanup: see.  */
   NOTE: invoked by various macros such as md_cleanup: see.  */
 
 
int
int
d10v_cleanup (void)
d10v_cleanup (void)
{
{
  segT seg;
  segT seg;
  subsegT subseg;
  subsegT subseg;
 
 
  /* If cleanup was invoked because the assembler encountered, e.g., a
  /* If cleanup was invoked because the assembler encountered, e.g., a
     user label, we write out the pending instruction, if any.  If it
     user label, we write out the pending instruction, if any.  If it
     was invoked because the assembler is outputting a piece of line
     was invoked because the assembler is outputting a piece of line
     debugging information, though, we write out the pending
     debugging information, though, we write out the pending
     instruction only if the --no-gstabs-packing command line switch
     instruction only if the --no-gstabs-packing command line switch
     has been specified.  */
     has been specified.  */
  if (prev_opcode
  if (prev_opcode
      && etype == PACK_UNSPEC
      && etype == PACK_UNSPEC
      && (! outputting_stabs_line_debug || ! flag_allow_gstabs_packing))
      && (! outputting_stabs_line_debug || ! flag_allow_gstabs_packing))
    {
    {
      seg = now_seg;
      seg = now_seg;
      subseg = now_subseg;
      subseg = now_subseg;
 
 
      if (prev_seg)
      if (prev_seg)
        subseg_set (prev_seg, prev_subseg);
        subseg_set (prev_seg, prev_subseg);
 
 
      write_1_short (prev_opcode, prev_insn, fixups->next);
      write_1_short (prev_opcode, prev_insn, fixups->next);
      subseg_set (seg, subseg);
      subseg_set (seg, subseg);
      prev_opcode = NULL;
      prev_opcode = NULL;
    }
    }
  return 1;
  return 1;
}
}
 
 
/* Like normal .word, except support @word.
/* Like normal .word, except support @word.
   Clobbers input_line_pointer, checks end-of-line.  */
   Clobbers input_line_pointer, checks end-of-line.  */
 
 
static void
static void
d10v_dot_word (int dummy ATTRIBUTE_UNUSED)
d10v_dot_word (int dummy ATTRIBUTE_UNUSED)
{
{
  expressionS exp;
  expressionS exp;
  char *p;
  char *p;
 
 
  if (is_it_end_of_statement ())
  if (is_it_end_of_statement ())
    {
    {
      demand_empty_rest_of_line ();
      demand_empty_rest_of_line ();
      return;
      return;
    }
    }
 
 
  do
  do
    {
    {
      expression (&exp);
      expression (&exp);
      if (!strncasecmp (input_line_pointer, "@word", 5))
      if (!strncasecmp (input_line_pointer, "@word", 5))
        {
        {
          exp.X_add_number = 0;
          exp.X_add_number = 0;
          input_line_pointer += 5;
          input_line_pointer += 5;
 
 
          p = frag_more (2);
          p = frag_more (2);
          fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
          fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
                       &exp, 0, BFD_RELOC_D10V_18);
                       &exp, 0, BFD_RELOC_D10V_18);
        }
        }
      else
      else
        emit_expr (&exp, 2);
        emit_expr (&exp, 2);
    }
    }
  while (*input_line_pointer++ == ',');
  while (*input_line_pointer++ == ',');
 
 
  input_line_pointer--;         /* Put terminator back into stream.  */
  input_line_pointer--;         /* Put terminator back into stream.  */
  demand_empty_rest_of_line ();
  demand_empty_rest_of_line ();
}
}
 
 
/* Mitsubishi asked that we support some old syntax that apparently
/* Mitsubishi asked that we support some old syntax that apparently
   had immediate operands starting with '#'.  This is in some of their
   had immediate operands starting with '#'.  This is in some of their
   sample code but is not documented (although it appears in some
   sample code but is not documented (although it appears in some
   examples in their assembler manual). For now, we'll solve this
   examples in their assembler manual). For now, we'll solve this
   compatibility problem by simply ignoring any '#' at the beginning
   compatibility problem by simply ignoring any '#' at the beginning
   of an operand.  */
   of an operand.  */
 
 
/* Operands that begin with '#' should fall through to here.
/* Operands that begin with '#' should fall through to here.
   From expr.c.  */
   From expr.c.  */
 
 
void
void
md_operand (expressionS *expressionP)
md_operand (expressionS *expressionP)
{
{
  if (*input_line_pointer == '#' && ! do_not_ignore_hash)
  if (*input_line_pointer == '#' && ! do_not_ignore_hash)
    {
    {
      input_line_pointer++;
      input_line_pointer++;
      expression (expressionP);
      expression (expressionP);
    }
    }
}
}
 
 
bfd_boolean
bfd_boolean
d10v_fix_adjustable (fixS *fixP)
d10v_fix_adjustable (fixS *fixP)
{
{
  /* We need the symbol name for the VTABLE entries.  */
  /* We need the symbol name for the VTABLE entries.  */
  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
  if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
    return 0;
    return 0;
 
 
  return 1;
  return 1;
}
}
 
 
/* The target specific pseudo-ops which we support.  */
/* The target specific pseudo-ops which we support.  */
const pseudo_typeS md_pseudo_table[] =
const pseudo_typeS md_pseudo_table[] =
{
{
  { "word",     d10v_dot_word,  2 },
  { "word",     d10v_dot_word,  2 },
  { NULL,       NULL,           0 }
  { NULL,       NULL,           0 }
};
};
 
 
void
void
md_assemble (char *str)
md_assemble (char *str)
{
{
  /* etype is saved extype.  For multi-line instructions.  */
  /* etype is saved extype.  For multi-line instructions.  */
  packing_type extype = PACK_UNSPEC;            /* Parallel, etc.  */
  packing_type extype = PACK_UNSPEC;            /* Parallel, etc.  */
  struct d10v_opcode *opcode;
  struct d10v_opcode *opcode;
  unsigned long insn;
  unsigned long insn;
  char *str2;
  char *str2;
 
 
  if (etype == PACK_UNSPEC)
  if (etype == PACK_UNSPEC)
    {
    {
      /* Look for the special multiple instruction separators.  */
      /* Look for the special multiple instruction separators.  */
      str2 = strstr (str, "||");
      str2 = strstr (str, "||");
      if (str2)
      if (str2)
        extype = PACK_PARALLEL;
        extype = PACK_PARALLEL;
      else
      else
        {
        {
          str2 = strstr (str, "->");
          str2 = strstr (str, "->");
          if (str2)
          if (str2)
            extype = PACK_LEFT_RIGHT;
            extype = PACK_LEFT_RIGHT;
          else
          else
            {
            {
              str2 = strstr (str, "<-");
              str2 = strstr (str, "<-");
              if (str2)
              if (str2)
                extype = PACK_RIGHT_LEFT;
                extype = PACK_RIGHT_LEFT;
            }
            }
        }
        }
 
 
      /* str2 points to the separator, if there is one.  */
      /* str2 points to the separator, if there is one.  */
      if (str2)
      if (str2)
        {
        {
          *str2 = 0;
          *str2 = 0;
 
 
          /* If two instructions are present and we already have one saved,
          /* If two instructions are present and we already have one saved,
             then first write out the saved one.  */
             then first write out the saved one.  */
          d10v_cleanup ();
          d10v_cleanup ();
 
 
          /* Assemble first instruction and save it.  */
          /* Assemble first instruction and save it.  */
          prev_insn = do_assemble (str, &prev_opcode);
          prev_insn = do_assemble (str, &prev_opcode);
          prev_seg = now_seg;
          prev_seg = now_seg;
          prev_subseg = now_subseg;
          prev_subseg = now_subseg;
          if (prev_insn == (unsigned long) -1)
          if (prev_insn == (unsigned long) -1)
            as_fatal (_("can't find previous opcode "));
            as_fatal (_("can't find previous opcode "));
          fixups = fixups->next;
          fixups = fixups->next;
          str = str2 + 2;
          str = str2 + 2;
        }
        }
    }
    }
 
 
  insn = do_assemble (str, &opcode);
  insn = do_assemble (str, &opcode);
  if (insn == (unsigned long) -1)
  if (insn == (unsigned long) -1)
    {
    {
      if (extype != PACK_UNSPEC)
      if (extype != PACK_UNSPEC)
        etype = extype;
        etype = extype;
      else
      else
        as_bad (_("could not assemble: %s"), str);
        as_bad (_("could not assemble: %s"), str);
      return;
      return;
    }
    }
 
 
  if (etype != PACK_UNSPEC)
  if (etype != PACK_UNSPEC)
    {
    {
      extype = etype;
      extype = etype;
      etype = PACK_UNSPEC;
      etype = PACK_UNSPEC;
    }
    }
 
 
  /* If this is a long instruction, write it and any previous short
  /* If this is a long instruction, write it and any previous short
     instruction.  */
     instruction.  */
  if (opcode->format & LONG_OPCODE)
  if (opcode->format & LONG_OPCODE)
    {
    {
      if (extype != PACK_UNSPEC)
      if (extype != PACK_UNSPEC)
        as_fatal (_("Unable to mix instructions as specified"));
        as_fatal (_("Unable to mix instructions as specified"));
      d10v_cleanup ();
      d10v_cleanup ();
      write_long (insn, fixups);
      write_long (insn, fixups);
      prev_opcode = NULL;
      prev_opcode = NULL;
      return;
      return;
    }
    }
 
 
  if (prev_opcode
  if (prev_opcode
      && prev_seg
      && prev_seg
      && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
      && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
    d10v_cleanup ();
    d10v_cleanup ();
 
 
  if (prev_opcode
  if (prev_opcode
      && (0 == write_2_short (prev_opcode, prev_insn, opcode, insn, extype,
      && (0 == write_2_short (prev_opcode, prev_insn, opcode, insn, extype,
                              fixups)))
                              fixups)))
    {
    {
      /* No instructions saved.  */
      /* No instructions saved.  */
      prev_opcode = NULL;
      prev_opcode = NULL;
    }
    }
  else
  else
    {
    {
      if (extype != PACK_UNSPEC)
      if (extype != PACK_UNSPEC)
        as_fatal (_("Unable to mix instructions as specified"));
        as_fatal (_("Unable to mix instructions as specified"));
      /* Save last instruction so it may be packed on next pass.  */
      /* Save last instruction so it may be packed on next pass.  */
      prev_opcode = opcode;
      prev_opcode = opcode;
      prev_insn = insn;
      prev_insn = insn;
      prev_seg = now_seg;
      prev_seg = now_seg;
      prev_subseg = now_subseg;
      prev_subseg = now_subseg;
      fixups = fixups->next;
      fixups = fixups->next;
    }
    }
}
}
 
 
 
 

powered by: WebSVN 2.1.0

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